kvpm-0.8.6/0000755000175000017500000000000011733530416012731 5ustar benscottbenscottkvpm-0.8.6/docbook/0000755000175000017500000000000011733530416014351 5ustar benscottbenscottkvpm-0.8.6/docbook/mkfs_ext.png0000644000175000017500000014553311733530415016711 0ustar benscottbenscottPNG  IHDRTfBbKGD pHYs  ~IDATx]u|{wb W R-P=w9ݝ9=I.~?GyfΎʲ+ѸcHA\~FE DW.GSͱOiOc!6e9Jym1[/{^Dq92KVKF#,|U*uo, ¼1 }8PA,~c3l.Q,M':c,p GP؍q#7N ;iJBNԈ] 0}O':;bd~5(\_0)\NM^ {9X !%b_$?@0(3w8n2 (')sWvw2"L3 Ϻp/#p]a+{R3n7}N͎;i;? _gqcc;:`1:vGIe%W Ae`2cnzJlia^;իF'rwpO7oSS,#B ^;0++ C% 2"v@CIzG`;/}ufń>ƚ';[?`hMY<jB8ik?G0" Зu0` a04ab"v@C;q^Ay݄I5Ʈё8Ͳs6.98h?V?}~-!c )P0caD| Xc4( s7d="}e.=n^9bk E,5ΔSXtCwq}^y wp5z$>ߺ%|?_ymL0KBǒ`/ٳ:uݸv[2sy"}ytD1.;4m5ov-{l#̧< &{Sy0"v@pe2{P'[O@ئYۼaOsFl7hZmYwtI91' r$3u|N~/JL5 A `7 G|݌߭LnqOvyk\ќ|vVt 0(UH? @,:`owm߾y #bp1te5<.3pYy8lPs/Blqzgwv:-Pn,d :n&hD<L`W=A/N0X; ^8UP0p^{Rslg`C dg7=Be_#+9]mƒ7lUWU[`p?cQnZYNrM(N%eK=tf4]{@e]-)@6m[{.v>{DO "r$g.ݱ絞܄ mm[tn$. _(rM{^Lc^aώ1U3aS#ϭ)Q?Rro2)!p=v(Qf?5r]N8))jQxTwÆYY yjclt뼇'/|C{Lwg{)Ly]\YB:at{pd'ƿ{3O tǞo|GZ̋zʉ!$>͟6wR sW\{NW]ix67*>k.3ETǮ_~)^Wq_g,#7ߢv )Sf]|׃WM !|#":Vo22 O[͍$jno&T)3.kQt%?sD-i9tܣ,egrIb1WʓoQft:z,Pz/oM͋;G?˲%3Is/bKNYr5L U>.<2݅+xfmof|O{5X50:.ϻ>`! u~'DhdcIGfn&[W^8ZK:qowf(z3]8G߲m;3V]yC{?/+JgK ںmm==+7=ݟ6~x_ceO|{] c;VUndU۪Y 6sNȲ޺alǪ~e|ϝ`Kgq˻Yvs qǗ nFch\ɪ/6Gnum-;?hY5~4ƏRSņSD>bKx)Xkiʜ%l>/QUNf뭷A?ί* noeV]0 ?Vio)cn,Gh3߭7۱f`9}/Y|f^(~4m(`{{~s# eG5.l,bDt .(5]^BЂ؂\?ءY0~QiW^;'I&xj Wd3n۶}{3eE5mґ@708iZnody9*%7ܺU@OM8'T+ UO.I߯U`1*,UvLiv򜫯?o[{=yjt%&[S楉c\x_:5,Epd%3>ϴw! 0 }Rn@juRPD*@WESE H0x~m? ѧL'(}=#SoƝ?[8pUiPϾ` =6F2>vm읧DE) L(aBB.?ӵ6xl!6T<~/T$J@< "(@”QJ lK&A;cHDko`&Q;Md^skʋw,?ټ)J]='w~(&-Ȕ8*Uq۫m)++klOM}0׹3[LW|)6V|wshR44uSXI2nPǪ 4aoU:}R He _oڞVɣ ya߇VR>,)0 61 ɼUuԔOwgRfj)+ش'>XWj ̛8!U}Qp d920zחZ2&NHqGH2nWv#elvcsN##N[tZ @ُn3rijK8 cL:emSz{?ƣgċ0/P{~XQ'C Yۍ-śv$}љ`/؅gI .M/`/eT,~q_c?YvY$}-֮u~xw-{q܃?? mɊwd]t1 %vIY3'EI&@y0sJSN@ .?e ƽrao,.][W"ٽSֿ3S.L˖.L/*W ыgyLp@u>ZuY?(]}dVw3K=H}2U츔d}]O/w3Ao/]78V L:/)H=$!"쨨9>rػc^뙥?ܵ7IKA6p1I8C4mHu9 _9ܮ^> _l5dg-z;]5+;U((!`1sRkr.|rmOuagՃ᳇ېb0 0b; M}X7_CR`vGx:Q^Z{y'HwX!Ϲ'x:>A!4c#35!}ljOAǡ=ۅIy#Pjb>)>x9`iW?r@B!Iqc(v;A bR)_#fn B6xQ|W.`*hNMKZzPV\T.x8|qOW.> fW2GXVHfE+)) ]c蘢(aRҒV\.OJN)-)F%h:]gdj5NZC5Rӓu]]k>{l2+GW[Ϸe14׫bb 0 f~Ff0 Ih]co:ʵ450 ͷue8zU}1pn/5N%I蛎rEaU9-}zƍ7UfɏGܒk>;\xaU+|߬\wL5oe?oтЌg_se㥦n^ߖq KnSo+)l^{˞kŮF8;hoSܤZQnƃv ?={1mjsƝ\я~|owZѳƀi;EQCq#ׯZѳR|,D6u?q-]55In=?~}p{5 &\:SV̀rǦBy)_۵盧-ɑf.k& ilBY\oo%פ"9ZzgM$Sy|eчK"7*O3lr_r__}?Jt<_&EUDl\̛n4:Qr³p^XJ c`%ޚ%e)IoxU6gϔ Lܝ:,6~ػ3|1*Uo'ʗ˒!? z%B4Mf.LyeON,|V8fyrVP{ZLu Խп^z|n(`+KӾǣf}j{߲s^L({7re\Ud#tX߮+2gw}rgqF?G<+(uÖuZ8j ^Oak~]z}MRW ~ݜD 'n!saܾ?Uv3 4)Tkgyo|-ά>L˟O?_;&(`]i-L׶O7qY^Z.p׎j/}0 |w^zy|Ƽww7fW)n7vJ@ 04a}197>Xk4P4c,ꃭa=_~)+ײj}6WuI[r+5[|Ӯ9)7}{(NHŦf}Ũe\7Ef/tL-Lgv|fL]p}olwɦMJԊIR?J`i !ι m\z伻/ HC.8O9Wݱ$G+"0wrasN?H2.9~()-yw\,t/6wZa2/I-&H.P$tw'oMX9.ek6IϿO%{VZXnFO8K=c4?a՜iqfiyXO14Ц_s#>F19<+,4>3~7XK _ U\G˿|?.F3{g埽rmeXݙ#κtBŗ+4c*]eմz]=Y6q64c2y zB&grh: <-c-tk.9"]&, U/YrtS B675wg\siT:9~/2|SE<-,@rycҨ 3%g#Nk(ky6M8ꬫOκzhU6{/*Ŏ"|OMV}M^^egHcW/5uꔩS.|`Kf1! #G4, CPN ҼQEzp'w`Һ[}ޞ.c 7^ZqV1N`/Fomui7 @Y!͢kHDq}06KB ɴ #C3ꏾcWeb'\>uĦ K%j|U>%$@Y;-){1"T@ ݈ UBHǬ{J_@ETXǞ[^XUi7?q`k/dqH4_*M[eܜ+bS\YR"䡉餧(H:kJ *Gyv0*5~^<37NnR2-U)B,JEi}PhJ vkL%Rz4oXPoY#Hθ%Io¸o< >>Ɨ-SkY\~)?ԧa^2^ӹosK[O}a0f0X[+,S_Lalv_ӦO ?'ԗD˞UdƛG/0駫ngCz<=\]̾/TΚnQBbsle߼'yąDkCwqZ$go/ϺcM=[둲#]7őysWZGn_94L1snT*|"W0 ׏#5(y 6씭j׏}qȄKQmO(-M%CZl3LpQ;g5ko_Yevb5vKwX:]X2w)gNa=^(:o⧫Θy&#t̍wMxO=?-tQi/^Z}s.x~_ǻrp$waç͘>s&]LQIgm~FkY\kE>u9Wfmc{ߍSJs,wo-l-/]s,~_96?Ҭ:9_V)2F_ݸI~?O?|>6m(q=TUM6p٤fvkgOL K҄H,IK[}Wfn`4{cNvJ*ߟ#~' dr6_þҴś_wN=ʋ8ǧ1 ;Q9˧_u7K?6Js^%#[*A?ǵwJ xaۋKN_x}{cq|慆_&] v?~j,}oz ;ߜqVf$֎5N& ݂N>{l B8`~X Mo{yS<,Ϝ5U7.qTm|U5N6 ݂p~' 柵x[U+.Ɲ\!\myl&!D&Y ٫ڰ_V | dX= sQAsI$F9@j/(WaD^·V #0Vl€6K1~jfj@,G nAj~S-z“cs/hlO "*JqHJI+?0M M9Wi+99e'3j+JTJUYIZuvU$3$8qqEG9ARDϟЭ)U.ڏ)Ct 4 P OQ' ?N=0鸯%}^׿6MvBhd9roAH?(ƣ) k0-IU)[9Ĵ\'G(tlv%Oطxg~sڛ7}ڲUmi=p9 LsvLH]e0,h& Ǟ}M sW.mgUt굏޽0Ez7kF\?$hKALYz ]_ɕݺ㗗dUڼ+}Q\W?Y_Uk)Ӷ{.,-=v+i2/rX+Ca}x3ߚ%k;׽$|⣛::T׻]kRk/j}0׾"J[ N:f G?g_J-)Q^䵲9zJt*ym*詈ӨWGt?D(g>r{}[ey}o^$홥AuSŚ[vt|/5i/oT7k\G'|]㞍K<ߗ?7?)?Y ?~򳎹/_F4rDVV=3 -]-12JǏ~aAFL^W_{m FwbuV?2MsDŽ%av~8"ɕ"E\iF}ce^+I[%g~0$,ۖH{o9aUا@:~ΙaVb{Ή)IYZaŻ]dR [=meWHPbM&x"a}qK|7jN}{gGSJQR/|*Q6Ȅ0^$ݧ[JnTg]KkOi C! Cft|_zGS !# $Im9M9˙ɬM( MbӳDUOdY7Ɲ1kyF&*LID36jŪ6rw?/5{"JƨF&N1.{o}󪺱8z’ko͒9Ksms\Y)f57EV)yofddBOpl~x$:>s{ !wdS^{;G4}UZhi3{E-ZFۅ .7쓕{, 䎇Ji۽~}vUu$v +VGT1Κ97( 0~e\{_.ٴ%Oطd~oИ}ue#g @n8({Nyiq};W&9O7%Y0mϒj7&J9g|w{[EKyIZCʐȇ.w' ū]߈\?>8ǿ1O/}ֶ ͈sMSD.ik:﹯Wv凷'z3U^y{+\ s:~}e-Oc{rKװ7=}v='eS^O_+#k `>[#}5kV{Cf K'6GcҸ3(ܢ9:,۩3_oKgÊOnPUvDiS.WJIӴ ;gϞVVܸ΅wm6һ/Z0syݻ&{@k8ⱶ;:;rTuZ2ղZxʬ3O]|퓟Wgwq&#uy՟< >@0l (Z7s3փ+{~z](mZ=1\v׫%ݾޥ#Gϋ0-jtO}o(_˜}"8V;\C B҈1g-Icڰ쾋̞=ssZZnټN9{E?sl|»60ֺoq3\pWk.L &DIW=~c?op9w=t3gys+7qc 9swm2e._ O=E׾̌18\g^ԙg/EFKs<%KfvWCŗL buEwp|3{Y3f޵`|,O5c]{;w~;Kpެ3ϸ♕f ;6nk>k:eY/}lyQs$6 fΚ1{pc`=!yzNJ;_~_//_?~WʎW_}#op~Q5Z\|*З7^~J~XoN QkcŪvGG-oy.O 79em<\s|G̎_L˲hkW<|jc[(v|Dןy%ϏS{Ꮝ{s<~77nxsIbUI#׭?Qw1*˺5y퟿s5G>>_׭_Ȅ+L|S 0X'kP0rW#GϜ+7KpRo_ZDkk I;< F`d-YApKD Rݐ^veQ"Dj01F؋8//T{g7f^} UV9/*Ƚs>G94;΋ c KH6͇|>z?ߒ{d/>N׾hV$N]rÝWΈlc\~ˢ,-C1 ؋8oLaN<{٣#Hӯ1tR~rYs_9;G+9 W޸<,ԏM{7ˁuܣov IF7\na@s3}ɇ|Rթ9o_MTwY]=-Ev#ʞ1ch7K2$%k̇TwY=7/aX|{#H)$^5?nׁJQ*gw|/l, />H|Hut & ,Ec>KB {J,4fzbho~YΉ0~87ҊY 6X~/9zݰ,c9}?;i6;;2J+o+!NL ql!`KHeTWћB'RzWe y?}8m>D \~Irz=&E [BNUtIT?4(#eބߚ\P|F8 {Uaի!c*ZJ*LRii9[cƽo.  9v5}rNrV~h5g_5Ec|f8ӻX,HF2ʸ),N?ɴ,~xg"|(?>ؘPSZuݯ>]ڮٲб1B$ J.-pc~\#&6ӻZ;x: ߦC^Ã_h^.rD$h5 #ԉzE Vioo?dXj0kv@߽~=[z;*eĄ͛t\jutdT||\&ֳoZ5D2@~d u7ޏ_REGEɸ7 p=O &BLqp>T Z"DFFLfn[T*XL _{Lѷ4mW? |a/!B&J%/3B?sox9 {N5F\Cw7` mnh .M5m2e 41PXu xgTUVfNNIE'&P(;v 7 ~9 [C\@4Mwvtfd475&'Q-kX{KȘc#X]U6&܇FMY5$ Fi5 x1P[֕{L+~(41+j_YWhJ 1dH[]]CCûwU&J_+>Z#;:P9=MAНE $AWog3*+ȅxY.wV5(Ɛ8i8@Ts7Pպɳ'h@_")9Z{np(< ˙>1Ju :ZDpcvfVG+@*sm= DB6{R)C{9ִiRt24KSѾªv "uC7m2eVrcb Di%deK=>c\^ڳmm;Th2&nR9Շ Re1(`ixi!N0YkwJ)= O qt 6N{b8gw]ήrڔHu(bіi*S}j8,X_݀&u|VZzQSR$%++k 8]k)Z)Z#1 #&̈N*"375qt(rUT$j$p߯@SG;r ITN~Vl6R4: }nFxc= d!ZIRF.m. SCbq1tWѮ"kb$5*h4/qX7 D`?kQP4gƂ(vW4huC j {zxR'LՈݻTELOSNTw.n U߽H5+GK ;vLUr߮E9!$];cϞ%MMz:$' cj:@v̌h)Af)#*@}xnM B X^YBcU90vzAaTO>q_ zD1v3%8!QQ79#+leV,^D2[UN9wsD=>pGqUM- '#$)㡰dfZLΈЩvHeZvV\X6II!@2:nK#%$I eԚmuz:4ĵIf64$)' ^N=fR*C`Z*3')D dHRĤ1͓sw4u6{TNՂ%:4UyI*"tl䫀%%/B1 }1)&NK;>)Xf*J@c[qO8#LgѾ*"s)JkݖWU%0`s6R*B 0@ fz,T_|*P8fR"`|tЀ/~{;q$4S.#HG:ې:!pITx^hd{W*IX) 6oS[ڰ:im#eB`$ eMo Fh''4޵K0 Ud6$`c7; վ{NסrM%iY-cⅻ6-"*}زÅ[wہ%eh^Cv]De_9EjFD=i P᮪^zk(e}E&FԒ@(Ƥ*&1^Ŗ WA MwۂIihb2VoTۡ-H\@[+* ()bd՛['BA:Z'V#<1ybH}7rqKӑ1 B=7#Нw1OƊ>͏VR*TEPVMVH$02> <\26%45V6HS3CCSЎ;q4f+ ռퟪS'1Av08t\ڢP(u]*Uꖖ `ȩgf꜔ޯ°a) 2GOH8?RL73i\O,:gztN@FOvɥaϗ YrD͙vִ)qE;и 'ztΚ=S]8?ROQGBUlF~V@j2sܿ $@B=f2:}v?)@ch_}|̀=J˖w~YQ!MSn᧟3#a ׷uvM.N*yqs0aچ?;~3N?dkGDڢP* <(hhmK$nXY!n>KHHŒH%X&JDSX _1pBBC ݍPTJ81 h]m!4$3 67 FM"J{+#+=qG(|Od_ڏH׆wP%HEbB bD̏oI y"A"CV1#=+8VI#6Ãlj 﫡۳"+xȟw`ӷI-81Lb94e۹ B?1b-y,0xC*n4j%RqafYEP('9dEQf%>1hnkJ'#jk@ x~|U\\LX :Nj#CFҨWuPϙ}_tqnE CBCCCCNLZvlN]qbn| Y^ьlFF@:rlZ'_$hJMhij}hhԎw? $贜xe [ Opq⣺:'77%5M*&$rsG|O6 X,fhƿiX ø1B&.c7WڿR3-MvCIs#Z1Hjٱ&yV>P|BB"P(ʄĺc0۱uN<9-&?Ó5uf)TrLȴZ!`KsɁ3 "Ulnٱ*yZRƽbC&̝A q Uɳ'F@ˎ $eKucc$m=rAo*&=7't!Psxq"e 02y?a`,GKjZ v@"eD|jzJZvlN +r;,1r RLtz??7*T&8} (Β?Χ)_`(S{UA&Cl}LZPwrFn(>(˚2!B ֮ 9w=|҂60ۧSMkǎQ]"'Ŗ '@_u`ߞb,- tWɞk|)Ԁ1`]m~noTP6{01u5VWuf⾝DO 󙽧6phAŋjܲg[u ѥ ƾ`Ҏ=4|d4 I@RAO"M&D) I p?+(=ШΝ)fXpK TYZ ! :#"}m 4!Q+& {3k!'m`ez1uXIɑ Q!Twt dܩвsc&ARbCHZ43?B8RdHiX|vss3M#RG$iL-k;41)9Y *sZvn,SKZj }7*rUq-{\V Շ+UNԐ!*NE e[.P E1hv r0x[c;ecoyyrTʜ09Yb 5 hSsΣ%QacB$PU)` \Br8m#0V @JDei> vt $Mť6BEXlZfjaE*R2bBVﯴHȋӈ:qTbN uuM"F򘴤 zJ@[&H!Q'd{HYRC*!#a>Mnݗ<+Xcƍ41)M _JZ0ąE@$EUe0;sB*CvC{]U6{Ttv&Ƈ)HNEuQyC=DHݞB7 Uaª1IjWb=E5a *UUu29 Hah]6D!RuADYab}&+8~efZ*Ap$NGbOY!v@TZQhGΈn$R ȅt8+l yW@sS%`~<;`9s`t=_;wWy O4&Ba񎿻@BӳPŇ4ݻaZCguE=6= B0[FV6s\{7AN` @hǥ9kC1MH]݀1`3X-Hao/EQ &:A! 2 )!f:BNc6ؘpD16?*.;/6), qv[I0@U1WW.P &1{s4).Xnlsf?qmŕ 6BuzTZZُ.DL:e l)aԼTؤ":kbt=XV2!wJBGZR0fZ˜ˌ>5Zc@ PMw)bc#R1wb4"`ӛPǣ8{DP@I6;N tD2qZ) ]t tq!r2{ %uTh a8) -/?Ѷw[m”qalc: 0@\_l]@P),\[E"ѾB4Z`~&#a瞞 U|dWEnRȎ! ܵ4cq> uJnJ[iBaх3OwPKR=). $IʝnQ^$&M|^a(pUw^ (DǼc~VAG4b0{ cff\&M۵s^㒫R##b2)x\0o.~4 9Ym00lyT[LFT`61b ??$*BjZ,EGfh2TTHR߃wڼ-ٳ2CKD"ijWi, I ӎ1ld2T*f'0tXY39*L:ҴZA*+?@&` c#btK9GXx=сl@$MLut|mH$h&9K<A`~qH$|= .˜ά*Ἥ!W /)Bi_dfhD @hIL9B MOM0D-6im\B) I#} !@)Ʊ_O-&]GHq I/q2c܊!A,6K\B`9I1u55@31ca-(Wѱцnh`0`BBBCB=ZwmI>!|0&mxA04W3AwqvN'iKsd2"#;tj}e=պ{ÁIR OkI%A)Og2*5;J_{͛ TANC; ?XdjwrH1/C3q? c!n,@7m|SVCWSuў]qjȰYa]76 =:-GLJS:JFϙ@R8m|/uC@$("xYy-"X3ؽ5q|Πu uHkf',-GK: ) OJ9 17-bIhrNn;:7 ͛5>\@늏5v;[ɌU^goə:!Ʊj?24B8yM% ΢DhkcR::;CuJ) սpv^]bANPE;Ub$,< ƙn'xEI t03m7ƎQSjsc遃ʩ!`siIssMH͟5ƵYOPBw͑S35Kp{q!*EAPF,vE֮z$D}4?+Y9eb&R(]Gk +_f7=^ #hckW'A_Q5:1 BH(0vHErzZR cB$"65~OS7ED|^R(S8L#Mbf\&CF횅&1%fkII/iIuw" Aӟ=kR L͈H+jM PJk&13agLiʝEQ9PҦM(HcLӴn罒 PH෯ i ɰ m"-JPfn?PSfZ*ZY maqBD,YLbWeUnn+EBƎȁ⺘ݺ{N\p. >KqCY(a/ -SiG K=bZB( 2":F*\_ fhm1> }q{Hc}7XJdˈ#⣾8,tRJڻ-Lsq||RB qFE,@ZVܠY )N:#$TGHLTL?k8kZo})ػ-[ 6'#SscN4!)_՞*dmzP4MLJRlhoo;P*&d2&ǰ?'d4h^ᅬx~d21O"Vv'I]ebl u:L&h1b1EӔsH$ )$,\ӫTNvl=EQ]|i}Euyu ?NQ"]Vee)N7OP$/+RSRG(*(?ym#b ;E"""e2= M&s[K`t%>?Yv# q/ (vn.BdrY||\]}];ܽcR+<՘w)C\Vh~Ve34MwrysSWF!**KץR{v>toK>}nu)`Ѕ/ )/㦅24@m{/ BHVDp̕P**JTm ̀lchbl #ƱgЦ,9UA Ocvd}w/OǽC ;IҬnT&H%^LooPG(F|89Ovcеd-NؽfsL;N!lBS.IR{,.8o.>ղ勷?P];»<;K_?X@r%|F˔q`L۾- }-]1)+R]Is_-6HrFOmc/?#Vt gV̺#M>zŏR9Vn=M7|r$^/YRsN˞(ӼYd5b\ K{?#G1sgUz`9lp{xPm2#Rh9{;1t)!h|Ao'$ EO_qɂnX2Z+jG/~d?U,wϺ~Qoh z IT)s8/Іj BX "hCscs]1fэ׌j" SE1Jgm@z4 [2wrlW hzAQTyE\vt=+*{~myðؓnz\({h$RS=3g~{[0߳NQ9c {dMHj=zpJJ2sg_\9y~+7aW#۳Z8۝NQ4MkZD%l꺺H-{\6@HD*dO6nWLJ-vܭV(HDr8.2Ά!A,K|b`hk\$-J#j'  4juGg׺egDFslim9rD(֨e%m=+p} tnE`@0ms]cHv65/m`͘ic7}I'/2&,6 ɥqq ۷nwwsES)12KV3/#`Q8{V N>%#թӫjMk{Uie}$R$$%HmߎIcϏLwM!"oZJT-ƆL@UoEvFF%af+T*Q*Ռ/#`lsa3;csu)Zuu!(q\ 7- c>5+ CC':ÙX;,0@NDնoˑ.-4Ɔ cCIQy22yTFmRJL&w0ZQ\eaHYXx$o;E;m a ϶FFJIDAT肨/;cZU+bfTDde(n ᅶ}۫qҰx ȬuHИ0;.r&3r&+!ENCiCJϴ nh&IlR*yu"7-Uƌ2NuGOJS@1c T"1T.1DdO&,e)&:MHqLQc%,m4F& J-N9vRuU(&HvZ& J=r!t|(2W]1Sb`;zVrY˿uc(@{)p#>s;:!SB҄H{[6nܲpEA#LiCc"PK7i0!OJR  -8.-.DDYLrXl2u T&>5uSa-J0)MH RO!t*X PvUWtwc;*!=NJin9? yz|߱9>8$aTHc7K? CYia\"ZocB(h&~~ R^;`$F'VƆ cOC\أa*!c ȜuEY(As8V^ JA'X3%ر<"!ٌ{鸏 8LmXHUB (BFm U&T6ulݲ!4X@U urTiωnC]|tg. ej74H̽˲ yT]_^3]_WVOF=E(bqmiMb0c3Uwq7}He\ X\SD+=e1Vl c7T4!Q "* "!T]uf&Hƃ1gVH}ER )DgPFnM1RTZrx[1*sGՔjjҔd@F%(jJ}3Hypg!ɹ=^@UW*ytrI4! 4~Ia2FN $;ڔ NVT$r$ MQhR4vFSQFV dJBD*tN)]>]"c@o*dzt~Ff+ eqYzSkۚwC<Aw'!)N(WxYuT*bсBT'k;+ f|z6K++D$)1BhŘYm4JuB7 ~BPIBQwtvn_̰p=YZ[Z 2ZF1CBHVĢpdDP*~O#;' ! @ I pr:t&9)9;'9\T&&&U7g`EJ_{m4BX0X-'OZ9vH%J5,wҁ:ih lk޻ =G@;17T e+.,S'ȇ8vEx,]q֓֗iRLWG7)OQCWVS\60,,lB~>$H"\ciE8HBcce 䤵AgaYXBfFZVUXea@(JHOU8Y"SwT+cfZ,.HQT]eKrM E-&DY|M}E򈤬h 2WW6tZi 茬P`,-%z+$efǫ83@60mP ej3fՇjř3"C}sikk;R/'Nڲy޽{Lc%F AX#Ϳ9i0h 0,Jc:RjȚ&,G) 24Z_Q&I͛*E6}S]А-2M12UK#u]V"bsF_y5fb G+hmª 1`+.<\)/HQtWqQ*1g(GKUYB+tGLQ6]Y}Ά>rDn&H%5.'MIp_J}%h"3GgDI2X L &NeI&X |"Jw c_pr Zwh6s\1BlNVIq [ FbDr:.U @X#!OLR 42JӦ K$c ˉU:.5q%vMHI|#*.5H!Q&9y}AIX\\v#ScP 2I8Nˉ޾gzP%MȷlGeo\)Qb$h1-抃{X'1ƍhX.Ϙ9duO< s<NC14!WocLj; T2[M {TGP@JɊs%?#nscXYwm :-m7L[mU4tWiIMFcB0`LcX#J@c4{HZ%!) *u6̌N`RۛPM3H ʫi1) ueVhn}[Q|ؚ$'zGM&JCtafee4sv6mΝa23Gy3x.1EQf4ZD"cXtN vp' UWy90R@HIJѪ#[1!Ħ LsFzD$@ *UIT%f%QEhyY @ʢd]U%NTPjRA P&fU8$!3+x^w(>>&OCh5 ڤûiA =%'J4讹F?~ݻv1q⤝;wLi'-FfVxV2E56!dnm\$-J#% H~9@߬"$or ij2gǻ%)y)=$RLKyL$ObP&CSLJ&)c"STvPСc-諅侲(FOtK=:Сn6防O :l?CÌnHsh0MmFv N(d&6dݖ}"CR8] iZӥ54|M`4"""u:Z$=:ȌDCC'G/;'|`҂0qVYӧ:HMujֱqGnxϥ4E5Չ%b{] 8j8l1[hqP@-=uli,,PAN՞ |Ee#F߯ @Ǜ]4Zmwhdt\8Z?/)`з4 Z: $͙p31N4.6T.BLUGv("EM](R.+ H"B(iGsVLVjzּFƥF{VU4, ! LUߪ#!q`wc\~NKR##7%Gػj{&mj(l5R P'fDD`,-եM6kҳ\Au)j@1Yٱ v AD"&: ܇< -JD"()EPRtF$9Zf 5>[MZ tߖ_QqTJ&F M0r>dG!ID"Ɉ!Cfs JlD$12MEvnٺsOau7#R"$TD'E-cB=jLFZB8$6Fbl7Pc Y|bB0BZ,V)ƴb" 1b}vӇ-!]*%#g%ɧMDLRR26%ohjG1)HYxBGIJ__5&=To0vM y@ ܝv#YzL/TǥcH0ežLP&)cv}CEuSo}$ zD꺪õf;NNO!no\ [~{q JGFnJr)+#FLD7Bu`ss̎RAX~ C+K8Y4wbxLah;Z{ 0"H nhm33Q؍6*$یvR$hCuYK 8gG >݅Qpeu*UT,dJ q حCIȈvU* BLM6:D6!vcox EJvX{qQ!ة2(C8fVmÞ0bPcC~}.خ+/n2)L"@[ꫛVv7T70`L3 Bm7TQ.b+ vfیccEдQ[VÒUBS}sbhؗ)(ɕ/H"TCeJfF&A ]Zjj04-+IZ|*|A?RG0~AV8wzc |C*úk*v D)!4J/9XkeHYHܨx9B$MIihDLoO>)PuV/aBJNV !Lm.[H!QG%IWRb奻i"ݽ*ɕ$Hʢua1 <>=`Dt`W8$% ̬` nx@$B 6z[ $ڕJN.DR$Rml6UmULFR[ ! IO;z\HDG%zGMg[B$GeqZ@s)ɑd{rh,:%':C>! O ObOD1A;cL4A\?ix*>J Y' 0Z-k?,HY_[N}峹PYhR* !+D"V'6o 0 "z}dLbX,'[Mch-MM!у`gV`{!d2TgBHU 4M k5TCS:>9-a}AwQ7)[Uli.=RjbT9Ss‡fYtzREl??P @hF! h^ N"I2IwMS{1g>=c;][ݭ]0^% >T qG{^3X,;dϤ {RcL3 C* !$ƴ]TZXR ?.N6V2 ;frrKhFKH/Ns,W7(K i:Lٙ]` c~cG4~!>1 %xe줞RýUuP֬z|?JUuܺ+RpܛvЍUCi:u}5Se@ bT8*53x 2Sf ,3=4i?o`<rVO^OY;4ʤz~DL8:TŲq8vmPmzvXDk~n'/k95eFC'&ȆF3NOdzh!956WuN ]5 x{Wyq~flbxaX|>~]~[2Jz \в aӵ8Xl9ךA,}c9]9^`B*vSW}h鍱G]I)JP_U"֖NoZ$f@6'fL6#@G= uzI*QŒdRL GMNǸg ]e%0F}m ]Msιsc ųbY @=֥.ȶ&ru>ۭPg"_vM=۞tttWEM&!gޚG={;<ՁCy0aL;.X+&+cCpgZKQezA78 Dálljm.ϬD^KQw,е|)?ՁOu 8qx(QkE]|X9c ʅr!rJ!sYg˖OMG%׬y*+yu {D.w?ꘘ;W>vkjsu}վݗVoRWEe` \ "bzUkWŁޞAoBirc[>LIJS*1pdu^ ]c-*'AuBe++%IuzU? pcP2-[yS)Q/^3nfOW^iwͮ@UDZ: FzSЃTrzv2Ĝ.#&'f:酥IG#]JxtfD&g9qkBTR|r<ӹ5yl2sJAГ_%(N+D3eH( ,ʌ6E+:ΊL0WyЊ̆B$)L+#s9X,+T˕tD"nTú8yҨ*D|>SyC@U~f_O]}dw,ζf[y\O_}lJ2j8Pskw`|L $Gˡ>@d%wj55{-ǯ8mnr)-;ͧO_j<3l蝝_~4UsQi69F$KCuƒPA"]MM Y+%0;|) ("tZ])A7:lN_2uƘ 58wtlOorLn;\=p^ {PF@n+֥/gSSϱuy<ܸ'5:*RʹAK >Gmzdʝ*9[˵y`i5Zz\Uc9h^^!Z֪׹rrY;˕X"rYWhWb$:nEA{Z ⁇0 Ml2 8}HVD7*KK6=v 1RKGnڭdF*ݒO<666u\PDG3n9cIb 5`1jkslpS:Vv7Lz<6E$WcC$ C:nxB='5|ppl1RKjq+OO^:j>5ଧϧSm]dbO:6m6/D'&byf8E6ʟJ O6 ܤab#7s^(cmw㼲f׭JOm\9K^97]f EUokP8zV9g8Tx ^rή㨋vGy*W;c䆣'/6xVlD=OY Deb?qj+z1>51NU-]^h Pd -]^XygϏTma83V xz>N.*Rr&0MM]3v~" :d4FRED2ۻ}v}ň!{]~ X9qD`J%KjIX ,mv1RCֻS\88a6xyԧ===(yO 60S`@sp@7,;sm<;qJt=d\82snlPᑡ"FɋB-[:7{_2oYvf|{ļr15iə)p^/O&;#]K I8e30΁CG9c 8Jjzg|oO,˕B82:f6:j'I[ y*m^,P*<^c)g,J&Dh\8I|$ӫT-˅bK5=>9ˬ BZX,1A:xpt!u4DBD%XLOrw,"(&Id<΋emD@%%<|/n tab_.9ry 9{kU+e#:FjKW\dC"Ue9 .tϗsO<X9U[㸥^_`fL,:{:Yi Rl_l1 M*G+m{8[ kE]0 1 "p btbpI’e3X*Q]ןq3l*g1"? ˆnMUv=K+0s4i%2cL00WX]i`sʹx`ao&'*rtCWu(٬B=yk }gW%dfKlȁs@v 6](hs\*W3Ll6+eEXƳEl8π\+lEY<'YŢk h#5Z颵Pb܌e"-}mcWMTvx9_хgB,;wl4ڭ6ޤ*}נ#`2-ͳ/2xv}M&5(H 9XIjZ$Ijll(b1EBq֠#ȞAUUUQ$Y5&WVc,!k4>n1RCnEAcDW\/h#h#5{Z%h#h#;f! AeAsNu3V,VZ-Luc HӣcGObq4dsX,=!'.9vԡ>hѨgp6>:<߷vz#'/N;web񄳻ͼ^^̈́S%6n*GV0[,]õ)Uˏ.m]^FJw2,;ua =vRbD,oj4K;jcMY`?8˩XrD7*{u کڨiʁvӻjnkK\7q4:{sPBCR)+ rzk~ $]dTu4tz-"Tx@4;;<M]%TzD/Zj"i\P]Á@,WDeH%= ;@p|8M1my9 E9 dn65ƆVG"ɬDs]~ ogJF~ @9FjfIR:VEqg&9WhXF=.r&DCfRFL'g2ѹDl D^Zͭ&dz.k?a6ɰO%zzf8P 4A686:4c7>e,NEdilR-nXa>Cn*P"VO4(dA-L$ JTHY]dk:FjƾcVNG&QW]-ꕙDi^dUo'˴B)JVnTpL,m= *Z:W,nDQ^ sRI'ՠ5ZQ78 t9]"i|I6Ә`Z!,e09[i\^'‘dXPj46*XyUlObC);lu "(a5X<}n`d޽@ֳќ'9%eKܩ|"Qs^۴HzQJ.H4^m|4n,PBBh7[̄SOeC<p-c785{$2>6"@J+j:yLtY9KMN9qdee\UY@MM.qӋ++'_}|HؚZLKbb˺zD.Me ޶3ʭ0r?~NemT|-M[B('.-JVo, tuLU Ol[eu4531lq\~1F㸲)%Y$~3r,q&9jK֪ DhYS]G\jG&d(#85}۽9YJk۲tu[T\2|Tv:o]˯Scquqjn5/ezڭXW*.JzCTmlmY R yE˃߀H=LT'||2zIx3mY 8КHݢi٩,˸ol|gqNG&rfZ  Ǥ(Kt6q8k'6rS2ϤeQh*A%1RSp;"ɒ$Q&)O RuzI,ܬ`!A 8 aA++3w 1RCf_OS}ARI J,WBoe ]+vwuY-Z cbCSv|2ynjEޓyr 7'OSFEFrc>\N9V9%i/l3//f©qⷯ 5>r#7+<.L:ۍXF EQ6gD,oj4׋nWU5#5vG*Q a_e?-.l;7QI͆ew%.90{)1TÁ@,hjlio4Rw \+$gᅒDz[MCի&hU6YPTC@< Ж?.2:{:=brfl*Pd Ӯ%V#ü)/H2 ަ2Sy0j2H$8htx|.@oal`vR u1RC8pOs3#d scC3v=U񱄹T"@)9=:7﵋+lX9 YDۏ4dÃ)T5pmhR7p,;;2k=c&z*?!q(hJ֍ /Dgcǡ@!1;abzl&\ZNgaۼ",3Ln$@) m@WWzbM{!(h9pι$m M.!jU*Pk&""9r6A5scKlK-ղ$J(\8NmRQq5{T<8 ˅bmk͛A#W_0!vS(Nsg8nS %ZpFzVm ,h٥t(˕HF[o_\mp \N CSNkuWZ"ՋV3A6v8 W, &" }?i-fB)f @j!cz2JMMSe3sޠP H9[\*ȹ!D/gB \ ˗FtR3tLP9 5|.JtY)??N1,yM,k\za4.o6KWl3h2s瓑x/ۭ ų% \$VV9@)rn>[TQƁiD8΃A׸N֞S9gdwq:Fjc:B6N@tLF/5N__Ij6CfrDC0FS9 B6|O7=(_wK`YFeјjjMM]{bD*[ͭfإn[5i-niǢLT(T2ʍX(+2 Qj똪.9 Pb4_)hj(gcɹc* qeu,sw sh6u?i@1F?E9q@L=M&H.ZHlt]|U. A:yK]1:Fj]31Z8't xN-Tq=Q@q˞GΝp;8"M'm%Yko¸N1RPB8c@8_rt9Jo 5wXmv$ᄩʥRj~~60NV L3)C*1޻_,KB2I-h/t]W Xnjg (HA猡xCdYefBgR t >iTs݄vC9Fj P@P~~AnbQ\YBp7 7ٔ_sz7aQZNVW tۘ:RAc.ZYr=B@|)n dc&TbV8 }1+POcAA.@9F PA14 y|8^ ;kvb(&x ^"9bAmx~I݉"ou)rQVΞԄ -ssG~H/Z[Fw$ǜT& cX&QFzf /IQ!Bl B -MFYRvŰnEQeYyIAdCX5A!A.@9F PAcAC33\.  [_=#S 1"hp!BE<>Tn;2k@9F:BuCOGGX̤R͒$uvN{wݘY1tP(_iL9=:w=<Ⱟ;zdփr TZU$iB>ow8Rb6+=2L;IgАl]]7[n62r X,r]UIŻ{dTnL 匱bƘ1}=*/|!dc +n9Ϫ}CoggMOzd6Aή:&s Ï~yQ~yʏ.FJ _W ؇}O?}Ӆ|zTS׼F6?/;;y>>(=[t'7?{s{wkӁsl+}2ߪ(Rw09&}k^v@Q?o_I^G>7̷{ZӗOٗ,0+mH__}w|&ؼM`kپG?q ssu+^D2я&Ȟf ~m?y捻sowok}Vk{j,ێL5(Rw?ퟏO)07-d?x|jx_┓ݿwsgׯ|G#_oo8=o桻[W4Sμm*ॹ_~ _8o<;o{K@>+Ѳ;?xˋLj+?Cq{H+?O?dgj#ȥ'_ݾi%ss[Za1Gns/r/M۵mضxjb\lpN?/l|24ӅRlv{jb|̞k֤gg~eW18O_x|{H>Ao_':sB62|$٫r]~l>/jӃ>I/ >DmZV=H~=wv ?Yu̶(Rw0η vszیfK+>{e?_imU -B"1+=,Vp8W~煦~ ڔml`V|>n_ U"3wORrO&jbQ PC~~m29ΉjUbpMiHr!j}kpi3|k?2ՠ#Hݱux'ѫ]=G |&q>M$g{^ռU+zy?9 %/?/p R믽Cş}'n֊xqLQ"?Q!ScL?zwd{"Z7q&zy]YU7>{w~t]hy1 e;W4t ^:Vi#5_Ls{lAh(#0.$ltՋ/iپst^~%˘V,hT4H }[JcխZz+</}b$WL淆w<}]K|bo/gWr=#S ZRrZE|30~/xc&~|_~߿uϗ?oqMlC*WLG]|Ӈ{Yx{_yܶSe;+Q^/hufٜz^zȑwP!5_^'Η߉뙿eHK_us_?ˉ;ުLr,巆~ǫqh8[oz[_׾gor}/{UY~u%52Ր~4Mqbym ?*M:s)i9~ӮΎe=yϛ Ï^?h44&`T4h"[slZRwp;?|? RŬV|2"_Ω'{/'[fFb {G< ǯ ' P}zYy55ѝ޲lp*Y uc,dr[v[!O-,f[VE1>,Fv[>OLxr=F9F!MMO4}``qp_H4|X\'OXRù\r9;8p QO/W<~fge98;;ۖ/o9a23+Ei2fZ_^* ,)ŝRZKvv+J!&$+$8&3l`>".ǦyB۳5ڞLii`Ryɂ;9115`sp8GQ3ڦ>K8cǏeefpҟKI Gpuu"C՚ *>Ǐլ8ߝ>#|߿w/!1﾿v %KBHl`6 X¢EL=z!o{Ri5ƀ1L{SLQ#G&JL@͌2uFAQZ28UE\ͳs?N-{#6͘۷=xܼTPc$&&z{a^郷xzuPg~ڽG#G͙;omlfZIȤq:I7K8װ*}50.^Ho"iD\ri=* *3}e|> )qOON6eV|s߾bŊ= QgE`ÏI;f葬 Uo޼YUV$Ȁ6M:g͚dɒ>HW` 2:\$MO(ISҴ6zuƧ%8ɩo=vcuw_2m\q ,Kr6Jt>| ᭍MYT&Lz[l5zȑ-(0py| oѼYz?~jՂwI0޼M+ 6l?c L>yf#F J^ݯIF_T_^f̨\=o/[۰Z =ä䔯͛L#|a0 socV.Ytk׮1c"0ĄD칳=FMLtPgBrˆwSggľ,e%ygAIwMmySS32~qNN.&>[,* χKNJtGks 81'7kl,@lv]8G)W+4422O:8rpa/*2(/^斛Z+WAJ*߹sE淢c 38 ׮A|3UпϴnϿ`.6&y0Fe:'~ÇcqsPux㈈eCkzu+x֭?..zw% Pjĥ7FoRTVGܹs!CD%y˖!C^arXu)Ju5k&NNN^߾ |bw&Q0?lj&+Wlٺuƍe6$\ڼi L""#a{XH"h $]6E.Zaܹƾ3uVo6 .ի[D :#uFVwFӝGlS&ܧr_=Wﳛ*%%<*U(Y2eQ~hSd`366F8-[У:ofB.Fxnv1lp&, ȱ5I4eʔ}o;7lt\\]14V,FS9J,@tH֙>M^ޞ{֭GBHߑ0a6Vvr% Al%alM8ߴV+,((x 4A3.->xHgdf;OƆٲ rs8$ہZ/WNCCCܹq%ngzNcD&VxHk0н[rUޯ+h cboLƾNnNnt4z,'ܸ~gzY(Ɋ+֮h_ߊk׬ֽ6<0PFPghb\ZP2-)12dodebky[/wWoOt=JD\u"N>mo D[U'@Y&dY:p [5v^^.11 cM}0w<0۷=g*Y3g񁯼{  *I#{ /8o:uL0CfOy۹Ks|\6!!2H66l޼y>|8Jo޾ lٲe˰WӍT8|LZ gJ0zI .Ʀ?y::) 8{ +JnlݺQ"Oɉ'y1)b_ TuJT$aٞq$h>uH_N׮\AbÆC&ǎg$1R`G:#gWNg46ZHڡJ5!'-=sW9^<ٸ~{[;GG{G{99%ߎ=7Gqny{{avܾ9mhtcÉ>>ކZOĊ0ǀi7lXߡCѓ *g?)`7!}2%Kl`1XХKG'G_&_hڬ[G9k׮ (bz4|"xgwzEr QPc H*TĮ/u[PZi XQF܍ݺboȑ|r^޸׮YajM0 ^0**PyU9L{_   awPW^ ̔DR")"m:x&0 olKpLT&L0,2? BrئcHN`mrc 댰>} 1xG:#uF+;#3?g<;VM;qZ5*x:`FF擄GI޺}AǖuGibb-vP]t!$Z$~O.ܹaV.ԩS˻sľ+f[ 44ǏazF@Q`)>hi`x 3~1Q?L"@.d;`ń ,(qp"N_dP!J&-zK,92e*(!|(2'j I LRu+B.fD'̢ 6x[Xl SRJ4u*ԡ*''/*u*2;gBA.˜s ;Sp.85x05d%'$2)TX̽λ U䖷H)3Rg(t8|TŭZfbֽC_ ^u#.F~Ľzuoa *wǀ~C|^ߊG9fԂї tVQ" Gߎ .ʡ@ xخQ fs*M#,X(Ûh WQ8HSh-ZEhIKrcL`6OlTl3gF2Ʊ!0L`{<؞ .DK N*$n VQgdC@KRN*3RgdQ ݸw={FGǢJ,ԡB Uz8H IQVD;:rn%̿$L&煮1Nn؍'60m o 1=v)[Շ,JXgz&hl4J@: `'7&>;<cIY]b](uv Yv:6 @^6Ph_chfҘΰjQԮC99%etYݱ37SP$Ŝ2 s0 D i7N lcZr z" +hbU\U)wFs xJU؜+˳ECdRѳ| !uXQ *D:_1ZTESxU*QTxɓTfq3M&'6Wn%ޤZ $B)M-wPg$Kb`9 ,0#gd1ޫ:z"JdHV)I,x%TS49<3fZKJ"E(YNd*F1HOt 3FM Rq$kThZ=JR 0,ׁR*A+`q)۷@D5JѲn]jZ;n 3AsxjØBޕ7̓.XIR$3 QeLa dL:4U d9aܒ.)d%J@b/TX";cGM"hjnWe&@2b>FުiMgnҊd+4 ^&AEJV,#P(=`Fz1 (GꌬL6GQZ5bgrSM)D9P l^DAi?+ΟИ'r4mlVXY`\5bRrN}WT '[h6M 3:~J]֡6iS:'Y !U]9"lɌZX/*ԖVՉ3NhZ)zir)&l`R3mWӦ6U*$6X uFfu9˖%8 ZKR)paWɈ0 %E5IAF`:LӠGERL&d@2ȿ)d.0?TB852w]>SAP3SgUׄ:uF!(6C3kC3Ah6{~/$jSS,xRL)r8v"2%4HaQ;!ۧ MTyLppe&"4`ϗ3KL) &Z4*jfxʒ:#uF2Δa sf5$Y S^W`NlBch.O. u:ƈ_⯒3(1ASؓLB)Վ5)jb"ԋmFԓ0D D&#rjZ10 6ڭJ13+:#w7@=CI`1_8T;lGm0X@DHы Ջv(HȢd2l&!٩^ѢT TR}uL^(Α:#oY:To %8,pqpp$͢Kll*>:0ɊzN|4բ~Qit Z@\2ѧi IDAT=襇F@JCcڱG>eggyO/o\Q["mffĄrƶ )USg̿=s5CsJw3(jOiTJVd2ՅJͩ+]tRoIjGlvN?5MZfFIlU2=iҘshm%%'WQ3#==W_fL$ cZBqhiղݣotqu+Wq.Y FdʬR]UZM(ǧbll iNy%=e\ܱC]܏ZGU!+AD0}cgMwpOdWlmZ%))(Fr7ǵaOŜ "]کPBLL4vX#uFOg;r,< W {綰h^I^ 4 ==bEߘhWWWcRTȍ`*1c-T#5 9QXTYIV $apcs;OO{W9 (qIVaLL 5X&( &.%.In3|U9ןY1.s [A- v25Wr3Z\45oe&`Vv2Zy&dzzfNN.Jk׉:]xGbSrmt'Q2Nڌ4i^dEj-_ϚDX9`pE 97Hd ff`BB]~]៦&Y=Z7@\=WYl5KDBS2yٳgkժ5rgڴ°Y-n.3t71N|4tDcb)5o#&u5o~QDAdx5j@vZS#ɚQ ʜX .D޺dt+e2z8Cv|#v%ʻ-oIJ-)e6סΨ䪝۷ojv8cF5̙38Օxtp@F+ӷO_D.Qi߈2u1P'zĄ>s912ј^z3@,bb'Ds̙3/]ٯի߿YfH a渐P>,\6֩SÕQWiOKZjze˄Sg4(-%8PF;p)/?? | o~_ +ݠjNN:NNכ4i~}~AV]N۵ou۶Aoѣ䩓ccp8;x>05ӈѶmvpO6h@xҥKx'^&r8IJ6+3cm=٣Ƕm[5k^l℉(YZn.& Bk՚>>Fiz:;vl OtnU{Cr%Ѱغ}{`7lX|۶nC"!!!..ΧO&|)WWjժ͞=M_~e)׮^_~/|u,{9-e^{Ϝ9n^I4dS{ǎ_ɳwb"_[L`ׯ_O^zsXX;8q8`^C5jԀ㋉C<8pXعK6n$6mܹ3~f͚TTy`VF_'o ߷w߀6m9n8m޽ÇWaCw [.a|0h@ԫ5i2>83 2øks#ή6l8C2b~^^^ go$]~f2ޭk'` 2!v@ǟ|ܩӋӉ'mQl7vXyLH'Ǐ,i;0Сl5vrH)D6E袖C@D צͳPkʴh7@ˋE9 DP7D!0 ֋ Լyado0@_a:imׅ?䓲@_`~RR2t…B ъ,@Hv$>5CCɃǏHsg9y䞽{1#91X֩ˈa:/s}Yfgl3)$]3cDI'Juټ9ӥ۳Ё4{ 튄Z&yqj xfl~9ixU+iA0 //`umgΞ!oڵmz8]v'Mjڼ|%n੗8V>uCB-Zׯq'+jxsg0e9k4ئ6S -I_ xt&6lo2kLf$03yWk̐#>&4Kv B%Z_3PM:ew6lx Ϛ=:v9{ B1xqh~ b&j1Ra}pV%KM[IZnNF~UV9jkG }}}NTR'Yllǎ?ǟ|c޽{nzҥ.k-YMO? I@\1oX-b2-4B,YlogxbL1l;u={֛o.8gZ]@]CkG?o&`["Sи"&ܜN;c`;w4{ΜAZx1& y4>!1UGDFVL$^`$'%Kf?\=z@tРAݻw|٧-Ϝ9C"[4oCмY3&lO? .'Nl1ݨlX~Xn޼}nlh.g~1b XN)[3+ѣTh`)55 H3Va)GG'OW Hב&o\!ccnj,/ٳg~#۷m[Xطo3fݳdpx˗-'2Οcm[v4BO`ڵH/± k׮8p`= ǎm߱ʕ+8֭1mۆiv,e&b`z N: z#-MۉU~\MKwzN8W79Gc r2H5kVdT$bT~1X:~.v5TDz=2?uc1_13y$[ٲѠ@3*wZP9һBaۨvm3j-Ĺ4閝bC ƹ7eQֲ{aVD48DqάZGsВ&ΡdžX#H^()&rF52oT9HDG:c qKvH(:R*3OEIÜ]bd٬%}5vK=Y`J։R R65pP`Q'ΎПV!QZt3Z\dW [b⼉"rɹ JE]BdI;fh=kB~OdMW\P,t]N@•0vp+e]8]ٽfC Ӎ ;EI6ioETyH^% c LV3ޡd(\䗉Ae*6#1pmhx`&; HqF LEL,qœh}cB,RGŇ dff$&;88!1Yq)%TY'33=--[R0`Ł0\J8_;g3Geiú|R\RΨ8lpƄ8vv}3(CIѩzz˦TΌY|veS2]: _u%zKNJhogons]ӭx2vyΈ\7},LAeŃ};T&pX%.p@aGe3Z`tF s=|B1[)8MXQ4WgE S``+Qn1$ a; N- LKK#]?fdb>dΙUFyu;ؘaDݬ"+zb~^#b>%dJ/ IIF窱w 3ɘ7gТVVG9"wFõDPgT288kYl|(8Mi&m)8!J>glƑoGX*aX V܄)Pd(nb<;5nif : m?;+;KLy 8".p@26[-"!gs||Q0Ti2IXV ]4QX*_\{Kų0hO2Y{R>Ə+ySQTO &6TR*$:s欐ע?X&m4IQ\m[IF| IP[$]=zD[ەTܹ}ɓ'MVbmF7%/yt)xʙ0(<9F5@5 eOd,Z߰a}>zڵM0xXhQ^zcǖ/_s t%Fڻo#v킷R ^ ݺvYv-bKųʳ~ݺj/&4`fjȀT(;(\/BRroڴ -'7nذlٲ?߿.ݴq~?w8vgNYhq̙֭M8޽{#G;իd9&O DX&ڵk֮]D '֮[r￟0eʔ 6]8Wꋘfjhb=<r>xxkѣG7jѣwک_/cƎ@h}SRRF_رcö[ǿ~Mpqv%˙Ovs4I6iҴbŊF!#=v5jtC3j(+J} R  X?ܾalCy˗-O:}~f> O:_/0W3:dpzسZGǏ̧@eRծ]$gΜ[ ҟ}˪ IDATRLIq.@IӀr$زu֭[jשPDsg>sQF"G:NEXAv<"]jkΟA CҔJ6l~yBTRLI[_"\?V)V Z@!ۼ~R?ֽ3H3dG}嗄C sf:`mĈnߴR0իiSnj۠A7oΞ93g͙s+:/F푄+2$G,Rs龂ZT.Pg6} x t]w)W&7E|°aϟףGw/O#Fbihպ5eu|~ժU1g&[׮~CuS*2g^XS駟d?yQ̡ }w߸qy?RZAR ܎;:'{USSRKĹo\ިqԔ\p󩓧U].斈CVd ka~qM -Ek%utr}=SfPVNUV4tݻT(!ս=fL krSRRSRPSNկ@IPJRB?G4EZ F'f|-ɓǟ|R=D>}z2yƍ7̟w7obog=֭ˇ9rx۶l쐛 j.]~iƣD<$ӧ'LH(6 ĸ:II<͠* B٠>= 4OE,SΟ?W^ ~4nSfR_P ?U6 DN󇦳;u>WÆk٢qz4Cyy,=skV<6iS4idȑ~~~tڅ[ho .fÇxp"ܔKN)ReTQHJڴited\Q쫨7DULt&N)í۶0aUt*P= tNH) TdX(J`mժUu_ZZϿk}pA+/ ϲq`_Tl&LN2~ѕ\jyH{1mbO T/r yۡ} ~As!'CΝ'O>M(PWܳy2(SE"U(gϝ}̙+S= mLSuR+)s;c""/ed`T T-jDZm MS ӟSq?666ÇW2nTHh_!g:tG+VC<設_>Zȑh<]gؼy3[6l~< ϯ:Ob 2l={>|8ȣABя>v𛃑qQF,:٣G?޽{7HDsY. )` =)YY{4P611Y͛77oX a$U)B_ѸiS,Ñ22͛;rhN ['gOTLW8r 0悅G0c*7p?zJznPp7WGY1J!rFH؁ l;9;#pմǏǤr)==k ÌRT̋CR5P8 +UTX.Kʕ|}Հu5)k&ɸ%鉵)wtH|6i1!>1""\1<($`z{ybD]p*ggg ГZ/ `!XzzOE?7\v-!>˫z$>֤H֭u|Α%Eԏ̑.tuփXhD!! FFEEaH"%>7ju2Zj<0\r5jTzie‘f͛GFD0sUB\\][$f͓NQh, ,s %{Վ0W$@-`-Ӫ>?;W^Cŕ)~ƍ ܥ3}|qRhh(9܇|A³gυ[]pڵ*BӬY3///r*r֭f쮞gb kPpV39}[)孛77nhT$XF~fM$Μ;Siˠ4'dF BޔHxJ<F<粇$I|B<} l4Q8:CXv Ew} sv...ջn4?kF˻vwb }X۷58::0 Qn߹SNm/gjz4V@(4eE5@5`rs#, :j]C`qEy`KOMlZX HVpvBLJU*8>N"PEg.x8De$Ƒ>8cOi 8FD`,qq-7':W䔬8h9gTi"?\nB?T%Hb;q* ˜l8DyNs nzdA׀ԩSSw$k{.[.NNujݽk'0 hΜ9i? ֭[yrED`ϯi$ WCN++0b|\I¬Z iYHsOEѼ?u88GD{/mx>ı@Dro6le|j@ ါ0H~R;lKeQ,MĥKdJdĥj[t=#".K!!˖-ʕ*_=$ sSbsżxT:gX|;1*amb=2xYY٠=VXG.ξ+a"h'(ezˆ ^t3XGZ=zchT<-S  uk 4x|b,78#+,fN#g$HQ,iH @5`.MBO돨<=Цlo_?ud/w <00S>5r ė$Xn#qpt0`J~Ê+dÙRx{ڵ|WPjZ^s칱8j!+;ꥁcS\ruuPG-jD\N ?|JNnN9r8ߤr/OVH* &NZ82,($y{ T2?/o8wwnKqPjh&4[17JMa?y(RbN.)9#G0h~1C%b!ʨ^bħz"UX=kf86ϯ O3B(i a Zq41$dBjJ;pV%쨨Jd _Ūh0V@[J2BH7 e`Xg 0K5 Nȿ"V*L5 K:HɨV FΨ[Kj4id˖̈́dɓ=wr/`'Z\ǥK"Ƿۿo[?΢&c#Olܸ7^)J^z',YԻvzqb <LVZխ{ U(k֭_K27lгW6϶EmG!8<۶m gO|S P   XlAj}AYfߟ۷kxys\iUΠџgϞys6_~=5k rwn쑣G\k֬iƿ?wٲe7m_ϟ_hq=}ǂZ;o.. 0'9\ EIL0aڵdpu_~ŪU }…|tlyS+ǎի[B ( *X42gTٵs}{+ gџ~WWWΑÇWoڷ'۷owF܇wygݢ{zyazݻw5ax8#FOeo\>\%GE5! [ Wς-^Vr 73b2n`[ڵ }VjTTTSԺ'F?tYʖ-3wi޸qÕΝՉ_g=z[׸#۶o+PbжX!]t嗟1>tħϜ φ ~ br5%A?AS.ц|JQ ^Vr y'&&aرsǎ裏RR P P /{ܴ|-Z 7FhՊ?s?Mָit|}o߾ӽ{7~1d`7o&5k!FEd\\]۴yvugm ѴgϟkL:sLxx8<+\$ŋIFɦE4!qqQ-P)4hF3mXϝ Ai<*} ֓ѣD:cS Xm]g,,nݼi\׫ ʛ7o V!&O D࿖CS P P XK1nɜv4rKH (S"ki/JYhvJsz ^)aPȭ{Bk֬uW/_#e q-]fBN_mZtkAb|܏Qs4H N ?4#+?F/- M7W7麬W޻?99GU`UJ/PJrs F;עzT`l ˵>c&%ZT)gkg(:xi))pS;:ʜL^zfFF#OQ"nR-L!q)kbYWJj]tuu+Ac&O!ܗ `L%==ͯr4شbMt|V;:9!? !!UV%@0ʕ,֬piQF 4iz9*2#3rʵj:񢣣.G֨QW'Sh,lsۻ/@Nѫ=^dR P ii5q.+3kpCiOŋ H "o\oPޞ=wHلȅ 3 KS%ѷ7Œ#NrQx#čM5fĻA?OK9 EzzxoVv qcǎ 9[R(a^ jz"h@ zYDlРˡC7lpE oa¼ zmL^c, O׫S}Zaaw!w Uzy*EFEʿ $Y؈U*V,WA,ykU*ʔS޽ 4Wá>=bKAKLqKAr驩MjܩsnBg(>>ޓc>x% &'KSEE <*'Ӳ)⣁9>G!Ξ9i332SN}Yz5G(W{0s{ɩvX|X<ť /qOH€@n^Y8^<\&kPqNǀth.((hСw-Z{zϿ:, oNzz={4j FA Ə׶]^zn޲dƍ/ _5օ^եK޽W._*Ӎ6_<([.K,y~qKوAIx!˘&Lx啗,LIV!;ׯ»teQr `WtLtYMbƂ^6""#EDFU\y2tl*4*yVZmXg߷eÆ+DH,sus\ !>zk57m?|._|f#jj \Խ[qޅ /hٲHDJyZD3fk?͙;re6nܸb劯߄[n,~k]4񕝕}'6[ T=( ˞wXy˖' %Rը^䩓d%>UW;w͟ V *yc'N &K\?qѽL*DГOo PEbb(o!(Yyw5|}}0߱}(<o 8z/Fx(&ϒ4i|NǏT1W{՗B{r͛! ݻFNV>|͚5ݻwH%$C[+WԻٻ8)sfL$ŀ @3{ FgB p* a#s9~&̾vիUWޫUc-@q!~lغ8?C0g/[oÏ*Iӯð̈pSrlJJNPqЫ%+AŁ?---𓚻W( JuxYC}_hM_Q,JiF wOٳghvSg\yǎ2 Ӎ2Y Q]ֽe'~EaKb{Zok9$_zeF9j֮UJËpXrusCY>^D^|$`^8eLB2ƪzZ*&#D~gKk˙I!Q(V{z;++x;%!kbhU1s꬯o8d(yd,~3a]]/zd,D̂k,JAeO,-Gƾ. pEcd$,}   {18|_CMc4{iSqZ1cF+Hʊ Nl&{å\P^xuL;/rŽKJn&vIISN=yыdki3Cx+988c4ֶ$xw ểՈt_yrF: c52$sh,'%Vo.0Δ]T۠!!m{>{ׯo.&o+㏭Z*88{*v [ϰog͜?]\̦کm̙6mZ^{U(_^95mg 1JNJXUVU''g#AȊϜ9?699ۇsu"~j q0ݜRW_ieip2փlSՕF*oR*RYYYptƚx{ x}X.i |%~X<I',rׯ]⅋Ǝժ0.&L.|ηz띷qhmsQtҭ[.m˱QlFNxEԴ]ɒ;|p43T[ZXZAAbK]ur C)d薍!OƆz˰-\KyǗmJ3c.aw~(Ch\zAhF@R=%A9(E9[3R*;9:>m% B@ ܒ{wJ'ƌ99ECN62 `E(jiebgo* =<%/GMITC Ѓ7a=\-Z Dfb =8֊^Tv0ZɎU:NF)9KK+WF.[[;wMyh<r04C0ްrl6m fId6]:T`VNi;ޏ 9d#^ĿΧvY֝nLKjbـ?StV|_9;%U<;%Nx{(_',,c 'n~R`LP؊)ҩ_L;! vtJ#R މ%R2Ӝ~[r^:s`Q"BӤR9t5SA \$|uKUiͤ D cV$|9ؘܖ@Z B4Fe ` J,AK+ TgVMkv,s:/Lnr19nmj`wO2륗RqKEKt&92,PA!`ZȆ-R .cԒN8I|t[ʒʶ%Ј!G.(\\%ˇ<-N8 ]U n)t CK MKC#IA;% A>X,JdLIii)*Kh܌m^cN3'g0I*a>=4X)t\*p>LEgT.΃XHZG:"Ffϙ#T̤OpXKuK"N!;}--uGJR:IB?wH92)_$! s"{N:,!`4 YrꌦOH Ɩ^avᔼΎ$N2'}@z.ZH*4B@.BY[K<$NW+rTnJʉ 3%.nDk[keG$ D $jEBzN__#=gU#>ﻥTccCPpHKkKsS^DCMp ,+-vsS^R/dZ0G:NF9kYU98԰/hcmn10L]f/ǒti1udH~B Rϵr mvo?7WW |Uo}}} cqpzmmj'i,W"9qM11q퍚LlGDff3F11d1Wc/]_Gu =H 7ot}A__vXtx1._??21A#B[0$TI}9Ztcǎ=s`5SzMKZm[G"`V@ `VsXij_*(.ٳg̀+WΞ=HAzD^.UDnǚFFi-Yml%f5y=Ol2ߝG}nz7AqFsG8.966sU#tC!*22|fTCܼ~|Mϻvmټz/[ӧO|[o=u^yUeze޵k֭A_-🇿۲n|'~i'KOKt{_믿o}Μ9 ~ &7OͷBQ1Ϝ9c-23w|`J;w} /k޽|?>ٴ{~ݾc 11..O`?,vopAہ!_-[\wu=sI7m< 7߳p~3zX{ [Vwo'N2QH F5 >|K7Jm'2j+mZs:w,00t11M*[7ILLLOO0a28ق.//jv(9)?+66|AII^yFxc\[ݭ>7|P[S}~ȑΝ{QFE@eG8hG"uPkka]o>ЃyY W\geec_yՕ7r_ʫ{A]W]uUMM TvnPEEEk`٨笒L~Dž o|ݻ.Yquu9V=.;w,h>oll9(G'y_GuiimMKKG)sm+..n+5q6 kP!ϿK/!&o?O]9rt1\' ^%f-\?nw<:wl~Mɓ #GbE~gy曯Am-[u_~eNvY%1_^|9ݷ>8|={4s Ͽ`^VYΟvcڵ/{dAC2y=e%W{nn0.DapyeF ތ!/Xpݬs$M6Ύ0Eq`BNY(.Y\ihU8v_^X\/#tM&ʁ9BΔއ'فqfgk*NNNvvܢxOS^[,,c|lJ,wm$Z⡍"na̾q>zȌ3OӨQ7'sq,?Yp?mBNcF{n9}?L#f/4<|/ٵ_ͯ .ӁF(0r|YuTJbL.@JNI6/u4tJg{p8a?*z1&M BX΍Ͷ.\:n־,cnjy<\KƏjaP(eVVoEފh9sIvZoSNpAk_xoDUtіesE?7C?$W_ܴZCω,,,Կ*+tiSN4>/RXXXPXƍWT/9ӑt!@zs|x_MYYwsu!O,:)&&11Q(e q^^v18}[ZR79nDlY9W_eE1""aUe_S!B& fG__(;EӸDT?ɓ7&@>_Fc5(`|PJ B`pЃ6PKNBj`dfg!")Lr_TrĐ xu?;هI:{2 e,o=e>Io URڮkH^ DC|2 $9 ɨuKø_AL4a!@gkkkWwgwI>,YKF򂇑Jة/hfznHh9ƃhljv$"z;;;ml8D95SkA:ǠSe!0ꝏ'V.8kCK 9EJdRbhrH? L!H B Ls&e$0!@,"%B09Hϙ\Ǝ@KKHC6I ަV ̫d3TT!@F19ǟ{+BMf`s^W*eo0j(.'Nvy4NF5_t @?@UvI&#ʼnWGOOF_ٿ s^:<@VL ? 'p3 "z]mmyy)qK%~b0HB`("OFĿ~ɇ25|aơFv775^Ӫ!:&.5%VCqp=*?P{h2j0CdϙA!Prޅ]倀}ni!h҄GwwwCCGSc#vO/...8Gd)mJ-ze1TCL n{dI==&zzIω޲=9ѣF `Rtk '>Ǯ]0xGFAk0ڈ r7hSZZj̃.MII#q!s*Dc)//7''wȄO 8Ȟ?}@Ϋq豱-..f닏x(XEoЯ0x-{>-f"斖d 0b{.+::{{??dV'Vy득U]UYY~>`pp`QaQQ1uU' ZYCj(l{km9%qx`06.xr)8bP2X9IF>&66^آklWK j2 N`y.a T${CC=3f]q;:;QJe|}]]7~ѣ#F}=.|rű9VO%x1TYYތ=̙#&{Mo("/n%]FX彅%vp)3f|ط*c=GA 5Бs|8K2\kK;eNe2,XlȾ5P]̀SW{_z饬,+s _TVU<3xLMK۶m19}(9' C)--9;w IKKŪ&,C/b[!Ϝ'%aynȳ[|(u›Y@iUKy%sҒsX2?_u@xTD2;+H>&̕cvF&pS;VDK_;Wh֭qww7}2^XsL}۷`O!!>9q{ի/lm>3Fo߾O?ݶxɒ~QIo/_~Gݷ]> +z?0 &O|)A8>|XִSL)g.F ቶ{g] +pq`4'NtuuAD0!z]6E}YpP3&Oem,VGG?%ңn@OYpoΥ3u۷P_ǻgΝ;S/ra'y'brwYD;w3fe{N8ɧVIoW\9R81Xr9{e˗́*q ! 6Fř3|D|-*A0J]III }:/1qԹsgo+\|aĭ[w*o$%l# m{.+**ܴq_dƌ[n`P _[?VbG@|(<+,` k &}_lD8Bv 7]Np&P9}Z|A^$M]sd8hsWZ Uj XAA>hK3^܆}=}) gUxbZx 6ǍCm!+++2~ -"$n90֯8Ӳƾܮ]o&r&NΞݻ_x94d2y1z낛+87l<TwkO,^ W syW^Zrb:Q/l|{!zew/~-d&֩JDEEEx‰x/D[#o"E@d {cp,//C>UƭCBT9" Y &$3fUV^T 3p޶qVw<֥Mnc"&e'2B`yzx>|Py<@{i$&Bx`#$χ0X-pƪ5O7Jݖ_'+Aokq&D" Z_!$h"3f%o[GGd$@nS9cNE? RRҐ?VIaYӓ;x .H]+(͠ig=Hϙp1VɆ-l W8;Ӽ}P[wOH\O dz˘$;FH(f2>L36;]KN 8 !(ePo[$?s... pntuqpoimSc zR9Y\ AB%" [ ф~S+vi" |1 Ez8ssfhҩt 78='^3R{M$=g*Or* uI!!ՐnK2W+iyEnRlʩq:OF0TL YB%qPպ# <L<. IDATPGĞ B =gC!@ s!@5猺{H8B "@znRqB F@9[Ñ="kdnRs)$!! !9n?E&%  vwwڊmKx(* dą0$s&^q- #Gut755 loo;"9mۘD!@(#0B:;:#kԈ4B !'.e iQ.2e oD4~2kcNZz&ZM](%n[1t$*Lu]wmذB$fE6BГӤ=4=EEzTms,𗜜,G*Y*{ #GԞrC^}e}&2HTQ$0!@B@OzNT݃F{yڳg@sR˯XGF < \?))oΝwy->F_fʔ_v?:˯K,7o믿^\\" ×/[ǁ?:<^ &74̛m]m-gjjjbB -K*Bi۶mE{Ycت/ع㑇·{q/jժUQQ[6s|p}GsW54ܾ;"/uAg'|288x۶m55/9k&#A ];v}ݠxeGҥ#ܷ&N'`5i$ݿCTs3fe J>F|g\T`܄omfs3I-,-k7SKL  ` 1VBXEKF|TGMLL<Ϝ9;*1QLW_{;O:mͷ;?Ksf%5Z[z{GOQ]JD"`eiɜDL9Ő u^1X]󩮮9~ѣG_ze xb,55u QXc@L6M^ >eK)++;طӡ2[:bڧF˗/TVg,,,̖܉X?3Tp^0%{(9-9yG{b޼J=7gKK7|5WW\qY$RXXlRGR19x]8q=gRj*xc>Q"&ŋxwǎ;1 څ[H"` Xº-*++JKErsAQQW]_rR쫉&67@dhH2HXPX8cƬ&IܱTBCN:PvoMREvrv9~XHHBGchhSJ@`ms*%p刎V' WW׬Li+$Uy2-{ ӗVy9(%hx_ɠ 6)Zt&<-1tiN.p)Iv"ΟnimMNI  pGTGMzS*]rL&,)ԛEvT DpqcX=_g'zx$' #3syď6$`jqf$UB@ dS؟rm9teZZɓ';pG<!-esҼNtih#&$Az=ǒG_Ug[w-,=R,hlI% ׿\%w"+ !.AX*x jwqhoRڍP}0{s&\B@9tIt 3x巔l'_gI !OŒ @o/ u =Ŧ@ZSdȭ[r섀Xx?Z  IP̭b$(! [ʼnE_3!@"0۟AK$q! 39 M:s$ ! }떒0#&AA*# †j!x$sw\8@YaJ K Bs8ͧڮiiӚQ!@ q p!hU3e#WnNVO 6%5  0,9h֖ ls}x ۔iuu]A~pKœV2{rs`iǯqƄJ&,ڵ%a(ReL-x#|#4$Q!`=ŊʒB@g>okk啜]AAAsK PPPȣ,,Ο?'!: UUFs$ !?ۂC :;4$tv"0(յKgB@[ #V?VT,ޠ֮455500*[2gl&M?b%7(0 Tyqey&M !,֋r^MMqL=<ПOO;T{**+2e#J0%.14u`uY+D 9Zl|(X<~dzj 4D=8h %8y46xB+D@ϵs>l]_gW'Jd-M6Xr9?'Ϯwy8;@oog1"/ '7-,9??_P_-^$I#cN='/51?/̤G"5j4P]][p19AnfS􅔙 >j#9 ƍ Np9Xa=z3-rݵ" 3wؿ*|FV/YxѢE*0&OYŗ_¯С /|٧53fL뮻Tꊫx>;oiYw}~аu뭷ƺ <}v8,] E ! ˏGꫯLO!9ԗQxÚ?|׶6wy'~i{H`r\6}.im`NϡoPF5w m>)}O >0c#tmq7nGFD8t_|sÆ~4n矯WϘ>Ta{ntK{vz'o۶m5+xϞ=;w|56*epqq:u4?y򔫋sdT_d_~ժU:eV\-¨l@@^zʹ>ý{۶muX_e  u׊v{gq[PhN8KG'$.$=y$B!9QB2u/7 7iFF؎K/m8ؠzӽߨ$!!rCOlÏ`cA'OB=kzX"}W_ϟ?oĈ<|qʫ[Ā=x3W\W[cǎ^͙uO=Ѝ"QYzG|$`#G|WٽH`׮]e,Exo0hzN;1  9 Qx0#]~}ǚ2~[n^8jha/_ ƿ>da>VL:1T ݆,Ó&OƷ))U2qqq2eʮݻȧN<ǎ9}Zo mnUPd/Qr <Ç{*^PºaFP9>~DCC#< 6o|5W3v}tq,y?T_[u_駟?3a>"N ŧOeaQ۱;z?FW\uV do2cm[ٳg?#gd|n6}FHd痙d]]H}U!&{08S- sd-o/OlOcYƏ%O&{ /-]=֭{oh?裇z ˡ(6MdܹhkŋoOIIfs;n8^v3kڶu[aQemhƆy]}#0?uB<&=X_|%<\|9sjy_}uŋcEwUN3ʊҒbS]qW]sJaʗb_M4J@!`*pA/.Ǐ{>>Z6QRO&#raaጙFn Dn?V"ٴ"du!2x k2%j*77H C!08zxgz.W.P P=!Џ,>99Y4BuiR?Z4>$a02ɍq#ZKe`1qS]x"AffvccfltO,'aSC*Bi!^1%KUL!`Hϵp;j =<=ml@5LWK$AͨQp#\ts 0qj!@:lYܹ-,GΠW w)?Ff qYi2 _ZiYّ?DjpdX5^C/KKJzᢢ"eezuDgCEQ!`{ʣgh\iokbHeEEBܦ=úp;9:zSPsJKFRU,sAAȔcpiB !9lHٍ$? (A2V.T\&$""/^iDds]G:TE\zVJzzUwtt! PNHQYB 1Bq8iUӲ%yԤ.l'+7_Pu55< ؽ-A p| WeđMMGjө"B 05g]VVhz4%%WS]“L=~8dzx*uT(9^>} td}}oENUSNwvv¢W\4ZCRB C9D'yy4;ǧ8+; IIXrWMnn`(wOn^. PoNn.6`effի38h#B7CXQQYRTOuBhnnU*`1cFC!'o .Pт!! %"" Q'K!{pPpCyyyyXoQ^A)Ɉ9bD{y 7-o!8G"L!? 3{z988b\rJUggkWVRzuۄ EP9TR?*q N+| Ev1}~UG?z uuc6 3IiI(@ &G[k'#ÇGDFa ޗ;Ԥ܂PJKOllj 0  |#G!`Gظ䤳xul| Er 79aa_mm[˫*+ll`gGm 6cڇh\ԫĜ3ީ?@R\TҬFwJ3WP]S[_p"H,_Q CH"v===k;CU镞&uV3!{̙m`\+ ';[Tbc. [}+9Sa$?!06 jǎz[ʨV)ȣd KUVXx `9::9 IDATӆű#f37dJNLDa54kE UCϩuexXY1k!jp$y!08='#"lMu qEA8<马,,,lii Tvo?bD~A^ý+ |$5Ϟ}z6oeJI'd]<88($$&11mmrq/+Cr]* ͫS!FՑ#GT('b#*$4D OW..4xxNU~UU^^ Gvs+/],h<Һ .9hz{ 7fl1 ƚBݟG'!@耀ƌ:Gd-GzoѽY/_EtLȥeG +QQ|[̅edY{ry#BxumUu^\\X "#P{|ȋ.!!辨èA`.E>P%#|PH)LUǎN :$VG8V_(g\;5FD^,W䷌ rkjqp wb£Râe=UtfeetUU )XqbyD#&ڊVk{._)4P$D >>0w{3* ׇa,IA,rM<(|3cۛuH ח*G!gRO[ZZSwCɵm낏OI)N`j!W#&G$S5:HLк=!`2p$(=G (5b O3g7 Xw7Trs322*+`֟>}VRT'/77҅*^FP~P$P6pHIOo@-majm ~^qQ 6*́iiZRl\pJ)< slp9x1ޡ݊iIB@"L닥GXliX 'c*kw0 x1;w bN3J>|ZeJ!; ;} {np}) b&|Jj*~cRԵ (y_~~,pp JyDbRluŌ uڵ(-)Y GW_uU~A>ɗb_M4 PP"E9DI~O]\ݎ;& qXSJ@`i+aUUԓ^a2b5N3V،9tp0LYdNZ-DoB&ѠXgWgl[V74 ڒ%xd jmi}Y(%h#DpzN%mqׅ[DX,Ν= bLH$B@*{!S8 %t>3>ʾ )$L.#>G@z YFlrH[b &6|zvs6mO! z|` 1dfefeg >vHu45V9{]576zxzڃ?TBnjkf_ B@ vv8!>ΝK.ĝ Rb !`Ba˭it14fߔ)Yt㏃0+'Ns&<4s |̙2yn88rǚ,|y [pb/4>aDA~!d暫ՑaHCn'aa"CMl(@Q~*USRRzfLĹY_0I{y51oCQX^yJ&8 +[dZ!@H!-!1RV"9?Khcm Ԥ'Nb dR(*,.7B SA@@|#r sr~ookގC=*++B T o]@Fď`*t!p6Gcc#T5mp^M\\UFl8=g'g+54\xl8bYY^/K>SrmuCJY"P__ĝ!GT466@>5 &gu| +!dM{mee oL#sx"e<HO?sTp E'!O x&YX0q77(/+AYň,1i\Օ#G^jiht e#X< OM>QڷEyr&LK"7UYE܁,8D!YYuAxjM%.\t:#O9ϡ“2Ag!31 ++"`,1e5,۱q PEKv2\5gNv<ӵ߅0 Pr'سg+Wo78s\pBBw =Þkl2ZK1ԈEA3`N cS|RΟ?/ɚhoB:Ƶ>Qjjl >@{ifPU&B$2@%;wu;G}*UY:] ,HexQs@LS}XY>5#aDYa 2ïRxUSYgs榮>y.y"S3VU $GL+;Td)CaX˕iv%Gcv@^큷W]{Y7G[ 洡Gӕ~?cP,9fBr%J/X{X}1v-u;va]ERݷ/b^cfn}w=kdZs禧Y3/eb{"b.@a C3'\{=nw25MZ znbj4\ YggGWQ3ML0fgfξ{=̚orX' ;V]>\¥h7<>=2j/h8G? Zw:5+JmLAuol!t07h@ ]p#枚og[+spGM-ehgxխBH B  pa؝edʄ j8 3'BN18j,.z:u)b[سdv.  !@+$f2nL5W$E(<Hn i<کxGCKnSlޤ!B2cω#g4ZU]s-(RXu.\7I(F$]ӛL",&ƈx$RF Qp+@:>8Q&By Ԥ&0B@&Cy +=6?П}}oǎ5; B ȄJe B"S!%wA>gwȑo`(7 %]q27ee/\H}$bB  9=CHKJHo>J2} )Β#$o]6n8 (!@@ȜCp<Qpp +noo14"BS2̐#&8 eLՁs _.O& B 2@q)pSs!mА=):E_RZўpTYs<!qbp`_2ǐ ?gnشzI'G!GЌͣXCLfX҈*KSPuaCx=B ҊX"ѨpKk+۰r@Ţ9m8eBHˍā'"]: EIWCzNpHBB w ϲda5GTH3yq7i'YDU\ȶ}-@/"ǵ=ǀzMd陙;1B:ߐ d;m!#uP_Ngr2\\ȴp]ZWU~Mpe2y4\zB4!@L0Zm3j X,,Y.38Oݻ<JollRXSfrj Նj+;;;'''׏^bb(Û޹s'H::;qRm'9yjr?klj1Ԕ鬯oe~LxDӡr ,(GjjY"=籘PPi^qVW,}Kkkv| `Ge B 1Ќ544Ƥ8zT,شssSS;!mvs9,vocVՕCz{zl+?LMvݹsfz,1U!2JMN^j3K)RJ9χH_{GesA1_Ze}=L)O[ /u$ջr MX+'&& `)ʅE"^12&ml\\\`hFceE%>ŅElK1ߘU%qn܄{`ê![&F& 󭭭,mimYlR x>d>h07re2Cz ˒uÁp\nB1Ic!qZw`Gtmjj"YHd36fjhh H(< lfcTfc#; B/{WXpd^Ι3Ϝ9[oa16&ol oLAܤ5wX Qdhr/A6͗hv$ϊGo2YVSRD,++P!lFфV dA)ۊ I|CԆԧQ*/6y3`)ժ$RʼdF!5%]_w*޵xqNYA zlimc,>vMMibɉ1ϹQ3e4_-n<3'L& {>ZU*@3d o%ؓޏQODݩONg,ˊeѮa-il~ tzl 0-y1c3I޸Q,~7FFF[Z*CuQ]mqom ]Țe2dH9֝XXY[W=q+з,/Hln[l ʘϱG=11֊Ǒ c 3ep.^񹹹ݻPceXUi3DK[rt# oNE)4]Vb]rB`ICT_]]2&MM{X`H657i0_%M55Mf 2Ë@QE*l CAݢ~߆ǃ~~LaЀ(|=7kvYyi`c>Q0k-##u=j E߈xhK"Fcވ?|aa[|ݱeF{MЩxDƜaؖPV]4Uջs62[4Y4h1 6A ~CR#;&0Mx0BK\n"\t F\PHo8TsRU>_q TQUQ4{1/,cܙWՕ듈XNm2Tڭ{87Abr&b]KUA߶m~_7A^bO3Yb1q*?UcM0 y@* Z4<Kcc_Xi .Ë$yK=uǞ$ ##KK֍ jOOozJKKA6^< s5JzbeW髱<갽?'ehn2)`Xv{ɆwKLttV'md_utc&+",;]jueI0 fA?L , f-ThLMjkio 9T#{NEFl巶rX5:u@l4}}}JeI11}}C ;~/WPmy 4wCcjDQvG \`5Ƥ։%":s766/^olj91nSkkecW{4y/O<;B滰<<0 6' v*tŽ6B*K!c&Δ(Xgg9vMMM8"1v;`3B r<6TurlsՀ-iCu!ͭɉIz8gʕ9JpR83lB+b##z."8! :sl}}9Y[[3|dɀ0 L"a̞z/vRrpg̃B/؍lx{GG JRVz|rG Nd'=29ų^C!O % aR?4Ec@^*;FGGu:uخ.fTZKJʘ5=[ %E@p_YtHjXOba"c4!t`z`9b[3TKJeml",ՈcP7w-0{gyH~$*")*(,zT! Fx ,J nl1rl? ,` UEpF.us!a-B& `zLBKűnC#k{4ռE PHY1oNj/`F!nyYnr(i]CȞ۟!0DvA@xa`,Ol~=y!Ƽmϼ(=~yuәpT CCH(+dh H %}K0 ̞#ḁ)FpE (% :ĉŵDGdӲ0L:LO jO8!B1(p#N5e+d0fhP3"aRυ-)ٕcNe nݒ1CQ0=c~&6,[$&p[0# 0 phݒ^!i%Ir!F !߁ aQH=GcC!@rmDB !琿qHYy\P]@"\Q##1Wg RF~ĥT*Xezmm5-J @rjks̓17q!"J*)];Ά u!РFŴA=G˜IAmf#?=!j 猆/_~;;=V P,/=}F #?:؊0sx-+Sjug܈wB  BYR\z9 ; PHR=Vy\OCC ca/Nx[ *UB!@dsZ!B粃;J!Rٟ gJ>`>zŢ"H*xb@>$- #\l{+5 N%.rmn ~Haޢ0KQZx|>ϛb,+I^uȴ쿱4X޾zQ->R/m,MY͒0r)I(R~ jhhRk0E"(< ''M(cqOqF+H{bZYOLu-کC-`"-i.CXua[,3iU=8} Fb;p]l Ě':}9)`KF\R Xkk:,$@#aW^oEɞ=uGKKyl]}h3(/+<O'֤>z`F l6w@kT .]/ c Hي0700kvVT lll*M J?"6T*^tKqcB"۷LR8:,gk.Pܐ0ZdHhjv۪p8nܸr[.}t 5շ.?TW<\f|O _r۷o 7aњTڹCNƘ00FBz.EܖگO+?Y|UgY\ֻZ^^o~g~>{|[8XUUKgznŹ yؘDQ^\_7'DN%hO#F]ݎ|'…-lq:FLƘ⡤(n!{.ݾvԩݻw5N~#^m\uUߏ8dgo,:|XqK=٧z~ò8_o|s.:Lx(G@xU|x/}s~яt= Cdb2򅆄$f,ezs(bwabno-^mf*oqoc ĝwtT\a &6G=V1+eS_W=$KsSmDY {w]w77G548K/*gO~_5pS~}|W0xG}ɓ?!=t󎀄$^ӍzfDEE7Wjpf=~qZBZQ^~[Q^deWG~؂IzzRܴ-//}oJvL_ϊ榖o 8{zUs߾ҋw;VVVVWW{W}c-2z>;_W:I%#8I0JB0†ܬF)..괰BET>xMLLsZ1b^jzf̷hVߤ߿oߥw>U}URtpS%bm;*.^_Gn̶}sw-{G &fmvll?AɄ22xc{s'|2n2V6(e)}Ff HɞX9Bv6l-Yzy]Nײuy豻I? KfѲ'YV_}۶lh_wEnSO_>+Z,{衵?AV+@»bݲ==pL[gS81_Xx/_Ѯ``ph5^\(]ǙO¸mRa.^}"~aj %11??{UWS׏O_>_U:U~u;jwpպ6896 x'|ⳃۛmnO?j}?:.@W}Ϗ~;0vq[yyEOO׿5Vs>gܹ'4p}駟~7J{G|)N!*&ܦ}wq{ #[(kXuYB1rA.qYRO؂̍ /tθM-t>5~vw̬?D]w^Yt|PZl~Cw^ݱGe(_<7_'Ο:2?N>{<ʫO[yt%1SmeFlx8GbSS?nXh(_Qæh~{,Ed-@+<{VWWť٥+WF?e"CKZ*y%)[zt`{iRU]З|N"$!qVH#aH\.q/CCC+T^3/J |﹡;|%JHpDVCMo穒*gF|ƘoJ%#59WGǫ*XT\^DNJ&L4\])x86;xF^ /T*L&qϻ~HZR={N+љ%R"+ܛVvHH c_6?kH67k.Lq# g>]rE7kk ȺfmO=g;3ntPr8*DZJ)˩N˜SÑVf(d(8JϜ>PWϯ|GF`i:":YlrI{ls/GΪ%,~ *4ܖ aS!PmQ$f;`#.ɺ%-P,űtNRF94@vp90],^p)m?*R7v+;keȾ dfI3s[٢0={T2yZ]^gBPerHTKחK;6V+JeeZ\^c.P a&scHz.y%&vx N"5ꕵZ߷}Ib˕ZM> c`mQ(.JbTT,7>|/F$)CWSFߏtH!a۾ S4 4! )L˜hTHz.$d B Ujh B 0B@j+RsO5nI\ )u%7HMR+=&.E 䇒Cl1y0빊-sup|F 5=Z|Ɖx'Ҏ@P|nvm䘺/m5O@Fڟ6BruXOϴ?hT+s4MP*R1Ш!VnIPh!WHNMR+%T!PX(H kB @*Nvg7RN˜& ha&wR YpP`:Lyi5uK"S%Nq1>!@EQoRdVbl[ ISa<5IENDB`kvpm-0.8.6/docbook/vg1.png0000644000175000017500000010540111733530415015554 0ustar benscottbenscottPNG  IHDR3PbKGD pHYs  ~IDATx]u|I~wI݋kKpp.Fi u|4緔d2̓dG;oCb ffI1GNй9]L W|rOx5 ǴS?LliMJNGm"2"7sjnԼpϰsNp( T'8JiidWcA y~ZQFۜcnGRoSQwbq"*b&'f7`[C5}OОv1Yr.3s33F.wR7vuB#t;xBR0@=T3Ӳ̄|əA'~1Ԟ0n桜YrU{rc6Ǽ~O1#Vg~&%IwELw wB;â!XзU ffT2g줜%UF2jWu˽Nsh(c3Jy| gbہAl9[) ?126'$@"ef t~˗Z@,k3IMͬ ]GZkH]S[]D*ߨjWoIpJ$qq?U**vfjD>8)1$3~23d3"m  VƇM\ oP㤑^޾!_?cj5i׌ffzBX"B(H a'sOKUҗ *5 ?3, WzC}1*%3gi^vڍLQD$\z8uFE|>탬^hԉw;N],wb=wbϣO%& ?gu/ ?]0ŀ x~t߇r=l.ykLH-n箾w'lkGV[?a{)sYTYu-7bs}Uio9>ꪣ*@>qzsO{xc`+_%F^ 忺)[f+< /<˛"[8CN2k')$ 2gۻZ,;l`C 66ۭ0y%@e lnqKaC\BnkYsֵM͹{>Dّ Y虫X99:fb iڿ5c]>2"W3Wm{cSG??&5/|#ϗ>{tf08+:&Yu{~<=cmVݸq\>665Vxvε2pm)BDʓl۹B65coj cYv.lH$:5m__l$:V%y2yi0JqG'yo=Zs{ ̓O3H{up#&)3'!>ZKC"esk!ۖ]:Ei"G|۲b <:'G$o@ؘl&1݂xw?ѯ鏫!>'Y\ ˇmؼM)f5zMoRGfV.Rv̜%90Z~nB {Wy8'lhB>#ϐ2g`q8Ϝ2I5Dlײ@ ײn9"ŬF)nȤnee:+CR"7)_޴zm] Ǣkfݦc֜je+`)c}8c̚SͽlK86YH~{hQi`'otR6ɷ B[BN%ٹ`K Ŋ(q<}#:Es:ؠz1JM 3ɯ쟾JbמᏓ:)Vn1̼[w|.~ͱSZѫ<>M|ymf?kH@dV?aek "KV=PF$s3#Y5:?k}dd~c*|@չSzfS@Seکk5 Z^s3Wl)QOT7|z0$]Mj_=f邿lo?W! Ye? J1s6äٝy@0uv-_7Uq{ZtbB) jcݞ3zVfH`a!_h*UHHH]I,(*d&!7㑾6=>O-VdS$ TDm.?[bE,'L[2,),]QZi9|M\X2pDj]`lJn6jg77B0"6gzt]t띦Ot{P!b*XE5"W"1SEO43K ]UX MAffiXW4JıjGt͕dulewz,+L ƟBL'hV )Mx{|k˦{LQĻvT7A Wwwv닂{@RIN¿vNj_w֎߿yiދHV[_Z ֏߿ N|I iؙ4hߠgäOG[N1Fս5waܿ t0o_kk[[ReLd4t<9e˖GYYYعfiVD[)UZW"$i궤.޹srv/g%P;'-3W䜜«SGޝ$Bwp;tfe&}q {n oϟvFں=Sq88IԴcG#isgϝ8qb۰aj.ՍUM0^|9O-.ϦV\8:u,xz|Ec۾vkg$snmܙZ¸) SS:Ћx=e;'0y!;rIgkA 6h0~Œ 4hy|VVAwk\L]٭{pŊ"""Nݻ%K$9  ʤR=ğٷcԁQMd2ab_aɇO>ɸl:wW##ޫ5#Gl0sC>9eIe*1,]>^pnEHyti%/PEȒR̼n/kVO߫36UKU ǐth$=)~d i@BVd[.>N&wGJzw_VyNϥ%άT"5wӭ[ׄxs$'j'Lw$ $pcux, 68.xq* [3wEǹ~1r7@9cVwo夠i jC3 $5ǭ|єE`vԊ5zvg%l55noMMUq szU]NfP{4`/+ϓ[R|ԥkb/mvv{ڵY,VZJ20 ^zST vmim#+>{[Zu]9CWAɊ6e8ax왊+uzhIDLlljZZ&M (=,MJRZk{ܜݻ{Y׽o3f`XYY%'&]|fZYL65+.|JCiаNOWvqXY"#=<~NNnn~>7hOPU1qKK (R'盛q\q^42py<WN=aҩ^!I[L@}p#iFkg};a424h̤Aa(s4hРbJ26qIv9F1(A;<*?24!>&.*k;8:qȥRi"msfj}|bqB\7>_JkRik?٧ @ P$"!A H(Ff} 2.TiK4'ed,ъP :D &>uT\\iJJ*6u=7|-*m"M7$6 h(a9l\6jaM7K6K hبI! OR*Ay<@,IkjL ỵ w lаIA=O!4(axQ7r/*+ИY IR&ӧoذAdR'H׍={&^ݣJ؈j(a O}&֙փm~j6#nVi]ۓگrgv_9!AD0{|>v? ٣=|er6F&HYLk/ *A3VlYIv 1{$-?ToȜY2|‰78 y s0F迫Ճm{5AxԾzrjCij$ yƟ G1T)636~KԵ6D#O4gj=~<3h>= ?ȇH6o^;ш󸆚Tyyt5[p:;~~f Tjm. {1W~`oJ۷]ͥ۷5k٬`0`[I y.mb\ r*p*CCspbh"6Ƀݏ>)>dXVmOOMZPjkk 3r??B㦦_lJ$LGL-^xVmffd<~lmixd:q<3BP[_ l )U-MMXlΓ'OJ7PyF0(`VIIIW\Y]T L͵}}xZZY2Fu?~8v^Kk*({OͭTk 7] 4{WvQ2ؙ㸥uppPk |3Z6c|g61 4zWD%hknXX%hi(=i%h4hfҠA3 43i((鼬:% e٢mb{{ĉB.imb4JjZV{JBa4wp ikЃ"礧;:;IDbH6X$ENԧ=uC`\H%m2h2L RRS|D"ѯ}xSSӔT={XtE[/Vէu``@`gl]fv<:7}jZ[DR&1-٭i``@sg(;)b7kfܙM^uW$IR.&3gܸq;wHdTıu6qƖ<_marBb˷8uRSO2A+3Ԅk/у[<~CBY[eڲ&Tĵ[7O-cru3eYߢ×oݻ/W8ӽؙICK~ަM[УI=z$›@O3u``@>SMG~Tqw6NnغϬE"dS^>3=?wcR+6"z ?I@TIHk7t4p_bk+(.23a˧wdfp]j`r> ɃqDku~WkP6flzL!į;c^|6׺RC"cƨgo~3]yaKy_+:=Q3@'qwfg&{e-ѻܾsm*?n}uҰ:|uQxnݺ] !O||CgCT; I.V|9mMxY:Idn92ÃDn*Oʓ_:ċk{]XVoE(-=mڴ1338^x,k5+Aݣ K8+zմf=őobݗi?z๱‰lG{Ҟ{ie;S+6iq1?S}$=h\*VZ$yYjzP{9.!j =`K.et_6# (\|>0{]j gjC,LϽ뮸fz7nرsڴ%Ϟmڴð,pW Íh50B'3&N5yƁM{("ͺy|9 $&Ύ[u⁖ϷNӮiИ5w4IɜQ`8,5 <Թ.5ꃇܿunXKk T.t6!%9"RokZ2dĤ/kWQ]p\[ Z{I]2!Ե[ *DFFN1K-'Irq z}ZmbS^zDRppRDJp#w>xq_X F=ŜyTt n^ߺqtõ?Ȩ"lF7%&ܮϗ C SG~ZXbޓL+G.xT1X[[`AJХڜJ×Mشdqys۶IX7 Wuvs{+PԹ֣{OMŀ vf(K[}ToB#thhh59,VZj20 ZnRr*A[&0h>ZJEM\&䵶`0N.gOPרEL$cccSҚ4mK4L&%9Q^YZ[ZX8;;3 Nkm"Rq+K䤤+/׬Y.CXT L-}&ǽZYZ?4&M~zp~Z;/߬m lC3|{tt{ө{qs3s'GGWWW~y&.@[ڿ,1q .ko Qjy|33SZ6b&%NN43J-3UAF`s'+tAƩZ (' 1@FiL4hfҠA0Ӵ61 :7T*fkgϣ>q"J&oJEzy|7o61 zPJB''H( iEb1=]h\:N|Xş %L RSST"֯}83ST=_7)46JJ7mڤi&M=rbvM6i"hȬ-4™=Bvt熓@`Rk )K|j셇kSQr~֛I[֦M y<qOIBs+m]unإQz $Uܽ6}6s>,_$?_&O ZF]Z80MɚEL3g{1oݻ{7㺳:@UUcO߈q:Zp+u3ׯezp/ n =n=K0XVزrێο^ȁ1p|*T}9SZʸ!vEӺ8ǻ}CM?*Ol8OP+ϢE*B`ߒ_i6?^936uKSۦ! y{W*XIN}5@Q`0.ngNAZ&&o_nܤXb`… SjL,X,ΓϬ8#G csQ6-[6fəo@gǓ:{+y7s- =%Ӟ:՝LLK6#LHO=2c8ϮF OQHnRDk箧OZZZnܴb鉟L|q7b _.y[wS)Ѥ?k]X{]9/Momr(AHdz9yc}ؾ^ydar; /SKPsхo  ?DX̎'~r1ƾ3_5U /ȧP\ѩ T4#"N:.L'.^tYbJ6&Y%SMjYi ѾE-Zno$17r9($ q׎[e~/wԥu ū:-$ԙpS+җ7n߼q,C @f(KgϙǕV$9z Qa׼% S;029S992Ead3j7od\77nݠ{3u6J7k>6"dMmrܺqn4&1X 5:&BJ%hs33{GW+AS!"U67q9vRj%h>ofjp釧i( fj\MF)b&=4Șe):;Q{:PiޭBoWm=zcBj:<~B.yR`Xxkw؀fn&ΘY0x;w]Xxfy#>uXYuKwމr; uXHz{Ko`j}Sio_JGa3'taǣC[v'mg khd益8e()|,6pf88=vL{"cIqKVë<2tesmktqzǯK 5F;˱m7>Ċ9V>]g._ݛ}~<(훻!g.]J;`55zF|=a_v/؛jsLz9R'òO;߳cÙmQ1n$BkL{Z쟿V.oKWn|'iLʩs%}:yy.1Ftm׺mK#Kyi"I^L?ZQdX8=۷ǼU+wq=j8^mcV E.{qͼZ R=ڒ2lĵ.cUp)ol%::nDMMcUp b?כqvܰoo爇QT̷ȹi$NNyV NͿʇIf锶Zm׺mK֮uvm [ήYQ4ǴwFݾTiXP඾+u~Dٔ%nvDq2:zIaeu35X֞Vr+g׾ݳFT2v&vL`ZyfdZy? ߟܰ쓰efBR@$U VnyZym{qpy=ηd˓%$oU>2t5\ވ*5/YÅA2̢<Dzlگ׹7z2LG3#G ̩7/-"YhAuX;^zd*Д *fV%eA_ONٰqi5i|gu0e P|ٽH0eX϶s[JQ*S>|gw mZyqd!ϓn=hʋ@j9 EH32#T>}u&nߨ5[BRqc_psl0|>IդJ~~ٲr+hiR ;lL`{_4m{}!_ac*qI8,}!R\̷k;1]`5^ֈOWeCv34;𥇡̣^a5^ݱO gˡuJ'3 $b.Z_+&u(~~~tܩ~xFUI%>:謾5_[ tBwYCk2S?Zd!gڞLPF3a.r}xkFo+jb5=\Ĩ 4A'3_@gxYLMO+'CbfZxڰu= OX 2& VcvfR9h̴vL?MKy3R5FIY›S˗-;뷦+U?zX݋֡יP/f1g`FތUpf:y _c 9sCT&$UcİW+F=@gXS;{ ST9UX"/}WX5؛n4>̕#8ক&, 俞li_@ݩ5xZ>?9` VZx֣SgSaIǣ'[hiL}'&Je\eo7>{Dy(i=g<lm[},'TؗGJ҇@nϾ*H,9ޑ?jvZ1!T7wԛKO7KtH4 *hy;l9nV M &ݺ}r(b;2dtnMSa1;֬P 8u**ti˟?6 V}% TaU~lzzFɱ{_{YW ޽GTV(d7gy:xiIj]͠jtBlZE{{t>䒆jr+v|c,Z VhWERڷAs:;1o;qhܚ&NgMjdA,^<~`oZѫ߀MӠ@la)R &]ʱC|h'xˏgTҏߩNL4hfҠA001_9YFСA 22IFiL4J#]!BMUr[,68 [QL;Z`2d_J feo2JR,9!CbҦ.PZj\oR@M͂VU*\. AG=2,dԑԥԪ7jBL&e啖 UI@ёVVVˍU^%|V_M\.#G&?Ŏ HQ& 9SIˎU Ɨ1f P'>=qgQBaӨc!UB"KNF(e%ArDnTyeaU{ f҂X0~o7I"{fZ~@ËO~4J @ap#_S>uuxױCoMU d҂"eɪ%7[wM2L(c^r龰9rnTMM Ukw\z:7;&eI)cZ$ҶniS;j$)=ɁiS=h촡xvB!E.dQmPZ] Ii&0A*ttxqyǵ [ZдI| ?!V$)愭OZޓ}moD&A?}pn3X%TTsŋss|^^yߴ]Ξ;iNjH(SK_nTtd^IHy=m/irdUYRb`EF$Bi w2gJڹϘ)I"Y{X]kpئ~;RF" DP{]{8:-%po:p@V\={m'3s(J;!=ad^cL+8<힯I{h (oV-HFf1dJ$*ND$ dnU+ZQON[nʴؤ.H $D$IJxY-KK=}]$d&AJvW0lZNI>ma Ϥr&"˛UB$I8nv1c,$I"PI#Ksn^tR%yqv,7z#Td)O^LjE˽UC{}㘳#d  c9<>]q(Ti%AO) ~ 052׫A^Ĉ Q |`|MTάJ"B1VJdOaˑGtILcO;sXk?̇C22cDdz>lucmdzI<۷/Vef[HĵA&0Qzzo41%jmeUA8D;I0>ݛwu?ܣNS'dfnp^|aVYMM*(U6VE!qL ӿV,SoܸVj {[[n߾SVHĄO>Y,sGTg ~URxh@c&A*&<1'D@*9;9x<~QghUlALJ`ffwL./q&&.M,VEj0_ALͭߠjes9b/"e2/SF"@$Ali,b`@j$:òMW2+S."Djf) 3A*4hFLD0iI@h01I`f a9 4faaa0BRA QF> Ѡa(iŸ3 iZҠa-R8FXvq   8M4c0Aȁ*ma-}4hF̤A4I/i(L4J#}I4J#3iK ߣ\c2YCH-@L&Sȥ^ff43iDz [\+R_<}Rhr,ߠJ*|oI?PRL]~v=>(IkjfV 6vf,%S33ߊ_?~(H%>j4"#dy7xRvkɃ o>%~zM?r |˵AGN{N(Ȋ.ݮ suNK%J&P#(G %Rvߴ+9;Uk6po2eiai Pƽʏ.D3?*ukho"e×o}dECbK\ZR xbVLlVUzpuvK׉ ??vUE].%}\ӳUuC|x~i'eOͯ 5K0i[NU7c^Lz;JrP#{. Uz.=h`?fo{ec ǜo3:x=Z ӫv}:z[ua\s4 U?_E[4e$wVr3ߌͧ,fdkv490ca ]XVf\AHq˖gnܽe\mFH GBZ!5[ʰ+BZ7wh?ՏmC{fNpz|F-psE{bZBb`ҐA'>?ZpnTɃvwy;B GD|8kĜB ١[goM>u䥱y}9ѺgQHX2Wk8fbV:Y%f̌>s Wp'QpfXeV%Ҍۮ=+/RURL#' f]yl;uρ*(:Eg:9ZĶ04y0&F(UH_$Mith>3ML_>ggcm+oSڴŵéI=uڏŸ"%Vȱ5aL=;,׶*;|q E7=&lccs>DEUj3kg&4z5v'Y6'γ=`OWb=ڻ]f™5^ɂ ʸ< @6pF YV:ƓÝmU[ u=`(EިLWu`*>$f*N5D;nQK͸M4%T25ڍ}˘˷BTjy+{OF e @w]|KV+Ң^vcYn^GS5;&J R%sw*zx42-JNUzu8Jf\\W|xX!K-}pdי T2׫չ9Fa홲EXfbk_Ķ ~^$;gm{/?>kuڃ ϣ+T'g,h2WmP0mʺaiJ4å5g=තg-0hJH|Y=UdIeΗ ry‹]swJ\L(۷aO;}zsb߿{پ|1 Ǘ= 0ڎeըSrĵQ_rTY]&y~߁c%mW]Axu7t HUܽvCkrTԓ=kKO&NPr˛\I`V 42븼2nk?}F \[q# U):܃X=z_!2:cs.h?}_j,J*37 ٯ2 fY%m ..˘epE#l6Mۡt י4hF̤Af& 4 }?RLzRL4hJf(@$"HBRX,pΐ,P ٙ-e/%Zd2Y- H/O/B.NO+sp8^!A0T:EY!|+Ver,_*CZZX/Z>,E_=nI>[-e "](♘ DRRKKtT{l)*Zd 7% Ƕb!}ؘ&TWI_/|qLg9rAI[t\D N=ba^ePu9¥$PI D"2gU<;c_oNfW:ɋGl^.\3jIڄË#/m^cܫqq#;xRS4i,&^⸷oxkQK!ůȌW|[qa5yʄЗ7n^VuH-fNd.fXp"I7#IȨ 6ZIأ:HYtIJHWma}"ɟ/[t~#=eWu&4qӾljJp$ gQ"q1jx;7o8Zs+@~Lqzˡ;`޸ې-tK4jD | M:[[upx̆/gA!ѱ:۩jU s]$QҤ-u%m\LZ=2ɮ;& $nZ'`i;3mfA}u!nͭp@7'Z7MIJ^uӶ!s*nvNtc57W0jPxR$1׎PQL?jYҗ7~TcƢ3_Wo}ضNыs@\۲R6ni_ 1^ ={=Ϲ-vJcъn6Ky c a۴ʵG9kqLt'c6FΞʼnrrE^sƫ,RTj.ԙ;UTRbo~2஫h VջZŽywpN- mӛ"ic֥@}qXQ\$$3]055]f͸cWY k֮KHHظi@,P /I u:U<{n?,kF1Oo5T$>x5GVΎ-W#U]_1eı7Vqpy3\s2Ŏ?vU6ڭԹrڄz2owi Dȳ[n Ć/icvCLz<,c;;9Λ7S͐J ,tvrLINҶD$brJ<8$@0L7?{=:]16wsk+d_2Z$kqvk^$Jĩ2R%'pccʔO}ɾ](nؿK bN]g~OVf޲!82;}\!s۟~ j+ף-*eIvS9s*ic֙ٓ'^9\'VW}F&'%ׯۯ_T:jhur1ta 1]whm/$Jxt7f,EMc&0:o6ȋlQUW6 ^ݵyڙ4}B56=b3.[4rFKQuZ+M84G 8kG-6h۔rΨSXI܃}Hۮj۞|2.'gp?lOT:<Cg@qQ̗GޖuJZGUr5d #Fֽ\b,Q#h}~]=rpF8uޅQ,v(TѨݻ~+Epsʸ^xӊQEo\:|ID)ϭPם3==o]} آb͒C4- 8'$[l`fa:"SVJWGqf3q*=}th)?}߰QCd \̹OL\-NOE fi/$鏘hATZVs,=*Sff9"P,$\MLLlmmLL>_KhAO2<0331eC\.Dr:n. 'r=5 0 +˷h43?Yv@hAf& 4hfҠA3 43i((lAffAV[8 rd))@: F pswK$ yb~`ft< (:S(Yڊ$DGYL,H$+kk!5{u>!  L Bi)Djgg'tK#ez\Ϥt ,[RDIU- 96/Je'>;F 1!$W(H@e&B$IB'!CjeA*qrbVs01D&Lm >MN,)@<60)"cDJE^8_DJ09 ӑ +34h3BzI*  $1g_os$'IRuY$!i+Sb#cy\M@* XxޘLG.IbԜ0E1R3ˑ"Ea$bԑđQi&5<9 P#a̝ ҕv j|† u 4O JREl;GӼ)ML\l88ڲũBNN:h &֖l cYڲ B5oS MiRK&oIJMW,$6* sfeN%UW9(2C5~lcY8~F jD Q|O,y\XE"R)x )&S YZ WICη@?BJGH 0R=kլYVͺj֭Y:)MijIqb c1Eqɘ "a'It&atfh(,*l&N]DŽU8@V8n~YXRoc#s l;7Y1B>؛x^1>9vz)Sf.y.ؘbR\SsR0Xf_, 𘐷Q %РA jud <=?O J-ݍ>=K0z6㸉3177c8ennb~j"@g=8nfj}m|&&&|qff`˷t׭U6Qֈ(551ur, . (/C׍ecO024h, A3  z˔ 4hРIL4h̤AVA1VA1VAQ?A_ȉRVթ6#1El`'G=\?glǧ4J(A$!JqaooO Yya^M|x[0Jqҏӊ{/^5>LU SY/OXDžHs43cc."z7@_n;xk2s_]+`g)QqX{5vԮRJ"29-}ワ:yqtƔc^n.`QϷ1rD$BWV=( 72jLkwfTֵ0EreүOwŅ|-,ʼn}ڿ)r۴Ii?g07[wL#IHf%;4q-Wb$daǖ튪:z&7$X#5o_ĺz&fxcfiNxXs_1H]Z&T-nlv~]:;y-SD=uE+Se[dAN﷞mvnp @"ۏ6S:Vb6tc(yJfI3!Gn_o7o媟eiRH%}a}ܝEqLY|m7!IUp8"I^=;_SoapH)U5eWqq"̐cF?+JViY6=3Go#-1^gv`NΦoI`-9|K%!A]+Lkwrdq$MGSwa $I@Lc3tOBu>ѡBPju 'In*ߓ͛RRiR˓8]&|OR!ß$$V8f@rMftiZB؉/d6oH^uOR)K|qzہ29vfs,SFvoӱ{ۍ?4q@jIfPJ}9kjuC®"6p\W';_Ms3,f)}"SpS8W,E =Qlt$TߠL& DM̹\޳gOL|{;;VXi^-Sce GtRMT\@"&Tz$$b 2_ ̲D"XW\BRX$)y61:::ռ)%*,pĔƲ.+\.Dr:_Y,QXff#'ree2׍ehfJKT@ΣAf& 4hfҠA3 43iРI 4hРIL4h̤Af& 449IENDB`kvpm-0.8.6/docbook/mkfs_ext4.png0000644000175000017500000011153611733530415016771 0ustar benscottbenscottPNG  IHDRTfBbKGD pHYs  ~IDATxuǟ̬빻pߺ{K]սjzi)ŪPË˺L~q{4p&O'gLU@@@c 7{ z w ||˗/Tˬ@) u'pMd2bBbx؇)O@pn-!,p<9ܯ | $;W_DѼ1 tG!}z? rc׳Ld!g<@=qbr? PzC']r+B.҈[rac[R6 POW%X(ʄo 7O}K}|*Y^K{*D6p!>~-q5sG'?L摿~e~.#Q*<{EMg4Cy\a{K3_q]y}^E麷?C>op: r[c}ˬo1~}8THp`{\ b"8~e LmBP{ 왨:Sqqe C">/v._$1ׇ}h>^~n=ί1nH0$s*CCU'b"(L$%ʞ2 -.e\#Rݲ-=wRh 0=)V_#L V3,'Ba~S>n!x@G"X0| #ʽmM.`3Xיe3.vD2`CRghhgƂ*0^j٠wp_v#"?{K$Da8k)Nx0r ]YDW 顕7Xe2cay)|q۫foO"{\zbf_9~\8aZyrTx^&DpT9xf0KwiBJGo3?>2w|cX[ ̈ {-* 'f8 'XsRf0 }0̯fc}N\VyJ@C 2wJw>> B0sǁ~;!@N Ch p/AZT r-^^heO^_>9b70ϰxS)*'"]#;*x.?я˃GֳQ x1gg z^"L!v $@QrLO ]Cf/=ŮDٸb"1%Ͱ b]kh?PptVG㎷Hs^y`0d_Y]!D0 4dfo\=[= {7gwF BwybD`e3\NC+ON"_ `6[Wx=ǨUU=:֭xp4Z.mE @h2 -:hoތ~^}u*hSLsxYhCf5^9pv۠/2_x5@4e+uO`Lɜ4n_AG 1@2t$d ap/! kr^!3K3x^].E VAXv}(5O1Y=NZ 6 @ rT;_^!TBHB+;. bWxo|o chwCO]o/| ^v[r@>~vj `n쿶{JcԺ@ACfS > F3W.ža][=s1إ~CxZ i9AmN{pU0`R9T*D1ry=|5G.7 =o/6`rSܾ[H;)4ʵ'O ro J)^ Z-G_9oi .bc3O~mpڴir/}I8۷2dϖW/Nʿٽ|Ǭ3 n*['M4ֿAh}js]}oXyCV~xS3Uh^}7. ;9.>mijz$Tՠ$; RTF=Wl~vǸ}U'NGN˕Ѻg+h8Q ~;;{ITW\ tΞ[='^1#Rd+g/Oe=#x3#_ ΀-JT@2T{:2iBXP2T)삘 8cnM ͂+;gӡ2P/ce0|O]#[}ȉ:ʟ<{z(^,CvIR2;QÜU>=K16nc#"y.~@ 8i_l%`c]Wq;/wɵ"hl>;%W_c'ccoc-c>r؎;Ə>cn0^It{m^T||]r$LO_~%7\6⾹K_s#7毢v3\qAv#J79Pf-xGfw^RYӢyL$~ҕ>wpڸKWX6GUu?q˒c3Wlxw~)3:]~NҶeGo2|]H[!nԣYroQp鋮2\g-pK`+μчWRx~K7\0`"W;V]RHs{O;%G]11rR<Wxb7PXn*l/RONs޳~ߺs绳TΉլ64Ρћwl%U|qK77y_6,=~^iw͔r_]vy7s^=w,?xtIO[YlusvS\`sKYeomcۗ&#_ahGwE="۸̊1׹c&^2'lc6/Wc? 4o6t3`|]׿yIk5Usͯ+uIMZ0߷?[2/jB#E g V-/xuvhQQ*ӑ?eN<+1?Mʑ.9- i#\|Y@k }zmbl-v6J_[jZ mt҄$UןP~5/] 9KOxh'.')Z2op$zY@? 尅geEgq uWOKuY\6!R93*ׯ-ʺi y32l׮v-V/sGjn K'D)&^vqfߜpݙ隨1_Pfm-|вi]c> N;:Pt[m'.Ň'),kȺ I$Zұ2YİeLx"( Z!~H½ ٹ㠤Y-z,yA =i3ћ5jIFg\#]ouVU u+ӎvT(+؇ߟNk-t,J;XDOuiPo4çJAܱać)ӆ,]_R`O[}?$v0d9WE5eŻWbVΟ(Yʂ&|zO@5éaWE5eeŻ}m?_dGƝEItJDakM&=4k6QKft NwșSbh@5fq}o=W-@ޅ~~V2}慹r:19 ˹wԔOvӖqGYX’aw[{e;sZ1pMKGκޙ0;%ؕSt"@i;<"t|OPtԘvh 04bhaehN%sJ}%)(_t6xW,f/t8ekn3m^mlSl*F挝};:cŊ%˲ϟPzvs֔ڐ8k_˺vcKo_}=F,^p^"؋,c! dM(&N`0{6YrNq,_}Ա{,Z|i. u߼ݡvlӇB۾mq^흙J vFʿlt[d\tя [Srk[gO0SHqδ>yW_=1}˯:c9{iӻ+\._۷s΋ʤqs.r 87ߗ-_[9Km/Ay-ݜ+W\2?;OcN>y}.G\}]u2wg+ũW|Uo]ӛ%lLp ˽kmTi3ʃtp5K:By:Sx+˻]|HDiW|ٲo9/qf~n9@i6Cᨹ:ޛr4Wznw.#s_@o>c2PfyȄ'afN*+)"^v,Y/{;/{/}a4-:Nc3iZwWݳğ54{Uw~4 Bb@0@9x87@{x=s_`[ˮw-.ꂷxx:tp;;L%d4ՙAh4x)c#|=r'>=``2 tF!ցSMC B O  ˌtLM! ocFc͇l FzTBj =u\ 3Nw -u_4L3Ÿ?) 0h9>=\?B~0yޏ| 6ܳqG ,9_{iわ7U9p ,\ Lo5zϘrW]85AfRۏxQ~ji廟r&}w\=-Fx{}t]sế`$km-G`ϳ=!9_6[Q 6ꗺ_{ Eg>Kûwc(L3,=bģ<3ť&}1aƘcDgڸl~X `-?^WKuc۸wƢ~yZ!otC_cs'ޫ}7+/ߘ@fF6Fxo=:|G-|Nx51=%,[m0ɒf\4 y~{WZs句a |Ju_JAsέO>0Kxfi܄3'z*ȗ3co[gʧ&Q}Z~}<5l<:Z9b`XX;.O*v{tZ6n,DzqgL{+%|zIXkx\'?yzۧ?W[YdirTbor:WUG-|z\r?|WAkGUjTd矯Z )Qjs׿Ѫϗ>:skm,ǚ|Ι-_[7/\cg݈컯(=sՄYT7߻Գ]S#5sYr,Sޞˆjd/?(2.jƀkϚEt|.2y2I |[sO@sQQc/4P@1O)R'Ϻ*-|O{ܹaWߖkߕ~֎w_.M+߫<3% `5wZQ [:L,%]yc#e1WwtP,P*pw{eo/민r^W[;Yן?*J.դvˆm6D)e{U#m m ƆNd^<9Q)0+Ux Je96H.Ri ;KokgptKn݇o{=|󻊱p1 M\0zO0Ye=kx?; `slgϿ8?;Ҕ &F W{iҌ Lٳⷪ>ߟ{I>qM[I)ä.Cy@[e^3gl "@cGXxq0m;֔_838*j5;ZcḦccGj/-d K{#YS%r" q<-3}ӧܑvmߙjK{3SqH=|in92n+cg<|YD2!Vbn5{0V4\8c,fib ?,3ϻl8>1Y]( yK=cӴ'(wS Mu= 4b )ry`WVm]r}(Q鋓Xw{ĦIg7kyQN%AQ2\(@9MԽ98-䄢K8b"R#G!J0ve<.uL!Eqfkv] e eoeݙ(DQ*#)( q6f3pQPP8d>Cڼ٩M~!&?),kӂG9sU&Qko9S-jkvMz-kٺPgѡrEXyWE;sFL{Ey7bձΊv1ʻj@hrg7jvzR ?|Aن>ϸ/rOMzdpk~:K6}77tXqƟkrc^+KIr23𕮷"RO`'%?d E0]ξaZ vϲ(|Sۿ|练-FК~*̆4W\ivoW˄9)a IȣMtO^Z@B$G\gOPc^y09 <3oG/J#yw(v΢kϒ#Ù睡xkCy|zB qsG߹f,i_-UT>lo|n#yI4 ʣï~ӽ,]tĵղ_ƈF}W*(U uL_ï^r^k#(f]U9 \MGEӯp_,%_qgX;2w~ 1(F޴짛z=Ⱥɕ>Et =θ/^{Q|gVڋ{K=w#ޖ]o~;֌w1z('ϹsIWnsG#LϽp+gKӾ'o- Q'qCN0\*_ XϱfŸKd[NM%$9zp6xd cj;Y.=8* 󬕙=4^ [M?;=?Y@Db>),\fuϋqcW}d|loND4BE}薷Ky:=58*xs=o8 } DO&m6IBׯnϋr }Oj͉r _Ow0AoZfX|^ Wk=b@V0)FclYLf_OPz*=Ɔ$!2Sn69 p91˲]]-M¿㉮Qkz|*ox4&A 5D %pǷApWg$%% '5(dnr@[tvv,,:΄ɵ$@ rCB)?>!P1@ D !$ rL !c@ BH@@ BYp1ͨ!p !$ rL !c@ YU9&q>T3^s?t̛vݿϲ` Tx‰^kƏ 5~zt1帷%|Z46o~m(? ƃu)ӣFtn0E-Tb맑t_>Uذ+: pp|1mp:7o5dI3^%#Tvŧ#.L۳nN4㿏.8G3VojG7Y@=f=8-$tGx|=Ve7<~rǼ&|?L҄ |ĴO8Y}?̧Prl뎏_^& h羻 SRys+~ztɞ͐#0Fp-8+bE߼6C23cIuwJZ/ִι ~}y᚛<圿|{ojͽ \I?ld9I֢_cjKhP'~;W7e}a4ВRsel+_15R8޺>)|ֻӥ=z:lT}U!=>UxT6lw6/l+%T7UTsŹ_0{-ڋ__7m=`c句|ϫ+ -HB^1Vg,(\_Ax)Gosa-9Y|:󱏟yγlg4f(‡Å?~ g)Q%uۤI>ך}MPyAykK?yY=R|[._[Dc4o}G6 7\k_SR{@C}׻K}5s֊B l|B<+^ayuw.I#G-XўhPL~fٓMJҴ"iU'ov&^zEҨ߹PsS5.QRYԨ$Qdx)%oo.1 Npo;ehtL5;w9 ނfدA6yyQ}kG?7.,[ x= T ;ZsfeS/bFZ2]qKW?q5gy>ؿK>:HPd*&$C&޼kZ9z`@%rX@")1,ƀ#yv3h*Qxz/HZt55ő||z;S(9EO]?Sn# FlNSk)00gۇނH2 ~3f.Hb5ũJ4ԗIΜT ;Z4%`D2i:Xy][-{{e4V(@ÎP7HELpQ Ag]#/󸔿ZHso{O>URA4ؒ HD16;vx3od "Z/f?~=uz;k(+Xm1f )mm_ֆY;]2m~4gMXfzǠ´EApqWY\C.+ƈB(p'O1v֙ϔ \(>›@ "͸ 't[:݄(t@Mb8k-xg<= =F.HOI:$aLd՚G;l%{}o&Q Mcd]Kz/ciE7ܑ+0 )Ͽ{qH6yezmnxq 2^G}\0vΛ2[0 O]=oM㭵~z~d{咠\dItQ⇮.}K>3#M LV<$ysyʲFS*V&rۺs+؆j5usO^f,iU=x=s3F??\vݫ3`WpW-NݳqKy7'K~#9v¦ o4aSZ)ׯPi[^gw1OzW?dQEw?z4n۰^rh]skc[6/y3}h f/'v=pgYDܰѦL;9ϙ@BPxWyÒG .y{ p{>nױmE^tɟ"yk8'I>~{K ݜSw~i_>NٸWiI7o!|~kxM?ܹtͯ~anǗe|lӟyiՆ>}h\o&ypK׾ikl\mwy]]2OhZV<(zUq#ҤsEnPɗ.z?Zنyn~gO}EOx#\>ŝu)2XJ{~Oλt[~~}3c$yA"By{@ TÜ굟 -ӝwkVܬuUvM$DgNq0)M]yyRv?[sY+7-Q2Y䨋X(o6$3ě5䏒s'E6=9&\zDž"ȑݱ@ks$xW4ҷ|Fra:l*y^. B<ˇ]}ͬ"qڼ yŎf+i)!sA"˽+>tcVw譳cDlw}ks ,RD8 Gb)1,fRDO"REu,sٚ)VlNqdRh`}$lgg^zϪ]cU~a(QDZҴ=|@\0ULiFh|kٛ0W}ai.`䄒V5˴[,S so9kG)&o,Ҹsﮜ51:!&s z xZ#t0v΃ %d-Zzg>ؚk7nƾSXYweGZ:iz#FLr6E}`::uT't>< (T(bq7iWy'=pSM zJËz6V% xydR;z{I {}ˆ1Д~i߿jc_\xik?Piu{zp&8$R\˜[s.K?ټvvKכ,o~RyH ,'Ln`]G)cc7rfRڂ;T\]n4~;/<` $ԺgyydR(uӮUtDٰFwysYt㲆9/-U=sk_/ʖkL@ȥF>U7ZcT̼soAӮ=sdnzTK_g@C&+Dd4@ ,In&!c@ BH@@ B"9&1@ D !$ 7Bݕ<@$wЁ1@ D !$ rL !c@ BH@@ B"9&1@ "={ !`q@IF9&1@ D !$ rL !(-A qn?~Uc jL A<o[ s9&1@ D !$]p?6e(ع-aH@‰"e " e8Ye6 #KhM!D\D\ P(*6VPob,Z Q3LfVdh'!?2,%- !H9B`uefM&DAf ( NBdCAeٮnRj>8eY;c i(h'QQ]ZiNP[w V fP˘nGFhlinfYX,&UgZ855ae23t-MBJEjzzYI BLPкdbpO.ݺa]`Lc@q 6Ci'|`YKRFAV;;X͛vbV>_0؛Ν7ܫ|`%J x\Y|=~ƘØ ,c28ucqlbYgׯp[K͛V.)^/=;z_Q $WTS__7??HâHw>)}yVVYv*1t|޷19^[.iÒG .yg p{>n8L1Ϯoc۲/?:w}wgR tͣ7zpˮ44ڔig=eXaT`WpWP6Wޫ^MּyniW}8u-ݜ,qƵuxn7&MNjٲ9õږ49I v^%W^fRʺ+펓niɿ-[t+}IʣfҮd!8%`㑯Wwϻ̤wΩdu,LUlͿ;ύQyvv͜֏^|Ofj ϴpG3֯^Xyli3J/SknugfJK`:ޫ_a B2!xBȵ"mI k0F.QV?p˙1~_'{3_#YW:6Q>q31zǯ}-vqt(9rdC~n&O9ϕ0T,Gu@9o̓Ջ{NǮxƎ]x,ANTC{wagҲE_RVTȲ `gҲywvtp\H;ecƌK]GDYÇ23Ce S 3EEFiSvv~D֭.+ B@4R)8(ȴa0DzAJI #|=_uѱB!K%fsޙg}&R;S"DŽ@Q))uuu۷ntB4M\\\RRB>+iF*9& EQaZ\&1wQ(Z* asM?0`drT&mEФfmWB=Pc;O M8<`9&1@ D !$j 2?h!@dplNdϊң +B i."hb5?0fn|JX,E" - 50ҝ/prWnsɥsWG'MQ@!0595hз5w )q 5!G@ Cn.*&ʨ Ao4#iǞh-7(bd@s N}Af~,ٙԈ=d4wvvi53-8>bI1%+[1r~OdzFDs(e^ءqVs>W+j8gxQxڏ(fܘ~%`(!9>gn.=ZTafiela"s;}]SGE~J1#[c/i3s:>gd5&UFFXCmA~IbMB>;f}=;a3rӕ?K=c|8ej,>pH3sddⰘ9Z.+ǍΕA[O`u jA_@z0ڟMGaJXR͙]KT3v/%1!\djj3=8Ssᾣy䔛خe%: qPgH?sVB!`;a[!Wޞ),kœq 68Z>cllFTia>>SM0]:Er "t3Z6G1zҝ/pr)o(pzd?g:o^JEhJNΐu4Y4M:}Ma[DNʽЊRO[5BO Ҵ4<-7nis GiJNwf:fL7mDܳsVQdai^ɤÓ$|QةcӢb !Z5Y:,-Wwuʤ(I?Aj0!MVnoGȉ]E3}njbݱHB҈xucL ;v T#2apO>mʏJc\H1s(>kPÉ&EyhӚT-Eβ=4B 9 9:* [uVwS$ V%gwV"cL:UpAg{"DŽ9wLrM΄i ݪ)#i%nZV.VLgh̴O#H$X)] >9}3<:%lӚMouqw@-] ^*k¬TL Ɉ)-I:Up1t6"+'{8$9y &n0PxXyaƪo./`1QViPKtTYTee9kguQ5̀o#:1.t(fUJk *- g7t0=(yܨ)#uu0iQ('* <LT.WvX9tTr5fY(%tt;hI:)Lԏl Zz3Cr(wdCQp;tIʘ0dz;P\ J4YӴƠNS\4F0Ъ$ufN ڤ23.聿K Vg1@i= QiXH;bw`71u6l\#{.f)ETrJgB3oH22SR$$uG/W}`6 bU\rv&-OHWJ2&%Q|`e1}R];a_vyɱ|!K;Ôym--wnUg&;} e%Yd pҝQryyiٳ&m^[5{r w"bc7KVV=Fa78\w%x|@Q4T N2Ia jF"hôR$&:d6 WjZ&SHܛCdt5ɷrLcqE+E9kG 2=+f iNF! E',"r!/!>'B^:<)+{ewaa @N;&I8YmBb -)2QH D.PħvlXDT&b-o\{2y'}u0fINM1 :B <6>!!B7^gFllh8QcZnI9!Zy~8cEf14g,hQ ! iC e2j2bc´Zvߪi!]ptՕgdiiĴ&mP V]^Q׬pHN6<=j*DRX!('`ba8n ˚-& f9l11,(qX>fxw`s0" 煵Yiaa:rœHc0ufU1T=|Xە)BJ$B8nH1jn*"ӟu}ZMH}rfqq(C$B>w bt̻0T9VgK i0iJQɑ-IK۬g#0HIPa)9ɍYڰ̳&H!TiLS7W$dl6[e*Yxʰp[qaxѐ5<>A+{{M,!KEI`Z#-U.'MRףx0UR$&-NFCB>v$(=3Ϊ6,uGW7%U.Mݜl1gyߩ&( ^ZYzhisF  +Q* e6&YYYbM29W8`&X.1ĶƧ(@9ڄ 2N%x4489_mzN]ZPKe)6c[V6FDg5i9#R"L=l$wqXR޸$R,&rde`bD8F$Qdɠ63JH <0LMK, cehiYM|n@X(j0)3"u80t8cRXQ4%k-)v5Wu8][RF%]NEUm&ìZQP`0B7(q:6:AI8"9^[\\kJ3C{J3 (RII#x@L^ss.`wB81G<ԷHm[Z_| "elRa9LTGw92&ctaFH66t<293[Ea}(ٗhkg-ŇbJ<:ٯ *x02$iǡY焓.u87@ B"y38H9 dy!E|9 3,kم%bMnqHû|0,jerPD%(yE.@ԑ$|aŒl0Zqqu5u qi+ҝA8bt(^o4Xa` G1./x?p {V&>gY+#;%>]ժiLہǺi:,*)--^ݯA4vcB$FtF| {)v>NFȞ qb0yj>l1Q"X-5%ƌ'$ߢ3 @1D$$HLlK+X_RTd`@K򨌼5vc]]۷>uqX`VUZQia%Kpe ;K;-/pⲆn+FLպIWs$%q&k+.h12,2>h̘(x&QfbFE 8ǁߪ 6R`K[eaYC*>-';QEЅݐ$\AXyMPlwy~5aq*=\54bGOVKX]ͱ#E R΁m3iQ}a: ZXQf(±}ةyj0=R+@2D;b0Rj`a=Q8K[١.mUX_tUeÔ0frVBݥN\wEq,{hXuM-zV~b~r)oH1A$Ҙa9F'e%k%-&g'M%Z% KLTYM\YQĨy) 1-NM ĥ)ىZ MI4 }q T^؊DhJHsftƌb.JNHhJIN#LˌJ(_[ ( -eaID4dGDm݌>.!xU(YY$!( r|DITx=I(ǓO 4{WMqyCgYq1$ToqĽq|*X^̊#]댢1Ij2F)v&'VJQbi͇dOtRY)e6Z-6- W/pٱ"dkW[RܱPŹ٢LSHJ V%BY 6J*1=  6uF8]yQ6rJB"if̷3.\j2 HF}P1GF5J$139ъ]ԻPEf{167:o)hmb̭K+"s)ҝ@_rmuI}}-{}Gc W @+#غnٺ8m2E`f|&'d—шvՕVXiܙk-wu+Py)E|4/2nhbZ*>nȡn#9Jԕɕ՚Y &* klc-]ueU輥`;Kی6s@9:NP<74reӕc+-UE͎W9imҒ]vbFgR%wTAبײ|4ّGKo+ R$*pfVF,PyiM6F}(ip!~'I#X8l=qdXScEie*&%'#NIhmFӲȴD6"n];#tvކI##A#}F3.+ D?lXjߓwMZ&EE2}δUV4u[8JWW5vY8+c2Uػ0Iҡ4*/)ŏCCqA =||TT*'dX]ey4cHt-mzVzPIweYǁA$Ҙ55S EK4ItG1@+S34R%ڸMgzac'E4B,2)Qn0ǝPyYcS;< w aR+RݭF! cXm,-&dB2Y1$ ltA" 8q$Q&e>P`_y{w]YUc@Be(m9lW41*5ރ#LNЈ#!E4V]CE[^?I VE z;zǤ \}y]ٍ:]QZac) $~ dpc?;hR2*ӹ֤奖쩶H22M V&GY0>К;0q1JZ?*/IM++SRx(GԕW}J@m/+{̊)iXBVm7!0d:Ѣڪ*&a**+3Db۷pVUVNv 8i:)=ǵ?dV11}'8\lJPT*V)JjbHTRea@;E*ZDQkm9b#@o[KkIqBhvCileH|+; N긘\186st"H"tgAC~#ZZ*I4[,B2Zd5'B^:\.=pp_Qu+r΃ٗ!P$!FcevPDXL4YzAn9 ! 0,ZV&-swg7MQda@aY͖h4Zo)qquuu2D4@!^7F8h0pN?0 { :A,/˲]nRi2>d2*U.]7z).I9Tn@?#+@&+BV q>+8=/!/79(t5.gH֟q)zdQ϶h EB"CtlxEgS,"eXfvИ4!/:QHښmHe."eB0qO1uu{M΋Ig(yDRvfYscEIU|El-jr V$ed)EY=rLW]QESQ)2b4Lȭh8Y{SMʋ2YZk+6'g2{ ˚*˪Z T)J7quEw_IO rL8Y.'I;#ӽgkj6F²Q r/8s4rBt.99!(˯ltDFM)q|nk ˌ9cJ1>SK)捏QUzUUIFNf`Zp/GY QycT`j,+)OSSB [&SLL` f E冨9nd²*[l<z򠢰{lg]O[p?-tQN+R4RQMl¦pBG.Q J.Qrhs6ДX ѵ8܉b5d! aj8ZOK#I,i0!-l3HJQ)670)M)<tBjFBQubz,j伒` NqH㱋fﻀFǀgk+N( 4` =y$4-B$Zl{krVV!D)AzϼӚaյ5vZ)}䚮XkPs͌\3Dr17N.r< kR/-G{}yHjQ{"DŽ$ N)6 Gzz&mTNԾ2F\ (sN$3WdˌX*&*,K B08סՠaZFٍ>7V$&~Ex NȧdbH\Rph봰sGNTs$1NyPʸ(\_Qif1lڲndž3 M:; U <$lwEy}09УZEo0g5Tz/&Q@r˾PqbRSzV6T5:P_ED+-8Ԃ$M @SgTkahYXLƴ*>.8gLͮ:% KHIS@SVWRXm*NQ^)6!3SC,!3XV":6F38+%-L)c !aJ2<` $rh(,II,/WÀHo h˦_|`k;{'0e0U /paʪ ZDwww6f3Uid 36&#-A@,:pR!(!T(OрR%baa!ۢB"C ZMgWmۇdGEF lmk)-)S*Z<'!v[(〈S RLJLoh8tN/8:֨511щ JόSg-9j !F#H-P0LV)e2ف@*9z rL&1s@m4\rц,Br@ dtҐ9€7)⃐ !$ rL !c@ c̲n i&qHûЍaZmT^{-:61&u&711[ U&i\X_/C I-CQC8h2ڢ`4baaam--᧶a;]!JmR 7`YKS(&@LFB\wLvg# &rfoa{{н'g"C b8.'IsXwKSp\nW;5 rAg$NUU4tGj)_1wU6uYY$جp 3@&^nd0-HNOPXSSUEu2:9;-ZI#l-nr R$e)E5:+G#3Ԏ~rf $aY9 je9,j34<65lgnnMpNh@gZUYgC،(KMY})备A0^ŌsORT⎦n G)Rc|@p8'IkZ>Z;:AާǬC3qkGMqYz\NCGŬ%|Tj[l<J kTl!*4g+*7EFjL- 36GC[ڍZK X -捏QU8|Ԥanʮ ' eˎ2GN:b$+@νRRvD%5Vjߒn1!CX$iH jiw]LkN ((Ydbad{;%-N#%)l: ;x$X D!XVGK5i)Z358Ċ0Ҏ3¤4%RD$kia˂915wX$8YPR%%E"yxR$$D"Ex18{OإZ,QBZ,f 0QCRDҨzCxrI) R$m/Փf(\3Fbma0NN9VgӪ Œfn:/5 0U!>rŠ%T_xƩ1iCQbi?2-R%(>|KzЇ$o@;Rʸu~usT\ P]֌ME>I)dÑ"nQlXY:VDjKaG$Ճhh12̉m\l3B"ԯ r[LTu%2ioyC&+$3Y@MIkzf,0ahg7V|/آ1alCUqv}CU38]ey}9tw\CEC:k˫9J+m,fL5ݬe9 kv-w ZlFKkЄr[LTu+Yƚ&6,Z)[ЇCU{澏$1 GkX ʔ԰%D(m;. Y@SS* 2 RF%&+)1RĨ+0%ħggg5TUobiSUcL)T$<)+sJRY~l KIccg)E|0_-hO[(a9b{*7Q@+%k,LMV AڤH YЖM0LeU^wpNaa*,^\]^z0U"uww6fgUid! y`s¸2ZD)Y~!#-A@,:p2Y1(JRIe2VCSСjbqiQR(R|=8n3Ei];vΌZZVR(4Z\ܽ[()SCaDsBxPȓ<3.Ҩ111 SC:|OV1T"X,Bd2Zɤ ah@x%e2q#UBOpɒ&o ! %K !c@ BH@@ B"C9vPXLYtL !=a9jeR co]jtEE! 11[m U ˣcceD4ea@~1:Cxxh40h4u4aH@x(l[F1M [cP}E3'Ǘ`f@Dءlr14f@C"! \vpyؒFA GgʁHVT6%HNS>Cۚt#0]߀(]@o&wbn-`?XSt6L"Rd}:&)BB~IDAT$'m!+ۥ#DHfhml7J-ʍ9cZ1>abR52F$I:9$l55LSY}ciaEKȄQE0i#P!$ьa!sØ 9W#sNfxA!X-fH!QŦ854Ȣ ZzMhDUQRq EЊ0Tѱ6'eL@adD"9nCQ,ay>IaXIҀpƽz9PM3kk vJ!G0Ʈ=Z0 Rˁ  }>#!74}_ p,G9Qh8ۓ]#ArLK)E=mF-1 betưh.BJ&Qh O tY[t\U؝aS}DdK{ܣ_dP̲M8$! Da#5;z`Zk;-,9E*hİ{cL+b"pcUS0g3uUvs6y`FDDQ)3#,G*VVHMHp5g(kt 9(\ A(EqRK_BB,),7q4 *Z,?"D}"89%Єqr}zE!Fչmێ(m%% LQQ:1sǙǑ= 824M(Bc MB5*uttTB\\8CI<ŗ O.QDC"! ES,!J"IX,Be2JIe84&c(LV"DC0aW") \B %1ǒq(!crׯN7cUp!r݆{A.a8irb(B8Dc2KH,K.&DC19 2W9QBbI! ,,K(kB41̱d84!r8q,1a@q;& C"ǡZ1haɺ HA@ B"9&1@ D !$ rL !c@ BH@@ B"9&1@ D !$ rL !c@ BH@@ B"9&1@ D !$ rL !c@ BH@@ B䘢؄D/@ĦF HiE~CPTwm>sCKs#q1@ %6.^ۯ(@m@&N$M<%6>x@ ذw?͖P(H1u}SSs[{`$^&TʨȨXJ(7(BZ"X,LR*Y Z 1 drT1&^& BP@@ B"9&1@ "!f9n܊b}Z%LϸB@-A"q|RD*MMM,ÈD"%MH!˲ 4jD%iJzzEi˲4M%xC@!tz}\2n5Mpl|lEiYxXw2D[}g= c!T`YVN!s5j0Q"RxйB "Pedz3Vb y"B1Z8c–w9Jlpr(qj:Jql%#'NH=\Ul,Bڶo_W<١UaƽO޲ 39&B{tykk:;콧ȴ~;bČ:ۮ-Ӿ'ol南& .P"ۖm5u5RϿrT1z<=9|u` >қ8{:` |9&B8?r[cs ;?eaW̺GiG|V_>W8;?Ks ' /Pikjj<ז.Mwt}4613^c1RӧU͸[w?#' 39&BOuakɴᱷ]uL[ѷ6}ظf}KFR1}Gw3qᘖgyT! >5*6Exވ y 6?ڒ7sLs>GA@@98} Ԉ3S*uvcgn󏭏\NVj/#H[:L Y};RaXqɹ_y'{Noـ!C.km6 0sbxg+ @)95 H[]ӛ_mgd1#FzrEKf9FbQ r6-vu_ryzsqOW!}cņ҂b Λ@i/>v/gT+g~Ǽ")D`@3~R{} 3̱aIj*-m~Y&oZC ~X!BLr*3Q踎u͛6k+[l}ѽOH K(ݶl}`;)ev$6Rʘ+T.̸kŨ*(h+(Ca,cX2 */ExYrssUJef}Aa\?#ѷpUnq=$Ը-s0p;xIvgފ/U2ֽ9݆ЎLTcoc u+VŢYv+UTy-[qCE8,& b""sG_,X̸'70E|\z+۵=u8TfqU:D\-h`Ix+r@kՒ"(A9$c ߿>SV;0w|\5YxM}v}eѡ"BGϱK8K|=C@; Z(F, ]qUcB)>2]rE^ \&M 䀈KKJ]H^L*cRNt^|Ҟ{D:.yH;18Lg掟s?w33ƂA譲~91gO{+eW|1ˀ/=b/~#>zkyR8,[oֳu]ЦCZu` )yݵ8۾ً-hVv]˖_=~Ί–.͟ Ǘ[E?ocLI5|=GnObd>3]as_'7㪥d ҅ekŖE|/q|zWýԝQRW9s+eT1UmO\FkڡZ],:88n߸%!n[W08d8ü.\rՊ^:XUr˭ݩq/~ fvY`|$8o sm@U؞BP$fyo=B!JFԠw+I 냃r% yb`)[*(r_A%;:޲ c0,tF` H9hdgƊF?. {Cuq/cnfy;=<}(""-^ƧhQvMD}8m+t.zՇNX ZYyt@_b9«Bim'n<ɂ-ۇAPg$2 :++,\ id1ם68.̨2,)YZ`fki)ϣ(M"/%M+3up-W(Ω~MF\0 ˽ߍr>l,my]ЎbNaRca*""U +]\rXu6< ^gK'RFGC1o;~YzJ}[|0_薹`ֲw6Ýcf/|!߯:md[jk~q2$}3W8 a}:3YVK?Ym7N+꣟%X8 ( ”:0 As+P;7∊"qϑ*Ñ7 Z Vؼl-V'aN?k0^ ]8( 01.YI{IdT&WJ)[敽d+;Irw~?%a-3檂/US M/^o~awR$f|9 Xdޯ~5 |h0o1fJLf{8*~]=,0Xc{ShgƷX#bF\f/fgEP]].ъ2DSb?z$`?>0㾚4D@TTN?ly°Y+*a;:%bE\) L)sΟc=݌Gt2&bq<f ˛#@(- DXE{Sm>Y9\I*+_uz76 |ūj心ˍ paeh]:pڪX-SWmJlkq^6oEI3DM Z$aMf: 8ӻC.r5*-V,쫒(sSEnV ѭWb#PJ* Zxi y "MS|NZ<T+.RRx/ol]|;o@$U M[\T`'/QU_ ֞o/MB1FfZ,fj+|}_C1Vl6lQQӶLHJwԚן[E #H Xϧh:*"l}`QM U8a#F7%4{.$Jg9cbuyٙe[^Ci ӀPLE?.i>\4Ml\kW!dƳYuybᾗ`iP3ߊʃN LJ1yBBCryVF:ʥnoPaҟxO/0"W %T* }T\??Ƕܞ-Xj!eث9:U!k7|?h:B󀂿"V~`ʟB8;2j<+O/׌Ѽ^YOyunSYG.ZXمO U-)r,˲%5LYV1r@ j%S.u74LfTIl2Bk d!@*ZRY ս]vɺfc)ߏڮ[)gMgNzY>lmkKbKeacZRv^O)yv6@3-]ϭh*հޚcv T bxv vqpه h%RO=9iGth߾CqK6#10}̷_OԹ}|ډ%tj߾ogsb3?ow5Fy %qMx`BwOOۣ}%*BZ0cΟ:xf\*/f7iX۷܇\1qN KOQ凅ӟѾ}ljmbJݳ`n۷oy3R麯hGL5Gּvq_,QQ] ־?{1mi]N|6;qXw z%W*Y)ngz~Kv,qzqۋһqヘH_‹!z07t{ۺI;签Ɋ 9ycRé+v޷Y?Y}sV#/m<ʻ.EKu/w؂jі1lr?ooOxɪ扪0i/`=a~uО}('υ7[#?t|ⓏvG1-g_åqSzqeoٷ٭R6}\Y!ziѻ?evӾ}PJWͿ|jm?>#[&,DV="mIQY_nA\ӱ4ܳ߰yS֤͇#$P.j:ͧW^?&m>$9rEp󧦎DКq>J~ƅ\^UYD 㫈)Ԛc‶r4’?ckSR(ӔOQqp3 VdJd byȕ9rA HTeb%&~ŸNYi2⭏3hR/~~jܿ^/ ySќ!͢mTXkA̩`PHCĕ4 !p'T~?4<-XM]bCU2'y4ք+7Yਵ9lU'W[s=˞q=W&\+q>%Fm#2T䡘"X?Yā]^Gn;ռf :s6[?]m/B~R.f7"Z\v>gN?YܛE ɽf^zϱr=[ rᨼ7sVLɵa@Um}ي'brJX3ήve'`{?rl ~Z)OЕ竟s[G~zw~(Oɲ_Q?&dC'/n%{,:M)"ʦҵSԓ M'~Ji:]!~5;rמ 乌3#BȎw}7NTRV` <ѾÄ&<_?(F_ovP42ϩvMT!Te?Pe4zqI͘]:u:}(/^P></V#3QY+Khw_]:u ~|cw~f:uǫoe/|ܧsnf3wF%^+D$*ڒbׯ\rn_v*sSpL2]ڇNj]T8#Mlݼ^%|؞ϜW"L7*[Y+4Q'f릍N#Y6#TuL'vJ8bڰJ.?.v%b8wk,mz*w >BBv% MU͓'90=@*:ZWv a2T(bHOqJha j% @ +βlMHH0  etLtRRaƊ6%Ui3_#@*[R Wk\WEDEz c0LFCZu:]UNQE3g!S:L U, D^n9Siyn0z{퓐P7'7? JnkGњ*ii+hO$S9%EP?iBVӯ^*b3B묣h,Bկhw8㸏?s޽{ ^o:4׸?xWǎ_|wKR<_w9=Gͽb{V+ouٱcHϛ4;xr (G~h?FZ>]t=z400pO?InǛl_aL r9eY/5)wѬM_ {vچYӾNzlƟlY8VfSq}c~YRBU:u:]mܸq͚5dRƎŀRݣnu7z;6p.=ؽ jӧYю Y0XteE!cjfv}9f0<4ã:4@*Y3?C ݷkyI&oh48_y5T7~<=;hT1M;?>TQ0E^jYv0vXo,LaTKw6k4yC࣓Y )))P@(v~ #E;sD*pS4@J@ DST`@@vVֶ[7m8og!Ο?h%;u\C*hdJb BCqnGSJ$jÂ&vZ1<*etLwܯb֮UN(J:p(%R4o,.U8Y,fk4Db CNv %M$$4OƻS/fP0RjryZ@,k4bbRd`!,:&f޾uSTV> 2EQJRPE*bP5.òlNרqSٔlR5u_x;54|5:Vw|T()ˋKk6MFoAOXkw9%j XV)Tb,1^'IV/ts)zvܩC'-<ӡIg1ѻsN0ԧEg͡];w{[6eXΝzќ%Bi1[Nz>5 TlKswyrԘ'T0=a2CÌFF9Q}׫w?שqǾHퟶPlÇuV~dq>M|#h ~֕wf>v}\o97-zZvFT , Ҟ%!m6[)j t4-U,xL:rVFuzvlDWV4Ud> vt5en8lӭ•r=|fG<4$!dX (q ج6B_}zvZ1PP"i58w'g(]3/Y# ij"<~h v Q@Wm˴BKl͒"J,B8.@"4]GV2Ũ!d&-Z`Y4M=x0 ϐlLT[ƹ _1G &:e ӑ'}2EH'6}ɔ7SV%Jgv;044TbHτ'Hɪq|l%L`PZJ|h4dgqvVT}@R(I۶mkXΜAZS4}{lq]W^y>c9lٶy %>_]zT=n=) Uʸ2#Xg F>3=M(fR *Kq< V<f2cb%b6rS2ihv-/Oz?s *EQPܾsGr*ʜ\^_'2Kaݦ.6+ \ :sWk: sڼP|qqAKn}҈tjz c4MFCxpNC!Su,Ze<>^}fgefe9cYl2ggfbt,VJhϩZaa-yGtdr x8.'7WRMF7Ct:ݺu2h4ڜ\2V*'XFQ;Jh٬ظ<'0Uy۪iYDiFh%B `!%ajV7lڸ!$(84,ԫ>Νx>3pOZ|}ng_0-)tɎ$̩~[pxe>MKOzܲFbݛC/̚9 2Ǩ=놇K\~h4DGթmcС5p r>X}yϠ68O]V':W^1`.7FܹK/皉xxr7&-qS8w.{>Ί:L)wO;0͑ݻtk?.4:WLٜtLaݺt~|ۿ\smz  )Faq:0!aSҏ3frݮ]Novmcpќ}~{׾_ɓwn;xJ`Y@'kwZI.dgVU[s֛f;0lJjҭvgܲx8(HiM7KegϞ^+]s(ƍij:oTB@Ӓcǎ.ZH& DoO^sm*¶v_mQ%*Kg,a5AmYAE+&}H~ܙ':Ur$F8e'ڮ.{ESam#]?48.AF9Yyf%]%m="fA MG1zӫ@vΒ";=a$B};ÓnsQ$Gÿ,H@Qo 񍚸qEyܳ5 *ưfafCFĭv߆cDfaUVqˋjJ.r,C3 c:EʚMԗgvիWOV8ykJ$oV&HK!K '(VSZX[r{Y=YɊfKټ6vĝ)V>J߭=b%v?$LڼӭkIHjB%tOfcq)F'06#Vfk\૒ N_OG- Suw`Lũ[KR\2bG+R B,E h4Z0vL/uSgƻw^t={>S??'[p!O?Z,f7F|a+xOVyw3n{3k+W@@:E[-QSZ-hÔ,q `Rm~?:Ƅd wkcwL= frߞ-Mm"/5Fgab kcǂ1JzT$W 4J!C?pg_|%Bhĉڵ5 (8Jx; z.-[vsOV1~a^qc_I6t~4K>eOKϙ+RBHNJ ,50S.ONJR(cgsZ ڿ8J[4Eiw2*8HP:p2a{ wzT0H[^*vr`nA,H=qجfdO~U|`Ry[K 1T U7Mefkܜe-&ï+WΛ;ͩSrJ64ZtLoxde*jϗ:7rڑԴzge+k(5uaڽS:ЫWLnZB u:ѕfܾ#*Jj@yH*R?(1G8BH IϜ>h MW!tոhוޜg$M6LfF+H ADhtzMBBRt2gךSY%+m[6m44ΪJLO{pLkKtht>BV8j&dR-ރ׌&cӘ@թZQx^wRuΝYHϯvxxTTRp2(a*ADx(\I'(M|\F<Q8*UP`PttАRIAX-P(Se$y>|DQ\ 7+JF+˪zոh>5Z!)FubD TO|E) Bfx3Lq|jd:OcrCv#S5 @ | Y@-Dg %Bt@ |/F8]pAiϲ,kgٸD&LR׉IIJr,]%^F |O~Zh@ z^N*E3~cæJg=fR:q\nnJ6W7j&77oj\4 n7&l6v!Ŋm1渂λVJϫE#Jbah& (X0 ò`b9VP}k(1<ӧ?l_[~Ծ+9~_)";= vwϏ_B& mf=DCU1rY\OgI=[?~۩ i°_Nj@dyp??çzCL%PXKʆo,},h?< eMfsLtD*1Ly9qRITtLv RE:xUkr~^]yέ_{oórXQ:`Nٜ\{vg/_bWOrw [^EXoe|ao~4in77ܰc`:ŧF}׎]{.3߯9s?oYvlMoJ5--_#tĺYfee-fsl6gge %$X%JEc;E)ܹw6jⶅ#;koX;7@[Ȇ}1i @Fo~Շ.>o<)Ce!rMFѠ&&Is< $:χO-ߏky>%ӗ4:c"ќI4ޟP@UA}C,` ,%h(*(iZƎfHwn I/v¦YZԦ$8.Prh +ViZ" f0B V`y LGs-yd{kk/Lmbf?N@[~ӺS i6lʬW:ȏ9`Y@9} ysԲ7q=~/ ܾ..j3iNϾGܡv#Fi,_cS*ioO')3J"/%^) %Yi={N}[fa ݍl;r-cn2<1BL&#cœ96޾ٿ~6 \齟z=ӌ.[ ȶڳeqǿH?nn>g*QooZ91pG~ faM; ?%x;J,ʧU'|fNͳa ) ʖ̆o\XfhYbܺ썑M#'ݞl@?&_FȎόH>xӽ*?;K*CxĘ4νl d0?ϫOٕoJo7G/:uzoVϧ*SO'/W䀰v/=y)G_AMlA<\xڰEVYv]K&ABf4qG8a?fkKnoTЏ' ^2ȄȄp͸^?oaxC m'kNyLSTffZqV뗑FSOP{],Yǟזx e=ߜMCd_aO7/yɑ*yجWHy## DBV+HƝcZK晴Y):yvy'tAxwyhj%'#hkT%XJ ';vr2U-Ɉ2edRpB)bȀ,;R& `YlDJ$dFQ%ԉlnT*bM}j?|&QmY+ ֒^,#~eY6.!h0ff S*Q1Iªw 52z]B!e-fJKw%?blUP#ɏ5^^Qo0 `2Bk:Q7iG̏^>)Z!b&`]ɨ[-::hSM-rs{\0d4ժϳ`0/=|YѶlR Uj˩|TZׇ֠!6Rp[R7}O{oe j_݅LpSiV4挗+O4,g 칅-lWh{~q Ǧ>Zn:r_uy~p׋6MϗGv#);J P%ʿGBf!N!!$v5Heo6|AyѪFN"&chXXԭ[G|Gw):oŒ&#%r/_27L'vħoپsI'{4W5S5h=<rq鬥]?sӖ??蚾tˎQ9uoغkWyI+ߛ|I5b˅ E"wn{Y!ؒӗٽV;BNbf3,6v`={ ֶL B1.Wޣg>X}w)Oٽ3~hro`rvhccW]2rW,~B65u`Է/%q[~z=zvɷ~:ѩf3=gӎd~[haonRg:f+ʊ+??roE6?@#9lK~L6dⰦRypGF([[G(Z'#SݲdF(=ӎ18*:iX`Z &oɣZ)d~q_~2T E[JK) Di b7BsWsO\\_M5nӟg Ps~?t‚ jK;WO9vl_n +{"M S` ,_ohL4c{RZ>oZ5Q򛢼Es]|qI"$[6S''f.[c! kdđ&\p#} (tŇ7:O2㟋#P@*P$_\x/6R` 0ku2AFa}W8Dg BMq,i1YEl6ggf">Z SJ9/@NK0&7h@޺Ǽv_m.h٬RR$K&Ѡ lV&CИ<øw `ˍ?||Oh?ľq ~t-vہmۗZw@MGo[}YG~_l4+`B4ӆ~psCu /͒2%y&S?dƜP{.|.=_iohJrA յ0F&1$4h0x\dLFv!7ԝm[olb̗^m}V峖] VtM?Oi 80ewl8r{ym^~3Um۵җ[o5?өtXz~{\ӎ%>k^J󷖭7,:c>Ev,tviу ~|+ݗaYtzĊj`dAM͹p?ۍʇL$H.j<| ]6l; 0ILxᵥ.cǼҿ~([Q"e4h÷ln˦QjeDq:#QԳz[}p-B2^/>t~O+j\WɅ"v@&W*bX,EcjO *QYVBVfY0j8D9m2U#aYCկUq ^1CEXSt>Prx' J- ~iñ䜒/nUt9kc9Ex8e_b㸱$y~E,kظѐ.LPEթ!$3/ G # @df(M\w#޹xP5 Є*7sΎs<-oMVIA7^ûbXhm?;{Iޠm4F'ɨ7 azI67@x8@H`hc,0it gИw]p۷1??/ȭM JrAeZ\ia!*M$L/UVl{S_u]KҎ ~ڮO&9tҰ iN=HsZWmv_op,0 E3L.;~hxXXhp{xsJjI|ieBףhϪy D{r?oiДJvᨖx>6xyUR 8oO^+\.iIדFcddDTTRi](GQBtcaW|8ʋ,?Hwa͂R嚙012 %* B/6VѨ33/]p_T計`&Cdvo΂r_nǽcܮ7hu\w#` @@(*\&  2[,c9 RMʄ/Sfg,Vdre^رd5Kt[C)9:I|頭)0ZVT| մ*@3E_oׇ&Sn$>/_-ywX@XnE/Eefd5]^MFFEQ|_ LX s;(lAPmƗh궥rEp,bI/4QΟ;++)a$25jMӏܞ *߀(ܝ;w5j04$T(dFfC2^cRm )o'B{#Gz5bZmxXXdDBrv=%u-O ʪƖ-)rAɴ<~Z\l(T(4L ^E;O "o@AP(r!i&@ v1QR@ud DLkZHeKzAKUSBe "TĬVA!<"$y^;Bb h$MFCnN.Zr9Q[B587,k2"""IHFԉ 0L,ˊ|3Uݽ+$U5 yQA7uY6++b6,8eYٜMH v;+6't3Yn5+YI >cc&d c4ѱRSހke/j uI,XV@Q ø_!Ӡ"HYVB[on᫟6jצpcdsVl>nLp~wGϹh2kէ|'4[y0܃'h͕>eӥ^_[ݶ6tIt`3d <ݓS@iׇKLWFw}}b吿8?|򝗲PH1PS6k?p=|{ȈJ q 6rTcd2CBCLFMF}hhdl㕹>eڟ&7J~ |ok?6SbZqO+aŔFthl;s5.f#^:gk>J Ʈ;Mfˆ9UT.[ʟ,Hm揫HO:kr 5ܖx~-wG\7&M,Z~YN޲>F# ]o>^W^Yy^WBQaBHo0j%_Ʈ jhȫCwLV}ת0PGz} MfYļ)^ԹCdž7y袮w% bznŶ;m㹑[ּ1@Vk4\B;0glX0MI?6oj,Bx^WE jk' l Ms8qMӶ|2eX-$$8&@r @ _XpSJ ,|~y}9*#NcY'Ӣ$ *mt^VlfFG0A~+$\lJh[K@I@xK! 5<5ee"5ꊗYV!Bȣ;l6:DvR16V ڊ7cg-i_'H)סcf:.ܞAǀՇ \?1d}:>ZXh\RZ!q-?vxs3lN4pqhuB3 |e_>o[k/T"TTRHh<cLtZzJ8ZE՗ZgKNn*ʃw7e3\}:az3{3^0xIA lO?Y9jWӯ{_E|w͟aSK̻%Va4jUVvMf38UJe`@@T`RI{ Fc]"?N: ᅮd)|pjRauno8bohjJnNjgg45sIA4%SK _cJ 7jJU|퐉<ʂ!ZmVB J;tp!GN;34Q| ka8*U:<$N c* f˷[g.n¼=oߏRJYP>2ހPRJC34#qAʫݺuJݻwB`hBd}u?9uhl5w HbR\!.o_Kʹy1Gb s S1cxBU, )-[>ftj5ϙ(i1ȕT&OpOiڹ!,^)/^γX|೯SBh. h8V_4/TGwD" B z>|?`YiW-(?}-"%x|xZ\%yBu^xBu,)Z} m d4M&\aZz{ E.hEAH9׸g%GeP-+Ƅv|훎60P?.  Ã+^+ؒ@< Ԡ[xBu,J|n=zHeRl.CP>vHTDR ]!E$y Jf[w[YcLA0R<7/ca!AT2pPNjMۊo~FVC4xB,ZY@pF&Hv{ZZzJJdQ[V7_[:UԒ@DҞ%L*aJ}hR)uj"J,@(pR<((bXI%2B!+&Vjē.2h\$ݳVY֚Y O E,%x.G%o@:njkxhI1ud= !`ƞoCˆkWAq]9xfI`ʾ,Eq¨pG{B#q(J"@*Zқa蟅(8 !TsØXKxgIo`<~V<8s=}!BO E,YROPqY (x,bI(s0) !*hqB%̱,toxhI,ƀ|3 qnHHe(K\"0B?FO E,WB8cDk %WRDzxF~@O U-)9k oS#6?R]VWAbׅLvʋ~=c_f|˩k~^v#e+V!IlwN=>\?kK}F{h;llՑۻn4<埋g$Y 䧬|ɭ&_גwXO߶Ư,UkWݨ@/C~u߯Xnm{V/~OjRLVJ7Q %NYH/ϟ=l/o>4iB!A޴@3uF_zRQoPɁW,@Kʡ-`%"nGlVj%F9hx*ƔL{P пATX }'N1~/ ij}%y^̧fM:p,2wت@th۷Ĕ\V"hDNDEg:2j+53VV*@ [U:*dA|j__c?~51zN zUT9T3BPh_.K?"5ClU]q#%R!dDP]$Df Bu#Ly,@ [*d>>K )dY @T}ŋ$jF%Y^ML.S(`!/K/BbɷYcZ/'!9%Uې@ T~rO71!^֔#{ڴmؠR"G *?aqW/^hP~*s04 ߴk:UO=ꓥ=Y1pm3d ė2'VP|Z^L@j?dC )ӎy3|[|PEYrʛ/S=z ˝ԬBx2!K=9"#D|M9um>.q|J2߀'_+pǀqk)3zr(vӳyPHCeҹ&iu<~SO0x [Ѱ[֯?\M !@~楜f^b-u#_ѓ1J=GL-" ټZq- K%᭎DKr2l3M?۫"뷟}}+\)%F8uyr#%^WqB@MYjV? !dcYF\samcEh.]"3.JZv,R-#[h)Ia ն}@4 q4Urypb@K@7c$kq=u2 sufN_e'3/\-zJSݹ@تcjDղ{K&bmB7b  =b Ӣ Ȗ=ZK _pu"T(ܖ3& ԭeVJKQZX.½/yk\^WQCjκXecCrg)Fyu|etZfWd! }[PP"Eۗ-wNw*%XN҈:|`,,y%Ouv7GLeTjqV+mBP(Pa=Pj-:rS)P(ܢ2Cd4E­/ym\^WACjκ4`a*mʥ4E!0vSJ3l]yL#5gdVFm*C[NӝI/|@Ezz :X7n`%ӆ=>rl+6LYFp,ONL]R_B*N(D|tG!T(J R筢2V6ShŸmfS,u !Pqm7b!*Ьr] !p]A4u.d^>tn>Fq{N3;mSIDAT6 j$R)CgLs ov,9lGBݒf oXY.ߘ~=sN;}dwo"P2%mًğ$q{ ga:XCPBX UTʆP:ԹFs8}*e0Qa :4rik^lH SIe2Z8 ,TB_wؑĺARYC6U_mI :I(1ԣ|FCRGd2)8/' B8\&JL! 0BH`@lT&|PA=y @!B} BuB0.)1vLX j#s0Ӏ4g Bb@x1&:K %E;gf2SgI@ 1L4Ƙ@A 1#.xr !DGz Bu1!weJg@8G1 ՟G?K %"Jg)H AE i<~(Jq} 2LXx-m@T*o l6߾s۴iV2@yY0μz`|:!QY!=z!M7rX';T) F "1w/GY?Oj"֪(g˻}=4!>TI?10 {7H_T@̹.]",[D-/ _oF Mgߚ_q> }^TO}0aUQgi@ZGM? ^=Wqp&񗝗2ڭ}`=Ӻmcod>@J$9%~Zzz Bf'""#RhLKKrr`yV.:AkVEa )#]6,1&D=c[C9wᴟ֞k~PҾi׮P<jHΛ-XpL_P̏KN!0–ۻo=ْ?~]0S?e–U_,o\V'˙[5eKn4ze ɎoW\0:"Q4Be{޽!!6d2;ؿ'BCBi2 Z6j5qp,8-@1bYk|Y['*P 0xdF;n݊?\a:} #8馋~{﵅"Z=!J~c࢟$zcZ;t W_ao?>tτzx:Vt$xo j_qPXMq}FEQzq!$4MGE9vh|lLɵXMqoߺhAwKi, αm# c'/hڶZuJtĕ /=K <<<ϖunߎ xT"xL%Ҷi`!Ac-K=;jJ̉6ܲ2<#*Eeirr_] ]z]z/;'(egiڼ\%oӪEVv%C4X.όv=˼^VfFe\!Agd􆖭X-*ZVeOjBCKjDFmvŠycYU^QǏ(Nᅴg CQV:|n*̤+rF$%נAÐP@\~QD ӊiҒ,0덆233 FcȳF ]+\ָ(уiYFFզZ %`WH{@x@ jiIpPl *\&畒U=˸Bt@xH@ * \^UC!թD2N%*-JJT n %Bt@ | Y@-Dg %Bt@ | Y@-Dg %Bt@ |bӣrIENDB`kvpm-0.8.6/docbook/vgchange.png0000644000175000017500000015462211733530415016652 0ustar benscottbenscottPNG  IHDR7Z bKGD pHYs  ~IDATx]u|-Pŋ{ @ ZšwB%'g\g?ٛyf潷ofޠ,--!-Sg#GH($9%ktEt8I7'B?I6JTcI&=xKn❢tb"x HD韜~ ْZz+SD= e7=뽿-[塹}wi@d `CblUI +-g88$fw`K9<' fV9]/z{ &#alk&RO҆ܣpK_A`7އn>pK$>;,G":q4];˘:q r X+'sHHO+E)@%TUݣ1Yvh>wň{H@=&/:A| B|ddYQI)!> uhJ3a;*rE pOŌ>};>~6 DF_H4JRUn_ de? D̆௉Ѓ{ͽz{#rqvd]o}X7}f*O__Ӝ D^Ke|/p }AY{aR~X|[|:|%@v_"^>Oq}7-'$_TRKr3A|IY@,;x3߁zfw.[o R7iͧ{h]:w);0v@ԬLzhfj~"1I$"'eLKJҎU?~%]:R)y{1:U .}znXD^Wy@~ -G }8f;dR]4pxF]淹S[o!.brZj8t+ 7nj㰪y*A.'6w#QAHTYW-C"'C2oyWe98ygYTZoY~Z76pßU7 &\49Ȼ{mqߴ,}9pkrHDw.;Fc IK#6z6~b_)x#Zd[vqgnG cM탃&FܯwåX|0sCY FEI˖Rvf4E{1 Ӂ/|i90GF2qpmz}7qu,&76p`[|4 9v$CFo}=р:ZnFD^G>l +e=PR,Õo 4/3އp F\W} n>kO~Y/pK$xQd\piZJ3Xfmҍ;=|Ȟgtp,[4Dh+0|Ѳ{tD-Ux7׽{? K["dl\<ʮɸzucg95hkM>:UGW~#ٳgn;/N{x>K쾘ό4Ѿ_݂:̿`}`%5笎|k~|ېx$b %=J=2/C_ P' 6 aeF0ZҖT]jd|3"vԗ /nz;Yr} ~gV~]Bʪ 67N|~$mj˸7j!-#-+~=ff0-;1Ö<@781z'^~}^܏n|f=d3Y_9WϞl#@-ۢ'r}|B={ e9\8×1l8 /&ٳ0sԣg1pCǦ^\߲5ic]_>s>={}yLٳ}nßus)cٳW3P,~FճWnzv2'>1ٱԜLۺߴg=S@޽#ݧ|D@>}z?-ӧσ61xO>Йo5K4뒖a;xqٸ_w COȕsalƾ}7d`t˖B|зetd9~w;Jk7B'̘rӟ];C>{zcԍp|z;?n Ud_<~< l>Nu{aߛ}~K;b8F~}߷Y qll7cF~ÂgkgW,بO1RэˎW[̆uoHa&qи /w>k>Wiܑ-_?קv>B浫Z=V}:- 춅>`Y7LK?0}Qk/y\+&`ٰ cdxBEH-__]Py䱯^q՜ᖭ xS:@P?pqv?eW~4%,w{O}q\_GwBEg/]˟,uE=tK/OڋtG3?. Wtm5ylSN~bzh2'&+6{;w N֨[JZi)oL:QTC9{Jyxoٷ.\S)L&Bvc3Z*(ot|HBYk)<5SBiS6w8tMEO9J|S[d%p T|?|Cj}c[.(fsD8_~>R׺s 0p|;ҝ L^" cӦ\mr8g0^o=nxFLZx.0g-_bRC;M {, !Bu416@>-*gr,+2L1mR:U  IJi}1=NN(kuԄQ>:vwώ'r-~{fYo_zŗrٵ"E[ܨ;C¡E;Q2)YTzqW5CP H&e~+ J||tl EQͨQ)ܾZWԦuE&&3Gy7戃"ӢḋB>8@lAx+/$~g_)fLOY ]3,CN01&\N9|G ! 4qZAX-TSS'PEd:Jd{;g3Էsr+TKuhYP X>߉6V2$Te%HBHIm ՅU[{!c@+5v< ;a['v'\ؒ6NQ[V38TJy% Խ[jR)8[@ذ̇.ĺ&c+JZҼZモ'hv&/T,!1يmq}]|md/V*BiP<$}]=y?I!PTm5Ξ߼ߑ66τ'BybhjZ }Q5W ,) !Z#uE:8'p4㉥LItng2fߧ7W2q|<#YvMeXpf@@IG2"@, 4Az XYoD~5wG\4TF?5'x0nsy|m"Y }MY|b.;裥~#3I]N/Yq7\:,q3n?VҬ-߼9wљsEȾ>Yzl)?ǿe!}K,:Tj\47SQr.ѧ/?TfEIm>YeG]jcÖ>i =;wmxx4(U͈UyU\ =#BcF$xEc$!w0 I ͆]/33&Lm@w]m1ܾm=x⧛r+-PNP^g}=-H=6CAeBoq&w/e{,2:Ci)!CW0g(>T A*p͕ ?uP!N<;sh3*jKw|1_PXI2u^{@wyuȓzNu.Z>OLF(`Z#յ}pϬk@k5;NlzrcchRwf] gmge+6Y>\٧:u.t lw~oWu/&F٣ۀ2Vf5]IwOݙ@mnwMVhsV2uSoVI6S2ڤ$@Y'i+T>}>~̞sNcjɚ/^K+ Kw:Ƚj:W{AP4`j),m tyO& Y8+"qS?Lh4*2w>?p$kM=>H{8}gHMɮ^UcnZװIs$n#pD̪`]o _/%oxMV ]挷)oJߋ _IVo~lK7<Σ4'6& Q#^@IjكC]7 Բ~In'B A<~;YK F )16Yo0}鹘bGdžΛ헠L@pyol4>0@mzィnpu/uHN-Ss_)j֛tD,~<փ5T H^k|N y"@C1ji 62PCH{9h4R`S8e&]zՊX.eBOQ+uTT&9jdu2*2үzA***4MiIQs2 t:j G/9+ &!&."<,eY3g1f cL[DSpWﻡD%_.!((L}yPLdg_ c`_sil!ɼТHA<41;cϘv}XV(..^V7p`(..B HвZ@\\];KAT0L%E BzZGgg,5V>ukPVZJBTegByRc4崹((FOԜ,Q= 1UUFd4Nj0P|Bb(~LMM3 &Ei#&j\jetEbp5j++fMAA,Xkj#\4`}FAh\.d4,#];KALD+)\A]AHAjL Jb?r ~FTa7̽>;w}mȃ~߫xΝ;߽It7饉7Ϟ6kΝ{cz)O={uܹgLu;WxͽCZYlmjحK /"@CO8 {?*#}=Vc2o ?~d^ܹ~ΰ{9&B^}[=ۗ?퍷}}Nwm8pA_0d-;E[aި_kZg/;5vm;dLӵjU8xw ?,?]]sȆꆂ\lF>z%4{.]t7C6,^Ys6bv"ApaaG罰>w;wkSJֲ܋<s+?4ED?~غSѩR ?| pn>ٲ8.XA+3F0.zK遟tݘL̈́!w]hZ7o~1\R+8s%;fU]R~ꂙHG8CK_g/2_76SM*(;Μ-yí'v6GM79_rY\UgjPd4Zٚzu>~ LJ3ѿbhg E?y6cc؟J@.<; I{vnGg e&hMӭ}!ZG*&sb^ݻ~GUF~#V&xGEvԠKl꺲t?"zы^M|(c|OmZOʺqҒ VF Wyeگ`p~gQ\!a""ׯ[ۮ};h׶~:bDeUߏM a#7QO_6mZK)!N6eb+h[PPPĺ-qXsUÿ_BOeǿs-\Ynn~hBtEOV2 r<6-E#N>拺rٯG0)( b4X꣧O%#kv/qDڏ_O r3BLĨhj- "5/bFꞢ CĭC1jJQd,҃)(HOE/#z WnZ DE˕J&XrPPPaQCO<ϛLԴ4J*RjѲe!EA\X7u']r:A^w.6pܔϲ iZ?q:}6;p۽{A^t6޽;evdrG;7|4ʴ!7_q'w֥dx1W-ǍVVy(ƌo-TYqkNo'}OŬ+'qoG9gq9cʊ;^xbs\zHyd>:F~Uv9 ۽{yB~jjdbYVlSS_ْ7;,ڳ톧l)Y:饱+X~8nǹaTdbGεwGm+:SdκO= Yj?\i_d8˱Ƴi* S2d U+D((]2{\?%Յ>fFsZb]k I6<)޷K#֋ƽ`"X[Z㲊4`DIuUEVg󣣢fQ%e}q윑Ѭ }h#Ԉ-M:)164~M6:u駟y{}Ï<ڷooNu3HFI~/?x)15c0xeEvz Yx o֯;?cbW]_s{'XKskpT( Ƃ>0&CD;'O{TK0FڎS_ttn?p7եg5 ,;{0b9Ũ|+(k~GNXtF lr=π8d5YB T_aF5rgm--Xg̳xUCKf]$GKJv/LD^)&!H[#KK|w=mnkCM&3Dzz]Wl6;&Y|)x.|a=q٪O ^q}ݺ0Ķ릏=5zܷp=MGmMouճ{3>V&"s7~ݺw{=o(@|7mڴ{zqfΜv_2;aEmڳg={ٻgӼWn8/2?̸qѬ_Y,e` :ჺFqQ]j 5+\ӜŇ-C,1h.eo?zBg-07s-0[fEK~w*0`g Z6k1ty(^>Hu벃5>;em&?j`*=WgqMyEƘŵ-y?X<>*2^}-=w+Kԩ0[F y>9%套^(/G0!@|q>ݚSjF{ޕkO}G;.N;E\B>QBuYwn5[yyL~=rO.iuQÄ7G;+?8]Nw|϶~0Ld{Io|˽2~q_ZR'a#?7'v ߌq1 q]ݸhTyշ^0O}Ս}%M0 kڶkr8'UYQ؏Vɓ;tqleE9,ؐ?OZ-&Q|DdzѶm6Y{GzS8%=OwR4=kzʲl\B[pfcAŲܵj&`y^8ٌ1cG=~@zV́dW)LXb#eJö9rOu!j?uaᑺ*BEͷ./S ,4,L"w(PmM!u5P_ȤmtEQ R QMG1!1D.4x70dq`Yd6[!" HQmy;͜ƙOxeen)?$L֋9P! )!D-crTIV Q[$_q\TTTiiyٞ[6,‘rQQё#5jUTTTC5ޭO{ua@԰.~B HѬ4PK5(2NVT\V,B6m< vk8gDߥgZsϷoj8L3$*3_cT*juPթgoZY%2<,,)1)9%YnW5/ n,e,-s_tT)ssszvi6 t'`.^,6jMr\s`Td$GLGoyj7UGkm|p'UmRvWuzϚ2yBӯ|cXzWzE"+!hO7$W{ժ0Bє~ +XOUC+DƚM:P&cJ`2ܿk:FFF>nP;"|N+])_YN. &ӠNt8vypCoCVm +6[O1Y)J@̎2BZj'.q~O.h2=u36L&kXӿ_,VLB 'ryYy[h/]r n^]`z4ZMjZ* ؼeF̌WfBQq֩&1%9955U]{P"KzEisX^6k q"ALXhXfFfhhXqQÇݹe5MtTtfFF\lZfCvHCNƒ+prTSӜ.}21NpfF*ؘh4yXNV@nV6PP\jDjga9 jc@a חK3%:.;6E**pfP@;\hYv\C?R E)rӀbڅB]^I]1hQP\mQ+CP|b+J f$(G qigm:\dˋ )u2jC'ǩA0Vc0L&cFSkXsJY3HA0L1L&ZR*h 4yZkѢ,Df4*m'IBI-ZZ,  KAAAhp|yyELFCJLFZMIM>u222B[ 7ب7[]jrE7lZO ;:z 栆Aְc\QQ*%h:4z]i*\A~TTøob?7O {zǓƒuK4ꁟoH剂G <$d2 d^WM <6L"? e[;J+ mg"lZ!={9tk˳ Bt/^1w=OXAO{jP=۬'DmZ´A=z0ѹsrMPK>D?v`={ %iZ@ e>#E3V62!rHMttikL?n'l]jU^.WCرGm8en7_K/zѫ/8G.]/LHUUUxDb!@Yҗl„0UXQ%g6gftQ)c4?-U%A)cLQz\ e;OL&Z4- K+BER>U\ȗò-~h@mX7cq^G83-"R#exL/>Lѵb2MtTH@!rF"cXZTtUw+^\Q(;>|KAAшhXo>2)}[W>11{Ä++rJׇz1Re<\$fbCUUyeA#@!ZӳG9\8p`ddQ}ۿ>gd:ٱ PuzdԨ:foÝ<-T:NA_Je>W9$O=g9rWe 2.sQrҫOec) ݧ{j՚=v⤢unz7)X{)//?|h=CdY+*rp;DFF6mZ?EO_0Ltl5ڶm+;ɓG./-GB y!EΩ#",2}֕qبk'$52tEze/_ֺuzEZmb|m[o2TPzBIc4V4j<7 N!'kNjyNu%eKI*1_.j[ ,SP4 4dqz~kn1y'#XNPܹeRxQg^?><:u]=Աy;W1z誫7۠UHfy ́s-YtjI HchԴT-Ќ̸8\nX &CJVӠ8Yv0kny-T\A01 F.64Q_;=1u松((|Fza223a%GڵK Dz&:**=#-.6VVEhzcIZQB0a:5O hIsn)(:J#BFPDF ˱*ZRTJ[JBO5j8_X8e*Ntpٖ{{IJg{b3l̢ËJ,xm&k3Q 7'@Y>nZܥ k+32`rMNڱts۬?u yp.:ٷהt_L8iY-Xk35#ܲD"1CAỞr5QkDžjZ ضa9J`9cUF`2Yw}HEZmepI=7'80xhO2|]4}pB,zSm5s<SCW7E0bW7vZ ;>w^:[.;= gg85),NHAO~& v ?089$v<6=BeЦDc ؛ȕ>1UZQjN3|ee1Oy;99wz4iέ=967>x{1 ʌ;=&{rtǦ<6Cڕ/=i~޷:q~WZVfp)E%9KS* ?gSB+i_gvB9`T!rX1FP9^\Xł'>?]z͔zlH<㊂Klw->Ί}: `'KkY vCS8]bY_:ѿ[y$naTgq-E  -ƶv:kC'0YGp,7k?:n|u!^ުOU{&vYTa8F0[0Q!KJ&)ϟ<6|:]mKz#&4)ß|;Ď>[θW~cX5u-&G}q݅܄:gZ,@LY8" U ?4`^ w 9IjFh WX38L$u]`7X2SFBa7ݒ]n1و#eTv1O}ոy>wӐ>=U/첻͇+IA9_ ѱ:]W:]UtLޠ'ë]H,9nxzQs~nwoZ=gx·sɃ:YP|k=J1w" ˩oo^-/~OE9%[vF:uWsDAA$!-}GnWUUY,f|X,Ȫ*b# 'l6=ONѝ'MK/=x΢Ҷ|IPocE[zUl_)Omh7$S)j-=U֣-fߺ!2((((+&A0bAłlM/\601;JC0(=-a^ DfHʹE R&<3j՟,y>yH%Bn[@`C饒]V(~cB7:QP4-6Xy1fh2\e{aKɈɡ rvHꨋj?1߫E 5fCZ?4w$]7!J8 帢b܎"{ɠwܵs箝;wX HFt9~](.}߬E:Gy0Ί_~j`.͛39a J͑+^}EnvJr5kzM줭B\;gڿR%= {ı%籪;9{=#pWMj 8E3:zp舘VkvvθISΟ=PԩSƌ/)<{)/8rh2l6IU*&ś{;tA{Al\BViԩS#GX|hr=K\z,2e233 jY~|h 6jq ;mayBJvǶmiiZ- а0r鯺~dC;iԺ[ dC  AN&ѷuaZV ];wHJd2w~+̮;4BRP4s=u4~ ǣjurr lV/X5 rFCrV)jZt;y;oG b놂%k ÄgdhCCKK9;^`9VDEFƪU*(7S0VQ*11d4 RRTJ1S ǃG(((J H-@ 2?j'c=8Nj51B0 5(M @2$&)((]O@/X_ aRDl:|7Eg=D|:_;8n٬9M~hzx,tHHj+)(^^H c,XV L0,ү"C+/J P;_6 ÆGFi4xUe Jꆂ9o5t[9 <7ZJdF`1lH,e+r\Z{aÓj*~ X7SP4EX7kiM#)fbe\Ued:]uTl`6U*ϸb7c~1,1ɿM]*lyݝ/0c^3FERq}Wo' @fsrZZW\T$QV%H:4P%a1n^\=wzBtѱ뙱.*6Vo])[>s}Vcv-\yÊYCK{wi>OG>[9oظŮ8m+~%]OUU1qqj^ ut:^UqZ/{c

{NZRΘh&D(ue2^27cYF -l6m?y qlv}!eXL=nu#.#<&V]1,%="S"w@!gLf,`0L&[T?n߶eovzJ*T7tNH 8RB օ>t`&3"@fm o٫EڃeGD;&NU_f-|ٙrM0_}6pFeɓV+T({W/Qp Oiy@wNYr¶wvݵse2m5}fm=ΧF 7P7mS'~6"b/y?}%!^HHLq@wxZ0'Sh.^_ Ĕrm3*WUJb|'KA#3gDqW\Tr-Ĺ"!1(EjkM}K>џQ<}xo+VԘ+-[v0Vye;A8s-[um&g>/泙;`P;]4]kێ\0ou,g៛6gζ s?ͺ-WVݻ nv>^G~OzOo,G!w>}ҍɌߟ ,ǿx~߸qV6BUDC`b? b RۣgrBsPXAP;=0ogͿ&6݈;Q`n7.,枷n֓=GO9quLOc0+kcH /U{|·fv2GʵTO(o{h|H|zhą0BU љ&? @M;M-(sfL;'kDۮkʐX[TPy{a;Vomɳ r.4{Z(nZH2dtzeCRS$ b]hڳtzBa6IzRYQVQ]]ݢE"b 3{=NU?piß̉~?Uk»vgZ;ct݈옵O zwض^Kh*p~o|ygZX/7ݪkilhB(SKȢp|gO}W8W:Rjr!,U BprUļLઃ~8Ye5dz.4bmHAtDzW j. } piؘm[66\Jm[S5j54!v_T<`bV${n$|jaA1}}̞~yS )h;Պgqҳs'6#s.FGOR%!y X_ĨP jUKlSD˭@$Ӄod)xET%L^^g;c{X.&&:2"bUzA a@ bBBBPNv訨h9EA`<% IGN{On+˜I84geޔv]acE~ʛ˲~ bB^w/'Űw~ؖSaamTH!.MagBJZї'#}HRvtǿxL#Yz]4Գ~I_پ"njMV-?X-5 z]-[jVkqqi?oy] @BgD#הΟ|)/2g}O<Ǥ{k0z)ϼ} ?e:o̤/zZKTm&<{oPw~<@}ͽye#_H6ptIP^s;wz L>Ʀ̮},^#Y ݾ|Nvĩc\^QshXzҲ2 ˲j:***&&:&:FR`XAڰȈ6ZYYYO(!QRfm0 E A=7M 'BpF<󂐑n2JKh'5[(Դ,GwW3[zp4 AOOdEFDgRPP4(_]^/82֝ BEEF--.?FcTl\EEe6u<lXL;)AuJjäWbB,f DAfu}JMGe+)/`@PP460.n#RС.lvGGOA !EP9_hL`:=֚RN_6yO>WK,Ƚ7@h*5BfhOO唂 f(NME.•O<}tz1@t>|haĿEGmo-~FѲm=;G}arrE;^|nj.n!4sO>6ڴ˦,4oXbuD[} w=D8Zqci ďw:ϟǷe鯶_M}}?A^_ݳ4`]3/of:_뫙s׼ß)/VO`J,s6n+^~vJK7bn1QQI)(D}4_L #F_f=i~bس]짰'={ګ:Y9:f= ݫk-V,Q7٧t0`Mߵߪ9UXѢ?3 m$zG+|qϟ~r_N{>O} 1C:jv?5GIH΂W2ݽ~ןMIb5s_&A[k S }=J_2kH$W[[߾ynȩ̡w>4O*bwޔo'ӻdmf%Nʸ뉛MuvHݟ뉛cuw4%5ҖMO]w|ݹ'jW$:yC81ŏs:?ͽ)d(OVcѻ78kYKug>ڍg:_sêIo;+}zӛ6f9z9pm;cpւM0,Oi8&w~oʥc_u7~]MtLnY3]37B}D5>uVK OAo0/{벸 \.ۗ?AXnQEYw=1dC^?䩝F,mB-N񿛣n̳ ;)V-Gw;1HaԙiB^M=ZE9NղߌZnϷ RMIj_2؝c6W֭r*l Y{OiXMD*ŒNć'wV*Ů]*kkr;D('=0^cJrʁ[yҴ&wV(xNzׇr^GOh77׍Bb*3`2ƍ`Ms%gV5⻺Ĩ6iTQv/ Ycİo\uhǟؑ]VcOfpg5GÉV+}՘LJ%1 v݅ ⴖU0"o?I/B"T bsxz01#8pA; )pQvQ)a@ =] .ЇR쿋Ǻyo،[@j-uh9ջ+;oJ*| Šun(/npk7npYMذUyv:m^̮P& q wSrR+4Ì6NQWf4Ju7SV/ϯ]p`[R/ye@ٲ3UP"`etElK|p (#L߼4gݑ RUpE08$~K}} !D^1L.W Sɡ 4&"\VUdl$a}wCx8op6C?^Ɇ']ĝ?Yzf7Ps;OguGP$ͼ]~l*?ӕ>CRBD^cB$,m #ufaJaq=E3F}9>~NFN[:Ͽ|Kk5G* Y̷yL>#zq[ze;ּ{#mʀ1CT?Rҽc~@׾rV?5F@]Db`bwOh9SGΛ ꤮~﮶ Ou0ze0ETzf~tX?ȼ^/M_M yw{0w:s{OOơɧαNֶ8)gfҔM(4mOrN~ݙ_([ ;?e"K׏C{jssOOyf!Faكa踄dfdd;[?Nғe6Y ,4Tm4Jn~TZ\DEAq qOB@5_]U*d4͗>g11g#"8,GYjBaLA Qz~qAqױVgAT(/ƨUaӉWTuko:5䮧lm]/6dȓ; Xצ0Lf5aǓÆ?P+TָO0`b8͐Cj{wE7uu(((*m4Hpu=^IqKtm"䶟Ws e,R 3˖YvHk V`yo@׾}UJo.i&A]IDd((!"z*e!#zk|ٶҏ}b\KnT\p<9E6~VcP-C@0OO|fۆc ȣ:1YUliPPP\Uxy%87: |1o8g!Q= |zw}'>>ܹs?B֭_~%ڹ] E.?=`O=7ء3clk! \vϵcsAxձCg%Ԡ sLJ b;0\aACL&kE@0n5xO?=7zBCC}=A?x !c]Ī ׏ <,ib{-Ce[h1ysZ2(eQ- TzMdy%SP49\N22Q!YL`}k5g/ؖ DhTMTTĜ>*..y}9͉tLfs\[ v?|'{uY/n>[{ڤ!DyYAȽyjӋ^jʋ`kMfacG!0#Y'>2g{#7oR7Rץ+Ïgn|I øl&깡hRi+bK0s}no._Zǰ{׌ⵞFTVg{߀.;9nձp |Kobn7_yC*- N!c r}[?bA )(zڔnAl M2'X$ |EzyvW!"y+q $<ضgE3zJ0>JaY{z zeBh!DoWOmKX0RPPP46` '̀ P#OAAA @3;U7 nh\ Ӟ}A >z &@|qA`äaW0`uCAAAAѸiX 63+)(((((A =ΪRPPP\J00VڂWd,xޠHuN2ofy6$$1!IPA{ forGuLΓ7 tNWRUI_W!TjMjZfvIX:BRh?PYYٺm 6=U 61LcTG(\<ÿ UUJbӸ^~OLƱ, L0 UUZuؒDNyn&fpӜ6gy@LTLRlwcQPb!8RPi]ⲑV>%-h],JT[9}8rv.[FtU C^X :AUA 3_@^W%/=AtJ{snN pg,"za^RTx`Ш5UUĨn7q򄻞Wۂjz]9aK3_CKJ%pPպtJ{jR 4b\+ޘ$cŌ{}y=bl1vtQU~%SfCJ.]G77$HWGs~tO_0I*[omn@Rv0 }֚]#+{C2T߾ib3pQn="0y_ێi5=ә}s&y|K7^jd׏ hUsʣkvm@{es'y?nӁ6mG@`⽫~F/5@{M{U3X¥hjv.sj-K},{j b+_.}g ?{^wعcVQLׯ}640:ȘFU* *4,VV@H8ZNG)?X/1q'n_:Y/W/OpOvjǼ/ѽ_NOL'zod#oqb9}xޏK}kRȺ6`$ә<2וKe:;?}᫟t١}\ 1}n>yN]/VMc믗椁O+dАc 4tS(U^q^ݢԮ%o( BY[N'_p:B]2Dok:IC\l ./$\U#W1"7=]GqcJű" Ug?xrBP9˨zLYtzK{71u<$wM &F2GqA*/ȓ>,3\d{.B.h?QN[DhER?1"!=R<˧X5FZ}!W6 1TA\tH+cTm 6>n-9C3M& #TJڥPȍ_-R'}ԬNtpaInL"yRa@͸@%G@\}l՗߯}v (Y%=ZDG K =:zNBx &N[[fmp6YD+唁'. U?"6UlxPIעA(KV.9vnqQ $Z6Nw "4Z>loyE[Z=5I8OG7) V_-sr*)u^j؋kĘT$vI,xjd\Bp:g?2J*7=_VПA_uB=G;RQTڒU1j@@#ær#g*WTc5sXīDt_? L@MN rM}rڜYz۵dg(! 2v$$̥ j.GonS{_.x꯾?jP^gdrJԔc/&ɓw3S55Y|b{2:g+_"oy1׵Vw5z񊚚3u*w8V\z⏯6߿]wFu5=}&^3S~Gu;rY&IDAT"CJ "LVw_Gr~өmW* "Ri >֮舾 \7H]潋?Ef&""'`b/̻bigu)Dn>ջ3u:eN!KYY<'!q8L˧D^@gor;wuʀ/l M;6yRgײj54GP\ho7g(\ETA/aV CӉC(݄A3j/;pB꒯PBm|ܤH(˃d2 /uR/=5򆏼tVnC8sHci\G>33Q:֌*nug8`X6.!k\uJ{J{kDj'-u34؛XhTcG(aa4¹ǎjTj1Mux矶䖙-e'V/tmVFy+ݕYTZLU4* -퍡 S;ORn @ -ƶ;FGGI,)-=y"D#L}%~h]U-{MzffϭKl|GAwgSݚu=,Q4AjA g򆏼t!|ᅒ:ml\LRBbXh(Mp`X6._kWn.YGS%u^S5rPxX\.1RɔJFQ*T)Z NGS&uR7MD$JB!dk AFKB*K7]Uc"RiPq@yPP4< Ph< ο*W/-8{#/u A"R' 憆;x"`e놂bPP,uWRP4:BN .)(x#/uRh(=cly blahWQ\):w>z#: G16PmhJ8&}u5DPPQp >z# %u<7bbbb ҒҪ*3xAr兊{ @PrЋk52MR _FG G"j.e(/5LA֖ȅuRc* Ob^@109DŽZ%Ĺ,Olj1 1W)r&3 1T ) &$(XgjT\Ғ#SǴjƌbTq!EUVʢjyl$P?MQJLbTIQ NQ"ݒ X*R8Ę#<HLIs<41%(u/$:¨fF %YGܽ@VAyXzh(F"%Nzp$ݤIaխRBbq ٳ+0 5Cj,5 F+UUccaK J ?}4kSBPQj 5a_T\d'hC4›kLEv&(IЧj,mz$S:od2PQpL?3HMI2abdCR-j3;gڄFb)eꇨd.Mr(4A!-3[!VSمS\k7AI$ 36 ׊gM4KRcAaaآbqqq]jŌ1Yh\&.#5lAE(ej.:\Za4icNt0/˩Tʈ-TJH + TG(} bT*U<$""d6fAXU*JBTp,MqEApW@Ʋ2ZV-$@CvP2J# uRh /ըTG( ; (U#/uRW.X : 4놂jVI#A0EAq S /uku)(S\RP>z1j-8aƤR}TG(/u d6#iBTJZd4ucBAE*FF_5u@`yvz.t^l( :N]]Pfi@ e!j 0DxSŹP6.=EDLHm}"6^Â\ޙ3ʈ8-LBeg& eF8uThoXt&7,pKUPqPqb,;}D_!Ii㵜ٙ6!Zվ }ޱ. zCDdDUe\p#+GT#sySxr \{BӲML ܳN\퀂+iS:G`k[kl֩uP'<D>B!b#z|k\.`(>q^%YÀ;] ķSSg35A!1m@BC7fxP2 -4a, %eLt{TcF.ϱ$*r>Q־k)=z$j Z˓;􈐣CIQhцr/Xln1ESy%.VC):΃`S1] rOWuN7TQ 4ӹ}se;Xعm @@B8*ii4;xk$#t7 ,spxb_w _]]f1oX,Zmhuu Dɣdesll `h3;%YDD$ BuهObZKVq2pz\o\$PRķp8uBr,[q۞-cb8U|8K\3A|r|hB"E!Q1 VUD f$,EeB|aaJ"H$b5-[%2 ^"j2V#CژXe$H,syhUV*V!S'e*.) ?87<13-Z}t߮=f-7 "K˃xŝ;N4 ﻌykIKT?g׊{clXV kX0ƮlD lkZ6 "x]Q^AIj?tYB1_,6q1TL-7ט+1q2HXwCܘ$op,SȰZuz:'j1VGf҆D(.D@yA"b`F.9bX@ cVoIV2=¹MVV*5%2D7EKVڟ#"%V;NUe*]ȣ`L 6,41-4y9lv A.82ZJG4ς!-uA6,` q%c\c"@` Ō1YPkUD 85[H`(+a;)d,cGkurSLFx;Q2$Mj ,M醸3I! o%b1YYX#TC!V{F_q MP;Wj@I q  k{=lINJ)wS++qRk}XLV&ĩW!Ƭ؂ǯVWTVn&q Briy2&}C\uG\GI"Pf@6U5؋e,z,eYX̂U[Fբcמ$)!  V}Q=)Gm%/9Qh FI *&TyLA5v.#A1&aBQAq V}aA`00\TpQ_BQƄEU5J/\|bB66zܙBxE[mPP/<[hDK$֝9STa koVu!%+Y`5âͫ ~A7s:,KJL2+CV>%A$*ĭh2Ewt^.i aX,)g;^l6{ +Nej #/[ *8?UDD*cb&$";H2?U3ӓ4QN99a1 K&g^Lqpb w&^yʣ]iɱc$h4#Zf|?~USUo,4[ǒFJ$g556XuQ3ӽJc[_k/!S'zQ|NO67[ZRݒ0uz~=\Rk؂,Z-ǎrWuR~𢄆/ĭe-^MU(Jc1ZdmdVmdnqBgf*1aeysuMbdcMaYQ-GpƀBVvCZaժD"z=hUt\zJUvoϢ+=4tN~0Ƴ>fZm$-s[̋ 2k 6[wQ#`PUWz}d40YNM5nfJZBm@ d-nq sƲ;P$fbh,(LV$IJ dH~a ʪ[u!1%e*RNj-7c'3$pmY}-ߌRNS,Rp+@ kAuSX!":p0[ev$z78+[!3cvmҪ[g-xӉosl#*nq} '@YVl#nq!DQ)E}{)s2F+%YƔ.4 EYl>jnq A[AAA ;30EN& "J%PB˲,5ծZV y>  _ Q/{G( Z |3  ,ew`=A C( ʮs,˲]^G(J  F,sEQ{U*AGA-prAѸA:zy,g[adYVvVrʼnƢ>)B Hi%|eoŐ%Lֆáɉ@hs+WCuHn~R[wBP(&!~-ZC)Qo{[ =zVGVEQ^D|)`dKL8oz䏞Z?1r}Pd91?m_{tx9ePm7+c|Ȝ19ןG=pO=ouϿC?9~Яs~= !v?h>6u J=ۿMꧾL85Ƈȯ_n}ѝ{gvI>"v5_/^7?+H,/'Ñ>^{w>с?o#__lq)T7(G{IsMJ+r%Gqb]b{;w6 dPhGOm6 9g1RcщY%տCn, {n>7 \f{Qz7sp0;~b/GFmkw4z7n.שu7M=,eܑk* ݯFCկ ]L8$cK ݱ&ilmw|Vg)B>eϰ|FFmk}oUy?Լ}77YՐ^ }kl'?!xӶw+yPX1s *Bƹ]Mlu葽VX&OdV% x^U7ND e `ss'OϼE6EXJh"fnI\TYJ`;s|6|J,$D([g4,jL񮐲H,/#Â:{}զxgHׇ&ayʂPv-U}c=U}A夰bn݆~M=?>2oC}5\h|]]:UV0}>xOxǗ}w]&ggLfeRhxZOI*u۾Ootkis>?btLJ^@<Dr"ɀ`(;ÊopJ&u}rr\H\-\s_#7 oa&X*O}'x¡_k}]}Rn+뼗 60j'oEdxc?p66~“q%I/Ѥc/Ώ_Sx˟H]? +,Of)cΝ\A,j^#/?K$}œ7kr,*97Yy,ǽOH_, W:_=uCf9f޸-'DpfG 3Wqg~~".V]x~e~anLψ! m{GˏǨo%3_CկVόR˻֟UL{(u[_ ڹwI={3t❏/gzh[һ>pu׿+i,VYUpz[z>#W|3yM wݻ5\hz\Fxpl|R('enn,E#gޗlYydVhǯ7qm{x_<wU*6Y= mK@lM{ 7;83>/N^Kl蕘Lm|ǟg(}Yg oŋVݽGr+N=rP0h4TnNwe.>ox@~CK  ]]<ɤQG4B$ IpAE=v6+!Dj%iBv!sGk+G26zA^gD)qG)q 4p4 .^rAA `'56\Qd2T* 2nO, "IZ@?'@,MO  uŨU3lɚx" Y3##M544!D2'B"ťUs i *w<MXI!)G 6m|tfdunI= (t͡`"k$zMF!mpEQQhGvI,s絀EOH)Ag%+q٩"{޳!^ $M,1^1WYwUPȪiYA29+t $.] ѧ6g9^N2}uvX<m{E`pg (tKGOH>"3uhWg *n!DC t˂dwccᆫ8{HU["^ ĹV^cˑɁTcqdyyY޶є: t$;t,;\I;;ȳ_{~Czb'zƍeqbjlSD_WϨ/fcKYP*j74Wt"mfQ]0.FN\:[7Ti';wluiRҝ>shc=ɬ\\d]>F8m8gsAJLoܵ?wW(;ӟrUG;(`\ JLW}&&>}cT~uVs|iw]𼒳m ON[$Ϛa~:,e[n%珜;wovF>yXH:a!ϙPy63];{۰"* qu-G.zPu 4Nf/Bݦiʮ3Jh4Ti{QT#g:Gm\VOhVOdxP[DY*A>L*&^yiK#c^ʬ\GƦ&I#PX2 MjVj";Zm:H(9j0v^At}#S @#SGrd&PcMCjZs8u`T6UY%jubPB xR檦zXU s$%[g.J`k4K[Ghq #@DHjE+u#GsE8e(ɘI*j%PC2&lp|L e*l9Qo(1;,KN\ 4sdftxZ8PISB9#GᅯSM'"In+.RME[\0oڳŞvS + 8"UTL&xPjPsR\])glnT|}!Jd$A%@$1I O<Uii29pu"% G_wTuثM}#fBX̰@l:,d/#I˭dm^ͬ9eKe dωnOmcnedK_ՎBHݍ*BV.AnE?r߲FtVywȟPX?3؜z W{Fq#Q%@wNd_c (%{"g,.krgTUZTdI5fMxh̗`L=!%b+P7dLI&dtO7`.oU|[gi6&*to0R^-ߛ.?PpoMl׷vyɓP^D9pLuOSdpQH\0Զ7wuHVJv D]\/eF0pr\; gfv#P= ?0lTWiPOadk4P`0y<Tcl`El&"mV, OuV{MsrpQa>y}ǖe[/2{z{H(T^7ž#r&#%V!^x1}v[=y:ڰs)RRApO?DC]ݲ/t,KPvyhoT1oSk 74f{_j]r3 M"Eݾ>Mz4.t(Q`,^_@4l hFb%+ uO M.ʒ}c헌nׄ{iKRlduMID kP|Ѣnb-v̖}a )SZ/L!峒6z9ǧZk0â6z-R,-XV R߄wqmd CU'*Wp$oŢnQEۂYQ|igE,'ѨLa"G)dgEfjhIUp˜u )% 5dwɄs#p$' {}jkZW%kˋMXun U"*3ҍP'XӋI|* auv  #!YoY˥3_u-:G6z`[0M&S<㳌ι ) *\I#Amp([jvhEQ멾.VK,R Jxt܍f@Tzgm?bWlpԔMUn-j-PFS#>uE"c^ZP9{KTr='!@2&1Bz, /-vvGy9DR` Kc@p.?`ALQ2?ٚE\!(TP%&cر=B,296LvZ&K dQޑݬ!3UQ*6.hsS0շTyNF`׬'|[mFhΙ a…;fkD<}t.JB2gax(!H"t85#<׿x}CK ZںJ~9($x8!si\ `,j80ACsGx8IJ@ g Jƕ|#TƊ m,;;07u-&ݮ&""W}.cUQA)u\ V} xEQ ecCSQs&G{ǹp+[Pd8f]>7F"<5WӦxIƒQHeA5,ɱmVUXQdpL9%YH0⁑P{{$nQ뇂+%eϟ3Rꆇ7bTLl۶9z'n:5fQJX8{PTgZ%y޹@@=l ]K;d۟ *™/ MvA״ >tF*Y+k "Mu={*\.}0 O.95  &s j6ՙlP]esMAT } WI|+S=G9XMfq}KW̓u[t-ԋ*jZEH?3Hęljl4Lu-FlY7cs#3ƢFYYQ"h8εf;6oQ\32Ϝ<޲ah̐%u[]1(pQ^&B4| Re%E2uXv[I944e --Zn fs 2[@${Z7nQVY)Fm .#+J ૪".r4u.kdhl6eo3#cUB yhIb"%uu\#>}jpp==`4{z3jg.Z-uK`u)ӽs˜X8dM* {yŋ6u.%.ض\e|,Vs1 JOfsZ7ㇼݷuEgxju__^KlZhQ[ ;=<QDSU-Wf}j7 d{<+TB_$P(dwe $-<֔ǪvaAS59]E󆲈k'ʍmVL#~/xxyֲޔ Q'm RlbXP^ i9aIOO߸?ƨ^Tiǽ1_JnjtT`59uo|#c/<{e[ñs_:s$IΊ\ʼϹ u[P=Z6Zz|U[m"ZhanQ6R2oy(2[-P/`! [̦?UQ8%-K!Xī4oڹLu<hڼOun3Xn9uljGM67PI;FUFe$-ۤQ}GB=]g<]u~,BCamL$~q\ngYT$H/M+\,irrFi׮ݢJgU/<ر\sjMhye{i{Y8_Y1ƃd'b|)dBt%>@ 5U(jzt۵Jg-ɡ8[~J27-+WI 7Wiyt|VֺMjAP+bw]E-Rڭ LB)r<OpAcvZ%9:EQܽ{RZsՂ tvu."-E*1h~?/-.()s8Sؒ##(@8.Nͅp"(BU> c'B;<69;Һ#qa8*J䋥o~x2|i2\06W {NGU\oS_)=&m۷V; 3+JjnKY<B=iGo֬0feu~MJ 29+ Q'2RJIy9!,lpi`\j1J7jƢ! '"HjDJf#+|65%"j5zKJdj[WnMN[1Xd~R5Dd^ ׭ jmf50Wm<;Pek_a6 0>~aŲgU|vNwSK&O= U8BڛM-؅cz'LU4nP_ V꧟|.eϓhJ(Q?-0㻯ڣRk؂cANTWzZ L^j0)?5bӦͧO_L&7oޒጜL&9gOu[ʺ"9ۮt[+ %30!:lH8aȩ)AH@ t؉ f)N/:rݨc`@&' OSSs-09,\Jrb=vulQYN=-=5'pβhcj\.~aIvj*HzށD,rUT\6k1ݽ+3WHȠjwyMRȠGdȃJJfܺ`XrR}E(c@9<(5 5z{;{NGOɊ" NX5UUvM'IR&%?}]]c0UjZaFkI&th  - 8e-궨t8p)]3ew1!nBFr=O$969E1EQAj%I#i%I5:DR(g)o+lˊUU*UR~tEn9SUBR=9|v( ^?w27Y*Hŕgͬ30ƈ ,ѣnQť[8笀dzn%d;%OHlX 9)>`]v !R235% p >#E1Ι@<Ѣnb-dR8lnz)s)a<%QHnmʹ~Ultƪ9wJ"kLѕ&P-Rt%DጐSB8cpɍ>5"38iA"E[ypĖ $5f19'ku*sd]΁A E"ť[ } @0,)o3l-Țm3YD"E[V!1B_A(B.EK2{ݤvɚ\A().r)cwAVc{H=  Jʒ|MAšAduaJ!s,@9nAdᜥ'S>LA4AՆs[s$Sp#\ Y葆 rY = R\`G R`G R`G R`G R`G R`G R`G Rx dWL&"*JdJW&d 2'(4jB FEQU""nծE#,2`G EQRSWǃ`QYRWTV BN٨(̡.uUjGQ|&ĎA`6cbsTq6}thj,,QÆX,[fRs,\CF#Yxe=>f..@ h2Ӈzo%=?wt 2r4ZYLVG҇sS/EcCuu*j2JĖ.Q熿}fp: !#G:/|AW csǽG3 1_pe=#f|.H$rC>_!=x|zo?ΨTQ[_mUP޾*W"{&5uj-*3K9p>Kӝwjt:_ hSSmfm}m?C=|M}'>>>\s-VW˩Fd}4;ҡ͛6;NEarFڛL{Vco # C F2cK, |u;għyk{۷! G=lu>Lo͇1xhF;zYG3 97BءGuz]0Dh.FU+*P*gG]l*3%N75(,jy|:R x?n}*E?k*ؐ蝶{g`o^;oaPm):u1D~G7^} \OQKxb셇}ܱߴҡrP0z{zvf2s\s)Ծjmjaq!0HwvMt+_u6T¹>:sֻ驱!Op`&"G EC2å-Oݱ(R);\CQケŦLuGz}Шt׽-5G=y4jC xsT?a~~=nddl*nvIEay゙ rpDci{{Y}mNX2Njrum-D~[BxR;Bo 1ߏi\]Ktnz53$zi͟JVM9c076eRpحMu^/}vgzWxd~"9?hAuD>6m[H\QeY0>e&ŒGPnJbƒ2c8jkeC8?L\R`B41bR)>JNMAE(uC}ͮGݱ`"=ogÌLEZdS7ʌ2?W_Ūv)OS##̘X}5;1L$Swg  Fʺdm[9zlrr9nټfٹmyH$'*Uxj9cDevh߀Gu\B8#[w呈Py2(7ҦMA'4AzT_;siy߽{_Qsoz+47{O?}9 9 _y#K}>1 g\~졇S#a.o-pHVc!>Տ ,˽kfphᣔ@ \ꪚ/X_>S,}Wt@j~[*TBzQeU~|S*5 LVpD %m޵c`pf^rjDcc''BMQ_(s2 = %&Q]r"YdJ$^'i}\Sչ2 A V+IRxB!TU2`G X@@)qG)qG)qG)qG)qG)qG)qG)qG)qG)qG)q?ـ%IENDB`kvpm-0.8.6/docbook/main.png0000644000175000017500000017550311733530415016015 0ustar benscottbenscottPNG  IHDRhobKGD pHYs  ~IDATxu\ǟTBQ@;[[؝J;8`8J_N<;;;gf`3AA IHB"j#ZD}5Mס3&‹Ь̈/AraVԞWXEL8IhF)!M3(,KOOq / TH?u @ D{Q*:i\LoLaE_Z "w߅ݽG ,ULnݺvA LwA eE.4"Uj33ʼF!P:1D;rNJT4j H /yhD.`J*(߭Pހy^FSɍ2'(i\ PDE7}|v|@y[aO@n't S |'hD;/\FTbBӹ t4H=п s^5'a2[/TPоhaRPrMLUŭ?T"326* wT#E$I  I0"AA $AJA @ @r5_N.HIRJm.'!DDD)4: M$cg:y☙}r)abum*D"$>5(f0?t͓@ edQ/*M Pm*$ RDTRA4G+2Rȁ|u;u6"uj@V˩j#@4EY f#T9S+R9o(Pw(V(uSo,_6:6E =zE@K-XF@XW[D CHB2)$H$EH"CR)IdHBbȐXFdH$$XBHD"JIQ7EDdXB$H*ɐXFJdH,CȐD)"$#)d+$>!\G96tB߿}WVR4={huC2|6ݽ)U2=B_YfC og!)/_V7_3JӾ ceL#;FrCПBg)o5kv+G@v^gow) {ۑF{Eqܞe_HuԝP~w`]Ph;sc!CGs?x`D0 u}i]'tEF5<PH?:+Ho:1DQ{a)!"""$ D$!+D^v`@ #$ 1IDs @'(a!+CRK %&s~< "B$jGGPVlv%@)2M{78wH_;YB'Aul̛r4f >?|.Fr\y-( s"og=?k۹f,iz΃=EN r? Ґ}aoWww7.~ u&̾?-ngݭg{vwaLCGγFӳ~5=-AcNoĕaڻ`[aـ_(Tc;go{yy~ү[+>gP*PG_I)#!ji3 ,wZ) ^W=ܦ%}Z>kk77g)VQAKvhֲY;yw<-[l2sdPsImznѼysO$jd H$#I)w5!$)TFJ22 DX*d"))DR(R,&KHDHJd2T&$$Ie)He9He0,b}mV ^6q=&)y(ҵSZ:~[z͗P,}r4/Wc;y-n<ڙ})ӡvL>0o!?ӱ!&8|!,Og^af=ZƱ)Enm!~ u f'/U8ۡ< evxf_D󕧳ɸn߾~`[*S"%84s}Dkv-/$e}sv-C#Zh;7[WZw9FS|:@#v}Aɪ>TX6- J*rDC.3_"@PΏJ J C~gPNGB)b ^NP$iZbG/,ZO4QPɵ []@!Sa}Qd2 -grHc{w!gG =Ҍ"2YEQ*5 3W sp߬1z{O]2I)̸Ik^af딚=,cס]~wo޽-J^<#]U ,$ϗZYYX!S{ʡ{익?&UNiK&95> mS/&zR'Ɯ`4:5!V嚕2drǫV(xDHNz/W\ͧ'DjA,_v!Lb&lpR mB}+*HRS}QQIzg t`"x*LY2sy ʃ$B塒ȓ,`/x#*r ȿ屭`Te%e`nba̒_E!5Fd=߂b}NZoIk=6H~}I1cjJ$FYV֚Xe'I{LPstvқ'M-mt> ufoRɛj!%Kg*xKmh{PАx@ X+7ZV#fp3b /&Я{ހD?¶M޴uEYL&l?(t@*49:YF_nXt> ^Lܞw(*WzwH2ٌ +DEȻ+E=,#V~Be=(J)g]$JOH,FEU|is(v:F,1X*Rw{-IYXW2um ǟ|˔Ȅɑm{/nj M~G3:5= n |"=5V[Ub/Mom9!(=-Fv쟷RRݻ$^s:;՘'=}Hsl/>%U`U+xK _|&I2>8SU`p ٩"E~~~qm:5>E$J=0$ ȝZ,g&JPc*NՖ臨}" $lV'ߍHSucApB&(`jA_aC :iSys5r'ͻvH:rʱ֡He*ŭc\5 KtܪBRΝd!H( ]=]5T:[C@o-N*/{`@$BW%ж~;vD\Lq*A+ڃwؓ峇pqܞ{3AcԚqu$ϲ3osz6{w}^8غ`ΥȮy3\IhTcf7ۗ+!X768mg/s ꕼs]]1Ve΀)"EjΞWd tO4ōj{hWsi9" #JjlXpdӎ]3v5>c9qrj @fby?Dv֌C?փ { v!R;]<2'ŗGezA^QTa(~(k} 8*^:ё!P١IHpq),B%nGzc2,?9ceoD^0*1)a ҩⱃr7˩,[kƟ&g mςG8jk(\_]5BL 5 K "'451('.x#8M%ڌJEQijdu "f'_e8vmhRBB B B9U ONvE+X[sgI#  (9CQ ŧ[$`XҺqgp:$ \e?[m|E{ɹ:Eyb!:񠘽*OVV5] {ߩ{Q8XB*s.0X(:>Լ:f~Q숦$5~ z+jTd Q/I"mF)Lۻ\]bwC5:`tLa2 )ĺb]$ PC0FL'P=ʙd +JmGS`(g ȜI" /pJrS3Ch 0L)Iøʷ޽ew fW9+̿^l ӱinųr^1^Z;]0Pş0|/.EtISc6t˨Rx [(BkDM/N&ԗb+^ѿhbٗxRJII!DR챠%_xj%P]00 KeǜBn !/_T`0PzD(B)` Hf/Vbi"A*,E$I*HBB/kJ "H4K9eB|0LQ)Wb;)rBl7q8f5;KUGY IYt*؇EL4J: BphD*Z A(9S `0r5=!W y_{BE#H"H:5bR):݁̉BҺOj/G'Yu` H[Uzw#o"yI;UDѮw|mŮ>zh B5ۡP)_Rpu0*)iVRs5:0G   A A0rB `(< @  3aA e{XsrM@"<Ʌ\!  A 1H;ؓ@HID2WW]u dPbƥ<[Cu)(5j'V/hhqKa?z-Nn_p")W`J(ߡ$ h֍,tuR*qqY>*4v*Q}~ ޸`0kp8` !T"aٸ40.ʥ Ay|m-bB1"ŬE PP54N FXc0 ץRL&x|n)fkִAHY,샄) >bOhiK㡠XB5x>eXӳFTi?7V?L~혞n( E".B,<2sʕq` o$3jx _wM! )?]y Zo?*}3]S6ɣe2Yjj*HA" *VTd-nP:R A镚XTGJfKY O:@Y91RmeHL(HG׀-#CFBɘx)Q&: Jyut>G1vE+'l@e) F=Ծa^SAkn)xTدBzKӳ\kL1`J`RaJo8Ơ?d xH_g-`ݭxn1jGɺ59K `5ϫ~bO[E9)u3B4ޞ<5iҤys*@k;n6u `0 Pa<YQo-nV7hy`0-zdf/pͭxooo9e -*a\]ˢPPRyV8K4Fmjo6Xpc0rM:Pu@ Pe`ոu\ѽqmgz9mSWw ~T2M6|Ⱥ7; hԵلY[;&nյeq~J umڪϴQ$ [&tkڦco3H)~TТAL9T 6^>6qg!~cn-1ML ;&lԵðe;t3eIXo}d,~|ە `? Ee8ȁ˯KrvZ֣Ǐw۝3vln_Z;f@K}p;<^M3mBʣ>mw5>7֝=C<3w;gMgxK6m\M.oH@ȬgntjVM]x[w?"AzP aa'9ņ~R!@qjQITo9|hBsLSP[ۨnߩj<ً7!M8v<FUz*CHӪ==,XsD\3sGK߶ø19jl`j1d Ⓟ:f}fLl)Ɇ+A( ay9$$McȬ{iSo@ȻGVku` )Z-ɪbSI.C 1 ʔeiNnXh%?F,y0%Biߒbȱh!.H4ɺ25}\f^NMj |3τ@Qsf$*5Λ> W ۽1zu/4RGb0?Kk"5` z)Q$؆=;w 9&:i_~K@K@LZ0|ҳN[WiCʕ!`pcirL&dN8(S߄[FwtDm\nwS~3Ye&s2E)"g' L}n 0i!'b0>`0 % *E HY-ڹ?Ni1O\׌%CcN'<7z/*`c $dB"?_mG@ʐufm~n=M(oÃ<4$ۇVlzB--g~/:`"qs͛]40@*Fjtk- Z,QZ{^gѧt {2) @KO1=Bx7 oxÛ:'Ʃ;S=ڵnyugh"@DZo7ݳ۷tMOX;9=ڷ17uq$˩?vZC=۴鵅 W~[oἋSowSNzl&Al\ r.[y]p^Ь'ܺcW_ֽ}۾^Ӥ˫ ^w:HQ|6rYЧq0wVoo;{B=N`0 `)("hk0eV_b0 `^'vfgV]ųr^1Q!Mz[Qw_/hĊ`0 Sdh:"ԗ<+WG3c޹C}:@+#\a0 `*tzi֣H$ l`0 &6d2 `Xt 2``0 &?DRpb߾B`0 DOWϺMOTbX9H `0 iAWX٨!. `0 毓*B*6"r`0 `0eBHFP&< `0LE#~~0 `0R㑟 1 `0?͡5 Z;{. `'I]*eIz>p:T_\ [/a”E u `0rz(\ ܿ`0 _^`0 `/Xc0 `01 `0 g `0 ȧIq@ c0 `0G*u `0 tA?8_`0 `/Xc0 `01 `0  `0Lu `0 :`0 S~z`0 )(U)`0 S[T?)`0 SQz`0 )``0 _X4(mF `0 S* H/ʻc0 `01 `0  `0Lu `0 :`0 S~z`0 ).\4" `0 ȧKqWُ1 `0  `0Lu `0 :`0 S~z`0 )TG" `0 SZ :\u?_`0 `/Xc0 `01 `0  `0LE*@EPs8t!\f1 `3DžP"ilT`)/ŋп/7 O _ `0 P^QhԿ:`0 WШ0 `0?`0 )``0 _XAy`0 `0U*Y`0 )6ED? `0 S~)ZzZFzD7X `0?E%}?|<#=A_osj^F.&&CVEPs>ͻ tM8U =`0 &KdUgOɥS(OYS ˸6BY'Mr5Fʮ@m[7cs®Bٟl=GGO]YW*'5h iʊxfS.'(ꞝKK(/<!^&f&B2*WoYpHK/|b􃟊sn89SVVc04Wn CB///!WBfm~o7zϬ),/JXje׮ vߪq?^ ;7cŤвE}]98EVu:w^zT-ɘtuꥧJL9zmk=2-uJ3Ov1ת͈ySʪԇW}x* 5Y~/)Vz='Ѭ2g9BU&4ၪ r.}'@%;\Sl͘Q>ʊ`7RO XU?9ߺ+kk%Å0mV袈#G7q JgBt@AFZ}0nUst+zfUYs%ެ.\>y)2)+gU3Zq═{'Z?ݼQ/eІftCǍu--.AF)+$͎~BQm3- {,9qFwN BDm|/& vĪ)7^&$ ,է.\>ַ;B5FwTWVcںP`~ gt\A/=q҉ŭ~Zt0ߖv|54&4WFS{z(1cj/MEoj<#d]h -##\T2o>]zLt@'&eblfblfP5׃c:5731fndG5]I @<̴ӱxJ6v0163p6a[h@I̮fC L"O\0&QmtFVH(m> 9Z5=|T~{달zΕ_{N"ӟ{UKletїa;r5ی^Kl"3+L&uә^Q!vzթUN ݵ";TVeb#n:.v,۸61o:y89R ]xj%`~GNzο`0zI? }V][i;ktk9ITs6sڕtiX9=tw42r1\ތSe$*6cz\5UYHY)"Ne J|@Hz.ODDŽu*hW{|'Ly머MƄ RVר2y2sqrc!,֢_Bz &Y#/Bwd(}UGC=5g:˛S#-UB?mge~z4efnl9uv lQfd/֌]], f g-|q{Uݨg[@)^`}S]TT;$H]hN`Aݢs~7>[ZbH3,u:LON۵gO=]ԿQ#2rGee!`Y2aXRJJG-Lɖn~@2.Zw9lfUxZrc[[LSV3R'T^u ^SV[gNY:zQWB]PԔj as2JA~]RJAQoM-i_Qi3}&0O[0Y;5]A[O6bJz~uFSz:1){DmlL,Adv2? ]*jO[iAYzJ^]gPu i .(]}O e@tYkp2Dz3٢Rz_%=բk c&$[*Wbyʮ|CΤ_ba] tOd`udJ,>7Y^i3ܟwl߾j,;|NfRf$IOή{L9ܵmӥ-Y0 {9]سBsGۦIqfӧSdee޻}<4ױՍuyˡ5;)ǧ~{1Vu*^PS%DÅ~T8=Az3j[wl&#ɷ,1)˒f7;7z&&` YHYMuYzOdſ%JǺ)bqʇ]MaѦ:؆  =>M Ҵ\}6Uių I-l}Fý 꺵IkDCݼξ}w靳%=;4wF]|z?Oaw#@rܿviN:m Z;|^-A+fڵoZLmgױ[K~ lq7D~ز69ƮsfsԥSvuSOOè4kJck)ˊg9|w)2++[oԺ)ǶiaĎ&~;^EzV-F+]xjұ[41Ҡ͹T\+Kz{ڶb&Q* 9ZR8G.y܁Zf;g bSOUs0qt{cХ OQtFw,t쇮[oꑛehi$gNz©3rݚm-uz-pT@[֩Ojۥr٨4WTFhU4Y -&wh+':\Oz> l˖Ǐ}{s+=N!lzs1j d3͛xP͍$ /B}v@Jer_n⟿4޼!H"##%RVٗjV,h_CE;s` 4pSe`${§A|q(}H DRX֪5[ׯ`Mn' .`JRRQ߹Ů5t?,:9bOLB$)$Shuj7 䇟&yrGl[2 k6CuXHn4!c*1 iRڳx'w)^*J=;uz mݦ e`)$2@Lv >oB^3HeD l̓A~^8*o|ɸl n _4wcejĻkn9:-Fֻ: ͫo>&sVPʋa1E-HIvxػ=pj1~E!?;uZRwfct}dG i Rz^}y~=u mf}hg$`)(B/;Mܰ0 4KVBSRy*&N|ᢳÇƍ{^J/CbY') rŲ/;F=daֲaKeH,~?OdmV\gH#] @ׇi{Ͱ䤖KS/mw ~=pbTvZ\>u.\sᵫ-7{GX|b]A̟B|6ww`Xk:r^xL[H!6g>H'G4ThӠ7z0̉kەS]ĭi֭w|fx5\ʑN7lֻ? XQ k+'/:r aO}3Ǭ:9c~gr^qIB; ʻiy#>GxսG3gw~iDb (cg-R bDFe"z_ʉO43j 59|E+h6T&UI]ݱ?2SmҖ^m̙YȲy]dTEcW` %A#{284= ٝvnu46B, ]qQwK-E̅ON,i?6DF?t 8QH;N49`0L %si;}gkLpyީGn5ۥ. +< a@3((~+~}.98~ k l|5._`f~<'C sgUc}Ɯ ZXV6ϡ♁WFuώ3K/xV$OWκ`*S'+] _6ߺ~J 2\PKq\"u`0ٵs焉 BDbBYFWWP"C@pR'l%EdHDbS5Z~@R-V܈00D*{woRpŞjlad}aQb9:|%$v-niEH@"Cb~n^ևGE}{~4^qģDVc֬e3qBD!)PY~MV˃`0e$]C]8M,=0޾2@h{NgAuٸXyc]M  u5g6>aߩX^/~{Z 95q5e4{f~"91ѭ:NESu jl^)30P~S;Tb.lW͔|Ѣ3==%7:>VWU":4j**˝9kVHeH"CϗU4bJdX ]Gw9kp5hߢz2h;%7:y?6ٹ'p*Yvo~E0v"QjjGvogQ& R$2Xl.$>}w7L] 0t:z|1]B}k3f#I֣K{> ;~nZ$orN\2qGuڭoV+QL9oLkykl~ڛiR (aIo& \i1 ET<5ooE[fY3YϯDeT %j1`*IH |¨q ,faN EG&u,^%[,MHN %UO-!*֎6gEݐvnȥOhgu0+9IE:^r`m&LFV#E|1rN/Y*˞"WEׇyu>{FՃ8LKS݋S^tX,8udgnvdKKOO{w̰ ږ`2˩ݿŒ6TWTp\S.+Tjj `0 2.:V(A*z&,{.:q~j6oKOiHDzB?g]▕n-.TDφ.<5^xt.Xv|N] P'R!޽zC"DFFu;&2B&׶sWUuDR `;,MXw]imzNgu{[6<{Gc#Bu _̭r,sAvvHcbLBy+EB)  j&_rSa0 )WBJ]yrg?V& ˈ)W4)e?Ϗy4Y M"?2?&ƽzi c0"Ij+d7FF_*VV4&ܸqLXV$Ȍ'wkiieiwŨlrSӹ#ﳨM$eRƶV6 TKU SSšI22SX,3gcAS{YZZ8w-TLӮKT:O?ߺUQG=z1QGn:y|C`05*(TUPWYo|'**0}wP-ޛQ >by˳n ·/ۈOσw"nxA>ޠپwqqQwuiOU7c6"͕E%b'{L{tOu7#0^ ȽckwW[6fOm9Gu>_jAFې>v.pj<<< x¨qu6j,R .rRbAJkЁ#8v'9|ЁK-2TI_ҶY[S6#Qcߓd]R+DaYdBoVy|OjozG[5!lk9;VУvWuPw?(znmߵ6[׼py?^xUcu#:C È,l}6|_ϥ&޿֤oC}@)F&]=,?%$1' 8iOP>.߲$̯a$4JZQܳD6mc{o{ *^Y?xy? 6}a>hݪN!6sm|u ϺD"H$bxE'dge|P(% и5> v_멷R-Jz؜}|ht<և*T Snax_vibص__ϡo'|[/ oi~7s,xկvnj̻\mޥ7Qo//b8h3?EE]t{ނ)Ş2ˮԿg_$bϺ]?d_5k^d+:r"c:#o|V >ϯ P-#֎Ux$[V*8ɒ4뗀gPXy6MMmfcFK˚3FS=:t}͹"Qj5[_dIV+gVֶ Y+{Z E11,jir?_8 cz|_ʩ⛿ov]}z_M6. {wYH;T> Pyb {cX/kW.T).;lG|]L8\בXSE^1.kDGV=B)afFoE͇XTho1m8lE{:3 s4k4N | k:rfGyZjPU[qWo6V^x#'vi``שMʛ6SqS vokȡžū'fH:V)D^\tċUQG@\lԇF M:Qˆ}Ӷn`_لP#7&z,I챉+ .*CWN<')k#~J\J;|t}Qo:̛`2֖7__cT7xč&[PͲWgZ{v=kD,Kθٵ.>!Ȓۮ| )5TB)r=#˾WT"tUͦrJi.D[h\gٛ(LkU?$ɱ 'i6ut)˼0oI^?~Xj,ԸwO|wpvν\} K}{bZV֍=O)+3*@"IVNFt`0Td X\6WR7Sǯ_neeiipr@&:`=g-ԱmkpO ݕUx v?/[a5taVvvǸOK j{qѷGj n}lնllK޽g[wDE&MHسzN~n&L/LXj,B!I~s|u1k/ǸK__4*[Sz>1BusRedLi%ebl)ݟ7$yB&fN|7M0eѼ{ `R"}hϿsEF{C|c<ȧ13Ojh7G|gd݋l,N=~Q1qO7%KeeP ⟟S8ŘGuC@.Q1hfTi7kpE_|ϐHEϟ # M(ehs[ d'mV6:y|lk&lyjmHp(kDwȪuZ[gg2r+%m,\mhodƫ]{e Rc3|r&6[/R}*u$_xa7e͚1zw= P> Ĕ="=9jA ȤDLS3(1)!h|_}n?_'Qߢ^c8zXI(3r뽞몝^c%z~hXywipOفy;D%!ۦ/y @6qO_?ݧ!7Kgl)Y(C C@) -&<9d>UCK>O \Ϋ8~r/ma!;侖/]ܺN':5]][[F}7|iUKvCNpmHy7uxT Gw۾;ּz|XΡ^jَ޹>lF:u~4h;vY˶k`˅ʹ3o]y=vzv;<N=~jl~?AMߞ/'H{`ժUUV\@AOMZǦ]>.&ʉ0$CSE(;ؤvNf<& VFYǴ316j2dɢ6B2=#aݝk7I Ҍ+511631wϜkK62316sh?kD veqo c3wvezvffvS ?xqJVNlݱ6ϴAy3cn}_G)^RAĥzǛ+ќNx%_DzAo?w.Ys:ұp>wi]F:^aH^thv}p ޫ75gs'}!^o2B׵(CW3uY=g[dL}üVi2rh9Ͼc=W{5#sk Xwa?b-Ӵ޻}] &+:_OW{֕z\;nQCA?پXc 2"~>__ o&<~{CwÝ#/˪^3h>{tՋ'kS zzJ_Y]R:+g]0_Iu7 kã9 m1Ejwps >`ocmF6y)ֲG6堆mM7νA(.| jloࠀ%M\<߀.+ o'/|NLneP@h{eNgAY,m${_-}״&|NLtocTehx.\ǩ˼Yِ'Y2Ѫlj"~6 H~s߼=~1ȌW[G.=┷Ux_-\W?T0L*ji]wѽr:uJM4cc\V ~bwcJT!־ ui#l0Dns _~u(V sʓҶLĝ.2"NzV-,;Èe+RzA*6ϸL4*2WePquơ{z_$q1LzY_f{2K;mY¹6ڟ&]$(((11!///6ϩ'!%\~#u{[7gl!^4D"v;rX,ZٲY" `Ii_vqo=LFf9:sMc36$ vYfͬ}RLOf>2w{c3juZ]T.Vtwm&ĄlۡN c3sֆhjaJZُUfaʔ%^ӾYͶ'ԛ4$7D۽ED*0_[ԭy%Oim +/%hoPPH*|}}܂}?O֣+ϼNNxov^Xch`ЯTnMM]*R͐~?3wWzê*ϻ==2Wd$_kM|KA(xo[o|55]k^f] N[ů#ezk a}|sY{SwGI4V0d$k@aZ;^3[D;Dy1ppcǏa|qL;Av}s?k։ÚYUfsj6`:7=JmGx͆J}OkI=\*iui0V)-k?]IuO~~LhZF3O"IѹgXܱjw@ѷGikf[;Vx,Ɯ3o;eio'|WLY`hPKe^o^!$)#IT* R$I|Lx`ʘ}Ӕ(bTj4𽈤/od<`JX X9$nt@GƹٛY5aj'ي.i2@@r벷"ԨɂfAb,uefMLmZ_UCljhnlVۨYv CԘ)dڽU~ öuTS!y5ilfbbj!|ke ^ᦽ:k8\nօi;RNkq4V0e(O\mZ_o>YW*)l7(OQqz6?t,$3?{:rD^2kax ٶ݀k4QN}%D" / 8y&;+ BH,HƵ=N M(wZ`o|`G*iaTӧז`KT<.y=/W ]؛/6y{b|]M/O98jaap?O3x2@GK. - "6;'\%媿=_~y}{J. Y[|O8* `1]$;>,T&gVD4Q[uȵ4#1)o]Ͽ~8Q)JXulɭ ږf& x,U~ g\ݺAU)֍U3)#ܪ~͌qk`ifFaJZ?E11k_!|lc;,u{[جf7d2vɑ~6.]ۻ d+q [IK.TU&5_"ãC#Fлw/_zHi`Z_vWۖ}I g! Sƨ q5pMo-WvLt˩\ߢfå5{ 5N2 @&{̻OMJjK)IkwͲ@~Ƿajv:R.uQny9MqH|+Pz)kl/F@< Ūڊk0eF7[=ycvTɾ۔WBMLhӾ/oѫp~[4չ|$Pꝍg wҭ̐(#67(L!~9G8\{%&~<``ؽQͬ$LiYReibb,Z|iJ'8|tGkv$1G,|ad'_6p 䧉 t͛,;YS3Zl=͞5GS"ҥK@WKOSo|KppfG*N6-*A(HӇv`0Š|(/5{71$$߳+6Qa-S'ck\oe\ֈM,Ԩɢ-SXO!%6$2(ʪ f ʿTV=|azG{r-Xp4e|9Ueg-Աk_[ }jhr[2:bT`|MuK}͡X$[hնjbA\A^wj+݌Τ[hZ:tH;Su!֗'ۍc}LpNFUDe2?P'6=W(d8wA>,0jڧv!㙆ml?C0R]o;Qp2^ ޳E+U%NTI?&|= 93r L}:xT;fK憽peg!_؃lm0.w8tfN,u\WKec[ ub/ g`O}VuZ~)"Qz9q D6A(Q܋U= 2o(-zĿ0mJ)C/#/Tmmmlt4.LYX2mi K77̆mhD@Ǣq߉˔H2coK vNo/|֥"_Pˀgh;[+iĝ֌MvGɞgoa×׶0316S)b0eNއ FŸ*nqD2jጊ5cWU2epj?$0b_9ud{gn_TXO|we3Z'X@JQ-e-)o ,K) m VϥM6LoRDF\~4Q(L|olk3Sjj 6wV]e޷KՂ^{z֟xl(7;{}{Yj8;›s^5MDQu'FDI(Wʻ)d/xLnUy[)^^a4Cc+ Μ>%|v>ގZ u>/7ϟ4Yh.ͥn/%]}Ϡܟ, >_6 ztțх4rmy%&id!t6(rE,Nۮt/nf ~Mkuc9WlWq"^[7445jZey%_ukXi6?N!$/,ۨqUSj {, )h1E!ˋz5QزdSȗ]ڵUMҤG'u4jjb~Y"wVVsjj+bs2 ۨ>k͎"D&vup3X,rEjz@ 8yӇUW/{lE|! ==ƒe E٣ceT4׽#2^Q9^1{e/ԳWMY ;gPh5~ZP@/9hȣMLփf&V=Fm_AǩX0 Sxyy%&&͢HAxyy`0Ԥ?3y>Kgrd6>aߩXz:n[Kg]i̊fJR|ehwMKU*xVZ{/ Wq/tiNiH_>扉nu-z[`US*|Y>gXWS-fc|/i9O4!OdƣU5Dj mt# 4;)y{F,m!Glvƶa'M6ڴZZ 3pƵI~zbۍzK7ϻ |kfحA5-{rzf'bj er+0-jԲ8qޙnu1?=0i mʥbǔDߓ{GWD_v1>`fݩ"N֟9-xAj,e4?ƚvnœ֮}Py&Xj !K;nom:9-Y&|22&B[hq?gh^I& 8a=zb#K =Uu{]&]kqP׵rpqZ ktl3z R~eK^ j^ڲժ"$?oҪ~ifYH3"ҶiU7znmߵ6[׼pԮ_xń5q| >\c[rM4{Ijcq{)$v0lP/kJjK)I[ĭ|Mu6 6ֈfY{LoZDN}FK˚3FSR^ˆTesL O|'| _>ń5q܇{3< nI 5xzt׵ų9cMN}lRf*Ƞ{[ GE"=6qwEE{h@ʉ$ =Sz9jk7OZ,^?1C @SY4..6..:Eª NHE)> I'2eZgoLJ u^DpmME3sR/,ƹ(Y8qܞ|]L8&ˆso[Ljl5 x6;|U9F×1qXK!R(`SBuƕIZ|wQ˴VU'؄cm76I-ҥFm=xg_~Xqf#y/߾9. >޷hšIʵ ZM+hɓ_WH(Sr HZ.@Pˈ"]A ضO8tPꚸ04`=0F0w"(w@-a•u6g2BLd,=ET͈^n6q>^SnZOWU^Mj_Jd.e]cρ mnhl_U_/-0e_+ 2_]h޽Ye y)[_`ZT=nTl\b=Lv>1GGFmƘ'5`Vj4|ɛ#> ]E6}ka ?x稘曌`h9lXG2MH~NCn*0юhF*SIΌSùjif#:W`wutݹ"Qjt7,JYG,:{D*J}qӑ&xi)QFCKl Մ1"~D%&,N2,KG5E3B8w2~O杝-v&nO/Ֆ'[aJūls^JT2bR/1X4Lgjg7DZO{_ =?t_\sNtXOma3dS}yS|tƶ2`0+dAbB0 O.YZZp=>H%ى/nŪMҸ}f/9zYr\A\w~riPrZ:,,+rj:#NM?I<Sp^osˁK&HWq:VulݽWJ> I.f&V-xX \̼-OC?363ÏMjdjlfZcpŲS!^nglf;$CpmIVf&f'|QܵVdIWo:N(Zm:atǺfz<}ͰEHo~,%,'l]Rd׈.r˖2 uXc0 wwბV8^'͞CTP{\ɏV=kK̍xa0[0h8fͷqqoP/ht޹>IL$O w0::De ^3hcʾy3[ލ|`c>=~g"ܷ<pΓt B?}|ci|/{2*a[:`U}Qn2cϚm L_9=fԢv5L[ tgAmo|zuL%m<QkЕ0 أm7bvSeW6wN5@~"91ѭ:NESu jJ?ysbr+ -ڽ'|ƺj48( |I:N],&XLIL-ZiITgVD߹Q[2gЏ$@MЭ-!jȅGbd$&EEtn[8{;G{Ϋ r^bg`0 _]WF'}87T}_$1?>w3731631<Տt)$[ve'S_$i':UKSh~"Ӫ^1ƢvK@3((Vş-}}xG>i3cޮ~ç "u{Yߡ\w㠣n?Wjikhsrl0{z-R .y+*(0 &duj;6 #LuY̤ZCWGd/i?L쨆`0"ZUt6!ҼI ?: k GJJO''sWb4tlۯ__YvOQm,LnT= H1'tá3sVb߯cĂSt6T_\-uj]S`zk a}|sY{SwGIϷ4fq"xo[o|55]k^f Q85&~~[1>T )'dޛdDycϦM-u8ud{gn_TXO|we38uuv{(5ܪՏ*Z>|(,ש_>M 0ۣcA̰7|-!S\TW'>S 3lєM'%/O[|@]nvY1ܙMT2~VuZ~)"Qz9qK@Wŧr344ڵW\sv$nt@GƹٛY5ajp#Eo4w2g3˅.ފTRMhӠf&6mV `(kI=\*iui06ߨs#;Sj+F\Y'kfUI1ٴ}(/3o6ԧVx1 x\_{+z54lS{zfVteYݪlκi0Jۓ] k69@[j_ɒsLy׳l:bWunkߠխ[-a)u_k LDC^Cӎp^ݡl0'!z֢6߁=$Ucygհr-s]24n?m^V/@0q >|0n{K_ g_6ߺ~IƜ ZX}>۰>(˜gsS~rU:Y? ~o\. ̏=`=xsh°~uj 0HAG;ʻ6|ke j 7\12'Fhkyc0@]DpkvlCl63Y +^kU5w<0oâ88tr l(j{KLILbKyh%1Fb](bT HDDAY{s3w7|W躇sw0z !W0II<)g'3Eݾgz>p^=fk۵zH+O>lEOtvGh ҕ$u앸θz6Tzhpk׶ wZiLm !Df\WV)éJ3ܼpjw?탮y;k;q`h+a`T~AJ/szØ~:aI<_K B6u5Aqq'12ak٬ 9Z_B[,?vv\ }ɷW4ꔘK;k. ό &^A ̝$&Qxh2_ܾUMEǺI uʢ]J9gܧiM}ݪ8x=QkEr}wR K't[毭5!@ףc=8R#YMmPήzwpOc5;Z!ôg6qdsGUjvE^ʹ޺be>~+y񣹿h47or1K{1mt3)0,ˮۊ4eRi^#+Ϊ[3NomydDZ~T&r̈́o޴x1j_uB8\Pp˖իU˟L!+hTg3 i'˿o;n&#%طwONcI҉LΞ!0\dŷ>mȌFE^%#vO~I{2NtUE]9J;+fר˫}b Vw'r> ҺGtWc ERN ?{cF|5Jjd;i̱# N?z[xw[&)_~^K`sm林>^]V%9[Rp}q^6)xӤ^n*Zv~wH==mjnXSٿ.-o>QVû_&!E~KWYѿQiYqS:];|%G}sʙd]QO׆6}Xd叼s׮-Z5jQF~1rԨ-Zܵ7BVU[|t!2Gk[o?R5Iv^Mim}d]J1ֶ͝ /90X}F25nJB}-VOߧz˙sy9Zǩ>CN0wMk)z#zz /!ҫM_H[qзﺼ`NyS7BXzoyhwT}Yi hOaAgvXK[f_j*$2"2F-"]7{B4ʿ&m6vK.X?j#|nM26|ɐC>!)hmՑ;E4[%ȸzVG+B|7X)5mKj2N<{orcr=;.6ΪtI&1ppG{o~# VKgyzD/t#NxUה _ZK͗u+tX#M Z-Qo[ B3xA\ߍ#oZ0xÕJ51׽aݎӎ;ofH\Ȋ;}8qGGc!nhMsfg IOcӛ~xvn$$,/޸ TVF sCUw*2Þ^ڄm_7oP#¤C^;Ӣ!2YF;> bkX)k0zġ[DWNfmVʢJwljjV{lߣYʄȈ {TvyWI PoK')oP-沤a,=aجntZVj5MFFFjZmpp+ٻvDO97r߾/"s*N)?gM覝2cW27)Wԏlo M²͚XD/&D-MndcbjdrƬ&Sܵk5voA_$ܯӵC[vR6MZA s! <{`r&BR~M5Srd7^j%i3.nlڅ1f-\S:.*}N'i( _rV٫j㕔=I W:uW\[Zg@uUu:}9~]0ޡ߁{j>m@j*{v_zt&U%{ jR٫73X͛GmZ̙\;='Z快Fx ~e6xxVjtᕚڿAbZT7{AڵnલW[~sQ6#Lq5!0vי蠿X(pgѷ9oImb-Y#1&t?3uBZV332'Ou)SRSS332jӵ]߇اG/h~޶osOr{r(Gt%H[Ϸazz7vTWk23Z=%l};&ww,{W5UZ*+՟p^tF7ֶJ늵%ݔrʜϾR6٧uwܬڳaӽzD@XԩN}sK"ձBy*eYZA ěv]{t_|.nfB^X8׺ËC6\0JlؔPυ._9}ц1ӏg81ݿ>(.)w>FOkK ?qOʼn]~#x:7w M|u_ GΧ͡ugr=,п{7T !R]xWBO5oٜ. ~l_nomk.D6|hrXYn:|`r]e tX؍f8tQ{iIT5`^~omZ~{J^B434aZbȬw=MʺLk{rgƾ[k< }ptQ=yjK"0tج)ͅ&&fy(ML f 3簗dVC=*+L 9F ve[ٰ#qdcAeF v6z !7IM<):rNȋu)M2&48@G8?77E:~ZȨLM.jФqQZnvȈ?V5߷:;ycKJ-ym 揪QLY)2˚yRR~g>UBL>]ZI?|҄I19ˡj4IP/ϗ` C]>d+vzJ"}wk}gف>Y+?]׍GؐK?t,YmFj0IɳgU`RoԵSť"<]!RwХ\! +4m',>Y} ە='MAghVTi9ݤ֐ d,<ޡSOC72!ԏR:=`\!;)"/mS={`jM&]I:u@L&揵v([iIPa^5'J-7!(U!wB\&×ljhQ礉\{er.݄l׹ ʘ!drSܬɓ&K!~l'g^rh0>(:>9K_+7:jɧ=Է.xlf3SC1Ei`TO?՝c/|ε^' 8d7^ŵF*wؓ7]?ڱkUihgװuGadS}^jdʆ"ߣ>sjj  L.kO^ݷ+0.`.=߆wSְyPozDdP(ϻ~5`&!:9*7ې=f&:yvN??Uk>-['j qB:eѮM!آmuBQe YyGѴVkQUƸJz*p{zŮkrBكkO/]:WR7"rGm) [߰] |[Y>S0,`]\ǀi ϦoBwCJew8fUEbsBf&vV/Dڅm[{([f_nËz|b;v?MN7[T9K<폺ۉ>aP鋆O$"(0&S{!3!xO׭\/t6a&m{wmvm{a+32Md.fm˜v>Ϡ~*9=uǪ-w&O/l;[ȧctSs}yίC^#Էw|{G[^q4'#:=%oҕT?βky'$Y‘OLmZ}ظtn*B(*w[rLtlܽGVOvFke浆:v36.:23ѵߎ[w=fR8z"FB3ƜYQ:*pJl\t̓gȠϮ*tlkҫMŧ*"u`5Vb:5?v"YY&^ g?mZFlmǧ,A !DZgܐ۹x;NٗNOiS%feJGaڂ _Y׏tV]Y5$:'\V)^yn7등nի;5v%sIp6YKy=OyO/XpQ3ڻ:t:=ty>'(wy89qۗ7xEkPv|ٯw:|f_ml\%ݵxU.QiiG !̙cmm]Brʥ %~ϔ^՜tu4ahZ E93_ 6tAÔǢR2Z8𱞒o EFuf>3. ]مoVɴkZy.٬k1nTcUj_Ba:EM۽Dqa_Lk. Q Y"mu5cڰFvC-q7+ !,8Qٷ|fB~n#N !8"If>i'?(\OX;eSyL_zy_+r0<ː9uR>o`~3 xXkUyo,Z)oȳB/6uM{~7cOەݕBfpn;w" Y1{g,}೾Mzܢխ[,>|oH {k !L3{{}YPcɪm-m|pHOئvAxkR(Ф΄ugOki{瓯 !+dMN=FyV\<ۢm{cܰ}{n7)爺6-#=:t[>p{/8 Q@I7P1:c-[|}SRRM{um&D9x1 7\I.~iq/.5_r֭=c׎r4>{&BzI.FOkK =Waw^KBi|k[~I9 suFomZM?u6lmB྘6~= ԦG["z/p*7w6o"}J?[B-;|g !H:-J_]}wNtmˮu.aiz03+W~ŊO'LBھ-_<ܵ wEo)i72B&羷)!H ݴSilJ !j.B5z Vv^wBEᕇmL[Pnx*7Bo供ƵRJ='do@Qv1{zTw|Sz>CN!L W76S2>UKMr\KΘ~WmyKnUdk[_=Z4. q t:!x~%N81jĈ.]%}z{<-{f՞ ȄP?RY8JٸZgAWwuQs6*՟p^tF^4ѿ:< -?qY)!;C| ʷ>.U`p ]+'lf=yݳBQY؜G{%.3SQѱq9,{D[_}MJgje|+&fm۷773ǒ~J(dtuιϬ䘤k~ ;Bj nZFVH+ 6f6.6 }&1s˳BoO'LdEi2%x ]Zȅ¤f߶k KO \=yExv15'k(Sw% !+9ešSr7%a9N=f@$:'.:kԂaZ شO Q\uVՌ&Ivq%T8(mޠIRk28˼qkV5m ^8ϥ~J7Q^YTl:%/]!L;L355J.ڼ1& ]^ [jաc"O uʢ]J9SܧiM}ݪ8x=QkEro]$dV'gU%~R7BK't[ͼ@U8Trh19/= i¤Mk֠kǠh2C{!{uB4nU7~wP9?8CŜqy}:m?Ǒ*W}~doyY<[(񸯣Һbe>~+y^ԳgϙS৴q7lPcIkv>A_pnBz[y'}M";y_4[}|>HY>:6޻kRvmV)2.N]ytV]bX ,HTi̼Z W!?~z[(Ցk}ޚoN}{+#nQqȡ f=~ZU׽Fuf># _C_֑̀MP_J:9Gs23 }w+d3Px{dDZ#DN.251mۮjϲ86Š!`^`tЯvIoxRSRbftKN:]fzz](^ 9^^C2XP̒T*cEmRI}T*{Uムsjn/ x- Er6 ^ RZ`<ǗgF (n[###B4*TZ7,|zXd/<{kֶٓY5Yq#؊N]a*GgAsE܏9wEnYʪ:pM.zAޏrǤμo<aïy{nwf;_:t;09Im >TQVf mi ]Jؖ/q(]gw1c&m_67\!Q/!H lc[>--Ul4`m2 GPU70]8s:q2[EdfnRCբd&: za ]\W!DeævК&7fz̀|Ltis %nim)OSqo۪dgvZ&R]xoӿ!Ǎ0˝iP2o:bߓYnžTVžRtg=<ۡS'?j-ŷL]ɰ ~JFsBuBWc7 ,ߺ#<;=j;SE "ܷQYM-l#aDsOSn^K߼9,:C(*lwɜfBu Ou.{aƞ &.e aA;侽{=#d{o6.qGN5gc].-[ !uUL^ErB`S ­AíWup˟U׍ܦ9*Ldaiɦ@YXZUW<7PP.fi8`jfkώ226*0AȘ5#K'jNdU(nuz]UJcx@(z%QZ* Zjz>ʥ_ҸѴ )ϯ&]YφNJk[eZ-y-E i=ֶJZmƬ NkbOli+ygG# 7c㢯ԪRiZmwnG\=lߚslٷ3Yy_s+hUa*o;$7b".%tØNJk[x}R&/{zTU{6(Q<Ǥs9hFpFvUZh IZ!5ߧzi4VY*>43(9OشhhF Ujas;qfvM:?E^3u9su#.朏ץ^4s!'TvJkfR mvֶv>kͬm5?;m MoFsUZWtjp?0%с]zk znq}7^yjy WrI/9qV螱kGN9Qc`ӽktBK \ǤoΞԀ;QWL{}T !t[77lTޡ()Bmz+Y7prsn?Ձ]}lLe.޼}ۦ4amnDGloz~~OP1qy'j|F#&p[`6!ʴjme}ӧw/9}{5۵ 9 yO'?jrYR&.ooet#MF}oKSBiwB3]j]ˋ+XU66.Uo&ۄ¤{s75[B~P"4qif6r!Hsy%uY2n %BG5sJ5:9~ûkt>ZP!.o~W#ۘˏfRfn>~v:2{;9(ҩja٪W-l+:ӞNeZw-ΫOx/'J0pͪ= ~x7\KeN(ejmtJ!]]nQd5r~N_ݩ}yKyk3ufλzw*)5!(edBR6Cu{ѼIjLFgwI}W/ffğ{߫JdW;QO !f{ 9.9++[KÜ<BXٕ !ܨe^s0R܃bޮEg`T~AJcOjܑswz^wBEJeQ4BͣlѸ]mv-*X4]*UZy!3_5(+|.-lBaRoԵSť"<]!I!>Y+?]׍GؐK?t,YYYϑ-LAʞ-qK_30|2&iY~f5pr%^=qPڼA7d< <S.훮YմaX$xK׵,!A߿Z|&5w"tݚ)WIG&2##3:27չ<#""TVn˸民u Ds>L cN~ w=-l)vm*6sç7u1D!.O&o]$dV'gU%~R7BK't[ͼ@U8Trh19/= iB GzTqp볥g'30^HvEdr v|rm*h}vM}u za^B/r3IUeæz.GA6:1* M͚\mBau+I['iǧ)-B=(7JUͥqRi:@R/Lgm[ˋO?bdtx6G( !<]Uz> Bd}րl֕CH?{q7Vd}SԵ}ܢڤ >IimX;iZ,|~L:u.љ_o9ts/-B(*2'qq]}}iٷUk2BtLFnqㅵb414cEyLOr X=Qj !l2rI}!7+o9#o];sJm7v !7^o/ cc㢯z\tl\t˧cϞ&?^HB~)<`SF ;qB=:=8/{?6m۷Cv~Q/!W*6.:6&xus7ϭ]s/V$֫:rONx, w=FfڥVw[~v,۷\v{ӭa oQOy+3˺^ڤBaegZlNP^(SbIoDDF<"6{[ C־7~}DVֆ& K76A׮=1`³)$Ÿ;Nѻq_u} =]RAӾd,pgSgNEIAURڎ~ ;跊W}αq-G-Լ+Yɂ_.N[>ڱԓE6m5}|+;33w>ΦwBh9_>aӢMK)LVmw-_xkUy=%d +BaҫMŧ*"u`5Vb:5?v"YY&^ g?mZFlmǧ,AδN^,zBEçtnZ.3!xO׭\ {tX=k8XSt/WLҧ tI!'7L:%^BNB**4!n Pz&j>嵏̙=t.ynʸs6ֳzN7ެb\ !үhkW.Q5muG.>Yhoϱ]-Т;v9Y_A5}|u@*cq?,jty^:N]ݔzH]s!l0)]-f^_k5Q{zdy ix]8Lo^'cݪWwj{K4˷>X&1w!WPM8.U]T98Ar;ÔkV[yFtTP•[U4OOH"+%!ڢoϥqZ9EK>xֵKu1%e69Gl`ZTFbVMr\M΍[= J^@Bݾw/*ƱU>XNB㾎wҮ| ]N{(roUIdCsO~')[|G`ZvPk5^3BO[`52 xXkĒ_@q+J.ʊ=.׶BmXϸl _ܮ W^hx٪8džVk^8pÀ$Iظ¬qZ\F.$sÿ̻;j<9oj_p1"aKGZae Ц>426IjLFgI}W[O8Bx_.)kw"Hg4kEVAv[/Ȟ/kmꑣ ei?u)7s=woeK38{ْ( ;J<>X_E}f"ުjԖC%_ԟEiNɂ=loRA7RZ!Rng^HC6V^x\@juBi㋑ _7Fwd뛻Ek7:VLCe5]XWoS#*n:+ m 0n;KԌwHxtϧxtBK>[(faJϠBˡj !kuUidM+Go,HTk{.n/8^x+L\Vк]G!đR7^%?=(04n4-8Cn{B4oNO`{:+m6c'3)6oiwv4`m@qwV;͛eמ\ʙ-έU] J(<ܨ c;)m^raw~ӣsطAڌiM=&˽7F33Ԧ7ڶF^H gOQL{~)̮`_J&kb_{m3!R Ƌ7O-oJrvKqnkv䔣51ݻ60){hʤ{Lw1zZ[J M uw^Jз( /0͛Bd íJHfzNGD0N^9 y6ݨڄ㻢\Gַ[mF!DsBn)4y%Lhcme=+;;y"öC&VUMnd~p7vKf~ !7g9P"Mw2.soxuns"W(>HPز)B>HP*{Z1]atwܬڳawSʵT$R6FB}?JY?]`D3Ua6:1wn3#[~AoykK>:efcnb`dxMY }{D:׵5J4oy2_ܾ隕 "@ɩb_}5^iZخST9#h>OKoVcZC\,x&!:9*7ې=BXzu?82mzƯPǡC]aVH-{t!':ݴg6qdsGUjv5{rjXٵ}J^7͸8kvYua6=*|~S+?QH6@b ďtPπEio(bsp !2(FY:ְP$v~=)_~&bRby==x쐌&)JeR٫ I&rSUF.ϾjnxT^όƳCu# 7Kﭑ!k[V*-] v=,h a5xk쑋 zlE{KƮc/j%wLnD]J؏ڸT5}I̻ftR٫\Z߹$mƥ-MI>f-\S:.*}N'i(FJ<9Oy6EHee0)Ш(ZLnVрCxL^_-.0,"Ma9_^6lJC>hØ3_B]R&>}֖4➊xFunM7a5dMSO\%(+ʮm*蠭+57a6+Sȳ:u~ߢ\|YDڕ Th?w=-ia[w!z)p8v0n{=S9ڣ>uPM^P0*}T @R4̔r!H 6B4Ymb'Gr~9j oyytzˡ{iGQ)e9sBNMy&j :u\@L⏵.([iI0y]Ol0d7S:)5HhV$56I#ER@fkjֶBqGOk(";oK>M!VvfŪR^(SbIoDDF<"6{[ C־7~}DVֆ& K76A׮=1`³)B8E>Sa~ 駺s,M)!/T\)B^mR\.>==>hU잵fwJT]N$ !++ ^MȞ-O>300șI_KFh4ދ[O蟾hԟMRk2#c2e&麕axv@6\7k\ǧ\԰^n:E.^ǩ׻R[k. ӝJk[cEm҅ l褴UVrRty2cmf̺d$VRy=3|!MΎF (nE_Ua.-wW܎{~gپ5 a~-BXy_s+hUa*o;$7b".%tØNJk[x}R&/{zTU{6(Q<Ǥs9hFpFvUZh IZ!6Qpadh9_>aӢMK)LVmw- %5byZuM{um&D9x1 7\I.~iq/.5_r֭=c׎r4>{&BzI.FOkK =Waw^KBoJHPmxxVM?ܪ܁ۼx뻶o+>M8+ud}KHeߖэlB4=-N !DZ覝2McWRQԏ6vYcʿw-/CN!ɑx:g˸}ë+v͊-D0pͪ= ~x7\KeN(ejmtJ!]]nQd5r~N_ݩ}yKyk3ufλzw*)5Q`a{ѼIjLFgwI}W/ffğ{߫ʎ ۫ݳBQY؜MձW;*ʪܾ<R*G4nkׄl4|6aXc_ߎ8sΤo@I1vl|ohMߺFd4o);WF.&5M];uMP\zz\+ӅB⓵xN ٻIǒ2ak٬ڒNdY:':k@+xO6og$5OEg-˼qkV5m iaNYk뉙>-['j q^n\K䬪doCBaAwpwj@J-&wY& . ;9xQ(u>۶Ə#;URȮ,xPN +`6]dEфGD{ &eu1)W3.N]ytV]bX ,HTk{.nA8?gTM6sWO223H,: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^PTiilfu"IKK  1dSu Ĥu6ffl N_(ؘ{lNu"? Ja0(_(*# aP"P$A:@Q=NJ? ^3: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]u@t"E^Hy.: ]/ dBӱ@F@I@(XYAVG%I)! -WN&Ht:݃KbkH^71NOO={rt2Q)c6JJ 钳 ^ȑl6_HWn``hǦ^v1ff&'nQD\l4xՔ6eI.;|`_SSSo߹+72nذ֖mjΜ>Yr%SSӼO:.qrLLWܬ|66JKs ,Ss,-(ʕ+ζ^5cccs3SRº(puXӱWM&dkHy.: ],_KIENDB`kvpm-0.8.6/docbook/mount_main.png0000644000175000017500000007411111733530416017231 0ustar benscottbenscottPNG  IHDRc1bKGD pHYs  ~wIDATxw|EǟݫN =&]zUPw 6TP"t5]/3re$wBg?<3{v u!@Nb!+]  X$;ċ>?;B ]$Zf)M9xbjSpgqouݥ^_k&qq-B|r ] bY4ⅵ[.sA5!墪ĈB9DΩj:Ҫ1j+!Dl捫pdVnN8\)Q=^%r⬪WGEct][I %n׍I7bM\$~T;V^\7IakVJZyWJVrօ-!䪫WW |T (Vp1!&z(ί;F*"rVZ(Fȍ퉟U5QR=ĵzE[k2& AF=x# Ha">,xٱQ[CZ%uR[k"`8 ᭖'%EQp*o51{p*"VA_{ v*ʓR'1s7yU֯ ѭzPX?l-SǶF< W[|cVw)6+ubRך먥\:[J|]_Z'o=Hg][{)W$ᄺhw ^jKxz# Wa!Ў謽FpT[IpRrC.vN̟Fpm~\eѹ뾷Vy]Z{ȵ]ޮΞz*Eu6r ^u]k!齂:I] V ګ-xۂ/}WFpz!b.J͵WOS^~lވ-q>" 87#<67QWAjn#^=vWP+1Tat6@3zȫAn-މ Fp`#6/(sF4>rEĥ(ݍsw~T粆| ]j ^֦7+"O"ۻ'wLyEC|^{V8(ިmGګQbJL*uW%r9❂T:Dmz^櫶B-nK V87ک-xѽD;6As>.By iů 73{7 >+P+\OyVjcA,j.D*cYeP^ך G]A].ʼꮝ>m%n_1~ ҇Knt iZi+x(WN k[X,a4+?P'D*Z? ^xy5\Ȯ@NN ?t#N4dJ1:E͈.'~ʣ냶B-ׯcjZWe=l kRux CaAD"nkb!֌"g =nl"z$iԡ}!Y^=ükoQoBpck5uV R,u15L!eeaa,< |ZrN.//[T}|;\,YNNBqbG"Zb۵u], (c\񲴈Yp#}RTg&gm*xY`0_@"  F1˲[3kl׍"& qqss"#Yz]pZ/ϧyC'zju\6u'5$܉f=+fC^^xBuYBXKPZVTa]wr]4fw-sF\RZβS.d<+/V|Atȗm.͵z=lʯLK\}mZJ閼+ Zn LH[Xؠ7   BTPZFB12^,0bPyA OM~x5<ǁW{ukhW Dmu>bh䵦׮C]P+vLZm]F !X>l Ę9ģvqh۵5þ&9}hXlcXtbm"z:.*'?:͝8ӅDki230Cr%m ɓZH-F^v*].-i0|wᅭDoq .jƣfiLaN=F|VcA#kFawk֜(bf[8B }ꬭH䵚go嘮>Z5`rK&rz>у9@h}b'MCpo_h-f '6^5lSۄ4WXp 1 ҟ%;|bڍynWV07Z: r#feb_|r`>8>~4@zE xqw~A}{Iro>r䔄XW:<C" ޺k# _r唯4>e?ZzV/΢WZ+[h/+$ǂ AAwjEݦq{3zA&=:c߾ޫt7/;Qg,yْӮ;:hqM'Zkq}D+.v{GnbO.U/}.\NW>Vp3w֥J uƥ6YpeVF}?t b#PY|BeZlqXI+Tl b*D\-{<Ӥ#\'LP|`?v9s n?y}Y2\J/xTw~ߊ>)1A2(]MFl_r-pd*B%2#FS$ Ua6,1fD$j kϾ-B:DJȤ9F!@ ,`/^|ه/W5%tWe۩SD"+,zh j0 2G<ć{D8ZH%OiY@l[L1>>6ibE&⋷+k@N- GȀ@(\{_VbUn)T}hrC_zI#Rs.AlHϼ3)·{g*+mկ$4 :.?Ul#ozx:Fz %~U*6Yd#CZh=.|>aP>ht]r\<678:n{ɒY+͆#|=¬ǔf3efsYֆM%K(,Ft).8.LWƠbP[S`Iڳ捎wP#oڼz?\ jJe6"E>mtZr[۸K.Y()-V{?>yaEuϧTFbP m甝z5mRC~?_},mĘLr~be,m&`9-G&e"3v U'ww .c%&Sy8ӝZ6Μ *y]=x!{ vzZYd-_jBx̯dGЮא !%&r}r\xj&?BJq=3Q̟rwσ<>wG _9u}AԔwXwvnAYO?b=uaW/_]s7_8i'}BQÏB+,&$&o\63վW'XwDK ?KP(,B XR(J`:KP( 0Ƃ Xx -M Ҙpe:kX'4U(Br Qa4 >Nۍ< B:ZB4& eJjZ3,g+%rk@Xlj :-5 BiduZ@IB쬳u`ya{ eeA*UIQ]B4J ]Tt\YYyhh֬JEcl41KP3`պoGuQ(J`:K IтP\^&ڝN;=={ xp_Y9@0RFP]VbTwIʶnѝt&iDH|qr2 ?7IlkP2{'RޕTq$Vo+_! oz #ݲ nSG>a.E꟥xL[n ˅~ILoyʂ;5<1;K\Ei^&HdI#UTƼ-Dr\dZTe\TjZשS(?"Ats r2r<Є،_._ˆrϴWw|9zQ?p.l! !U$6{^.N)'z|IN?B<>En+0jޘÊh8= XX,AaX ֡/C `Ar(J%J-fF-+) BI+mH#T`Wkԥ: 44w[pT۾iA MwϳNFf Vp({7ЖxXog|bM/o^|o! ˸':gƲGTѯ׆Sj-ޛB>ڀgF#BT*j5Z& (%7}{^7}q ۔S'|Aӏh6w(;O䕱=3REdA?ru%0vaY'vNyOԻWvČe=gw@(9{ݻאG0C{!͑wY"@' 2F&OXc4CB}]N|$K9e\cji|m85erV{8EG+v# Eg#kuXۺbEDjuZBӾ~Ƭ|qO3^[_Ңʚ~jGT>޼cY"ʏ,jzkYc >[@1e[wlwFɆا|?եYF&&ܱ־h<Շ$e}q{~U*XCpw7IDŽTyl,:amAZTyL㏆*NΝ}ɝ?Ok{vy=3 Y"<@po J68_ /Pb!{H$,I$aA# +>]ﲖǦ?~a[sŝJLB -6#V.).4WfNSsucg}H`Quōes/u z9-x`#{?oYrXzvsN|/3l,=׏?]L$ipjR 6 N7(3N&1%E_wugJev9L$SȳJ%"xyzH~}w}ѭ6۟ry)-wޔ'mqP/ ٝ:ÑTwݻW޽z?:YYYr£uJkxa,n+^}{;&̺g*xj3nl6٨(ʞv(Y5} ;uyyw>wx|ex͇W[G_sRZVvnݺKe+z}Zƨ޽DQ4@ІӠ`Y&2&~㺵͛gH$5OYKJI[jpQ Vbc܎N&;TԜ:vlX+H:44>1 I1Znݺ5p,'+JJKwܑIz#;|2yu~_wFGKJjej6NJ]Z%DR%%'#ЌJdi%&&%\|'j8ui(Ic\XPxú:^8 REG&''DG+JAtzZ5,5QTr<:*J7z^9S(JBPk{֡7q* be4$BCMC1Y? XZE*:4/X8B4z\ `}+4BP~` "p Bi4T{&"bb?KuB4J: B_Yw. c, $He* jj[b08$2228$eAꊊ 4׷ """CB$b5juii)5 ŏ?NOJIH8NWZ\DAJIII&\Nn>TZ'9U"uҒl>o̩_kjц/{5USo( 4`,juf\RThx-zc!Efyy_zn󻪷H!+|sۈ^aO!'4oт-8n.g{O=2%q?L=ו˟ߧϷhԵ̅>qP~}}q%5兵go;3?nÅLu[><{W@\`tc\^QhuZZZ%jNo0_:/~ZxTiӺA{/ji& mjglꦥ&[]|\2BMS2e;ftWkl}ŠWzn3݄ ykߝyk?o穭yt/hDaYndF-ˀhȒ} ק|B~n{+0|jGGWPv~ii,唱GI, u:xnsv ק tb۾Pkcۯϰ?l怮o>_?9}](;MkVي@rZwxHz.ʶH;4n˜r~~Gr TOgŎ~ڲ/z" [nLK301|eDF2b\%å{7E > hq4pbk }-~p[iNV-,N^3.R4̓{鋏c7yJ;&Vn&OSrYE!\j27X} w\\n2""*Ղ!ƜuQB3=9!#2{)hz/SSb9kXhŇ￿oKgqluܝ:@ ZjڕM#o{-7uAnYpQϽ9_,*mc/l7n(W:ɐm%$9y/Viܕ5&J 3d}iόa75sҠЖ>qGz(6艉k;/ WI,Z*Qm膞/P&&@`KeҾ}Ov˱`@FB\HBlғ)|izPHb@2Q\5ShY ƪx2!Ghkyv}ćcC88d|,lBOo~":$T'>z炪C^p^OӸ*+68FQq7X8wzx҇p&ݝ$ m6E,UDKQA`t(ufot"88gOy;S& I&a+ ?8e"K%+>3Yʨ~E e\#?]| s-^v1ěҶ)(;n㕱lN5c&BU=^h<:ĮW̞4w v=7TQXPYn=6^>@"ӈzG׀*}c?F;o,Ɉʇ~Lܐi_13 xyBޝ'qƏxk4Reo sЉݟaXΝ6jtnv CF,vceee'Oڽc+F$WȍF[jNE~NR Wmg h֡aY&&醵k22%IM8%e[C(LS:"Tؘa%T&ujL&ر#66ɔ@PiʪL&ui(UR?tfobpp\$ѱZn5} 3`0\&+.+۽kgXPxپߴR:׭ӷoxHh_ֹq]f~1MuiC Wm>p.jeN JJJB޺5$44=y|\B*5yyYgtZMbfI*h}jֹѩihshEgaCBBS҂CB =GeYUPPDDdrbbttRd o5 k èT*\7 z(JBPЅݮo{֡7q*zB-ui(`Mf>Ys %*ݷAACMCk"\γ +j} r,qJU8"@P(F$:_a{#K B ,Tg) %PP(xZqX'#H$˲tuZ'<2K=s `0IxDdppXxF)+-,BA+mJ#"X0ДSPP*q%yyޠo"JzmyY ƘaT,1d2]x1H[ V$J8Nזl>֋vu2~ ~{ VyBNĂР<1y^ו3Wy51yfg]죛%W33ٕu2jkeNmmzcu0izVՊFըԔ9"UٰEY+-"dźe7V7ouQi*DK:F9P .U/9&I?tnWNY넆hVVyfE\Sl_OHdȶJNvԒL@ ̺@/g̱ ē#ce9Ɍ1aYA B5޵&Z m; -_匪^XrW=Rxnza O}w 0LjןKԓiE*wxF9y @>il F zh2"v$&: .Tį^4Μ_j-iO *_}ZmuDPjKxmfЧk4^A TmEF8-/8sc< Kz;^ŲWPs'cݱVT xꎄ.Xc$gBeϷX^vﯭ:yYG AOPf"1{=iXG:[+7~`0 ";;G&Wx.r<;B@sNi?zm)csw뻎h!ۼ`ڣc3@d?b-פKO+jsY g( qbV*2/[}w9y}_x~˃YE{>Jls#ܓ} Gv :}[nR g0\Ŭ+&[jN\Vt]1ir@CMsòl|uMH8.-↓tURsİ2`|Her`;vĨ*Yo:;mgX jjzC`. mݻWhXd-pT./+)۵kg||\LLD6e?Pmk+,,hYYI{>B˶a7 Mˡ R%%&BwHhHzzF\|L*1-yYYY:iI͚TԒ֡=YaBBBRRBUEE%o=m胂"""%DGE+J[CCMCO86aJ%ˣ z`VBP*r]gjjJOއr˱**((z BU> 5 =* xB E*㵔5 3> uP(J` ȸ. Bi,c--E B (܇BPn|.7ˆ  hp`gxޢi4kڴY"BP^VZPW^V h$!A7/~uKXõ7uh6]_gEyBIxȠ[Eחaӛ5<.j/GSӹt`g:V76r߬( :95]tZk4Z HNIx|xX hAap*FӟUu?;x3MfuSS _x5d2bIqExuTt 0 B,]ͱޜ@YמJ<5g_}\6>:G͏0sƣFI0)27:_zQMx?Z 4s=ZȬ1{ ˊjlq?}Fێ2͊4A|0BNOj4GiQ1rqLɩ-l;gLowsW ZA zFrW8џ\S1l{h{KމW-[ñYQo0zS MXhl$;o&1,4B#X$~Y{&B&h=ҽ[sb'p r*pR$ ]+3bێX[x{u-mm#v~;9_SF[z=JYFmܽ w_4k],d2s+`k1DZ&c²1Z."9Bb}E$OiHBą6+o؛/Z417ʝi7Ӱ1=%.}wqA#eࢍpű_fovҒuQ[}`OՅad$4_zBS_^(_11oWYԻm7yuh7H0]F~[ºk-'Ӧ3?|}'EItsOE*ӧ@‚Bڟ֩)vZ 4ʖC{c}xK~K'G:rMoO>iVп+ssF;.qfh5|^guUqFUrh2:UaaK~u4_zx݋?N44P ˱M2-=Mb3I?;BЃ;v2+s,޹36::H_v:pL"6LC΂kUt:8o1?IZa={ 1 cBİ'Kݳ'.66::Hq]E3Ǧ;`//aFPUܪ5&D-"'84$55-.>N.-sYZmӦ ͚5 R@+G:n:P'P1BBBRRUEE'm胂IJ%SmA'ui(^B~ UR.k7  ,* B.+J1+ק=SPPA1\qJ\=![ ֡x/\hN&nXZ ]BP Y B ,OB4T|z 0P(jxo@P(%c,-!,C7P(76.0帰pUp0IxޢjWTm)J6x7 M9ĠחLI҄f,&ӕ˗-u:o]2ʏ2} ůceyu:%KJ F7=6g-o/w-oz??n#>~J:#B\W[W%Z.&>^xYUGGGdr52!wSfM џw\yֽ3W15#Vz پ5d'X-)=o;~ Ǿ5`mhXh򸝜d Skt= NfyfwX)4yylO?BEBa xdXccV˲&c̲N.VFTڽ .zkɥrUaOxh( go?`BF#D Ʉ պw&> Ęi.%ڥP(?e J1[,p,5zby[Fʕ&ːic;~䝝ɀa%t RWħ Y= ( 2YLv>'vd#Z;!_EkEq-忿ȑ>5?[i=P({6~ai4ZLn4\P(*+u8Uv[eˉXiH|3oV p`ܛ *&:zϮ],IeRy 2 1̞={bT*j Ҡ^3 EGEtM7t344l2`ıL^V^gظ( |㺵 "PT*UbӦ`!iiRle赺Ą*Y q"pIAAE%%'۷O/˲AJexxxb(R P( B0bG#d2YdDh0 p,r\!yED B|_۽s  R);UoIO c]}B521T`) ?SR(J`ǺE^)J#gF/D/ڃB4F@/7HP(,B ? (@6A.ŠY B ,~o@; qqay OF$0X؛bCyEE,'*'.BB/KU&bHK @hLcl4Y44<288e%`Ѩ奂 Wȩ Z.4(?RZZz4}yAo44MLp^_^VfJ&͚櫗.* ck M__^ ĺ._-Z)B8DpS[{~YNS&x^h2[4 O0z^Re;{&$8ؕ~tӡ̆vtτ~zV\Ϊu|s7UQٖF@&1 JIM iCP|"8$$55Nm7b:`RQ^juZ5:h04KJ( _[7C>^+~v~?z3֏M `Q#$_bnVC.WcڬzK 6m'^ЖF Wȕ>5Ϻu&IJMZ:"*d0 iMyG z[b%Dw'wӡ2ė# ,æyB{2ebً<]:{1-T?n>qcGH[OQ )n.11%.h 8,|}O u=7>&]aʈt%r=݁ן9}P.oy}]YpLG1{هss>_p*EN%K]zG} ll@Yr l5Y/Xr!6w-ӟlo˿{O7^d!w[f_ _>M10_[cc{> E#M/4k{*v?;j ߗ+20DմÏ_ݓkB`Na2#dV#,?w.S'<>uB"h3xHJ7Cܙ!aHlvcO|zߴ?s cMΠc|yD?ջ'G~p߹=B{.dتSNr|RЈzkFwBcb6qԶLcb2 =dU3גwX}5&D(-㻫8.e1*Es\DeCq,=|{Gx[G֟,1JNncd $T/0۷歫9=wՂciTvH"ny߫yGwX.̗>왐߿f"*C*#]MhaΔgaoH#$&v~DL*өS'>\~ 9uA1=Bʸ6}xqpcP&L:3 =6.M1.yԴ)â\il!?.sic>xΗWAtSbb0&Pš8;"eIAɽM\1 MK㗣V-|C\/V Bm#yVz%Il[?hưA={zZPÝ ]o}cc{e|չ䴕QnzW3 a1f@ _0vƒy:\}^+v9+ܵcF$Ba08mޚa췺(Q||7aC鲆W r/{DbޒϬ}L9'{^[6|'.Y햮S鸉_(G=f;̷g.~vw>{Qb$@c$&޾-66ekfTI$[:uYdQjJu7 !򡐃#uIe2ѹWr90̞ݻ&jm9X}lFczanxb6$?6{о_ چӯU9cޣMBl29 "bL 98˲z~=z 6L' q,'*Ȅ1cMׅ/czOOՄeI\ꑀ.n@uE9qVB0||βa+Q Jhھ}[phHjZZ\LD*ME9i&MU*quϮ}oa'{Sl ܜNBS(b1ϖSB ||Y~!`Ѿ% V6 R(KJKOo^/`eXRTȁylx;?15-4,\*k>d l2U^<) %NbB>,?۾A_s!Tdqah2\Perc9)ˤ "e% GP( B.HD Ap߀a0s R);J2#ȤڍPq XXuvB U3O݂j%XNP(   1Pwo2,) 1B3O۾7@ ‚@7P(GoLuP(F &Xu3*@ 񂱯x9Kt=°  BPe\hUóY X!`,ԌFu߇@uB4bxA@ 4 !:eB4r!7,XBP(P(#CBPla/ibOPD2wyH @_m  }A5bk ӨՅۏB4&As/%S|iFӼEKZjI(J#CAM;s&,,p"UzMɂ + UA5:B4J:ml|u:XZLxTѺg !F=) 広h6қx䅜S(ʍc\UKP)~7?̻E7ǎG $_N~_SyOzʏi?|,1͗-aپpmC:~P;wS!)79or\7 *#VoWC7{M$ K~rޒ7E0@G~?%k%!Ƌ[iGT}{ۓ|fð9owQԼo>2sHÛ^`y㞝4<[hڢGHY$GCZz  ?uz)>6?/?6K}w9:Vm۷*._rճ_/yԾ?ZveuH7;Y꿡פа=+]ʺaAِG#Zd\s f}!OA-,՝N勞o{F<2eTb#皽Kiу:hkPMʪJa]FX!ÏG3uB:J…'ջxl~!EY_#V`2%1I5 1SuHrUo?Tg zACF BɨeKLZblc]8=] J$.e1*M6I!-zPuY|0zܼGU5<0{o'l;/xb.?nfcI2m=[ ƒ3Xqv9D$ "SśTX6,Թ_u5I!-z|P?hWvMW~GO.APr\A0s0"UbnO>}k4umʏ^[vc2q`҉YM{?m\I߃JrUo?i-ܟ܋?}{x ?;p]Ey0Лl6 "0,d捩ɶJ$ݺ$}) %c}B躈 !Ua#IJPv~oagZ 0QP(#{t X?KUZ ,Bg4 h` _ ( %,BSBKP+ do:B!Xb3/HXAq+#˺" :EL.S(BB/K@ch0`W23+9ǁ+֯fgs~ޑUJ&=z[sa˳/ ^yam_|44/xSOLJE(2ApJ{T-',&yaASdi~Q3?BP|עZH0Ƙ.s_|3Kg.+򜅋ؽhٌg\ܭ-Δ R|t5eKN75?t>aEO;G16欜O}`wK|q!*#BixO}oQ h0dFĔlA@ޱ=5bymmdyWTJH)+( Mj1rރxyHYq|wEc2X]gB cUuP<3[kQ\ꃫ3Zi,{r  yX@Z bM#@~JlHJϾkw #eLsY_rI#R0(n ]wwᥨiWvJ)>2u#Tx\չS-Ȥ^}kHʹxFo60 ðOYa0VA+$(!NuXC UK=ۯ*rv`,MǒͧLƲ3ih#A1R b2:}\0֟qʍ@ 6I~iCi/9wC"%`I*}"p 2K4V'YO?l}'oaw5e115CNxϭҁ2IcLj5 c1ٺu/-4_ş4FciRL08/)WuDzJ@c o`[_A^idHڸ8 Rw&# ٯĭVn1vSn`sm}c'U#P^6ˤMzc]msب^n+Lu GK 1QQZ*؁A%!7Tа"A^P1à򜬬8vi)Fl9%ɝ#U(U'Oҵ+Ҩ+zG{!r V&!cgBcxhhпSқEDJe2ZM)TVRUW:{h-[E ¢{%8àDH Xd[!1 2 jJ4ATtBl$/?o߾F`ULTT|\BG s=c"NRT*Bi\?EB f7r\䈱Mlϲu+NR(P(r [ B.taxBa, LgQ(1@aBBxe[޸%KP(n Xz>\P(D0uo (J#zu_u},uP(F |]XR( !f:!,k@P+Q_Sa:xB4n7b:B \O,BP &IyJq W.^D9o"O =9d2iʚR,1%lA҆,R5fIt̨4F "*+aawޢhQzƓ'@bcϟv.gR(GZ(//d4՚`ێ yyh̭8}Z*HTO=AU7MCXHɈBuB 8Ʉ1Eg+W$Iecl4M1Lc ȼmǕ>)i`Ul,BH[TT|q'_e0݁7\7l{]\169(Tg)\g{ݟ}ξ{q7ʮXXaŒ,$.)YPvHA-Y 0;X<)L\f#u=gw^''ʔܿ^U(d!imoQ$xCڷ}+.r䚞 LVv?!;[ƌTg)@qoisKll }vU2}dj9*1ER+Y!9{Z!yt콉JJ [@;>oѡDDj[sr{NvQ rVRwX-Tg)Ûr/]Mkk``FF2HjX\w_~TڴEB!qM'7  KgXbb OLēߠ`pgᅮ 冯w "2+F>(; @-C,e+4jc9w&h>ّ{s5XS@Ĝ{鼥)!QL-㖓EY[< ֢uS'`J윜k-c^ MJRVԩ-2]Fˍ`: + <ƘœW،h9}ULC}>ߡ&cư!2[oGvhi⼓l?{ SxH^do콪!~x񲅟U9Fkf\U,lYXޤkŪ:f 1 6o s$, z<־]gddLK.MDxm)b ])$QF'*:16Ik/ MbU+BRFB,B_ճѲ>mڨW k(_.xpxdc>zO.42G=KF03K_jAA h&@T'o%I|WWW@dQO>)#p͆=ޛrUG1=>EF>Qj7j_q <\ޣ˗O80ZnӺULLu9˲͚&ݳ'-%vm*_?`\k 胖QR  $,fJB%J,˦ &&%mW}ɈBJ?k˭;^t)%9)""z1](K87&RD,UisPCtᖕ!9`<4$k3Y>j٢ymc -)-aFJCBB˨EOw7jl @\l+!6IpΠMmLWHuqR7r" u-nh\e;4d۰,.ױyC'] x;J:5 +ts|& n\8CZ2A}Z v--̚5.<Ȕ,)Z$$$>hN_Ntt4AAAOo1|0xarz% `;6wr0oGnGO'XjAw"[jQd;etttѡ7T#FwE!**JrmRRRPTjPRJrr2---1agc#.rm!kawm0]s;citlO 5޾ c7*[nHnbĈ1ь9::: "@"FC{{;IIjVh4ZVѣiooGجC ? m=p|3>PiHv#鶳{عs]ۛoc`Ķ:38.n]-mm`=YmAM(Rih5i4BCCj $,, E֨-nht3ph_ddka]J{mWj`gg|t/;s[f=`x< *|BAp wdk4]&sbK L̴YMh@h4؜jdDt4===F sFIgg 5c]#փ9f]swYfG^5wlrt='2.u{悉 W걫f)WMjP̮rC&|Ю.4F5jn>%kTWWqe[Sǥs5h4477s%ǏKM*0T** Vdbwc *j jf0qHGq͒`{NpU]skG=%0P^ʁcÑ#GIJJs ;wdҤI466@VVw::hlj"44'Hv7۫:@[xBzm&|&j53ƣR( hZ222}P3m{{; L4 ΣhׯwTvhh(TWWsmzERel*6"#" BQ}`4}*J߻ |._dr(dSm"/3]<1j$Qrw轛S61Cww7f„ HKK~}KK yww7sRhll0˻w;gر=zλ呔D}}{[>W\2p(D`UNOOlt%{\K”:q$4Z-_:̴5%ƍ43ctRRR5+f^;wVIJ͝;$(ttt%۶@@_@Rq5bcQ~@C45Zfgĉ&jP|gApD%0HKr0SQ^A]{W_bܸtPTZZZ۷NHHӹ~ތ F1 zLԅhu}9ECNIPb&''Eri{zAkjJvOw7g 22kWBY hb+spuΝ=klӣtvvVQܹsL`1¢spchkkt Ν;vA[(a@7t\tP' F ^~ߠ >nz\g-'MDGGFdd$\r"##( V{ -(0P`ZStޛQwJ;ޕzb WMhr$;U:%`g0”{VKNN.555̞=nmAMVǍ9I&qUd߸q7nK`` 9T<@¨Q˶T:_*؎;Ɯ9s#//Y3gw^ƌcYV| ptsRWW@` ҍԃIvtXp S]75b&M᯾ EQHKK&# tuw@B(:8m鿽TӡyJ[}YpKs>hgg'׮]6ܹcjJ+Wvā7np%eKOOW\y%;l?'@oߣܹsj QTd\v#GmvАP\B\\,Q|!#_`tv84m9^yd)2Xi$ws]vCbR-VKB(4-ٳ̙3$JuU F1vXZx03RB ;2u>eA۵{Zָ 'QV,j.\8o=>;[Scŭ[7y&1cuͲ->)줻PBBBꢲyP٘̈Cb(B7``` a\rޞ3% pB1 <&wfvjIVXj'6} ygeۣâ|s W"].Q܏;}PlyPƏRPw Qp$WvWΰc:.;|vt+,rNsTtsb_h+v؋N]%wtDsܣIN_d;&c$t\$z}6Kpa怄3{pcp,w^U: qjEϾfz}fpo8x>=nC x_܌[3}XOAp g إE]=㐺;8celA;~/8c S+uTA<anC#(`'t{+0`J'llsvY10N:"pBi9OοnGY` -) ֑@ 1$ `Ni^ ;۔ 0Չt"c@gZkDz_3)|A]$ Ca)7=Q !pk~?.ێ6wqa) E0q{@7K|*('~) :Cש-?H@ aiY [@gS6E/;RnG_,77./8)οUr pan.}3(rݎe {_gwpr!{;C %prؼh_,o~6'MG!11˗.g2& @{`͚59/ggnಁהg)tn6oz)Sw]5EAvpT(e\H3BTn<{120^ x>HPЪb8 kX:(ٳgf{;y̞= Uh=qi i!---ٳF"##))3/0(ܜ*++YbѺJűGmT./| GppWի۷V,_NRr2`*++cʔ)end˚5kطm7[tߪUؿ ͛nj3=ѣGyeĈdddaT?}esM-3Uz{qYY'66Нǎc޽h4WNh=wioo'r^z%V?#0(bf̘A`O39yj{ۅλ丙Y𨾛]5(Ft >#qp| <րӧOo^.]FTT۶n //m:;ơC5,sm݊VeVvM ٺu fbڵ蠼%en^o , ,, .;/ZZZxp1$Ǐbn֭[3Lqq֮}Tc`cǎC߶ EQ] ==]vQXXHbb7 rZf#GsǞ={ػg׬[w#Gڵ0ępEPT_,[(}yyy=#ޣΝKDDs~e̍_Z8s4%%%:.?{O\PesR[[˒%K;v۶ ~%% `jjj )N8tf;F` @:;pyn*kL=]A|F. ELow%MMMHKM>y!?0{?HHg钥7%v2w\EAvh qqqu~;{׈hZ8i`na\S1T>Swp旔0v*پ}<:QQQt޹Ԥώԇad]@lT}*55O>)[-C|p@'p2Hu}lDkm9\py$CqAp y4 }xHiiiFiSs3cǦټސ> Z'N-|{s$''q PS|ccDdDEEz6{RSS9{Ns t(ϖ20qDf`;!111,Xw~[ZT(bT'CZZ---L蛠hǏ'/?_?o sii}!^ `^o <} |9أ( jyqپc;Ο/UgVf&+hnjdffټސ?.]Doo/Z`O ؿ?裡!333ٽ{7464}x-\lڴԽ\pk峥L477Ldef׹wp]]]\vrM_oG%rM-əՇ!> 1|qwvv6۷otwuq-WwnMf ^l"|lY1q[s`n}snQ5z*#22@Q8rRRRV3fDIڦ8j2kV6})nb츱ܷ>ښ?`D+f=[DFFR\\L}]C{bcxض};>ZE!)i4-V>[4c >c),*b̙uA, WxƏhOVXeA|&1(wmvv<AL wx~^y`fW2[-}_MpW'N U3,  x.\χ^ecu$΄z@y<@SAa=`R pi\ѣVxKr9/Jt 0ALa~? g <.A1{r ؁@jh4o"@`` H |$@;(0dH Ag(ArEA hZ SPܾ}/[#;^r=P`~) w'}g_ɨbnY(tww3zhZ-===^O@bbb|2aaaC7, Sr%BY'_Oacs @բQ"$4TPQtXn:!((h( j j "<& A~lu$=`` o+CFs=>O #l KG_J?#ZEѣG_ ZMDD׮]k҇=4prG^mv sl wwwn7FjFEAۋZu@1+@V?q~(0G?N)RYA=VZE5nk!MLLl)E`[i?qq1,aQHAVZEEQQ5}?y D|%7j_ǏbH#>R zkV@/ { QYrsE>gΜ~O?5_uu5.\`ݺu&O˄ Z;\[[^F&zŞAȥ(x{!)y` .ľ@5ݽ{CQWWG[ FN qxogjkQ*6WUU3{*cqٔumm444#8l;ikk?uȥK]Q;AUqXx[ȁA]E}Nܹe1r$8Pشkևp,?JGR$==cG2s,Μ9CBBuutv_ ~ÿ&""Fë9?+H#`nAӦO'00p]?yE^qᲟ"+Wd߾}f\z{32GrrrLgPYyv 3gAAKbWi)SNe͚5Fundkײo^nܸO^z FÉ͖ǖ2ܿz5%((bfΚI@@ IR8 xqA?&d4tlW[[_MKclZZ_^{ {N/̙0ٳ<9S_˖QSSC{{󣿡R+'iiiFݫfƌ̜5АS'ORU]V!''RNEQܼuL> NrrrHO0Zs~N<ɱcX`h ӦNos)VXNrn$wwn )7.#G˵'=0yy?>@jkOr1/[Vya45516- FB`\?gΞy0**mӦ|2:;r (Vɘbcc9Y[K@` $C oGQQ>A .O_~_~xMGDGܤ/ 51?5չ?PGAbWFs̝;sr-8@ݬ\/b mf]ʘ1FoܸAUu[B`P~VorrFΜ9h͖͔ʛ:e { hhge}YtI{}:x?eI$O`rFFj & Z_TTė_~ILL iiHNNBrr qqq&&M ˠ;vl:::Ū?~<7Դ4Μ9Rf&M^F $#@AA۞C1¹U*b8%pz#*yEŭ[o&%9K>uMK#;;1c4m]^Hh(gp 2g߷;--/a¹fo\\+VDj9}4vo~dYT֎%DzoݳWN3@v yfVZ_Eyy9a(Tgr^,**ʙ?7kW`lm…ׯS^^ןxХ7A2{lF'&dUR ~`i   8\>cTV71]L6Lۍdڵ&M11h AA'=aqtrQQ7mbk!`8=kLزe p&eg?HS_W琝&NDA71G}D\l,%}sUOm6=BRRO>EcKf̘g)7Y3g%   8WmvFõIOOիfkhhA.]LLHƦ%77HN8Aܸq?F>րl23 s7~~Z9Py֋DEG-[dR]]Feƌ׀nRzNC   KQ ::ۄGX'Hpy'B(tNGG/   >DwW7Q\zАPY `5g@RZ݋պVAAAA\FצI?<    A   0ָIkk+]}KT>ooooooooǾ!4$1c$p]Z&.6"AAAAinj"--p\vqL8aHm sH[!H[!9w׮]#--iYK80mT*"#6FAAADEF P9nC q(AAAABnyk    #$        0b_&O^ 0C?  ]/Iچ ޕ\8ުׯϨ'4,+W0gNMilh,YBfV`\82&Sxb)E~ Wl^ JKwRy,Z C: Z[/DzevO/; Z \Z;ᎶYl׿΢<yFO>oyG݆cȑdMbႅŹ) IL4 \[0m4'N{wh5`xAVUUV]{{;o&9spl۶M[[?6x N7-hmme@Fyy۶ok_{± :tza6nHll˝2A觭wy|[ݻl߾}Xs+g)ళx555h};JrssQK-//3fؽg7;vsh.W\]<#.ZO?(cv}M!;PPLpv6h8|0{vx| G/̆p<=RTTDXXz d-+mnʌ3N>7Md˖/9c4&L`„ ',\ ~r]#**ŋ"EQ8}吝"rs @W_QVVF.Xx1Y}T<822wn!?111|6FvI݅ DEEt2̙c}Q'OwnܸAq| _uֱ׮W_Vҝh4Z.\hwYwL1q{Km0y0~ 'O… TTs)uj /SVVL6 5P@,[#릲~+V]q}滼V#Gl;uF%r62bbc))Yoa}kILLdҥ<#V׆f~m{RVVFpP/&''Oa[ƍ#zD4O&33ӧN1bҨu삟(fnevpkF9jjj8p`?] >CՒv>s.]ʪU/d߾},^x|[6oDb{yL3ϣP>hU)ڨ(gر/oHNN>ٱcCU[[KyY_ETT46mCj8XyGEȿ #R4^Jʘ1lݺեCNֲg~ y[o%EEH5vٳ;vQy}ٳ{7c?5^JYYcƌߗ͵ ޗ+++lCjj׿2)E!'i}U|dffcN~a]~x%p1-[Xl++-v^}YpVZL@K<6myꩧP@ᱶ~ 7IOO7> m]_ ]]twu1jT"O`>OνveʕnΑT?̍''2f ER1q$ʱn)):jɒżuR-[illk}hjjҥK} Je>St#_{ッwȈ#ihh`ʔ)F2۷)/+||Rʺ ׾FYYoJ}VsqQb\Pc=zbg5#"~:ɺsڵDD[Gaq4773i$e}ߠt'Xzsdgpa=[1bΝˎ;Xf:GRt1c[|IOO:Onlkp->cycY!}qyMoN[ڇ+Wg?d. ǧ9F *@(F772a-[ȑ1ܸq樯4kx!霬՗)Yg%kJgΜa̙egN6:'YnZGqF~0q 9Ncc464eߨQrQxD+$ SN36"}6рn$АѣG_GrW|'#SI(..63йϸt껦 -֖ Cݻw3x>=裺Ja S2bGQck1bklhW_3Zf'NBQ0yȨ(ϟ믿nSO=ſ1i$eKmC?jw}z#G 秏O7i-m56]Qva7b3xSYHpӧMgmSWWǎۙ6}~}rRN۷ ""F>c򭽫 >_XX͛ix֋ټi36ʕ+8|}! r6sNw;x2sL8~8w8~8ʞwŋh 6 IDATR#^IO~^;KK9}OtנW, +:u*Tyfn޼Ikk+mOƎ۹u.\?7Zwޡk'޵c}7rihjjm+i>}:w젾zvn=fŊ}ԃT\p۷ͥ.]@RRMF[k7aJ@,[|'̞=[蚭Yk~+Fe~_<`{6A;ݻ̾:t}RG}HNN ?LߏiO`L[.ѯ>v}|RFT|駀n"G*Y?\w׮}۷g32EsFYgϦMo 5k>ƍyQ1)|Sbʔ),[uVnI(:tx/Y_?̇!t =g7Ϻ`\ycVbccy/سg7qqq,[䶋/t'򗺙ɗ,ܹsyy|l'c|=tJڗə(@uu5zO0>@Ѱh" ={+v-֖ Cssl߾+V @Y7o;y|G\rE [k7aJ@,[TWSS]'/^yihpcܹlڴ{}BO*VчꗽaM~Ȝ9sHPP ."̠#m5I6]~^uׯ3>=λwQLʕǓ`aAzA)lyE 7c\vׯ8(*UO`W    9$        0B   0@    #$        0"@Qlտ֪AAAAŔB1ZiEzFכoXݟ& ؇_/m|AyJsRiׯ mKx7p\~}F}}=aa\9srl^oJNcC===d2:6a),^xgטW`1_SWWgՖ7n_@hh(˗/#;{MۼiMM̢E mƹs(--ѣG5e ',,̫v ~ĤI?>#FpH/3 ϯ_c1w\N=#ml+0%C07H wzz'yjVkoo7$g_m۶}koԩS淾ERRܱCxFyy۶ok_{±|CVSSV~>ݼy={6ݷ*ءwH{[o3e'=Zw>ڵKXWؽg7;vs lٲEo|[nm6YbMLʕ+TTTPVV<AwO~~>Kwk6S8v6lo{6CG15 'PL@ʦLpa @dggV߿K,]y@uu5ΟB _ (u}FQQy/ݻwپ}}滼ݾ};ӦOg}=6ȶۙ>c:=~YFFFbKIx k׳n:vq5~<~=]KyY!!!z]_u  #Yd o>h8z(h%Övj`10{R:nf,^!a(bV,3P;zrrr8z(j~555ݻ֮偵kٳ{7ү?r;wd…<~=.]үϙ6m|ŋ۷waхtqvΒ%KwԩS|&*c٘1s&N>~Qs;UUUΒ?;طn^ee_O`ܸqrnHJJ 7Ʉc")) Cn8枡? paUwpw| "##1rH9ȑ#}hdee#++yGkz:|0FN;#..ӧO 6?A|zN4999y.RO{;2zL&&$&&Ӳ… ѹKF$%%a8y⤦zWIHNNÇqDk!UUU@m_?&O\c̘18m@kٽ?8wF>}|;wl|ӧ[_t ~t2tz߇z {\BT>fee㳥U˗3PSDGc Itg5|tͥlݲ_}%g_xԩS8|ߋtBW\[oE):: ƎEjZ`q/1|M']tu/GC۶m?殻4՟hx ''#_{KttzY.{~aը MQgN8s4Li}G>~ 厊P ]| [lEAA._v W7Æ ǪU&NԔHhJrc*š֧S{^CMM v16ϝ;סC.Ν;*!+V@0f$$ByyLj jo~~>sUu+Ν(jZJѣ 89C /y?x`[/\ nzOs;cƎC7Caa!V+NY_W;w}w Tg27sРA?`3uZ KNB{ؼi3 nތ*ƽ;zoɓ'QSSe˖itXnwkYk1꩕DۯB,[ճglڸ8s V\бz"55"`==0`}<7wСCquiF\|cƎӦM7beh pRa9_|9.\ ]سg>c$''c̘1uR觟~ ^o^ie]ʪ*t".p hJ8s[Z3B3 Eтpw81@AWkv:84/9&Ob;(2 9hhtxWj -@T0&v ~?dOeAUo'ϓcک}1jA\8=6ޟ3gt9Fpﵜ;J^"ha8pLP8~"*"B ht89P: 28ԜkA\և# c^ |SM ?ADX/4a3|A@@D s1}:\m +Sgwg#@DˁA,Sr@P )y  p, ȁslhh/3I A ZhON޹>#NAuHTph7Epj9q]VLp%A-rrp< L p>9##H'98޾ /Q@ADi"$/bOSLEѲ GA-h4g?.i@);x8yy{~A4 cR$ 9Ɖ gc5wtPTA-r26c)?d9rtR4MHA-؇4-cRT8pƉRX>LpmbZA$@DB6".8`3@vk><; FE ZD 6g 2yQ@Gl;}GTr 9Q `R0N29ŋ׃A> N3H: F1<,m& ` Dօ@hI# G:CV x@@}D,x]4xNrA4D 7IX^Vr /D0p&*'e@ }Sܷ S8Y dzpЃ7D7@gN67x8f^c (69j=.`QfTW&ͨNˆiNطmkV6 fU7wTd9 ZVwr1^X9ZDDilsgwuzE GA-G8PŸJ?c=0UMgJ Mnz\d|Z%3h=푓 On\ m `` s`b=GF45L=2 ZbY hqp.&(NJ]8v.^}dfH#`1՝v1IcՇ` yC1~O~ҥ=C8g5@C@N 9ฮd_lFUUbFޣ u9RZؕ7K86TaC}xU>%hv, .]BLd/(*,t/t{<:#+;[UwAARڶfIt:=N oA. _[ (|eeEtL&\x:wcN8~RՎ٥ G]ѣHKMuj(t\A׮]qxVB۶AAAeTII RRRm"=- iTZ CDx{Vl6ЩSG f VE~Y,f& :vDQQl6Tg&r\j7Jk򨼏dTg=̧̏rKׄ|e~_r"8n-z4]G^ydiee^y%i4^xo(-,_((,tl6"'7GS:&G;j_:/ة_4] "x@ш$&%f8i+jE\l,A}+ &2LZ^_8F#ʴ=4I͛piݫ'&NիWV _eghl3ݫ/>:92٣\*++ѣGWTx48StPҽʄIJ`4]ӒQ^QŋӧN%\(SNݢXp/^tWVV6FE\֭v8t*GyEo:.X 뎊vMArA4"F6ME:ż )[V﫾.D|^'NGL6 ?;XaW^ 0jØȾtK<ӵRV:u*>[E?ԩS8<@_ w|nNM:W[oulMRuڮ]&0pwc-.iJJ ***`SNr-**+69Y]RRrݛk5HT/6lA~W i#~Gjjjśo? YwZZ**+֛ rA4"<#6.NrEV1y>q!?AxZՂ˗/y|g 峵~Ȭ]  ,DLt4#FN;F_ye zw!;R,`֬;o^hJK2z,\0g] %%xg0p@<3|v<#ݻ7]FMdYri)/^ݻ!#=ͥ4doݻ=nu7>>C(l?sŋSO_>xp%RSFbuV`@~2yv,[Q^_|]=a!B'MgݻI#vrh zG(.:2ew1|PlۺK}ӧaݞur|TcrEi}} aZ~i:Um7ݯe<V&S?`V9.K~z˖@vVzzAA FDCg_8!)1E jWAf XO?&{A h7yYf F B` HFBnێX=\,0?[cm4y2s /9sF?}$'%z^?U5_oLf3VYz۲믽c7m 㭷PQYQpcyWU FNy&͛ЩS'7߀dª5kQWWz9E@Q{3g` `!~{]8}4֮]_K%-ARbۺ 'OK/h,8_\;fށq̙;&OƧ~*'4yQRR6A7^wuY߰,ܹspY]V_8 Z]f:5[%itfμCuDXt?b=Z<38w^x.m^KKXlzUQo (hĴpA4fQVVZo" rA4"z^ZqYdff;0^I.FVO`L3qsmmtp'OCPz9_> :w-͛e}aѩcۻs0uke=-MNH߼i|AmCذq,uܿ~gux衇,ٻw1;uo ۷oֲq)2ދ` ,\&شq#|A!lp4Pݍq'xmڶŬYw]d}kVO?Ν: |~M6[qa7B̙3~l6 >C̹gX"w~GRRc&{J@NNJޮӑFٳQ]UzĞgZEm}=f̸ e9xTS]\Tw]SԹ>\tɳ>imK/᫯_۷os?&Uo "hh@ F8TVV]vDuuؾcl6c&CY'(Hr`ƌcg`Ȑ!xg~\\C1&e3rM&y8w0Q^lXdYQ=bx MPR\|޳ib<'ѷo_o. ;;Wc 9ԹA:?sXo .!1G||<(xX,Mxu:"|\]t]~?42":/S*l6|q뭷?AUU%jjj\tGDD.+nz?BDбc''pij믰l2,_))me2u z==.muA =Ƒ# #d#fո[0c ̜9SLᅬ޽{ԥ#2EFD`=ܦ"jnrY2v&Ò%Kpe?q<.|b_r+س{78{~[rǣ9zDCϾ{qa̹cb1cǍÛo"k?~,7z 7k„FiYKyyPSS65,݋qk*Ə:?d|t)J.]?^Gxp[n__p!/7w[#:옲x7< nvCd sŒw{K`޼y.N7^EExqӘ1.:]E(>{5D^▮^狊PWqٻW:Uy[PYQB[(/+xݭ̩S8{K] c}}d2aU6qI0&bUءݻ A 6<TIMTsA5g z^PJ9u 1cy΋.h:i8s X^_8qgGx:2EXAбr ?Ӧ5YĠh{1eYɧƋ/ӧOC^\t,X.Y``={  ɓ]騽./a0F曧#:&͓&S&Nc>y,@BB 2ɧL8=ztk յN~t EDdNh_Hza,7&井|f wua9R2]felr^ 8i >v`1())AII ƌ"'BEwUX0UM-hӦ5ڵPNLqfggM6.שKyAӡM6P]QQwBii,i`/mZ'##=MmXT"lI*Jߕ=u4:b… X̸U.{hDqMr߲?q ^DCn:C)ܭ4|D8zNoʁq8?/=:|D+"LBd|>6&I5\~]>}:Lf3tu8q6=zpRѣ2mXЭ[7G"*: ]fݻwGjJn`\ `+ Z`0d׊I8#1p{r6Ѽy<#66ϜFRÉNtt @4c1skKKѹsgo:N@2SSSCAAVX])msg"P[Ik0_k=Х둔x69E}m(GGG;.#\2!"#'˺>@eFVG ZWa3"*}4*JN|WQ(tk5C&P_ վ 𩛜AA"c^My܍ |cEx w!%?L\dCu hȧYL4 Ls`U.9wC7Xc!(p -- AD`# " PN(4!l8#-[~_:f;DBj@S Ida2Cp q GADcȾ2?Zi7a4EhC,sc@]SOc~A|c8d05juA @1A7x֘_5D1=iZ+'29ʅT: nmTdF6sk(eL4DZ`׺*ջ'9k~&* *!ԍ Ѱv  h&` % ) v׺j2a%O!eOÜm%WMu95trdڐwx^'h6}9- C iZ]@4}S@EE>u oƚjtU-ۄs[RAG K"]%"`iPCV)⼝#LEK8!r:aaEj%*~eΫC@ENU1*R|u4 4|a4Oc^-+][uEDd; D&p :&gx|)1LqL UBv > tFq8}$#=;ھtorN%wMUu|ԦZ<.|x|r۬`*&I\O ?yCF]֏SA]q@27 h|r8AvE=#!Efh0!BNc XVM5؃)^?je<[)nƷG,t$x=Inn&k7 ڔ ?A'/yZ~䮔hQ2`RjZN?DQ Z$X AD4CoE( v#3r()nt c蚜IBFPz\aku8<FPADx GAD43iHGǂ(#7{a~M'yy[]jxG\u^Ay\mM,3dA\]# *#AOcagM],^eU y:|OkyZhSq 4X sv IDATa4]$h^up @r GAUHAGH>o5 BVY~yTϗYq4 !%.y(KYp5ҦM+] 6G@C}=.\?JWaq8@d%9cx8pp:ׁ@t@T8c,ƼMɕriBe~:rtR}"Oѭ ɣ5km[l__0k1Jk!h@7|fXQ WAlohm`6+xmA,cD8DcW>HKKETTlf hhh@󈍉AbbR8TA]8{&s5uLt1@d4X+uP\Q h8N`>._)ߋU`΍Zͤ X _k{7|8YOm{ 8DEEBg`4@g+8&DNg7S$87 Ʉhc3qu#9|tTqzʖ!11iPE ` в}(Ypv`8"V}DVC laAX e;ÐBct t4S]^Kwǀ"z_.:ÁWȸYjQ8 !OXuHٛ>z`00"3} G *> (-+C+] +y_w CLLLXTA$l6ZnxL&u0܂u !9|$./4r04bah$gAY4ÑL+] 10`0¡ `P[[N@P__JHO31 `E>\y'2 ^1¤QVrYzB0Ev """t5Ơ] 5hݺ l6L Kƣ$TVd(&%FeE9ZJtlMHJLBt ݜCUuFǿjlBVu֢F0/ ArA\A@uM :v SCĦzw,LV` VZB~9CI5lٌu3ȿѫ)Kv6mڢ H͆jddjEeUg0CY@ Hi(?\p' >#dz={A0`2a!2⡎7@``j0"ϣIɭQ__(saa8*¢/\XQ ՊH$JDuuQ[S$4pBq HluuAIpD4?\: &s|EHMTUU8ǩ84M^k*E}}~G:u eeeHKOǰC!tisۦENN.  a-GK=rNO3OQcLD̾USWY sR Ua1+ |݀%8 44h6EUUh ^[Wm!!6#YB t6q( ~?%I(((7ĶmPQ^iӧ#!!^,[L8Af iiW] vBmM LVP\\;vȎ`0<6*+-î;1qİm,룹5p\N?N:S":B'ard aF  V ft&F6Tr'ELA]2%=d_-(AG@7~O?4xzWT~scqa0`(-;wUV7nR 'OvYw9r$k]ӦMCT &[1 6E^#5- z>l{~7Ĝ9seF`wζT|;xg]ڬu}Ьf4HnÇwIM ͢"A>_\{-:/;ww߅Çmp?]vb̘1:[aIX|9֯[&`uiӐ Z~3;;}n2mSzS~GAA6ņ l܊Acrڵ m67n܈#c<444o={0tPպpМygb;GBV0`Mf9R3\n N)T4(S2ax$%%aҥxſcoQ_}"p "%5={]3KgRxƌKdAwb90a<>9 1.]:cٲe4i"~0|(_~qơ[n "^< B[amxP]]?DJIMz/m66A壮juf˦+:Hmj2sNydg#HfE׮]pe8pF׮]TW@n0bp1غu+6l܈wPSS5k`̙4 e100Qcј瞅 TR+]v6ZeرcPZV0S 66uuAbb͎v27D\-ׇDֽ; 7ZNOMM߷n!k}?g͛7cɒwc!95^xw8B-[ {9o~ /^9ի׿ }Ë/ GAuu^y|g7oԗS/ceClYbk׭KC̙w >>|6 z>x=\xK~ÇUV=k`T^[(رcVZgb٘n9z(6l؈Cd`I:mbbb<븎= 78LSAE̞=~1r+>}7_ F5j;2ߍ5 0|a.e$s; ج>%֬YƁ1`59Flܴ -] Mm 8q~Y|.e9k6a2=|TG>Sy22x?̞O?bٲ(x7|3&LΛ6mg|-.Q8abe ׺EBq ANnwff75#`!-=]>3EEԩ#Qh"66p뒓z- , F={"))f٫ժU+xXdG 0qYs1χN#-= z>x}䄵m6l؀,1 񨬨k&33狋ѡc@QQqt79i:|8DN#ƍ?{TTT࣏>Jl݂/sEnn.>c9Ywށs砦K˗c̙C[oYys%=ヌ+c;UVN1ݺIǰnZ\ '9Exuq- Ղ۷w̛7<tV^~;{t߿I;۷qqCmbK_?3a8=8C;w #11 o`b:ի;:Bv`߲hS n-h׮ka.]b۶(,(DaA!oۆ]ҥ+oێZ`ƍAץo߾ظq#Nr & q>WKG~ӧd2r)n݊=zzȭ\ FfLmf6Q\|}ySU_3Mu>پ=~pXۦQшBqq1֬]暫ӹKl۶ EE(,,¢j>ڴibWGzzt<-[g.iFII JJ.ȑeg_mۦС_~9&;|.soѳgSMOw8кukTT\~=s ?`Ҥ޽FÞ={d_ǥit/1yyys=h̘1%ۂ лw$''c֬رC~-`媕٤۝w`؛ =l֡ɧT֭0{o0p$'%ᡇONA!66ii7w8ݎ' ؘIzí+W*-̷cv+}c7ѳWO~3w^~(Ϟ˹sNtmZF֭ѱcڱKֱsNfl={Ĭٳ &bU畫Wbi'-mP{sll߱S|nݲsC^Ѯ];̝svtΝ;1[Qي:C-QWj 1~JME8f޽q֭_@Z\W/O;w7DLL sgUpAp_%y0i ϣUb"rsrД Qk.;[''v.bqq8x }Qn`ҥ8z( "=͗vڭmߎ<1<#GOm۳GO‹.mUeUQ~?^>c݆ 1O ݞ޳g/MƎ%KEuu 1ٽ1psбcGt1cb5rvE}m6kL?{DsINʐQ(  R!C**Ȑ"C( 82jJ m$if5M_#{r<<︨ls'IV_ 7V.#,,L߮t )BppP))) $nݺȝT *#Ǝ@TWިUt /ر7͕->!>XM0g-Wzs =."""ʕX5UwyJOʕ`]V3vҥKh'g#$$Brw1h SSDO>+~^ĕWШa# պ/ڽ߬-r 4*ɠR 3DRRΝ?3g`rIIIA`` ^ Pw-VL4j|!ƍ >L} 9V}K/>BЫW8U%rJR oWA2۷@!S"oloD8Tg<~'-GBkSشy3{o1mڴڵkው46o޽{Ѿ};̞!zEw|Yơm61cuN6) -!tYLI9jA- A@Bu wršuѾCGgiP(\\\4봷o٪6oތs/ A"-[Yիa1zZ>X Ϭ|Vd哽Nڐ˝f:tQYf&\Kqؼi6mYߴi3lټ &&V7 ^bK*۵Ï'~ԙ:tBM4Ŧ͛qe\t7o9^;wpt%kb 7v W}vZs ~;]s5mnWplٲut 3Xv-J) S}4#Hr'tKnTf#5qF;Pf|JY}zXvβ=n !CwQM56>"?VX7;v‚ p=խ7ǿӢ {UC–-[?cucǎŦMɧ\r0e\pAo>dR+.|b2$ :@&cF{B۶m5_2^+Y߯o_,XnO-[&ڱIv=cYީC|;v`P(?W\zaaBC |̥rl2s4ׇGٳPR% 4X=t`(JDdYK/-[&RSS &P)Ux=GzG/?e}}^ڽ0JD/B J \<.`AB/fwd *I$LPv>Bxb}>~sH2Jͱ3^x gm޽{p1rʞgOg` $Zu\5]Oԯ_7MKk|4;|[{Ҙu8=Nsc2x (J' IDAT %dB I AHk 㱈ǨTZqzJUO/3BzhѢEgb`k+Ne=Ԝt&oW9ƌ(=|)9 HO x I IRB%J܁A:BM# "_.@V Ԭ5#"*j쎌Re8W77$Μ9S|Ϝ=eƕ"(a#0J{6b+yJnnI W'8g2.ʴ"urR7I5@g'ak@IYӐnn#Q::ﴟn=(sh EOcͼ{)) }#rhWҕ [u{Su@DDELRmڶL&Ps սؼjUMv5 IWyڶmŤLE )Hxp.!"%DDT߿@l&uZޛ=jJZܸq pm]$""@QXz-8qG|Ru $Z?GRSRH+ myf>>HNNRuq[^8HCZZ:e""1EW!""""D]ka ȁp""; RDfFBD r9ADdԽ!b`0- "*4 i󃧇u+|k1y$&<+xjD""P/zZbjOy)Jv 7_ެ7os''OD=-ZعsΝ;ѫOoʞ_cǎEʕ?= T\""y62sZYz@2uj!))Ѭ4hs?“'O SHKKCl\͛gtsjއb40o~N>A0ܹ9cuKֵV,_II_0sf=WQr&%1Qq@٥Z3F2n V/Lo ~5j$*VnA]uST:3jնB@lٺ֨ڡAnQ WHH(.ơi-/ yB֭3u, @-{l f]TA "*>cdȕj ;v+p=ܸq&M2Mp,^+RSSq%3V>!>ظ8̝3Gg5k!6&&_y早7kukסIz&Mn:4on[i>_(D]ªUѦM֭[a՚UAtt4V^c0:`ժU_q}̞=[Ա4w^صk'ٵ {2?իq}pK/6{Y;v̭C(Z2eKDD3Wb%jVT\G2MM }{1e$TZ#X*̟?*TqtNCǟM_k޼9͛&7iŋ޽;dr9͟ڥf}xx8$I»テL}~ӛW I2Y7Q84j @Æ .Æa/^}"##51Q~ r 뒖.]*RR &PTHLLDCT2.4H*BPBV+yI_< $ܹ$Y^36*{u7n7OHm@DDDf͚̙3Xv :vd"q@"""Qvm􏈀 vl"1@DDD:틾}ںDDDTH5ȁ[JNCDD,Q-U'h_nƮDD<*Ӱ S@+-=0UJCDD]DDDDDDD"""""""@`DDb(W М4DDőT""*Ll@DDDDDD@ """"""r 9`bVN+8 U !8"]'HK,l@DDDDDD@ """"""r NS ycˈ:6i9@w:!""rD3 cWWLǘ~xzYc ,'UhB';@VZ *n&bڞdymJۺ8DV2LݓT50QHCDD""Z}2/Nf%o[ny|~2+ "b""䄮 *bYL拿?ں(DDd|jz .B!$Z-r~Mŏ$I2= Dp2'i&Mdp'bV7'GM`ȰaaӦ/ ӧEP:u3gNۺHT^fG0RNJ@VQxܾg۷u"ziRQDD9}ߺg߷n3och٪N>}1@DdCt.SRp! 믿)S1uT|W8tMУGOsx=|puu4,]VZaӦMXv5v;vn/ X^÷|dͲGok];wb={fϞuk],ŋq!DDAq.֭݇[c%t}-] &`8~8*'ߣE:y7o?|β_oâŋqOpsuʏ?6{DDDd{2TGH-Q""*J>>h߮vکYkNth>>8vFPbȑ8v3v *U~HMMŨѣPreg##59ޜz^?Ǐ7IP^}/W#G1܈f͚UT[o_uf,[,u3M_{-[ĤɓѠaC4h'M¡Ci6c諯"%%LAU :?&ݾ{Qxg1}t|wf wPPΟ?lѸqTX'M!rB֩U!!:u1 ՛|ejؠ! 菰0жhbX>F+;ֽ{(1 ž=!@pp0 4 Ѯ]{shذ||˖Ž{P|y@ttej޼ ]߮]{:uR'өh׾ADDD1섡]Cƍq& :Tu6XzիWu6zզukY'XկЃ%K_EJj*.]ر1e 7b0wB+ uZ!fcƌg~> cƌ,ܹ ,^s<{K,A]ѹsg,[wӿÙ3Ѹ`8qRRRY߱Cl ̟?ߺaƍ萏@}@DD[ٹhu 2 ,?]t֛kÆaݻ|}}1|pDFӛ֔M@}cʔ)Z*FiQ^0mT^ ,@ kp̙bQ6S^lnnl/ \6l8z7@7ߚ??Zn !vlߎG1qd&p&VZZjMPj """ۓ.]*=zd0JDRby$&%B}_TR0JD/BG{YwyIkz^@ 2$9 B rd:#TӼR ן( ܼy 6DRRT*4_Ox&wQDvvb~|K $I;Ο?ի)='Lo=i09<<0刺4ΙÒ2 JPA&A)F({^"W/B=/IIII([OgyJlT""*ܹ>n~:||} nŮDDD֠P*m]BT(l]""v "*aMz!r՞o*T?\%/[Po'#yuQven3 WV p ,Z?+q""r\7”!Fʞ;9A&c;* 7yWbi_[(c.s͍l@DDT@*y`I).WvePE!""+03`*%9*K?}3*Ƽ>m] bœ 2n,ȑ0@DDDDDD@ """"""r 9DDDDDDD"""""""@q!%@Z2Q%"rDDr B^.LN] IIIr 23p!!$I|k|U*PprrF`:ϐ """#]s5kbÇ`xy{ٺ8DDT@ M5V{`MǣN` Wwww[H=yf((M}DG`ٚ!:i~~ey 6 .67oބ.f?7SDcGs4s @DD@S'ͫM"Ȏ5kgϞYy,h,.6.g>.!!9e2T*}#..V>&&!fqxgɧ+q|$ۋ W6SR%{dZhd2 {vcǎؾ}N5kC=fzYs5[UkQBY3܇7ʸ IDATw W6{[}e3_V-f(.u:냃*DDvRJX|-][" ˖/Cʕtu\]0dtMn1c&^ w2 .DΝ;])SbXp!*T(W_{ gΘ7FʯgϞ$ M {k./QI'-]D8}4Ƽoi*3Ie$rL(! zμ$DV^z^`snBRRʖY^J^&|||!ۛcQ3g\$$$ٳXn:t`|32ҭ=JOOuJ5J~/x~~etܩy*U &LȌDىJ"JVE!""+` RُtdS!̚5'4Y-[-c>/>/.,1(ԔT_xyr`sJix)f-VFӘ5""b JAǶ.QT. YDDDfrss`DDDdɑȁ0@DDDDDD@ """"""r 9DDDDDDD$""k"""""*  ҫs+{CW!"*mQab BWI* !T*ȴPDD8;9C.C$6&6ip1@DT( ddd\yxzzں8DT$''#]ɉ>;DDvNTǨYy!)1E" kW \n"Q0@DdRSSP|E?ӧOm]"*>}H@p.m]$""*>>Ȏ)J$''iOl]"*d(J[ "";&@ZZ2m]"riHKˀ6Tl]"* """"J SSjc e2+_r9"a-BDTBy%_``\| xx)2|||l]"cl@DT 9yTXΝ;0iD'N;w=ؼ sB1'®Dd- ={… svuwnŋѳgO̺._l"X@D@Qd7Ƿt2'u"#7슋 "E`Ӗy}y3"E@$߿G~dff/00@lrڴi] 22۾ކc萡r&Jɓ?b䨑ޭ} FDǡiӦ޽;< y&KEŔ:uYuz)<~lbc BJJfYrr <ضmMiSa֭8xÖ[гG8ƍh|q#6m5Wi?~W#OOw޼yhڴ}V|1~T4/_LgBxxE"@D@QAsNͲvCc0j("4,Fıc,رcQreD@jj*F~*WFDԤ;r&LzaPz5x?v`HIMTE ƞ={1s ED95Q2d Cm_mÆ QQQ;w&mHH(O}U^ dM3gNȑ!T* `B[/8i"׫'ݽ{0g|gpvvh_D+=-@:@D@Q#iS6BG @ 441hѼ :: aa^>>HH???% }yӚz߰aC<`ꅙHVF>#( >|>o٥]ff&Oヒ Y/*N@DDT^*6n7~WYަM[|5u1 VF6mѦMkY d+<,/T\t cƌ5~ܸqz*ҠP*ꚷ̙Э{Wh_DDDd}l@DDTBBCꢙֽ{72̛?0ڥ<u!<|}}#22RoZS4m!ۇ)'jժ9j={c^0;Dll,e2RǩxZPTHOO+R? \nrDDD""@T^.Г'O 1>55~~R)P(œJ?x>L_ؗN~%"" 9IruQ <<)aUvT*nތG]sJ9^n/dH?.miīDZZY;˗-ÁѧK3v4KjF DD$"r`?3瞭.;a%HNN6ԩSӻΝ:bx￘>]4l"6xӧW_YdR^ف֭0z(4i!п?N{jҸ1;ߺ'O{A|5t.]AuT˗-,x"v킮]ŋ'N@##:Ouغe ٍҥKcgnKDD"Úeѵ`|0DFÒp%Z=̟_~=5k!)1Xav~-B4l߱w'Odb p?緼!?޽X?ok:Ͽ~l߱.X6<j^笾swǎꫯx,^[l7,Z Wj~ 7fρK Ś5V۾ގ:u`e laq,/kԟDDd v "r`-ٳCv:u;~o]6` Cc\2`Iء=/YR(奼//[;`ٚ 4@ 4ij>nPR% :za/ IXbuw܁\.G \[lIо}{ef.\8k@3տ_|gy:u*/^! ~4K 1z(|L,_1ԩS}b""҈<5+? Z6;N:c_믛X|9:u\$%5/^@֭L97m_!!6rJL2wYcĉ k<|-ԩ?7z*@ݠnP]ܸqV>#G~%KcV)@ҥ1|.]ŋ _lXo}b""bևb}&fV Ǐ0;CQ^ 0>rK9/?j?_&ݵuVfMͼO?>>>Сpwpww޽{1uXxaE<r(J(  Jes3IcnJDD@۳{7ƍ-[",,T7Ƽh;믘4i2W@8qNkԴ/CCC675!CbT [oM1Ѹ~:zkD(KH'*Uys5yZ-*7]L<>>>xt.]ܹH˲`<9Gg_Çcb˃ DDd "c$ ** B^|g5C|nB޾6t`Ljٳꫯ_׮ݰ|2MM,[=z0/nM}*qcw1s ӧ5+0c*7mFflB ==m۵Ep<~2m۵Ezz?vy  ǎaƌ0l K?|e˖aA:y|w{. gÇ~->/F~iiio RaÆ[p. TJ899A$FDD1L&f`U}5l_Pt<2&M?!!!hڴFiv~oMO?}_z ж];7^N;=Hh76PwgQ\W\6o PT Pd=_vm/ZhF묷'`Ԩԩ3zӉG)g`A0udhڴ)p~[0iDСO43220pb89$=vԩ3fbwXpޞ6޹s?SB@ݭ ?L&*0YTY>ss˽cNLWJ̛7g;Riuvf ^ ~im\tIg^oZZJ#TP)h_Q o0{I'cz׹ yecXCfք Xd @jj*&N͚Ikι( drl!]""&A.l  HOOuɊҞsҘ;33̔;{턇Bxx/╁Ko:s%@(Խl""*"@,mޛ| ӝ7L&e`Mɦ%RDRr3EL&Ϳ3+ExukogĉԹ3jgusI" 1Vo{䂾QQb drT`_Y? 7R(J=ERb"RSQ( K+񆫫+232Ō ҟʕ gI sI& 5$` dY-D]:=28BwwwL+O?~ {_,~\WO.KosIV""g 1IRGnn.ES8*" g*L_\tҨX*T(77WcXH2$IDD~Eju5.4_%G_d$G/8O:8Jtpuu$ ɇvA&ԏdYcu4DDT "s2 Bǻtd$I(UܲZ8lP,#e`&.$I@Ywϑ/5RP$AR!"* %"s$EDMUVCDD-L&A@DNDD%DDvN_W䙈 $[q""@QXzݔP~i4B4""HP)UZ4ToeL1DDb IØ  $` r#z۸0DTbeȮs~1@DdMtePTwꈨ HHDd8 DTTŢy@Dd2230@Dd $"*l <}HKKi9MFf&ܹ Ef&Bl+ 䣝@k ȩ m07R 5JLD0\Qre89;E""Lv@ v "vn*U @D_]8FQXi4J%233-+Q>8;;C.C|m>}K"'0\o,G]r  iC %5NN9~Y}QŚ)J5G.9gWTZ<=!ˍ9O3y|~DDT1@DdRRRPlYg#==!I+6^s#=穽IMDD $"dN>ӨTJ$ V*'NMAJJ2T*t:i&S{LgNs ""K0@DdT*32l&#- PT<-9c ȁ0@DDDDDD@ "" u#~fk>~<9 "|o0mO: |MpEy;pϞ3y=K޽{EDDDD%Ȧn]ŋsvuwnFTTuf4.%ŋg4ol DGC&A&!:&+Ϯo?>/r x̟d|EzY&"≁"9yd124֞]\K/amym+Kd2a˖ԩ3}GQ%^!! %H$@PZ@i6' "V:bXҤ% M zȑ\)|߼ll޽ٹk/GxFo[ZT61jhˢŋ͵-)7;L߾XzU2Ĵ.)O^,WL)<9eˉOx/[nw3K= k׮'k׻Yx5,)9]ӳ}̝7Kӎi+/;̄7_ǜ:G#"1b8t~α?/rgΜ?&Ν>pǙ8~ !(;BQ. ^ K.zjXb%-fر;E_ͪʝ_jj*șGxƎKRb23g~Fv=k5ކ =k6CeuVMwĉkߞI'ibqvm+g.8_9 IDAT+Ze L[{УG֮]K^zε~s2衇/?aL:0{l>C~Kcg˖‹/12]ov-4ߩ2 !"@!Dѹsgo ??֯#MTt4Æg˝ߣ=FHh(Ɉ# oڵ5?1ѴjՊ Oaz呑Aff&v&%%lr^zrUWyM "88MaF98~i-G}vԪU_<Ŭٳ[&,.F=m۝/=PvmzHִiͳӃ)zoٜ?ӽG7_!D$BqU*a bŘf3-AHLLyָ-[_˝W-6Ek۷cGlX ][OCaϞ=%;s ?wpqq-W^}5ٲn{dٲstٳ%믿N>}iӺ =ٻWyi Ya<3)aau-OhΝ;ԩ(U W.Y7|3 *%))v@#==@eDv*~DDxš5?0I|6qL ߛăss):̞͑#G>b5l35s&'N"88C}k}\"7 x?ҺukRn!U2ed v#XTy3hZ,P~kYV!(M+:MASpZ)<ZCAE(:4 ޕ<L^u\e3/tǎbCKOGU59r-5X#.(:\$&%ҠA /yZ-YS'O1v8su:lڼG qFPpm9kv.yY,&\-9(,PQ PMP8hZ׊iE=zSTu+DBc6:ߏR I!B?W_~Uj !5t B!Ai祗^*u@F!5T!Dyiʳj_iv)۲#ĵnrVYwR.rKnsG!ĵ%]B!BT!B!7 WmZ+ݿsOlog g!EB!Bq!B!"B!B! DBj1,e#@TΌ *B!B! D*B!BT!B!7#@!IrvմG-\r-*s}-f+dvyC|Wy/_E5;7#G$"">iSټiW~N<ˋy{}l$ ~ag;ٵ{'fh೙3˰**ٜGnn4hJNN6.]$''Hԯ߀J;n3љLG!DYHEBbbZ?X7oLxxMdۋy擑'G>>nÌW ᡇfeڊ &MnFQ22.a2X,bd%#zƍ#kT{F!HR 7`θ3?3t؁Ygr,] OhLyٌla)7ļRRR={=7vI|'oߖ9gMkɴ=~-'Ǧol ڽW^ylذ92t#Ly2[lu=gnfLAݳNG^П?,ïĿ:0ߒ:u ҢyrTU%# 3-apnN.fdd\BUK뵞?fᄄҧozSr7<.eIxxxOГY|}<ĨѣhժO<8SN%!!2pCA?Uߏt}]u"""x}~\#$0)xxն.O?amع]wmbۓL 6v^zGWjٲ%7Ru TjdffK^^ͫ}yfBUK@hԨ!mڴa%%Z%2vƎâzj8?Ox;Y9ۍ.8B!"BqYt1K..izY|}%%%5l==ЏCWU;2gΞ=޽{W^QƎ`l{o<|0m۶Ij>Ǝg wԟOڋ"r}sA<ȇ3>&>WzYoRRRaЅ EoީITL&:V( & M+}K. [~=G #*:( ?lg#ivX233mڵohk{ cԨ'ر}{9BQ>5@!cǍeL4`lx<3~S?ƌʝ?><2j믽g|r/[;oۺlmMW /QnÆrK.}oƎK>}Rݺu53c4i҄?:~~~ 16 TU#;;`0@(BNNӧ7sf~&,11We(ޘM(#p.8B! BQChf}+@Q;w(qaLaGhƧ}l &114&+*m^KKݻ++uԑN:?E۹S8ڷ]v,g3WjРO-m(i?8J=S^ǩS~!Nω'u^O 9/z..?~,WqXP[ntz&OoQww-Ɣb1̚5ɓfp~}G!ĵ$B!<<BXh(uQjqB!nR B*CMhhs 8yUqp'vmEQJMWnBˤ"@!U(xzz憯/䘬nnF48B!IEB!7 xቪ(it:-BaT!JSE !H 5\ll}[~jΤ_S\m4cc㬟gee/߿?/ ߵk7z;̰aØ3{ B!\R 7obl *$mڹsGQҥKٵco{;vӚ~ ox9pw !BT.Brд8NƱ.f#lKJNwcի7RF0jhnfk~z,O _ѣG]Yt){>?`ʲ7+?UeM<GBB,]JNnnRi3isU 0.][oױcְLIMet҅vZV'hܤ 4'o6S_qSÆ3o|=˸zg88B!$$$`blL6Y3 ~oOf1X,>cǍ)1TfΜI=3$%&'Ӯ];ΙSr7 62w\yd&Ob떭,[mQ}iGiZ|9=<_|FWW_`M4LLvX7شkXܵj{:r$nD4mr $BM*JXX(M6KxxMf~J[MXXO>8;߼6i҄M#Yh-&..zꕘGGJ}daһOov]r7?'G݊a2o ~)ZjEڵ:lR^^dff[oYruv7} |'gܳ] !ړ! L6E'xXxZZ , %9#Gi..;;wgôS~}|}}_X gEs֮ib0\9-Τ:ȅ KMso`< gѴj зo?4M_"//nG۵k^yаkZҨf4MCQ :Ng!!Bqdm?:֭6l(AAA8q~ x,W_}Gzz:oW^Q$$#*:p{ۢPUQZfDD8:5k~d,ba/+/// ̐!4iiӦ1yd4n\ܩ];//o fKhW !e$UBqYx1/FQbagǗT&7{ӉO~?t؁spYi?/xMƶmd߾};(}[6lDjJi94?  ''YhZ+q1=3rk_|Ɍ3+l6c2rM h4ͥKht#<< "0r1͕RN!B!;n ga)Ãٱ#>>y;n,Lƴy>z4 Xj~;}_>#N9/h ء#g&9)D̙Cǎ.ÆHMI3{SHJNf6;v`9={{2}4psglQ1]6!5K^z f\!4zOON>ÉǩRs^\\\9~YƭS'NYh1! :~On(z=L_Bww6 |` oiiiQA|x&LxL{{qf<0p  vv|鿉g;mhKeCQ(SL.`7EU97 yf3fsnnn(o'דAWQ- hMqutTM ˗ ]rrl6_ǸsgҎaXƹN4ZlѣjCVV?_9;w`ڴŕxRU6=m2hРwetx{gԯPgS̉'9q8[P;(xW2ݻа0BCB얡*ZaΘ%K*(Nqxt:jծWnq guv8#4.9. (lyY(òe#.6F.WI&DaջS{?CN`F7rss8u>^Bhh(uQ压+&M2d$]\Xz^AG (NUPTС-@! sBXYTڌ޴~vIw /" .._8w:ud!-h*[’%K9y$gϞAڒSXr%{!7'^}zѷO_K,b!ǎc֭r<;ώo:i݇#<"5kְxbO.]J| \R++sc⋅ /v09!!!<=fLkf[oͿl &44̀ss;wǽ 6l-hСCٰ~u$$4>} w.2Һm['N(wYD$AA^!@\υΐo!UnN.[괨4M+q4:h1-:ݴI|@@.ܹk*v'5%#Gi..6hwݕ> ϣm6^x4M#11_~պn-x7nGIy]V^=4MǧŋfUS.:OE<[Tt=qm=Vߧd2UTBq5W* b;@ʹN:΀wA17:w/E_}^@xx8dwh&~}.1|ڴaСq)O+7vV, nvjْdn6*1_?? ~+r⦛n*wB?XΝP3 لbCQ͠T % i, VTͭ"pRTp)<==$@ nHK}Mo n^hn]e&6m[nWV||O#iѼ9Ga9L< t|}|%5ug*s={ĉZwБ9卦i̝3>F7w.<_ǎ2|`mƖ;!Dxy3h`4s7%bvj~}`u -$7 ZsG+"9!6y 6hEMѡ*z, .nxbS^ɼs8x U@۶mAXr%2aaa :>f̙͔C?̎;˔Gllyiƺn׏]?̂ x!66~wv/TAQqpA灋,ffATW Ǘ,ȧVTVG󄆄TvQN}+B!Jf4̘>M`p{%-SvӮ}󊎎fڴ6zQjWRrzar֫W^z^|s޽[:w3Oqqq-ef 7WKE$%x衇x衇*5ҕBq#2\pq! ykB!,UUQU\Џ[`0JxR!nƎ`)\˜)SU,FB!r, YYY(`4-h4A(,,KeU!DyEB!L^^75lhd%''"͍9'.. FQ?jUs cŖkTl`0+f:ttBBC۸a{kc-)$UxqX,dggӼEKLdd\*1ɔhid3RR@m۶ujLQuUGIB5z~ؿJٳuk_4þ}4jԘvѨQ#2333dggkeSXnG"<<; :]&gUJTvQ;LtꄄijeǮ-[q={ϏǏyfkE@MpUQS#\?BܨTU%##Mg29<7'6lġԸ?p.]Z!5_Uפ)Nc7oOu|Gt#YеK-^7Gt_HLJȑ#hݺ5۴vnn݊ =9{ ???nBBd玝\pqnrv`NH[*W[}~gnmߞ jٳGUsS=)))up\&4,7үiNCpp0uшؿ \b%ݻ@VXascӶm[^{u̟Ϲsر#LJ~ ,Y_?? q噘;w ̙3ٓe˗SV-Ҏ0nXVZU,u_UUe˖-,YS'Oп?={b4e{⋅;v-[8\Fp8|8M٘עMlvsUݮ~&&&믉{}kvիWŽGff&6ll~۾i]6IM߷YLJ={еkW8MFg׻f̟7ϚwkgLqطyl߾ba }F憿?;iwG6ұc69fo>:wwٕt,Rmn_~{Mdudefٍ_HWz~ OtG'k%8UT!čH423+9ό?YhT ut֍ݺnݺbOwWXs?_~Օ|wwwoknn{`Ϟlܸ~ټ ݻwqmv72o\2'm6-_fSW^}-[8NMwy5jD`` ^iӆ7:^?=;t^ԫW:جo6[%77Z۷ބd?[o#8s @}}};nL~yt:ZiCfF| <3mڴAӕgtt4/2όO׻rL_lI֭ٴq]v ;{6͛7s1Φb݋dg[OZVT]ӡÿ_\p 65u_.erJhݷUmS]ֳw߫qBT]bCS?pE()/UUaF"#i&lܸΝ;[=MС< |` ? `0azu/O/VZE޽ٴy3 `Ӧ՛={3>ak׮'$::}@1͛[Yx"GiI'Y4sWyQ~_:_n}a|Ԫ]wuA׭7CEHHuY[{ذaC:vEXr%={EZZׯgԨQuwo|۶v93:wP_29G\5"}v|', oЀ:Å1cF򈈈 6.cukGN={v0%Kޭ>>UqSݎGQζy^Ku>Bt e(JWt (ԽGVXaSPxȅ  Yf[.]{.~i&NLvv6))ۇR m;wviZ안-[wfۛ[S]^.lATx^nz8q8 ~qkծE޽XTKeժU<أundM,kӱnZ;%׶-HJJb٘L&4{8AAAGDv0&%٩f Gˋ6m2ə8W N:"Y(#xxs񫪕f͚5ttu8ƍ8-~TQmZ0~;aad\`-4yx˖-eTKշ}2]=x8~E Dե( zSNT;QN'v8w۷oc˖_lnz=Ν# a>'R[ůWK,7Mdd$/&<[iKBDVSuiv_8y~Z,^⯿,_ܺ_rݞrfa巵O?ÆOΙc:4>ӧOgȑhޜ#G0g&MT4>>>tSͿfUVu;EUԩ}rߠݺu/)q7DԸ+:+ 1fYyA6ˢQڼ@F#gVVM o" R>w*w{񰷏KZhWRՏGJ1Ҋ,Zܳw9>Oz>}ǏS'<%pqq vWO;XXݘ2"-L8Vom)ihc鴿Om8TV\+/LXX(C-u}֩HX:>1ĶlvMTGk߻zbbbؿM7̛o#BL5"@!Փ'ٳ6[S;(l7Ņ'O_ ~/.,qyL.m¯\vfճ׸qb9J[QڵoOK,gI;ZGGV,_A9s 7mi !j&B!D`0 ;'-[ҤQC6jшj :SN.C ! ?Yh|5GNHfˋ8Z!nR B*ݝ:((ǟ~ AS'эN:ߧO N ~i۶]%=qr}^ ?BT[кuLeCQ *"Ƿ"B?>4[ii ǢtLz7zW.n \Q6://oBCxzsΝ?'PU :wwwkvPэ#W6\]]qW1qQT3zMEYСgpZ4.9ZkEJq?]!DThETdrBq:-9uZQT(l+=N7]P]=ьFo43Vy&D ( Gnn&,^膛z+*?.\<.Jn:S&%bFQ-谠ȯ)֬_ 4и<-:Vz!DR4P5DZӘ?˿룠)NW():CࡡyT qCxzzb IDATၪxNs@N\LYYPͦ7XhMUɿWK'e֡T QTYfޗKԯB44MC@S-P Z @AS MbtGsDU%čDQy/Jㅊ-% ZQ445)A+uش.(eq}ڍ;S\D!EUJE3;C:\2aQU޶1+]5Ge ePk@/tg'g0p@UےK\Q= -,+Ph _Fņ'ny4h'iNKeeLܗ~|'L>^ $4ЏzƱϖ{U~ɱa0Wr7>Qy/t5[(*DA)ur]ҕv=j,xA5u hV h9n[P!:[ϰ@t3U躭f{1ktyҋf(--%IN*Yq+ʹ; %//;w."lW#MV4[lO \>A>͝/?o/Cr'ouCc9SZAʫL8Eri&/ e@LnwAW_ĄX1tx8ػUMTP4K{@B%V@T?9fق hk2_4{|e _m=l0::^m l޼ovZi&O(dff\7iÇw=˞Xۍo 6MZ:t-[뮛رcryO,Y¢"==ʊ 3 77 N83a|}}۽l?%88:howq6n\I`|.UKV4233IO_ժ1u˲_~S'ذat .kKצcqw6'O̔F|v`;z@@HsD1O8 ٶiH(DЯW(&_/N[t7cӹy1)+k>0QWo詳`Eeh%/l=ˌQ=_})_<1`rJxɵqw,_|cXp![Z"@t4n/#~ޔD!6*v)`h&oH+4}>C~=tR-V‚Lő[h?CEWt#")62^6(.(Q7wz8SZt3edbjo*4ľ-^ޛ|HJiEuWr9OGǺ OՆ@ M !lFJH?+AMݸDѿwRSkv9_xC\: ƶmۘ6m۷oc5ρIIE||<3SR~ǎ6̙Chh(&L`̚5PO^pۻw/70~0w\VZle{޼OSRRx{̑Ϗs';U۳l ,`˖-`67~:h~Ŷ9 +hJW:yk>۰qLxons`XIm}wP-ۏHU4P4 U@BtMuͱX,F#ʌBW`e+C4tn[-4wð=ٲUUا pY@͓7D1>]ɔ)Sx뭷2e -<Ýw.reڼL&Ek=={l% ;@*/V,..&=}=]`tzgL&)))PZZʺuXK-ZvOPT-"hܺG. ꯿l6Џl>\ŕGW^!22ynt.N ̙32ݿzѡ;>`syIm@Ld#/EGzuRGeTQP $)t]h?;ŌeBǾZ*CKņEơ<~͝/s'Niԗ#EoLHgAOmq6}S9oHmd`HF$fV{Wte_/`DboqWo/1ԅmOOGX!:H[7OSJ]]~&""# h4bXܹTUU#S v%P J䀍Z؋he6U54Z|a/::>FDt$<<޽ٳg7n(5 00Sw5lI.[EwժU$$$BPP0⋝RPfϞ믿X#ښ27N.G^n.I{$77o2e zӹ;zUQQQ,Ztgn>sxK;(:%}WUUw>娐PEh%=lUpYG廱:FaI9ѡ( .:sPYÎTUOdbݎbaA$mUhsxjmJ~":,g/6Ԛ9WĬq_ףkףiW5JEME44{[E k'&lI{bROB`6Q[[K||555d 6JqEI=5#}sM2qX<[|eՅqo5.|w5SMcŊέX>dRSg!-5&cȐ2cLJJJf6g1|*fϞCll,gϞeݺu,^MUTo2a2Ǻu:lߘ1c}72b=uzޖ2ʧ 2Դ4|HKKly8q ƌ _^}BzX/q{ˬٳ jj.g :Ǻ2㢨3ۦuImc}2, @/zyqJ @\4?Nбj*1-be8}sheM??~=w8 ٧/d/ e]P"mZxxΙ5c.\( (}U. F%o!څjîLee4fs>>>$%}L~eqپ}e˯\)l/FJhH?Tg2m=¬9KX,Lޏm{O8 ?e=׸4r(U/l566} _& 3fp6g`b":|@XX)))m `IMM動_̴9vhm ۹9u*0 4)Se;ɷ̮DKc4g,[ ʴii]wfZ^ _C{7}nQ[g<NRYU"^(m@EUA1hD1zbGwX_8m:S9c99X]AmH3mT]X,1r(*+˻d]}Jw۷ۧާQQYI F\kחz;L`@`y^;2VUH6nH>1.FnxWߜGbNP]^VSnZ[ _wDcŲu 'DGc#֙),>OVv>&0B\>οAUJhh(&$$C3߇A]EbZ1zEchzV8~.->{)e-Ý/6vj?ygΑw Z*eך~8x4O緥MbCa<ٕu=FS(*ʅdu@CH*B\~f//|}}t1liYmm ޘftd]侽.\ZV_okp lc~۝*:G~Q }E ;ix}-Vѿ~>^x~hF Zbf~a A@ihi_趯 !.5(yEX1.5iдk@@؛O)-F0^^hU0o}gݖTULuUӀH{x_:[-pA=zy'ҥKM6iгuxwO3d6h)իװrJ~kytYMm=ZU5XѪ,fZjA,nZi@@%hB\It @U c̠. zښ4z}놖F tQPVEEW X {0tѭiF}UU.Lձ+3)Ν;1כt EQ7gr/{D~~ͭO_#@XX(ϗy~gN֧.LutлaC<-o~~ӎC[B@B!t`08SXHddKC3n|֭[8:/^u@U `0x-R=nk_6Ms-TUBKy:?6Xz!W; !Kdv6Q=i7;<,g=VAm^^6&Su 77A{<}=?y}Bd1tP2y.}@[פB꫗5? ! L&%.'gO,A9j10zyaw˛STUV`v,f~iz}w.\Ǡ-yHʏ\BqU@B!ɟ`vebQDDFaX[ss+ٽg7zdo6ƣ7Eaq?}Zv sᆹN[h4rM7rM7:z-. U]BlX]!Bvf4 %4,[q(k?Ղhޮ0Z,dǶm[ %4,B|c !#:: Op1"#"ǗZRT\j0ҳW/"k6q;޻zZ/B\M$ B.CUU7(--\Y 4 jϗаpBCC dkq !H @!](!(8:Z5 ||}BDB! &L~&4MCQt]GU[!= !KSE !K;G{#ΉB!Btk ZEү_?Ƶ^W'LEmMY5&4$#F0eʔn3CM8 !B!h+JUU_X(L8C//2رu!33tv.B!W O;رcE>>$%%3s F/^z%|}}~%%% /ɯ~+Μ9Czz:99TUWǴiHHHpʻq@ ''}~Ea֬Y=M6/P 2d0_?oof}(/g]gdرcdffp=z4 p;{ۛH)//'..믿xt]ĉڵƍcG'>>UUٱ}k׮e]KvvB!4x0oٻg|H%'S[SmHw!C~dfd0} ~0eRS׹Ν;믓âExG8yc}ee<(>,^{-ݻ[< ӧ9}, ?#G[oaXyDZZ[=rټճZM>@LA~>Ç[w?~&U<|Gz2x:9|KryH @!Bqը"=}=^{Eg͚Exx8Æ L6 M7i{`СmKKKQT???f3 ,7n %%~wX?w $$$ϔ)SطoG9~lik @ff&ӦM#<<ܑ=ᎂg[AF|3SR cN*(-+܂\&5@!]ihbAuEh4**3Ē%J|~,X1c\&&&1(j !!>}?uիWszA߾}Κ=.] @DD~fΝo4K IDATlE늋Y~=9q15))=zk.f͚ő#GѣI? **i't]w.44̩AϚXl8m>-Z2oEU5и,[,xmv]BXV3E`` FBEEΝZP1Os?`ԩ|lHO`N),,d߾f+W'p_XXg %)9iݪ+9t 3ÇS[[s>DUUF͆t>3,FO&$$o^Ξ=KLL EEE V+*n6=kPP5,y zѱ%&&q! [VDVV=VmUI(]!]b\o&@Cmm K||c6X,]d!Ub5pnɓ@hHN @jj*'O~>Duu5_}U;vcu>|8`PRR·~ QlV۷sΑ255ed[YTVVrx L0tΜ9`B=Mלl-[Ʈ]DEG3gΜd$-BeXVjjj:TVVLc6CR`߇w-ƍ.)!D*'Of_{QSJ^^33fo̘1DDDo>>#TEa,Xu?lnSy###'';xG7Cr޽:l9Q >cjT^{-۷wyL\l,suMrr27t;yPP 妛n"))y_؃=2)S:\W_}E?_vmQVVJRR2ΝC״Ҕ+!$$ԩBt6]x6 4~W r{ M1`UTtՈM _Л5bpT>3g '7e34ӦOL1ܼ}b\1ۗݦD^1i[5xWc֢h [QPl^FÞ鍾+li!.ˣ0ͱl_>bW&2TJhh(&$$C3߇A5@!]l___jj=_mm ~~~ޘ[xbv|I:e>7L+A!+Ε .m&!Bt:]ש&:*T=~MMMubl7̣=Jdd$ xtvܸN׬YˢEw1{, _}w.[n>i{!Di/<uf3wGwvhA!W4]ש!(8*ZRo!$$Çvd-[%WSO=ILL.]|ȯ+k^m>]: ĕ@BM.cՖ}jЌ*ft EQ0XV8r7^>FbmJ{o~شi3{C ?yb/xj&!)hD4{+i.:nTh46;'X5+555<=Efm6 6x`>r8pshhϗ9>߿A/ھ[z !d!©x!:n]UZ9}( FBTUm f+&N #cgֽCrA第˾5[!D&- g0hK0@t$]݃-i>Ut_oOfZ5 /$&? qp-R^Q{K㏿S9Zztu]ҥ$?]Yny~BtmB+(Pdtv)J]ZOUg9OtX[,7$,<ՠ_ **?ƛoOٓ)'.{({,V+ocǎj+[!D'!]!©r?"/yuܫAГd2BLϜ`06%`4w"$$b^3~T>}ϘXu߻ZMo㏗٧ˉ'22ӯOg^o!.',ySwwEB\TMmu,i :jrZS@Xh05lٲāH?/4 A\[Ǒ#9zфc4:_39?1cz5[w.]Jtt4sq,[f EEE{R',q.]ʂ[naǝW^}іi _~e.\H߾}X,<а0o֔I @!p OZmI8fߚ-ݧ~&zFErq;JdD=񥮮BP#{ ':o۲L$,4[oY3g\Ry\o!ܹ[xkgϞ}6~ӎͲ>Gw@{qUUoݺuXӵȑ#tGAA5 @fF#G9kenM__I's]Zlذt͛@YY ),,d}Z@@k5\2v#Rq睋.K] (yuh/ y_TU0m0KiyΖrfŠ#$$`EA˖ےM7M7Rz N={ |>>QWWGmM|}+NMߒXo5e21|8۷mc̙?~}M%E됃l2 r_l߾li3e]wh"G wڃ (](FXuKUA4tM|uy2RSb3LdD8='8( v_mjh4JѬ3SeƌNd %e33%ű.9)'N'˓&M&@`%%%GO/g…NROosJXr%sFaXW{$ cܹ=zFIFFyy̜9\2225ʹ[@NN;w.UPUCCVmC\-]o]QT 楥߯߱<';ǩov `Bs<hp&**U7Կ?-'y\ڂ M2|pV5L N.z뭎Xŋ;(]w뚖]yPVVƆtxbz"77dx-]0˾}>|8Ǐg}m***vF/_XqFƌM>}m_W˖]T.kͽZB.̠hx הm&#l(nцtr^<ޚ5رz >(oWv^ @l\y$+}^^^DDe郟11}ؼy3QQQJgsy47n7||BfZ]V|=j*HII!((R^zEe'.>޶?!+Wѯ_?H ؾkn]2\BBB2u*ocY\\t\L:/b(M`` LM?731m4s Z丬7pra@J !D(m5{s[[_9~O'/y]bk毪Ji>/h%DSY * ꯿!CFNN999:mhC8דhh0fnfx/kzLLbŊ9r$Ǐs<߿?Ǐcȑ4~QF9UR6-]m9_{PWWǩS믙0ac9sfg^>]\9zAll,_ͨQ]dNccc֯_vC!5u999dggft&i ]Xà\]EѤX:\E2:ܧ݀[#(Ib nDEE1qDVXѶ ڲe˰ZL6ܜ OIWLU_:-؈#0 {(O~';O>h]өݭ?fRSSb3mtP 2d~~x|~LN҆}|ca^\Mj7=cƎe={>}bHHF#򇅅cj*}t]W^<4{W^}SbŊ<4 99J2@ZI54#GBQU &NȈ#83g '796Rii)Iɔ?_Wuf3XVÄhHuUn}=xr@>tPT]F#}etq9x'zO īՌE`5T݊bZ\{E׹pz {i๼<o2\oͿ,[nSvZөpfq P>LHHh]EBteFZG:i ў4TPTZSrv\o] n*]Muu56mG}? 6:jc٧[u?u{o!:p6s}}tU4 ZX`n#00?x*$ ]7W\V՞FjX3蚆(ԮAOnOKB=G߿RH.Bх [ 4VBt["iVlALqBtW+B.B婙Պ"=(iܵu;i:OTT$L42oL<;eeey&~[*z$ ]hm5]T !8[W!ڇ5k4y2wʆ HOOgΝHKMe-\rYD#!œS6!D"@/ӧO/ s>G G!11#ӣG)lƍ26}:FJBх`M~X !KR1k)//o%11p~rcOps| I[[஻`ժUرc;($!^e5!DW:ևIڜv? @Ϟ= o!$$={:OIE||<f}v@@Ke] s,PBty:&-H-U_yUؐοo/^ Z Xŋc4^Ι3_Wlllq|ZUuH-!l]l!t]Bt1!!!L:|, > , (()mXXӿ:neKZ!D(mMZ! k@ s-}=Rf"<KBх)bo WM7K3v,˗/ٳCBB&O w||#'Gɿ/FSNcV~Ѿ$ ]-٥B4Wyʓ1!BAcB!Bq@B!Bq@B!Bq@B!Bq@B!BqYBXV,VKg F CUGhBqI @!, ӷo!!_])++#//UQ1=|BK1]Pچ!}_a!Z͌1 Ŋl"uCgWNE`04ZuTҫC@B#!"!aIwv:lڦJ\~nz!h?B!)MӨ$<,:sU[ g߾=I\RBÇЫkPP13;CEeGر[,`!ܲ`~N=ʬl2vԹh~1b8}G믓(ۿaCu ^s>Zq;wr3t3ݮgkk[Bѹ$ B\ڣ"XpE? c1i '?&_3<ﲒ3?/O?Kjϖa.g M72#u>JChDΙEOS{s\PC=Ĩf:^cڵ@B\$ B\Z{/szaÆuHų-xiLA Fy71G`ēO5r^~α IDATPxzs=~d<j= ɓ|( kX{n~~M[oEyyc}>s̞͘ѣENNcba횵0~8>:Jzz>fP[[+!$ B\Ra{xMul 9nAA&ٿ_Xb7ld Nƍ :U?c^zE.}9aڛ?oxgK8xcE-B6|˙#P);r|7od n׿K̘1-c?r:W|s/ oo׬Yo?+Wr1Ǻ47O歷fӷY !D7 !*Ж ǫw0tH,G)~ 3Eg;M,}ĵ޽{4(i٠Aٳ۩11mtT l Ԝ+!۩+)!΅D蕥͜^R OCvGjAnI $&K-mq:'G!""<(iikyǘ8q"<3uk׬_QF3R$ ݀ B\R 7,3 /@$f&M}o|I c5bܸqeYYY v;K[U Voіsev*u%8xz*z_mU*S³T[ϰapƏrɓ';{%Xv7999,,R;sY[f94 ///طz!EBqh˓?c&# eUd -u o<#&zISغuӲ۶2yJmOs{jinA{ƒ-c+VNZAdTm23הSؾm/ yػo/׬SFî]\=nx}deu f"-C!'!*ЖʭۓcGG xyc lЁy'=g}3;+,rI&e2"Gr0!\Quv]]Bq sX  e˶ڕvwf{~~άOw~~T儵nUUR}z5͜ݐ'ҝ?k|Pe+Yi['}K=:}~ӟD:{u{3 Gqs=?z'u}ϧx#GhuuUGH.)0==O֥kuhUUçӟ|/–׷tGuG|@G{D_zQ--]qu==S[$w}ZjvɋH_U'OJ:ջ>UW;׿=g?יq[Q}OLzAr鼃M7ot{߫/|!:`uydWέt-FΝ{]oy[,Ee.վ}YWkպĽQ?Z7Q$OO TkjU5*T:l6+}wݭ}O/?'K#xxss:k#W~wŊ_oH}zv/ފ^n]|Io=tRq=q;* ºLԔo#y6H&n[ʼ7:Xg_7 i0_~YΝBfkRWǠi~~^=훗uMob@П1FOm7ߠw-ӮҶCIқYUMCKze.s9瑇Jl$_<%5KZ1FC¦umo[رc W:~~!G]¡C;/~쳺x<Ғ~Ӛh]Q~O0>$Lyٚժfsuzg'-?g9ףdw5j?ydj&]MB.SinnNV߮뮿~՚~;}󚛛(w5 ?/\ffjrѣG|oU]-_j{ƇD1gϜ3&mllLJS4kf32 ^j{ƇD acg0ƨ6SLufUp&h`X$`Ha83فi*d#B`Z0v`"CjN1lPf$`x$`HS`j#Cb0"$jmD Q0=WEO+0$O ޾|+y@]!E\!].L C0R(u1oL*[D 0qoT]F"0yj'XXYӨzcǴS@Vq,c$ziun ?ЍgdUIZk40Vr[i{(Q+)[Sɭ6,V ,xo$kl볱dC(6t~]Ӊƌ֨i՚贿Wn4 (SN.mH:e:J^9c뽍d'Yke"yQ$τ uEF5E$xaS~cSAcCA./ EL(!##+٨ռwKfWD$G}NJRQSˆ6fCc UM65Ga|[1+n/1(9Zk%]Aq"(>شߺd¦fE"Æ)/j 򢦌BZc8'1RqR"Gq+h(3J#{? )6JďR<|`$䵒qVOI ʦW{  gӥlf@Aʓ[G ha@)U}csu0goq[ D+?8HU'kNO8kɀ53_VzؤwWn}'@9P zğ2= }ܠHF&:6eu{lH(|0I$ ^SSc}{WP@yP^d:o}z[qA-D&/sai'8$myA-wLz(J-i2@N=ZG84O;@jw$%H('ke[mA2 [vt'Mpӑ @Em8v(-J'=/LHE$@n0*#`qNI] O@PJݓgMdJA$LMz2- N$V>IDQ怓H;6g $ E"@-Xp@\>wh?&cZA/dP2${?y`gtupT e3o3xD d'ڽ2 )HXR .@2#L@"{'%'(P3~ù?LO~G"=dRc*`ҺtE"a)D OT޴+&D%B"!@DHP"$(JD%B"!@DHP"$(JD%B"!@DHP"$(JD%B"!@DHP"$(JD%B"!@DHP"$(JD%B"!@DHP"$(JD%B"!@DHP"$(JD%B"!@DHP"$(JD%B"!@DHP"$(JD%B"!@DHP"$(JD%B"I"Tdm4UaTT]  F]whW_*VVzCvvU h$JfssSbv( UV#90#Z 4!,%i,%0ty}팬/.FF2FfBhYkE0ҭޢJzXY9s+Vj4D.T AEˌ9#`HfSE rci/ljnnN7x6FVj'Zt3#`>g<ěN!. oZfx ;p #ئg4t_n0>'o:xxW7Y`2Oylku)Nf[}_ښ97!Fxzi׮Y{:lJ-lœ|f<%pmسm|~6ݢBkt*!-gxu oq<g=1x{o!̂qƛVMO><%^%qƻ u.)l6W?BO?_Ϙs=mllt-`ƺk}}oco>@'N/c0]s 5M%m&KߚJY[u͎Rx7;? f;\&oxx3xS3xs3o;3%^.޳gϪViZ8YkE(bIE.ǾUʯn&E=z\qbg>hvvVgϞlGZm=_Wշm}w[{hZ6[- :m/dm4|/}~RFwcS4u o4^s2mxx;PxoxFo☷mi7FN=֗A++i}_a3{kyo?|;pntM:>1`o7Y{׾5=dGbfbmiݵl{VAl%-gj5EwGěLv8M okv7ukx%oǶ3xnwx&og #̌ժ~誫4???z!,//ڽ{fffIXY IAPў={d_>1:!UUinnNAP[>"v=cT(|5f9KTUUA ޽vU*dc#+/\ꍆ"s :uRzC^јv}U+8p@Zya\ꍆ*AEa{ŗ]0bmfS/?.ڬoNz`Tܓ䶀0lOHzޫnQR|ع͆x?񄖗A &I|IENDB`kvpm-0.8.6/docbook/CMakeLists.txt0000644000175000017500000000013211733530416017105 0ustar benscottbenscottkde4_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kvpm)kvpm-0.8.6/docbook/index.docbook0000644000175000017500000007355111733530416017035 0ustar benscottbenscott kvpm"> BenjaminScott"> benscott@nwlink.com"> ]> The &kvpm; Handbook Benjamin Scott

&benscott.email;
&FDLNotice; 2011-12-13 0.02.00 &kvpm; is an application for the &kde; environment that lets a system administrator modify partitions on disk drives or logical volumes created by lvm2. KDE kvpm lvm2 Introduction The KDE Logical Volume and Partition Manager can manipulate logical volumes, physical volumes and volume groups as defined by the Linux Logical Volume Manager, LVM2. It can also write a partion table or modify an existing one to add, delete or resize partitions. This guide assumes a basic knowlege of LVM2 operation and disk partitioning. The following is a good starting point for learning the basics of LVM2: http://tldp.org/HOWTO/LVM-HOWTO/ Currently most LVM functions are implemented by the standard lvm command line programs. KDE dialog boxes are used to determine the parameters to pass the program. If you are already using lvm on the command line the dialogs will probably seem familiar. For example this is the dialog to change a logical volume's attributes: Logical volume change attributes dialog &kvpm; change attributes dialog Getting Started The <guilabel>Storage Devices</guilabel> Tab When first started on a system with no volume groups the screen will look something like this: This is a screenshot of &kvpm; when first started &kvpm; main window The devices found are listed in the large tree list to the left. Clicking the &LMB; to select a device brings up additional information on the properties pane to the right. The colored bar across the top of the tab gives a visual of the partitoning of the device. The colors indicate the filesystem type, ext4 or swap for instance, in use on the partition. In the upper right corner is a progress bar. Most &kvpm; device related functions can be accessed from a context menu. Right-clicking on a device in the tree list of devices will call up the context menu. A Volume Group Tab Below in an example of a volume group called MyVolumeGroup. If more than one group exists additional tabs are used, one for each group, with the group name on the tab. This is a screenshot of a typical volume group. a typical group Volume Group Information Panel Across the top of the window is an information panel about the volume group as a whole. It shows the size of the group, the amount of space used up and how much is still left. There are also entries for the uuid, clustering status, default allocation policy, metadata areas, whether resizing is allowed and the extent size. The Logical Volume Bar Below the volume group information panel is a graphical representation of the group in the form of a bar divided into segments. There are four logical volumes in this group. Each volume is represented by a bar segment according to its relative size and has a color representing the filesystem, if it has one. The last segment of the bar is green and depicts the free space remaining. Clicking the &RMB; on a segment of the bar calls the context menu of actions that can be taken on the logical volume that was clicked. The menu also has an entry for creating new volumes. Logical Volume Tree The large tree list at center left show the logical volumes by name, along with some information about them. Selecting a name in the list window shows additional information about that volume in the pane to its right. Here, the volume Vol02 is selected. Clicking the &RMB; over the name of a volume calls the context menu of actions that can be taken on that logical volume. There is also an entry in the menu for creating new volumes. Because volumes often have an underlying structure, such as segments or mirror legs, the logical volumes are listed in tree form. Snapshots are also grouped under their origin volume. Clicking on an item in the tree will expand or collapse the branch if the item has more items under it. Physcial Volume Information The last two panels at the bottom deal with physical volumes. The left tree list shows the physical volumes by name with basic information, while the right panel gives more detailed information. Clicking the &RMB; on the name list brings up a context menu of actions that can be taken on the physical volume. Volume Groups Working With Volume Groups Most actions that can be taken on a Volume Group are done from the Volume Groups menu discussed next. It has menu entries for the creation, deletion, extention and reduction of Volume Groups and a variety of other changes that can be made. The Volume Groups Menu is located at the top of the main program window. Except for Create volume group all of the menu choices act on the current volume group. The current group is the one inside the tab selected on the main window. If the Storage devices tab is selected, only the Create volume group choice will be availabe. The <guimenu>Volume Groups</guimenu> Menu Volume Groups Create volume group Create a new volume group. Volume Groups Delete volume group Delete the volume group. A group must be empty before it may be deleted. Volume Groups Rename volume group Rename the volume group. Volume Groups Remove mising physical volumes Remove any missing physical volumes. This may cause data loss, use with caution Volume Groups Extend volume group Extend the volume group by adding new physical volumes. Volume Groups Reduce volume group Reduce the volume group by removing physical volumes. Volume Groups Split volume group Split the volume group into two groups. Volume Groups Merge volume group Merge the volume group with another. Volume Groups Export volume group Export the volume group. Volume Groups Import volume group Import the volume group. Volume Groups Change volume group attributes Call a dialog that allows many group attributes to be modified. Creating a Volume Group To create a new volume group select Volume GroupsCreate Volume Group. A dialog box will ask which devices to use, its name and other pertinent questions about the new group. I have checked several devices and entered the name MyVolumeGroup. After the dialog's OK button has been pressed notice the new tab titled MyVolumeGroup is now present on the main window. This is a screenshot of &kvpm; creating a new volume group. create a volume group Because &kvpm; uses the lvm2app library to create volume groups, not all options of the command "lvcreate" are available here yet. Some, such a clustering, need to be selected from the Volume GroupsChange volume group attributes dialog discussed later. Changing a Volume Group By first selecting the tab called MyVolumeGroup and then Volume Groups a wide variety of changes can be made to the group. It is also possible, and sometimes more convenient, to create a new group from this menu This is a screenshot of the volume group change dialog. change a volume group Most Volume group attributes may be changed here with some restrictions. For example: changes in the extent size depend on the alignment of allocated extents, so some sizes my not be possible. A volume group can not be made unavailable if it contains mounted or otherwise busy volumes and the allocation policy is only a default and can be overridden by most commands. Logical Volumes Working With Logical Volumes Nearly any action that can be taken on a logical volume is done from the logical volume context menu discussed next. It has menu entries for the creation, deletion, extention and reduction of logical volumes and a variety of other changes that can be made to a volume. There are also two sub menus, one for changes in mirroring and another for handling or writing filesystems on the volume. The Logical Volume Context Menu This is a popup menu activated with the &RMB;. See The Logical Volume Bar and The Logical Volume Tree for information on calling this context menu Create logical volume Creates a new logical volume. Remove logical volume Deletes the logical volume. Rename logical volume Changes the name of the logical volume. Create snapshot Creates a snapshot volume based on the selected volume. Merge snapshot Merges the snapshot with it logical volume origin. Reduce logical volume Reduces the size of a logical volume. Extends logical volume Enlarges the size of a logical volume. Move physical extents Move a logical volume's extents to other physical volumes. Change attributes or tags Changes the logical volume's attributes, such as allocation policy, or tags. Mirror Operations on Volumes The following three menu choices allow converting an existing volume into a mirror, changing a mirror or converting a mirror back into a linear volume. Mirror operations Add leg or change mirror Add one or more mirror legs, change logging options or change allocation policy. Mirror operations Remove mirror leg Remove any mirror leg from the selected mirror. Mirror operations Remove this mirror leg Remove selected mirror leg. Filesystem Operations on Volumes The Filesystem operations sub-menu provides six common menu selections for working with a filesystem. Details are provided under Filesystem Operations. Filesystem operations Mount Mount the filesystem under a directory. Filesystem operations Unmount Unmount the filesytem. Filesystem operations Extend filesystem to fill volume If the filesystem is smaller than the volume that contains it, extend it. Filesystem operations Run fsck -fp on filesystem Force fsck to preen a filesystem, use with caution. Filesystem operations Make filesystem Make a filesystem on the volume. Filesytem operations Remove filesystem Erase a filesystem on the volume Creating a Logical Volume If a volume group exists and has free space, a logical volume can be created in it. Select the tab titled with the name of the volume group you wish to work with first. Then a dialog can be called up by right clicking on the long green bar or the (still empty) table below it. selecting Create logical lolume. I have called it VolumeOne and it will take %50 of the available space This is the volume group A dialog for creating logical volumes Mirrors To create a mirror simply bring up the Logical Volume Context Menu on a linear volume and select Mirror operationsAdd leg or change mirror. Then select the physical volumes and other options from the dialog. A mirror may be created with multiple legs or as few as two and the legs can be striped. When the dialog is finished the data on the original linear volume will be copied to the new mirror leg or legs and synchronized. Remember to account for the space the mirror log takes when selecting physical volumes. A mirror can be returned to a linear volume by removing all but one of its mirror legs. This can be a convenient way to move data on a mounted volume. Simply create a new mirror leg on the destination physical volume and wait for the mirror to sync. When finished, remove the source mirror leg and the remaining leg will have all the original data. Working With Filesystems Filesystem Operations The Filesystem Operations menu is available under the Logical Volume context menu and the Device context menu. Filesystem operations Mount Mount the filesystem under a directory. Filesystem operations Unmount Unmount the filesytem. Filesystem operations Extend filesystem to fill volume If the filesystem is smaller than the volume that contains it, extend it. Filesystem operations Run fsck -fp on filesystem Force fsck to preen a filesystem, use with caution. Filesystem operations Make filesystem Make a filesystem on the volume. Filesytem operations Remove filesystem Zero out the beginning of a filesystem so it won't be recognized. Mounting a Filesystem Filesystem Type The main tab of the filesystem mount dialog provives a list of the most common filesystem types that may be selected. The dialog attempts to identify the type and check the correct radio button in advance. If needed the user may select Specify other and write the correct type into the dialog. There is also a place to enter the desired mount point and a Browse button for convenience. It is possible to mount a filesystem at multiple locations. Mounting a filesystem Mounting a filesystem Mount Options The options tab of the filesystem mount dialog provives a list of the most common mount options and a place to write in a comma separated list of any additional options required. If unsure, it is generally safe to leave these at the default. Mounting a filesystem Writing a filesystem Writing a Filesystem In the following dialog the device /dev/sdc1 has been selected to write a new filesystem on. The type ext4 has been chosen and the begining of the volume will be zeroed out to erase the old filestem. Writing a filesystem Writing a filesystem If ext2, ext3 or ex4 are selected the Standard Ext Options tab will be enabled. Selecting ext4 also enables the tab labeled Additional Ext4 Options. This dialog may also be used to create linux swap space. Most filesystem types also allow an optional short text label which can be supplied here. It is not necessary to erase the old filesystem but doing so is recommended. If the old filesystem is of a different type, the remaining traces of it can fool &kvpm; and other programs into misidentifying the new filesystem. &kvpm; uses the standard system programs to write filesystems. If a filesystem type is chosen for which the needed program is missing, &kvpm; will generate an error message and otherwise do nothing. See also, Programs. Standard Options For Ext2, Ext3 and Ext4 Writing an ext filesystem Writing a filesystem Ext4 Only Options Writing an ext4 filesystem Writing a filesystem Configuring &kvpm; The Configuration Dialog There are many aspects of &kvpm; that may be configured or customized. To access the configuration dialog select SettingsConfigure &kvpm; The dialog has three pages: General Configures the data to display or hide. Colors Configures the colors used to represent diffent types of filesystem Programs Configures the places to look for support programs General The General page is the largest of the three pages and has two tabs. The first tab Tree Views is for configuring the tree and table views. The second tab Property Panels deals with the property panel that each tree or table has associated with it. See here for more detail on the layout of the trees and panels. Any changes made to the configuration may be reverted to the default simply by pressing the Defaults button at the bottom of the dialog. Tree Views The columns of each tree my be hidden or shown as desired by clicking on the appropriate checkbox. For example, to hide the mount point column in the device tree, find the section titled Device Tree and uncheck the check box labed Mount point. Some information, such as stripe size, is duplicated in the property panel and may be hidden by default in the tree view. The amount of space left on a device or volume may be set to show as a percentage of the the total space, in Mega/Giga/Tera bytes or both. There is also an option to enable a warning icon in the remaining column when the amount of space left drops below a user set level. Property Panels The panels are divided into sections which may be, optionally, shown or hidden. Checking a box enables showing the section while unchecking it will hide the section. Colors The Colors page is for customizing the colors used where filesystems are shown graphically. Each filesystem type is associated with a labeled color button. By pressing the button a standard color picker dialog is called that allows the user to select any desired color for that filesystem. Swap space, physical volumes and unused space also have colors associated with them that may be changed here. Programs The Programs page has two tabs. The first tab sets the search path for needed support programs. The second tab contains a list of the programs that are needed and where, or if, they were found. Search Path Most of the functionality of &kvpm; comes from standard LVM and system programs such as lvcreate and mkfs. The search path is a list of locations in the system where &kvpm; should look to find the needed programs. The standard locations in the list are: /sbin /usr/sbin /bin /usr/bin /usr/local/sbin /usr/local/bin Using the provided list box it is possible to add new locations or to remove unwanted ones. The search is done from top to bottom so moving items up or down in the list will change their search order. The Defaults button will restore the standard list. Program List The second tab has a list of all the needed executables and the location where they were found. If a program could not be found an error message will be displayed. Credits and License &kvpm; Program Copyright 2008, 2009, 2010, 2011 &Benjamin.Scott; &benscott.email; Documentation Copyright 2011 &Benjamin.Scott; &benscott.email; &underFDL; &underGPL; kvpm-0.8.6/docbook/mkfs_main.png0000644000175000017500000007563611733530416017044 0ustar benscottbenscottPNG  IHDRTfBbKGD pHYs  ~{>IDATxu|Gǟ=C,P8 H) Z(Z]J$bw9\dϒ \,nn (6rE̓䉅cyTR6,^Qvj>rT_n]1ŗY;rh'>[ڥ(݁8y((q6qO}G ;) sVNW㺱"C&@QIG1c3 +.rϧ$gۋ' TwX@2@Yg!E̎Ib"ffBr|QfH,[e)ĦJ7!m׬lŕP7}v_IKL8`>Y~XSar _2Ħ];Yׅ8Z <5sS'w>L摿\+PU\xݳEBg-4J)>w\;KmfOÚ*śe^;;`׮q!׎5ֱ:pQg:T]a C qM{n,ySBlByQ&\S[gi 6>l<['j!YXr2ACw_g}d%:~Xf/xÜ4Ys/mkn.Tytn$\h v *`2؄o]cW?s~S8`pq BT8ZT r\p,6^hems$f) <^ƬfrRnvpfCf#ø=/ᚅޤEQvsLL]:lkgJfM/DʺqSy?aĬS[P6Q;{~V7wqa79|pНa,{ bx󸮶ަ`ol#Ͷj4zת5Zmk4{ζ "dFƔ+I*s:Wq J0R Ux~ϝڔk]T6>jVfsisu 6gUg[f{l±v+r:sA[7 i.\E V-8_mwLM୓ep}lo8Cf7jplAEeǦ)`+\3};gJIcv ڠ]T^pm+A%0?x[sws'/`={6vllR4w5ˮ)"˷[kz$ЕlvU;N! K0x  weǬܘĀ2?dvVn>7p1eDlO3W׈wJ]o#%|U]*-[Z^r+OٺĢrÓQa4:k]Jgp^zuH'~bDz8n[&2úv/ezX3cU'1Z7Aـث瞳'F6hРjOcjn<~;5n[A\ܙ~k™gs^OA\K[:nO,jꬻ5C ̽'lᑍ5l4|kҰaoi:' S?dEΉKwo팭wJ Zy|2 gJ>>nݺuW.*kͥyyJV}rlLsW_YCM,CN{kAF 6jU"k %ކOV8Z72obQVZUN (oXQW.b˗Hq w]Wkxb"bpMX k̉E="Ӈ1a2W"ozj/2)qT1x۲o${h#38\d ?!䮨p1q톘 8?ۤp5%6 B(qYL tc3L[SF;;?iT^;>чD%KQnެqPQ>59kn+[6M@x~)Ӵ d̲~jdWo,W]4#M~μ"bz W>g)F֢۠ÿsf@py~˟Xety1gql9 o5EDgp^h d'G07A+P(Ts Z0V5;>׌y[O?Hei߈&G3aQ@TnoV/Qr5frbU.RJ8NpU[:zu^J|~B`_e Re]{m3\Zm=4UTݨ}gy-IȩZp?WydO[9MW|T< E.uB+ Wٍ $ᝒpR}":+ZYaޓ}X*YJ~3g7QdM,(,8[MFM />[x$v/NX65 晒7YoL9s왳osFvY1<\=f# 5ppϢԉ??| DwkePϱ]sUO޻nh!?bɈ;r)@-Å`sUO޳t2 Ν?̔#yY㻍.A<4]q݋=KScU]cV}Nb*H6wEWޢ lOO^Дo]QD{gK[FVMXpԉeY-ڸW<۳;VOrqmuz5+d<9yASUE)n/C1{ݝec={?f*nn6c罐A w<#)F}9UmFy+Y\Kܛ(P3…U˸+.ῗ:[Dݪ ͍8Vk[v"WW\hwԇCI{Om%{ m]/z>bR Xyk}ت[ fVy2Sv/M(|h;Lyt3&ʈ%tue|ڧmk,@ɞS f,ץmdш*:*Y8$TO]􎝏"{$ ӷgYGvD벣q2iz-" k颳gϝ]Tf^ƗK.Ӳ/Et1ջvY:}zͩ,+ iSƷH}+lРw#$yW&_{{ƗK.ݪ/M8]TISu:6 @KȞj;39mPWkHui *@ڎȹi.$Rl ܱ[ҜeTזl<ռ 8 c_IAںպ s=@G5T!~Xԧilf+?MK2 Omd6>16El C>scF1-)wiEt/_5l)B@Ry|"!N6d Ed dKY1i(B !>,՘7ؗ&@ po|}Ś.JJPdFSo)6вތTŚ;kpX|浊0* sc ?~ _4f cJS@P@g 76^1~Ir^l{N(-/Pui.? |3[a\yĩ9(߲?EmÕ;a +t |  #WVQh?\xV{{$=}pƙ+ouU%a[ㅛ~SJ Gғ7\qn̿Z7_|0UDJV[ЌO1*#a8e J@X;:DiO\P `|'"W%r- [m.?|cmWr󟸠0$3IllM3:š'D_*οNzdl#1px,~ۡyM_n`s fIgpDM08} jbz%ܮ8@M^k^_ܼ@xQ @F6,gIo֥sNR"@$Q[nr%Itqq`lNjӌo~ J޿,)RDshvRBKHHu3+nIy"" 7_KScdSCSkԜvh^c_7\kFRFǎEbETmŚ0EOƓ!i-Co"Xh񦹃> /YY~IO_}g/,^iΠvSbnysS6 ~9H+sA,k$ˌNZw Ry:7W\Z4"ۑM~H/8DTU Dcg3?|i3{M;a76 eŦJӢM_tdH^"]hӜAL5avˮlv$+ SE̼b_E1"4lԟmf Kct GuY6d0wo9ba-8l;MHۗy΁rFN?xgE7qcrLY#Y<-wCFz8eY.Ba~-qGEWGf%JIR5wj/ PaESyFeȨrz^LZsPQ6FQ0  Fiʴ*uz^v =ES%J {Rj_v,3lj|cgVUl5rl{D#lkaS :o iX'C'xT/ݣG/Q]7ٹܔ$!O07z0o?sᑶ„Ԅ''?;ﵝuJeoEUU)Iئi,lՄ][҇rH喠n} ?֓gD*}0~u\4bs%8HINR ^:Zqh6@L/8'M*_9.ܼq1O<>eoL\I(yMp&`^5E3渍_{i҈M-.! kO[s'Y \4b_?֓=>|q*Pio'-%*2&]0Ցz)ӬOFX v=pYr {rUMc;KFG#>euOd̗@R}kI$A}S|r_a8e/AʁOo:v`rלוe~Ɖu,pEq ׬ٸraW/jd9F l㚅6Nٲh c5ؐ+V' x}Vy՛_Gl'8fj?\41Ľ7싅Ve1yzE`*B+7z~홆X4O<ܕmimk&'kUqV[UW맮aWڥguպ5k~uX9b~;,o|FoӁ65*uaK b._9&.@L -.B5;|L׼RBGƔ hڰQ56Nb8I 8^w?/2G``KkN6hRDn%A$RG"J^sF#-NN<9!&tP]|AǙUSӸэM^j|=uL}EFZ[b/<՛~Q E(bv)wO,j7 bq@C:#a(![LڿWkE #:P\ܡ5+l_o2Nrfh8#UkNYy4ɸ Qe<9~Y~h<\9韷(FHI/R Jl9;袭0E.}8Ik8t`qM&zgMGv#wz$Wn1CFZ޿T-Wy=s挞&Ք5e J4ʗx'ΒP{&'c$cs_JB}W@=xAњ=Ę gKM\ξ]G){%PU@S4 ,@!0yDRc98ko@] 4M~74d-Tּ_/\'כ%ch.ye"}P>i"8<|jb~i^-죏pHZïqM |p"V=\#vv#h k[jӡ ګI{UP1X(ɾ .Ac;:v+G8 dEviM6l2à}}_\J'$Ҭ)O$Xh 2KlVdǪ bH):Δ$,gﵽ[#{qu76ݦkAY/;EQ/J^ao@Qff= 7 Vٲi֥#\wy]y&}' (/[TlJM1=(*ZaUXdkGccTY`C+Z~*}o;5騆)SŔryi4Ä/Jo]6 𽝖L)j:hf7ͦ;{VHn}+?'O}^ԧǴx$G,86+ !Ks/`c> f1:?qg㬆Ǐ1{{`i0 B۽:uÞ]; {0Eq{<ڱ: WBwkEB:VKuju:n!Xw((;u}ݷ[t^j5v,$6s On? ÔJ@ƒVy9]zEgv“ 'I,I>mYXr% Ԕd(Y{]9~A-R. w #`9t'#xg`4`m"RqwA=*KIex! Fsg$F#$*2JJ| H!D&WDFFݽ{mȓ+i*UU*4 uR߽‚6{~eٔTBըAm8Vg:C!beuZq ́]{*ʳ R Y'X$뎑|@}jT'\Zx`1EQo~f cLd,<[MEߎw\MIK{cBG8tđ+@dHW9xT&>wEd~yu3v1AL97}}pdG}8wt?2xjƧtnÿY1!9BabG z5(L CoC=g_BPrP*ʄeޙiZ1ͦ|R4D^ckq@WoLsн_azΒJ+yi_4m9Wf~4i!|D%6-sǸ0?hrA@ )1'V:eaƔoUz|-C%ߚ7}z(:ėǛUJ)B -dX#BGlZ~4pk21~%B> k<|ISc_ZlBQyT^ZU9a^OtOl~) zo{S$!L|)~oVYrZ^8wuq5(^aP"IT6dok=QX]R􉕿̹1 hσ$Ss.f^5 - &ǼR_|+s5ʎ7,9-/cUS\\YM߼N.qYkD/־Y=ߩy{%yH{$칟{zòI^[|=a'-i.7}յyVg?o1~T5S>"br;ެ䴼{{n8a0w,I/9êD m>ח%XHޠHpȾ];˕+g1J.o;$XHM=|t]peRP۫ 1j4^$x \w0M @@ pA$xf--HV#3B ( ^'VVE@0;!xYwI$qǛyvH ;INd L<AMDZ`)J( MuRH(h00B( *Uzrk`d؀{R%h4ե#"D"FIKI&P%Ku'e2zˠ /L7p CkG7јTVRY$JHQܺ~>Y}lUD̦w{絑 lsW2hl{= YLțT SoƁ55|cxF̞vp}w[8KMM-SFNW) ltRՔ)DL0hƖ}F_?|9 m[JdAjl4EkF{ʗ[yZIRT)AECV+x|^_:m+/3$ ۾U |uߒh K3F.{s6j5D^{ڙ1H˷]<(.bg,>5_?dִsl C[zoDξ:du[oVAN*B)T-ۙ Ẃ)yΏlECB*%qZ*VF?kM?{o4J:U&ы]j[N9ӧlgԛ^&'Om"3y 3 _qchΎmhY dMw[rLQGR  |,.NT*9BýG_SL. `ćEzAI* 4m1瞭1wL6V) (=w)!<c(4k 1mp9f"U&<!-^WՔ; e9Z*)/x́$Wq!ib¡;DzzN FX@8^cLyhW~mطz3fU?*H<q\X2kէpW sHmGc˗/qˊa/2qTܦk?UX]RUS^5ڎjWL/?schY0%&AY\u/I=!UxO"-1MKeDX"dϣD⨏f~[=f-.mD//$r?tzсxQۆnl)5?;mӦM4mڤ\A0Ǜ 3mI+ty\V~8y#I]ysc!::cnOƲ,kTrJ۷4j  uZf jt.\TR`@̓*\Rgg-oO0Lph=;w+% s2emyɗ> EhHȉ')Z`\,>yDHpB|+ $i/>c  888==}M6 kuFH8i#KIɧN,QXpp@ oҴ)orNDy`)4ZJ} 0^ ExE{􈟯_Tr"(##E||t*TXXx΀ [̛-UX,%or< h|e}}}_ZYF(2Ëd4VAK%)#lb?V(H"FYV0RL&IRӦS"$Z Is\{*A 85 !4E Ba\}R[$8FAA>>h0T$hʤ؀{R%h4՚eD"ZD(J(O<2#B"hBl)}$N_s9dX!n4RjZuשVi5j\Y[7|էk]\ٙW:q?e"v6fP`= YLyӍ#-_}9d|޺֝geuosK3wgq)Q5Juz:o*%RLÇybX#>O].I/X? ن7j){sv˽<8gB\{&-eCsu 8o46I>w^ 0H9\`Qj$>Pz;,Ĝ;柭>Wi;^17o޶5t/ۅ|Y $Fv LeT|5dCGDs{v{G bwOYI d!De~cp,+`}t36Yb܎ZI(ƿz3&3+̧P*;qӼi/l8[1KQg2.L*#~p?ǯKI8eEw[|35R,2H9huz(yt:0o񐨑3-iy"e-6K'63EEˊ=l(fl ~EVp珙Y5ʎ<ŵmoRfB-KYyi7lo׮OͲ,0 <00tXXѢEd2URh)KSQ's;vn=%bq VhY0L*ʤRIS9Z -Xֿ=w>>:(E BPSdӗZ -cWY?ʊ^ݬRh)Ml0kˎaG7A† |~AP@8{ ;Yb7f]BB#C9F-DZ,k0 Q4-^Z -EW9jP+ JLNJb ZT {7Z -]\ZwE4hh5a"PU$sK3P( 3<~$2#Bh)T.Q֞f, a˯/Rј*WcIIh8h0h5ׯUHMSF_ij#Z5oAKֿ= O!oWVd}bsq)e#k*Hӕi2e=/,p Cs6)+m?Bk-Z}YU. *q,-!Rpd)b[.ȡuZTʈvt6H[>4|E^ZaեfREIWS;صVu\Jfbiɋ~S%["d .UE*=?"PpXJZˊ7l! կQIOr59ڷZ{~),eDxKK9z*2)ጎ&vl\IOloWƥ(*u݈f!r=?(AyX/;fbaI[.q&&)# M l2~JXJ+JWc9;5,МkY3A[7E"7XUί^0OwG"ľgO&XS:(@$7mرSoޜLEt:p(0ł#?mAM)/uӿpǷ Baxy:Z? meYkK9U̦Yo)fB.:sƸhJ]8݋3I }*6 \hUT/ωԠC|(|Nfϫ."AL0&?L261).MṘ'R|jXd>+UUݣd#BK&a8D؉PN&\J^) Ab|]p;۴i#۶k7rHcM ˄D@3"ؒ2{Åg*K=v«~sZmJY<% &3K+k8#GM˫h)#KQAjo*F |5Gsu{')ԼڛU Tm{maQ@3hH,ɊJ|❷S2)w,ޥk:BS2mCά۶ps$eZn_fr>ֶ; ڔ!b[[Iu2όpGSԫW ]#ghڦ>:GSq]?(~K/   0z[vo/nX5T_vY+/u!VZ-έUeCʬ?TqL*-ѢK7RöƝѩXAZS}ok8f/UtpFEz֮lZEV/SiquzixO~7_{Rfy_H|SZ߹CX Ya ;}b(rȔU5;eS.w`4v+]5ں?{DZo))wޫ=e$RNɞHd*V@}|ZʖwR_c}ATH-Up`Aҥwn)4{*R P֝O̪VE:vUvbDՂiabzYR4߉aa29'VൔM,Eih)$m0c=+"QpHZٷ{wMZk4rP%irJ铧  D[=rm{{Ug-{.@?RG<تE~9Qaa@Q=ZT$g_FGGUJ*+<467lWᲆv6DK!F~]JQOD> WIׯ^;s?e偁a%-RT&Q"h)w<3Y4-|ǐ)B. jtdRT&JYbyˠRHI+uŵÙ_Q}2Euf Z =yL˾iZ y[ A++HZ iX`PF# %%uFI ?dP$ǕFQӆG &5%4:DJ%2M4~Ȭ }6ya (#E? TI9FI0Y*@$꬐$뒊5 zA4Ȼۛw{+h4UTѨ5ԴlwFFQ(*޽sǗ_)x#ל;p{uv>f{ulYVގ1 $dDFJRZ n)ub`eAH,A]=>(A GrY?|@1H `JUH0JJRxAu}\|!+|跟/CΏ TFpnkw(v~'Y$\ntjM_Z|#[ NQ2\,thd"cZrʉҒ>L9O͹5➓DAQ7Zm?@ (2Ya-ZZ&]4jUpHH‹|]}95WF9?pGghz i͹O7~w2x CۇFU)K&eK%2ZAztaI ?諹uʏ쮷E 4<Ҕ*^[^TW..kdAbqP#;y+Lu(L1"U%N0l#O;Ҿ;ےM{vvvѪ;ZxNQYW-E(!q}RhC>#a/rd#9חпغdWo:FB%' Sct ʈ)7Sj/Z|Fvu@STA;<8_!Х*W,*t"KL@ٷy:nu !+wRɃzu?nX,vB!iqh iX~RA $]'춎ir2cgITjgLk(zϿ,e&I(]IIdN-%)i%~VBq4pv6I|Rh]OIS؇h|[wnfc({}?aYԔ5h( sT/]PB9m;e t.JKt:XW$F@HqQ7m(YVh\@ ,W;/O2EpѢNhնX,xY,R4ߩaJe  CB"J)_nK6 0TҎdvO)m\/MoAlp<vq !fBIVB3A"At{5m댬p)@(IRRΜ>RH#p6=]9#p1wxNAB?+:R24Y=* ) 8+c6;Sys7<MS'Ϸld"0#2!&Z^dRJ*||yO`Z A<^h+SFx*ΜcYa\VD"Ee2G=h "JHR8CX(‰tZ܏+ @@H]Aϯ(2D@VX#T"J%iFn&4CT)QצkX 0(D#XY!< G.Wd@"!BX BE)2&7u7`\B4=i2 (=ANt RNz4|@p㑕yǻAP8It RNzb( Ɂ#5F}B#`p1  14U$4T&{̃VyrD(v ^'h42QQtիW/E,J~ 2@ H'orȺR )Q<=]b퓮RU!!ŔJ;b q=qԏ:wl#{1x/|@}<˲))劄/E/i*RimGwc1%mYh? gJ)$|ܮ\76 |q22A^B79#gG\׬^s'O|+(aҤRat:-Йy2` ȫlJyyuWb}Ҟ~wds %p -~MX%u~S*€p?ݽ B6>ؠ S"t$Dfy-XlzC68rfsbSRy{{uOR:~'k+lܖsU80샡ȇǛyt"TAeTTyf@CeiBʣ&g5N'j ;w؟&W*Զm YdeBt 풼]3cϕVƹnh<󽖔jY[gٮɺۻy?BީFK*kvKq .fز۳b]Fʝ]wgfΎN64ח|qJTVI:g,X{L ʎ7F/L{%DYe^ Jf« _p(rQ Vck/_[OU~ vp^n] >:6MV}7F1۝㸄z |8s=VX HMMahFdFZs7_(Sly1OXfah&Dg 6ePXZ-ו-T<:fY?Rٵ+W4mR) !E}Jk~"RMQ88 {eX!@88O3@qqS痔z*U 1g ryĉ n (~+2ҵGO+۶8oҠi#BTcy p5iӝr8MʤҒ%? *^p_EppHdRܺک陴ΔR#ۀ88:C, @Y>EQ~b(HV\*( DG!R!(&r@h"kz:D,;xv0{OG@C1Fq(X|/FC(B86[ t40io^A!F޾$=f ,)pFy{2WV bG8*O[VmeCQǂ&A)P2T^8ρ q,zXH3SيOVq,#cA 9ӎnyYYc6= RnyGQ8k <>`,p*b_sA1O AO=+AǸAcACҔʸBD,N63.ǿXVE&P$Ja 6"9)sQQe}ȹGwgԫ_b%\!޻yb X ;V ^:̟XW.fҹ_EEŦ.>ݺa[ 1¦%*CTJWT@x^=IA̻[:kC"`R%B΄eM_ N[ RW՞6# &Ps Il`)k?w{¶+={O{y-eZ]8|-1CZ꽾c>i]?Pq܊u!gtLtOYdé*PigԐ 4'Z}ɫƩXޣw,+>mC7_ }3t7ɟmkwdaXaXxǭ>QM?os; u޾zŤm|ȥgJ}A옱ʼV%~}Uj zT2€څMHk|.Պ $N.<卟*qDJȤ5KJtRL^N'GPm?P5H* oۧ-StP"qZD )fFgVln|Α\7Ɋ\I|d- YNJhϬ[ƿ>:ЬηFA@>FmOIdoJ Tw-_{b\!ZG %@ -~MXXU|ҳCoΉuD"ćqX3_#=y9#q6*0 gV/)m[\I-aS+`S*E !,S2"TC})OT"&c^x:%"*cONo*Wf!Aswe.'2[`'?GikخJF&n!O~uOްpAp}) {nu=t?5##5f#h@:&Rj:p/%##Շ5Xؠ7R"X*E3m$֍e{ūF}jܥmD.fVGse'1dENx&A~1CRaoDJ@Asn*~9QI_KI zWN eIk]5~QߌܵjOD\]AQuD.fVGM)h߱0=3pF#Vs4W&~ü q't-W(*r:=}-aaXªm\*LӦT A;Rn!yz2 |2}rH(&hp@(p CFƣHa[[gE8ak$)R^8^6? P$c8%COMN{Rb#]سbB,S$''+.ame%P$B1@3D"Hb퀷AcAWV LA+pp&܅A]A+A G ^1 W A|BA$ A1 Wr ~ s  ;++A++A ( ^1 Wr B7A݅nqAƤ'NV x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr #x( ^1 Wr $4M'& )^ AuBx"]RZS4MҳOn$&c#8%$o@ࡃr+L8|'F3XR^bŰAslFFX2̩pDz2ut52 S y"! )ES q0((Pa-#8E"(2Hr MI$RXBZFq EQE_# .AcAA+@9F Xmk0 DnB!C3rɚ1A H !ŋKaVKhFXXr ^˲FKt@Y$)0&eYaށAJQN+@j$XØOC~q؂r ˲JJ.ZRc> E(Uzʢ%iwXR(È ^r !Dsqp&&?sCzr䜢 qzj0n2|Aujo (*իח.?E7./NK'wfxA9FpǿJj掶~?Iϭ_Ѝ QpGoPTK?|d@tx owa,@2Nz2cze?JžeU 820QԬ:iɺW_h耨潇 i]Z{:@s|#qF_%Ng=ը7mM;~?l2zت[{yoC+J) ipmAPv|ޯN˓Gm*סՃgTXޣGv,+HF떮;z/k]+(뗬>|A\n#:SYQĝ1> ^G8g_Oڽm^RӾ~~~Ąěnۺwxv#=X/d/=quΓj&"9p\ ({`| )~ YIe}wivj|P>`宾pYEq׬YrаK]J8pDGC-[f)[xf8ǕZv׍Wk9lm٣gXCVL7EB\;0ud\xXN-*L;7o-XFL|b˄W?'޶L` q@IT#@@pfhMHJD?֮Mz]eKcrfe; ޺~QkW)8aQ'I/T|-եSjkWHJԺ| .߲m/3qGmT{}JpDRv>ǺJ81pt(up8W܌*&SDU|U"C =9aEB}dgb)z"G$W{yJ|7]0g0xr/ƴT? 9^qڵW$-x(Pb޼8)_Jjp@%RbD1 ጪDO"Fe|ҳoIH8 $hsP Gǟi9kؘ;U"U urLڳBD 'ZAHФo&z?RnOm*%*|t/SNQx8.M1ڟ~bԯu#OI*|5VѢyRT83AHS[5 ͈ݸ0?jhǝ&Gr`=ȋJT2Jʹ7C`rD1#C:D_#e΍_Z1SK ]{֫_^۱pcJ|88NPF'ё>i]5 J5o_ UF6KRF1G}rd-k&cup'p/R>sBmYIFAP.'%Б6_gMՍ׿ݧʜOV8)Q9uT\T#TܱK~tKUQh3~U#QGE46pTT~"BKiި!2V}+ۑAջ}pDP]D\ueĄZF1:q~FGqlc))7L#zw[5f(S|2JvS*I6Q@pt慟bw-X6W3AsWÛ5Umm4]WJ[N&^ '*DD*w35̷^[yR@i:)鵯ojJA(Z&4he*5My,:M.v݉۷daڕ~8IsQ\ų8Sdx %$&k4j*eZȶD"=Ll.=y*E7Zj(=՚'Ofي+ ) @(H|pmPV\.+%zS5r ^ 2]2t@Y#.RXP_pDoflc"(U„AAA@D$b A RD")((2ΔՌ(.d,;>$ #x( ^1 Wr #x( ^1 Wr #x( ^' AIENDB`kvpm-0.8.6/docbook/typicalvg.png0000644000175000017500000027432311733530416017074 0ustar benscottbenscottPNG  IHDRnY>bKGD pHYs  ~IDATxu\S_{ۀ1JPBR@@@Q,P;~vҬ 1@Q;{;/ :_GA "\HhH˭P:GhL <D"!tVD>Uy"`:~>W06})bB h~CeK4RqQ~4Cq&{.P.!91Z޿D>~xx>C .&DpLLׯC0%` 9A ^T? BGU9pF9͕a@O#!Q{V#4yґTR0+B9J+["*%KZRVk#JvJ0dmX$ &&BZ QF]e.b<(C,vD e>B~0*!,Hjg|rR? RCĺYH|-N/k$ 椬BHZo*փHj B#$^sBR$iQNԀr[!4BHoȐ 7('\;A >OP)zK^_ʶ]ΘzsSYUKOQWΌT rPEJQv 0yZR'4+Gڨ@}窛E6"QR"f"(-n\biur4FXAPA5]_RM@I$$I@$AIIA~$I $W(%IOA#@hHz_OMBB iрhZ~<$ J[6:^RS_{Lu?VCCgGt%Wߩ[񫻏Íki*B"؅P 'eIh'Xy Tc_jG=tnŏfb^=Ow1huuljy|=xHǠEfA^nE]W;h$bs|I\ 4 4A   R# &&$H@RCSϊCjG!$e"HMFBf;@# B2!\D1?D">>bCߞm21j4 -$Q.AJmʧZݵIBP8G'ȍ)gBI8 |y$ iE5XA8#TѪAi jP4zj, L hD"hDAP"@$)5$q!EI֡ OAcbcGF9cu_Hy{_;Mm4Դ#h=0\xx?潫0/Ժnݠ-Gڒhl~׍XnP+oGgbuZ$1tӝWLkWԀid|U[wӼnPQjҩ%yo2[AA-.<'.-6 ja\|^~ǃc4S'NAN}/=9wu0Es>\-ڍ_>1 '8Sڵk56,qj2MMjת5AW.QMF?~0qXaGB1+Ssz5ؠˤ{7$600 0Y'e;a2ٮN5ur}ۨ!#\a|:իW}Ωr ws+w=!%<껬5R;w\9(W @s8rhV:GW_[v $5>-7}yznlupcP i<դ*ZyȆOyVP}פ>%ZY}Z,HLoeU$HDڛM-at8E Q4h}Ǵ%ePXB%H(4)RPB ŔPL ĔP$iH"SB1)RHB ŔFB % Iɓ&UfB4:g-U@D$AXI!|5_yqk/ós7G ʵaTB>9خG{w&5XrͳK&혶_(_Hڭxȸҷܘn+GYt*F[EOE]6ȭq Nn3߼ynE;> ~;Ãܹs-xWpA迦l5=6CU/w_y-KgϔUgqކ|cy7?yMЖ _9E^0aw.ns1@u6/߹{jnըQ<ۺͨ=74[Z$ M) ! %#E! $4HGLhZLˆ- %B" d6ҴH6@$HHQ"-Z6@F"Z6JRBB$RBBGisf̸I sF-Al^Wӝe= t谢GU&[aϏ%gTaׯMcuf$5F7ȨӪ~,J]u6J9rRi9׿ j5ucgSɄDu;$Dn)de8$ Gc[z#Aȵ喝ޥH@W3ogm\kSDAe|KLty|e]VȳI} dIhBmiK@t-˥C58!&![X?$]Z]acpBJZ wΌkz$;-?) .%7% XɓsGqT IlF{rECѴc;O.+8ԋ$E,iV7s,$,#&mf$(Jǔ3- ?i0%Sg}4)vr$dZ Y՜1S;fq l`&/F҃d.ntHL;sk$N~p`&.z:Qߒ`wn.TUOB5t;ce"D *3o`ɔZL+Kk>#۾k_L};O\0x i6GG?}GЮ2)3J3Q՜>BJ (!r3qp吪p4PjnQuܹF0'qr*H{#b}%|p; f;lu3O :!Aݍʖq#U2PAe6PW⼅3TqP5J+Y6嫯ÁQa:8d{tS]NLٰӣ뛲@#*Ɣ kLB"JTv uIs`'wla yNF34Ss$?%M %mk̷u$<8vz -YMCW[zפݶUdY5kq 6GS%esPaڰ3bR`T44übi;O֏^~yl5{>f%R HS>NiUH3ꇸ) $?3vf 2OTT-fHgRd7QtxԚ9G,2T7f]:r=2[%2ЦJD#=vq ro$SmWz%)-ole6o:ط `<jBaN*&r/'ϿOQwg.dڃ[wXswe}!YQS:$OئSԏ6jf{~QiB$J4%/_)^˯BJҕʲkJ[yilD<ػd+i\](*kjȽe-ɍg> Nn8'΂zviB "+mVR4.<7,46CLSY%W9oG֟x*;vKwC}R7LʃԍT~`qeg5l6^Q!mC$@&e,S}t=UĜ;uS]vgW3SMk֑mmTYNP_S=t= 6n2O.FKY@ AE>5,Ռ o% i8Bx>ÏH14b5˦ߑ&M,QQ)zSOkO5mHk曷k91!ZqmzQ BKaKնuS;!18yV»$1?q}{4s۸73S g_exLdXz*K!,1XR}(m;TVaWll hע\h@jWIV4un%\/$ht uJ5pk6 i kt1ч0F.MFUʑTQc$ytU7ljn+ [vM١[I-+@ߑ~hY%V}֠G 6h@/#;,*SuԋA(waV&@㻐+T &&LhrDtD֌_ .(C!-~˳*-I#wɝspa~hH(tMpl8KeQdBy`m xa>!I([p]^(G ?((B~X[)-#` h H=.4H' }~ @IwV  ;C@H$w9 rșC:ٔWisBIӍ Gmoi&֯?!|p dV*;8 sƁnĺ];t6-_>hC5+EMQ^TQ3!w3ԸU~h1W󝺠yxIA:WHsrdA.{$z#%bJfGʖ" d@gp$AZ)]>HD@ .QA9:"D03+:gi(H* \OtIIR>S4d0$4nz*g<_|9v}ǿav1\x6%Ȭ4re~bq%1zf#u4k׮O[zJi7mHHMCD$knRCz0 aO9"BW(4B#[VvU t΅{p()*za#<g RpfȬԻ\GtY溻uUD XM(i\bt~RTS }A PįKuHIJos=6+$A4M!` R#$I$ 2FY3A̸$I@$H=, @) lK*ڑRQ]if$i~tG$3:F@"@4 D#i`N hHؔĔ0Pz^ɲd\%5z~$fN)DAQr>J!Fdvl>\\RtPM?A@$I"I [2  H$_II?~^IZwiѴ hCM4AЈ&h&h dI"@L vuw @n=/hEnC(B߻zR]p=o48*u0: h&L//&*fp(D|,I0 !qb# Mr`+PC$"Ʀ$ZPD%m_B!ML{0cHAlhvz,vdס"H^Ll;30 1[ƶAH,Ez,__AMC=/;cZPDa~3u4ɧeeK] ˚u.̈I'[_TW"*ӽjBu[c߄L,)+(Z c\bCGEfb#D :*%sPD_Vt vMi7 XĶ#/BZP4, } :{w]L %] VoFf+Q'8: %"1w$Pva@K]="-ht.?SU3T ۢCE1*cRCp`)Xb1 m zPx lbVfS(6(*<(GEŶ#ɰwdX'&&HbgB=*t11A,`Z hks5FoK=(;7@ś,csE`F F꽒[VuI$<=BAVh񺺺}& Yd͆{" C cbB^Si2:YYŮ:aPƋ%(oZAl8b0:ݕzboY!uQ n  B@(bHD}DxUHmBfu1LLǺL>bo򑢢O\jhc0Q5%2wWԟX@EDŽR.""\6(*--cUX  -,,, Ư,*ʄ|;|c~TKR)sʞn(`JHNKST( m_>"ⳕMQ>iGw^*ZhZ(bXLLkej5y6h85߯87^gQL* oMF W%'&`hH+摖fansR:N4EmbW,Q mCY=%LI@ɔiZP4MI$)Zm&6ishsm8xɿ`A͍bLѩT ) W{ѯN-1@)?N4QVII*R0t{x8a\Ӄ[NJ+SnMl9D)_sP!ؠF5j51iӍh kAQ;H!f~QY4&_*`+H꽒~ʫ,Qʵf;5⽘1B]JKL!@XBIt[\ςCy^ >zc B9(US6~)^|]^y<2o,mrD9F.1'>pZSǛ|s4uHӅ/nk~g ף+IQ5( u]|̱}+f9)[BQBV{o> 0#W7*3u _L!D?s˦'WFqۘdFm*3?*+yWuvAߦ5]p.r F#g˯`DWn];fKo?9m ~JI>(f]|Io=ffLCs-G/Z 7mֽa@Q"դMK^qb5k61L(Eps wx UҨߧYg߭T +IQ$ۛ$v39L;}~>|{y5 zAL1fp#g#x/8Q}L;7S{_{,w{T/zmC m܊}^ cɏgذlΣG:,>VmZ5 MSZCP/oX#EfۼƀDn?ps *u&4HR;qB0-W!\FR9Wz/o2ڋ9]k\w/uڹBL!,n(emS+蔇{myK;NΝ:iK#+mI!G.'0gs6^]Lݞ=bv5a' kʊFn:s9M1J7lC"E^i $ޯ5myč<ٺK iTG0蜝H/>hGW(V=X>VqgN)'c=uѓT5)rhšS;mR#Kqi;r݁GgWmĤմ-۷jm;^Q4EB<`}6z+hhAe;XЮXV}E$ЪiA3;aW< 7zvs)|a,ǖnᝓ^4'{.w\=J(Vth֮?Rܶ 2ث#!nPel sW1BcZK]&u22>|Y9wQ€Mp3vѯKV!߅#.܀{Wao1_WW#Q?2!   u?[[K?"ߴ&ȡn~VVOl4,:) 毁(fZܻ}_P7Q{QJj=ѫ1"0tطQy ƕ'-߮rY3&m[c;{maahX|~ e~L4غbis-?=*RWߺk)F/D(keFuv&A=U`kN`TI"Ck{' iWaU_7~!)c8 /z[l.y}wjӳ1]`jŒF,f!H̔#GTl36ӐA͌iJI㒿X+0V+C3)@G 㺛 $I aI<ο3 IF3[8-k[$ [LӼB*VүV.f›|$IH[s| r1x_d[} IS`ZRv9ihD$IgOv}Rƙ/JYhOҭK1?kU!é/+-GX3og?fQאcϾ;:vl GAfZjsBcaRgSak)cV޵rDrmGtjiIhs> 21J)n`ϨtC[S@c"%B13P)\$bx^2s-'%whѝiN$%$?Ӎ`r6 (v#đB0͝kuj{eDa۲*{18e 3$&L~4(Qc@yx55, {GˍS`It .~ejLS(+6 ^&z-ۿuCGyn$s26*{@z419 aҋ}Tc,2e K҈F\hM!ȓGPH$?IW̱+f(:}w,[gZ2tițhy 䚓Z (ĕchW*7Uz}nG8 h ArS֮ =iپr;UgeFpM z6`'}CTMihN ǫg6^f@ b`0_zBa7g %.YT1 Y_EK{.D/&Z@aG,FlR##|W]K¯(wi.8&+ ӿ8yr T^w5,M$J wkԷe+v_kc 4pi\|}@c!NoCz5 %KOg>ͫa smז#r}̐ܗJ~w͞[4aдrµҩӼ/Wo84A``SqnUJ}sz/cMv (klE ׫;z/0oV5v4N w} .<D l+< T6v\{OBC<tױ[R$e|[K:]Q$QɿK<==@* J85vڷQ{ }8ɮ7i]AvRX,\1 57%?}q$7Mo*daf&'FzC'{?Gc:zjvSd0l)`0/GCPk:#UM}euL'CTF*M֭[Oc FlTUhwi<>`0߀zSbN$-ʆƎ]g6adIn^KvA6NDV1߻St! Rmy*H(Nt `5ԛFlH$OW:`2jDvvP(yP(_K@E!^D B!g.`0 /d1 ~15H$"Lrl6ˮhعC%*@p$FE1HcX 9|>/1!A,睐jJRR.#-$,;̟B\l `0Łi9WτDd*Y$57H+Q\`0LH'!<o$FFXj `0)YYffiii<6Ey޵? 9!<EQoH, `0~05 K`0 `0  @SSSx\ΊA//_]g1*UTi?~ =2D"!¯ +6Vl j_IzZzbb"A:^RS[l1e] XC# Vl՘5τcQ,$At ``!`Qcic1`jjZ}td` H `01Y<`0 '%Ɣ̼ɣõL#%=+g3.b?``J<$ z9(oGN(Ί Z|@>دVyk;GnKn%I?UX8Um;D_ztS;𰱶)[\Jim,Jz[.*QJJYO ˗u{C$ **r/T9Um=p(`*6U]ME:jmve`J8H)jN<qyɉIɉIg뜜iʸF ymqǾI@ewDF_QݜDW?m݆aq1/,oM\ ~ `tv4$E&~對xUU$g+v>hmG|G.=ώv8V*l-=41Us5?tK~ĭV\| 70mrYk.e:wY%nxՔqz4xGp(ь]3ƭ}jtLŤqO_w1SuMDK'.?)+j;d=>&G۵wDљrҡO48]MOWW7-X2ң]_NQJ;佗G2v}in>u,M[UPַ^9 &-"+}v{D6~qEq͚u[mnyoVÏ|֔וrNKH^v6.~MA䊫 +`{'~lX + "nvhjH/юAظ/WκTvןޞu1O \~Ǘ'q&UCӇ+}<_MDg<^{ϕtH fK=W FzNBDlݦaYGcMȻ_[ױ.ss\|؍#~d), AkDG\m{쇙Ңr,qgi {ϊn}zY1ȹ)ηtf:vӲ-ۗ}n|w;j|rR& |,܋o?z]Q<M|o-աOec -l T&5x_@}xCxLb*H}wpNl Wse\F]V57Y>kÝ_JٿQV[jfuLTW#?|L'?p_m=Э7߄~~}aCJ.ztRB1&69rkv\PLwWgN]gvͮnhB.):SuΠek[:Ʊ&7wlXފb6?0]RfO'$&$'y~pg>Tw1 I S?\MJ`wX1fךnN{="Jsl͘VӈLڡ\KR{gLR5w̚&'.Bjٛ-+t60>?-v߹[k 7$eްO-ɬ=+diz vA>r"[gn'KozXٔ2u?hG!Jj9~ی6:CW40r᭪:LsM|d!A̅j{yELgӿjo33Hcȷ,fOWoK/0Qxxsowvh.W9 C;:Kemԩ\x;E]i ,*NAWd1yV^6JۊRn֤uYQ/Gf7†`rl*u}T^I6vuK[ۺ\mM/͋uID|l󃽾-/F%mL2lL, ayn/2ǧ. Д:M0H02}(+5Ub~MS<{ԙ.{ ɈaԳ4{5Ԋw/yr`MIʃ彇ܬ~ cjҺ:dpJy$}{f^UF8=ɹGHM ?9706'xB黋ztsi575z$%!lUsʛ 魘N}ZyfNEie~׸U^Zc%R5Wݻ`&*ip M˘|ˈU[ov[(hhO_v mҳ HaO*~CISf6 0QWMK^lXEҍ'5&)=o˃h~>ن^];_JŽ^إ']wyݛ9v* jS%߲%G$VnG)UEW7׊lzZh~G*4+ڃvZ+n"^xn/:sS VٌP{ f*d>U{Јb =z YIÐ^sմwӷFtih;G&?~Búv+jF|Ywww0.X9?EgJ2-S>'$'v`tn栃cmbB3$ D罒Qޔ#/c.mGގr:qXrQ"Uq=NԱ% w{?,h3{?pJ1aoC?Y\ւկL DzQ0r“dU8)Vެk&m"ĜqNa^IBM5~w_Ǯqc/ \/? YD = =SQէy%c²t8Y|YrrL"`, NJXn&:װ°;-ʷt$g.?fٲ!f=$Gxkp+A95z-SZ01u트3 2sm:mˀo]˴РY./ՑO JVPװTcR:8hec˱`|0m|G|\uya* @>ȶG;[.lR͒d459ZivD}UͤTV) = yu|بrY7{ 쾙9qg:&F^N.feej{t735_CնF]ö:;NH}D;"F5UxW9oߚ8Ȧ#j[HK,KQbY)٪yBn^U3k`wf#LIcﮍ3wH휹ۤ[6SǧoѠ'" 6CtoUDaK9^2no2VΙ7+g6$ ۵,8+ae;YɌeg/.S,yL(u$ps[C(q4nTP"̈uAq.l' l"Iʇ{wz?:C$iY ,@E eWqDlّ&*sb謷vZ9UȬ' n̓26c}b>*yTn5Cx-K,fļ|]%R>;iVڠDMZ7&~7#3D{l@]% {/ȕkPd0dRXؾ};Q;&o.>\^7f棼bӭ)oA]~h-by,&{9TK(Ŗ]ifvr轠e>)ԠUrH] UnM}jݔ@bkoAKZ =z87?f ˥pnYRijY&+ǾjuVm 2&Mk]I4鹎i\K;Iw Hf}|]-2J{}lo<^%Ȣ$_]mwa tX ahZoԎޥ Kyw6݅PQa4nOXJƵͣSt2y=ob1J9Rg4 L톎z|uZ4[!B~cPMg,B[~(tjI2lK);+rÓ˺x |sߋpڍIyٜ[ɩ\gmg[+eйmk36 hh3:L1]7}n^fplt#yn^{#FzM=yАN>miU]r(+Yѻ~-K) SίfQ*;.z]iѾU8`R{=jxTl0U9`=fꟖw[^>~&s~5}MyVg;>[Yz-+44~xD^?sypYRyӸC]4Pۚ֫VQ!W>G׍PyD.<9M}/۹*?p$=+;ٗo4M>27ҫ=97Kss0xl]E<\6详h Ʃ6d*:>o >]]~Z=y iW=<1wʷfX_7|ݤ& ׀$wΆ?Z;j.}GVEP uVe2yk>47zy?]ݩ\ŮGKM7G'= kIAK'%MHoOpe^4s|D1a vzlg̏e\܋9Vu4Rp}"&uuMx+P|Y:Ҥ'5{ ʬ*_4p|oxS.]`v`̈́ RΝc*}k/'6&vW.ңdyɕDzlG0нB* 7G Ѿa!sm~).АmڳXxaPP;N IX1XKVs,l5c5r6|}wƄs{.h)fϠi&,~"'p>фƓ vMtB)h̒ o{aĆ67ۼaH?1vF'/-ϒ|;:h6gq /κ钥u tSno]3'8+wKZ |~A8qv].;bRu$K[̋{̎{jf\M?ռC۵7|<8ǕTY f ,V\^R]}aM>*\ NU1T4ЄG^L83q&q,`:tY59vЀ bcC7lfEp*uy{Cb`dSQ;Zؕ&]:uR?ukUYmV`>dE?e\3r߻>Gn2rǓTۨr~qK-Y:B`nM9ף#ꏈs@$>=KYiCKWLlSȠl`%h#uE` ]r$y@$OJ$>}"[i5^ϡ+YbΛ_v`Ɗ:Zz?<ظ$)ȼ^T3L+{x(y%K< /o~3)ؔ`0 ^J}И"`0&>bpFe?n^I `0Jh)Q &r׈vusy]ꌼ*_TN9"ˁ4Ő]SZ>u\]}Y~[ΪTy]y%&Lt懣3tS6h9%`J=&twc0+X ,ʒZa]Zds+[8:8BXUNP)]bN94dF~p?\[=W~+n&-FtS;𰱶)[\lFnxĞY ZtsI51n#e ~͍tuurغ; \]\=b$)JzOٿW5_n޻Ѡ%Ɂ1=tζ8\Gi7z:He]+7=Ȼt}I~YPAg;0{1g[!Xf.u/MJ#K(x\+kلGu}\]L:AA蟷7\*3mΦNǮV)4ŷgt444DiwǷZhk10q3t@%34po kJ uwSBfO'%'Fe_Vzf|XQ{ņZP"Kt%nFX\l [W_g5?~tsO{#_lrPdZ@4@&|oUL_m=Э7߄~~}aCԼCOj7?=?9qcleaonn_!~iC#"_f~rh}1%#͈1 鰝ʗqġU72)eO):^*ha[{9i })B⬘ETuضZFe<徛R{ ֳɘ!\33;ZWy: Vso?w d^T7NuЩO pqk0b_νr]۱K_f髨(l.C*sǴba2q3`{w2e{Sbab[tsekɱ33]|=Z._9{Yo[շ3BthqhgZ~0r᭪:LsM|*1/Uܐ.UF2Ug"5zvO(Jg^^u[u[왝[۹֟|U]:X۹6}mϫU}gWѹ|nmZoCU35V2cǗQ%ȝ@G'v*^A[ n\< 25L=N&[{;Vŗu_U4Rnl޵<\*y)~u<\*^p4"[gn^O0r*\Lݵ{YيMGo;6Ȓɴ킺_sQj,[5q[;kZBzg.Ɣ:p Lڰzِp~ۆg׀ѶglYun/#&G""#?= f!i@J2.YS%h^iJL3k6?%0lY떕gCӄ´г+7KH՚3q^eMXlx$'$J0!5OZѪg&9 R$By'{˳c2v?K}v{D6~qEqo_>ĽyoVÏ|֔SL8PK9LC0fPkwN^+74= Z$::hCf?DEiH^seT'gB>ͻj^!^,Kxq@cP dT핷B#"71|8hnH[?<9腏3h8_4tMWsdNG*4+*;4,p7jRxcu?O<vSdHŶ燵{&ҽ,hS] _"SZ3_֣Q>VW6p+A'Zm-V YJ~V|Ep;(Ӧ| >%}謷g?:V<۲zmWrwB#q[s/8ǁ^aua@֣.[sSxػ͜};wvr*쐺u'(jӍج}(킺_KX}:ɭؘ:}_768s]qΦ\>kPMR_{T;C]|ʠj0yM6RLOI[:`\eQVV jPɫzŠqz/VބmY㴑·5/DtDm EdZ'frȂN*¡96Rh)XZWu[Wʳ&74)<Wq*ܴ\8Ʒth=M@粠k>6vK; <͆\ץ$:9'o?u>Y!'2,Q«ML48* ?ڥNF p*ĥqGOQM]ܛwrK 0–!,LKQÝCE Xۜ[};+p24/k"HR jk~-FJӖiob4FwqI2#le\L&{JLS_2f ,[/kc/[ $DS&ȱ ()I%k@3;l^JV^'NYyHz[8JO7 kRE:wԋ,5jf`i#SqMK^lh*/ am7 偉z9Qjx/CfO'%E<6nձL2lL>Z iTw oUJopviXƿkoӺC(cYwãr&Djl|ˉF5Q/:}&}) ~,${N0Y zoNԎDļ׍(c%Uã~#) x~4o曡?1k7F3,dFo3)ӿxe|wpa!ݣ{" GM<|c%Uãg[C* 钙;K$O7mru(tIk;[X"̈yXpR>;iVූ*M _Pt, .aY{HYsS cuCgأ̡gNLQH0Ԁភ?]۰'[wm.98wx0Uk04କW3.~|d.I1Lj)H|qhw%@gLN|5Z?: ~ːb5f%&tgIB17>jAc`:4fwɐT0EV{i}>YϮqgo7{ww/iB&}MlԽF8sGgtՑo5EGE<:6UڿӺâ? hP qXj9a^_cԘA&5fso%rq`tËYŷYF^36 hh3Rø^ v9Vptur-5u߬johF> K&M4?tb1Ǘ?]i<JZɻUuwg,g܋ԓ{ *o~׶VU)~ye2λ0$yٷ+xyװ_\6MC,=Ί5Rך?Q=)㜧 48Pp1M<=+&ŵU$+UQu#+J8G˂U=~Z>%~ޞӠ\Y0'~P2m6}ׄ29Ӫi8Ҥ๞&5]cEzR~o\_g?⼻6i~<*µIɑp5}Z=W<{t!ҭ]PWk*57M=ݳ}F3T],c3lORƬ^;7*i^|ڶ(^m:Xx*ID(c! XbK3lbX|T3L'r.P[סRl=bc ` d79Wqf1 Ø69ŀ`0zSnD#ң `0-!TH5[ILKM"`0 [HO# XUo^ľI `0 PzjJwVVetc%-H^cρ{j8V"(({%1 `0zMI `0`S`0 'ؔ`0  6%1 `0zMI `0`S`0 'ؔ`0  Sq!ra`0 b%4v`0 Ȼ n0 `0E6%1 `0zMI `0`S`0 'ؔ`0  6%1 `0zMI `0`S`0 'ؔ`0  6%1 `0zD [ $r>a0 `ITDJV"Jb0 ` `0=$`0 FO)`0 lJb0 ` 8S) `0糪TcI$1 `y$-I `0L^3*V"+`0 lJb0 ` `0=$`0 FO!7 ?fX: `0.JV|n6R: `0=a0 `Ԡò+`0 lJb0 ` `0=aބ`0 GK2XH%=X:!,8G -=0v\`UtwS76t g!O%Ї/0 `0*0z.`0 (J31 `0zT7_`0 z+j1ⵀ0 `0*<wpc0 `Y$14@_ߢQ5E_cӜ @ lc[ys b1%I eh$  ~ F BZff66RS) \nxd$Sm\w*Ō`Yc0  0GxaXׄxkkҥKaYa @ g||YSRf7J&$3Z`I1 5!X6j"IK S$Z .8[ `0bEr*,C!^u%x/w[|_ o XJ_ p:f}1}K8816,`0Y$_{QݳHg.[vQJ+|@H֞MGYV^z$)?wޫ&e+8_cW?6Ui.ŀ褫,|neÅ\M lnaѾ]{_qip_L1DbzvdGS7_}m4_\"=UkvֳY~ĊOKP"=hH1H;GksS<*Td%`N^~k'(ôߨ,Y2&7Ft2^n3kfmzhRg؊'6!SW?N3b&TlDԅ=|УL8nաo^4ؠnͬ]~]d\Qe5g!JsIk{װ"r bmSӉwo΄y5VtUuC _NQJN P-}#Z['ǘSNDöVrw~xMZPRokRǖrlcqvmshƜ =(wq`PRqEM΃G/ڔEbu[t~yyw?N-l"-Oc}a|ƾ4[rWMߙb#+Qх[m;v.Wz-`'Զ;Ϛ3N#xJԴBz,JcO11[75_8NV\V"xI*HXKo[C͉Ԟw7=2k?o3or*OǏ1lݑ'.zto]B,9mh3;܊iZz OAޭ_{k{éٽZqbE܍,,,謊9G_ Ͳ.C-%H&h1^̵( ;O7a¤tʛ܌@*tJԘPMY8E4Vt`!P!^HR_\D7 z=M?Ke6\_*_N 24d@0  Hua K\kn @*=)?/=aн!<Wk-7_һFݎ*Z% wQˁöرkM{cFYIB_? Z ogmd`Ұ_gw FL* -]edf,[ф es?~4`(rYIAkj׮E$<>qncX&9GVm۷9bE3;8ROqgѿ!IjtT˓*ZaghK2F R-ڔKKᗛ/m,e``fH#(hTiҀ m| f{uhƺs."E7/,Oe]ZJZMТcv40nh+NK=c@?CdXʘPMLMԩѩTjWUt-ҧŪl^ICkӌ(]t'aO~F@xaEH22SƔ@L̐d%$FhPFfF iT꛽S&y7VĿ'[9fǧCXx.Jz&j6Xj)٭YfbiPK`2HCψ83gQܟ-)Զ2Vk׭3j+gNlM[6qsqMZic1Iʹ:ѿ+ qFӓ/80CqoÎVz-+Rkq1בYM4$2߼Cts\z*_ȓ(ki\MJ? CIhCqiU 1-K1?=Ȥ(v*E@Js<Lb9^MT lNSO>Op/ک@sLM0Uzg]2XjcrHRNf,1++~} f@\8NDqL]j[@~R _P.Cuؒas2;({kU[z?E= nVݒ @79c3[s9T ̘tpT w6'Xx|WRiŝ}Op0{?}D Z5]'u^0rnTMpqO"Qj]*\bhk{jHtqa]Gn>*>[L Ӣ[dfׯԳg/.;luڑ\nǏY@{yke(qvZćҞ׋ `vis i`f2D??]y04ʿt( Z Ҽ;6|!0BzhTmK>kZadʈ!DEWAKPtLxv4(W50ay*V$aQ{}-g';}L]jPTtCĊA~uE14UJ_ <#/_,`UjgNz?y{~na:{qry0>jVϗ>pM,]͌}w# m׾u.kEF[ vxJ'ӳmâS^9+(1ێvQ#ԥn>v+ns'ر{jrC=ǘѣ:thgc7#{8۫CЃgoJضb:0?ݯ, ;•x"HBm^﮽YN $2vŨBߡUW}%M8jQT oFkf#0o) zxRMC<yTҕ9 a3[G/f Nq*G'oZޠy#21-B ld7ט/NLQT|\K9L&ӽٓY\_=9fc8&E_uEPyߩte÷< SB;;ŋ`0|>֭[011iܸ1gt>^tx,ϒySC^mngEN>QkCu{'mA+iaRjĆʬdj&tRU bxf0^^9bh&鰝vYRM8ЪTÛdls3$&1pƀ{Ș_[꿝5J +N7iHєЈ$A"v`0zyEn"#r{ͽP`zZLD$P 0ڏܰDtQƑ8O36;h5l_Ɩju}G?szLMVߒeGe0'\ˌI랎1%9Vyb¡e@]{ cg綣B$wy}ajo:w}9&$-K⿹ 𤼵{|yAF<{ 477g EQBdԙFFFW.MJ^$W6^~,XvCoԖ H}v% l2 mG'Y:UwOě,E_Ϯ;aqY4򶵶l2@W!߮`}qMer{%QY lʩ_yāZF$'&K&0ּ_H$ Es*޼ysMHT2Muj1ܚ2z {Tw/uh~:oV'ŮKn=* 54%w053c`0ajffol;]г([n$&Itâ^K}v_}0,{/cNG\|lRv8Йo6 s.Ok6V_Ί#=؛x@yk;kr/Ir;^z SP!r0a[v3>+Y>y;p`ϼ;V/ vxCKx|_4~gΚAQ@PҬq#bXƏsx)x"sLJt53DC ,}l츗B)wܷU^0EMI?SP9dQ/tlxM[aKy,d^僟)40پ0CǬ9Qj/{%?.=iüa4tYKk2 #+ Ǜ#MHWlMb 痄}ArA2OYS!ى*&yE_]s_0Lhj5 OMM)+F>:c|Ϣ8U=[b`:fǵV73şիcɻRPt]W0 b|!hn<Ϫz2LW&ft $мtџ2Fud՛Z 괵q}vĄ9$iQۂTevY4MILaY:W{ڈrʪ#zj.ްM-P9_~ ';I c$ɬԠ~W^;v`R4 ґ>necjD&m]V)GNJwQk %kR)0l :0Rmz#4&"}_rjϨ{l1[6^^=X7N$3DCRUc2#eG $(tv>6h0sّRT29ռF@>x }vvLp'2]$%}nGY(ڭi]usM6_V5?(=dcYp<ZH'" 6Ctoeгxﴍqi!g,{ YaY{HYsS cIhR'uFN6vJGkBzhI}9nT ?'k;{FO_[=W~+n&eD޶J9k;mc+7B(S2at|us歏i4 ػkcn/g;g64RТ%x w7̿гZ+9τJCŻ|'ti?oiP7?oG|dYG9ӷ1gutvp dKS7fc *eйm!$L!iYxʔ)\.O "JRn95g,m63ޣѴgn]e3/;f9gMykzZOPˁ4 _~MlFTQ=|udɡňo3jðذ[ *>w[@ewDF_Qݜ(_o/>ιJ"DFFu=J:zy@+fDq֦`](þcWޝfl\DŽPZVe'u+mYw p`0GѣQƍϝ;F;x<@ qg⾶jߙŒ*n_RaꗋjZ rѼyjx@b'hP3a:%.6riL O<PփQ#&{d K o0ju^ؐ1*Tz[qK.S xxRM,`hiy꽠]z2hi8À3]qq:h^ 5=byV:}숻b@_6F$A B}Wm?Nlq]Th4ѻN6^ݖ̒>w s}dUbƀ?zh[ ϗD6 rDܱ#HLd8y[Az žojl2!CBZ,4I!1{Ms D>jӜ Jl0 ?-)Qd~!YYSNQ4edd62404"I ƨ%ak!c DA4B'b~-NQ7B1(xt=4kD}=q&Fyei < BJ|jԶG9<~ݩ ;;2/ΜVn;Ƭhtkx!?:ь4׾d\A?W5|821ɛS[ے@g=^ĺ0Bnqx5Pa::,@%D2?Exx{tЩYk[:?]`p(@H]G]m;8c=Gw\mO<03tNWY 4  (J(?peC,"N,KO/$yqs/RQY!FwN[HA7xEKģ--(x8~2'?Dv ;F(4Ci`~pR V_כVC>`';!QdN] [L2# I!۳*W1@Vc>t{FAVm/Cu.׿q F@HL!i}{X !Et&JG|s=ա^Z BqXòݑ g##o 4nT0BbK,4{É,/ۀBuo9/u =֮e4;w̟l /mDxHnER6.$4DB!$HHYZO޿uK$IuQplwm+o޵f$?z[}F{uHEtZʛjo>7<ߞGnl k|2#o4;p0%_lJ"1m`~M/ ":ȡ.N~=I'xgu=LfD#HB#1?6"X;p Hl2ҙnlұbYgƵ[G{($4 ͈{k̈́%?ϟέ2h~֍k6X۽84BS#g k>0@.1@(V;!qF_hؾ&)WA\'N&GQ9:W?e_AJ$,`Pb,&(@ 40H$&`OQ3)BHnvK?"p;zX~3O"fU#{vެ\RT;$ ~W9DHghRjC0KWR )ĥ[7?{wEVJRiE Q1P^110W"( WC@@Ç[vgfgYzʹê^.1^n=g!tdK[%jd=q=tflљs-Oac*Tσ8>h:i?r9!g2vma#;hm;]>@FlPϵluB[[IbLl_YƲ|! :ԨknͱJ#hݻnB2(W5n/߿G;5$J~g ZH\0ڒ@p9pk{>#OEXU5;(}㪖vը4ŵK۔!*{+$]6?6VEeޛW!NhZ @~1wB'[l?tL7\(j7jDRsѧ\<@ϩzYVN~NX.RHklB0XoDBD-:y, W&F! 2ٸߟ0yii ݠˆ,gC/ UyGpg˭_T~w'zT|KqLF:sq3 m/ڷo|wU>٠~❛,%l雡Y"ŇxXȑKl JFطuT2 5s9:W(+EVFԧϭ\v~0d{#%7%!d [0)Z}.uՌ8Wӣ"hby'na|#]ο8f*Mf(M!QߜjNϞ캹MV ~9n/Zco4m=o7.)0)7-$.=e;-=i`! hÄ͉ng3OfMv`dkg%dvMd0jPI  Mc4N}Rz!@ !Hh9:vpƹĆ]s(tRbdzsTE`3}qVZYA؅$ vZ4vWy|5ӆDb~tw%9{K6wѤڸz˙o(tE] cak:[K\_BfpXRy%)T*M*-Re~1^\6л 9B!T-<$r!4]Hke?\ xwrS0_y=%)Ucxhjw|ȈI8ct5]@jm%"CmChUȩ7h}9Պ])u~K ]m5%j&-O6snp:888F߉$ղݐ]zR 0/-"?HԈ, u5W_ٺ.v[uid&ˉ 8r4BFWl)u #NqiN:vkJsq[rgϜ򞭜kWH)RlCަr/?Uwi߀$eO}ğd1Vz'R.6xw` MU VM.BpbwN[MUCӤmIFt(RӒR>~,+YIuͷo/z(\z&"" .(-;X’=6)tJKNM:>8qxv*#3b[ٚ* )??_^^ޡo=tE:jLbԴ60!IjٝiEw*De_m\|ؔ%wiGߗjQѾconwٰ?kd25[;>\j(M6 e,Q]f;ҪDo領/S95ŔYOrxpRBv gZc/V~od/;aettmOSV>c= n\|KsU`x|bDZ_ ySӒv-nD/<r9lNAAAG :|1^ ǫYZ fj1ߞmn{?2E :~Eh=/cw.xNq+{K>M'f9'nnj3%~QBMM(m:[o{{ufjl9qwp3C7lxWPv [[(&$p6lq# 4zAoʌ)\$3U{)]dNh,&ބvU6e>;%G&' VO;-YzvWyi)2*M$H2to]Uʭ&J.6 EW, \n oI; <5a :&kޗΗQݶ#+vĊ6)IjO=2{2Wu!l0Z9*1ȫǎ 2Q=T _'i""PZQQEf(JrKo c݃D]Wf\7rA u򊇉$)jm6/]dqW+k1(t$3ݗ3 F^f_7G[IFDl:x, åR  w%%%nڬ,Pa4^f:l{S Kw@_ТTu w]h]_h= ᅴ ק#UqJIkwRI~wIVHTYS^4D=>YV^.}ׯ,*,*Aiט*O~"ڪKcG;CqCl:cNH& 4y$i:SN&^c~q5?dӄ뛶Η< I۴^'W:sZwe[3N뛸H,IQg՚^jj#E?^=G>?P|ƒ_9~AGʟÏyc+vyF݌ K$ڵ`,^8-- yBfi1,7b͊5bم&vin 9{FU#['^$LVCU 0DUZHzq{tÂ?{\ii׻,h8.~X"`v-W"??s悢]ʒUfx~XvMvhDAmky{T['ȥKƍ'##p͠ {{{il6-Z )8Qpt+$%%1F+{p=-}Pu}jVVhm-,\8ߦ {>?Qڬ2 Ƒr;\~}~i67`cHLc* r-Zp8C  eGarʶ4[8kC C (ftjdeeY,VbbLKK㖗bde^rV抓Oli-We+эB߿_XVQJ9&Ig淚32B5ut:}4x`:'m/?eʼnOs6v1S-$p+ΏZy.M<5U5Zt˓:ij_.]޲c~Wףy~qQVI@ AD A 8eB!Tgx"BEحuhNT/Zco4m=o7.)0)7,rغ|LX~nQ}:[#];"ji^rBO܌+Cg~/NituI+ש˂+mo *QA@?B5~GUf&Z~.p3T-yGv킧 U^_ !Q):ݷvɴ1].O6R+V]b dq)3k}E21pK\0}1/E karR,ga{;fLX7+plMK+pn'8V55(mɏ31" 0շttLKy>_ j?5eyG}'N4';nI}>|^+nQiɩE?7*?ψIaYhIɹO[>0Nہ+n%hm1yv0 UXr#xRϖdqUw@f*3@1hToB Q\$<1CS+IѩCV'VPGc8$sT^^f:S*RR4'Ēp3-9QJ&>}GFVlzW@V!&*įVa.M{bID5o6p$UsMk>OWuYeMsYX^y)L)H[}աeuS1dԞ4^iIB=^#W"Dp9[&vUѕZwnrbHTf.'f$a(jpwnFz ^^ED%ٙO}jyIcv-}۪I3ڏX< QtԴ!X,B)B!S8kRSSjvXQwLnk`3z+.fw=+plث.{nR(:?Kl&>eXpy/. $ UKftP!IsxZ~7|a{/>yR$3G?W G̶F\\0BOWO {{ӳ٪=|K"Ra,םRM5b<>/Or&BaQ}Si_Ն8cSBvrzK 7tʰn~7Sg,ȕ}l:毃w{rҦ!r(+( u6jRf\.)((бC?x5Klmg%p٣e+giۚ;?=qC/m2֙&({+tƄ=o0 YJ<Ďc-$Vq.͆0S8kq%9|/+WXts?p-&MvAڙd?@֗'M:Z[93wV V?tatWL A }mGM~Nj| .3i̜y^tαn?WFZ˪FR Ath>u'.PJ+T*J7wH(ZjP(rkxKB;K^9໑'rQ]giK[b aBqO=6d=;Lcւub^(;c\%e'Sȧ ˚r}̖J sԇx/#[$geeERž/+j$Io %wwCϚ5k֬YNNNp kv}+< h~7UN gZItկ˭JqY fT2CA~(y?RcynLd]+H|vdbWJ>k^6wܘ-TE}+8)ODXqaլf&Gs0S-W@V2tѲ+|YO-x&Y8.JPH}))gee" __t’'$(۩WUsd}RFSF#W't3: .].T|R[UCㄽorD/:jz\-']3JOCMUaΩ`HBIw6~kCU ,~.[lo~̑=ͪO\z͡ / T5:O\~F=>Ykz[3om wDxS_1VOQFWnc3fvrbLMInٟ1D07G U?W/ -M.aaFLR;͢{\Z?&% VNohÄ.:m 6pT@UQ*y~{{H aAl`xFcƎ56)4 hh9<.Ml@2z]5#}/% -Pegߐ7 3P 5Um_t|9暕)ƿOf,z4*-ȸQaiV |!;텳v:!j͂ޯ36TSPS|$Дa)rk_iubrjZO<Pؗۍ@*ôukI )Z T _u4P3߭/ꃭqFP*-$Woz%o]L6qsstԍzNt7 z}NtY [ɩQ8J|۱iɩ98BiEFq-(U 8Luy*@}+s_jj)k)8B5UDM?:䕌q҉ɮn9di^`(B?UX"E8o㼭I2bM"ދ*d:f1[o/n_̕]^sjcKiҌL)ޥtuSlc7yO:42D9n+#vCէާsiAWԔG{{ >JHNMGMxGkr`!;9bVW=zӼ"G3+L sUGconwٰ nC,U54;N&GVF[髩jiZ["(eZ'w6e Su 5UG*%A5kjyg$d熥{/26v15T>e{)EY|<&n9/:jz\J~kCU F+0vFjz'~-jVqac}*~~G3vK7kw{ <NaF7MY2OW79\;骝\L+_ 9twm'+5\Wn\v~][vv.oA` w`مa׷ bqEO3{|u&w梻* H7Ox2s8cqe澚noj3j[.87BMWUƏ011A9ς 6'|fHF 8[ǯEt̗ fj1ߞmn{?2evUZMRXnG){ƯC{ȴ-߳No!JÏc磊f}Y>~qYRV/~9n/n{ʜ|#{}|ݩ讣\墓9æIƜΟ}:(BUSveFo<;W駣=EO.NNs U(J1OidG.Ǟܘ4Iȷ'&n< yhT΁FƶZQ2r-u6SVjlnpw$ȪXf3h=}%#XruŅ>^WB!|r:Hg M{;'⛱8[ftQg0m=63|8/mSGVFmdNǰFk5O*80/ESFWq0I}[9zY=67^ TfŽW~ rֳ'.~}2k!˲U9anܶzi[K1LֹXJWIڜzɟ٣:nyVF0Jӝ$lМ/c4%Vm?W{<MA-8E g*v9?#&)bIɹEЩ:J[섂!n*ki=uyϗM_,.Q:2t Ä'zB/X2n:O\n'C :У͘aߟh2?*6.J:ΰJg?''OEXjX׉|Nzw,Dj)uv{ɍ-,2$$.v鋸ؗK |rZ#s!j 5T5-}7I\@<x)at-EMI+2Y^DONK('$'IW/-"I(5"DIbYWG|CLTOҔu5WNLNM+Is, U\Iڜ,S_iZOT~4YrϷll`~OOgwt 2i.\K:tH^Zt և 2x_ݱK󩯋)V=[h?/͖nH`.tIwӬ0)Zw.Vnmdl|(N-kv\W {gb(BMTc%RD]|V;hˏMrS__~S:A|%&f]ߴu>#Iәt 7i0[J&pj&^M#NqiN:vkJIbb+{цwm'vQȫx\{-g3g*5V(:Iێ>U\oO8SO4ZvV&LT! `(vBr MC>eef`$ 2C>341u"W!j& R7yX:rFtZu J"B͇>j0x!B!B0D!B$B!% %B!P-!P󑚒C+$  HW rr:Yպa(B5޾VU70, Zvv7:ت2P!j&Bhk0L, TY&3,4CIBo' 45(P5kjj&':|!j&DBGb0"֛c(B!j CIB`;j gaY`(BQO &Jh_dþ#P!B sEnZN~iɩ7' %B!԰hʴC;DΓiʴ}`XuDf(3 [J䕾?t?R;U椚&CIB)IOMMMIjqգ=I-ژ"Ed@kθTB`ZΞCNǨ|\:{"xĎs;2I5UoMIBp8C;糚``"䗚2:t}(-{.a˧P.AY k^U笛w'%\&[%B&FJ"BMIzjj>]5J"BMFӾI玪 op#BMF J"BMI>+7 lD!B$B!% %B!ı!aP`(B!j CIBb;޲c~Wףya؛js6nb&f;ۭ\QSUME7<1FBIiBVҗb&6;GXjus=+̴3RSm9qwp3C7lxW uB7勤TqR#y.Wþب~cКWD>^< i4oy~9n/n{ʜM9)}?|Ãg`(B5_K6wєm\=LR_r8[ftQg0m=63|8Lb rG2<+* k1[4N؅$vZFxq5 OwEP<5%!PESIѩ_DvjRE˥ZrJTfDcШL%F߄i[3v;JrPТtwүh*BeT^+^JX&]K@n$H.8Qa^ K偦iubrjZO<MQZSq30D!2 '{妾>TA1t Tzh4zj_ON-$:t˗{PVآ}Ԑ;{,@o;ԪAi\n+pJ"Bk.Q^u{ή" Nm^%I3r`x MCO22yiwmȆƺZz6>}XjEƘ봶|TAzp@ ?㛗ª4nIi+ 2 !(B!Tx"HBh><K BzFci.G/>aҗ^UiRa5Mӭtԟ`HB!j CIB!TKJ"BZP!j&x\.AXFqk<!P3AѲ2PdgeҤj9!P3ahl1(+3&QuᝡYBfBO߀L!{S)+gdNuZ$B!|c97B!P-a(B!j CIB!TKJ"BZP!B>B5))>rY@" w '/שZ!P3kUu }â@egg}󪳍j-I %Bf"<,@.(e2BC1D!v"@SSKN^UVjrRSnBfB$`{$)) j9!B0D!Pb;j gѤ*uv# %B!TsVɏV;ǙGrDX0 %B!TkݼҒClsm0)䗚z %j0D!o!Iu:#@S0vnmzFnyEUMUrBf?pz/qTxvp(P U Oyӵێ)JWOٗiԍ;Zkj^t+81Ajv?I+~EB 4zJ"B}F} 鄧q׍{.}+1jHpo+i豗1tɸx]~yߎO1/H'Oq{6rܢl AEݎ1.sD.n?l;q\MV9MDs?ܚzaŞ=rO?w0,CIB䑏 ;g3Fs1gbB G*;yZ,Ja2 d@#c{gᆞ9Qmfm`U:YF;r1 iEc=-!d2!aKotާeasfS٩ɪw> O(G\"$|v5LmuI|ޡm,a}FG03@$B!j텛j+v#g_w Hi*"HrgO7ȸiϡ+?쇛Vhf kmk>po/2g*Ra}-n+< N~MU$3c@AS TEMȊMU)ےI!Qer dM^Ft&D졩~GHJU% %B!TkݼҒSӒCδR,WE\eo -% 'R{lW4:i>ؐ|hqwTEP~KHL+b6OgbۨԤ#(lL}ہWPwQ9$2$X9'IT[نwIɩi?W% %B!`ȪV-[([yOylPͼkJ@L;R oR8/7o}/,͠ oqzoMlKz#uNxZĩ]o*n_o>}`X޽L[idۿjXrؒUJBô9-L%ejv -;̢BR3/ͥCat[x>Ps2miМiǘuz{u e0z2zeWz$hc>T_=4ٷ=:1qSWKμWk8=y^gj@cDzp@ ?㛗AoiݒV4e@ CPBDȅ|x!Xy!#hwa_>}K_6Z =}AMYO;(}5_*VѤ:^>ZOJ.ǐ!B (L <|] ȦEBs{LG76CYC#fCIB57SӰB!j CIB!TKJ"BZP!B!߇l4ܟw %BҦ˚υ15 `(B5cv aj9P!j 5a>3T5:O2[T/:jz\ ?jd·]}.,?QVzj&sNDvw6~kCU x˻P!j)ow֞ iҶ1>}gyEpa=eNM[dhƝSoYz{6}8S韩ݸĈQ7A.|#{}|IK$B!\2_L4s''?}Gu:Cc2 pNKޢU^/ L(ɱ'.#ʤFJS9 1?ҹ [u:KUi ;<!P3Ed͈J$~vBA c5ZT;3b"vijjjY.+j r7x?Y(HSiR˦ưZur~G&ti;pŭ>HPup CIBy"X﯇i ֒ 4E-ffD*_ԯiEOaӔu5WNLNM+IsZߩOEDfF)Pؗۍ@.4OJ"BSUTێ>dPĊ1\sq[rgϜHݦfK-$/ Gs$B!<{ҒPfop#BZP!B!P3r r@5B[1D! *j$;+&%U1D! CcӐAY6  Mj>B5zd )ݛ<, TLY9#v:: %BCGO_GO5B!j CIB!TKJ"BZP!B!B0D!B$B!% %B!P-a(B!j CIB!TKJ"BZP!B!B0D!B$B!% %B!P-a(B!j CIB!TKJ"BZP!B!B0D!B$B!% %B!P-a(B!j CIB!TKJ"BZP!B!B0D!B$B!% %B!P-a(B!j CIB!TKJ"BZP!B!B0D!B$B!% %B!P-a(B!j CIB!TKJ"BZP!B!B0D!B$B!% %B!P-a(B!j CIB!TKJ"BZP!B!B0D!B$B!% %B!P-a(B!j CIB!TKJ"BZP!B!B0D!B$B!%j6㉰B?BAdr kJp8*X!PIC 3YjXPdgeH_P<7)Sr@B58B DlܢFUFDvfFȇw25jJ {$=Q0z(!jj=r#db`0\67$F1oRrBFBF!#ŀ|!B8?ڎvPU"+/_,!$.:ndV amGk;;QPrYb@k;BX_[%B!P-լUbహECHow=ӥtt ]rDP2/ .lC#jXl'G\k==Y>| Phb Ho$-p#jP9ib? 8ĪA(IPt!PCAdff/~B!'X䚤.B)2tS'Tb0mmA;<$hb5 3G^娱U?9B@r)$EF+a息Z Ң,Y+Fc/KI<^0+ω"nG!#wR{ ~#\Җ~쿬 ^aboq3Ӎ}a~LiX1_Mhq8\))CƼ Ґjɢ?>(iсһn mP'>ŪeT']WMiAE޿՜aey)ɂ]KnU嗥QUP-bGL5nVa2,Wxثn+͵R++; KFnZ2Dacc_s*& t! (&GHbhvr>\ } ݾeNFrɊXO^|M~PR*w' Ddf~c 2WW,{xÛdB/ۤ%$~yj@hz!PMN9R_.lv)Ӻ$_>oh&MqI ddrï|i !1XNJ˲ S(dr˖?LŬ?%$]cNr$Vm{Mn %]Pd%VR_uHZ~}G;'^ñs$=@p;yŏ|5}Vn'wxt-Qt*44 OU$TA#fnn}Ͽ'6W32-:p.9(H?8i[9b}Du3s܅[VGHNNYyKLOMMMIjߍi٣[}ĮI: d~}e+" FҒ3YQp?k͹~A:+p#cXؙ?ֻ# "J_ $Kn}3:/{{M՝Wsx0z C %#sL& x_YbŨ9 bg8vf]]WȨJXUQ⛒P.yrNZ;F?Mla}_e+MۮtT!8QJu-jU. %-5Pdjv6%/ѫL~}Mm9hqn%dO 8|~d 7X8˓$+,+<"4׵ݏJT'`?<39d>6EkA5䥨T吁?*Laì[1ȴ0ʪf~t&kR_ZHi=Pמ,{D" ʮp>MS4Οi~zx$ًwe*Dï|zڤNz_uR3O2imJdG]JZwD2)%~:9iX8CE))E ;iݕATޝ[R-[~'>$%<(h(-h<`tzo@s)YnFЫWU\Rj&9+JI) d/y\yuS *V$UFgTL]Ƙ!߯_u?4O FC(!3Qor**QnXP"@*3KW%@'Mw;& HLcβADZؓHvo$MVF03yHߑmyN]~JK^x[IDATcsE׉/g >._d. 5Ir$ QdiY e%.z4%i9޽sCGXwW ?Pb9HUҖ+@~:W}Ŏ?(ƪW*h,+-oeu_xS" @ZA~׶_Kd1 M1K J_EJKkV;wcZ9%djwen!׮R D{hL,/EUPUhO޹QB-k+גㄠdGL$雓9fȽvp5jw*JSþN=Mq߇/Oq zzjIʪ1_jjmTW]zڵk\f׮]^ayD׶?`P)HvfR@+'z?X2λ,~{pRd[J:3 ȎϓjLޏ,F:-ȍYVy-TAݜ\[%),k0uTQaڇu0T/ZRkv|+#+tW Ex2sd*IEE刪{IuPȵTwCDVHi00>~Kדqp {KɶȬGgӥt{٩/] 0Y3>A<@V0_*4\!&%M""US-R Ľ~B(9Hbi l6cFN:7 ;dO (Hp7 ^X9Ν2}iC ,PUljE$۫vu}с{҃\>/۽wS~L ,+gz_NjJeQ6=gĩc^*RЫVt*_M R^'wλZ ][E Rm\|OF8b,ibsعÏd+Ҧkzx4f FU_UQbPdZ1}f\Eg0YpdI'OB~Z59rhЧ3iS ^g.;Nst\mߞ\J7/]xKva؏ jW1 =PR$iu!C*oWl/ = [B ƍKb3JzF2Bio[N^!9-ϙi>~9 jBaVvJKjNP5Fhc{tk=]*g[_:q"Ƒ'\nvmm4+ !>2imKـ#O$VCF``\!PP5ed9;`A`\!ПQPҶg b@!;7Iow~ Ph^5 %BB Ʀf%}wu*" &T@!8VE:/BJ|IgI%?׺[>`^d_D|MJ`Q 4\(-5^( .7e0_SVp]jg3"vs= Uޠw)G9[=;W;Ci:@72j&r$h:N򓣗N{&*twd^'50ǯ51{9Qa$ȴ5nꞭ k`ɋKƸ"Ξ(ЌJ7u[ܫ @zOu'.#?w߹gbD/;~s&"#>WR~̋ljoC+EA"7Arr:Ylx' ($/KxXhKBv}BQH[|jbruoջx(~Щ*2ܿfMY;nh)Uv܎o~ dmݾb$Dsw-9dD ]WxGwiג>/ugf Ү:dy1[8oUգVup-!ֽ@VȒE?.Ψh{:E9^C]l=5-Q?qaE9?u EtVS($/2s.,>4/nwo_k}^"~UgZF 4'|@4ː$8_nɕѵpv#@ w]{c7}t˖g^\Wzld6(6md $|}cջ Jcew|Ӯӡ@e{QD|V桧ov2)Hk uJ*WԪ 5t{;VFή^KTu#|3:@s{,7yT,c1=eH"i;v)\|ʴcj`ҢAdRun\@ؼJAPf Vj W*{.5|ۭ)i(Gd;ܽZڹd6xNt )=y3T@TF@[[db!TvA,DBI! L~&b',~p굌^&@ȃu :OqLNxwf*3?xTky3欄┦Ҷ;Cҿ ̍l (:0J97:`~U&,$D@Oojki(~ |u!Y2UBMu!U*ch('dm['IlwEi+t*ARwD oYH3ǗYGx"|rּJ>uՄQ:=)Ž}'kfE!:CywiglZȼVQ\CJOUIQKD #"@SSKN^ kjj&': %o]}"/$DlM>;szShֽN{V($쯏I=M7Pv>X޶Nն>˶ |) YLfVH1*j6=lɻ{{u﹓_.;t=4!AjX\F;/$ `wHw/u _W ɤI-IM6Y:椳|'%M%>3 # /8}oPtr;>DV!Aso7MDPW*КY?Rk*.~Tm})nNWʎ^)M^с~M5 YXKD Y(U ޼.Cɪ:6G "xBYbTZJD Wt[H/iO\+ݞd⑵&9>onPP&5ZAfwzQyrvsILt9!PīZ!! E"\AGߡ p3|%!{=^Fܭ'w_tY"蛐ueu {i<[c`gir$Nj{vSuWK^מڒR`X{\s63BkfL\oߴ:SL))1ŝtͰ(dunj!'fE= )l$ YXKH .5F z/"xB6UvJ|!$yzVBόAaZ$ODj6h^esH{u7 g* YeR2ev"N Glk[,adȳB3R#[HID!InʳɝhQ*PZh؏nqZr]&GZ`7R8ӕ#b :)@alrT%AnCOyt Fz"{h8j=.Hypc_Y dM-'~RqaN'+YS$,ŴOԖQZ/)).IؓY*Fzt|>2DאBTs)J?5+Y[DNl?L{['ٙRPk}ǝll4ܟU[N,QYͲj.ѠB& E-hq[yR:FƨE~E|T˱'eG4ٸRB7oa4#fh:Yiw.OS\Rke3n rM\pե}>0ҩH(.YoiBs\H*%#v!4kؓT% kA6b-BkBPw)@*c>lC_0pNh6jQC5~)9lwv0Ľsz6+tnl͊0Ku$n @7~7/_3҇[UaPX!w<gXCgyYj!%i INGUiRy PG/6wspMxo\|6p9[dRQ+md$ u]q;>R&>88j݂}OݏB^֞yPhѾ}ʇ,gmVN 1,>|BV\64lm=:Xuj goԋF ˰ݼn8KΪnz/\ YV\W$ݡX@]zD])|?4b$qfW DCr>PVbȊ|wU>٠~❛,>aҗ^|hRml/xnKpZJEed&\|XwB5F0H/YJOCNc4i[۹O|jy ΘNgu}'RuapL%ŷ47#zEz^BI;: 4P «"Bt ٜ1ȫǎ 2Q=T _w;f}tNLy ( Ѧ̬q^.%rLv킧 U^_ Ѝ~f.YWb9 N:k]jA3q(ayF}'/ݲv9*k>PnQ7$ ?GuY_7PYnԨݤl6[q$SINITi ?+!GNwڅ\N#r9UoE hB/rKDb{ l-zGHRf-A9/Of*3@1hToB ~FLR;?3`[Ó"*Wl'V#se;M讧~ŵ$g纡ȒP섂h@Uue mF]uQrqT|)aM6L`ڡZz(F6+pl& /2Mw4*j22SyE+R2Z>i4/~r\ª#e4e]MկSӊRy{aSTs/"'%~Qz N%"Il7dq³. eL`TnFW3!_ jk{/ܽkDʪǴ|QSؗۍ^ %11V_Xcuutut>_X>e>~rS N/򥚟v.c=SC왳y^y$i:SN&^c~ﴂVrPѕ0[J&\~ [56cO:妽=pQ\݇Gc`uMGp*UMi󞭜kWH)O{OH7|_߸ؐ6 U ;0a7۟#ƒ'xۯ눂ظoaLy8dyٗЗg*ܘ=|SAc-K鼨ckB:o-*&uG#y(ϥz{"4k4K"].:({#ԡeAivZlh{pXg!U8gN;>?t\c>GF}E'c0Yd;͜,sNEv^svu:ۻP_vvur$ X7BG~̪ȂL{lC6 4ҳq);Ų2,7bbKj(;hLb}t[gU;j/ ?$jEƘ봶|TAwevX0欷_{T1PԲ'tK?>Ck=W ޹ں~&`@J nGXpo7Y 2m}&M ]ҷ</EϿJ,dvk?6; \gQapG;8$5销m ]xvoY^ wch;UG?rC2Ysv?5yIXS(ׯP%mR gUD gsߞ5iϧ^,~y'w?<ա$ nsód2峿CngGiP@}kEx9EBkvW_~S&<|cOyC>v|Q'ŞtKeoҹt(w*3t}ՂCs@%;73e~Cdž,佘wS˷閿Ysmֳ}Y=>Z %_nzLvv#O {*Bxq-7tʰn~7SgFcg?hc韩m]vղ>E}|{EA.".z;ǓW!]YNU OU' HmݳLѫvL9ͤ[ L~xv'`1o~a\^=}ܭHͻ Ec?NQıcOOINfFht?@c֗nۓx5[ݷ/w0c~L78)|/&:a%D5Mu;o6KQ֫M:ʑrZez+Cp}4ifui@߻%yFciޏ2Ѝh=TC\奵:l}mR.3'_1yWySzK}>h;)ɱLw5;sx%z /fI!bS Tk5).S׻]wueJ+0 R,nim8 BVUs="ݭCIewg&Gq0(z?7)+j n"㭯Cl[̊~DzN˟|+W(n1>7nzvrJ)P;nx?(͆k/ ॗ]RS2}}Rͱ--?7as?;K^?r23:t,NS֕zOs׋~]Cv$.Ӈ U 5k 4535M))kEf zj0klPZ1"%ET({ ꨒ{-/U,=Hjw;-5/HJn=2׮UT>k 2\FHpbN/f(?]~8).O`AoSxW'xo\R0mbCXr=7E E?!'mlѶT>G_(K- K]".v2ES-?B))k(p0*a2OSEcZ]`\ /MqtǛ̺04 ]4TܶMf166L 2&8vBMST,5;V<! z:~g0o6_Y#M !b;[&fdm(aB+N}CjĻ[hjhn ,jMAl 7$IGnO%/oyue鐎T5t: Y|%.籬Y 2Mj]!7"YBMR;iDm?fǝX󡷾Y3ηsӡ;5^A=J! h-/&J}YV iy#3/O=Gߗi9pLyh_ͱ7SWP7;lXA~ djw; 8hj*%̋95j[ ?dNZFom?8cbaꏅ6]|.l՗}HMUCU^6ވ, |>kh0=9q*riHS$/# CFZwKMK:U8_s?t_@D/nD{C>_dI7pnQiɩA'nnUt?zۘooZ?J41+w+~Pyن]. ,=:oXunJl+KvTٷEcWC*Q=T t2:nٝVvU(sKE\4;ܞJ-oyj߶Lgt}Mqg՗nѪP PS:Q3HMUC/K/:jz\ ?jCyҺ˺υeR']3JOCMUaΩ`@.-_A#heM]|ad[=Ei]͘ǷɉkF25m'gJPuXUy-}CdU9YNɢ{5'$E=*Lz'}Ts+Fym<'{s<1V^nasKrS_ s~N}ÓVUP(a"+J3\MQ&q…Q?ޜLl}#&Hy|;LM9)}?|-e縥_v)sjg&F3z"cƙJLŷ4W;j} 3cIJ$T*D{]-D*-~SJ[VJZhHەHQ03?}ƕ+̙yΗ  ?MZޥ*Oo 5a/$ SNJ66V1.#< 7/ .(t=h7rڝ5b=,1]c22Gk, e >7<2,ї*Byg"jO4Ȳst;b8y-\46}%ՓJ츇v0;7Zyuô?9w2K"j < mҫv*b/&\vL7׋nIJLKꟂae TƾƩEY_ST 7!`ybVP䥈@- VEM[h&NFbfbn9ƿoEj_q:j_Id>o{Lq_l&/`,حMb㖾=#~Ɖ}zixuYf*ϭj}؎& ?݆9 CY=2@}m Iy,c^v09LyF@=%\ܙ3/!NɍV鸇vd((Xl0n%ep/+ק`dƝ].°6tSYS7Ykߎ{M]vL ˻QB\` ZoB7p"|n`i_ki!+Yv9ٟ5I)~eEm謓aG,͟Ke٤y4hiH Z2O|l {6_G ;lŬ٣/՜NHYr=s*χZT޷}ϼ=pgjlR}aj;هvEٚjŰ9, Mrfv|?wb'2 .5^O?jJ[.Qoz yyOo4&p/n.NyMS7gMX޵Ĵlt2~jrmSמ2KAߐ )/I$BMҘiwQ{4HPk/#GYvU|r꾺_jMXsgK- H|rjRu@$;7vgfC$/ ˻>uz4ٝ.aFݎ`%O ]~'<27̦)~N5;Urޖ Gl)Z87ij!W _Y捩 ˻9{l Ů*N {%ނ[!r<CmR3K23zr*+s^8˴Ѥ룠v2ڟ Xu~(YkI;60 ke.0TU7uIOvj;)Ӈ1mKHᆾ䎝o"<5-N2RWVڔ2SJ+ReI%ō߸8aZJ*M<=پR*OXd[ԜN0r]c]ўCP$IbВyb,i~8|m+OE5.Y~bm7F" b~:]Hl^ߐEyMSC!BG"BvZɪ N WePQ j'S!tZIA|~ ~&]JVFm>|gtUwL{a{/k|#lG06?C'?HWşvt*86!fף ܶF8:1!.mnƷnSp')Ȭ+2j2'ȽCRM+ɵ+ʑxSUDCbOLmGRo>QNB+7=z_P~r1//l4lE(d}}tx2 C55^h]Mo?38Uw5CfEP]9RF7| h'v[dcFW6Q/W3LMZDn6u,%EYs3{F0-gS&R)"Cn! P5"_w+5)I>rVA$CmìNQ $NwϽ%a]!%\%lAQ"Y[ m䗪Kg"ET2sbܕctq|b6fe<1br+E Ej&,a}KRhYB3Y2F&d<3`H(z o깰ϱ&ї?֮Rsysg1"- vt<^2}6 ߻}HH(^l9@CRW_*~MQlxp+)tr+b(IG}UX@֝hK]r/k]>L?obiJKGJ~X 'Vz1m 7/ .(t=E4&sr:Tmǽ.3ǟy)q{]c6~_׈Յ:_D^̻#gў{MLuSV^iY$+ &+t=C켞<]k$󴗎|a)Mlv[7>yԿSɨ}cbT60>XU0o^SGJ7o&O2!3iQyԬ6#4="KD"eװԞ7V85qc? <@3X x@ҫ=ƌH%0KKw{܍MSzFG+!1F+s%*arȻXwf:G'&Ds.omG[wd ^ǟ>~=Q}jI!H^Ƚz:홫\fR4ϖ%&׳hUIy]]fyIX5={~g`$BG ҈&0>ʉ˫m!RU4NLMK^aqߝ;\FR2^+7:;$ݸrQTy\$^?3e leUW>ui4>+!O0fHhDTϿϫ ?u:6> Ϟ\?%լܘc, 2w^ͷhE/BJC \PY6MO=`JRòέ.}-AZGQiT o}**xڀzb*rBKYҴ` rʂqD ~ MK}*zzZ}W$B# SmG߾6Taqݩ[φVV憝˴vh҅d7]]ǟgg=?+Xi È8~[1dgȹtb@LwO6+ኩwkԠyv)`mcI9?l(%d MtPpqUWصz}V g5TJzIjڹYC!WdӭN\tbS5sY@t8H}zl}W$B3I \8Glx6v^ 5ָ-in4hXcr&.~B7*f怃0DxAa)iDyf7obϳ?D~TE]7cSC!ԍXʵ|)/?oT;tB5+Y~bm7{7[4Id˾jDbC]dJz>馩!Bo)!BVRTJ{NB{fӷ!]$#A{J B!ϡBC̶FUuԾF@*Ze9H ֦QhFy;m7CU)Yr./U̽J;;a룂'p L8s#jBGkvԾ)ܬ ҭUgX}IOxox#eRk'M~zB~cyc!lŤؑxўp?BtQ(4lˌ[2LFU{eQj<*UFt=S}I*o65U(5V֣L 4 M_qK\w{9]FDwȌM4O R}`sCIX){Hp1oܰY!PgPS/wiIěす}verr}w 7TΞ.l eO1SO!5fuw<|5 r +wʄ'SF+oyMyf *q^V1.#< 7/ nw-G!:KGpW';LSz'C*b]ɓv/j"mea$~=8F,Ȕ)6Q O+8mKjQTU5%=O^Ouޡ p~+w9vinE^trz `p۾j%.ZTkb#^G3p[0}d5},VCg9Bb:Z^g[GJNvOzܢ[I}܋*CEY)qÔjG(*aǻjG ̪ag=<3qj&>dmj0nݯ, +w9<hw _nFz}/kq<^<ڬs2vZ[E#j4خΙ?n忼[tx#UUAIPp.&Fxw+Vyd)q9+Snm5Ro5%-3rry?vR L]0(\@\|OBkp@7CS_~i[M]ˉidyn3>\3^#SId7-G5P{&\&S)0қ/﮸IX|>ؐ3M3GrtA(Ɍ!7J@61OxneenؙM'S*ɡڣ'Ws^rM>V &4M4dC+kB„ ߕnSg+o0X┰YWV/m GcY7\fTyb\^P76#ގ&4MxWPv_T[vCܟ- ^=~Y{#04G^l'@jX8Y峓2M $E)m\4/t~(YkI;e^3d2g'Y\`atԔC\%L]pC_r7jٖyg+[mJp`)E&=նiJh3.-j0yo%K?ݏ~b~0" 1{w$Ԥ3v:x˔hm/g}_ KHO m׺ j|F2UgxGy`G3f[nb.}4Wbzj%+|-vз65s e!VETnd@PFpA S"u ~1t^uGE &{sM 7cϭ{C&Ia=f=\|kG1s=޾""1nc6m~<ݯ+W1΂FVX CC@iv) H qv=}5bV=9 6ϿD'2sfuzsOJI X N%EyMSC!3`gIB)/3dɍ[N?' +f~JfaJKJBL5H]i=](Ah@X*)[F",>7Ykߎ{M]vLj%v޳ssMW "Ɠ9ǟm0ʿ,G3Bho/?x^cmuvlYY@GV߽,bh|܌GJp>gU4V#ou:Tαj{M6ي1m5y7}Llܒo"?"p9|UrsZɪ N W1CB}',VCaEEETGk2ƷO#?݆9 CY=2"+NLNUy5AL WfugD4jf=oP5Ϯ5ݽ䡓E%`Ϗ<4nw UKC6oz;tMr7A3_rJ<[]) 5!Z,mӋrn, /GnV+x1o!3NA&ml(z6n9WUt[~-f >`liQjSm<(i[ox1]&v}?6. @6Z8ZHӭf_oev -9R]7U\H^7TCYYncl{ 5BzTNIM5%K6yc^~I$V !Ԭd=/hH$C/S |]:sMnw#e럖3D*bϞ˚}?*>sx1 2)[Դ'er?ꇺleEY˙B>Ǽ<@Z[N1d=2[p'+᧌-$թ{+ʑxS8J݇ĞdP+[;+ yƄxzFzF.E$볧ϗVu?~ʮꯏ;ZjF.=Sq爡[#;dVDz Qj젝,jjtqGgeO .yP֝8{_$:g(KlwYJm5#)fjEg`Hr~=Z_֭L}\ױ&/pWMomìNQ jۉ~PKeoRnI[r˖5+oIK~t&aYYKU,3']Y=v]N7'f2FlZnH(Z-d?05p#:Lׄ윐GyF )]M=96$gބ[1G7Z)"`*mG\.#!ahCj+n߇ĨŖMHG Nuħhm8<+JVlp Ãrԭֶ_qJ^Ξs`n+J?obiJKGJ~τƭO8q=< co^$\PzQi./Lu\{]g?S2}dl׈>o9=sg&~Y۪akJ*)瀔pKEĄzF5pSD&п**, M4psK#~RhGHJާ/+'.KU#815-zV .)~wrIn r{Ozu;MWV1QYexDe88}ysLj֧.1]#סy\p%H-~z4˽F(4]IP_zdVX'k.uKxa|4qd%q@WI&f,9coQk%t# nVpot۝}/RnF7զ|Շ~^S+'8o>kC5AKrNfǮe볪$g`=Ю?Դs?L eEٛ}^l[urZ}^T$Ib9b#dz%L<|lZj&;ѴpbXcr&.~B5T}o6Ey[UE]bBn5!_ 7MQߐE =tB!зGi"BvZIQ126 BW01 B!P7<B=PNvro];jm4ȔJa(By*U{DUTT!Tj;I %B4ccjhSzLur&SBP!԰%!B I))%%圬vnB46G"F c(B! CIBtО@!z:F@, *--TQhS (Ш 4%A6}Ŋq2)8/m7!N?#{p;QSB𮙜/'dw;rCIB? Ro\nq?zy0AHDFF}sPFm',Y8]|<\fTyb\`:h9, uѪb3nA*Д'{e`:hFO^Fհp3EmYJYYN3+Ш 6F2jOG[~1U*ο|Tdx=92Ip~$0#Сe7Y)|v~@(u)$B!,YǸrtq%ˌLRPN\@M k`~Z#~Osjp;fΘy~bNoR Ֆ^va! ZqAkb.}4Wbzj%+|Ym<*y>%#.k@P>;299مCsbt oRY-[%y9_CXO?2`“gb*/xٸI <3da %B$D4f6F"y~൉Ҋ,a;3/ fF7Xo/fve^Ecfn]Ńc+Z&eQZ}$d__W?ޏ6nW3% Y~nΫ6(**W):u6 ˷ .{݇)KBt$Trq՜]TXxkU}#v켿]̟#g%^E5UY}]$uXN`(BgA=j|zɝ%]׎M])EikHH9% å_0dgѥay>_T:fkHQhT~2sKWvT߾J LZ+nJَf6V~Jf0feeuY%u$E< KIׯ.?U:z;0nِ=5^>AQ:uk3c{]|P!O nD[{:7㛟dFNV@&o4/=Hcuٱe >jªc 2$7V߽,bh|܌GJp"ؕuM5ټ1% t!Q/k?޴-[Mp,^rnVvw "&rRٰ[ydUHuRKWRG OCONZ %BD]妃05\VIګfo[,Dm|ckgmDMXR\ TXNeeg3YSU^M%S$•|7(kN7`L7}::xu=kG+l^q(S~e WHX?99ԜZ{jVyN=^~c,}JVuqBQo[hƂ/O[I#YY,HH]-noL,-3JMQ~J bqm0]%m 5K-t$e.o%ζ;=H]YjSʄ[L) fyq:j!~L%LL|c5%5{=>K: 2#ْ0*('Vw\(IzTNIM5%K6ySI먉Bźw_NVKPO?>oXd= $2pest5"Xk%B!P;a(B! CIB!NJ"i++j˭l1D!Iڢ$"c(Bы/,ǺIZr ߿ow"DGB= ]CSH sVP$$mw J"Tt 7lF!B턡$B!j' %B!P;a(B! CIB!N8!P}8\.@ηZ[.$$ 2h.kJ"irrrކ+hhj/B|y5x9hCIB=ؘ** B(g2%(ؘ %B!N [IIYRJ IJI)))de;vB԰>6P85vCIB!NJ"BvP! 02V^6HFUPҷv=n4B)f'Pmþ2;L_@So —YhѨ *3vؽ&U33=Mo{_ln@8x/֝' 4o8_e@*گ]iq;!ϧ,_ѯ$~I|wM",K[&fܶj<;svOד 4+}|HKOxkɷes^l/'~پk[;_~!?Ŀ>X "![ݠq X+O^=)FٛtAKךK|y2^mOѷx3@> F;!POtrzYUW%&iuEv 6NyT G[hT]g#l@Hݒ:a)קNY{5*ӵ'G&1ޫ:Ӱh[T]{Qe}HS),3|'M~vXYCY,`_|&3:Qs]õ re p> բQlѢZu)Q)ړ |bXF ad6@WSjv7:)4|PnwMJ1qsx5vHEۼ`NN<jrW%ְ2Imt7\.#R KFp4-ys>Tq b ui4`r:P2?:-͉.*k O&Zфy ӆORLyTۺ^H@ҍlʨ;\>6ۇg)JR;P!qGlΟЉN EY_S*UfQʫlۏc񦿗Rmyk'J,͹6MBN*Z[U^jήG_[[.?i@ꑝ7b ?y0Cџ8 ?(vywCٌ'F IeL0"fjSgNSK{, S5V 6'cP+Rs1uAT2fs:EF0 *i A3Q&^_V e梻3/zV%o_|j4U\ $v3'˛^.rTN5CIB{mGfx}cMw: K_ܬ#N \7fhVB J2h⏇a9i1Vt ,N8zfFѡz*tㅏ{[KAhfojptJ=Hld?G_!1փ>Nު-T^=!CaxCLJ҆|6#\QM Rh'mt$JF^Qִ-$eJaB./fֶP7MsTߌ{9uDkrCehlY)$Dr8Mk%1.]jxi2ʔ[4eȾBjFuu$BWhA%mɌ-J:ly^xWTcd0EV$/kd=wfXt2 @i?Wp ݚ$ߵ;1nJu.T3N~X+C$!vC\mOP6r,][ 1Ӵb8o\ HP ۔l9.27ɔڮҪԿ,ٞ1n=U4sGsY%IOڟehظ}{;me[H61OxneenؙM'B&dl[|IbIuUqR [_}+ CIBXgŽuI8v?^X0[g@@٫ݻBzTWs9 1peYFVR&bZJA}%!9,1XK&KI>1eW%$)FG{PW8)z] Q^:J$#j+'($7b 7ֈf5o,HPoTMkБ!6 Rۯ0~~s- a0waω#^y=2zuz/j&X&rڗ!#D!$02Kz~f:]![{PK^VX'Hj[sf I?-)J^!z<e.n鿿 vqW=)x\B2ޭC)xBp’4I(+㍶aMJ^m 㛟dFN\F?) %Bt↳&>q,0b1=6av?{/.U[@aKs!j _gE*WNm&Ϗ2c8 uaRTuf4рn˃MThTu|n2yNDUP?yˣ,V3O?V.u!evm'ÛNK_2pQy!z>}j=`2c/uu+Lqϸm{[UW=iG7d8zwL/GiU(vj'瓵6JRkOz?,xswb 9cם<6@-x~wtyv>mOב^WG*QFi9KzHZBT~}3.~HKz}tpߧ҆vJ{^4.9˖\X,NӃhet 4*vၠmHx4ъVW,L>I1iRdOe1>+1=5C>,`r:P2?:-͉.mg.ORR4*憧WӉ$B!:QE@n73y굃' H=FlaUU;FTr4rވQboVCI+zlMHys]d nV,I6hu#(7v5S 8J} *L3uݹTv.MNJTFh ˈTNς$B!:+W("OQth xV}Nr-Xl r9xbgݺaMB?iɵ2(NM㨎@GIffm#$Dr8\RU謆 !P`Xnʵ|G t09f쾪cX;3=3yzk4Id˾jDbCT!BA٫=;Gf1YNoeZM$B! 7V\ftBu)xd-;|OzV!POS]Yr1j .[]Yc(B!HEEQaIDoP!PO^XuED$B)$L|YVP+(zT; %B@t UB6p#BvP!B!B0D!B턡$B!j' %B!P;a(B! CIB!NJ Bu.+$w(:-(*,,F!% P'4~!/,o~!:-ϋ~VLwC! oxxeebAE!H*ޡꄂ6 &&.&B;B]Gp#BvP!B!B0D!B<ͦ)*a BZJٙY-g/o2[HHB!z~,p!7' 3!B'GWq# ,$=jyyyDQh w!B?̷o^TW(7uy(pe9eeL;BE The file: /proc/self/mountinfo must also be present. It was introduced in linux kernel version 2.6.26. NOTE: If you compile a new version of lvm and devmapper, libparted also links to libdevmapper and may need to recompiled. I have been using parted v2.3 but v3.0 should also work now. To compile the source just "cd" into the top directory, the one with "CHANGELOG," and type "cmake ." with the space and period after it. Next run "make" and "make install" if all goes well you will have a working executable. Translation files are in the "po" subdirectory. The icons in icons/local are from Silk: Silk icon set 1.3 _________________________________________ Mark James http://www.famfamfam.com/lab/icons/silk/ _________________________________________ This work is licensed under a Creative Commons Attribution 2.5 License[1]. [ http://creativecommons.org/licenses/by/2.5/ ] This means you may use it for any purpose, and make any changes you like. All I ask is that you include a link back to this page in your credits. Are you using this icon set? Send me an email (including a link or picture if available) to mjames@gmail.com Any other questions about this icon set please contact mjames@gmail.com [1] Also CCA license 3.0 on web sitekvpm-0.8.6/CHANGELOG0000644000175000017500000001466511733530416014157 0ustar benscottbenscottversion 0.8.6 Fixed immediate crash when group is clustered. Fixed configuration dialog buttons to work more as a user would expect. /etc/mtab permissions don't get mangled now. version 0.8.5 It is now possible to cancel a running mkfs process and some other long running process should follow soon. Sizes may now be specified in traditional powers of two (1KiB = 1024) or in powers of ten (1KB = 1000) SI units. version 0.8.4 Added to, and improved, the configuration dialog. Property panels can now be configured. Changed the detection of mounted filesystems to use /proc/self/mountinfo rather than the depracated /etc/mtab file. Fixed a bug that deleted symlinked /etc/mtab and replaced it with a real file. version 0.8.3 Added new configuration options and made showing some panels optional. Improved the appearance of the properties panels and made them look more consistant with the overall look. version 0.8.2 Added a splash screen and moved the progressbar to the mainwindow menubar. Improved the dialog for adding a mirror leg or changing a mirror log. version 0.8.1 Improved and added to the kvpm config options. version 0.8.0 Added icons for most menus. Added several more for the Tree lists of physical volumes, logical volumes and devices to show mount state, active status and missing volumes in partial groups. Improved cmake commands for "make install." version 0.7.8 Added a menu option for running 'fsck -fp' on a filesystem. Removed call to ped_file_system_clobber() which is no longer supported by libparted. version 0.7.7 Changed lvcreate to allow the extention of snapshot origings by shutting them down then extending the lv, reactivating and extending the filesystem. version 0.7.6 Added more selections to the mkfs dialog such a setting inode size and many other advanced options. The lv size chart graphic now is presented in the same order as the volumes appear in the volume group tree even when the tree sort order is changed. Added a new column in the pvtree showing the use/ignoring of metadata areas in a pv. pvchange can set the use or ignoring of metadata and generate a new pv uuid. There is now a dialog for merging snapshots. version 0.7.5 Re-worked and debugged add partition dialog. Changed the code for selecting the size to a separate object that contains a line edit, combo box and slider(replacing the spin box). Aligned the device tree view to right alignment with centered headers like the vgtree. Added support for basic usage of ntfs file systems. version 0.7.4 Added a menu option for removing the filesystem from a logical volume just as it was already possible to do with a partition. version 0.7.3 Added a small docbook handbook and some icons. More bugfixes. The filesystem operations menu now has a dialog for extending a filesystem to fill its underlying volume. version 0.7.2 Snapshots now are nested in the tree like mirror legs. Compressed all the seperate vgchange dialogs into one. Changed the volume group tree widget to keep the same trees expanded or collapsed when the widget gets updated. Added a column to show the metadata areas on the physical volumes and their size. version 0.7.1 Replaced missing include in "lvrename.h" and fixed the "remaining" column in the first tab. It was reporting blocks and not bytes. version 0.7.0 Many internal fixes. LVM now supports resizing mirrors so that was added along with mirrored mirror logs and multi segment mirrors. library lvm2app is now used and required. Btrfs is supported along with volume group splitting and merging. version 0.6.2 Shrinking and growing physical volumes is now supported. Filesystems can be removed from partitions now. Removed the menu item for rescanning only one volume group. It was causing crashes but may be added back when I have time to do it right. version 0.6.1 The filesystem grow and shrink parts of PartMoveResize have been moved out of that object and put into their own files. lv reduce now looks at the minimum shrink size for a filesystem. A simple setup function was added to set the kvpm configuration to something sensible the first time kvpm is run. version 0.6.0 Partitions can now be moved and resized for ext2 and ext3 filesystems. The "fsck" program is now required too. version 0.5.5 The kvpm "settings" dialog can set the column hidden/show for both of the tree views and save it in the config file. version 0.5.4 The volume group creation dialog can be called from the "volume group" menu now and create new groups from multiple physical volumes. version 0.5.3 The settings menu is partly implemented. The search path for the needed programs such as "vgs" can be configured. version 0.5.2 mount points are on the main tree view now. New and blingy "create partition" graphic like "gparted" has to show the size of a partition about to be created along with the free space around it. version 0.5.1 The panels get a more finished look and small areas (under 1 MiB) of free space are not shown anymore. version 0.5.0 Can create a new partition table. Better support for GPT disk labels and a visual make over of the device tab. Needs cmake version 2.6 or better. Adds and removes partitions from partition table. version 0.4.6 Added calls to i18n() for support of translation to other languages. Some bug fixes and work to the mount and unmount operations to allow for overlayed mounting. Help menu for bug reporting improved. version 0.4.5 Added new dialogs to import and export volumes groups. version 0.4.4 The lvproperties widgets have a little bit diffent look now. Small bug fixes. Lvcreate now has a name validator. version 0.4.3 Added more information the the pvproperties widgets. They now use a QTableWidget to show the extents used by each logical volume. Added an new function, lvrename, which does just that. version 0.4.2 Fixed a bug that caused random crashes after mounting/unmounting and certain other operations. The mirror corelog option of lvcreate should work now. version 0.4.1 Still doing coding style unification and hunting down small bugs. Extending non-linear volumes should work better now. version 0.4.0 More additions for handling mirrors. The "properties" widgets improved again and background colors made to match. Many bug fixes. version .3.3 Lots of bug fixes. Volume group information display changed again, much nicer now. version .3.1 Changes to the handling of mirrors, still not done. Copyright notice debugged. Many coding style improvements and bugs fixed. Volume group information display changed but still needs improvement. kvpm-0.8.6/CMakeLists.txt0000644000175000017500000000115011733530416015466 0ustar benscottbenscottcmake_minimum_required(VERSION 2.6) project (kvpm) SET(CMAKE_INSTALL_PREFIX "/usr") find_package(KDE4 REQUIRED) include_directories( ${KDE4_INCLUDES} ${QT4_INCLUDES} ) add_subdirectory( kvpm ) add_subdirectory( docbook ) add_subdirectory( images ) add_subdirectory( icons/local ) add_subdirectory( icons/app ) set_target_properties(kvpm PROPERTIES COMPILE_FLAGS "-std=gnu++98") IF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE "RelWithDebInfo") ENDIF(NOT CMAKE_BUILD_TYPE) install(FILES kvpm.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) install(FILES "kvpm.1" DESTINATION ${MAN_INSTALL_DIR}/man1 COMPONENT doc)kvpm-0.8.6/kvpm/0000755000175000017500000000000011733530416013706 5ustar benscottbenscottkvpm-0.8.6/kvpm/removemirrorleg.cpp0000644000175000017500000000312311733530416017631 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "removemirrorleg.h" #include #include #include #include "logvol.h" #include "processprogress.h" #include "volgroup.h" bool remove_mirror_leg(LogVol *mirrorLeg) { QStringList args; LogVol *parent_mirror = mirrorLeg->getParent(); while( parent_mirror->getParent() != NULL ){ if( parent_mirror->getParent()->isSnapContainer() ) break; else parent_mirror = parent_mirror->getParent(); } QStringList pvs_to_remove = mirrorLeg->getPvNamesAll(); const QString warning = i18n("Remove mirror leg: %1 ?", mirrorLeg->getName()); if(KMessageBox::warningYesNo(NULL, warning, QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QString(), KMessageBox::Dangerous) == KMessageBox::Yes){ args << "lvconvert" << "--mirrors" << QString("-1") << parent_mirror->getFullName() << pvs_to_remove; ProcessProgress remove(args); return true; } else{ return false; } } kvpm-0.8.6/kvpm/lvpropertiesstack.h0000644000175000017500000000175211733530416017650 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef LVPROPERTIESSTACK_H #define LVPROPERTIESSTACK_H #include #include #include #include #include #include class VolGroup; class LVPropertiesStack : public QFrame { Q_OBJECT VolGroup *m_vg; QStackedWidget *m_stack_widget; QList m_lv_stack_list; QScrollArea *m_vscroll; QLabel *m_lv_label; public: explicit LVPropertiesStack(VolGroup *Group, QWidget *parent = 0); void loadData(); public slots: void changeLVStackIndex(QTreeWidgetItem *item, QTreeWidgetItem*); }; #endif kvpm-0.8.6/kvpm/lvpropertiesstack.cpp0000644000175000017500000001071011733530416020175 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "lvpropertiesstack.h" #include #include #include "lvproperties.h" #include "logvol.h" #include "volgroup.h" /* Each logical volume gets a stack of widgets. The widgets display information about the volume highlighted on the tree widget. One widget for each line (segment) on the tree. The stacks are in turn loaded onto a stack for the whole group. */ LVPropertiesStack::LVPropertiesStack(VolGroup *Group, QWidget *parent) : QFrame(parent), m_vg(Group) { m_vscroll = new QScrollArea; m_stack_widget = NULL; QVBoxLayout *const vlayout = new QVBoxLayout(); QHBoxLayout *const hlayout = new QHBoxLayout(); vlayout->setMargin(0); vlayout->setSpacing(0); m_lv_label = new QLabel; m_lv_label->setAlignment(Qt::AlignCenter); vlayout->addSpacing(2); vlayout->addWidget(m_lv_label); vlayout->addSpacing(2); vlayout->addWidget(m_vscroll); vlayout->addLayout(hlayout); setLayout(vlayout); m_vscroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_vscroll->setBackgroundRole(QPalette::Base); m_vscroll->setAutoFillBackground(true); m_vscroll->verticalScrollBar()->setBackgroundRole(QPalette::Window); m_vscroll->verticalScrollBar()->setAutoFillBackground(true); } /* If *item points to a logical volume we set the widget stack to the widget with that volume's information. Else we set the stack widget index to -1, nothing */ void LVPropertiesStack::changeLVStackIndex(QTreeWidgetItem *item, QTreeWidgetItem*) { QString lv_name; const QList members = m_vg->getLogicalVolumesFlat(); if(item && ( members.size() == m_lv_stack_list.size() ) ){ // These *should* be equal const QString lv_uuid = QVariant(item->data(2, Qt::UserRole)).toString(); for(int x = members.size() - 1; x >= 0; x--){ if(lv_uuid == ( members[x])->getUuid() ){ m_stack_widget->setCurrentIndex(x); const int segment = QVariant(item->data(1, Qt::UserRole)).toInt(); if(segment == -1){ m_lv_stack_list[x]->setCurrentIndex(0); lv_name = item->data(0, Qt::UserRole).toString(); } else{ m_lv_stack_list[x]->setCurrentIndex(segment + 1); if( item->data(3, Qt::UserRole).toString() == "segment") lv_name = QString("%1 (%2 %3)").arg( item->data(0, Qt::UserRole).toString() ).arg( i18n("segment") ).arg(segment); else lv_name = item->data(0, Qt::UserRole).toString(); } if( item->data(4, Qt::UserRole).toBool() ) m_lv_label->setText( "" + lv_name + QString(" + %1").arg( i18n("Snapshots") ) + "" ); else m_lv_label->setText( "" + lv_name + "" ); } } } else{ m_stack_widget->setCurrentIndex(-1); m_lv_label->setText(""); } } void LVPropertiesStack::loadData() { const QList members = m_vg->getLogicalVolumesFlat(); delete m_vscroll->takeWidget(); m_stack_widget = new QStackedWidget; m_lv_stack_list.clear(); for(int x = 0; x < members.size(); x++){ QStackedWidget *const segment_properties_stack = new QStackedWidget(); if(members[x]->getSegmentCount() > 1){ segment_properties_stack->addWidget(new LVProperties(members[x], -1)); for(int segment = 0; segment < members[x]->getSegmentCount(); segment++) segment_properties_stack->addWidget(new LVProperties(members[x], segment)); } else segment_properties_stack->addWidget(new LVProperties(members[x], 0)); m_lv_stack_list.append(segment_properties_stack); m_stack_widget->addWidget(segment_properties_stack); } if( members.size() ) m_stack_widget->setCurrentIndex(0); m_vscroll->setWidget(m_stack_widget); m_vscroll->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); m_vscroll->setWidgetResizable(true); m_vscroll->setFrameShape(QFrame::NoFrame); const QSize min_size_hint = minimumSizeHint(); setMinimumWidth( min_size_hint.width() + 18 ); } kvpm-0.8.6/kvpm/processprogress.h0000644000175000017500000000221011733530416017315 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PROCESSPROGRESS_H #define PROCESSPROGRESS_H #include #include #include #include class KProcess; class KProgressDialog; class ProcessProgress : public QObject { Q_OBJECT QEventLoop *m_loop; QStringList m_output_all, m_output_no_error; KProcess *m_process; KProgressDialog *m_progress_dialog; int m_exit_code; public: ProcessProgress(QStringList arguments, const bool allowCancel = false, QObject *parent = NULL); QStringList programOutput(); QStringList programOutputAll(); int exitCode(); private slots: void cleanup(const int code, const QProcess::ExitStatus status); void cancelProcess(); void readStandardOut(); void readStandardError(); }; #endif kvpm-0.8.6/kvpm/vginfolabels.h0000644000175000017500000000113011733530416016525 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGINFOLABELS_H #define VGINFOLABELS_H #include class VolGroup; class VGInfoLabels : public QFrame { public: explicit VGInfoLabels(VolGroup *const group, QWidget *parent = 0); }; #endif kvpm-0.8.6/kvpm/vgmerge.h0000644000175000017500000000143311733530416015514 0ustar benscottbenscott/* * * * Copyright (C) 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGMERGE_H #define VGMERGE_H #include #include #include class VolGroup; bool merge_vg(VolGroup *volumeGroup); class VGMergeDialog : public KDialog { Q_OBJECT VolGroup *m_vg; KComboBox *m_target_combo; QCheckBox *m_autobackup; public: explicit VGMergeDialog(VolGroup *volumeGroup, QWidget *parent = 0); QStringList arguments(); }; #endif kvpm-0.8.6/kvpm/misc.cpp0000644000175000017500000000556611733530416015361 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "misc.h" #include #include #include #include #include #include NoMungeCheck::NoMungeCheck(const QString text, QWidget *parent) : QCheckBox(text, parent) { m_unmunged_text = text; } QString NoMungeCheck::getAlternateText() { return m_alternate_text; } QStringList NoMungeCheck::getAlternateTextList() { return m_alternate_text_list; } QString NoMungeCheck::getUnmungedText() { return m_unmunged_text; } void NoMungeCheck::setAlternateText(QString alternateText) { m_alternate_text = alternateText; } void NoMungeCheck::setAlternateTextList(QStringList alternateTextList) { m_alternate_text_list = alternateTextList; } void NoMungeCheck::setData(QVariant data) { m_data = data; } QVariant NoMungeCheck::getData() { return m_data; } NoMungeRadioButton::NoMungeRadioButton(const QString text, QWidget *parent) : QRadioButton(text, parent) { m_unmunged_text = text; } QString NoMungeRadioButton::getAlternateText() { return m_alternate_text; } QString NoMungeRadioButton::getUnmungedText() { return m_unmunged_text; } void NoMungeRadioButton::setAlternateText(QString alternateText) { m_alternate_text = alternateText; } void NoMungeRadioButton::setData(QVariant data) { m_data = data; } QVariant NoMungeRadioButton::getData() { return m_data; } QStringList splitUuid(QString const uuid) // Turns a one line uuid into two shorter lines for veiwing { int split_index = 0; if( uuid.count('-') < 4 ){ return QStringList(uuid) << ""; } else if( uuid.count('-') < 5 ){ for(int x = 0; x < 4; x++) split_index = uuid.indexOf('-', split_index + 1); } else{ for(int x = 0; x < 5; x++) split_index = uuid.indexOf('-', split_index + 1); } if( split_index <= 0) split_index = uuid.size() - 1; QString const uuid_start = uuid.left(split_index + 1); QString const uuid_end = uuid.right((uuid.size() - split_index) - 1); return QStringList(uuid_start) << uuid_end; } // Checks to see if a device is busy on linux 2.6+ bool isBusy(const QString device) { struct stat st_buf; int fd; QByteArray dev_qba = device.toLocal8Bit(); if (stat(dev_qba.data(), &st_buf) != 0) return false; fd = open(dev_qba.data(), O_RDONLY | O_EXCL); if (fd < 0) { if (errno == EBUSY) return true; } else close(fd); return false; } kvpm-0.8.6/kvpm/vgreduce.cpp0000644000175000017500000000711711733530416016224 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vgreduce.h" #include #include #include #include #include "masterlist.h" #include "misc.h" #include "physvol.h" #include "pvgroupbox.h" #include "volgroup.h" bool reduce_vg(VolGroup *const volumeGroup) { VGReduceDialog dialog(volumeGroup); dialog.exec(); if(dialog.result() == QDialog::Accepted) return true; else return false; } VGReduceDialog::VGReduceDialog(VolGroup *const volumeGroup, QWidget *parent) : KDialog(parent), m_vg(volumeGroup) { setWindowTitle( i18n("Reduce Volume Group") ); QWidget *dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *layout = new QVBoxLayout; dialog_body->setLayout(layout); m_unremovable_pvs_present = false; QString vg_name = m_vg->getName(); QList member_pvs = m_vg->getPhysicalVolumes(); int pv_count = m_vg->getPvCount(); m_unremovable_pvs_present = false; for(int x = pv_count - 1; x >= 0; x--){ if( member_pvs[x]->getSize() - member_pvs[x]->getRemaining() ){ // only unused pvs can be removed member_pvs.removeAt(x); m_unremovable_pvs_present = true; } } QLabel *label; if(m_unremovable_pvs_present){ label = new QLabel( i18n( "Select physical volumes to " "remove them from volume group %1", vg_name)); } else{ label = new QLabel( i18n( "Select physical volumes excluding one to " "remove them from volume group %1", vg_name)); } label->setWordWrap(true); layout->addWidget(label); m_pv_checkbox = new PvGroupBox(member_pvs); layout->addWidget(m_pv_checkbox); m_pv_checkbox->setTitle( i18n("Unused physical volumes") ); connect(m_pv_checkbox, SIGNAL(stateChanged()), this, SLOT(excludeOneVolume())); m_pv_checkbox->selectNone(); connect(this, SIGNAL(okClicked()), this, SLOT(commitChanges())); } void VGReduceDialog::commitChanges() { const QByteArray vg_name = m_vg->getName().toLocal8Bit(); QByteArray pv_name; vg_t vg_dm = NULL; lvm_t lvm = MasterList::getLvm(); QStringList pv_list; // pvs to remove by name pv_list << m_pv_checkbox->getNames(); if( (vg_dm = lvm_vg_open(lvm, vg_name.data(), "w", 0)) ){ for(int x = 0; x < pv_list.size(); x++){ pv_name = pv_list[x].toLocal8Bit(); if( lvm_vg_reduce(vg_dm, pv_name.data()) ) KMessageBox::error(0, QString(lvm_errmsg(lvm))); } if( lvm_vg_write(vg_dm) ) KMessageBox::error(0, QString(lvm_errmsg(lvm))); lvm_vg_close(vg_dm); } else KMessageBox::error(0, QString(lvm_errmsg(lvm))); return; } void VGReduceDialog::excludeOneVolume() { QStringList names = m_pv_checkbox->getNames(); QStringList all_names = m_pv_checkbox->getAllNames(); int boxes_checked = names.size(); int boxes_count = all_names.size(); if(boxes_checked > 0){ if(m_unremovable_pvs_present || (boxes_checked < boxes_count)) enableButtonOk(true); else enableButtonOk(false); } else enableButtonOk(false); } kvpm-0.8.6/kvpm/lvsizechart.cpp0000644000175000017500000001070011733530416016746 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "lvsizechart.h" #include #include "logvol.h" #include "lvsizechartseg.h" #include "volgroup.h" LVSizeChart::LVSizeChart(VolGroup *const group, QTreeWidget *const vgTree, QWidget *parent) : QFrame(parent), m_vg(group), m_vg_tree(vgTree) { m_layout = new QHBoxLayout(this); m_layout->setSpacing(0); m_layout->setMargin(0); m_layout->setSizeConstraint(QLayout::SetNoConstraint); populateChart(); setLayout(m_layout); setMinimumHeight(45); setMaximumHeight(75); connect(m_vg_tree->header(), SIGNAL(sectionClicked(int)), this , SLOT(vgtreeClicked())); } void LVSizeChart::populateChart() { QList logical_volumes; QTreeWidgetItem *item; LogVol *lv; QLayoutItem *child; const long item_count = m_vg_tree->topLevelItemCount(); while( (child = m_layout->takeAt(0) ) != 0 ) // remove old children of layout delete child; for(int x = 0; x < item_count; x++){ item = m_vg_tree->topLevelItem(x); lv = m_vg->getLvByName( item->data(0,Qt::UserRole).toString() ); if( lv != NULL ){ logical_volumes.append(lv); if( lv->getSnapshotCount() ){ logical_volumes.append( lv->getSnapshots() ); } } } double seg_ratio; QWidget *widget; const long long free_extents = m_vg->getFreeExtents(); const long long total_extents = m_vg->getExtents(); const int lv_count = logical_volumes.size(); QString usage; // Filesystem: blank, ext2 etc. or freespace in vg int max_segment_width; for(int x = 0; x < lv_count; x++){ m_lv = logical_volumes[x]; if( !m_lv->isMirrorLeg() && !m_lv->isMirrorLog() && !m_lv->isVirtual() && !( m_lv->isMirror() && !(m_lv->getOrigin()).isEmpty() ) ){ usage = m_lv->getFilesystem(); if( m_lv->isMirror() ) seg_ratio = ( m_lv->getExtents() + m_lv->getLogCount() ) / (double) total_extents; else seg_ratio = m_lv->getExtents() / (double) total_extents; if( m_lv->isMirror() ) seg_ratio *= m_lv->getMirrorCount(); m_ratios.append(seg_ratio); widget = new LVChartSeg(m_vg, m_lv, usage, this); m_layout->addWidget(widget); m_widgets.append(widget); } } // if( free_extents && !m_vg->isExported() ){ // only create a free space widget if we have some if(free_extents){ // only create a free space widget if we have some seg_ratio = (free_extents / (double) total_extents) + 0.02; // allow a little "stretch" 0.02 usage = "freespace" ; widget = new LVChartSeg(m_vg, 0, usage, this); m_widgets.append(widget); m_layout->addWidget(widget); m_ratios.append(seg_ratio); } else if ( m_widgets.size() == 0 ){ // if we have no chart segs then put in a blank one // because lvsizechart won't work with zero segments usage = "" ; widget = new LVChartSeg(m_vg, 0, usage, this); m_widgets.append(widget); m_layout->addWidget(widget); m_ratios.append( 1.0 ); } max_segment_width = (int) ( width() * m_ratios[0] ); if( max_segment_width < 1 ) max_segment_width = 1; m_widgets[0]->setMaximumWidth(max_segment_width); for(int x = m_widgets.size() - 1; x >= 1 ; x--){ max_segment_width = qRound( ( (double)width() * m_ratios[x]) ); if( max_segment_width < 1 ) max_segment_width = 1; m_widgets[x]->setMaximumWidth( max_segment_width ); } } void LVSizeChart::resizeEvent(QResizeEvent *event) { const int new_width = (event->size()).width(); int max_segment_width = (int) (new_width * m_ratios[0]); if( max_segment_width < 1 ) max_segment_width = 1; m_widgets[0]->setMaximumWidth(max_segment_width); for(int x = m_widgets.size() - 1; x >= 1 ; x--){ max_segment_width = qRound( ( (double)width() * m_ratios[x]) ); if( max_segment_width < 1 ) max_segment_width = 1; m_widgets[x]->setMaximumWidth( max_segment_width ); } } void LVSizeChart::vgtreeClicked() { populateChart(); } kvpm-0.8.6/kvpm/pvpropertiesstack.h0000644000175000017500000000206611733530416017653 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PVPROPERTIESSTACK_H #define PVPROPERTIESSTACK_H #include #include #include #include #include #include class PhysVol; class VolGroup; class PVPropertiesStack : public QFrame { Q_OBJECT QStackedWidget *m_stack_widget; bool m_is_pv; VolGroup *m_vg; QList m_pv_stack_list; QLabel *m_pv_label; // The name of the device QScrollArea *m_vscroll; public: explicit PVPropertiesStack(VolGroup *volumeGroup, QWidget *parent = 0); void loadData(); public slots: void changePVStackIndex(QTreeWidgetItem *item, QTreeWidgetItem*); }; #endif kvpm-0.8.6/kvpm/removemirrorleg.h0000644000175000017500000000076111733530416017303 0ustar benscottbenscott/* * * * Copyright (C) 2008 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef REMOVEMIRRORLEG_H #define REMOVEMIRRORLEG_H class LogVol; bool remove_mirror_leg(LogVol *mirrorLeg); #endif kvpm-0.8.6/kvpm/mountentry.h0000644000175000017500000000347411733530416016313 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef MOUNTENTRY_H #define MOUNTENTRY_H #include #include #include class mntent; class MountEntry : public QObject { Q_OBJECT QString m_device_name, // for example: "/dev/sda1" m_mount_point, m_filesystem_type, // ext3, reiserfs, swap etcetera m_mount_options, // options (per mount), such as "noatime," set when mounting a filesystem m_super_options; // options (per superblock) set when mounting a filesystem int m_mount_position; // More than on device may be mounted on a mount point. // This number is zero if nothing else is mounted on // this mount point. Otherwise numbers go in reverse // of mount order. 1 is the *last* one mounted, highest // number is the first one mounted. int m_major, m_minor; public: explicit MountEntry(MountEntry *const copy, QObject *parent = 0); explicit MountEntry(mntent *const entry, QObject *parent = 0); MountEntry(QString const mountinfo, const int major, const int minor, QObject *parent = 0); QString getDeviceName(); QString getMountPoint(); QString getFilesystemType(); QString getMountOptions(); int getMountPosition(); int getMajorNumber(); int getMinorNumber(); void setMountPosition(const int pos); }; #endif kvpm-0.8.6/kvpm/executablefinder.h0000644000175000017500000000230311733530416017366 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef EXECUTABLEFINDER_H #define EXECUTABLEFINDER_H #include #include #include #include class ExecutableFinder : public QObject { Q_OBJECT QStringList m_default_search_paths; QStringList m_keys; // Names of the executables we are looking for QStringList m_not_found; // The ones we didn't find static QMap m_path_map; public: ExecutableFinder(QObject *parent = 0); static QString getPath(QString name); void reload(); // rescan the system for needed executables void reload(QStringList search); // As above using the stringlist for the search paths QStringList getAllPaths(); QStringList getAllNames(); QStringList getNotFound(); }; #endif kvpm-0.8.6/kvpm/masterlist.cpp0000644000175000017500000001027211733530416016603 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "masterlist.h" #include #include #include #include #include "fsprobe.h" #include "logvol.h" #include "mountentry.h" #include "mounttables.h" #include "pedexceptions.h" #include "physvol.h" #include "processprogress.h" #include "progressbox.h" #include "storagedevice.h" #include "topwindow.h" #include "volgroup.h" // These are static being variables initialized here QList MasterList::m_volume_groups = QList(); QList MasterList::m_storage_devices = QList(); lvm_t MasterList::m_lvm = NULL; MasterList::MasterList() : QObject() { m_lvm = lvm_init(NULL); ped_exception_set_handler(my_handler); m_mount_tables = new MountTables(); } MasterList::~MasterList() { if(m_lvm) lvm_quit(m_lvm); } void MasterList::rescan() { ProgressBox *progress_box = TopWindow::getProgressBox(); m_mount_tables->loadData(); progress_box->setRange(0,3); progress_box->setText("Scan lvm"); qApp->setOverrideCursor(Qt::WaitCursor); qApp->processEvents(); lvm_scan(m_lvm); lvm_config_reload(m_lvm); progress_box->setValue(1); progress_box->setText("Scan vgs"); qApp->processEvents(); scanVolumeGroups(); progress_box->setValue(2); qApp->processEvents(); progress_box->setText("Scan devs"); scanStorageDevices(); progress_box->setValue(3); qApp->restoreOverrideCursor(); qApp->processEvents(); progress_box->reset(); return; } void MasterList::scanVolumeGroups() { dm_list *vgnames; lvm_str_list *strl; vgnames = lvm_list_vg_names(m_lvm); dm_list_iterate_items(strl, vgnames){ // rescan() existing VolGroup, don't create a new one bool existing_vg = false; for(int x = 0; x < m_volume_groups.size(); x++){ if( QString(strl->str).trimmed() == m_volume_groups[x]->getName() ){ existing_vg = true; m_volume_groups[x]->rescan(m_lvm); } } if( !existing_vg ) m_volume_groups.append( new VolGroup(m_lvm, strl->str, m_mount_tables) ); } for(int x = m_volume_groups.size() - 1; x >= 0; x--){ // delete VolGroup if the vg is gone bool deleted_vg = true; dm_list_iterate_items(strl, vgnames){ if( QString(strl->str).trimmed() == m_volume_groups[x]->getName() ) deleted_vg = false; } if(deleted_vg) delete m_volume_groups.takeAt(x); } } void MasterList::scanStorageDevices() { QList physical_volumes; for(int x = 0; x < m_volume_groups.size(); x++) physical_volumes.append( m_volume_groups[x]->getPhysicalVolumes() ); for(int x = m_storage_devices.size() - 1; x >= 0; x--) delete m_storage_devices[x]; m_storage_devices.clear(); ped_device_free_all(); ped_device_probe_all(); PedDevice *dev = NULL; while( ( dev = ped_device_get_next(dev) ) ){ if( !QString("%1").arg(dev->path).contains("/dev/mapper") ) m_storage_devices.append( new StorageDevice(dev, physical_volumes, m_mount_tables) ); } } int MasterList::getVgCount() { return m_volume_groups.size(); } QList MasterList::getVolGroups() { return m_volume_groups; } QList MasterList::getStorageDevices() { return m_storage_devices; } lvm_t MasterList::getLvm() { return m_lvm; } VolGroup* MasterList::getVgByName(QString name) { name = name.trimmed(); for(int x = 0; x < m_volume_groups.size(); x++){ if(name == m_volume_groups[x]->getName()) return m_volume_groups[x]; } return NULL; } QStringList MasterList::getVgNames() { QStringList names; for(int x = 0; x < m_volume_groups.size(); x++) names << m_volume_groups[x]->getName(); return names; } kvpm-0.8.6/kvpm/partitiongraphic.h0000644000175000017500000000145411733530416017432 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PARTITIONGRAPHIC_H #define PARTITIONGRAPHIC_H #include class GraphicBody; class PartitionGraphic : public QFrame { Q_OBJECT GraphicBody *m_body; public: PartitionGraphic(QWidget *parent = 0); void setPrecedingSectors(const long long precedingSectors); void setFollowingSectors(const long long followingSectors); void setPartitionSectors(const long long partitionSectors); }; #endif kvpm-0.8.6/kvpm/changemirror.h0000644000175000017500000000354511733530416016546 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef CHANGEMIRROR_H #define CHANGEMIRROR_H #include #include #include #include #include #include #include #include #include class KComboBox; class KIntSpinBox; class KTabWidget; class LogVol; class PvGroupBox; class VolGroup; class KIcon; class ChangeMirrorDialog : public KDialog { Q_OBJECT KTabWidget *m_tab_widget; KIntSpinBox *m_add_mirrors_spin; KIntSpinBox *m_stripes_number_spin; QStackedWidget *m_error_stack; PvGroupBox *m_pv_box; QGroupBox *m_stripe_box; KComboBox *m_stripe_size_combo; bool m_change_log; // true == we just changing the logging of an existing mirror LogVol *m_lv; // The volume we are adding a mirror leg to. QRadioButton *m_contiguous_button, *m_normal_button, //Radio button to chose *m_anywhere_button, *m_inherited_button, // the allocation policy *m_cling_button; QRadioButton *m_core_log_button, *m_mirrored_log_button, *m_disk_log_button; QWidget *buildGeneralTab(); QWidget *buildPhysicalTab(); QStringList getPvsInUse(); bool validateStripeSpin(); void setLogRadioButtons(); public: explicit ChangeMirrorDialog(LogVol *logicalVolume, bool changeLog, QWidget *parent = 0); QStringList arguments(); private slots: void resetOkButton(); void commitChanges(); }; #endif kvpm-0.8.6/kvpm/partchange.h0000644000175000017500000000454011733530416016176 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PARTCHANGESIZE_H #define PARTCHANGESIZE_H #include #include #include #include #include #include #include #include #include #include #include #include class PartitionGraphic; class DualSelectorBox; class StoragePartition; class PartitionChangeDialog : public KDialog { Q_OBJECT StoragePartition *m_old_storage_part; PedDisk *m_ped_disk; PedPartition *m_existing_part; // The partition on the disk now bool m_use_si_units; bool m_bailout; PedSector m_min_shrink_size; // Minimum size of the fs after shrinking long long m_sector_size; // bytes per logical sector long long m_max_part_start; // start of biggest possible partition long long m_max_part_size; // size of largest possible partition PartitionGraphic *m_display_graphic; // The color bar that shows the relative // size of the partition graphically DualSelectorBox *m_dual_selector; QLabel *m_change_by_label, // How much are we growing or shrinking the partition? *m_preceding_label, // Free space before the proposed partition *m_following_label; bool m_logical; // Are we a logical partition? void setup(); bool movefs(const long long from_start, const long long to_start, const long long length); bool shrinkPartition(); bool growPartition(); bool movePartition(); void updateGraphicAndLabels(); bool pedCommitAndWait(PedDisk *const disk); QGroupBox *buildInfoGroup(const long long maxSize); public: explicit PartitionChangeDialog(StoragePartition *const partition, QWidget *parent = 0); void getMaximumPartition(PedSector &start, PedSector &end, PedSector §orSize); bool bailout(); private slots: void commitPartition(); void validateChange(); }; #endif kvpm-0.8.6/kvpm/fsprobe.h0000644000175000017500000000114011733530416015513 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef FSPROBE_H #define FSPROBE_H #include QString fsprobe_getfstype2(const QString devicePath); QString fsprobe_getfsuuid(const QString devicePath); QString fsprobe_getfslabel(const QString devicePath); #endif kvpm-0.8.6/kvpm/mountentry.cpp0000644000175000017500000000511411733530416016637 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "mountentry.h" #include #include #include #include "logvol.h" #include "volgroup.h" // MountEntry provides a thin wrapper for 'mntent' structs by providing // QStrings rather than const char arrays and a 'mount position' property MountEntry::MountEntry(MountEntry *const copy, QObject *parent) : QObject(parent) { m_device_name = copy->getDeviceName(); m_mount_point = copy->getMountPoint(); m_filesystem_type = copy->getFilesystemType(); m_mount_options = copy->getMountOptions(); m_major = copy->getMajorNumber(); m_minor = copy->getMinorNumber(); m_mount_position = copy->getMountPosition(); } MountEntry::MountEntry(mntent *const entry, QObject *parent) : QObject(parent) { m_device_name = QString( entry->mnt_fsname ).trimmed(); m_mount_point = QString( entry->mnt_dir ).trimmed(); m_filesystem_type = QString( entry->mnt_type ).trimmed(); m_mount_options = QString( entry->mnt_opts ).trimmed(); m_major = -1; m_minor = -1; m_mount_position = 0; } MountEntry::MountEntry(const QString mountinfo, const int major, const int minor, QObject *parent) : QObject(parent) { m_mount_point = mountinfo.section(' ', 4, 4); m_mount_options = mountinfo.section(' ', 5, 5); m_filesystem_type = mountinfo.section(' ', 6, 6); m_device_name = mountinfo.section(' ', 8, 8); m_super_options = mountinfo.section(' ', 9, 9); m_major = major; m_minor = minor; m_mount_position = 0; } QString MountEntry::getDeviceName() { return m_device_name; } QString MountEntry::getMountPoint() { return m_mount_point; } QString MountEntry::getFilesystemType() { return m_filesystem_type; } QString MountEntry::getMountOptions() { QStringList options; options.append(m_mount_options.split(',')); options.append(m_super_options.split(',')); options.removeDuplicates(); return options.join(","); } int MountEntry::getMountPosition() { return m_mount_position; } int MountEntry::getMajorNumber() { return m_major; } int MountEntry::getMinorNumber() { return m_minor; } void MountEntry::setMountPosition(const int pos) { m_mount_position = pos; } kvpm-0.8.6/kvpm/partitiongraphic.cpp0000644000175000017500000000550511733530416017766 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "partitiongraphic.h" #include // Two classes are set defined. Graphic body is privately used by PartitionGraphic // PartitionGraphic exists only to put a nice frame around the body widget. class GraphicBody : public QWidget { public: GraphicBody(QWidget *parent = NULL); long long m_preceding_sectors; long long m_following_sectors; long long m_partition_sectors; void paintEvent(QPaintEvent *); }; GraphicBody::GraphicBody(QWidget *parent) : QWidget(parent) { m_preceding_sectors = 0; m_following_sectors = 0; m_partition_sectors = 1; } void GraphicBody::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setPen(Qt::blue); const long double total_sectors = m_preceding_sectors + m_following_sectors + m_partition_sectors; double offset = 0; double length = (m_preceding_sectors / total_sectors) * width(); QRectF preceding_rectangle(offset, 0.0, length, (double)height()); offset += length; length = (m_partition_sectors / total_sectors) * width(); if( length < 1.0 ) // always show at least a sliver length = 1.0; QRectF partition_rectangle(offset, 0.0, length, (double)height()); offset += length; length = (m_following_sectors / total_sectors) * width(); QRectF following_rectangle(offset, 0.0, length, (double)height()); QBrush free_brush( Qt::green, Qt::SolidPattern ); QBrush partition_brush( Qt::blue, Qt::SolidPattern ); painter.fillRect( preceding_rectangle, free_brush ); painter.fillRect( following_rectangle, free_brush ); painter.fillRect( partition_rectangle, partition_brush ); } PartitionGraphic::PartitionGraphic(QWidget *parent) : QFrame(parent) { setFixedWidth(250); setMinimumHeight(30); QVBoxLayout *const layout = new QVBoxLayout(); layout->setSpacing(0); layout->setMargin(0); setFrameStyle(QFrame::Sunken | QFrame::Panel); setLineWidth(2); setLayout(layout); m_body = new GraphicBody(); layout->addWidget(m_body); } void PartitionGraphic::setPrecedingSectors(const long long precedingSectors) { m_body->m_preceding_sectors = precedingSectors; } void PartitionGraphic::setFollowingSectors(const long long followingSectors) { m_body->m_following_sectors = followingSectors; } void PartitionGraphic::setPartitionSectors(const long long partitionSectors) { m_body->m_partition_sectors = partitionSectors; } kvpm-0.8.6/kvpm/volgroup.cpp0000644000175000017500000003406111733530416016273 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "volgroup.h" #include #include "logvol.h" #include "mounttables.h" #include "physvol.h" VolGroup::VolGroup(lvm_t lvm, const char *vgname, MountTables *const tables) { m_vg_name = QString(vgname).trimmed(); m_tables = tables; rescan(lvm); } VolGroup::~VolGroup() { for(int x = m_member_lvs.size() - 1; x >= 0; x--) delete m_member_lvs.takeAt(x); for(int x = m_member_pvs.size() - 1; x >= 0; x--) delete m_member_pvs.takeAt(x); } void VolGroup::rescan(lvm_t lvm) { vg_t lvm_vg; lvm_property_value value; m_allocatable_extents = 0; m_mda_count = 0; m_pv_max = 0; m_extent_size = 0; m_extents = 0; m_lv_max = 0; m_size = 0; m_free = 0; m_free_extents = 0; m_exported = false; m_partial = false; m_clustered = false; m_active = false; // clustered volumes can't be opened when clvmd isn't running QByteArray vg_name_qba = m_vg_name.toLocal8Bit(); const char *vg_name_ascii = vg_name_qba.data(); if( (lvm_vg = lvm_vg_open(lvm, vg_name_ascii, "r", 0x00 )) ){ m_pv_max = lvm_vg_get_max_pv(lvm_vg); m_extent_size = lvm_vg_get_extent_size(lvm_vg); m_extents = lvm_vg_get_extent_count(lvm_vg); m_lv_max = lvm_vg_get_max_lv(lvm_vg); m_size = lvm_vg_get_size(lvm_vg); m_free = lvm_vg_get_free_size(lvm_vg); m_free_extents = lvm_vg_get_free_extent_count(lvm_vg); m_exported = (bool)lvm_vg_is_exported(lvm_vg); m_partial = (bool)lvm_vg_is_partial(lvm_vg); m_clustered = (bool)lvm_vg_is_clustered(lvm_vg); m_uuid = QString( lvm_vg_get_uuid(lvm_vg) ); value = lvm_vg_get_property(lvm_vg, "vg_fmt"); m_lvm_format = QString(value.value.string); value = lvm_vg_get_property(lvm_vg, "vg_attr"); const QString vg_attr = QString(value.value.string); if(vg_attr.at(0) == 'w') m_writable = true; else m_writable = false; if(vg_attr.at(1) == 'z') m_resizable = true; else m_resizable = false; if(vg_attr.at(4) == 'c') m_allocation_policy = "contiguous"; else if(vg_attr.at(4) == 'l') m_allocation_policy = "cling"; else if(vg_attr.at(4) == 'n') m_allocation_policy = "normal"; else if(vg_attr.at(4) == 'a') m_allocation_policy = "anywhere"; processPhysicalVolumes(lvm_vg); processLogicalVolumes(lvm_vg); lvm_vg_close(lvm_vg); } else{ for(int x = m_member_pvs.size() - 1; x >= 0; x--) delete m_member_pvs.takeAt(x); for(int x = m_member_lvs.size() - 1; x >= 0; x--) delete m_member_lvs.takeAt(x); m_member_pvs.clear(); m_member_lvs.clear(); } setActivePhysicalVolumes(); setLastUsedExtent(); return; } /* Takes orphan volumes off the child lv list and puts them back onto the top level lv list. An orphan is a volume that should be hidden under another, like a mirror leg, but doesn't seem to have a parent, or something left behind by a misbehaving lvm process */ lv_t VolGroup::findOrphan(QList &childList) { QList all_lvs = getLogicalVolumesFlat(); QList orphan_list; lvm_property_value value; QString child_name; QString lv_attr; lv_t orphan = NULL; for(int x = all_lvs.size() - 1; x >= 0; x--){ for(int y = childList.size() - 1; y >= 0; y--){ value = lvm_lv_get_property(childList[y], "lv_name"); child_name = QString(value.value.string).trimmed(); if( child_name == all_lvs[x]->getName() ){ childList.removeAt(y); all_lvs.removeAt(x); break; } } } for(int x = childList.size() - 1; x >= 0; x--){ // sort mirrors first, loners last value = lvm_lv_get_property(childList[x], "lv_attr"); lv_attr = QString(value.value.string); if( lv_attr[0] == 'm' || lv_attr[0] == 'M' ) orphan_list.prepend(childList[x]); else orphan_list.append(childList[x]); } if( ! orphan_list.isEmpty() ){ orphan = orphan_list.takeAt(0); childList = orphan_list; return orphan; } childList = orphan_list; return NULL; } QList VolGroup::getLogicalVolumes() { return m_member_lvs; } QList VolGroup::getLogicalVolumesFlat() { QListIterator tree_list(m_member_lvs); QList flat_list; while( tree_list.hasNext() ){ LogVol *const lv = tree_list.next(); flat_list.append(lv); flat_list.append(lv->getAllChildrenFlat()); } return flat_list; } QList VolGroup::getPhysicalVolumes() { return m_member_pvs; } LogVol* VolGroup::getLvByName(QString shortName) // Do not return snap container, just the "real" lv { QListIterator lvs_itr( getLogicalVolumesFlat() ); shortName = shortName.trimmed(); while( lvs_itr.hasNext() ){ LogVol *const lv = lvs_itr.next(); if( shortName == lv->getName() && !lv->isSnapContainer() ) return lv; } return NULL; } LogVol* VolGroup::getLvByUuid(QString uuid) // Do not return snap container, just the "real" lv { QListIterator lvs_itr( getLogicalVolumesFlat() ); uuid = uuid.trimmed(); while( lvs_itr.hasNext() ){ LogVol *const lv = lvs_itr.next(); if( uuid == lv->getUuid() && !lv->isSnapContainer() ) return lv; } return NULL; } PhysVol* VolGroup::getPvByName(QString name) { QListIterator pvs_itr(m_member_pvs); name = name.trimmed(); if( !name.contains("unknown device") ){ while( pvs_itr.hasNext() ){ PhysVol *pv = pvs_itr.next(); if( name == pv->getName() ) return pv; } } return NULL; } long VolGroup::getExtentSize() { return m_extent_size; } long long VolGroup::getExtents() { return m_extents; } long long VolGroup::getFreeExtents() { return m_free_extents; } long long VolGroup::getAllocatableExtents() { return m_allocatable_extents; } long long VolGroup::getAllocatableSpace() { return m_allocatable_extents * (long long)m_extent_size; } long long VolGroup::getSize() { return m_extents * m_extent_size; } long long VolGroup::getFreeSpace() { return m_free_extents * m_extent_size; } long long VolGroup::getUsedSpace() { return (m_extents - m_free_extents) * m_extent_size; } int VolGroup::getLvCount() { return m_member_lvs.size(); } int VolGroup::getLvMax() { return m_lv_max; } int VolGroup::getPvCount() { return m_member_pvs.size(); } int VolGroup::getPvMax() { return m_pv_max; } int VolGroup::getMdaCount() { return m_mda_count; } QString VolGroup::getName() { return m_vg_name; } QString VolGroup::getUuid() { return m_uuid; } QString VolGroup::getPolicy() { return m_allocation_policy; } QString VolGroup::getFormat() { return m_lvm_format; } QStringList VolGroup::getLvNames() { QStringList names; for(int x = m_member_lvs.size() - 1; x >= 0; x--) names << m_member_lvs[x]->getName(); return names; } bool VolGroup::isResizable() { return m_resizable; } bool VolGroup::isClustered() { return m_clustered; } bool VolGroup::isPartial() { return m_partial; } bool VolGroup::isExported() { return m_exported; } bool VolGroup::isActive() { return m_active; } void VolGroup::processPhysicalVolumes(vg_t lvmVG) { dm_list* pv_dm_list = lvm_vg_list_pvs(lvmVG); lvm_pv_list *pv_list; if(pv_dm_list){ // This should never be empty dm_list_iterate_items(pv_list, pv_dm_list){ // rescan() existing PhysVols bool existing_pv = false; for(int x = 0; x < m_member_pvs.size(); x++){ if( QString( lvm_pv_get_uuid( pv_list->pv ) ).trimmed() == m_member_pvs[x]->getUuid() ){ existing_pv = true; m_member_pvs[x]->rescan( pv_list->pv ); } } if( !existing_pv ) m_member_pvs.append( new PhysVol( pv_list->pv, this ) ); } for(int x = m_member_pvs.size() - 1; x >= 0; x--){ // delete PhysVolGroup if the pv is gone bool deleted_pv = true; dm_list_iterate_items(pv_list, pv_dm_list){ if( QString( lvm_pv_get_uuid( pv_list->pv ) ).trimmed() == m_member_pvs[x]->getUuid() ) deleted_pv = false; } if(deleted_pv) delete m_member_pvs.takeAt(x); } for(int x = 0; x < m_member_pvs.size(); x++){ if( m_member_pvs[x]->isAllocatable() ) m_allocatable_extents += m_member_pvs[x]->getRemaining() / (long long) m_extent_size; m_mda_count += m_member_pvs[x]->getMdaCount(); } } else qDebug() << " Empty pv_dm_list?"; } void VolGroup::processLogicalVolumes(vg_t lvmVG) { lvm_property_value value; dm_list* lv_dm_list = lvm_vg_list_lvs(lvmVG); lvm_lv_list *lv_list; QList lvm_lvs_all_top; // top level lvm logical volume handles QList lvm_lvs_all_children; // top level lvm logical volume handles QByteArray flags; QList old_member_lvs = m_member_lvs; m_member_lvs.clear(); if(lv_dm_list){ dm_list_iterate_items(lv_list, lv_dm_list){ value = lvm_lv_get_property(lv_list->lv, "lv_name"); if( QString(value.value.string).trimmed().startsWith("snapshot") ) continue; value = lvm_lv_get_property(lv_list->lv, "lv_name"); const QString top_name = QString(value.value.string).trimmed(); if(top_name.endsWith("_mlog") || top_name.contains("_mimagetmp_") || top_name.contains("_mimage_")){ lvm_lvs_all_children.append( lv_list->lv ); } else{ value = lvm_lv_get_property(lv_list->lv, "lv_attr"); flags = value.value.string; switch( flags[0] ){ case '-': case 'c': case 'O': case 'o': case 'p': lvm_lvs_all_top.append( lv_list->lv ); break; case 'M': case 'm': lvm_lvs_all_top.append( lv_list->lv ); break; default: lvm_lvs_all_children.append( lv_list->lv ); break; } } } for(int y = 0; y < lvm_lvs_all_top.size(); y++ ){ // rescan() existing LogVols bool is_new = true; for(int x = old_member_lvs.size() - 1; x >= 0; x--){ value = lvm_lv_get_property(lvm_lvs_all_top[y], "lv_attr"); flags = value.value.string; if( QString(lvm_lv_get_name( lvm_lvs_all_top[y] )).trimmed() == old_member_lvs[x]->getName() ){ old_member_lvs[x]->rescan(lvm_lvs_all_top[y], lvmVG); m_member_lvs.append( old_member_lvs.takeAt(x) ); is_new = false; } } if(is_new){ m_member_lvs.append( new LogVol( lvm_lvs_all_top[y], lvmVG, this, NULL, m_tables ) ); } } for(int x = old_member_lvs.size() - 1; x >= 0; x--){ // delete LogVol if lv is gone or is orphan delete old_member_lvs.takeAt(x); } lv_t lvm_lv_orphan; // non-top lvm logical volume handle with no home while( (lvm_lv_orphan = findOrphan(lvm_lvs_all_children)) ) m_member_lvs.append( new LogVol( lvm_lv_orphan, lvmVG, this, NULL, m_tables, true ) ); } else{ // lv_dm_list is empty so clean up member lvs for(int x = m_member_lvs.size() - 1; x >= 0; x--) delete m_member_lvs.takeAt(x); } } void VolGroup::setActivePhysicalVolumes() { PhysVol *pv; const QList all_lvs = getLogicalVolumesFlat(); for(int x = all_lvs.size() - 1; x >= 0; x--){ if( all_lvs[x]->isActive() ){ m_active = true; const QStringList pv_name_list = all_lvs[x]->getPvNamesAllFlat(); for(int x = pv_name_list.size() - 1; x >= 0; x--){ if( (pv = getPvByName(pv_name_list[x])) ) pv->setActive(); } } } } // TODO -- this should use pvseg properties pvseg_start pvseg_size // It should be moved to PhysVol void VolGroup::setLastUsedExtent() { QListIterator pv_itr(m_member_pvs); while( pv_itr.hasNext() ){ PhysVol *const pv = pv_itr.next(); long long last_extent = 0; long long last_used_extent = 0; const QString pv_name = pv->getName(); QListIterator lv_itr(m_member_lvs); while( lv_itr.hasNext() ){ LogVol *const lv = lv_itr.next(); for(int segment = lv->getSegmentCount() - 1; segment >= 0; segment--){ QList starting_extent = lv->getSegmentStartingExtent(segment); const QStringList pv_name_list = lv->getPvNames(segment); for(int x = 0; x < pv_name_list.size(); x++){ if( pv_name == pv_name_list[x] ){ last_extent = starting_extent[x] - 1 + (lv->getSegmentExtents(segment) / (lv->getSegmentStripes(segment))); if( last_extent > last_used_extent ) last_used_extent = last_extent; } } } } pv->setLastUsedExtent(last_used_extent); } } kvpm-0.8.6/kvpm/devicetab.cpp0000644000175000017500000000342111733530416016340 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "devicetab.h" #include #include #include #include "devicetree.h" #include "devicesizechart.h" #include "deviceproperties.h" #include "devicepropertiesstack.h" #include "storagedevice.h" DeviceTab::DeviceTab(QWidget *parent) : QWidget(parent) { m_size_chart = new DeviceSizeChart(this); m_device_stack = new DevicePropertiesStack(this); m_tree = new DeviceTree(m_size_chart, m_device_stack, this); m_tree_properties_splitter = new QSplitter(Qt::Horizontal); m_layout = new QVBoxLayout; m_layout->addWidget(m_size_chart); m_layout->addWidget(m_tree_properties_splitter); m_tree_properties_splitter->addWidget(m_tree); m_tree_properties_splitter->addWidget( setupPropertyStack() ); m_tree_properties_splitter->setStretchFactor(0, 9); m_tree_properties_splitter->setStretchFactor(1, 2); setLayout(m_layout); } void DeviceTab::rescan( QList devices ) { m_device_stack->loadData(devices); m_tree->loadData(devices); } QScrollArea *DeviceTab::setupPropertyStack() { QScrollArea *device_scroll = new QScrollArea(); device_scroll->setFrameStyle(QFrame::NoFrame); device_scroll->setBackgroundRole(QPalette::Base); device_scroll->setAutoFillBackground(true); device_scroll->setWidget(m_device_stack); device_scroll->setWidgetResizable(true); return device_scroll; } kvpm-0.8.6/kvpm/kvpmconfigdialog.cpp0000644000175000017500000010224211733530416017736 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "kvpmconfigdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include "executablefinder.h" KvpmConfigDialog::KvpmConfigDialog( QWidget *parent, const QString name, KConfigSkeleton *const skeleton, ExecutableFinder *const executableFinder ) : KConfigDialog(parent, name, skeleton), m_skeleton(skeleton), m_executable_finder(executableFinder) { setFaceType(KPageDialog::Auto); setDefaultButton(KDialog::Cancel); addPage(generalPage(), i18nc("The standard common options", "General"), QString("configure")); addPage(colorsPage(), i18n("Colors"), QString("color-picker")); addPage(programsPage(), i18n("Programs"), QString("applications-system")); } KvpmConfigDialog::~KvpmConfigDialog() { m_skeleton->deleteLater(); } QTabWidget *KvpmConfigDialog::generalPage() { KTabWidget *const tabwidget = new KTabWidget; tabwidget->insertTab(1, treesTab(), i18n("Tree Views") ); tabwidget->insertTab(1, propertiesTab(), i18n("Property Panels") ); return tabwidget; } QWidget *KvpmConfigDialog::treesTab() { QWidget *const trees = new QWidget; QVBoxLayout *const layout = new QVBoxLayout(); QLabel *const banner = new QLabel( i18n("Set columns to show in tables and tree views") ); banner->setAlignment(Qt::AlignCenter); layout->addWidget(banner); QHBoxLayout *const horizontal_layout = new QHBoxLayout(); horizontal_layout->addWidget( deviceGroup() ); horizontal_layout->addWidget( logicalGroup() ); horizontal_layout->addWidget( physicalGroup() ); horizontal_layout->addWidget( allGroup() ); layout->addLayout(horizontal_layout); trees->setLayout(layout); return trees; } QWidget *KvpmConfigDialog::propertiesTab() { QWidget *const properties = new QWidget(); QVBoxLayout *const layout = new QVBoxLayout(); QLabel *const banner = new QLabel( i18n("Set information to show in property panels") ); banner->setAlignment(Qt::AlignCenter); layout->addWidget(banner); QHBoxLayout *const horizontal_layout = new QHBoxLayout(); horizontal_layout->addWidget( devicePropertiesGroup() ); horizontal_layout->addWidget( lvPropertiesGroup() ); horizontal_layout->addWidget( pvPropertiesGroup() ); layout->addLayout(horizontal_layout); properties->setLayout(layout); return properties; } QWidget *KvpmConfigDialog::colorsPage() { static QColor ext2_color; static QColor ext3_color; static QColor ext4_color; static QColor btrfs_color; static QColor reiser_color; static QColor reiser4_color; static QColor msdos_color; static QColor jfs_color; static QColor xfs_color; static QColor hfs_color; static QColor ntfs_color; static QColor none_color; static QColor free_color; static QColor swap_color; static QColor physvol_color; m_skeleton->setCurrentGroup("FilesystemColors"); m_skeleton->addItemColor("ext2", ext2_color, Qt::blue); m_skeleton->addItemColor("ext3", ext3_color, Qt::darkBlue); m_skeleton->addItemColor("ext4", ext4_color, Qt::cyan); m_skeleton->addItemColor("btrfs", btrfs_color, Qt::yellow); m_skeleton->addItemColor("reiser", reiser_color, Qt::red); m_skeleton->addItemColor("reiser4", reiser4_color, Qt::darkRed); m_skeleton->addItemColor("msdos", msdos_color, Qt::darkYellow); m_skeleton->addItemColor("jfs", jfs_color, Qt::magenta); m_skeleton->addItemColor("xfs", xfs_color, Qt::darkCyan); m_skeleton->addItemColor("hfs", hfs_color, Qt::darkMagenta); m_skeleton->addItemColor("ntfs", ntfs_color, Qt::darkGray); m_skeleton->addItemColor("none", none_color, Qt::black); m_skeleton->addItemColor("free", free_color, Qt::green); m_skeleton->addItemColor("swap", swap_color, Qt::lightGray); m_skeleton->addItemColor("physvol", physvol_color, Qt::darkGreen); QWidget *const colors = new QWidget; QVBoxLayout *const colors_layout = new QVBoxLayout(); QGroupBox *const selection_box = new QGroupBox( i18n("Volume and Partition Colors") ); QGridLayout *const selection_layout = new QGridLayout(); QLabel *const message_label = new QLabel( i18n("These are the colors used to show the filesystem type \n" "on a partition or volume in any graphical display. Colors\n" "may also be selected for swap and unpartitioned space.") ); KSeparator *const left_separator = new KSeparator(Qt::Vertical); KSeparator *const right_separator = new KSeparator(Qt::Vertical); selection_layout->addWidget(left_separator, 1, 4, 5, 1); selection_layout->addWidget(right_separator, 1, 9, 5, 1); selection_layout->addWidget(message_label, 0, 0, 1, -1, Qt::AlignHCenter); QLabel *const ext2_label = new QLabel("ext2"); selection_layout->addWidget(ext2_label, 1, 1, Qt::AlignRight); KColorButton *const ext2_button = new KColorButton(); ext2_button->setObjectName("kcfg_ext2"); selection_layout->addWidget(ext2_button, 1, 2, Qt::AlignLeft); QLabel *const ext3_label = new QLabel("ext3"); selection_layout->addWidget(ext3_label, 2, 1, Qt::AlignRight); KColorButton *const ext3_button = new KColorButton(); ext3_button->setObjectName("kcfg_ext3"); selection_layout->addWidget(ext3_button, 2, 2, Qt::AlignLeft); QLabel *const ext4_label = new QLabel("ext4"); selection_layout->addWidget(ext4_label, 3, 1, Qt::AlignRight); KColorButton *const ext4_button = new KColorButton(); ext4_button->setObjectName("kcfg_ext4"); selection_layout->addWidget(ext4_button, 3, 2, Qt::AlignLeft); QLabel *const btrfs_label = new QLabel("btrfs"); selection_layout->addWidget(btrfs_label, 4, 1, Qt::AlignRight); KColorButton *const btrfs_button = new KColorButton(); btrfs_button->setObjectName("kcfg_btrfs"); selection_layout->addWidget(btrfs_button, 4, 2, Qt::AlignLeft); QLabel *const reiser_label = new QLabel("reiser"); selection_layout->addWidget(reiser_label, 1, 6, Qt::AlignRight); KColorButton *const reiser_button = new KColorButton(); reiser_button->setObjectName("kcfg_reiser"); selection_layout->addWidget(reiser_button, 1, 7, Qt::AlignLeft); QLabel *const reiser4_label = new QLabel("reiser4"); selection_layout->addWidget(reiser4_label, 2, 6, Qt::AlignRight); KColorButton *const reiser4_button = new KColorButton(); reiser4_button->setObjectName("kcfg_reiser4"); selection_layout->addWidget(reiser4_button, 2, 7, Qt::AlignLeft); QLabel *const msdos_label = new QLabel("ms-dos"); selection_layout->addWidget(msdos_label, 3, 6, Qt::AlignRight); KColorButton *const msdos_button = new KColorButton(); msdos_button->setObjectName("kcfg_msdos"); selection_layout->addWidget(msdos_button, 3, 7, Qt::AlignLeft); QLabel *const jfs_label = new QLabel("jfs"); selection_layout->addWidget(jfs_label, 1, 11, Qt::AlignRight); KColorButton *const jfs_button = new KColorButton(); jfs_button->setObjectName("kcfg_jfs"); selection_layout->addWidget(jfs_button, 1, 12, Qt::AlignLeft); QLabel *const xfs_label = new QLabel("xfs"); selection_layout->addWidget(xfs_label, 2, 11, Qt::AlignRight); KColorButton *const xfs_button = new KColorButton(); xfs_button->setObjectName("kcfg_xfs"); selection_layout->addWidget(xfs_button, 2, 12, Qt::AlignLeft); QLabel *const swap_label = new QLabel("linux \nswap"); selection_layout->addWidget(swap_label, 3, 11, Qt::AlignRight); KColorButton *const swap_button = new KColorButton(); swap_button->setObjectName("kcfg_swap"); selection_layout->addWidget(swap_button, 3, 12, Qt::AlignLeft); QLabel *const none_label = new QLabel("none"); selection_layout->addWidget(none_label, 4, 6, Qt::AlignRight); KColorButton *const none_button = new KColorButton(); none_button->setObjectName("kcfg_none"); selection_layout->addWidget(none_button, 4, 7, Qt::AlignLeft); QLabel *const free_label = new QLabel("free \nspace"); selection_layout->addWidget(free_label, 5, 6, Qt::AlignRight); KColorButton *const free_button = new KColorButton(); free_button->setObjectName("kcfg_free"); selection_layout->addWidget(free_button, 5, 7, Qt::AlignLeft); QLabel *const hfs_label = new QLabel("hfs"); selection_layout->addWidget(hfs_label, 4, 11, Qt::AlignRight); KColorButton *const hfs_button = new KColorButton(); hfs_button->setObjectName("kcfg_hfs"); selection_layout->addWidget(hfs_button, 4, 12, Qt::AlignLeft); QLabel *const ntfs_label = new QLabel("ntfs"); selection_layout->addWidget(ntfs_label, 5, 11, Qt::AlignRight); KColorButton *const ntfs_button = new KColorButton(); ntfs_button->setObjectName("kcfg_ntfs"); selection_layout->addWidget(ntfs_button, 5, 12, Qt::AlignLeft); QLabel *const physical_label = new QLabel("physical \nvolumes"); selection_layout->addWidget(physical_label, 5, 1, Qt::AlignRight); KColorButton *const physical_button = new KColorButton(); physical_button->setObjectName("kcfg_physvol"); selection_layout->addWidget(physical_button, 5, 2, Qt::AlignLeft); for(int row = 1; row < selection_layout->rowCount(); row++) selection_layout->setRowStretch(row, 1); selection_layout->setColumnStretch(0, 30); selection_layout->setColumnStretch(3, 30); selection_layout->setColumnStretch(5, 30); selection_layout->setColumnStretch(8, 30); selection_layout->setColumnStretch(10, 30); selection_layout->setColumnStretch(13, 30); selection_box->setLayout(selection_layout); colors_layout->addWidget(selection_box); colors->setLayout(colors_layout); return colors; } QTabWidget *KvpmConfigDialog::programsPage() { m_executables_table = new QTableWidget(); m_executables_table->setColumnCount(2); const QStringList headers = QStringList() << i18n("Program name") << i18n("Full path"); m_executables_table->setHorizontalHeaderLabels(headers); KTabWidget *const programs = new KTabWidget; QVBoxLayout *const programs1_layout = new QVBoxLayout(); QVBoxLayout *const programs2_layout = new QVBoxLayout(); QWidget *const programs1 = new QWidget(); QWidget *const programs2 = new QWidget(); programs1->setLayout(programs1_layout); programs2->setLayout(programs2_layout); const QStringList default_entries = QStringList() << "/sbin/" << "/usr/sbin/" << "/bin/" << "/usr/bin/" << "/usr/local/bin/" << "/usr/local/sbin/"; static QStringList search_entries; m_skeleton->setCurrentGroup("SystemPaths"); m_skeleton->addItemStringList("SearchPath", search_entries, default_entries); m_edit_list = new KEditListBox(); m_edit_list->setObjectName("kcfg_SearchPath"); QLabel *search_label = new QLabel( i18n("Set the search path for support programs") ); search_label->setAlignment(Qt::AlignCenter); programs1_layout->addWidget(search_label); programs1_layout->addWidget(m_edit_list); programs->insertTab(1, programs1, "Search path"); m_edit_list->insertStringList(search_entries); programs2_layout->addWidget(m_executables_table); programs->insertTab(1, programs2, "Executables"); fillExecutablesTable(); return programs; } void KvpmConfigDialog::updateSettings() { QStringList search = m_edit_list->items(); // system paths to search for binaries search.removeDuplicates(); m_edit_list->clear(); for( int x = 0; x < search.size(); x++){ if( !search[x].endsWith('/') ) search[x].append('/'); } m_edit_list->insertStringList(search); KConfigDialog::updateSettings(); m_executable_finder->reload(search); fillExecutablesTable(); } void KvpmConfigDialog::fillExecutablesTable() { QTableWidgetItem *table_item = NULL; const QStringList all_names = m_executable_finder->getAllNames(); const QStringList all_paths = m_executable_finder->getAllPaths(); const QStringList not_found = m_executable_finder->getNotFound(); m_executables_table->clearContents(); m_executables_table->setRowCount( all_names.size() + not_found.size() ); for(int x = 0; x < not_found.size(); x++){ table_item = new QTableWidgetItem( not_found[x] ); m_executables_table->setItem(x, 0, table_item); table_item = new QTableWidgetItem( KIcon("dialog-error"), "Not Found" ); m_executables_table->setItem(x, 1, table_item); } // these lists should be the same length, but just in case... for(int x = 0; (x < all_names.size()) && (x < all_paths.size()); x++){ table_item = new QTableWidgetItem( all_names[x] ); m_executables_table->setItem(x + not_found.size(), 0, table_item); table_item = new QTableWidgetItem( all_paths[x] ); m_executables_table->setItem(x + not_found.size(), 1, table_item); } m_executables_table->resizeColumnsToContents(); m_executables_table->setAlternatingRowColors( true ); m_executables_table->verticalHeader()->hide(); } QGroupBox *KvpmConfigDialog::deviceGroup() { QGroupBox *const device_group = new QGroupBox( i18n("Device Tree") ); QVBoxLayout *const device_layout = new QVBoxLayout(); device_group->setLayout(device_layout); static bool device_column; static bool partition_column; static bool capacity_column; static bool devremaining_column; static bool usage_column; static bool group_column; static bool flags_column; static bool mount_column; static bool expand_parts; m_skeleton->setCurrentGroup("DeviceTreeColumns"); // dt == device tree m_skeleton->addItemBool( "dt_device", device_column, true ); m_skeleton->addItemBool( "dt_partition", partition_column, true ); m_skeleton->addItemBool( "dt_capacity", capacity_column, true ); m_skeleton->addItemBool( "dt_remaining", devremaining_column, true ); m_skeleton->addItemBool( "dt_usage", usage_column, true ); m_skeleton->addItemBool( "dt_group", group_column, true ); m_skeleton->addItemBool( "dt_flags", flags_column, true ); m_skeleton->addItemBool( "dt_mount", mount_column, true ); m_skeleton->addItemBool( "dt_expandparts", expand_parts, true ); QCheckBox *const device_check = new QCheckBox( i18n("Device name") ); QCheckBox *const partition_check = new QCheckBox( i18n("Partition type") ); QCheckBox *const capacity_check = new QCheckBox( i18n("Capacity") ); QCheckBox *const devremaining_check = new QCheckBox( i18n("Remaining space") ); QCheckBox *const usage_check = new QCheckBox( i18n("Usage of device") ); QCheckBox *const group_check = new QCheckBox( i18n("Volume group") ); QCheckBox *const flags_check = new QCheckBox( i18n("Partition flags") ); QCheckBox *const mount_check = new QCheckBox( i18n("Mount point") ); QCheckBox *const expandparts_check = new QCheckBox( i18n("Expand device tree") ); device_check->setObjectName("kcfg_dt_device"); partition_check->setObjectName("kcfg_dt_partition"); capacity_check->setObjectName("kcfg_dt_capacity"); devremaining_check->setObjectName("kcfg_dt_remaining"); usage_check->setObjectName("kcfg_dt_usage"); group_check->setObjectName("kcfg_dt_group"); flags_check->setObjectName("kcfg_dt_flags"); mount_check->setObjectName("kcfg_dt_mount"); expandparts_check->setObjectName("kcfg_dt_expandparts"); device_check->setToolTip( i18n("Show the path to the device, /dev/sda1 for example.") ); partition_check->setToolTip( i18n("Show the type of partition, 'extended' for example.") ); capacity_check->setToolTip( i18n("Show the storage capacity in megabytes, gigabytes or terabytes.") ); devremaining_check->setToolTip( i18n("Show the remaining storage in megabytes, gigabytes or terabytes.") ); usage_check->setToolTip( i18n("Show how the partition is being used. Usually the type of filesystem, such as ext4, \n" "swap space or as a physical volume.") ); group_check->setToolTip( i18n("If the partition is a physical volume this column shows the volume group it is in.") ); flags_check->setToolTip( i18n("Show any flags, such a 'boot.'") ); mount_check->setToolTip( i18n("Show the mount point if the partition has a mounted filesystem.") ); expandparts_check->setToolTip( i18n("This determines if all partitions of all devices get shown at start up. \n " "The user can still expand or collapse the items by clicking on them.") ); device_layout->addWidget(device_check); device_layout->addWidget(partition_check); device_layout->addWidget(capacity_check); device_layout->addWidget(devremaining_check); device_layout->addWidget(usage_check); device_layout->addWidget(group_check); device_layout->addWidget(flags_check); device_layout->addWidget(mount_check); device_layout->addWidget(new KSeparator(Qt::Horizontal)); QLabel *const label = new QLabel( i18n("At start up:") ); label->setAlignment(Qt::AlignCenter); device_layout->addWidget(label); device_layout->addWidget(expandparts_check); device_layout->addStretch(); return device_group; } QGroupBox *KvpmConfigDialog::physicalGroup() { QGroupBox *const physical_group = new QGroupBox( i18n("Physical Volume Table") ); QVBoxLayout *const physical_layout = new QVBoxLayout(); physical_group->setLayout(physical_layout); static bool name_column; static bool size_column; static bool remaining_column; static bool used_column; static bool state_column; static bool allocate_column; static bool tags_column; static bool lvnames_column; m_skeleton->setCurrentGroup("PhysicalTreeColumns"); // pt == physical tree m_skeleton->addItemBool( "pt_name", name_column, true ); m_skeleton->addItemBool( "pt_size", size_column, true ); m_skeleton->addItemBool( "pt_remaining", remaining_column, true ); m_skeleton->addItemBool( "pt_used", used_column, false ); m_skeleton->addItemBool( "pt_state", state_column, false ); m_skeleton->addItemBool( "pt_allocate", allocate_column, true ); m_skeleton->addItemBool( "pt_tags", tags_column, true ); m_skeleton->addItemBool( "pt_lvnames", lvnames_column, true ); QCheckBox *const name_check = new QCheckBox( i18n("Volume name") ); QCheckBox *const size_check = new QCheckBox( i18n("Size") ); QCheckBox *const remaining_check = new QCheckBox( i18n("Remaining space") ); QCheckBox *const used_check = new QCheckBox( i18n("Used space") ); QCheckBox *const state_check = new QCheckBox( i18n("State") ); QCheckBox *const allocate_check = new QCheckBox( i18n("Allocatable") ); QCheckBox *const tags_check = new QCheckBox( i18n("Tags") ); QCheckBox *const lvnames_check = new QCheckBox( i18n("Logical Volumes") ); name_check->setObjectName("kcfg_pt_name"); size_check->setObjectName("kcfg_pt_size"); remaining_check->setObjectName("kcfg_pt_remaining"); used_check->setObjectName("kcfg_pt_used"); state_check->setObjectName("kcfg_pt_state"); allocate_check->setObjectName("kcfg_pt_allocate"); tags_check->setObjectName("kcfg_pt_tags"); lvnames_check->setObjectName("kcfg_pt_lvnames"); name_check->setToolTip( i18n("Show the path to the device, /dev/sda1 for example.") ); size_check->setToolTip( i18n("Show the storage capacity in megabytes, gigabytes or terabytes.") ); remaining_check->setToolTip( i18n("Show the remaining storage in megabytes, gigabytes or terabytes.") ); used_check->setToolTip( i18n("Show the used storage in megabytes, gigabytes or terabytes.") ); state_check->setToolTip( i18n("Show the state, either 'active' or 'inactive.'") ); allocate_check->setToolTip( i18n("Shows whether or not the physical volume can have its extents allocated.") ); tags_check->setToolTip( i18n("List any tags associated with the physical volume.") ); lvnames_check->setToolTip( i18n("List any logical volumes associated with the physical volume.") ); physical_layout->addWidget(name_check); physical_layout->addWidget(size_check); physical_layout->addWidget(remaining_check); physical_layout->addWidget(used_check); physical_layout->addWidget(state_check); physical_layout->addWidget(allocate_check); physical_layout->addWidget(tags_check); physical_layout->addWidget(lvnames_check); physical_layout->addStretch(); return physical_group; } QGroupBox *KvpmConfigDialog::logicalGroup() { QGroupBox *const logical_group = new QGroupBox( i18n("Logical volume tree") ); QVBoxLayout *const logical_layout = new QVBoxLayout(); logical_group->setLayout(logical_layout); static bool volume_column; static bool size_column; static bool remaining_column; static bool type_column; static bool filesystem_column; static bool stripes_column; static bool stripesize_column; static bool snapmove_column; static bool state_column; static bool access_column; static bool tags_column; static bool mountpoints_column; m_skeleton->setCurrentGroup("VolumeTreeColumns"); // vt == volume tree m_skeleton->addItemBool( "vt_volume", volume_column, true ); m_skeleton->addItemBool( "vt_size", size_column, true ); m_skeleton->addItemBool( "vt_remaining", remaining_column, true ); m_skeleton->addItemBool( "vt_type", type_column, true ); m_skeleton->addItemBool( "vt_filesystem", filesystem_column, false ); m_skeleton->addItemBool( "vt_stripes", stripes_column, false ); m_skeleton->addItemBool( "vt_stripesize", stripesize_column, false ); m_skeleton->addItemBool( "vt_snapmove", snapmove_column, true ); m_skeleton->addItemBool( "vt_state", state_column, true ); m_skeleton->addItemBool( "vt_access", access_column, false ); m_skeleton->addItemBool( "vt_tags", tags_column, true ); m_skeleton->addItemBool( "vt_mountpoints", mountpoints_column, false ); QCheckBox *const volume_check = new QCheckBox( i18n("Volume name") ); QCheckBox *const size_check = new QCheckBox( i18n("Size") ); QCheckBox *const remaining_check = new QCheckBox( i18n("Remaining space") ); QCheckBox *const type_check = new QCheckBox( i18n("Volume type") ); QCheckBox *const filesystem_check = new QCheckBox( i18n("Filesystem type") ); QCheckBox *const stripes_check = new QCheckBox( i18n("Stripe count") ); QCheckBox *const stripesize_check = new QCheckBox( i18n("Stripe size") ); QCheckBox *const snapmove_check = new QCheckBox( i18n("(\%)Snap/Copy") ); QCheckBox *const state_check = new QCheckBox( i18n("Volume state") ); QCheckBox *const access_check = new QCheckBox( i18n("Volume access") ); QCheckBox *const tags_check = new QCheckBox( i18n("Tags") ); QCheckBox *const mountpoints_check = new QCheckBox( i18n("Mount point") ); volume_check->setObjectName("kcfg_vt_volume"); size_check->setObjectName("kcfg_vt_size"); remaining_check->setObjectName("kcfg_vt_remaining"); type_check->setObjectName("kcfg_vt_type"); filesystem_check->setObjectName("kcfg_vt_filesystem"); stripes_check->setObjectName("kcfg_vt_stripes"); stripesize_check->setObjectName("kcfg_vt_stripesize"); snapmove_check->setObjectName("kcfg_vt_snapmove"); state_check->setObjectName("kcfg_vt_state"); access_check->setObjectName("kcfg_vt_access"); tags_check->setObjectName("kcfg_vt_tags"); mountpoints_check->setObjectName("kcfg_vt_mountpoints"); volume_check->setToolTip( i18n("Show the volume name.") ); size_check->setToolTip( i18n("Show the storage capacity in megabytes, gigabytes or terabytes.") ); remaining_check->setToolTip( i18n("Show the remaining storage in megabytes, gigabytes or terabytes.") ); type_check->setToolTip( i18n("Show the type of volume, 'mirror' or 'linear,' for example.") ); filesystem_check->setToolTip( i18n("Show the filesystem type on the volume, 'ext3' or 'swap,' for example.") ); stripes_check->setToolTip( i18n("Show the number of stripes on the volume, if it is striped") ); stripesize_check->setToolTip( i18n("Show the size of the stripes, if it is striped") ); snapmove_check->setToolTip( i18n("For a mirror, show the percentage of the mirror synced. \n" "For a snapshot, show the percentage of the snapshot used. \n" "For a pvmove, show the percentage of the move completed.") ); state_check->setToolTip( i18n("Show the state, 'active' or 'invalid,' for example.") ); access_check->setToolTip( i18n("Show access, either read only or read and write.") ); tags_check->setToolTip( i18n("List any tags associated with the volume") ); mountpoints_check->setToolTip( i18n("Show the mount point if the partition has a mounted filesystem.") ); logical_layout->addWidget(volume_check); logical_layout->addWidget(size_check); logical_layout->addWidget(remaining_check); logical_layout->addWidget(type_check); logical_layout->addWidget(filesystem_check); logical_layout->addWidget(stripes_check); logical_layout->addWidget(stripesize_check); logical_layout->addWidget(snapmove_check); logical_layout->addWidget(state_check); logical_layout->addWidget(access_check); logical_layout->addWidget(tags_check); logical_layout->addWidget(mountpoints_check); logical_layout->addStretch(); return logical_group; } QGroupBox *KvpmConfigDialog::allGroup() { QGroupBox *const all_group = new QGroupBox( i18n("All trees and tables") ); QGroupBox *const percent_group = new QGroupBox( i18n("Remaining and used space") ); QVBoxLayout *const all_layout = new QVBoxLayout(); QVBoxLayout *const percent_layout = new QVBoxLayout(); all_group->setLayout(all_layout); percent_group->setLayout(percent_layout); static bool show_percent; static bool show_total; static bool show_both; static int fs_warn_percent; static int pv_warn_percent; m_skeleton->setCurrentGroup("AllTreeColumns"); m_skeleton->addItemBool("show_percent", show_percent, false); m_skeleton->addItemBool("show_total", show_total, false); m_skeleton->addItemBool("show_both", show_both, true); m_skeleton->addItemInt("fs_warn", fs_warn_percent, 10); m_skeleton->addItemInt("pv_warn", pv_warn_percent, 0); QRadioButton *const percent_radio = new QRadioButton( i18n("Show percentage") ); QRadioButton *const total_radio = new QRadioButton( i18n("Show total") ); QRadioButton *const both_radio = new QRadioButton( i18n("Show both") ); percent_radio->setObjectName("kcfg_show_percent"); total_radio->setObjectName("kcfg_show_total"); both_radio->setObjectName("kcfg_show_both"); percent_layout->addWidget(total_radio); percent_layout->addWidget(percent_radio); percent_layout->addWidget(both_radio); percent_layout->addWidget(new KSeparator(Qt::Horizontal)); QHBoxLayout *const warn_layout = new QHBoxLayout; QHBoxLayout *const fs_warn_layout = new QHBoxLayout; QHBoxLayout *const pv_warn_layout = new QHBoxLayout; warn_layout->addWidget( new QLabel( i18n("Show warning icon") ) ); QLabel *const warn_icon = new QLabel; warn_icon->setPixmap( KIcon("exclamation").pixmap(16, 16) ); warn_layout->addWidget(warn_icon); percent_layout->addLayout(warn_layout); percent_layout->addWidget( new QLabel( i18n("when space falls to or below:") ) ); QSpinBox *const fs_warn_spin = new QSpinBox; fs_warn_spin->setObjectName("kcfg_fs_warn"); fs_warn_spin->setRange(0, 99); fs_warn_spin->setSingleStep(1); fs_warn_spin->setPrefix("% "); fs_warn_spin->setSpecialValueText( i18n("Never") ); fs_warn_layout->addWidget(fs_warn_spin); fs_warn_layout->addWidget( new QLabel( i18n("on a filesystem") ) ); fs_warn_layout->addStretch(); QSpinBox *const pv_warn_spin = new QSpinBox; pv_warn_spin->setObjectName("kcfg_pv_warn"); pv_warn_spin->setRange(0, 99); pv_warn_spin->setSingleStep(1); pv_warn_spin->setPrefix("% "); pv_warn_spin->setSpecialValueText( i18n("Never") ); pv_warn_layout->addWidget(pv_warn_spin); pv_warn_layout->addWidget( new QLabel( i18n("on a physical volume") ) ); pv_warn_layout->addStretch(); percent_layout->addLayout(fs_warn_layout); percent_layout->addLayout(pv_warn_layout); all_layout->addWidget(percent_group); all_layout->addStretch(); return all_group; } QGroupBox *KvpmConfigDialog::devicePropertiesGroup() { QGroupBox *const properties = new QGroupBox( i18n("Device Properties Panel") ); QVBoxLayout *const layout = new QVBoxLayout(); static bool mountpoint; static bool fsuuid; static bool fslabel; m_skeleton->setCurrentGroup("DeviceProperties"); // dp == device properties m_skeleton->addItemBool("dp_mount", mountpoint, true); m_skeleton->addItemBool("dp_fsuuid", fsuuid, false); m_skeleton->addItemBool("dp_fslabel", fslabel, false); QCheckBox *const mp_check = new QCheckBox( i18n("Mount points") ); mp_check->setToolTip( i18n("Show the filesystem mount points for the device") ); mp_check->setObjectName("kcfg_dp_mount"); QCheckBox *const fsuuid_check = new QCheckBox( i18n("Filesystem uuid") ); fsuuid_check->setToolTip( i18n("Show the filesytem UUID") ); fsuuid_check->setObjectName("kcfg_dp_fsuuid"); QCheckBox *const fslabel_check = new QCheckBox( i18n("Filesystem label") ); fslabel_check->setToolTip( i18n("Show the filesystem label") ); fslabel_check->setObjectName("kcfg_dp_fslabel"); layout->addWidget(mp_check); layout->addWidget(fsuuid_check); layout->addWidget(fslabel_check); layout->addStretch(); properties->setLayout(layout); return properties; } QGroupBox *KvpmConfigDialog::pvPropertiesGroup() { QGroupBox *const properties = new QGroupBox( i18n("Physical Volume Properties Panel") ); QVBoxLayout *const layout = new QVBoxLayout(); static bool mda; static bool pvuuid; m_skeleton->setCurrentGroup("PhysicalVolumeProperties"); m_skeleton->addItemBool("pp_mda", mda, true); m_skeleton->addItemBool("pp_uuid", pvuuid, false); QCheckBox *const mda_check = new QCheckBox( i18n("Metadata areas") ); mda_check->setToolTip( i18n("Show information about physical volume metadata") ); mda_check->setObjectName("kcfg_pp_mda"); QCheckBox *const pvuuid_check = new QCheckBox( i18n("Physical volume uuid") ); pvuuid_check->setToolTip( i18n("Show the physical volume UUID") ); pvuuid_check->setObjectName("kcfg_pp_uuid"); layout->addWidget(mda_check); layout->addWidget(pvuuid_check); layout->addStretch(); properties->setLayout(layout); return properties; } QGroupBox *KvpmConfigDialog::lvPropertiesGroup() { QGroupBox *const properties = new QGroupBox( i18n("Logical Volume Properties Panel") ); QVBoxLayout *const layout = new QVBoxLayout(); static bool mp; static bool fsuuid; static bool fslabel; static bool lvuuid; m_skeleton->setCurrentGroup("LogicalVolumeProperties"); // lp == logical volume properties m_skeleton->addItemBool("lp_mount", mp, true); m_skeleton->addItemBool("lp_fsuuid", fsuuid, false); m_skeleton->addItemBool("lp_fslabel", fslabel, false); m_skeleton->addItemBool("lp_uuid", lvuuid, false); QCheckBox *const mp_check = new QCheckBox( i18n("Mount points") ); mp_check->setToolTip( i18n("Show the filesystem mount points for the device") ); mp_check->setObjectName("kcfg_lp_mount"); QCheckBox *const fsuuid_check = new QCheckBox( i18n("Filesystem uuid") ); fsuuid_check->setToolTip( i18n("Show the filesytem UUID") ); fsuuid_check->setObjectName("kcfg_lp_fsuuid"); QCheckBox *const fslabel_check = new QCheckBox( i18n("Filesystem label") ); fslabel_check->setToolTip( i18n("Show the filesystem label") ); fslabel_check->setObjectName("kcfg_lp_fslabel"); QCheckBox *const lvuuid_check = new QCheckBox( i18n("Logical volume uuid") ); lvuuid_check->setToolTip( i18n("Show the logical volume UUID") ); lvuuid_check->setObjectName("kcfg_lp_uuid"); layout->addWidget(mp_check); layout->addWidget(fsuuid_check); layout->addWidget(fslabel_check); layout->addWidget(lvuuid_check); properties->setLayout(layout); layout->addStretch(); return properties; } kvpm-0.8.6/kvpm/tablecreate.h0000644000175000017500000000156211733530416016336 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef TABLECREATE_H #define TABLECREATE_H #include #include #include bool create_table(const QString devicePath); class TableCreateDialog : public KDialog { Q_OBJECT QString m_device_path; QRadioButton *m_msdos_button, *m_gpt_button, *m_destroy_button; public: explicit TableCreateDialog(const QString devicePath, QWidget *parent = 0); private slots: void commitTable(); }; #endif kvpm-0.8.6/kvpm/pvreduce.h0000644000175000017500000000077111733530416015701 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PVREDUCE_H #define PVREDUCE_H #include long long pv_reduce(QString path, long long new_size); #endif kvpm-0.8.6/kvpm/pvgroupbox.cpp0000644000175000017500000002717311733530416016637 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include #include #include #include #include #include "pvgroupbox.h" #include "physvol.h" #include "misc.h" #include "storagedevice.h" #include "storagepartition.h" #include "volgroup.h" PvGroupBox::PvGroupBox(QList volumes, QWidget *parent) : QGroupBox(parent), m_pvs(volumes) { KConfigSkeleton skeleton; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", m_use_si_units, false); setTitle( i18n("Available Physical Volumes") ); QGridLayout *const layout = new QGridLayout(); setLayout(layout); NoMungeCheck *check; const int pv_check_count = m_pvs.size(); m_space_label = new QLabel; m_extents_label = new QLabel; KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); if(pv_check_count < 1){ m_extent_size = 1; QLabel *pv_label = new QLabel( i18n("No suitable volumes found") ); layout->addWidget(pv_label); } else if(pv_check_count < 2){ m_extent_size = m_pvs[0]->getVg()->getExtentSize(); QLabel *pv_label = new QLabel( m_pvs[0]->getName() + " " + locale->formatByteSize( m_pvs[0]->getRemaining() ) ); layout->addWidget(pv_label, 0, 0, 1, -1); addLabelsAndButtons(layout, pv_check_count); calculateSpace(); } else{ m_extent_size = m_pvs[0]->getVg()->getExtentSize(); for(int x = 0; x < pv_check_count; x++){ check = new NoMungeCheck( m_pvs[x]->getName() + " " + locale->formatByteSize( m_pvs[x]->getRemaining() ) ); check->setAlternateText( m_pvs[x]->getName() ); check->setData( QVariant( m_pvs[x]->getRemaining() ) ); m_pv_checks.append(check); if(pv_check_count < 11 ) layout->addWidget(m_pv_checks[x], x % 5, x / 5); else if (pv_check_count % 3 == 0) layout->addWidget(m_pv_checks[x], x % (pv_check_count / 3), x / (pv_check_count / 3)); else layout->addWidget(m_pv_checks[x], x % ( (pv_check_count + 2) / 3), x / ( (pv_check_count + 2) / 3)); connect(check, SIGNAL(toggled(bool)), this, SLOT(calculateSpace())); } selectAll(); addLabelsAndButtons(layout, pv_check_count); } } PvGroupBox::PvGroupBox(QList devices, QList partitions, long long extentSize, QWidget *parent) : QGroupBox(parent), m_devices(devices), m_partitions(partitions), m_extent_size(extentSize) { KConfigSkeleton skeleton; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", m_use_si_units, false); if(devices.size() + partitions.size() > 1) setTitle( i18n("Available Physical Volumes") ); else setTitle( i18n("Physical Volume") ); QGridLayout *const layout = new QGridLayout(); setLayout(layout); QString name; long long size; int dev_count = 0; NoMungeCheck *check; const int pv_check_count = m_devices.size() + m_partitions.size(); m_space_label = new QLabel; m_extents_label = new QLabel; KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); if(pv_check_count < 1){ QLabel *pv_label = new QLabel( i18n("none found") ); layout->addWidget(pv_label); } else if(pv_check_count < 2){ if( m_devices.size() ){ name = m_devices[0]->getName(); size = m_devices[0]->getSize(); } else{ name = m_partitions[0]->getName(); size = m_partitions[0]->getSize(); } QLabel *pv_label = new QLabel( name + " " + locale->formatByteSize(size) ); layout->addWidget(pv_label, 0, 0, 1, -1); addLabelsAndButtons(layout, pv_check_count); calculateSpace(); } else{ for(int x = 0; x < m_devices.size(); x++){ dev_count++; name = m_devices[x]->getName(); size = m_devices[x]->getSize(); check = new NoMungeCheck( name + " " + locale->formatByteSize(size) ); check->setAlternateText(name); check->setData( QVariant(size) ); m_pv_checks.append(check); if(pv_check_count < 11 ) layout->addWidget(m_pv_checks[x], x % 5, x / 5); else if (pv_check_count % 3 == 0) layout->addWidget(m_pv_checks[x], x % (pv_check_count / 3), x / (pv_check_count / 3)); else layout->addWidget(m_pv_checks[x], x % ( (pv_check_count + 2) / 3), x / ( (pv_check_count + 2) / 3)); connect(check, SIGNAL(toggled(bool)), this, SLOT(calculateSpace())); } for(int x = 0; x < m_partitions.size(); x++){ name = m_partitions[x]->getName(); size = m_partitions[x]->getSize(); check = new NoMungeCheck( name + " " + locale->formatByteSize(size) ); check->setAlternateText(name); check->setData( QVariant(size) ); m_pv_checks.append(check); if(pv_check_count < 11 ) layout->addWidget(check, (dev_count + x) % 5, (dev_count + x) / 5); else if (pv_check_count % 3 == 0) layout->addWidget(check, (dev_count + x) % (pv_check_count / 3), (dev_count + x) / (pv_check_count / 3)); else layout->addWidget(check, (dev_count + x) % ( (pv_check_count + 2) / 3), (dev_count + x) / ( (pv_check_count + 2) / 3)); connect(check, SIGNAL(toggled(bool)), this, SLOT(calculateSpace())); } selectAll(); addLabelsAndButtons(layout, pv_check_count); setExtentSize(extentSize); } } QStringList PvGroupBox::getNames(){ QStringList names; if(m_pv_checks.size()){ for(int x = 0; x < m_pv_checks.size(); x++){ if(m_pv_checks[x]->isChecked()) names << m_pv_checks[x]->getAlternateText(); } } else if(m_pvs.size()) names << m_pvs[0]->getName(); else if(m_devices.size()) names << m_devices[0]->getName(); else if(m_partitions.size()) names << m_partitions[0]->getName(); return names; } QStringList PvGroupBox::getAllNames(){ QStringList names; if(m_pv_checks.size()){ for(int x = 0; x < m_pv_checks.size(); x++){ names << m_pv_checks[x]->getAlternateText(); } } else if(m_pvs.size()) names << m_pvs[0]->getName(); else if(m_devices.size()) names << m_devices[0]->getName(); else if(m_partitions.size()) names << m_partitions[0]->getName(); return names; } long long PvGroupBox::getRemainingSpace(){ long long space = 0; if(m_pv_checks.size()){ for(int x = 0; x < m_pv_checks.size(); x++){ if(m_pv_checks[x]->isChecked()) space += (m_pv_checks[x]->getData()).toLongLong(); } } else if(m_pvs.size()) space = m_pvs[0]->getRemaining(); else if(m_devices.size()) space = m_devices[0]->getSize(); else if(m_partitions.size()) space = m_partitions[0]->getSize(); else space = 0; return space; } QList PvGroupBox::getRemainingSpaceList(){ QList space; if(m_pv_checks.size()){ for(int x = 0; x < m_pv_checks.size(); x++){ if(m_pv_checks[x]->isChecked()) space.append( (m_pv_checks[x]->getData()).toLongLong() ); } } else if(m_pvs.size()) space.append( m_pvs[0]->getRemaining() ); else if(m_devices.size()) space.append( m_devices[0]->getSize() ); else if(m_partitions.size()) space.append( m_partitions[0]->getSize() ); return space; } void PvGroupBox::selectAll(){ if(m_pv_checks.size()){ for(int x = 0; x < m_pv_checks.size(); x++){ if(m_pv_checks[x]->isEnabled()) m_pv_checks[x]->setChecked(true); } } emit stateChanged(); return; } void PvGroupBox::selectNone(){ if(m_pv_checks.size()){ for(int x = 0; x < m_pv_checks.size(); x++) m_pv_checks[x]->setChecked(false); } emit stateChanged(); return; } void PvGroupBox::calculateSpace(){ KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); if(m_pv_checks.size() > 1){ m_space_label->setText( i18n("Selected space: %1", locale->formatByteSize(getRemainingSpace()) ) ); m_extents_label->setText( i18n("Selected extents: %1", getRemainingSpace() / m_extent_size ) ); } else{ m_space_label->setText( i18n("Space: %1", locale->formatByteSize(getRemainingSpace()) ) ); m_extents_label->setText( i18n("Extents: %1", getRemainingSpace() / m_extent_size ) ); } emit stateChanged(); return; } void PvGroupBox::setExtentSize(long long extentSize){ m_extent_size = extentSize; if(m_pv_checks.size()){ for(int x = 0; x < m_pv_checks.size(); x++){ if( (m_pv_checks[x]->getData()).toULongLong() > ( m_extent_size + 0xfffff ) ) // 1 MiB for MDA, fix this m_pv_checks[x]->setEnabled(true); // when MDA size is put in else{ // liblvm2app m_pv_checks[x]->setChecked(false); m_pv_checks[x]->setEnabled(false); } } } calculateSpace(); } void PvGroupBox::disableOrigin(PhysVol *originVolume){ QString name; if(originVolume){ name = originVolume->getName(); for(int x = 0; x < m_pvs.size(); x++){ if( m_pvs[x]->getName() == name ){ m_pv_checks[x]->setChecked(false); m_pv_checks[x]->setEnabled(false); } else m_pv_checks[x]->setEnabled(true); } } } QHBoxLayout *PvGroupBox::getButtons() { QHBoxLayout *const layout = new QHBoxLayout(); KPushButton *const all = new KPushButton( i18n("Select all") ); KPushButton *const none = new KPushButton( i18n("Clear all") ); layout->addStretch(); layout->addWidget(all); layout->addStretch(); layout->addWidget(none); layout->addStretch(); connect(all, SIGNAL(clicked(bool)), this, SLOT(selectAll())); connect(none, SIGNAL(clicked(bool)), this, SLOT(selectNone())); return layout; } void PvGroupBox::addLabelsAndButtons(QGridLayout *const layout, const int pvCount) { QVBoxLayout *const spacer1 = new QVBoxLayout; QVBoxLayout *const spacer2 = new QVBoxLayout; spacer1->addSpacing(10); spacer2->addSpacing(10); const int count = layout->rowCount(); layout->addLayout(spacer1, count, 0, 1, -1); layout->addWidget(m_space_label, count + 1, 0, 1, -1); layout->addWidget(m_extents_label, count + 2, 0, 1, -1); layout->addLayout(spacer2, count + 3, 0, 1, -1); if(pvCount > 1) layout->addLayout(getButtons(), count + 4, 0, 1, -1); } kvpm-0.8.6/kvpm/partadd.cpp0000644000175000017500000003326311733530416016040 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include #include #include #include #include #include "dualselectorbox.h" #include "partadd.h" #include "partitiongraphic.h" #include "pedexceptions.h" #include "sizeselectorbox.h" #include "storagepartition.h" /* The "partition" we get here is usually a ped pointer to a the freespace between partitions. It can also be an *empty* extended partition however. */ PartitionAddDialog::PartitionAddDialog(StoragePartition *const partition, QWidget *parent) : KDialog(parent), m_partition(partition) { KConfigSkeleton skeleton; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", m_use_si_units, false); if( !(m_bailout = hasInitialErrors()) ){ getMaximumPartition(m_max_part_start, m_max_part_end, m_sector_size); m_max_part_size = m_max_part_end - m_max_part_start + 1; if( m_max_part_size >= (0x200000 / m_sector_size) ){ // sectors per megabyte setWindowTitle( i18n("Create A New Partition") ); QWidget *const dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *const layout = new QVBoxLayout(); dialog_body->setLayout(layout); QLabel *const label = new QLabel( i18n("Create A New Partition") ); label->setAlignment(Qt::AlignCenter); layout->addSpacing(5); layout->addWidget(label); layout->addSpacing(5); layout->addWidget( buildInfoGroup() ); m_dual_selector = new DualSelectorBox(m_sector_size, m_max_part_size); validateChange(); layout->addWidget(m_dual_selector); connect(m_dual_selector, SIGNAL(changed()), this, SLOT(validateChange())); connect(this, SIGNAL(okClicked()), this, SLOT(commitPartition())); } else{ m_bailout = true; KMessageBox::error(0, i18n("Not enough usable space for a new partition")); } } setDefaultButton(KDialog::Cancel); } void PartitionAddDialog::commitPartition() { const PedSector ONE_MIB = 0x100000 / m_sector_size; // sectors per megabyte PedDevice *const device = m_partition->getPedPartition()->disk->dev; PedDisk *const disk = m_partition->getPedPartition()->disk; PedSector first_sector = m_max_part_start + m_dual_selector->getCurrentOffset(); PedSector last_sector = first_sector + m_dual_selector->getCurrentSize() - 1; PedPartitionType type; if( first_sector < m_max_part_start ) first_sector = m_max_part_start; if( last_sector > m_max_part_end ) last_sector = m_max_part_end; if( (1 + last_sector - first_sector) < (2 * ONE_MIB) ){ KMessageBox::error(0, i18n("Partitions less than two MiB are not supported")); return; } PedAlignment *const start_align = ped_alignment_new( 0, ONE_MIB); PedAlignment *const end_align = ped_alignment_new(-1, ONE_MIB); PedGeometry *const max_geom = ped_geometry_new(device, m_max_part_start, 1 + m_max_part_end - m_max_part_start); first_sector = ped_alignment_align_nearest(start_align, max_geom, first_sector); last_sector = ped_alignment_align_nearest(end_align, max_geom, last_sector); ped_geometry_destroy(max_geom); const PedSector length = 1 + last_sector - first_sector; PedGeometry *const start_range = ped_geometry_new(device, first_sector, length); PedGeometry *const end_range = ped_geometry_new(device, first_sector, length); PedConstraint *const constraint = ped_constraint_new( start_align, end_align, start_range, end_range, length - ONE_MIB, length ); if(constraint != NULL){ if(m_type_combo->currentIndex() == 0) type = PED_PARTITION_NORMAL ; else if(m_type_combo->currentIndex() == 1) type = PED_PARTITION_EXTENDED ; else type = PED_PARTITION_LOGICAL ; PedDiskFlag cylinder_flag = ped_disk_flag_get_by_name("cylinder_alignment"); if( ped_disk_is_flag_available(disk, cylinder_flag) ) ped_disk_set_flag(disk, cylinder_flag, 0); PedPartition *ped_new_partition = ped_partition_new(disk, type, 0, first_sector, last_sector); if(ped_new_partition != NULL){ if( ped_disk_add_partition(disk, ped_new_partition, constraint) ) ped_disk_commit(disk); } ped_constraint_destroy(constraint); } ped_geometry_destroy(start_range); ped_geometry_destroy(end_range); } long long PartitionAddDialog::convertSizeToSectors(int index, double size) { long double partition_size = size; if(index == 0) partition_size *= (long double)0x100000; else if(index == 1) partition_size *= (long double)0x40000000; else{ partition_size *= (long double)0x100000; partition_size *= (long double)0x100000; } partition_size /= m_sector_size; if (partition_size < 0) partition_size = 0; return qRound64(partition_size); } void PartitionAddDialog::validateChange(){ const PedSector ONE_MIB = 0x100000 / m_sector_size; // sectors per megabyte const long long offset = m_dual_selector->getCurrentOffset(); const long long size = m_dual_selector->getCurrentSize(); if(( offset <= m_max_part_size - size ) && ( size <= m_max_part_size - offset ) && ( size >= 2 * ONE_MIB ) && ( m_dual_selector->isValid() ) ){ enableButtonOk(true); } else enableButtonOk(false); updatePartition(); } void PartitionAddDialog::updatePartition(){ const long long offset = m_dual_selector->getCurrentOffset(); const long long size = m_dual_selector->getCurrentSize(); long long free = m_max_part_size - offset; if(free < 0) free = 0; KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); QString preceding_bytes_string = locale->formatByteSize(offset * m_sector_size); m_preceding_label->setText( i18n("Preceding space: %1", preceding_bytes_string) ); PedSector following = m_max_part_size - (size + offset); if(following < 0) following = 0; PedSector following_space = following * m_sector_size; if( following_space < 32 * m_sector_size ) following_space = 0; QString following_bytes_string = locale->formatByteSize(following_space); m_remaining_label->setText( i18n("Following space: %1", following_bytes_string) ); m_display_graphic->setPrecedingSectors(offset); m_display_graphic->setPartitionSectors(size); m_display_graphic->setFollowingSectors(following); m_display_graphic->repaint(); } void PartitionAddDialog::getMaximumPartition(PedSector &start, PedSector &end, PedSector §orSize) { PedPartition *free_space = m_partition->getPedPartition(); PedDevice *const device = free_space->disk->dev; PedDisk *const disk = ped_disk_new(device); sectorSize = device->sector_size; const PedSector ONE_MIB = 0x100000 / sectorSize; // sectors per megabyte // If this is an empty extended partition and not freespace inside // one then look for the freespace. if( free_space->type & PED_PARTITION_EXTENDED ){ do{ free_space = ped_disk_next_partition(disk, free_space); if( !free_space ) qDebug() << "Extended partition with no freespace found!"; } while( !((free_space->type & PED_PARTITION_FREESPACE) && (free_space->type & PED_PARTITION_LOGICAL)) ); } start = free_space->geom.start; end = free_space->geom.length + start - 1; PedPartition *part; if( free_space->type & PED_PARTITION_LOGICAL ) part = ped_partition_new(disk, PED_PARTITION_LOGICAL, NULL, start, end); else part = ped_partition_new(disk, PED_PARTITION_NORMAL, NULL, start, end); start = part->geom.start; end = part->geom.length + start - 1; PedConstraint *constraint = ped_constraint_any(device); ped_disk_add_partition(disk, part, constraint); start = part->geom.start; end = part->geom.length + start - 1; PedDiskFlag cylinder_flag = ped_disk_flag_get_by_name("cylinder_alignment"); if( ped_disk_is_flag_available(disk, cylinder_flag) ) ped_disk_set_flag(disk, cylinder_flag, 0); PedGeometry *max_geometry = ped_disk_get_max_partition_geometry(disk, part, constraint); start = max_geometry->start; end = max_geometry->length + max_geometry->start - 1; ped_constraint_destroy(constraint); PedAlignment *const start_align = ped_alignment_new( 0, ONE_MIB); PedAlignment *const end_align = ped_alignment_new(-1, ONE_MIB); PedGeometry *const start_range = ped_geometry_new(device, start, max_geometry->length); PedGeometry *const end_range = ped_geometry_new(device, start, max_geometry->length); constraint = ped_constraint_new(start_align, end_align, start_range, end_range, 1, max_geometry->length); ped_geometry_destroy(max_geometry); max_geometry = ped_disk_get_max_partition_geometry(disk, part, constraint); start = max_geometry->start; end = max_geometry->length + max_geometry->start - 1; ped_disk_remove_partition(disk, part); ped_partition_destroy(part); ped_constraint_destroy(constraint); ped_geometry_destroy(max_geometry); ped_disk_destroy(disk); } QGroupBox* PartitionAddDialog::buildInfoGroup() { QGroupBox *const group = new QGroupBox(this); QVBoxLayout *const layout = new QVBoxLayout(); layout->addSpacing(10); m_display_graphic = new PartitionGraphic(); layout->addWidget(m_display_graphic, 0, Qt::AlignCenter); QLabel *const path = new QLabel( i18n("Device: %1", m_partition->getName()) ); path->setAlignment( Qt::AlignHCenter ); layout->addWidget(path); layout->addSpacing(10); m_preceding_label = new QLabel(); layout->addWidget( m_preceding_label ); m_remaining_label = new QLabel(); layout->addWidget( m_remaining_label ); KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); QString total_bytes = locale->formatByteSize( m_max_part_size * m_sector_size ); QLabel *excluded_label = new QLabel( i18n("Maximum size: %1", total_bytes) ); layout->addWidget( excluded_label ); layout->addSpacing(10); QLabel *const type_label = new QLabel( i18n("Select type: ") ); QHBoxLayout *const type_layout = new QHBoxLayout; m_type_combo = buildTypeCombo(); type_layout->addWidget(type_label); type_layout->addWidget(m_type_combo); type_layout->addStretch(); layout->addLayout(type_layout); group->setLayout(layout); return group; } KComboBox* PartitionAddDialog::buildTypeCombo() { KComboBox *const combo = new KComboBox; PedPartition *const free_space = m_partition->getPedPartition(); bool logical_freespace; // true if we are inside an extended partition bool extended_allowed; // true if we can create an extended partition here /* check to see if partition table supports extended partitions and if it already has one */ PedDisk *const disk = m_partition->getPedPartition()->disk; if( (PED_DISK_TYPE_EXTENDED & disk->type->features) && ( !ped_disk_extended_partition(disk) ) ) extended_allowed = true; else extended_allowed = false; if( free_space->type & PED_PARTITION_LOGICAL ) logical_freespace = true; else if( free_space->type & PED_PARTITION_EXTENDED ) logical_freespace = true; else logical_freespace = false; combo->insertItem(0, i18n("Primary")); combo->insertItem(1, i18n("Extended")); if(logical_freespace){ combo->insertItem(2, i18n("Logical")); combo->setEnabled(false); combo->setCurrentIndex(2); } else if(!extended_allowed){ combo->setEnabled(false); combo->setCurrentIndex(0); } return combo; } bool PartitionAddDialog::hasInitialErrors() { PedDisk *const disk = m_partition->getPedPartition()->disk; const unsigned ped_type = m_partition->getPedType(); const bool logical_freespace = ( ped_type & PED_PARTITION_FREESPACE ) && ( ped_type & PED_PARTITION_LOGICAL ); const int count = ped_disk_get_primary_partition_count(disk); const int max_count = ped_disk_get_max_primary_partition_count(disk); if( count >= max_count && ( !( logical_freespace || ( ped_type & PED_PARTITION_EXTENDED ) ) ) ){ KMessageBox::error(0, i18n("This disk already has %1 primary partitions, the maximum", count)); return true; } else if( ( ped_type & PED_PARTITION_EXTENDED ) && ( !m_partition->isEmpty() ) ){ KMessageBox::error(0, i18n("This should not happen. Try selecting the freespace and not the partiton itself")); return true; } return false; } bool PartitionAddDialog::bailout() { return m_bailout; } kvpm-0.8.6/kvpm/pvtree.h0000644000175000017500000000212211733530416015361 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PVTREE_H #define PVTREE_H #include #include #include #include class VolGroup; class LogVol; class PhysVol; class PVTree : public QTreeWidget { Q_OBJECT VolGroup *m_vg; QMenu *m_context_menu; bool m_use_si_units; QAction *pv_move_action, *vg_reduce_action, *pv_change_action; QString m_pv_name; void setViewConfig(); void setupContextMenu(); public: explicit PVTree(VolGroup *const group, QWidget *parent = 0); void loadData(); private slots: void popupContextMenu(QPoint point); void movePhysicalExtents(); void reduceVolumeGroup(); void changePhysicalVolume(); }; #endif kvpm-0.8.6/kvpm/fsextend.h0000644000175000017500000000111611733530416015676 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef FSEXTEND_H #define FSEXTEND_H #include bool fs_extend(const QString dev, const QString fs, const QStringList mps, const bool isLV = false); bool fs_can_extend(const QString fs); #endif kvpm-0.8.6/kvpm/vgremove.h0000644000175000017500000000073711733530416015720 0ustar benscottbenscott/* * * * Copyright (C) 2008 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGREMOVE_H #define VGREMOVE_H class VolGroup; bool remove_vg(VolGroup *volumeGroup); #endif kvpm-0.8.6/kvpm/misc.h0000644000175000017500000000326211733530416015015 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef MISC_H #define MISC_H #include #include #include #include QStringList splitUuid(QString const uuid); bool isBusy(const QString device); class NoMungeCheck : public QCheckBox { QString m_unmunged_text; // QCheckBox text() without amperands QString m_alternate_text; // We can put anything we want in here QStringList m_alternate_text_list; // Ditto QVariant m_data; public: explicit NoMungeCheck(const QString text, QWidget *parent = NULL); QString getAlternateText(); QStringList getAlternateTextList(); QString getUnmungedText(); void setAlternateText(QString alternateText); void setAlternateTextList(QStringList alternateTextList); void setData(QVariant data); QVariant getData(); }; class NoMungeRadioButton : public QRadioButton { QString m_unmunged_text; // QCheckBox text() without amperands QString m_alternate_text; // We can put anything we want in here QVariant m_data; public: explicit NoMungeRadioButton(const QString text, QWidget *parent = NULL); QString getAlternateText(); QString getUnmungedText(); void setAlternateText(QString alternateText); void setData(QVariant data); QVariant getData(); }; #endif kvpm-0.8.6/kvpm/removefs.cpp0000644000175000017500000000343011733530416016240 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "removefs.h" #include #include #include #include "storagepartition.h" #include "logvol.h" // Removes all traces of any filesystem on a partition or volume bool remove_fs(const QString name) { const QString warning_message = i18n("Are you sure you want delete the filesystem on %1? " "Any data on it will be lost.", name); const QString error_message = i18n("Error writing to device %1", name); if(KMessageBox::warningYesNo(NULL, warning_message, QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QString(), KMessageBox::Dangerous) == KMessageBox::Yes){ QByteArray zero_array(128 * 1024, '\0'); QFile *const device = new QFile(name); bool error = false; if( device->open(QIODevice::ReadWrite) ){ if( device->write(zero_array) < 0 ) error = true; if( !device->flush() ) error = true; device->close(); if(error) KMessageBox::error(0, error_message); } else KMessageBox::error(0, error_message); return(true); } return(false); } kvpm-0.8.6/kvpm/vgreduceone.h0000644000175000017500000000101611733530416016363 0ustar benscottbenscott/* * * * Copyright (C) 2008 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGREDUCEONE_H #define VGREDUCEONE_H #include bool reduce_vg_one(QString volumeGroupName, QString physicalVolumeName); #endif kvpm-0.8.6/kvpm/mkfs.cpp0000644000175000017500000005443211733530416015362 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "mkfs.h" #include #include #include #include #include "logvol.h" #include "misc.h" #include "processprogress.h" #include "storagepartition.h" MkfsDialog::MkfsDialog(LogVol *const volume, QWidget *parent) : KDialog(parent) { m_path = volume->getMapperPath(); const long stride_size = 1; // volume->getSegmentStripeSize( 0 ); <-- must convert to blocks! const long stride_count = volume->getSegmentStripes(0); if( hasInitialErrors( volume->isMounted() ) ) m_bailout = true; else{ m_bailout = false; buildDialog(stride_size, stride_count); } } MkfsDialog::MkfsDialog(StoragePartition *const partition, QWidget *parent) : KDialog(parent) { m_path = partition->getName(); const long stride_size = 1; const long stride_count = 1; if( hasInitialErrors( partition->isMounted() ) ) m_bailout = true; else{ m_bailout = false; buildDialog(stride_size, stride_count); } } // Determines if there is any point to calling up the dialog at all bool MkfsDialog::hasInitialErrors(const bool mounted) { const QString warning_message = i18n("Writing a new file system on %1 " "will delete any existing data on it.", m_path); const QString error_message = i18n("The volume: %1 is mounted. It must be " "unmounted before a new filesystem " "can be written on it", m_path); if(mounted){ KMessageBox::error(0, error_message); return true; } if(KMessageBox::warningContinueCancel(NULL, warning_message, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) == KMessageBox::Continue){ return false; } return true; } void MkfsDialog::buildDialog(const long strideSize, const long strideCount) { QWidget *const dialog_body = new QWidget; QVBoxLayout *const layout = new QVBoxLayout; dialog_body->setLayout(layout); QLabel *const label = new QLabel( i18n("Write filesystem on: %1", m_path) ); label->setAlignment(Qt::AlignCenter); layout->addSpacing(5); layout->addWidget(label); layout->addSpacing(5); m_tab_widget = new KTabWidget(this); m_tab_widget->addTab(generalTab(), i18n("Filesystem Type") ); m_tab_widget->addTab(advancedTab(strideSize, strideCount), i18n("Standard Ext Options") ); m_tab_widget->addTab(ext4Tab(), i18n("Additional Ext4 Options") ); layout->addWidget(m_tab_widget); setMainWidget(dialog_body); setCaption( i18n("Write Filesystem") ); enableOptions(true); connect(this, SIGNAL(okClicked()), this, SLOT(commitFilesystem())); } QWidget* MkfsDialog::generalTab() { QWidget *const tab = new QWidget(); QVBoxLayout *const layout = new QVBoxLayout(); QHBoxLayout *const upper_layout = new QHBoxLayout(); QHBoxLayout *const lower_layout = new QHBoxLayout(); layout->addLayout(upper_layout); layout->addLayout(lower_layout); QGroupBox *const radio_box = new QGroupBox( i18n("Select Filesystem") ); QGridLayout *const radio_layout = new QGridLayout; radio_box->setLayout(radio_layout); ext2 = new QRadioButton("ext2", this); ext3 = new QRadioButton("ext3", this); ext4 = new QRadioButton("ext4", this); btrfs = new QRadioButton("btrfs", this); ntfs = new QRadioButton("ntfs", this); reiser = new QRadioButton("reiser", this); reiser4 = new QRadioButton("reiser4", this); jfs = new QRadioButton("jfs", this); xfs = new QRadioButton("xfs", this); swap = new QRadioButton( i18n("Linux swap"), this); vfat = new QRadioButton("ms-dos", this); m_clobber_fs_check = new QCheckBox("Remove old filesystem first", this); m_clobber_fs_check->setChecked(true); radio_layout->addWidget(ext2, 1, 0); radio_layout->addWidget(ext3, 2, 0); radio_layout->addWidget(ext4, 3, 0); radio_layout->addWidget(btrfs, 4, 0); radio_layout->addWidget(ntfs, 4, 1); radio_layout->addWidget(reiser, 1, 1); radio_layout->addWidget(reiser4, 2, 1); radio_layout->addWidget(swap, 3, 1); radio_layout->addWidget(jfs, 1, 2); radio_layout->addWidget(xfs, 2, 2); radio_layout->addWidget(vfat, 3, 2); radio_layout->setRowStretch(5, 1); radio_layout->addWidget(m_clobber_fs_check, 6, 0, 1, -1, Qt::AlignLeft); ext4->setChecked(true); upper_layout->addWidget(radio_box); lower_layout->addStretch(); QHBoxLayout *const name_layout = new QHBoxLayout; QLabel *const name_label = new QLabel( i18n("Optional name or label: ") ); name_layout->addWidget(name_label); m_name_edit = new KLineEdit(); name_label->setBuddy(m_name_edit); name_layout->addWidget(m_name_edit); name_layout->addStretch(); lower_layout->addLayout(name_layout); lower_layout->addStretch(); layout->addStretch(); tab->setLayout(layout); connect(ext2, SIGNAL(toggled(bool)), this, SLOT(enableOptions(bool))); connect(ext3, SIGNAL(toggled(bool)), this, SLOT(enableOptions(bool))); connect(ext4, SIGNAL(toggled(bool)), this, SLOT(enableOptions(bool))); return tab; } QWidget* MkfsDialog::advancedTab(const long strideSize, const long strideCount) { QWidget *const tab = new QWidget(); QVBoxLayout *const layout = new QVBoxLayout(); QHBoxLayout *const top_layout = new QHBoxLayout(); top_layout->addStretch(); top_layout->addLayout(layout); top_layout->addStretch(); QLabel *const override_label = new QLabel( i18n("If enabled, these options override the mkfs defaults " "for ext2, ext3 and ext4 filesystems.") ); QHBoxLayout *const label_layout = new QHBoxLayout(); layout->addSpacing(10); layout->addLayout(label_layout); layout->addSpacing(10); override_label->setWordWrap(true); override_label->setAlignment(Qt::AlignCenter); label_layout->addSpacing(40); label_layout->addWidget(override_label); label_layout->addSpacing(40); QHBoxLayout *const lower_layout = new QHBoxLayout(); layout->addLayout(lower_layout); QVBoxLayout *const left_layout = new QVBoxLayout; QVBoxLayout *const right_layout = new QVBoxLayout; lower_layout->addLayout(left_layout); lower_layout->addLayout(right_layout); m_stripe_box = stripeBox(strideSize, strideCount); right_layout->addWidget(m_stripe_box); m_misc_options_box = miscOptionsBox(); m_base_options_box = baseOptionsBox(); left_layout->addWidget(m_base_options_box); right_layout->addWidget(m_misc_options_box); left_layout->addStretch(); right_layout->addStretch(); connect(m_sparse_super_check, SIGNAL(toggled(bool)), this, SLOT(enableOptions(bool))); connect(m_inode_edit, SIGNAL(textEdited(QString)), m_total_edit, SLOT(clear())); connect(m_total_edit, SIGNAL(textEdited(QString)), m_inode_edit, SLOT(clear())); tab->setLayout(top_layout); return tab; } QWidget* MkfsDialog::ext4Tab() { QWidget *const tab = new QWidget(); QVBoxLayout *const layout = new QVBoxLayout(); QHBoxLayout *const lower_layout = new QHBoxLayout(); QLabel *const override_label = new QLabel( i18n("If enabled, these options override the mkfs defaults " "for settings that only apply to ext4 filesystems.") ); QHBoxLayout *const label_layout = new QHBoxLayout(); layout->addSpacing(10); layout->addLayout(label_layout); layout->addSpacing(10); override_label->setWordWrap(true); override_label->setAlignment(Qt::AlignCenter); label_layout->addSpacing(40); label_layout->addWidget(override_label); label_layout->addSpacing(40); m_ext4_options_box = ext4OptionsBox(); lower_layout->addStretch(); lower_layout->addWidget(m_ext4_options_box); lower_layout->addStretch(); layout->addStretch(); layout->addLayout(lower_layout); layout->addStretch(); tab->setLayout(layout); return tab; } QGroupBox* MkfsDialog::miscOptionsBox() { QGroupBox *const misc_box = new QGroupBox("Inodes and Blocks"); misc_box->setCheckable(true); misc_box->setChecked(false); QVBoxLayout *const misc_layout = new QVBoxLayout; QHBoxLayout *const reserved_layout = new QHBoxLayout; QHBoxLayout *const block_layout = new QHBoxLayout; QHBoxLayout *const isize_layout = new QHBoxLayout; QHBoxLayout *const inode_layout = new QHBoxLayout; QHBoxLayout *const total_layout = new QHBoxLayout; QLabel *label; label = new QLabel( i18n("Reserved space: ") ); reserved_layout->addWidget(label); m_reserved_spin = new QSpinBox; label->setBuddy(m_reserved_spin); m_reserved_spin->setRange(0, 100); m_reserved_spin->setValue(5); m_reserved_spin->setPrefix("%"); reserved_layout->addWidget(m_reserved_spin); reserved_layout->addStretch(); misc_layout->addLayout(reserved_layout); label = new QLabel( i18n("Block size: ") ); block_layout->addWidget(label); m_block_combo = new KComboBox(); label->setBuddy(m_block_combo); m_block_combo->insertItem(0, i18nc("Let the program decide", "default") ); m_block_combo->insertItem(1,"1024 KiB"); m_block_combo->insertItem(2,"2048 KiB"); m_block_combo->insertItem(3,"4096 KiB"); m_block_combo->setInsertPolicy(KComboBox::NoInsert); m_block_combo->setCurrentIndex(0); block_layout->addWidget(m_block_combo); block_layout->addStretch(); misc_layout->addLayout(block_layout); label = new QLabel( i18n("Inode size: ") ); isize_layout->addWidget(label); m_inode_combo = new KComboBox(); label->setBuddy(m_inode_combo); m_inode_combo->insertItem(0, i18nc("Let the program decide", "default") ); m_inode_combo->insertItem(1,"128 Bytes"); m_inode_combo->insertItem(2,"256 Bytes"); m_inode_combo->insertItem(3,"512 Bytes"); m_inode_combo->setInsertPolicy(KComboBox::NoInsert); m_inode_combo->setCurrentIndex(0); isize_layout->addWidget(m_inode_combo); isize_layout->addStretch(); misc_layout->addLayout(isize_layout); label = new QLabel( i18n("Bytes / inode: ") ); inode_layout->addWidget(label); m_inode_edit = new KLineEdit(); m_inode_edit->setPlaceholderText(i18nc("Let the program decide", "default") ); label->setBuddy(m_inode_edit); QIntValidator *inode_validator = new QIntValidator(m_inode_edit); m_inode_edit->setValidator(inode_validator); inode_validator->setBottom(0); inode_layout->addWidget(m_inode_edit); misc_layout->addLayout(inode_layout); label = new QLabel( i18n("Total inodes: ") ); total_layout->addWidget(label); m_total_edit = new KLineEdit(); m_total_edit->setPlaceholderText(i18nc("Let the program decide", "default") ); label->setBuddy(m_total_edit); QIntValidator *const total_validator = new QIntValidator(m_total_edit); m_total_edit->setValidator(total_validator); total_validator->setBottom(0); total_layout->addWidget(m_total_edit); misc_layout->addLayout(total_layout); misc_box->setLayout(misc_layout); return misc_box; } QGroupBox* MkfsDialog::ext4OptionsBox() { QGroupBox *const options_box = new QGroupBox("Ext4 Options"); QVBoxLayout *const options_layout = new QVBoxLayout(); options_box->setCheckable(true); options_box->setChecked(false); m_extent_check = new QCheckBox( i18n("Use extents") ); m_flex_bg_check = new QCheckBox( i18n("Flexible block group layout") ); m_huge_file_check = new QCheckBox( i18n("Enable files over 2TB") ); m_uninit_bg_check = new QCheckBox( i18n("Don't init all block groups") ); m_lazy_itable_init_check = new QCheckBox( i18n("Don't init all inodes") ); m_dir_nlink_check = new QCheckBox( i18n("Unlimited subdirectories") ); m_extra_isize_check = new QCheckBox( i18n("Nanosecond timestamps") ); options_layout->addWidget(m_flex_bg_check); options_layout->addWidget(m_huge_file_check); options_layout->addWidget(m_uninit_bg_check); options_layout->addWidget(m_lazy_itable_init_check); options_layout->addWidget(m_dir_nlink_check); options_layout->addWidget(m_extra_isize_check); options_layout->addWidget(m_extent_check); options_box->setLayout(options_layout); return options_box; } QGroupBox* MkfsDialog::stripeBox(const long strideSize, const long strideCount) { QGroupBox *const stripe_box = new QGroupBox( i18n("Striping") ); stripe_box->setCheckable(true); stripe_box->setChecked(false); QVBoxLayout *const stripe_layout = new QVBoxLayout(); stripe_box->setLayout(stripe_layout); QLabel *label; QHBoxLayout *const stride_layout = new QHBoxLayout; label = new QLabel( i18n("Stride size in blocks: ") ); stride_layout->addWidget(label); m_stride_edit = new KLineEdit( QString("%1").arg(strideSize) ); label->setBuddy(m_stride_edit); QIntValidator *const stride_validator = new QIntValidator(m_stride_edit); m_stride_edit->setValidator(stride_validator); stride_validator->setBottom(512); stride_layout->addWidget(m_stride_edit); stride_layout->addStretch(); stripe_layout->addLayout(stride_layout); QHBoxLayout *const count_layout = new QHBoxLayout; label = new QLabel( i18n("Strides per stripe: ") ); count_layout->addWidget(label); m_count_edit = new KLineEdit( QString("%1").arg(strideCount) ); label->setBuddy(m_count_edit); QIntValidator *const count_validator = new QIntValidator(m_count_edit); m_count_edit->setValidator(count_validator); count_validator->setBottom(1); count_layout->addWidget(m_count_edit); count_layout->addStretch(); stripe_layout->addLayout(count_layout); return stripe_box; } QGroupBox* MkfsDialog::baseOptionsBox() { QGroupBox *const options_box = new QGroupBox("Basic Options"); QVBoxLayout *const options_layout = new QVBoxLayout(); options_box->setCheckable(true); options_box->setChecked(false); m_ext_attr_check = new QCheckBox( i18n("Extended attributes") ); m_resize_inode_check = new QCheckBox( i18n("Resize inode") ); m_resize_inode_check->setEnabled(false); m_dir_index_check = new QCheckBox( i18n("Directory B-Tree index") ); m_filetype_check = new QCheckBox( i18n("Store filetype in inode") ); m_sparse_super_check = new QCheckBox( i18n("Sparse superblock") ); options_layout->addWidget(m_ext_attr_check); options_layout->addWidget(m_resize_inode_check); options_layout->addWidget(m_dir_index_check); options_layout->addWidget(m_filetype_check); options_layout->addWidget(m_sparse_super_check); options_box->setLayout(options_layout); return options_box; } void MkfsDialog::enableOptions(bool) { if( ext2->isChecked() || ext3->isChecked() || ext4->isChecked() ){ m_tab_widget->setTabEnabled(1, true); m_base_options_box->setEnabled(true); m_misc_options_box->setEnabled(true); if( ext4->isChecked() ){ m_ext4_options_box->setEnabled(true); m_tab_widget->setTabEnabled(2, true); } else{ m_ext4_options_box->setEnabled(false); m_ext4_options_box->setChecked(false); m_tab_widget->setTabEnabled(2, false); } } else{ m_base_options_box->setEnabled(false); m_base_options_box->setChecked(false); m_ext4_options_box->setEnabled(false); m_ext4_options_box->setChecked(false); m_misc_options_box->setEnabled(false); m_tab_widget->setTabEnabled(1, false); m_tab_widget->setTabEnabled(2, false); } if( m_sparse_super_check->isChecked() ) m_resize_inode_check->setEnabled(true); else{ m_resize_inode_check->setEnabled(false); m_resize_inode_check->setChecked(false); } } void MkfsDialog::clobberFilesystem() { if( !isBusy(m_path) ){ // Last check -- just to be sure QByteArray zero_array(128 * 1024, '\0'); if(m_clobber_fs_check->isChecked()){ QFile *const device = new QFile(m_path); if( device->open(QIODevice::ReadWrite) ){ // nuke the old filesystem with zeros device->write(zero_array); device->flush(); device->close(); } delete device; } } } void MkfsDialog::commitFilesystem() { hide(); clobberFilesystem(); QStringList arguments; QStringList mkfs_options; QStringList ext_options; QStringList extended_options; QString type; if(ext2->isChecked()){ type = "ext2"; } else if(ext3->isChecked()){ type = "ext3"; } else if(ext4->isChecked()){ type = "ext4"; } else if(btrfs->isChecked()){ type = "btrfs"; } else if(ntfs->isChecked()){ type = "ntfs"; mkfs_options << "-Q" << "--quiet"; } else if(reiser->isChecked()){ mkfs_options << "-q"; type = "reiserfs"; } else if(reiser4->isChecked()){ mkfs_options << "-y"; type = "reiser4"; } else if(jfs->isChecked()){ mkfs_options << "-q"; type = "jfs"; } else if(xfs->isChecked()){ mkfs_options << "-q" << "-f"; type = "xfs"; } else if(swap->isChecked()){ type = "swap"; } else if(vfat->isChecked()){ type = "vfat"; } else{ type = "ext3"; qDebug() << "Reached the default else in mkfs.cpp. How did that happen?"; } if(type == "reiserfs"){ if( !(m_name_edit->text()).isEmpty() ) mkfs_options << "--label" << m_name_edit->text(); } else if(type == "vfat"){ if( !(m_name_edit->text()).isEmpty() ) mkfs_options << "-n" << m_name_edit->text(); } else if( (type == "reiser4") || (type == "jfs") || (type == "xfs") || (type == "btrfs") || (type == "ntfs") ){ if( !(m_name_edit->text()).isEmpty() ) mkfs_options << "-L" << m_name_edit->text(); } else if( (type == "ext2") || (type == "ext3") || (type == "ext4") ){ if( m_misc_options_box->isChecked() ){ mkfs_options << "-m" << QString("%1").arg(m_reserved_spin->value()); if(m_block_combo->currentIndex() > 0) mkfs_options << "-b" << m_block_combo->currentText().remove("KiB").trimmed(); if( !m_inode_edit->text().isEmpty() ) mkfs_options << "-i" << m_inode_edit->text(); else if ( !m_total_edit->text().isEmpty() ) mkfs_options << "-N" << m_total_edit->text(); if( m_inode_combo->currentIndex() > 0 ) mkfs_options << "-I" << m_inode_combo->currentText().remove("Bytes").trimmed(); } if(m_stripe_box->isChecked() || m_ext4_options_box->isChecked() ){ mkfs_options << "-E"; if( m_stripe_box->isChecked() ){ extended_options << QString("stride=%1").arg( m_stride_edit->text() ); extended_options << QString("stripe_width=%1").arg( m_count_edit->text() ); } if( m_ext4_options_box->isChecked() ){ if( m_lazy_itable_init_check->isChecked() ) extended_options << "lazy_itable_init=1"; else extended_options << "lazy_itable_init=0"; } mkfs_options << extended_options.join(","); } if( !(m_name_edit->text()).isEmpty() ) mkfs_options << "-L" << m_name_edit->text(); if( m_base_options_box->isChecked() ){ if( m_ext_attr_check->isChecked() ) ext_options << "ext_attr"; else ext_options << "^ext_attr"; if( m_resize_inode_check->isChecked() ) ext_options << "resize_inode"; else ext_options << "^resize_inode"; if( m_dir_index_check->isChecked() ) ext_options << "dir_index"; else ext_options << "^dir_index"; if( m_filetype_check->isChecked() ) ext_options << "filetype"; else ext_options << "^filetype"; if( m_sparse_super_check->isChecked() ) ext_options << "sparse_super"; else ext_options << "^sparse_super"; } if( m_ext4_options_box->isChecked() && ext4->isChecked() ){ if( m_extent_check->isChecked() ) ext_options << "extent"; else ext_options << "^extent"; if( m_flex_bg_check->isChecked() ) ext_options << "flex_bg"; else ext_options << "^flex_bg"; if( m_huge_file_check->isChecked() ) ext_options << "huge_file"; else ext_options << "^huge_file"; if( m_uninit_bg_check->isChecked() ) ext_options << "uninit_bg"; else ext_options << "^uninit_bg"; if( m_dir_nlink_check->isChecked() ) ext_options << "dir_nlink"; else ext_options << "^dir_nlink"; if( m_extra_isize_check->isChecked() ) ext_options << "extra_isize"; else ext_options << "^extra_isize"; } } if(type != "swap"){ arguments << "mkfs" << "-t" << type;; if( mkfs_options.size() ) arguments << mkfs_options; if( ext_options.size() ) arguments << "-O" << ext_options.join(","); arguments << m_path; } else{ arguments << "mkswap" << m_path; } ProcessProgress mkfs(arguments, true); } bool MkfsDialog::bailout() { return m_bailout; } kvpm-0.8.6/kvpm/vgtree.h0000644000175000017500000000242211733530416015353 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGTREE_H #define VGTREE_H #include #include #include #include #include #include class LogVol; class VolGroup; class VGTree : public QTreeWidget { Q_OBJECT VolGroup *m_vg; bool m_init; // is this the initial building of the tree or a reload? bool m_show_percent, m_show_total, m_use_si_units, m_show_both; int m_fs_warn_percent; void setupContextMenu(); QTreeWidgetItem *loadItem(LogVol *lv, QTreeWidgetItem *item); void insertChildItems(LogVol *parentVolume, QTreeWidgetItem *parentItem); void insertSegmentItems(LogVol *logicalVolume, QTreeWidgetItem *item); void setViewConfig(); public: VGTree(VolGroup *const group); void loadData(); private slots: void popupContextMenu(QPoint point); void adjustColumnWidth(QTreeWidgetItem *); }; #endif kvpm-0.8.6/kvpm/pvextend.h0000644000175000017500000000074011733530416015715 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PVEXTEND_H #define PVEXTEND_H #include bool pv_extend(QString path); #endif kvpm-0.8.6/kvpm/vgimport.cpp0000644000175000017500000000162311733530416016263 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vgimport.h" #include #include #include #include "logvol.h" #include "processprogress.h" #include "volgroup.h" bool import_vg(VolGroup *const volumeGroup) { QStringList args; const QString message = i18n("Import volume group: %1?", volumeGroup->getName()); if(KMessageBox::questionYesNo( 0, message) == KMessageBox::Yes){ args << "vgimport" << volumeGroup->getName(); ProcessProgress remove(args); return true; } else{ return false; } } kvpm-0.8.6/kvpm/progressbox.h0000644000175000017500000000145711733530416016443 0ustar benscottbenscott/* * * * Copyright (C) 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PROGRESSBOX_H #define PROGRESSBOX_H #include #include #include #include class ProgressBox : public QFrame { QLabel *m_message; QProgressBar *m_progressbar; public: explicit ProgressBox(QWidget *parent = NULL); void reset(); void setText(const QString text); void setRange(const int start, const int end); void setValue(const int value); }; #endif kvpm-0.8.6/kvpm/mounttables.cpp0000644000175000017500000002423311733530416016753 0ustar benscottbenscott/* * * * Copyright (C) 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "mounttables.h" #include #include #include #include #include #include #include "logvol.h" #include "mountentry.h" #include "storagepartition.h" namespace { // The following functions are only needed locally const int BUFF_LEN = 2000; // Enough? bool isWritableFile(QString mtab) { QFileInfo fi = QFileInfo(mtab); if( fi.isSymLink() ) return false; else return fi.isWritable(); } /* This function generates an mntent structure from its parameters and returns it */ mntent* buildMntent(const QString device, const QString mountPoint, const QString type, const QString options, const int dumpFreq, const int pass) { QByteArray device_array = device.toLocal8Bit(); QByteArray mount_point_array = mountPoint.toLocal8Bit(); QByteArray type_array = type.toLocal8Bit(); QByteArray options_array = options.toLocal8Bit(); mntent *mount_entry = new mntent; mount_entry->mnt_fsname = device_array.data(); mount_entry->mnt_dir = mount_point_array.data(); mount_entry->mnt_type = type_array.data(); mount_entry->mnt_opts = options_array.data(); mount_entry->mnt_freq = dumpFreq; mount_entry->mnt_passno = pass; return mount_entry; } mntent *copyMntent(mntent *const entry) { if( entry == NULL ) return NULL; mntent *new_entry = new mntent; new_entry->mnt_fsname = new char[BUFF_LEN]; new_entry->mnt_dir = new char[BUFF_LEN]; new_entry->mnt_type = new char[BUFF_LEN]; new_entry->mnt_opts = new char[BUFF_LEN]; strncpy(new_entry->mnt_fsname, entry->mnt_fsname, BUFF_LEN); strncpy(new_entry->mnt_dir, entry->mnt_dir, BUFF_LEN); strncpy(new_entry->mnt_type, entry->mnt_type, BUFF_LEN); strncpy(new_entry->mnt_opts, entry->mnt_opts, BUFF_LEN); new_entry->mnt_freq = entry->mnt_freq; new_entry->mnt_passno = entry->mnt_passno; return new_entry; } void deleteMntent(mntent *const entry) { if(entry != NULL){ delete entry->mnt_fsname; delete entry->mnt_dir; delete entry->mnt_type; delete entry->mnt_opts; delete entry; } } } MountTables::MountTables() { } MountTables::~MountTables() { for(int x = 0; x < m_fstab_list.size(); x++) delete (m_fstab_list[x]); for(int x = 0; x < m_mount_list.size(); x++) delete (m_mount_list[x]); } void MountTables::loadData() { for(int x = m_fstab_list.size() - 1; x >= 0; x--) m_fstab_list.takeAt(x)->deleteLater(); for(int x = m_mount_list.size() - 1; x >= 0; x--) m_mount_list.takeAt(x)->deleteLater(); QString line; QFile file("/proc/self/mountinfo"); if ( file.open(QIODevice::ReadOnly | QIODevice::Text) ){ QTextStream in( &file ); while ( ! (line = in.readLine()).isEmpty() ){ QStringList subs = line.section(' ', 2, 2).split(':'); // major and minor dev numbers m_mount_list.append( new MountEntry(line, subs[0].toInt(), subs[1].toInt()) ); } } file.close(); for(int x = m_mount_list.size() - 1; x >= 0; x--){ int pos = 1; if( m_mount_list[x]->getMountPosition() > 0 ) continue; for(int y = x - 1; y >= 0; y--){ if(m_mount_list[y]->getMountPoint() == m_mount_list[x]->getMountPoint() ) m_mount_list[y]->setMountPosition(++pos); } if(pos > 1) m_mount_list[x]->setMountPosition(1); } mntent *entry; FILE *fp; if( (fp = setmntent(_PATH_FSTAB, "r") ) ){ while( (entry = getmntent(fp)) ) m_fstab_list.append( new MountEntry(entry) ); endmntent(fp); } } // Major and minor numbers don't change when lv or vg names are changed // Also lookups by fs uuid won't work with snaps since they will match the origin // and each other. QList MountTables::getMtabEntries(const int major, const int minor) { QList device_mounts; for(int x = 0; x < m_mount_list.size(); x++){ if(m_mount_list[x]->getMajorNumber() == major && m_mount_list[x]->getMinorNumber() == minor){ device_mounts.append( new MountEntry(m_mount_list[x]) ); } } return device_mounts; } QString MountTables::getFstabMountPoint(LogVol *const lv) { return getFstabMountPoint( lv->getMapperPath(), lv->getFilesystemLabel(), lv->getFilesystemUuid() ); } QString MountTables::getFstabMountPoint(StoragePartition *const partition) { return getFstabMountPoint( partition->getName(), partition->getFilesystemLabel(), partition->getFilesystemUuid() ); } QString MountTables::getFstabMountPoint(const QString name, const QString label, const QString uuid) { QString entry_name; MountEntry *entry; QListIterator entry_itr(m_fstab_list); while( entry_itr.hasNext() ){ entry = entry_itr.next(); entry_name = entry->getDeviceName(); if( entry_name.startsWith("UUID=", Qt::CaseInsensitive) ){ entry_name = entry_name.remove(0, 5); if( entry_name == uuid ) return entry->getMountPoint(); } else if( entry_name.startsWith("LABEL=", Qt::CaseInsensitive) ){ entry_name = entry_name.remove(0, 6); if( entry_name == label ) return entry->getMountPoint(); } else if( entry_name == name ) return entry->getMountPoint(); } return QString(); } // Adds an entry (a mntent struct) into the mount table file, usually /etc/mtab. bool MountTables::addEntry(const QString device, const QString mountPoint, const QString type, const QString options, const int dumpFreq, const int pass) { if(!isWritableFile(_PATH_MOUNTED)) return true; QByteArray device_array = device.toLocal8Bit(); QByteArray mount_point_array = mountPoint.toLocal8Bit(); QByteArray type_array = type.toLocal8Bit(); QByteArray options_array = options.toLocal8Bit(); const struct mntent mount_entry = { device_array.data(), mount_point_array.data(), type_array.data(), options_array.data(), dumpFreq, pass }; FILE *const fp = setmntent(_PATH_MOUNTED, "a"); if(fp){ if( addmntent(fp, &mount_entry) ){ fchmod(fileno(fp), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); fsync(fileno(fp)); endmntent(fp); return false; } else{ // success fchmod(fileno(fp), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); fsync( fileno(fp) ); endmntent(fp); return true; } } else return false; } bool MountTables::removeEntry(const QString mountPoint) { if(!isWritableFile(_PATH_MOUNTED)) return true; QList mount_entry_list; mntent *mount_entry; QByteArray new_path(_PATH_MOUNTED); new_path.append(".new"); const char *mount_table_old = _PATH_MOUNTED; const char *mount_table_new = new_path.data(); FILE *fp_old = setmntent(mount_table_old, "r"); /* Multiple devices can be mounted on a directory but only the last one mounted will be unmounted at one time. So only the last mtab entry gets deleted upon unmounting */ while( (mount_entry = copyMntent(getmntent(fp_old))) ) mount_entry_list.append(mount_entry); for( int x = mount_entry_list.size() - 1; x >= 0; x--){ if( QString( (mount_entry_list[x])->mnt_dir ) == mountPoint ){ deleteMntent(mount_entry_list.takeAt(x)); break; } } endmntent(fp_old); FILE *fp_new = setmntent(mount_table_new, "w"); for(int x = 0; x < mount_entry_list.size(); x++){ const mntent *entry = mount_entry_list[x]; addmntent(fp_new, entry); } fchmod(fileno(fp_new), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); fsync( fileno(fp_new) ); endmntent(fp_new); KDE_rename(mount_table_new, mount_table_old); return true; } bool MountTables::renameEntries(const QString oldName, const QString newName) { if(!isWritableFile(_PATH_MOUNTED)) return true; QList mount_entry_list; mntent *mount_entry; mntent *temp_entry; QByteArray new_path(_PATH_MOUNTED); new_path.append(".new"); const char *mount_table_old = _PATH_MOUNTED; const char *mount_table_new = new_path.data(); FILE *fp_old = setmntent(mount_table_old, "r"); while( (mount_entry = copyMntent( getmntent(fp_old)) ) ){ if( QString(mount_entry->mnt_fsname) != oldName ){ mount_entry_list.append(mount_entry); } else{ temp_entry = buildMntent( newName, mount_entry->mnt_dir, mount_entry->mnt_type, mount_entry->mnt_opts, mount_entry->mnt_freq, mount_entry->mnt_passno ); deleteMntent(mount_entry); mount_entry_list.append(temp_entry); } } endmntent(fp_old); FILE *fp_new = setmntent(mount_table_new, "w"); for(int x = 0; x < mount_entry_list.size(); x++){ const mntent *entry = mount_entry_list[x]; addmntent(fp_new, entry); } fchmod(fileno(fp_new), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); fsync(fileno(fp_new)); endmntent(fp_new); KDE_rename(mount_table_new, mount_table_old); return true; } kvpm-0.8.6/kvpm/vgmerge.cpp0000644000175000017500000000542111733530416016050 0ustar benscottbenscott/* * * * Copyright (C) 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vgmerge.h" #include #include #include #include "logvol.h" #include "masterlist.h" #include "volgroup.h" #include "processprogress.h" bool merge_vg(VolGroup *volumeGroup) { const QStringList vg_names = MasterList::getVgNames(); const QStringList lv_names = volumeGroup->getLvNames(); if( vg_names.size() < 2 ){ KMessageBox::error(0, i18n("There is no other volume group to merge with") ); return false; } for(int x = 0; x < lv_names.size(); x++){ if( (volumeGroup->getLvByName(lv_names[x]))->isActive() ){ KMessageBox::error(0, i18n("The volume group to merge must not have active logical volumes") ); return false; } } VGMergeDialog dialog(volumeGroup); dialog.exec(); if(dialog.result() == QDialog::Accepted){ ProcessProgress vgmerge( dialog.arguments() ); return true; } else return false; } VGMergeDialog::VGMergeDialog(VolGroup *volumeGroup, QWidget *parent) : KDialog(parent), m_vg(volumeGroup) { setWindowTitle( i18n("Merge Volume Group") ); QWidget *dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *layout = new QVBoxLayout(); dialog_body->setLayout(layout); QLabel *name_label = new QLabel( i18n("Volume Group: %1", m_vg->getName() ) ); name_label->setAlignment(Qt::AlignCenter); layout->addWidget(name_label); QGroupBox *target_group = new QGroupBox( i18n("Merge Volume Group With:") ); QVBoxLayout *target_group_layout = new QVBoxLayout; target_group->setLayout(target_group_layout); layout->addWidget(target_group); m_target_combo = new KComboBox(); QStringList vg_names = MasterList::getVgNames(); for(int x = 0; x < vg_names.size(); x++){ // remove this groups own name from list if( m_vg->getName() != vg_names[x] ) m_target_combo->addItem( vg_names[x] ); } target_group_layout->addWidget(m_target_combo); m_autobackup = new QCheckBox("autobackup"); m_autobackup->setChecked(true); target_group_layout->addWidget(m_autobackup); } QStringList VGMergeDialog::arguments() { QStringList args; args << "vgmerge"; if(m_autobackup->isChecked()) args << "--autobackup" << "y"; else args << "--autobackup" << "n"; args << m_target_combo->currentText() << m_vg->getName(); return args; } kvpm-0.8.6/kvpm/maxfs.cpp0000644000175000017500000000763711733530416015545 0ustar benscottbenscott/* * * * Copyright (C) 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "maxfs.h" #include #include #include #include "fsextend.h" #include "logvol.h" #include "processprogress.h" #include "pvextend.h" #include "storagedevice.h" #include "storagepartition.h" bool max_fs(LogVol *logicalVolume) { const QString path = logicalVolume->getMapperPath(); const QString fs = logicalVolume->getFilesystem(); QString full_name = logicalVolume->getFullName(); full_name.remove('[').remove(']'); const QString warning = i18n("Extend the filesystem on: %1 to fill the entire volume?", ""+full_name+""); const QString error_message = i18n("Extending is only supported for ext2, ext3, ext4, jfs, xfs, ntfs and Reiserfs. " "The correct executables for file system extension must also be present"); if( !fs_can_extend(fs) ){ KMessageBox::error(0, error_message ); return false; } if(KMessageBox::warningYesNo(NULL, warning, QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QString(), KMessageBox::Dangerous) == KMessageBox::Yes){ return fs_extend(path, fs, logicalVolume->getMountPoints(), true); } else{ return false; } } bool max_fs(StoragePartition *partition) { const QString path = partition->getName(); const QString fs = partition->getFilesystem(); const QString error_message = i18n("Filesystem extending is only supported for ext2, ext3, ext4, jfs, xfs, ntfs and Reiserfs. " "Physical volumes can also be extended. " "The correct executables for file system extension must be present"); QString message; if( partition->isPhysicalVolume() ) message = i18n("Extend the physical volume on: %1 to fill the entire partition?", ""+path+""); else message = i18n("Extend the filesystem on: %1 to fill the entire partition?", ""+path+""); if( ! ( fs_can_extend(fs) || partition->isPhysicalVolume() ) ){ KMessageBox::error(0, error_message ); return false; } if(KMessageBox::warningYesNo(NULL, message, QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QString(), KMessageBox::Dangerous) == KMessageBox::Yes){ if( partition->isPhysicalVolume() ){ return pv_extend(path); } else{ return fs_extend(path, fs, partition->getMountPoints(), false); } } return false; } bool max_fs(StorageDevice *device) { const QString path = device->getName(); const QString warning = i18n("Extend the physical volume on: %1 to fill the entire partition?", ""+path+""); if( ! device->isPhysicalVolume() ) return false; if(KMessageBox::warningYesNo(NULL, warning, QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QString(), KMessageBox::Dangerous) == KMessageBox::Yes){ return pv_extend(path); } return false; } kvpm-0.8.6/kvpm/storagedevice.cpp0000644000175000017500000000702511733530416017242 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include #include #include "storagedevice.h" #include "storagepartition.h" #include "physvol.h" #include "mounttables.h" StorageDevice::StorageDevice( PedDevice *const pedDevice, const QList pvList, MountTables *const mountTables) : QObject() { PedPartition *part = NULL; PedDisk *disk = NULL; PedGeometry geometry; PedPartitionType part_type; long long length; m_freespace_count = 0; m_sector_size = pedDevice->sector_size; m_physical_sector_size = pedDevice->phys_sector_size; m_hardware = QString(pedDevice->model); m_device_size = (pedDevice->length) * m_sector_size; m_device_path = QString("%1").arg(pedDevice->path); if( pedDevice->read_only ) m_is_writable = false; else m_is_writable = true; m_is_busy = ped_device_is_busy(pedDevice); m_is_pv = false; m_pv = NULL; for(int x = 0; x < pvList.size(); x++){ if(m_device_path == pvList[x]->getName()){ m_pv = pvList[x]; m_is_pv = true; } } disk = ped_disk_new(pedDevice); if( disk && !m_is_pv ){ PedDiskFlag cylinder_flag = ped_disk_flag_get_by_name("cylinder_alignment"); if( ped_disk_is_flag_available(disk, cylinder_flag) ){ ped_disk_set_flag(disk, cylinder_flag, 0); } m_disk_label = QString( disk->type->name ); while( (part = ped_disk_next_partition (disk, part)) ){ geometry = part->geom; length = geometry.length * m_sector_size; part_type = part->type; // ignore freespace less than 3 megs if( !( (part_type & PED_PARTITION_METADATA) || ( (part_type & PED_PARTITION_FREESPACE) && (length < (0x300000))))){ if( part_type & PED_PARTITION_FREESPACE ) m_freespace_count++; m_storage_partitions.append(new StoragePartition(part, m_freespace_count, pvList, mountTables)); } } } else if (m_is_pv) m_disk_label = "physical volume"; else m_disk_label = "unknown"; } StorageDevice::~StorageDevice() { if( !m_storage_partitions.isEmpty() ){ for(int x = m_storage_partitions.size() - 1; x >= 0; x--) delete m_storage_partitions[x]; } } QString StorageDevice::getName() { return m_device_path; } QString StorageDevice::getDiskLabel() { return m_disk_label; } QString StorageDevice::getHardware() { return m_hardware; } QList StorageDevice::getStoragePartitions() { return m_storage_partitions; } int StorageDevice::getPartitionCount() { return m_storage_partitions.size(); } int StorageDevice::getRealPartitionCount() { return m_storage_partitions.size() - m_freespace_count; } long long StorageDevice::getSize() { return m_device_size; } long long StorageDevice::getSectorSize() { return m_sector_size; } long long StorageDevice::getPhysicalSectorSize() { return m_physical_sector_size; } bool StorageDevice::isWritable() { return m_is_writable; } bool StorageDevice::isBusy() { return m_is_busy; } bool StorageDevice::isPhysicalVolume() { return m_is_pv; } PhysVol* StorageDevice::getPhysicalVolume() { return m_pv; } kvpm-0.8.6/kvpm/partremove.h0000644000175000017500000000103511733530416016242 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PARTREMOVE_H #define PARTREMOVE_H #include class StoragePartition; bool remove_partition(StoragePartition *const partition); #endif kvpm-0.8.6/kvpm/fsreduce.h0000644000175000017500000000117611733530416015664 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef FSREDUCE_H #define FSREDUCE_H #include bool fs_can_reduce(const QString fs); long long fs_reduce(const QString path, const long long new_size, const QString fs); long long get_min_fs_size(const QString path, const QString fs); #endif kvpm-0.8.6/kvpm/unmount.cpp0000644000175000017500000001375211733530416016127 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "unmount.h" #include #include #include #include #include #include #include "logvol.h" #include "mountentry.h" #include "mounttables.h" #include "misc.h" #include "storagepartition.h" #include "volgroup.h" UnmountDialog::UnmountDialog(LogVol *const volume, QWidget *parent) : KDialog(parent) { const QList entries = volume->getMountEntries(); const QString name = volume->getName(); buildDialog(name, entries); } UnmountDialog::UnmountDialog(StoragePartition *const partition, QWidget *parent) : KDialog(parent) { const QList entries = partition->getMountEntries(); const QString name = partition->getName(); buildDialog(name, entries); } bool UnmountDialog::bailout() { return m_bailout; } void UnmountDialog::buildDialog(QString const device, const QList entries) { m_bailout = false; if( entries.isEmpty() ){ m_bailout = true; hide(); KMessageBox::error(0, i18n("Can not unmount: %1, it does not seem to be mounted", device) ); return; } else if( entries.size() == 1 ){ m_single = true; if( entries[0]->getMountPosition() > 1 ){ m_bailout = true; hide(); KMessageBox::error(0, i18n("Can not unmount: %1, another volume or " "device is mounted over the same " "mount point and must be unmounted first", device) ); return; } } else{ m_single = false; bool unmountable = false; QListIterator entry(entries); while( entry.hasNext() ){ if( entry.next()->getMountPosition() < 2 ) unmountable = true; } if(!unmountable){ m_bailout = true; KMessageBox::error(0, i18n("Can not unmount: %1, another volume or " "device is mounted over the same " "mount point and must be unmounted first", device) ); return; } } setCaption( i18n("Unmount Filesystem") ); m_mp = entries[0]->getMountPoint(); NoMungeCheck *check; bool checks_disabled = false; QWidget *dialog_body = new QWidget(this); setMainWidget(dialog_body); QLabel *label; QVBoxLayout *layout = new QVBoxLayout(); dialog_body->setLayout(layout); label = new QLabel( i18n("Unmount Filesystem") ); label->setAlignment(Qt::AlignCenter); layout->addWidget(label); layout->addSpacing(10); if(m_single){ setButtons(KDialog::Yes | KDialog::No); layout->addWidget(new QLabel( i18n("%1 is mounted on: %2", device, m_mp) )); layout->addWidget(new QLabel( i18n("Do you wish to unmount it?") )); } else{ setButtons(KDialog::Ok | KDialog::Cancel); enableButtonOk(false); label = new QLabel( i18n( "%1 is mounted at multiple locatations.", device ) ); layout->addWidget(label); label = new QLabel( i18n( "Select the ones to unmount:" ) ); layout->addWidget(label); for(int x = 0; x < entries.size(); x++){ check = new NoMungeCheck( entries[x]->getMountPoint() ); if( entries[x]->getMountPosition() > 1 ){ check->setChecked(false); check->setEnabled(false); checks_disabled = true; } layout->addWidget(check); m_check_list.append(check); connect(check, SIGNAL(clicked()), this, SLOT(resetOkButton())); } if(checks_disabled){ label = new QLabel( i18n( "Some selections have been disabled. Another " "device or volume is mounted over the same " "mount point and must be unmounted first" ) ); label->setWordWrap(true); layout->addWidget(label); } } connect(this, SIGNAL(yesClicked()), this, SLOT(commitChanges())); connect(this, SIGNAL(okClicked()), this, SLOT(commitChanges())); QListIterator entry(entries); while( entry.hasNext() ) delete entry.next(); } void UnmountDialog::resetOkButton() { bool enable = false; QListIterator itr(m_check_list); while( itr.hasNext() ){ if( itr.next()->isChecked() ) enable = true; } enableButtonOk(enable); } void UnmountDialog::commitChanges() { NoMungeCheck *cb; QListIterator cb_itr(m_check_list); QByteArray mp_qba; hide(); if(m_single){ mp_qba = m_mp.toLocal8Bit(); if(umount2(mp_qba.data(), 0)){ KMessageBox::error(0, i18n("Unmounting %1 failed with error number: %2 %3", m_mp, errno, QString(strerror(errno)))); } else MountTables::removeEntry(m_mp); } else{ while( cb_itr.hasNext() ){ cb = cb_itr.next(); if( cb->isChecked() ) { mp_qba = cb->getUnmungedText().toLocal8Bit(); if(umount2(mp_qba.data(), 0)){ KMessageBox::error(0, i18n("Unmounting %1 failed with error number: %2 %3", m_mp, errno, QString(strerror(errno)))); } else MountTables::removeEntry(cb->getUnmungedText()); } } } } kvpm-0.8.6/kvpm/fsck.cpp0000644000175000017500000000426111733530416015343 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "fsck.h" #include #include #include #include "logvol.h" #include "processprogress.h" #include "storagepartition.h" bool fsck(const QString path){ QStringList arguments, output; arguments << "fsck" << "-fp" << path; ProcessProgress fsck_fs(arguments); output = fsck_fs.programOutput(); if ( fsck_fs.exitCode() > 1 ) // 0 means no errors, 1 means minor errors fixed return false; else return true; } bool manual_fsck(LogVol *const logicalVolume){ const QString path = logicalVolume->getMapperPath(); const QString warning = i18n("Run 'fsck -fp' to check the filesystem on volume %1?", path); if(KMessageBox::warningYesNo(NULL, warning, QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QString(), KMessageBox::Dangerous) == KMessageBox::Yes){ return fsck(path); } else{ return false; } } bool manual_fsck(StoragePartition *const partition){ const QString path = partition->getName(); const QString warning = i18n("Run 'fsck -fp' to check the filesystem on partition %1?", path); if(KMessageBox::warningYesNo(NULL, warning, QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QString(), KMessageBox::Dangerous) == KMessageBox::Yes){ return fsck(path); } else{ return false; } } kvpm-0.8.6/kvpm/main.cpp0000644000175000017500000000332511733530416015341 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include #include #include #include #include #include #include #include #include "executablefinder.h" #include "masterlist.h" #include "topwindow.h" class VolGroup; class PhysVol; class LogVol; TopWindow *MainWindow; int main(int argc, char **argv) { KAboutData aboutData( "kvpm", 0, ki18n("kvpm"), "0.8.6", ki18n( "Linux volume and partition manager for KDE"), KAboutData::License_GPL_V3, ki18n("Copyright (c) 2008, 2009, 2010, 2011, 2012 Benjamin Scott") ); KCmdLineArgs::init( argc, argv, &aboutData); KApplication kvpm; if( geteuid() != 0 ){ KMessageBox::error( 0, i18n("This program must be run as root (uid = 0) "), i18n("Insufficient Privilege") ); return 0; } QPixmap splashImage(KGlobal::dirs()->findResource("data", "kvpm/images/splash.png")); KSplashScreen splash(splashImage); splash.show(); ExecutableFinder *executable_finder = new ExecutableFinder(); MasterList *master_list = new MasterList(); TopWindow *top_window = new TopWindow(master_list, executable_finder, NULL); MainWindow = top_window; top_window->setAutoSaveSettings(); top_window->show(); splash.finish(top_window); return kvpm.exec(); } kvpm-0.8.6/kvpm/vgcreate.h0000644000175000017500000000337611733530416015670 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGCREATE_H #define VGCREATE_H #include #include #include #include #include #include #include #include class StorageDevice; class StoragePartition; class PvGroupBox; class VGCreateDialog : public KDialog { Q_OBJECT bool m_bailout; QLabel *m_pv_label, *m_total_available_label, *m_total_selected_label; PvGroupBox *m_pv_checkbox; KLineEdit *m_vg_name, *m_max_lvs, *m_max_pvs; QCheckBox *m_clustered, *m_auto_backup, *m_max_lvs_check, *m_max_pvs_check; KComboBox *m_extent_size, *m_extent_suffix; QRegExpValidator *m_validator; void buildDialog(QList devices, QList partitions); void getUsablePvs(QList &devices, QList &partitions); void limitExtentSize(int); public: explicit VGCreateDialog(QWidget *parent = NULL); VGCreateDialog(StorageDevice *const device, StoragePartition *const partition, QWidget *parent = NULL); bool bailout(); private slots: //void limitLogicalVolumes(int boxstate); //void limitPhysicalVolumes(int boxstate); void validateOK(); void commitChanges(); void extentSizeChanged(); }; #endif kvpm-0.8.6/kvpm/kvpmconfigdialog.h0000644000175000017500000000315611733530416017407 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef KVPMCONFIGDIALOG_H #define KVPMCONFIGDIALOG_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include class ExecutableFinder; class KvpmConfigDialog: public KConfigDialog { Q_OBJECT QTableWidget *m_executables_table; KEditListBox *m_edit_list; KConfigSkeleton *m_skeleton; ExecutableFinder *m_executable_finder; QTabWidget *generalPage(); QWidget *colorsPage(); QTabWidget *programsPage(); QGroupBox *allGroup(); QGroupBox *deviceGroup(); QGroupBox *logicalGroup(); QGroupBox *physicalGroup(); QGroupBox *pvPropertiesGroup(); QGroupBox *lvPropertiesGroup(); QGroupBox *devicePropertiesGroup(); QWidget *treesTab(); QWidget *propertiesTab(); void fillExecutablesTable(); public: KvpmConfigDialog( QWidget *parent, const QString name, KConfigSkeleton *const skeleton, ExecutableFinder *const executableFinder ); ~KvpmConfigDialog(); public slots: void updateSettings(); }; #endif kvpm-0.8.6/kvpm/devicepropertiesstack.cpp0000644000175000017500000000442211733530416021016 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "devicepropertiesstack.h" #include #include "deviceproperties.h" #include "devicetree.h" #include "storagedevice.h" #include "storagepartition.h" /* This stack widget simply displays some information about the drive or device selected in the tree view. If nothing is selected an empty widget is used. */ DevicePropertiesStack::DevicePropertiesStack(QWidget *parent) : QStackedWidget(parent) { addWidget( getDefaultWidget() ); setCurrentIndex(0); } void DevicePropertiesStack::changeDeviceStackIndex(QTreeWidgetItem *item) { setCurrentIndex(0); if( !item ) return; const QString device_path = item->data(0, Qt::DisplayRole).toString(); const int list_size = m_device_path_list.size(); for(int x = 0; x < list_size; x++){ if( m_device_path_list[x] == device_path ) setCurrentIndex (x); } } void DevicePropertiesStack::loadData( QList devices) { QWidget *stackWidget; QList partitions; m_device_path_list.clear(); for(int x = count() - 1; x >= 0; x--){ // delete old member widgets stackWidget = widget(x); removeWidget(stackWidget); delete stackWidget; } for(int x = devices.size() - 1; x >= 0; x--){ m_device_path_list.append( devices[x]->getName() ); addWidget( new DeviceProperties( devices[x] ) ); partitions << devices[x]->getStoragePartitions() ; for(int n = 0; n < partitions.size(); n++ ){ m_device_path_list.append( partitions[n]->getName() ); addWidget( new DeviceProperties( partitions[n] ) ); } partitions.clear(); } addWidget( getDefaultWidget() ); setCurrentIndex(0); } QWidget *DevicePropertiesStack::getDefaultWidget() { QWidget *dw = new QWidget(); QHBoxLayout *layout = new QHBoxLayout(); layout->addWidget( new QLabel("Random String For The Layout") ); dw->setLayout(layout); return dw; } kvpm-0.8.6/kvpm/logvol.h0000644000175000017500000001530211733530416015362 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef LOGVOL_H #define LOGVOL_H #include #include #include class MountEntry; class MountTables; class VolGroup; class Segment; // This class takes the handles for volumes provided // by liblvm2app and converts the information into // a more Qt/KDE friendly form. class LogVol { VolGroup *m_vg; QList m_segments; QList m_mount_entries; QList m_lv_children; // For a mirror the children are the legs and log // Snapshots are also children -- see m_snap_container MountTables *m_tables; LogVol *m_lv_parent; // NULL if this is the 'top' lv QString m_lv_full_name; // volume_group/logical_volume QString m_lv_name; // name of this logical volume QString m_lv_mapper_path; // full path to volume, ie: /dev/vg1/lvol1 QString m_lv_fs; // Filesystem on volume, if known QString m_lv_fs_label; // Filesystem label or name QString m_lv_fs_uuid; // Filesystem uuid QString m_origin; // the origin if this is a snapshot QString m_log; // The mirror log, if this is a mirror QString m_type; // the type of volume QString m_policy; // the allocation policy QString m_state; // the lv state QString m_uuid; QStringList m_tags; QString m_fstab_mount_point; QStringList m_mount_points; // empty if not mounted double m_snap_percent; // the percentage used, if this is a snapshot double m_copy_percent; // the percentage of extents moved, if pvmove underway long long m_size; // size in bytes long long m_total_size; // size in bytes, size of all children (mirror legs/logs and snaps) added together long long m_extents; // size in extents long long m_fs_size; // fs size in bytes long long m_fs_used; // bytes used up in fs int m_seg_total; // total number of segments in logical volume unsigned long m_major_device; // Unix device major number, if set unsigned long m_minor_device; // Unix device minor number, if set int m_log_count; // if a mirror -- how many logs int m_mirror_count; // if mirror -- how many legs bool m_virtual; // virtual volume bool m_under_conversion; // Is going to be a mirrored volume bool m_mirror; // Is a mirrored volume bool m_mirror_leg; // Is one of the underlying legs of a mirrored volume bool m_mirror_log; // Is the log for a mirrored volume bool m_fixed, m_persistent; // fix the device minor and major number bool m_alloc_locked; // allocation type is fixed when pvmove is underway // (and maybe other times) bool m_active; bool m_mounted; // has a mounted filesystem bool m_open; // device is open bool m_orphan; // virtual device with no pvs bool m_pvmove; // is a pvmove temporary volume bool m_snap; // is a snapshot volume bool m_snap_container; // is a fake lv that contains the real lv and its snapshots as children bool m_is_origin; bool m_writable; bool m_valid; // is a valid snap bool m_merging; // is snap or snap origin that is merging void countLegsAndLogs(); void processSegments(lv_t lvmLV); QStringList removePvNames(QStringList names); // list lv children that are lvs and not devices or pvmove* QList getLvmSnapshots(vg_t lvmVG); void insertChildren(lv_t lvmLV, vg_t lvmVG); void calculateTotalSize(); public: LogVol(lv_t lvmLV, vg_t lvmVG, VolGroup *const vg, LogVol *const lvParent, MountTables *const tables, bool orphan = false); ~LogVol(); void rescan(lv_t lvmLV, vg_t lvmVG); QList getChildren(); // just the children -- not grandchildren etc. QList takeChildren(); // removes the children from the logical volume QList getAllChildrenFlat(); // All children, grandchildren etc. un-nested. QList getSnapshots(); // This will work the same for snapcontainers or the real lv LogVol *getParent(); // NULL if this is a "top level" lv VolGroup* getVg(); QString getName(); QString getFullName(); QString getFilesystem(); QString getFilesystemLabel(); QString getFilesystemUuid(); QString getMapperPath(); QString getPolicy(); QString getState(); QString getType(); QString getOrigin(); // The name of the parent volume to a snapshot QString getUuid(); int getSegmentCount(); int getSegmentStripes(const int segment); // The number of stipes in the segment int getSegmentStripeSize(const int segment); long long getSegmentSize(const int segment); long long getSegmentExtents(const int segment); QList getSegmentStartingExtent(const int segment); QStringList getPvNames(const int segment); QStringList getPvNamesAll(); // full path of physical volumes for all segments QStringList getPvNamesAllFlat(); // full path of physical volumes including child lvs, un-nested QStringList getMountPoints(); QList getMountEntries(); // Calling function must delete these objects in the list QString getFstabMountPoint(); QStringList getTags(); long long getSpaceUsedOnPv(const QString physicalVolume); long long getExtents(); long long getSize(); long long getTotalSize(); long long getFilesystemSize(); long long getFilesystemUsed(); double getSnapPercent(); double getCopyPercent(); unsigned long getMinorDevice(); unsigned long getMajorDevice(); int getLogCount(); int getMirrorCount(); int getSnapshotCount(); bool isActive(); bool isFixed(); bool isLocked(); bool isMerging(); bool isMirror(); bool isMirrorLeg(); bool isMirrorLog(); bool isMounted(); bool isOpen(); bool isOrigin(); bool isOrphan(); bool isPersistent(); bool isPvmove(); bool isSnap(); bool isSnapContainer(); bool isUnderConversion(); bool isValid(); bool isVirtual(); bool isWritable(); bool hasMissingVolume(); }; #endif kvpm-0.8.6/kvpm/mounttables.h0000644000175000017500000000252411733530416016417 0ustar benscottbenscott/* * * * Copyright (C) 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef MOUNTTABLES_H #define MOUNTTABLES_H #include #include class LogVol; class StoragePartition; class MountEntry; class MountTables { QList m_mount_list; QList m_fstab_list; QString getFstabMountPoint(const QString name, const QString label, const QString uuid); public: MountTables(); ~MountTables(); void loadData(); QList getMtabEntries(const int major, const int minor); // The list entries must be freed/deleted by the reciever QString getFstabMountPoint(LogVol *const lv); QString getFstabMountPoint(StoragePartition *const partition); static bool addEntry(const QString device, const QString mountPoint, const QString type, const QString options, const int dumpFreq, const int pass); static bool renameEntries(const QString oldName, const QString newName); static bool removeEntry(const QString mountPoint); }; #endif kvpm-0.8.6/kvpm/deviceproperties.cpp0000644000175000017500000002050511733530416017770 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "deviceproperties.h" #include #include #include #include #include #include "misc.h" #include "mountentry.h" #include "physvol.h" #include "storagedevice.h" #include "storagepartition.h" DeviceProperties::DeviceProperties(StorageDevice *const device, QWidget *parent) : QWidget(parent) { QVBoxLayout *const layout = new QVBoxLayout(); layout->setSpacing(0); layout->setMargin(0); setLayout(layout); layout->addWidget( generalFrame(device) ); layout->addWidget( hardwareFrame(device) ); if( device->isPhysicalVolume() ) layout->addWidget( pvFrame( device->getPhysicalVolume() ) ); layout->addStretch(); } DeviceProperties::DeviceProperties(StoragePartition *const partition, QWidget *parent) : QWidget(parent) { QVBoxLayout *const layout = new QVBoxLayout(); setLayout(layout); layout->setSpacing(0); layout->setMargin(0); layout->addWidget( generalFrame(partition) ); KConfigSkeleton skeleton; bool show_mount, show_fsuuid, show_fslabel; skeleton.setCurrentGroup("DeviceProperties"); skeleton.addItemBool("dp_mount", show_mount, true); skeleton.addItemBool("dp_fsuuid", show_fsuuid, false); skeleton.addItemBool("dp_fslabel", show_fslabel, false); if( partition->isMountable() ){ if(show_mount) layout->addWidget( mpFrame(partition) ); if(show_fsuuid || show_fslabel) layout->addWidget( fsFrame(partition, show_fsuuid, show_fslabel) ); } else if( partition->isPhysicalVolume() ) layout->addWidget( pvFrame( partition->getPhysicalVolume() ) ); layout->addStretch(); } QFrame *DeviceProperties::generalFrame(StoragePartition *const partition) { QFrame *const frame = new QFrame; QVBoxLayout *const layout = new QVBoxLayout(); frame->setLayout(layout); frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); QLabel *const name_label = new QLabel( QString("%1").arg( partition->getName() ) ); name_label->setAlignment( Qt::AlignCenter ); layout->addWidget(name_label); layout->addWidget( new QLabel( i18n("First sector: %1", partition->getTrueFirstSector() ) ) ); if( partition->isFreespace() ){ layout->addWidget( new QLabel( i18n("First aligned: %1 (to 1 MiB)", partition->getFirstSector() ) ) ); } layout->addWidget( new QLabel( i18n("Last sector: %1", partition->getLastSector() ) ) ); if( partition->isNormal() || partition->isLogical() ){ layout->addWidget( new QLabel() ); const QStringList flags = partition->getFlags(); layout->addWidget( new QLabel( i18n("Flags: %1", flags.join(", ") ) ) ); } return frame; } QFrame *DeviceProperties::mpFrame(StoragePartition *const partition) { QFrame *const frame = new QFrame(); QVBoxLayout *const layout = new QVBoxLayout(); frame->setLayout(layout); frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); const QList entries = partition->getMountEntries(); QLabel *label; if( entries.size() <= 1 ) label = new QLabel( i18n("Mount point") ); else label = new QLabel( i18n("Mount points") ); label->setAlignment(Qt::AlignCenter); layout->addWidget(label); if( !entries.isEmpty() ){ for(int x = 0; x < entries.size(); x++){ const QString mp = entries[x]->getMountPoint(); const int pos = entries[x]->getMountPosition(); if( entries[x]->getMountPosition() > 0 ){ label = new QLabel(mp + QString("<%1>").arg(pos)); label->setToolTip(mp + QString("<%1>").arg(pos)); layout->addWidget(label); } else{ label = new QLabel(mp); label->setToolTip(mp); layout->addWidget(label); } } } else{ layout->addWidget( new QLabel( i18n("Not mounted") ) ); } QListIterator entry_itr(entries); while( entry_itr.hasNext() ) delete entry_itr.next(); return frame; } QFrame *DeviceProperties::generalFrame(StorageDevice *const device) { QFrame *frame = new QFrame; QVBoxLayout *layout = new QVBoxLayout(); frame->setLayout(layout); frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); QLabel *name_label = new QLabel( QString("%1").arg( device->getName() ) ); name_label->setAlignment( Qt::AlignCenter ); layout->addWidget(name_label); if( device->isPhysicalVolume() ) layout->addWidget( new QLabel( device->getDiskLabel() ) ); else layout->addWidget( new QLabel( i18n("Partition table: %1", device->getDiskLabel() ) ) ); layout->addWidget( new QLabel( i18n("Logical sector size: %1", device->getSectorSize() ) ) ); layout->addWidget( new QLabel( i18n("Physical sector size: %1", device->getPhysicalSectorSize() ) ) ); layout->addWidget( new QLabel( i18n("Sectors: %1", device->getSize() / device->getSectorSize() ) ) ); if( !device->isWritable() ) layout->addWidget( new QLabel( i18nc("May be read and not written", "Read only") ) ); else layout->addWidget( new QLabel( i18n("Read/write") ) ); if( device->isBusy() ) layout->addWidget( new QLabel( i18n("Busy: Yes") ) ); else layout->addWidget( new QLabel( i18n("Busy: No") ) ); return frame; } QFrame *DeviceProperties::fsFrame(StoragePartition *const partition, const bool showFsUuid, const bool showFsLabel) { QLabel *label; QFrame *const frame = new QFrame; QVBoxLayout *const layout = new QVBoxLayout(); frame->setLayout(layout); frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); if(showFsLabel){ label = new QLabel( i18n("Filesystem LABEL") ); label->setAlignment( Qt::AlignCenter ); layout->addWidget(label); label = new QLabel( partition->getFilesystemLabel() ); layout->addWidget(label); } if(showFsUuid){ label = new QLabel( i18n("Filesystem UUID") ); label->setAlignment( Qt::AlignCenter ); layout->addWidget(label); const QStringList uuid = splitUuid( partition->getFilesystemUuid() ); layout->addWidget( new QLabel(uuid[0]) ); layout->addWidget( new QLabel(uuid[1]) ); } return frame; } QFrame *DeviceProperties::pvFrame(PhysVol *const pv) { QFrame *const frame = new QFrame; QVBoxLayout *const layout = new QVBoxLayout(); frame->setLayout(layout); frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); QLabel *label; label = new QLabel( i18n("Physical volume") ); label->setAlignment( Qt::AlignCenter ); layout->addWidget(label); if( pv->isActive() ) label = new QLabel( i18n("State: active") ); else label = new QLabel( i18n("State: inactive") ); layout->addWidget(label); label = new QLabel( i18n("UUID") ); label->setAlignment( Qt::AlignCenter ); layout->addWidget(label); const QStringList uuid = splitUuid( pv->getUuid() ); layout->addWidget( new QLabel(uuid[0]) ); layout->addWidget( new QLabel(uuid[1]) ); return frame; } QFrame *DeviceProperties::hardwareFrame(StorageDevice *const device) { QFrame *const frame = new QFrame; QVBoxLayout *const layout = new QVBoxLayout(); frame->setLayout(layout); frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); QLabel *label; label = new QLabel( i18n("Hardware") ); label->setAlignment( Qt::AlignCenter ); layout->addWidget(label); label = new QLabel( device->getHardware() ); label->setWordWrap(true); layout->addWidget(label); return frame; } kvpm-0.8.6/kvpm/maintabwidget.cpp0000644000175000017500000000423511733530416017235 0ustar benscottbenscott/* * * * Copyright (C) 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "maintabwidget.h" #include #include #include "volumegrouptab.h" MainTabWidget::MainTabWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *const layout = new QVBoxLayout(); m_tab_widget = new KTabWidget(); m_tab_widget->setMovable(false); m_tab_widget->setTabsClosable(false); layout->addWidget(m_tab_widget); m_unmunged_text.clear(); setLayout(layout); connect(m_tab_widget, SIGNAL(currentChanged(int)), this, SLOT(indexChanged(int))); } QString MainTabWidget::getUnmungedText(const int index) { return m_unmunged_text[index]; } void MainTabWidget::appendVolumeGroupTab(VolumeGroupTab *const page, const QIcon &icon, const QString &label ) { m_tab_widget->insertTab( m_tab_widget->count(), (QWidget *)page, icon, label ); m_unmunged_text.append(label); m_vg_tabs.append(page); } void MainTabWidget::appendDeviceTab(DeviceTab *const page, const QString & label ) { m_tab_widget->insertTab( m_tab_widget->count(), (QWidget *) page, label ); m_unmunged_text.append(label); } void MainTabWidget::deleteTab(const int index) { m_tab_widget->widget(index)->deleteLater(); m_tab_widget->removeTab(index); m_unmunged_text.removeAt(index); m_vg_tabs.removeAt(index - 1); } QWidget *MainTabWidget::getWidget(const int index) { return m_tab_widget->widget(index); } int MainTabWidget::getCount() { return m_tab_widget->count(); } int MainTabWidget::getCurrentIndex() { return m_tab_widget->currentIndex(); } VolumeGroupTab *MainTabWidget::getVolumeGroupTab(const int index) { return m_vg_tabs[index]; } void MainTabWidget::setIcon(const int index, const QIcon &icon) { m_tab_widget->setTabIcon(index, icon); } void MainTabWidget::indexChanged(int) { emit currentIndexChanged(); } kvpm-0.8.6/kvpm/lvremove.h0000644000175000017500000000135411733530416015721 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef LVREMOVE_H #define LVREMOVE_H #include #include class LogVol; class LVRemoveDialog : public KDialog { Q_OBJECT bool m_bailout; QString m_name; LogVol *m_lv; public: explicit LVRemoveDialog(LogVol *const lv, QWidget *parent = 0); bool bailout(); private slots: void commitChanges(); }; #endif kvpm-0.8.6/kvpm/pvpropertiesstack.cpp0000644000175000017500000000526511733530416020212 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "pvpropertiesstack.h" #include #include "logvol.h" #include "physvol.h" #include "pvproperties.h" #include "volgroup.h" PVPropertiesStack::PVPropertiesStack(VolGroup *volumeGroup, QWidget *parent) : QFrame(parent), m_vg(volumeGroup) { m_vscroll = new QScrollArea; m_stack_widget = NULL; QVBoxLayout *const vlayout = new QVBoxLayout(); QHBoxLayout *const hlayout = new QHBoxLayout(); vlayout->setMargin(0); vlayout->setSpacing(0); m_pv_label = new QLabel(); m_pv_label->setAlignment(Qt::AlignCenter); vlayout->addSpacing(2); vlayout->addWidget(m_pv_label); vlayout->addSpacing(2); vlayout->addWidget(m_vscroll); vlayout->addLayout(hlayout); setLayout(vlayout); m_vscroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_vscroll->setBackgroundRole(QPalette::Base); m_vscroll->setAutoFillBackground(true); m_vscroll->verticalScrollBar()->setBackgroundRole(QPalette::Window); m_vscroll->verticalScrollBar()->setAutoFillBackground(true); } /* If *item points to a volume we set the widget stack to the widget with that volume's information. Else we set the stack widget index to -1, nothing */ void PVPropertiesStack::changePVStackIndex(QTreeWidgetItem *item, QTreeWidgetItem*) { const QList devices = m_vg->getPhysicalVolumes(); if( !m_stack_widget ) return; if(item){ const QString pv_uuid = QVariant( item->data(0, Qt::UserRole ) ).toString(); for(int x = devices.size() - 1; x >= 0; x--){ if( pv_uuid == devices[x]->getUuid() ){ m_stack_widget->setCurrentIndex(x); m_pv_label->setText( "" + devices[x]->getName() + "" ); } } } else{ m_stack_widget->setCurrentIndex(-1); m_pv_label->setText(""); } } void PVPropertiesStack::loadData() { const QList devices = m_vg->getPhysicalVolumes(); m_stack_widget = new QStackedWidget; for(int x = 0; x < devices.size(); x++) m_stack_widget->addWidget( new PVProperties(devices[x]) ); if( devices.size() ) m_stack_widget->setCurrentIndex(0); m_vscroll->setWidget(m_stack_widget); m_vscroll->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); m_vscroll->setWidgetResizable(true); m_vscroll->setFrameShape(QFrame::NoFrame); } kvpm-0.8.6/kvpm/pvtree.cpp0000644000175000017500000002524411733530416015726 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "pvtree.h" #include #include #include #include #include #include "logvol.h" #include "masterlist.h" #include "processprogress.h" #include "pvmove.h" #include "pvchange.h" #include "physvol.h" #include "topwindow.h" #include "vgreduce.h" #include "vgreduceone.h" #include "volgroup.h" /* This is the physical volume tree list on the volume group tab */ PVTree::PVTree(VolGroup *const group, QWidget *parent) : QTreeWidget(parent), m_vg(group) { QStringList header_labels; m_context_menu = NULL; setColumnCount(6); QTreeWidgetItem *item; header_labels << i18nc("The name of the device", "Name") << i18n("Size") << i18nc("Unused space", "Remaining") << i18nc("Space used up", "Used") << i18n("State") << i18n("Allocatable") << i18n("Tags") << i18n("Logical volumes"); item = new QTreeWidgetItem((QTreeWidgetItem *)0, header_labels); for(int column = 0; column < 7; column++) item->setTextAlignment(column, Qt::AlignCenter); sortByColumn(0, Qt::AscendingOrder); item->setToolTip(0, i18n("Physical volume device")); item->setToolTip(1, i18n("Total size of physical volume")); item->setToolTip(2, i18n("Free space on physical volume")); item->setToolTip(3, i18n("Space used on physical volume")); item->setToolTip(4, i18n("A physcial volume is active if it has logical volumes that are active")); item->setToolTip(5, i18n("If physical volume allows more extents to be allocated")); item->setToolTip(6, i18n("Optional tags for physical volume")); item->setToolTip(7, i18n("Logical volumes on physical volume")); setHeaderItem(item); } void PVTree::loadData() { QList pv_tree_items; QList lvs = m_vg->getLogicalVolumesFlat(); QList pvs = m_vg->getPhysicalVolumes(); QStringList lv_name_list; QStringList pv_name_list; QString device_name; PhysVol *pv; QStringList pv_data; QTreeWidgetItem *item; QString old_current_pv_name; if( currentItem() ) old_current_pv_name = currentItem()->data(0, 0).toString(); clear(); setupContextMenu(); setSortingEnabled(false); setViewConfig(); KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); for(int n = 0; n < pvs.size(); n++){ pv = pvs[n]; pv_data.clear(); device_name = pv->getName(); pv_data << device_name << locale->formatByteSize( pv->getSize() ) << locale->formatByteSize( pv->getRemaining() ) << locale->formatByteSize( pv->getSize() - pv->getRemaining() ); if( pv->isActive() ) pv_data << "Active"; else pv_data << "Inactive"; if( pv->isAllocatable() ) pv_data << "Yes"; else pv_data << "No"; pv_data << pv->getTags().join(", "); /* here we get the names of logical volumes associated with the physical volume */ lv_name_list.clear(); for(int x = 0; x < lvs.size() ; x++){ pv_name_list = lvs[x]->getPvNamesAll(); for(int y = 0; y < pv_name_list.size() ; y++) if( device_name == pv_name_list[y] ) lv_name_list.append( lvs[x]->getName() ); } /* next we remove duplicate entries */ lv_name_list.sort(); if( lv_name_list.size() > 1 ) for(int x = lv_name_list.size() - 2; x >= 0; x--) if( lv_name_list[x] == lv_name_list[x + 1] ) lv_name_list.removeAt(x + 1); pv_data << lv_name_list.join(", "); item = new QTreeWidgetItem((QTreeWidgetItem *)0, pv_data); if( device_name == "unknown device" ) item->setIcon(0, KIcon("exclamation")); else item->setIcon(0, KIcon()); item->setData(0, Qt::UserRole, pv->getUuid()); item->setData(1, Qt::UserRole, pv->getSize()); item->setData(2, Qt::UserRole, pv->getRemaining()); item->setData(3, Qt::UserRole, (pv->getSize() - pv->getRemaining())); if( pv->isActive() ){ item->setToolTip(4, i18n("Active")); item->setIcon(4, KIcon("lightbulb")); } else{ item->setToolTip(4, i18n("Inactive")); item->setIcon(4, KIcon("lightbulb_off")); } for(int column = 1; column < 6; column++) item->setTextAlignment(column, Qt::AlignRight); item->setTextAlignment(6, Qt::AlignLeft); item->setTextAlignment(7, Qt::AlignLeft); pv_tree_items.append(item); } insertTopLevelItems(0, pv_tree_items); setSortingEnabled(true); for(int column = 0; column < 7; column++) if( !isColumnHidden(column) ) resizeColumnToContents(column); if( !pv_tree_items.isEmpty() && !old_current_pv_name.isEmpty() ){ bool match = false; for(int x = pv_tree_items.size() - 1; x >= 0; x--){ if( old_current_pv_name == pv_tree_items[x]->data(0, 0).toString() ){ setCurrentItem( pv_tree_items[x] ); match = true; break; } } if(!match){ setCurrentItem( pv_tree_items[0] ); scrollToItem(pv_tree_items[0], QAbstractItemView::EnsureVisible); } } else if( !pv_tree_items.isEmpty() ){ setCurrentItem( pv_tree_items[0] ); scrollToItem(pv_tree_items[0], QAbstractItemView::EnsureVisible); } return; } void PVTree::setupContextMenu() { setContextMenuPolicy(Qt::CustomContextMenu); pv_move_action = new QAction( i18n("Move physical extents"), this); vg_reduce_action = new QAction( i18n("Remove from volume group"), this); pv_change_action = new QAction( i18n("Change physical volume attributes"), this); if(m_context_menu) m_context_menu->deleteLater(); m_context_menu = new QMenu(this); m_context_menu->addAction(pv_move_action); m_context_menu->addAction(vg_reduce_action); m_context_menu->addAction(pv_change_action); // disconnect the last run's connections or they pile up. disconnect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(popupContextMenu(QPoint)) ); disconnect(pv_move_action, SIGNAL(triggered()), this, SLOT(movePhysicalExtents())); disconnect(vg_reduce_action, SIGNAL(triggered()), this, SLOT(reduceVolumeGroup())); disconnect(pv_change_action, SIGNAL(triggered()), this, SLOT(changePhysicalVolume())); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(popupContextMenu(QPoint)) ); connect(pv_move_action, SIGNAL(triggered()), this, SLOT(movePhysicalExtents())); connect(vg_reduce_action, SIGNAL(triggered()), this, SLOT(reduceVolumeGroup())); connect(pv_change_action, SIGNAL(triggered()), this, SLOT(changePhysicalVolume())); } void PVTree::popupContextMenu(QPoint point) { QTreeWidgetItem *item = itemAt(point); QStringList lvs; if(item && !m_vg->isExported()){ if( (QVariant(item->data(3, 0)).toString()) == "0" ){ // 0 = Zero used extents on pv m_pv_name = QVariant(item->data(0, 0)).toString(); pv_move_action->setEnabled(false); if(m_vg->getPvCount() > 1) vg_reduce_action->setEnabled(true); else vg_reduce_action->setEnabled(false); // can't remove last pv from group } else{ m_pv_name = QVariant(item->data(0, 0)).toString(); vg_reduce_action->setEnabled(false); if(m_vg->getPvCount() > 1){ // can't move extents if there isn't another volume to put them on if( QVariant(item->data(6, 0)).toString().contains("pvmove") ) // can't have more than one pvmove pv_move_action->setEnabled(false); // See physvol.cpp about removing this else pv_move_action->setEnabled(true); } else pv_move_action->setEnabled(false); } m_context_menu->setEnabled(true); m_context_menu->exec(QCursor::pos()); } else m_context_menu->setEnabled(false); // item = 0 if there is no item a that point } void PVTree::movePhysicalExtents() { PhysVol *pv = m_vg->getPvByName(m_pv_name); if(pv){ PVMoveDialog dialog(pv); if( !dialog.bailout() ) dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } void PVTree::reduceVolumeGroup() { if( reduce_vg_one(m_vg->getName(), m_pv_name) ) MainWindow->reRun(); } void PVTree::changePhysicalVolume() { PhysVol *pv = m_vg->getPvByName(m_pv_name); if(pv){ PVChangeDialog dialog(pv); dialog.exec(); if(dialog.result() == QDialog::Accepted){ ProcessProgress change_pv( dialog.arguments() ); MainWindow->reRun(); } } } void PVTree::setViewConfig() { KConfigSkeleton skeleton; bool changed = false; bool pvname, pvsize, pvremaining, pvused, pvstate, pvallocate, pvtags, pvlvnames; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", m_use_si_units, false); skeleton.setCurrentGroup("PhysicalTreeColumns"); skeleton.addItemBool( "pt_name", pvname, true ); skeleton.addItemBool( "pt_size", pvsize, true ); skeleton.addItemBool( "pt_remaining", pvremaining, true ); skeleton.addItemBool( "pt_used", pvused, false ); skeleton.addItemBool( "pt_state", pvstate, false ); skeleton.addItemBool( "pt_allocate", pvallocate, true ); skeleton.addItemBool( "pt_tags", pvtags, true ); skeleton.addItemBool( "pt_lvnames", pvlvnames, true ); if( !( !pvname == isColumnHidden(0) && !pvsize == isColumnHidden(1) && !pvremaining == isColumnHidden(2) && !pvused == isColumnHidden(3) && !pvstate == isColumnHidden(4) && !pvallocate == isColumnHidden(5) && !pvtags == isColumnHidden(6) && !pvlvnames == isColumnHidden(7) ) ) changed = true; if(changed){ setColumnHidden( 0, !pvname ); setColumnHidden( 1, !pvsize ); setColumnHidden( 2, !pvremaining ); setColumnHidden( 3, !pvused ); setColumnHidden( 4, !pvstate ); setColumnHidden( 5, !pvallocate ); setColumnHidden( 6, !pvtags ); setColumnHidden( 7, !pvlvnames ); } } kvpm-0.8.6/kvpm/sizeselectorbox.h0000644000175000017500000000531711733530416017311 0ustar benscottbenscott/* * * * Copyright (C) 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef SIZESELECTORBOX_H #define SIZESELECTORBOX_H #include #include #include #include #include #include #include class SizeSelectorBox : public QGroupBox { Q_OBJECT long long m_max_size; // max size possible in sectors or extents, not bytes etc. long long m_min_size; long long m_constrained_max; // working max less than or equal to m_max_size long long m_constrained_min; long long m_current_size; // current size in sectors or extents, not bytes etc. long long m_initial_size; // Starting size in sectors or extents, not bytes etc. long long m_unit_size; // the size of the extents or sectors in bytes QSlider *m_size_slider; KComboBox *m_suffix_combo; KLineEdit *m_size_edit; QDoubleValidator *m_size_validator; QCheckBox *m_shrink_box; // Allow partition/volume to shrink. QCheckBox *m_size_box; // Lock size to change QCheckBox *m_offset_box; // Lock offset to change. bool m_is_volume; // Is it a volume or a partition? bool m_is_offset; // Are we selecting the starting point offset of a partition? bool m_is_new; // New volume/partition, not resizing old one? bool m_start_locked; // Start out with the size check box checked bool m_is_valid; // Valid data? bool m_use_si_units; long long convertSizeToUnits(int index, double size); // ie: convert MiBs to sectors or extents void updateValidator(); void updateSlider(); void setConstraints(bool unlock); signals: void stateChanged(); public: SizeSelectorBox(long long unitSize, long long minSize, long long maxSize, long long initialSize, bool isVolume, bool isOffset, bool isNew = false, bool startLocked = false, QWidget *parent = 0); long long getCurrentSize(); long long getMinimumSize(); long long getMaximumSize(); void resetToInitial(); void setConstrainedMax(long long max); bool isLocked(); bool setCurrentSize(long long size); // return false if size is outside min or max bool isValid(); private slots: void setToSlider(int value); void setToEdit(QString size); void updateEdit(); void lock(bool lock); void lockShrink(bool lock); void disableLockShrink(bool disable); }; #endif kvpm-0.8.6/kvpm/lvremove.cpp0000644000175000017500000000741611733530416016261 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "lvremove.h" #include #include #include #include "logvol.h" #include "processprogress.h" LVRemoveDialog::LVRemoveDialog(LogVol *const lv, QWidget *parent) : KDialog(parent), m_lv(lv) { setButtons(KDialog::Yes | KDialog::No); setDefaultButton(KDialog::No); setCaption( i18n("Delete Volume") ); QWidget *const dialog_body = new QWidget(this); setMainWidget(dialog_body); m_name = m_lv->getName(); m_bailout = false; QHBoxLayout *const layout = new QHBoxLayout(); QVBoxLayout *const right_layout = new QVBoxLayout(); QLabel *const icon_label = new QLabel(); icon_label->setPixmap( KIcon("dialog-warning").pixmap(64, 64) ); layout->addWidget(icon_label); layout->addLayout(right_layout); QLabel *label; label = new QLabel("Confirm Volume Deletion"); label->setAlignment(Qt::AlignCenter); right_layout->addWidget(label); right_layout->addSpacing(20); QHBoxLayout *const lower_layout = new QHBoxLayout(); QVBoxLayout *const list_layout = new QVBoxLayout(); const QString snap_msg1 = i18n("The volume: %1 has snapshots.", m_name); const QString snap_msg2 = i18n("The following volumes will all be deleted:"); const QString msg1 = i18n("Delete the volume named: %1?", "" + m_name + ""); const QString msg2 = i18n("Any data on it will be lost."); if( m_lv->isOrigin() ){ right_layout->addWidget( new QLabel(snap_msg1) ); right_layout->addWidget( new QLabel(snap_msg2) ); right_layout->addWidget( new QLabel("") ); right_layout->addLayout(lower_layout); lower_layout->addSpacing(15); lower_layout->addLayout(list_layout); label = new QLabel("" + m_name + ""); label->setAlignment(Qt::AlignLeft); list_layout->addWidget(label); QListIterator snap_itr( m_lv->getSnapshots() ); LogVol *snap; while( snap_itr.hasNext() ){ snap = snap_itr.next(); if( snap->isMounted() ) m_bailout = true; label = new QLabel( "" + snap->getName() + "" ); label->setAlignment(Qt::AlignLeft); list_layout->addWidget(label); } right_layout->addWidget( new QLabel("") ); right_layout->addWidget( new QLabel( i18n("Are you certain you want to delete these volumes?") ) ); right_layout->addWidget( new QLabel( i18n("Any data on them will be lost.") ) ); } else{ right_layout->addWidget( new QLabel(msg1) ); right_layout->addWidget( new QLabel(msg2) ); } dialog_body->setLayout(layout); connect(this, SIGNAL(yesClicked()), this, SLOT(commitChanges())); if(m_bailout){ hide(); KMessageBox::error(this, i18n("A snapshot of this origin is busy or mounted. It can not be deleted.") ); } } bool LVRemoveDialog::bailout() { return m_bailout; } void LVRemoveDialog::commitChanges() { const QString full_name = m_lv->getFullName().remove('[').remove(']'); QStringList args; if( m_lv->isActive() && !m_lv->isSnap() ){ args << "lvchange" << "-an" << full_name; ProcessProgress deactivate(args); } args.clear(); args << "lvremove" << "--force" << full_name; ProcessProgress remove(args); return; } kvpm-0.8.6/kvpm/pvmove.cpp0000644000175000017500000003252511733530416015735 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "pvmove.h" #include #include #include #include #include #include #include "logvol.h" #include "masterlist.h" #include "misc.h" #include "processprogress.h" #include "physvol.h" #include "pvgroupbox.h" #include "volgroup.h" struct NameAndRange { QString name; // Physical volume name QString name_range; // name + range of extents ie: /dev/sda1:10-100 and just name if no range specified long long start; // Starting extent long long end; // Last extent }; bool restart_pvmove() { QStringList args; const QString message = i18n("Do you wish to restart all interrupted physical volume moves?"); if(KMessageBox::questionYesNo(0, message) == KMessageBox::Yes){ args << "pvmove"; ProcessProgress resize(args); return true; } else return false; } bool stop_pvmove() { QStringList args; const QString message = i18n("Do you wish to abort all physical volume moves currently in progress?"); if(KMessageBox::questionYesNo(0, message) == KMessageBox::Yes){ args << "pvmove" << "--abort"; ProcessProgress resize(args); return true; } else return false; } PVMoveDialog::PVMoveDialog(PhysVol *physicalVolume, QWidget *parent) : KDialog(parent) { m_vg = physicalVolume->getVg(); m_target_pvs = m_vg->getPhysicalVolumes(); m_move_lv = false; m_move_segment = false; m_bailout = false; const QString name = physicalVolume->getName(); const QList lvs = m_vg->getLogicalVolumes(); QStringList forbidden_targets; // A whole pv can't be moved to a pv it is striped with along any segment QStringList striped_targets; NameAndRange *nar = new NameAndRange; nar->name = name; nar->name_range = name; m_sources.append(nar); forbidden_targets.append(name); for(int x = lvs.size() - 1; x >= 0; x--){ for(int seg = lvs[x]->getSegmentCount() - 1; seg >= 0; seg--){ if( lvs[x]->getSegmentStripes(seg) > 1 ){ striped_targets = lvs[x]->getPvNames(seg); if( striped_targets.contains(name) ) forbidden_targets.append(striped_targets); } } } forbidden_targets.removeDuplicates(); for(int x = m_target_pvs.size() - 1 ; x >= 0; x--){ for(int y = forbidden_targets.size() - 1; y >= 0; y--){ if( m_target_pvs[x]->getName() == forbidden_targets[y] ){ m_target_pvs.removeAt(x); forbidden_targets.removeAt(y); break; } } } removeFullTargets(); if( !m_bailout ) buildDialog(); connect(this, SIGNAL(okClicked()), this, SLOT(commitMove())); } PVMoveDialog::PVMoveDialog(LogVol *logicalVolume, int segment, QWidget *parent) : KDialog(parent), m_lv(logicalVolume) { m_vg = m_lv->getVg(); m_move_lv = true; m_target_pvs = m_vg->getPhysicalVolumes(); m_bailout = false; if( segment >= 0 ){ setupSegmentMove(segment); m_move_segment = true; } else{ setupFullMove(); m_move_segment = false; } /* if there is only one source physical volumes possible on this logical volume then we eliminate it from the possible target pv list completely. */ if( m_sources.size() == 1 ){ for(int x = m_target_pvs.size() - 1; x >= 0; x--){ if( m_target_pvs[x]->getName() == m_sources[0]->name ) m_target_pvs.removeAt(x); } } /* If this is a segment move then all source pvs need to be removed from the target list */ if( m_move_segment ){ for(int x = m_target_pvs.size() - 1; x >= 0; x--){ for(int y = m_sources.size() - 1; y >= 0; y--){ if( m_target_pvs[x]->getName() == m_sources[y]->name ) m_target_pvs.removeAt(x); } } } removeFullTargets(); if( !m_bailout ) buildDialog(); connect(this, SIGNAL(okClicked()), this, SLOT(commitMove())); } void PVMoveDialog::removeFullTargets(){ for(int x = (m_target_pvs.size() - 1); x >= 0; x--){ if( m_target_pvs[x]->getRemaining() <= 0 ) m_target_pvs.removeAt(x); } /* If there is only one physical volume in the group or they are all full then a pv move will have no place to go */ if(m_target_pvs.size() < 1){ KMessageBox::error(NULL, i18n("There are no available physical volumes with space to move to")); m_bailout = true; } } PVMoveDialog::~PVMoveDialog() { for(int x = 0; x < m_sources.size(); x++) delete m_sources[x]; } void PVMoveDialog::buildDialog() { bool use_si_units; KConfigSkeleton skeleton; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", use_si_units, false); KLocale *const locale = KGlobal::locale(); if(use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); QLabel *label; NoMungeRadioButton *radio_button; setWindowTitle( i18n("Move Physical Volume Extents") ); QWidget *dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *layout = new QVBoxLayout; dialog_body->setLayout(layout); if(m_move_lv){ label = new QLabel( i18n("Move only physical extents on:") ); label->setAlignment(Qt::AlignCenter); layout->addWidget(label); label = new QLabel("" + m_lv->getFullName() + ""); label->setAlignment(Qt::AlignCenter); layout->addWidget(label); } QGroupBox *radio_group = new QGroupBox( i18n("Source Physical Volumes") ); QGridLayout *radio_layout = new QGridLayout(); radio_group->setLayout(radio_layout); layout->addWidget(radio_group); QHBoxLayout *lower_layout = new QHBoxLayout; layout->addLayout(lower_layout); m_pv_checkbox = new PvGroupBox(m_target_pvs); lower_layout->addWidget(m_pv_checkbox); const int radio_count = m_sources.size(); if( radio_count > 1 ){ for(int x = 0; x < radio_count; x++){ if(m_move_segment){ m_pv_used_space = (1 + m_sources[x]->end - m_sources[x]->start) * m_vg->getExtentSize(); radio_button = new NoMungeRadioButton( QString("%1 %2").arg(m_sources[x]->name_range).arg(locale->formatByteSize(m_pv_used_space))); radio_button->setAlternateText( m_sources[x]->name ); } else if(m_move_lv){ m_pv_used_space = m_lv->getSpaceUsedOnPv(m_sources[x]->name); radio_button = new NoMungeRadioButton( QString("%1 %2").arg(m_sources[x]->name).arg(locale->formatByteSize(m_pv_used_space))); radio_button->setAlternateText( m_sources[x]->name ); } else{ m_pv_used_space = m_vg->getPvByName(m_sources[x]->name)->getSize() - m_vg->getPvByName(m_sources[x]->name )->getRemaining(); radio_button = new NoMungeRadioButton( QString("%1 %2").arg(m_sources[x]->name).arg(locale->formatByteSize(m_pv_used_space))); radio_button->setAlternateText( m_sources[x]->name ); } if(radio_count < 11 ) radio_layout->addWidget(radio_button, x % 5, x / 5); else if (radio_count % 3 == 0) radio_layout->addWidget(radio_button, x % (radio_count / 3), x / (radio_count / 3)); else radio_layout->addWidget(radio_button, x % ( (radio_count + 2) / 3), x / ( (radio_count + 2) / 3)); m_radio_buttons.append(radio_button); connect(radio_button, SIGNAL(toggled(bool)), this, SLOT(disableSource())); if( !x ) radio_button->setChecked(true); } } else{ if(m_move_segment){ m_pv_used_space = (1 + m_sources[0]->end - m_sources[0]->start) * m_vg->getExtentSize(); radio_layout->addWidget( new QLabel( QString("%1 %2").arg(m_sources[0]->name_range).arg(locale->formatByteSize(m_pv_used_space)) ) ); } else if(m_move_lv){ m_pv_used_space = m_lv->getSpaceUsedOnPv(m_sources[0]->name); radio_layout->addWidget( new QLabel( QString("%1 %2").arg(m_sources[0]->name).arg(locale->formatByteSize(m_pv_used_space)) ) ); } else{ m_pv_used_space = m_vg->getPvByName( m_sources[0]->name )->getSize() - m_vg->getPvByName( m_sources[0]->name )->getRemaining(); radio_layout->addWidget( new QLabel( QString("%1 %2").arg(m_sources[0]->name).arg(locale->formatByteSize(m_pv_used_space)) ) ); } } QGroupBox *alloc_box = new QGroupBox( i18n("Allocation Policy") ); QVBoxLayout *alloc_box_layout = new QVBoxLayout; m_normal_button = new QRadioButton( i18nc("The usual way", "Normal") ); m_contiguous_button = new QRadioButton( i18n("Contiguous") ); m_anywhere_button = new QRadioButton( i18n("Anywhere") ); m_inherited_button = new QRadioButton( i18nc("Inherited from the group", "Inherited") ); m_inherited_button->setChecked(true); m_cling_button = new QRadioButton( i18n("Cling") ); alloc_box_layout->addWidget(m_normal_button); alloc_box_layout->addWidget(m_contiguous_button); alloc_box_layout->addWidget(m_anywhere_button); alloc_box_layout->addWidget(m_inherited_button); alloc_box_layout->addWidget(m_cling_button); alloc_box_layout->addStretch(); alloc_box->setLayout(alloc_box_layout); lower_layout->addWidget(alloc_box); connect(m_pv_checkbox, SIGNAL(stateChanged()), this, SLOT(resetOkButton())); resetOkButton(); } void PVMoveDialog::resetOkButton() { const long long free_space_total = m_pv_checkbox->getRemainingSpace(); long long needed_space_total = 0; QString pv_name; if(m_move_lv){ if(m_radio_buttons.size() > 1){ for(int x = 0; x < m_radio_buttons.size(); x++){ if(m_radio_buttons[x]->isChecked()){ pv_name = m_radio_buttons[x]->getAlternateText(); needed_space_total = m_lv->getSpaceUsedOnPv(pv_name); } } } else{ pv_name = m_sources[0]->name; needed_space_total = m_lv->getSpaceUsedOnPv(pv_name); } } else needed_space_total = m_pv_used_space; if(free_space_total < needed_space_total) enableButtonOk(false); else enableButtonOk(true); } void PVMoveDialog::disableSource() // don't allow source and target to be the same pv { PhysVol *source_pv = NULL; for(int x = m_radio_buttons.size() - 1; x>= 0; x--){ if(m_radio_buttons[x]->isChecked()) source_pv = m_vg->getPvByName( m_sources[x]->name ); } m_pv_checkbox->disableOrigin(source_pv); resetOkButton(); } QStringList PVMoveDialog::arguments() { QStringList args; QString source; args << "pvmove" << "--background"; if(m_move_lv){ args << "--name"; args << m_lv->getFullName(); } if ( !m_inherited_button->isChecked() ){ // "inherited" is what we get if args << "--alloc"; // we don't pass "--alloc" at all if ( m_contiguous_button->isChecked() ) // passing "--alloc" "inherited" args << "contiguous" ; // doesn't work else if ( m_anywhere_button->isChecked() ) args << "anywhere" ; else if ( m_cling_button->isChecked() ) args << "cling" ; else args << "normal" ; } if( m_sources.size() > 1 ){ for(int x = m_sources.size() - 1; x >= 0; x--){ if(m_radio_buttons[x]->isChecked()) source = m_sources[x]->name_range; } } else{ source = m_sources[0]->name_range; } args << source; args << m_pv_checkbox->getNames(); // target(s) return args; } void PVMoveDialog::setupSegmentMove(int segment) { const QStringList names = m_lv->getPvNames(segment); // source pv name const int stripes = m_lv->getSegmentStripes(segment); // source pv stripe count const long long extents = m_lv->getSegmentExtents(segment); // extent count const QList starts = m_lv->getSegmentStartingExtent(segment); // lv's first extent on pv NameAndRange *nar; for(int x = 0; x < names.size(); x++){ nar = new NameAndRange; nar->name = names[x]; nar->start = starts[x]; nar->end = starts[x] + (extents / stripes) - 1; nar->name_range = QString("%1:%2-%3").arg(nar->name).arg(nar->start).arg(nar->end); m_sources.append(nar); } } void PVMoveDialog::setupFullMove() { const QStringList names = m_lv->getPvNamesAllFlat(); NameAndRange *nar; for(int x = names.size() - 1; x >= 0; x--){ nar = new NameAndRange(); nar->name = names[x]; nar->name_range = names[x]; m_sources.append(nar); } } bool PVMoveDialog::bailout() { return m_bailout; } void PVMoveDialog::commitMove() { hide(); ProcessProgress move( arguments() ); return; } kvpm-0.8.6/kvpm/processprogress.cpp0000644000175000017500000001405411733530416017661 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "processprogress.h" #include #include #include #include #include #include #include #include "executablefinder.h" #include "topwindow.h" #include "progressbox.h" ProcessProgress::ProcessProgress(QStringList arguments, const bool allowCancel, QObject *parent) : QObject(parent) { m_exit_code = 127; // command not found m_progress_dialog = NULL; if(arguments.size() == 0){ qDebug() << "ProcessProgress given an empty arguments list"; } else{ const QString executable = arguments.takeFirst(); const QString executable_path = ExecutableFinder::getPath( executable ); if( !executable_path.isEmpty() ){ qApp->setOverrideCursor(Qt::WaitCursor); m_process = new KProcess(this); QStringList environment = QProcess::systemEnvironment(); environment << "LVM_SUPPRESS_FD_WARNINGS=1"; m_process->setEnvironment(environment); m_loop = new QEventLoop(this); if(allowCancel){ TopWindow::getProgressBox()->hide(); m_progress_dialog = new KProgressDialog(NULL, i18n("progress"), i18n("Running program: %1", executable), Qt::CustomizeWindowHint | Qt::WindowTitleHint); m_progress_dialog->setAllowCancel(true); m_progress_dialog->setMinimumDuration(250); m_progress_dialog->progressBar()->setRange(0,0); m_progress_dialog->setModal(true); connect(m_progress_dialog, SIGNAL(rejected()), this, SLOT(cancelProcess())); } else{ m_progress_dialog = NULL; TopWindow::getProgressBox()->setRange(0,0); TopWindow::getProgressBox()->setText(executable); } connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(cleanup(int, QProcess::ExitStatus))); connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readStandardOut())); connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(readStandardError())); m_process->setOutputChannelMode(KProcess::SeparateChannels); m_process->setReadChannel(QProcess::StandardOutput); m_process->setProgram(executable_path, arguments); m_process->start(); m_process->closeWriteChannel(); if(allowCancel) m_loop->exec(QEventLoop::AllEvents); else m_loop->exec(QEventLoop::ExcludeUserInputEvents); } else{ KMessageBox::error(NULL, i18n("Executable: '%1' not found", executable)); } } } void ProcessProgress::cleanup(const int code, const QProcess::ExitStatus status) { m_exit_code = code; m_loop->exit(); bool cancelled = false; qApp->restoreOverrideCursor(); if(m_progress_dialog != NULL){ m_progress_dialog->close(); cancelled = m_progress_dialog->wasCancelled(); delete m_progress_dialog; } if ( m_exit_code || ( status == QProcess::CrashExit ) ){ if ( ( m_exit_code == 0 ) && ( status == QProcess::CrashExit ) ) m_exit_code = 1; // if it crashed without an exit code, set a non zero exit code const QString errors = m_output_all.join(""); if( status != QProcess::CrashExit || cancelled ) KMessageBox::error(NULL, i18n("%1 produced this output: %2", m_process->program().takeFirst(), errors) ); else KMessageBox::error(NULL, i18n("%1 crashed with this output: %2", m_process->program().takeFirst(), errors) ); } TopWindow::getProgressBox()->reset(); } QStringList ProcessProgress::programOutput() { return m_output_no_error; } QStringList ProcessProgress::programOutputAll() { return m_output_all; } int ProcessProgress::exitCode() { return m_exit_code; } void ProcessProgress::cancelProcess() { const QString warning = i18n("Really kill process %1", m_process->program().takeFirst()); kill(m_process->pid(), SIGSTOP); if(KMessageBox::warningYesNo(NULL, warning, QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QString(), KMessageBox::Dangerous) == KMessageBox::Yes){ m_process->kill(); m_progress_dialog->show(); m_progress_dialog->setLabelText( i18n("Waiting for process to finish") ); m_progress_dialog->setAllowCancel(false); } else if(m_process->state() == QProcess::Running){ m_progress_dialog->show(); kill(m_process->pid(), SIGCONT); } } void ProcessProgress::readStandardOut() { QString output_line; m_process->setReadChannel(QProcess::StandardOutput); while(m_process->canReadLine()){ output_line = m_process->readLine(); if(!output_line.contains("partial mode.", Qt::CaseInsensitive)){ m_output_no_error << output_line; } m_output_all << output_line; } } void ProcessProgress::readStandardError() { QString output_line; m_process->setReadChannel(QProcess::StandardError); while(m_process->canReadLine()){ output_line = m_process->readLine(); m_output_all << output_line; } } kvpm-0.8.6/kvpm/physvol.cpp0000644000175000017500000001023011733530416016112 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "physvol.h" #include #include "volgroup.h" PhysVol::PhysVol(pv_t pv, VolGroup *vg) { m_vg = vg; rescan(pv); } void PhysVol::rescan(pv_t lvm_pv) { QByteArray flags; lvm_property_value value; value = lvm_pv_get_property(lvm_pv, "pv_attr"); if(value.is_valid) flags.append(value.value.string, 3); value = lvm_pv_get_property(lvm_pv, "pv_tags"); m_tags.clear(); if(value.is_valid) m_tags = QString( value.value.string ).split(','); for(int x = 0; x < m_tags.size(); x++) m_tags[x] = m_tags[x].trimmed(); value = lvm_pv_get_property(lvm_pv, "pv_mda_used_count"); if(value.is_valid) m_mda_used = value.value.integer; if(flags[0] == 'a') m_allocatable = true; else m_allocatable = false; m_last_used_extent = 0; m_active = false; // pv is active if any associated lvs are active m_device = QString( lvm_pv_get_name(lvm_pv) ); m_device_size = lvm_pv_get_dev_size(lvm_pv); m_unused = lvm_pv_get_free(lvm_pv); m_size = lvm_pv_get_size(lvm_pv); m_uuid = QString( lvm_pv_get_uuid(lvm_pv) ); m_mda_count = lvm_pv_get_mda_count(lvm_pv); value = lvm_pv_get_property(lvm_pv, "pv_mda_size"); if(value.is_valid) m_mda_size = value.value.integer; /* // The following wil be used to to calculate the last used // segement once the "lv_name" property gets implemented // Finding segments with lvs locked against change (snaps mirrors and pvmoves) // will also be possible // remove clunky code from pvtree.cpp when this get implemented! dm_list* pvseg_dm_list = lvm_pv_list_pvsegs(lvm_pv); lvm_pvseg_list *pvseg_list; if(pvseg_dm_list){ dm_list_iterate_items(pvseg_list, pvseg_dm_list){ value = lvm_pvseg_get_property( pvseg_list->pvseg , "lv_name"); qDebug() << "Name: " << value.value.string; value = lvm_pvseg_get_property( pvseg_list->pvseg , "lv_attr"); qDebug() << "attr: " << value.value.string; value = lvm_pvseg_get_property( pvseg_list->pvseg , "pvseg_start"); if(value.is_valid) qDebug() << "Seg start: " << value.value.integer; else qDebug() << "Not valid"; value = lvm_pvseg_get_property( pvseg_list->pvseg , "pvseg_size"); if(value.is_valid) qDebug() << "Seg size: " << value.value.integer; else qDebug() << "Not valid"; } } */ return; } VolGroup* PhysVol::getVg() { return m_vg; } QString PhysVol::getName() { return m_device.trimmed(); } QString PhysVol::getUuid() { return m_uuid.trimmed(); } QStringList PhysVol::getTags() { return m_tags; } bool PhysVol::isAllocatable() { return m_allocatable; } bool PhysVol::isActive() { return m_active; } void PhysVol::setActive() { m_active = true; } long PhysVol::getMdaCount() { return m_mda_count; } long PhysVol::getMdaUsed() { return m_mda_used; } long long PhysVol::getMdaSize() { return m_mda_size; // size in bytes } long long PhysVol::getSize() { return m_size; } long long PhysVol::getDeviceSize() { return m_device_size; } long long PhysVol::getRemaining() { return m_unused; } int PhysVol::getPercentUsed() { int percent; if( m_unused == 0 ) return 100; else if( m_unused == m_size ) return 0; else if( m_size == 0 ) // This shouldn't happen return 100; else percent = qRound( ( ( m_size - m_unused ) * 100.0 ) / m_size ); return percent; } long long PhysVol::getLastUsedExtent() { return m_last_used_extent; } void PhysVol::setLastUsedExtent(const long long last) { m_last_used_extent = last; } kvpm-0.8.6/kvpm/lvrename.cpp0000644000175000017500000000535511733530416016233 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "lvrename.h" #include #include #include "logvol.h" #include "mounttables.h" #include "volgroup.h" #include "processprogress.h" LVRenameDialog::LVRenameDialog(LogVol *const volume, QWidget *parent) : KDialog(parent), m_lv(volume) { setWindowTitle( i18n("Rename Logical Volume") ); QWidget *const dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *const layout = new QVBoxLayout(); dialog_body->setLayout(layout); m_vg_name = m_lv->getVg()->getName(); m_old_name = m_lv->getName(); QLabel *label = new QLabel( i18n("Rename Logical Volume") ); label->setAlignment(Qt::AlignCenter); layout->addWidget(label); layout->addSpacing(10); label = new QLabel( i18n("Current volume name: %1", m_old_name) ); layout->addWidget(label); QRegExp rx("[0-9a-zA-Z_\\.][-0-9a-zA-Z_\\.]*"); m_new_name = new KLineEdit(); m_name_validator = new QRegExpValidator( rx, m_new_name ); m_new_name->setValidator(m_name_validator); QHBoxLayout *const name_layout = new QHBoxLayout(); label = new QLabel( i18n("New volume name: ") ); name_layout->addWidget(label); name_layout->addWidget(m_new_name); layout->addLayout(name_layout); enableButtonOk(false); connect(this, SIGNAL(okClicked()), this, SLOT(commitChanges())); connect(m_new_name, SIGNAL(textChanged(QString)), this, SLOT(validateName(QString))); } /* The allowed characters in the name are letters, numbers, periods hyphens and underscores. Also the names ".", ".." and names starting with a hyphen are disallowed */ void LVRenameDialog::validateName(QString name) { int pos = 0; if( m_name_validator->validate(name, pos) == QValidator::Acceptable && name != "." && name != ".." ) { enableButtonOk(true); } else enableButtonOk(false); } QString LVRenameDialog::getNewMapperPath() { QString path = m_lv->getMapperPath(); path.truncate( path.lastIndexOf('/') + 1 ); return QString( path + m_new_name->text() ); } void LVRenameDialog::commitChanges() { QStringList args; args << "lvrename" << m_vg_name << m_old_name << m_new_name->text(); ProcessProgress rename(args); if( !rename.exitCode() && m_lv->isMounted() ) MountTables::renameEntries( m_lv->getMapperPath(), getNewMapperPath() ); } kvpm-0.8.6/kvpm/mount.h0000644000175000017500000000427311733530416015227 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef MOUNT_H #define MOUNT_H #include #include #include #include #include #include #include class LogVol; class StoragePartition; class MountDialog : public KDialog { Q_OBJECT QString m_mount_point, // The desired mount point m_device_to_mount, // The complete device path m_filesystem_type; // ext3, reiserfs, vfat etc. QRadioButton *ext2_button, *ext3_button, *ext4_button, *reiserfs3_button, *btrfs_button, *reiserfs4_button, *xfs_button, *jfs_button, *vfat_button, *ntfs_button, *specify_button, *udf_button, *iso9660_button, *hfs_button, *atime_button, *noatime_button, *nodiratime_button, *relatime_button, *data_journal_button, *data_ordered_button, *data_writeback_button; QCheckBox *sync_check, *dirsync_check, *rw_check, *suid_check, *dev_check, *exec_check, *mand_check, *acl_check, *user_xattr_check; KLineEdit *m_mount_point_edit, *m_filesystem_edit, // User chosen filesystem type *m_fs_specific_edit; // Additional options such as acl and data=ordered QGroupBox *m_filesystem_journal_box; bool m_is_writable; QWidget* filesystemBox(); QWidget* optionsBox(); QWidget* mountPointBox(); void buildDialog(); public: MountDialog(LogVol *const volume, QWidget *parent = NULL); MountDialog(StoragePartition *const partition, QWidget *parent = NULL); private slots: void selectMountPoint(bool); void mountFilesystem(); void toggleOKButton(const QString); void toggleOKButton(bool); void toggleOKButton(); void toggleAdditionalOptions(bool); }; #endif kvpm-0.8.6/kvpm/removefs.h0000644000175000017500000000074611733530416015714 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef REMOVEFS_H #define REMOVEFS_H #include "QString" bool remove_fs(const QString name); #endif kvpm-0.8.6/kvpm/fsdata.h0000644000175000017500000000106511733530416015323 0ustar benscottbenscott/* * * * Copyright (C) 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef FSDATA_H #define FSDATA_H #include struct FSData { long long size; long long used; long long block_size; }; FSData get_fs_data(const QString path); #endif kvpm-0.8.6/kvpm/pvextend.cpp0000644000175000017500000000137611733530416016256 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "pvextend.h" #include #include #include #include "processprogress.h" #include "storagepartition.h" bool pv_extend(QString path) { QStringList arguments; arguments << "pvresize" << path; ProcessProgress pv_grow(arguments); if( pv_grow.exitCode() ) return false; else return true; } kvpm-0.8.6/kvpm/pvgroupbox.h0000644000175000017500000000354011733530416016274 0ustar benscottbenscott/* * * * Copyright (C) 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PVGROUPBOX_H #define PVGROUPBOX_H #include #include #include #include #include #include #include #include class NoMungeCheck; class PhysVol; class StorageDevice; class StoragePartition; class PvGroupBox: public QGroupBox { Q_OBJECT bool m_use_si_units; QList m_pvs; QList m_devices; QList m_partitions; QList m_pv_checks; QLabel *m_space_label, *m_extents_label; long long m_extent_size; QHBoxLayout *getButtons(); void addLabelsAndButtons(QGridLayout *const layout, const int pvCount); public: explicit PvGroupBox(QList volumes, QWidget *parent = NULL); PvGroupBox(QList devices, QList partitions, long long extentSize, QWidget *parent = NULL); QStringList getAllNames(); // names of all pvs displayed in the box QStringList getNames(); // names of *selected* pvs long long getRemainingSpace(); // total unused space on selected pvs QList getRemainingSpaceList(); // ditto void setExtentSize(long long extentSize); void disableOrigin(PhysVol *originVolume); // disable origin to prevent move from and to same pv signals: void stateChanged(); private slots: void calculateSpace(); public slots: void selectAll(); void selectNone(); }; #endif kvpm-0.8.6/kvpm/devicetab.h0000644000175000017500000000174411733530416016013 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef DEVICETAB_H #define DEVICETAB_H #include #include #include #include #include class DeviceTree; class DevicePropertiesStack; class DeviceSizeChart; class StorageDevice; class DeviceTab : public QWidget { QVBoxLayout *m_layout; DeviceTree *m_tree; QSplitter *m_tree_properties_splitter; DeviceSizeChart *m_size_chart; DevicePropertiesStack *m_device_stack; QScrollArea *setupPropertyStack(); public: DeviceTab(QWidget *parent = 0); void rescan( QList Devices ); }; #endif kvpm-0.8.6/kvpm/progressbox.cpp0000644000175000017500000000271011733530416016767 0ustar benscottbenscott/* * * * Copyright (C) 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "progressbox.h" #include ProgressBox::ProgressBox(QWidget *parent) : QFrame(parent) { QHBoxLayout *layout = new QHBoxLayout(); m_message = new QLabel(); m_message->setMinimumWidth(150); m_message->setAlignment(Qt::AlignRight | Qt::AlignVCenter); m_progressbar = new QProgressBar(); m_progressbar->setTextVisible(false); layout->setMargin(0); layout->addWidget(m_message); layout->addWidget(m_progressbar); setLayout(layout); } void ProgressBox::setText(const QString text) { show(); if( !text.isEmpty() ) m_message->setText(text + " >>"); else m_message->clear(); } void ProgressBox::setRange(const int start, const int end) { show(); m_progressbar->setRange(start, end); m_message->clear(); } void ProgressBox::setValue(const int value) { show(); m_progressbar->setValue(value); if( value >= m_progressbar->maximum() ) m_message->clear(); } void ProgressBox::reset() { show(); m_progressbar->setRange(0, 1); m_progressbar->setValue(1); m_message->clear(); } kvpm-0.8.6/kvpm/devicesizechartseg.h0000644000175000017500000000146711733530416017742 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef STORAGEDEVICESIZECHARTSEG_H #define STORAGEDEVICESIZECHARTSEG_H #include #include #include class StoragePartition; class DeviceChartSeg : public QFrame { Q_OBJECT QTreeWidgetItem *m_item; StoragePartition *m_partition; public: explicit DeviceChartSeg(QTreeWidgetItem *storageItem, QWidget *parent = 0); public slots: void popupContextMenu(QPoint point); }; #endif kvpm-0.8.6/kvpm/devicesizechart.cpp0000644000175000017500000001233711733530416017574 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "devicesizechart.h" #include #include #include "devicesizechartseg.h" #include "devicetree.h" #include "storagedevice.h" #include "storagepartition.h" DeviceSizeChart::DeviceSizeChart(QWidget *parent) : QFrame(parent) { setFrameStyle( QFrame::Sunken | QFrame::Panel ); setLineWidth(2); m_layout = new QHBoxLayout(); m_layout->setSpacing(0); m_layout->setMargin(0); m_layout->setSizeConstraint(QLayout::SetNoConstraint); setLayout(m_layout); setMinimumHeight(45); setMaximumHeight(45); } void DeviceSizeChart::setNewDevice(QTreeWidgetItem *deviceItem) { QWidget *segment, *extended_segment; QString usage, path; double ratio; QTreeWidgetItem *partition_item, *extended_item; StoragePartition *partition; StorageDevice *device; long long part_size, device_size; int max_segment_width; unsigned int part_type; if(deviceItem == NULL) return; while( (deviceItem->parent() != NULL) && ( (QTreeWidget *)deviceItem->parent() != m_tree) ) deviceItem = deviceItem->parent(); for(int x = m_layout->count() - 1; x >= 0; x--) // delete all the children m_layout->takeAt(x)->widget()->deleteLater(); m_segments.clear(); m_ratios.clear(); m_extended_segments.clear(); m_extended_ratios.clear(); device = (StorageDevice *) (( deviceItem->data(1, Qt::UserRole).value() )); if( !deviceItem->childCount() ){ if( !device->isPhysicalVolume() ) usage = "physical volume"; else usage = ""; segment = new DeviceChartSeg(deviceItem); m_segments.append(segment); m_ratios.append(1.0); m_layout->addWidget(segment); } for(int x = 0; x < deviceItem->childCount(); x++){ partition_item = deviceItem->child(x); partition = (StoragePartition *) (( partition_item->data(0, Qt::UserRole)).value() ); part_type = partition->getPedType(); part_size = partition->getSize(); device_size = device->getSize(); if( partition->isPhysicalVolume() ) usage = "physical volume"; else usage = (partition_item->data(3, Qt::DisplayRole)).toString(); ratio = part_size / (double) device_size; segment = new DeviceChartSeg(partition_item); m_segments.append(segment); m_ratios.append(ratio); if( part_type & 0x02 ){ // extended partition m_extended_layout = new QHBoxLayout(); m_extended_layout->setSpacing(0); m_extended_layout->setMargin(0); m_extended_layout->setSizeConstraint(QLayout::SetNoConstraint); if( ! partition->isEmpty() ){ for(int y = 0 ; y < partition_item->childCount(); y++){ extended_item = partition_item->child(y); partition = (StoragePartition *) (( extended_item->data(0, Qt::UserRole)).value() ); device = (StorageDevice *) (( extended_item->data(1, Qt::UserRole)).value() ); part_type = partition->getPedType(); part_size = partition->getSize(); device_size = device->getSize(); if( partition->isPhysicalVolume() ) usage = "physical volume"; else usage = (extended_item->data(3, Qt::DisplayRole)).toString(); extended_segment = new DeviceChartSeg(extended_item); ratio = part_size / (double) device_size; m_extended_segments.append(extended_segment); m_extended_ratios.append(ratio); m_extended_layout->addWidget(extended_segment); } } segment->setLayout(m_extended_layout); } m_layout->addWidget(segment); } for(int x = m_segments.size() - 1 ; x >= 0; x--){ max_segment_width = (int)( ( width() * m_ratios[x]) - 2 ); if( max_segment_width < 1 ) max_segment_width = 1; m_segments[x]->setMaximumWidth(max_segment_width); } for(int x = m_extended_segments.size() - 1; x >= 0; x--){ max_segment_width = (int)( ( width() * m_extended_ratios[x] ) - 2 ); if( max_segment_width < 1 ) max_segment_width = 1; m_extended_segments[x]->setMaximumWidth(max_segment_width); } } void DeviceSizeChart::resizeEvent(QResizeEvent *event) { int max_segment_width; int new_width = (event->size()).width(); for(int x = m_segments.size() - 1 ; x >= 0; x--){ max_segment_width = (int)( (new_width * m_ratios[x]) - 2 ); if( max_segment_width < 1 ) max_segment_width = 1; m_segments[x]->setMaximumWidth(max_segment_width); } for(int x = m_extended_segments.size() - 1; x >= 0; x--){ max_segment_width = (int)( (new_width * m_extended_ratios[x]) - 2 ); if( max_segment_width < 1 ) max_segment_width = 1; m_extended_segments[x]->setMaximumWidth(max_segment_width); } } kvpm-0.8.6/kvpm/volgroup.h0000644000175000017500000000602711733530416015741 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VOLGROUP_H #define VOLGROUP_H #include #include #include class LogVol; class MountTables; class PhysVol; class VolGroup { MountTables *m_tables; long m_extent_size; int m_lv_max; // maximum number of logical volumes, unlimited == 0 int m_pv_max; long long m_mda_count; long long m_size; // total size of volume group in bytes long long m_free; // free space in bytes long long m_extents; long long m_free_extents; // free extents are not always useable long long m_allocatable_extents; // extents on some physical volumes // may not be allocateable QString m_vg_name; // this volume group name QString m_uuid; QString m_allocation_policy; QString m_lvm_format; // lvm1 or lvm2 QList m_member_lvs; // lvs that belong to this group QList m_member_pvs; // pvs that belong to this group bool m_active; // if any lv is active the group is active bool m_writable; bool m_resizable; bool m_clustered; bool m_exported; bool m_partial; // some physical volumes may be missing lv_t findOrphan(QList &childList); void processLogicalVolumes(vg_t lvmVG); void processPhysicalVolumes(vg_t lvmVG); void setActivePhysicalVolumes(); void setLastUsedExtent(); public: VolGroup(lvm_t lvm, const char *vgname, MountTables *const tables); ~VolGroup(); void rescan(lvm_t lvm); QList getLogicalVolumes(); // *TOP LEVEL ONLY* snapcontainers returned not snaps and origin QList getLogicalVolumesFlat(); // un-nest the volumes, snapshots and mirror legs QList getPhysicalVolumes(); LogVol* getLvByName(QString shortName); // lv name without the vg name and "/" -- skips snap containers LogVol* getLvByUuid(QString uuid); // also skips snap containers PhysVol* getPvByName(QString name); // /dev/something long long getExtents(); long long getFreeExtents(); long long getAllocatableExtents(); long long getAllocatableSpace(); long getExtentSize(); long long getSize(); long long getFreeSpace(); long long getUsedSpace(); int getLvCount(); int getLvMax(); int getPvCount(); int getPvMax(); int getMdaCount(); QString getName(); QString getUuid(); QString getPolicy(); QString getFormat(); QStringList getLvNames(); bool isWritable(); bool isResizable(); bool isClustered(); bool isPartial(); bool isExported(); bool isActive(); }; #endif kvpm-0.8.6/kvpm/executablefinder.cpp0000644000175000017500000000563311733530416017732 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "executablefinder.h" #include #include #include QMap ExecutableFinder::m_path_map = QMap(); // Static initialization /* The purpoise of this class is to map the name of a program with the full path of the executable */ ExecutableFinder::ExecutableFinder(QObject *parent) : QObject(parent) { m_keys << "dumpe2fs" << "fsck" << "lvchange" << "lvconvert" << "lvcreate" << "lvextend" << "lvreduce" << "lvremove" << "lvrename" << "lvs" << "mkfs" << "mkswap" << "ntfsresize" << "pvchange" << "pvcreate" << "pvmove" << "pvremove" << "pvresize" << "pvs" << "resize2fs" << "resize_reiserfs" << "udevadm" << "vgchange" << "vgcreate" << "vgexport" << "vgextend" << "vgimport" << "vgmerge" << "vgreduce" << "vgremove" << "vgrename" << "vgs" << "vgsplit" << "xfs_growfs"; m_default_search_paths << "/sbin/" << "/usr/sbin/" << "/bin/" << "/usr/bin/" << "/usr/local/bin/" << "/usr/local/sbin/"; reload(); } QString ExecutableFinder::getPath(const QString name) { QString path = m_path_map.value(name); if( path.isEmpty() ) qDebug() << "Excutable Finder: error " << name << " does not map to any path"; return path; } void ExecutableFinder::reload() { KConfigSkeleton skeleton; QStringList search; skeleton.setCurrentGroup("SystemPaths"); skeleton.addItemStringList("SearchPath", search, m_default_search_paths); reload(search); } void ExecutableFinder::reload(QStringList search) { KDE_struct_stat buf; const int key_length = m_keys.size(); m_path_map.clear(); for(int y = 0; y < key_length; y++){ for(int x = 0; x < search.size(); x++){ QByteArray path_qba = QString( search[x] + m_keys[y] ).toLocal8Bit(); const char *path = path_qba.data(); if( KDE_lstat(path, &buf) == 0 ){ m_path_map.insert( m_keys[y], search[x] + m_keys[y] ); break; } } } m_not_found.clear(); for(int x = 0; x < key_length; x++){ if( (m_path_map.value( m_keys[x] )).isEmpty() ) m_not_found.append( m_keys[x] ); } } QStringList ExecutableFinder::getAllPaths() { return m_path_map.values(); } QStringList ExecutableFinder::getAllNames() { return m_path_map.keys(); } QStringList ExecutableFinder::getNotFound() { return m_not_found; } kvpm-0.8.6/kvpm/fsblocksize.cpp0000644000175000017500000000216511733530416016734 0ustar benscottbenscott/* * * * Copyright (C) 2009 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "fsblocksize.h" #include #include #include #include "processprogress.h" long get_fs_block_size(QString path){ QStringList arguments; QString block_string; arguments << "dumpe2fs" << "-h" << path; ProcessProgress blocksize_scan(arguments); const QStringList output = blocksize_scan.programOutput(); const QStringList temp_stringlist = output.filter("Block size", Qt::CaseInsensitive); if( temp_stringlist.size() ){ block_string = temp_stringlist[0]; block_string = block_string.remove( 0, block_string.indexOf(":") + 1 ); block_string = block_string.simplified(); } else return 0; return block_string.toLong(); } kvpm-0.8.6/kvpm/vgrename.cpp0000644000175000017500000000626711733530416016231 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vgrename.h" #include #include #include "logvol.h" #include "mounttables.h" #include "processprogress.h" #include "volgroup.h" bool rename_vg(VolGroup *volumeGroup) { QList lvs; QString new_path, old_path; QString new_name, old_name; VGRenameDialog dialog(volumeGroup); dialog.exec(); if(dialog.result() == QDialog::Accepted){ ProcessProgress rename( dialog.arguments() ); if( ! rename.exitCode() ){ lvs = volumeGroup->getLogicalVolumes(); old_name = '/' + dialog.getOldName() + '/'; new_name = '/' + dialog.getNewName() + '/'; for(int x = lvs.size() - 1; x >= 0; x--){ if( lvs[x]->isMounted() ){ old_path = lvs[x]->getMapperPath(); new_path = lvs[x]->getMapperPath().replace(old_path.lastIndexOf(old_name), old_name.size(), new_name); MountTables::renameEntries(old_path, new_path); } } } return true; } else return false; } VGRenameDialog::VGRenameDialog(VolGroup *volumeGroup, QWidget *parent) : KDialog(parent) { setWindowTitle( i18n("Rename volume group") ); QWidget *dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *layout = new QVBoxLayout(); dialog_body->setLayout(layout); m_old_name = volumeGroup->getName(); QLabel *old_name_label = new QLabel( i18n("Old volume group name: %1", m_old_name) ); layout->addWidget(old_name_label); QLabel *name_label = new QLabel( i18n("New volume group name: ") ); m_new_name = new KLineEdit(); QRegExp rx("[0-9a-zA-Z_\\.][-0-9a-zA-Z_\\.]*"); m_name_validator = new QRegExpValidator( rx, m_new_name ); m_new_name->setValidator(m_name_validator); QHBoxLayout *name_layout = new QHBoxLayout(); name_layout->addWidget(name_label); name_layout->addWidget(m_new_name); layout->addLayout(name_layout); enableButtonOk(false); connect(m_new_name, SIGNAL(textChanged(QString)), this, SLOT(validateName(QString))); } QStringList VGRenameDialog::arguments() { QStringList args; args << "vgrename" << m_old_name << m_new_name->text(); return args; } /* The allowed characters in the name are letters, numbers, periods hyphens and underscores. Also the names ".", ".." and names starting with a hyphen are disallowed */ void VGRenameDialog::validateName(QString name) { int pos = 0; if( m_name_validator->validate(name, pos) == QValidator::Acceptable && name != "." && name != ".." ) { enableButtonOk(true); } else enableButtonOk(false); } QString VGRenameDialog::getNewName() { return m_new_name->text().trimmed(); } QString VGRenameDialog::getOldName() { return m_old_name; } kvpm-0.8.6/kvpm/volumegrouptab.h0000644000175000017500000000216011733530416017131 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VOLUMEGROUPTAB_H #define VOLUMEGROUPTAB_H #include #include #include class VolGroup; class LVPropertiesStack; class PVPropertiesStack; class PVTree; class VGTree; class VGInfoLabels; class LVSizeChart; class VolumeGroupTab : public QWidget { Q_OBJECT LVSizeChart *m_lv_size_chart; VGInfoLabels *m_vg_info_labels; QVBoxLayout *m_layout; VolGroup *m_vg; LVPropertiesStack *m_lv_properties_stack; PVPropertiesStack *m_pv_properties_stack; PVTree *m_pv_tree; VGTree *m_vg_tree; QString m_group_name; void readConfig(); public: explicit VolumeGroupTab(VolGroup *volumeGroup, QWidget *parent = 0); VolGroup* getVg(); void rescan(); }; #endif kvpm-0.8.6/kvpm/lvcreate.cpp0000644000175000017500000007615311733530416016233 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "lvcreate.h" #include #include #include #include #include #include "fsextend.h" #include "logvol.h" #include "mountentry.h" #include "physvol.h" #include "pvgroupbox.h" #include "processprogress.h" #include "sizeselectorbox.h" #include "volgroup.h" /* This class handles both the creation and extension of logical volumes and snapshots since both processes are so similar. */ LVCreateDialog::LVCreateDialog(VolGroup *const group, QWidget *parent): KDialog(parent), m_vg(group) { m_lv = NULL; m_extend = false; m_snapshot = false; m_bailout = hasInitialErrors(); m_fs_can_extend = false; KConfigSkeleton skeleton; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", m_use_si_units, false); if ( !m_bailout ){ setCaption( i18n("Create Logical Volume") ); m_tab_widget = new KTabWidget(this); m_physical_tab = createPhysicalTab(); m_advanced_tab = createAdvancedTab(); m_general_tab = createGeneralTab(); m_tab_widget->addTab(m_general_tab, i18nc("The standard common options", "General") ); m_tab_widget->addTab(m_physical_tab, i18n("Physical layout") ); m_tab_widget->addTab(m_advanced_tab, i18nc("Less used, dangerous or complex options", "Advanced options") ); setMainWidget(m_tab_widget); makeConnections(); setMaxSize(); } } LVCreateDialog::LVCreateDialog(LogVol *const volume, const bool snapshot, QWidget *parent): KDialog(parent), m_snapshot(snapshot), m_lv(volume) { m_extend = !m_snapshot; m_vg = m_lv->getVg(); m_bailout = hasInitialErrors(); KConfigSkeleton skeleton; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", m_use_si_units, false); if ( !m_bailout ){ if(m_snapshot) setCaption( i18n("Create Snapshot Volume") ); else setCaption( i18n("Extend Logical Volume") ); m_tab_widget = new KTabWidget(this); m_physical_tab = createPhysicalTab(); // this order is important m_advanced_tab = createAdvancedTab(); m_general_tab = createGeneralTab(); m_tab_widget->addTab(m_general_tab, i18nc("The standard common options", "General") ); m_tab_widget->addTab(m_physical_tab, i18n("Physical layout") ); m_tab_widget->addTab(m_advanced_tab, i18nc("Less used, dangerous or complex options", "Advanced options") ); setMainWidget(m_tab_widget); makeConnections(); setMaxSize(); } } void LVCreateDialog::makeConnections() { connect(this, SIGNAL(okClicked()), this, SLOT(commitChanges())); connect(m_pv_checkbox, SIGNAL(stateChanged()), this, SLOT(setMaxSize())); connect(m_stripe_count_spin, SIGNAL(valueChanged(int)), this, SLOT(setMaxSize())); connect(m_mirror_count_spin, SIGNAL(valueChanged(int)), this, SLOT(setMaxSize())); connect(m_stripe_box, SIGNAL(toggled(bool)), this, SLOT(setMaxSize())); connect(m_mirror_box, SIGNAL(toggled(bool)), this, SLOT(setMaxSize())); connect(m_mirror_box, SIGNAL(toggled(bool)), this, SLOT(enableMonitoring(bool))); connect(m_mirrored_log, SIGNAL(toggled(bool)), this, SLOT(setMaxSize())); connect(m_disk_log, SIGNAL(toggled(bool)), this, SLOT(setMaxSize())); connect(m_core_log, SIGNAL(toggled(bool)), this, SLOT(setMaxSize())); connect(m_size_selector, SIGNAL(stateChanged()), this, SLOT(setMaxSize())); } QWidget* LVCreateDialog::createGeneralTab() { m_tag_edit = NULL; QWidget *general_tab = new QWidget(this); QHBoxLayout *general_layout = new QHBoxLayout; general_tab->setLayout(general_layout); QGroupBox *volume_box = new QGroupBox(); QVBoxLayout *layout = new QVBoxLayout; volume_box->setLayout(layout); general_layout->addStretch(); general_layout->addWidget(volume_box); general_layout->addStretch(); QVBoxLayout *upper_layout = new QVBoxLayout(); QHBoxLayout *lower_layout = new QHBoxLayout(); layout->addStretch(); layout->addLayout(upper_layout); layout->addStretch(); layout->addLayout(lower_layout); m_name_is_valid = true; KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); if(m_extend){ volume_box->setTitle( i18n("Extending volume: %1", m_lv->getName()) ); m_extend_by_label = new QLabel(); layout->insertWidget( 1, m_extend_by_label ); m_current_size_label = new QLabel( i18n("Current size: %1", locale->formatByteSize( m_lv->getSize() ) ) ); layout->insertWidget( 2, m_current_size_label ); } else{ if(m_snapshot) volume_box->setTitle( i18n("Creating snapshot of: %1", m_lv->getName()) ); else volume_box->setTitle( i18n("Create new logical volume") ); QHBoxLayout *name_layout = new QHBoxLayout(); m_name_edit = new KLineEdit(); QRegExp rx("[0-9a-zA-Z_\\.][-0-9a-zA-Z_\\.]*"); m_name_validator = new QRegExpValidator( rx, m_name_edit ); m_name_edit->setValidator(m_name_validator); name_layout->addWidget( new QLabel( i18n("Volume name: ") ) ); name_layout->addWidget(m_name_edit); upper_layout->insertLayout(0, name_layout); QHBoxLayout *tag_layout = new QHBoxLayout(); m_tag_edit = new KLineEdit(); QRegExp rx2("[0-9a-zA-Z_\\.+-]*"); m_tag_validator = new QRegExpValidator( rx2, m_tag_edit ); m_tag_edit->setValidator(m_tag_validator); tag_layout->addWidget( new QLabel( i18n("Optional tag: ") ) ); tag_layout->addWidget(m_tag_edit); upper_layout->insertLayout(1, tag_layout); connect(m_name_edit, SIGNAL(textEdited(QString)), this, SLOT(validateVolumeName(QString))); } if(m_extend){ if( getStripeCount() > 1 ){ m_stripe_box->setChecked(false); m_size_selector = new SizeSelectorBox(m_vg->getExtentSize(), m_lv->getExtents(), getLargestVolume() / m_vg->getExtentSize(), m_lv->getExtents(), true, false); m_stripe_box->setChecked(true); } else{ m_size_selector = new SizeSelectorBox(m_vg->getExtentSize(), m_lv->getExtents(), getLargestVolume() / m_vg->getExtentSize(), m_lv->getExtents(), true, false); } } else{ m_size_selector = new SizeSelectorBox(m_vg->getExtentSize(), 0, getLargestVolume() / m_vg->getExtentSize() , 0, true, false); } upper_layout->addWidget(m_size_selector); QGroupBox *volume_limit_box = new QGroupBox( i18n("Maximum volume size") ); QVBoxLayout *volume_limit_layout = new QVBoxLayout(); volume_limit_box->setLayout(volume_limit_layout); m_max_size_label = new QLabel(); volume_limit_layout->addWidget(m_max_size_label); m_max_extents_label = new QLabel(); volume_limit_layout->addWidget(m_max_extents_label); m_stripe_count_label = new QLabel(); volume_limit_layout->addWidget(m_stripe_count_label); volume_limit_layout->addStretch(); lower_layout->addWidget(volume_limit_box); QGroupBox *alloc_box = new QGroupBox( i18n("Allocation Policy") ); QVBoxLayout *alloc_box_layout = new QVBoxLayout; normal_button = new QRadioButton( i18nc("The usual way", "Normal") ); contiguous_button = new QRadioButton( i18n("Contiguous") ); anywhere_button = new QRadioButton( i18n("Anywhere") ); inherited_button = new QRadioButton( i18nc("Inherited from the parent group", "Inherited") ); cling_button = new QRadioButton( i18n("Cling") ); QString policy = "Inherited"; if( m_extend ) policy = m_lv->getPolicy(); if( policy == "Normal" ) normal_button->setChecked(true); else if( policy == "Contiguous" ) contiguous_button->setChecked(true); else if( policy == "Anywhere" ) anywhere_button->setChecked(true); else if( policy == "Cling" ) cling_button->setChecked(true); else inherited_button->setChecked(true); alloc_box_layout->addWidget(normal_button); alloc_box_layout->addWidget(contiguous_button); alloc_box_layout->addWidget(anywhere_button); alloc_box_layout->addWidget(inherited_button); alloc_box_layout->addWidget(cling_button); alloc_box->setLayout(alloc_box_layout); lower_layout->addWidget(alloc_box); return general_tab; } QWidget* LVCreateDialog::createPhysicalTab() { QString message; int stripe_index; QList physical_volumes; QVBoxLayout *layout = new QVBoxLayout; m_physical_tab = new QWidget(this); m_physical_tab->setLayout(layout); physical_volumes = m_vg->getPhysicalVolumes(); for(int x = physical_volumes.size() - 1; x >= 0; x--){ if( physical_volumes[x]->getRemaining() < 1 ) // remove pvs with no free space physical_volumes.removeAt(x); } m_pv_checkbox = new PvGroupBox(physical_volumes); layout->addWidget(m_pv_checkbox); QVBoxLayout *striped_layout = new QVBoxLayout; QHBoxLayout *stripe_size_layout = new QHBoxLayout; QHBoxLayout *stripes_number_layout = new QHBoxLayout; m_stripe_box = new QGroupBox( i18n("Disk striping") ); m_stripe_box->setCheckable(true); m_stripe_box->setChecked(false); m_stripe_box->setLayout(striped_layout); stripe_size_combo = new KComboBox(); m_stripe_box->setEnabled(false); for(int n = 2; (pow(2, n) * 1024) <= m_vg->getExtentSize() ; n++){ stripe_size_combo->addItem(QString("%1").arg(pow(2, n)) + " KiB"); stripe_size_combo->setItemData(n - 2, QVariant( (int) pow(2, n) ), Qt::UserRole ); m_stripe_box->setEnabled(true); // only enabled if the combo box has at least one entry! } if(physical_volumes.size() < 2) m_stripe_box->setEnabled(false); QLabel *stripe_size = new QLabel( i18n("Stripe Size: ") ); m_stripe_count_spin = new QSpinBox(); m_stripe_count_spin->setMinimum(2); m_stripe_count_spin->setMaximum(m_vg->getPvCount()); stripe_size_layout->addWidget(stripe_size); stripe_size_layout->addWidget(stripe_size_combo); QLabel *stripes_number = new QLabel( i18n("Number of stripes: ") ); stripes_number_layout->addWidget(stripes_number); stripes_number_layout->addWidget(m_stripe_count_spin); striped_layout->addLayout(stripe_size_layout); striped_layout->addLayout(stripes_number_layout); /* If we are extending a volume we try to match the striping of the last segment of that volume, if it was striped */ if(m_extend){ int seg_count = 1; int seg_stripe_count = 1; int seg_stripe_size = 4; QList logvols; if( m_lv->isMirror() ){ // Tries to match striping to last segment of first leg logvols = m_lv->getAllChildrenFlat(); for(int x = 0; x < logvols.size(); x++){ if(logvols[x]->isMirrorLeg() && !(logvols[x]->isMirrorLog()) ){ seg_count = logvols[x]->getSegmentCount(); seg_stripe_count = logvols[x]->getSegmentStripes(seg_count - 1); seg_stripe_size = logvols[x]->getSegmentStripeSize(seg_count - 1); break; } } } else{ seg_count = m_lv->getSegmentCount(); seg_stripe_count = m_lv->getSegmentStripes(seg_count - 1); seg_stripe_size = m_lv->getSegmentStripeSize(seg_count - 1); } if( seg_stripe_count > 1 && m_stripe_box->isEnabled() ){ m_stripe_count_spin->setValue(seg_stripe_count); if( physical_volumes.size() >= (seg_stripe_count * m_lv->getMirrorCount()) ) m_stripe_box->setChecked(true); else m_stripe_box->setChecked(false); stripe_index = stripe_size_combo->findData( QVariant(seg_stripe_size / 1024) ); if(stripe_index == -1) stripe_index = 0; stripe_size_combo->setCurrentIndex(stripe_index); } } QVBoxLayout *mirror_layout = new QVBoxLayout; m_mirror_box = new QGroupBox( i18n("Disk mirrors") ); m_mirror_box->setCheckable(true); m_mirror_box->setChecked(false); if(physical_volumes.size() < 2) m_mirror_box->setEnabled(false); m_mirror_box->setLayout(mirror_layout); m_mirrored_log = new QRadioButton( i18n("Mirrored disk based log") ); m_disk_log = new QRadioButton( i18n("Disk based log") ); m_core_log = new QRadioButton( i18n("Memory based log") ); m_disk_log->setChecked(true); mirror_layout->addWidget(m_mirrored_log); mirror_layout->addWidget(m_disk_log); mirror_layout->addWidget(m_core_log); QHBoxLayout *mirrors_spin_layout = new QHBoxLayout(); m_mirror_count_spin = new QSpinBox(); m_mirror_count_spin->setMinimum(2); m_mirror_count_spin->setMaximum(m_vg->getPvCount()); QLabel *mirrors_number_label = new QLabel( i18n("Number of mirror legs: ") ); mirrors_spin_layout->addWidget(mirrors_number_label); mirrors_spin_layout->addWidget(m_mirror_count_spin); mirror_layout->addLayout(mirrors_spin_layout); QHBoxLayout *lower_h_layout = new QHBoxLayout; QVBoxLayout *lower_v_layout = new QVBoxLayout; layout->addLayout(lower_h_layout); lower_h_layout->addStretch(); lower_h_layout->addLayout(lower_v_layout); lower_h_layout->addStretch(); lower_v_layout->addWidget(m_stripe_box); if( (!m_extend) && (!m_snapshot) ) lower_v_layout->addWidget(m_mirror_box); else m_mirror_box->setEnabled(false); return m_physical_tab; } QWidget* LVCreateDialog::createAdvancedTab() { QHBoxLayout *advanced_layout = new QHBoxLayout; m_advanced_tab = new QWidget(this); m_advanced_tab->setLayout(advanced_layout); QGroupBox *advanced_box = new QGroupBox(); QVBoxLayout *layout = new QVBoxLayout; advanced_box->setLayout(layout); advanced_layout->addStretch(); advanced_layout->addWidget(advanced_box); advanced_layout->addStretch(); m_persistent_box = new QGroupBox( i18n("Device numbering") ); m_persistent_box->setCheckable(true); m_persistent_box->setChecked(false); m_readonly_check = new QCheckBox(); m_readonly_check->setText( i18n("Set read only") ); layout->addWidget(m_readonly_check); m_zero_check = new QCheckBox(); m_zero_check->setText( i18n("Write zeros at volume start") ); layout->addWidget(m_zero_check); if( !m_snapshot && !m_extend ){ connect(m_zero_check, SIGNAL(stateChanged(int)), this ,SLOT(zeroReadonlyCheck(int))); connect(m_readonly_check, SIGNAL(stateChanged(int)), this ,SLOT(zeroReadonlyCheck(int))); m_zero_check->setChecked(true); m_readonly_check->setChecked(false); } else{ m_zero_check->setChecked(false); m_zero_check->setEnabled(false); m_readonly_check->setChecked(false); m_readonly_check->setEnabled(false); } m_monitor_check = new QCheckBox( i18n("Monitor with dmeventd") ); m_skip_sync_check = new QCheckBox( i18n("Skip initial synchronization of mirror") ); if(m_snapshot){ m_monitor_check->setChecked(true); m_monitor_check->setEnabled(true); m_skip_sync_check->setChecked(false); m_skip_sync_check->setEnabled(false); layout->addWidget(m_monitor_check); layout->addWidget(m_skip_sync_check); } else if(m_extend){ m_monitor_check->setChecked(false); m_monitor_check->setEnabled(false); m_skip_sync_check->setChecked(false); m_skip_sync_check->setEnabled(false); } else{ m_monitor_check->setChecked(false); m_monitor_check->setEnabled(false); m_skip_sync_check->setChecked(false); m_skip_sync_check->setEnabled(false); layout->addWidget(m_monitor_check); layout->addWidget(m_skip_sync_check); } QVBoxLayout *persistent_layout = new QVBoxLayout; QHBoxLayout *minor_number_layout = new QHBoxLayout; QHBoxLayout *major_number_layout = new QHBoxLayout; m_minor_number_edit = new KLineEdit(); m_major_number_edit = new KLineEdit(); QLabel *minor_number = new QLabel( i18n("Device minor number: ") ); QLabel *major_number = new QLabel( i18n("Device major number: ") ); major_number_layout->addWidget(major_number); major_number_layout->addWidget(m_major_number_edit); minor_number_layout->addWidget(minor_number); minor_number_layout->addWidget(m_minor_number_edit); persistent_layout->addLayout(major_number_layout); persistent_layout->addLayout(minor_number_layout); QIntValidator *minor_validator = new QIntValidator(m_minor_number_edit); QIntValidator *major_validator = new QIntValidator(m_major_number_edit); minor_validator->setBottom(0); major_validator->setBottom(0); m_minor_number_edit->setValidator(minor_validator); m_major_number_edit->setValidator(major_validator); m_persistent_box->setLayout(persistent_layout); layout->addWidget(m_persistent_box); layout->addStretch(); return m_advanced_tab; } void LVCreateDialog::setMaxSize() { const int stripe_count = getStripeCount(); const int mirror_count = getMirrorCount(); const long long free_extents = getLargestVolume() / m_vg->getExtentSize(); const long long selected_size = m_size_selector->getCurrentSize() * m_vg->getExtentSize(); KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); m_size_selector->setConstrainedMax(free_extents); m_max_size_label->setText( i18n("Size: %1", locale->formatByteSize( getLargestVolume() )) ); m_max_extents_label->setText( i18n("Extents: %1", free_extents) ); if(!m_extend){ if( m_mirror_box->isChecked() && !m_stripe_box->isChecked() ) m_stripe_count_label->setText( i18n("(with %1 mirror legs)", mirror_count) ); else if( !m_mirror_box->isChecked() && m_stripe_box->isChecked() ) m_stripe_count_label->setText( i18n("(with %1 stripes)", stripe_count) ); else if( m_mirror_box->isChecked() && m_stripe_box->isChecked() ) m_stripe_count_label->setText( i18n("(with %1 mirror legs\nand %2 stripes)", mirror_count, stripe_count) ); else m_stripe_count_label->setText( i18n("(linear volume)") ); } else{ m_extend_by_label->setText( i18n("Extend by: %1", locale->formatByteSize( selected_size - m_lv->getSize() )) ); if( mirror_count > 1 && !m_stripe_box->isChecked() ) m_stripe_count_label->setText( i18n("(with %1 mirror legs)", mirror_count) ); else if( mirror_count < 2 && m_stripe_box->isChecked() ) m_stripe_count_label->setText( i18n("(with %1 stripes)", stripe_count) ); else if( mirror_count > 1 && m_stripe_box->isChecked() ) m_stripe_count_label->setText( i18n("(with %1 mirror legs\nand %2 stripes)", mirror_count, stripe_count) ); else m_stripe_count_label->setText( i18n("(linear volume)") ); } resetOkButton(); } void LVCreateDialog::validateVolumeName(QString name) { int pos = 0; if( m_name_validator->validate(name, pos) == QValidator::Acceptable && name != "." && name != ".." ) m_name_is_valid = true; else if( name.isEmpty() ) m_name_is_valid = true; else m_name_is_valid = false; resetOkButton(); } void LVCreateDialog::resetOkButton() { const long long max_extents = getLargestVolume() / m_vg->getExtentSize(); const long long volume_extents = roundExtentsToStripes( m_size_selector->getCurrentSize() ); if(m_size_selector->isValid()){ if(!m_extend){ if( (volume_extents <= max_extents) && (volume_extents > 0) && m_name_is_valid ) enableButtonOk(true); else enableButtonOk(false); } else{ if( (volume_extents <= max_extents) && (volume_extents > m_lv->getExtents()) && m_name_is_valid ) enableButtonOk(true); else enableButtonOk(false); } } else enableButtonOk(false); } void LVCreateDialog::enableMonitoring(bool checked) { if(checked){ m_monitor_check->setChecked(true); m_monitor_check->setEnabled(true); m_skip_sync_check->setChecked(true); m_skip_sync_check->setEnabled(true); } else{ if(!m_snapshot){ m_monitor_check->setChecked(false); m_monitor_check->setEnabled(false); } m_skip_sync_check->setChecked(false); m_skip_sync_check->setEnabled(false); } } /* largest volume that can be created given the pvs , striping and mirrors selected. This includes the size of the already existing volume if we are extending a volume */ long long LVCreateDialog::getLargestVolume() { QList available_pv_bytes = m_pv_checkbox->getRemainingSpaceList(); QList stripe_pv_bytes; const int total_stripes = getStripeCount() * getMirrorCount(); int log_count; for(int x = 0; x < total_stripes; x++) stripe_pv_bytes.append(0); if( m_mirror_box->isChecked() ){ if( m_disk_log->isChecked() ) log_count = 1; else if(m_mirrored_log->isChecked() ) log_count = 2; else log_count = 0; } else log_count = 0; qSort(available_pv_bytes); for(int x = 0; x < log_count; x++){ if( available_pv_bytes.size() ) available_pv_bytes.removeFirst(); else return 0; } while( available_pv_bytes.size() ){ qSort(stripe_pv_bytes); stripe_pv_bytes[0] += available_pv_bytes.takeLast(); } qSort(stripe_pv_bytes); if(!m_extend) return stripe_pv_bytes[0] * getStripeCount(); else return ( stripe_pv_bytes[0] * getStripeCount() ) + m_lv->getSize(); } int LVCreateDialog::getStripeCount() { if(m_stripe_box->isChecked()) return m_stripe_count_spin->value(); else return 1; } int LVCreateDialog::getMirrorCount() { if(m_extend) return m_lv->getMirrorCount(); else if( m_mirror_box->isChecked() ) return m_mirror_count_spin->value(); else return 1; } void LVCreateDialog::zeroReadonlyCheck(int) { if( !m_snapshot && !m_extend){ if (m_zero_check->isChecked()){ m_readonly_check->setChecked(false); m_readonly_check->setEnabled(false); } else m_readonly_check->setEnabled(true); if (m_readonly_check->isChecked()){ m_zero_check->setChecked(false); m_zero_check->setEnabled(false); } else m_zero_check->setEnabled(true); } else m_readonly_check->setEnabled(true); } /* Here we create a stringlist of arguments based on all the options that the user chose in the dialog. */ QStringList LVCreateDialog::argumentsLV() { QString program_to_run; QStringList args; QVariant stripe_size; const int stripes = getStripeCount(); const int mirrors = getMirrorCount(); long long extents = m_size_selector->getCurrentSize(); if(m_tag_edit){ if( ! ( m_tag_edit->text() ).isEmpty() ) args << "--addtag" << m_tag_edit->text(); } if(m_persistent_box->isChecked()){ args << "--persistent" << "y"; args << "--major" << m_major_number_edit->text(); args << "--minor" << m_minor_number_edit->text(); } stripe_size = stripe_size_combo->itemData(stripe_size_combo->currentIndex(), Qt::UserRole); if( m_stripe_box->isChecked() || m_extend){ args << "--stripes" << QString("%1").arg(stripes); args << "--stripesize" << QString("%1").arg( stripe_size.toLongLong() ); } if( !m_extend ){ if ( m_readonly_check->isChecked() ) args << "--permission" << "r" ; else args << "--permission" << "rw" ; if( !m_snapshot && !m_extend ){ if( m_zero_check->isChecked() ) args << "--zero" << "y"; else args << "--zero" << "n"; if( m_mirror_box->isChecked() ){ args << "--mirrors" << QString("%1").arg( mirrors - 1 ); if( m_skip_sync_check->isChecked() ) args << "--nosync"; if( m_mirrored_log->isChecked() ) args << "--mirrorlog" << "mirrored"; else if( m_disk_log->isChecked() ) args << "--mirrorlog" << "disk"; else args << "--mirrorlog" << "core"; } } } if( m_monitor_check->isEnabled() ){ args << "--monitor"; if( m_monitor_check->isChecked() ) args << "y"; else args << "n"; } if ( !inherited_button->isChecked() ){ // "inherited" is what we get if args << "--alloc"; // we don't pass "--alloc" at all if ( contiguous_button->isChecked() ) // passing "--alloc" "inherited" args << "contiguous" ; // doesn't work else if ( anywhere_button->isChecked() ) args << "anywhere" ; else if ( cling_button->isChecked() ) args << "cling" ; else args << "normal" ; } if(m_extend) extents -= m_lv->getExtents(); extents = roundExtentsToStripes(extents); args << "--extents" << QString("+%1").arg(extents); if( !m_extend && !m_snapshot ){ // create a standard volume program_to_run = "lvcreate"; if( ! (m_name_edit->text()).isEmpty() ) args << "--name" << m_name_edit->text(); args << m_vg->getName(); } else if( m_snapshot ){ // create a snapshot program_to_run = "lvcreate"; args << "--snapshot"; if( ! (m_name_edit->text()).isEmpty() ) args << "--name" << m_name_edit->text() ; args << m_lv->getFullName(); } else{ // extend the current volume program_to_run = "lvextend"; args << m_lv->getFullName(); } args << m_pv_checkbox->getNames(); args.prepend(program_to_run); return args; } // make the number of extents divivsible by the stripe X mirror count then round up long long LVCreateDialog::roundExtentsToStripes(long long extents) { const int stripes = getStripeCount(); const int mirrors = getMirrorCount(); const long long max_extents = getLargestVolume() / m_vg->getExtentSize(); // The next part should only need to reference stripes, not the mirror count // but a bug in lvm requires it. Remove this when fixed. if( stripes > 1 ){ if( extents % ( stripes * mirrors )){ extents = extents / ( stripes * mirrors ); extents = extents * ( stripes * mirrors ); if(extents + ( stripes * mirrors ) <= max_extents) extents += ( stripes * mirrors ); } } return extents; } // This function checks for problems that would make showing this dialog pointless // returns true if there are problems and is used to set m_bailout. bool LVCreateDialog::hasInitialErrors() { if( !m_vg->getFreeExtents() ){ KMessageBox::error(this, i18n("There is no free space left in the volume group") ); return true; } if(m_extend){ const QString warning_message = i18n("If this volume has a filesystem or data, it will need to be extended separately. " "Currently, only the ext2, ext3, ext4, xfs, jfs, ntfs and reiserfs file systems are " "supported for extension. The correct executable for extension must also be present. "); if( m_lv->isOrigin() ){ if( m_lv->isOpen() ){ KMessageBox::error(this, i18n("Snapshot origins cannot be extended while open or mounted") ); return true; } const QList snap_shots = m_lv->getSnapshots(); for(int x = 0; x < snap_shots.size(); x++){ if( snap_shots[x]->isOpen() ){ KMessageBox::error(this, i18n("Volumes cannot be extended with open or mounted snapshots") ); return true; } } } m_fs_can_extend = fs_can_extend( m_lv->getFilesystem() ); if( !( m_fs_can_extend || m_lv->isSnap() ) ){ if(KMessageBox::warningContinueCancel(NULL, warning_message, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) != KMessageBox::Continue){ return true; } } } return false; } bool LVCreateDialog::bailout() { return m_bailout; } void LVCreateDialog::commitChanges() { QStringList lvchange_args; hide(); if( !m_extend ){ ProcessProgress create_lv( argumentsLV() ); return; } else{ const QString mapper_path = m_lv->getMapperPath(); const QString fs = m_lv->getFilesystem(); if( m_lv->isOrigin() ){ lvchange_args << "lvchange" << "-an" << mapper_path; ProcessProgress deactivate_lv(lvchange_args); if( deactivate_lv.exitCode() ){ KMessageBox::error(0, i18n("Volume deactivation failed, volume not extended") ); return; } ProcessProgress extend_origin( argumentsLV() ); if( extend_origin.exitCode() ){ KMessageBox::error(0, i18n("Volume extension failed") ); return; } lvchange_args.clear(); lvchange_args << "lvchange" << "-ay" << mapper_path; ProcessProgress activate_lv(lvchange_args); if( activate_lv.exitCode() ){ KMessageBox::error(0, i18n("Volume activation failed, filesystem not extended") ); return; } if(m_fs_can_extend) fs_extend( m_lv->getMapperPath(), fs, m_lv->getMountPoints(), true ); return; } else{ ProcessProgress extend_lv( argumentsLV() ); if( !extend_lv.exitCode() && !m_lv->isSnap() && m_fs_can_extend ) fs_extend( mapper_path, fs, m_lv->getMountPoints(), true ); return; } } } kvpm-0.8.6/kvpm/lvchange.h0000644000175000017500000000430311733530416015646 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef LVCHANGE_H #define LVCHANGE_H #include #include #include #include #include #include #include #include class LogVol; class LVChangeDialog : public KDialog { Q_OBJECT LogVol *m_lv; QCheckBox *m_available_check, // Make the volume available *m_ro_check, // make the volume read only *m_refresh_check, // refresh the metadata *m_resync_check, // re-sync mirrors *m_udevsync_check, // sync with udev *m_persistent_check; // Set persistent kernel device numbers QRadioButton *m_normal_button, // allocation policy *m_contiguous_button, *m_inherit_button, *m_anywhere_button, *m_cling_button; QRadioButton *m_poll_button, *m_nopoll_button, *m_monitor_button, // dmeventd monitoring *m_nomonitor_button, *m_ignore_button; // ignore dmeventd monitoring QGroupBox *m_devnum_box, *m_dmeventd_box, *m_polling_box, *m_udevsync_box, *m_tag_group, *m_alloc_box; KLineEdit *m_minor_edit, // User entered device minor number *m_major_edit, // User entered device major number *m_tag_edit; // new tag KComboBox *m_deltag_combo; QWidget *buildGeneralTab(); QWidget *buildMirrorTab(); QWidget *buildAdvancedTab(); QStringList arguments(); public: explicit LVChangeDialog(LogVol *const volume, QWidget *parent = 0); private slots: void commitChanges(); void resetOkButton(); void refreshAndAvailableCheck(); }; #endif kvpm-0.8.6/kvpm/CMakeLists.txt0000644000175000017500000000340111733530416016444 0ustar benscottbenscott set(kvpm_SRCS changemirror.cpp kvpmconfigdialog.cpp deviceproperties.cpp devicepropertiesstack.cpp devicesizechart.cpp devicesizechartseg.cpp devicetree.cpp devicetab.cpp executablefinder.cpp deviceactionsmenu.cpp fsck.cpp fsblocksize.cpp fsdata.cpp fsextend.cpp dualselectorbox.cpp fsprobe.cpp fsreduce.cpp logvol.cpp lvactionsmenu.cpp lvchange.cpp lvrename.cpp lvcreate.cpp lvproperties.cpp lvpropertiesstack.cpp lvreduce.cpp lvremove.cpp lvsizechart.cpp main.cpp maxfs.cpp mkfs.cpp masterlist.cpp mount.cpp lvsizechartseg.cpp mountentry.cpp mounttables.cpp maintabwidget.cpp pvextend.cpp pvreduce.cpp partremove.cpp pvgroupbox.cpp partitiongraphic.cpp partadd.cpp pvproperties.cpp pvpropertiesstack.cpp physvol.cpp partchange.cpp removefs.cpp progressbox.cpp misc.cpp pedexceptions.cpp processprogress.cpp pvchange.cpp snapmerge.cpp pvmove.cpp pvtree.cpp removemissing.cpp removemirror.cpp sizeselectorbox.cpp removemirrorleg.cpp storagepartition.cpp storagedevice.cpp topwindow.cpp tablecreate.cpp unmount.cpp vgchange.cpp vgexport.cpp vgimport.cpp vgcreate.cpp vgextend.cpp vginfolabels.cpp vgreduce.cpp vgremove.cpp vgtree.cpp volgroup.cpp vgreduceone.cpp vgrename.cpp volumegrouptab.cpp vgsplit.cpp vgmerge.cpp) kde4_add_executable(kvpm ${kvpm_SRCS}) target_link_libraries(kvpm ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} ${QT_LIBRARIES} blkid parted lvm2app) INSTALL( TARGETS kvpm DESTINATION ${SBIN_INSTALL_DIR} ) IF (CMAKE_COMPILER_IS_GNUCXX) set_target_properties(kvpm PROPERTIES COMPILE_FLAGS "-std=gnu++98") set_target_properties(kvpm PROPERTIES LINK_FLAGS "-Wl,--as-needed") ENDIF(CMAKE_COMPILER_IS_GNUCXX) SET(CMAKE_CXX_FLAGS_DEBUG "-ggdb")kvpm-0.8.6/kvpm/devicesizechart.h0000644000175000017500000000214111733530416017231 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef STORAGEDEVICESIZECHART_H #define STORAGEDEVICESIZECHART_H #include #include #include #include #include class DeviceTree; class DeviceSizeChart : public QFrame { Q_OBJECT QHBoxLayout *m_layout; QHBoxLayout *m_extended_layout; // The layout for chart segments inside an extended partition QTreeWidget *m_tree; QList m_segments, m_extended_segments; // Segments of the bar chart, not the disk. QList m_ratios, m_extended_ratios; public: DeviceSizeChart(QWidget *parent); void resizeEvent(QResizeEvent *event); public slots: void setNewDevice(QTreeWidgetItem *deviceItem); }; #endif kvpm-0.8.6/kvpm/vgexport.h0000644000175000017500000000075511733530416015744 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGEXPORT_H #define VGEXPORT_H class VolGroup; bool import_vg(VolGroup *const volumeGroup); #endif kvpm-0.8.6/kvpm/vginfolabels.cpp0000644000175000017500000001422311733530416017067 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vginfolabels.h" #include #include #include #include #include "misc.h" #include "volgroup.h" VGInfoLabels::VGInfoLabels(VolGroup *const group, QWidget *parent) : QFrame(parent) { QLabel *extent_size_label, *size_label, *used_label, *free_label, *lvm_fmt_label, *resizable_label, *clustered_label, *allocatable_label, *max_lv_label, *max_pv_label, *policy_label, *mda_label, *uuid_label; setFrameStyle(QFrame::Sunken | QFrame::StyledPanel); setLineWidth(2); QString clustered, resizable; QVBoxLayout *upper_layout = new QVBoxLayout(); QHBoxLayout *hlayout1 = new QHBoxLayout(); QHBoxLayout *hlayout2 = new QHBoxLayout(); upper_layout->setMargin(0); hlayout1->setMargin(0); hlayout2->setMargin(0); hlayout1->setSpacing(0); hlayout1->setSpacing(0); upper_layout->addLayout(hlayout1); upper_layout->addLayout(hlayout2); QVBoxLayout *vlayout1 = new QVBoxLayout(); QVBoxLayout *vlayout2 = new QVBoxLayout(); QVBoxLayout *vlayout3 = new QVBoxLayout(); QVBoxLayout *vlayout4 = new QVBoxLayout(); QVBoxLayout *vlayout5 = new QVBoxLayout(); QVBoxLayout *vlayout6 = new QVBoxLayout(); QVBoxLayout *vlayout7 = new QVBoxLayout(); QWidget *label_widget1 = new QWidget(); QWidget *label_widget2 = new QWidget(); QWidget *label_widget3 = new QWidget(); QWidget *label_widget4 = new QWidget(); QWidget *label_widget5 = new QWidget(); QWidget *label_widget6 = new QWidget(); QWidget *label_widget7 = new QWidget(); label_widget1->setBackgroundRole(QPalette::Base); label_widget1->setAutoFillBackground(true); label_widget2->setBackgroundRole(QPalette::AlternateBase); label_widget2->setAutoFillBackground(true); label_widget3->setBackgroundRole(QPalette::Base); label_widget3->setAutoFillBackground(true); label_widget4->setBackgroundRole(QPalette::AlternateBase); label_widget4->setAutoFillBackground(true); label_widget5->setBackgroundRole(QPalette::Base); label_widget5->setAutoFillBackground(true); label_widget6->setBackgroundRole(QPalette::AlternateBase); label_widget6->setAutoFillBackground(true); label_widget7->setBackgroundRole(QPalette::Base); label_widget7->setAutoFillBackground(true); label_widget1->setLayout(vlayout1); label_widget2->setLayout(vlayout2); label_widget3->setLayout(vlayout3); label_widget4->setLayout(vlayout4); label_widget5->setLayout(vlayout5); label_widget6->setLayout(vlayout6); label_widget7->setLayout(vlayout7); if(group->isResizable()) resizable = "Yes"; else resizable = "No"; if(group->isClustered()) clustered = "Yes"; else clustered = "No"; if(group->isPartial()){ hlayout2->addStretch(); hlayout2->addWidget( new QLabel( i18n("Warning: Partial Volume Group") ) ); hlayout2->addStretch(); } else if(group->isExported()){ hlayout2->addStretch(); hlayout2->addWidget( new QLabel( i18n("Exported Volume Group") ) ); hlayout2->addStretch(); } KLocale *locale = KGlobal::locale(); KConfigSkeleton skeleton; bool use_si_units; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", use_si_units, false); if(use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); used_label = new QLabel( i18nc("Space used up", "Used: %1", locale->formatByteSize(group->getUsedSpace())) ); free_label = new QLabel( i18nc("Space not used", "Free: %1", locale->formatByteSize(group->getFreeSpace())) ); size_label = new QLabel( i18nc("Total space on device", "Total: %1", locale->formatByteSize(group->getSize()))); lvm_fmt_label = new QLabel( i18n("Format: %1", group->getFormat()) ); policy_label = new QLabel( i18n("Policy: %1", group->getPolicy()) ); resizable_label = new QLabel( i18n("Resizable: %1", resizable) ); clustered_label = new QLabel( i18n("Clustered: %1", clustered) ); allocatable_label = new QLabel( i18n("Allocatable: %1", locale->formatByteSize(group->getAllocatableSpace())) ); extent_size_label = new QLabel( i18n("Extent size: %1", locale->formatByteSize(group->getExtentSize())) ); mda_label = new QLabel( i18n("MDA count: %1", group->getMdaCount() ) ); uuid_label = new QLabel( i18n("UUID: %1", group->getUuid()) ); uuid_label->setWordWrap(true); vlayout1->addWidget(size_label); vlayout1->addWidget(allocatable_label); vlayout2->addWidget(used_label); vlayout2->addWidget(free_label); vlayout3->addWidget(clustered_label); vlayout3->addWidget(lvm_fmt_label); vlayout4->addWidget(extent_size_label); vlayout4->addWidget(policy_label); vlayout5->addWidget(resizable_label); vlayout5->addWidget(mda_label); vlayout6->addWidget(uuid_label); hlayout1->addWidget(label_widget1); hlayout1->addWidget(label_widget2); hlayout1->addWidget(label_widget3); hlayout1->addWidget(label_widget4); hlayout1->addWidget(label_widget5); hlayout1->addWidget(label_widget6); if(group->getPvMax()) max_pv_label = new QLabel( i18n("Max pvs: %1", group->getPvMax()) ); else max_pv_label = new QLabel( i18n("Max pvs: Unlimited") ); if(group->getLvMax()) max_lv_label = new QLabel( i18n("Max lvs: %1", group->getLvMax()) ); else max_lv_label = new QLabel( i18n("Max lvs: Unlimited") ); vlayout7->addWidget(max_pv_label); vlayout7->addWidget(max_lv_label); hlayout1->addWidget(label_widget7); if( !group->getLvMax() && !group->getPvMax() ) label_widget7->hide(); setLayout(upper_layout); } kvpm-0.8.6/kvpm/storagedevice.h0000644000175000017500000000306611733530416016710 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef STORAGEDEVICE_H #define STORAGEDEVICE_H #include #include #include class MountTables; class PhysVol; class StoragePartition; class StorageDevice : public QObject { Q_OBJECT long long m_device_size; // Size in bytes QString m_device_path; QString m_disk_label; QString m_hardware; long long m_sector_size; long long m_physical_sector_size; bool m_is_writable; bool m_is_busy; bool m_is_pv; PhysVol *m_pv; int m_freespace_count; QList m_storage_partitions; public: StorageDevice(PedDevice * const pedDevice, const QList pvList, MountTables *const mountTables); ~StorageDevice(); QString getName(); QString getDiskLabel(); QString getHardware(); QList getStoragePartitions(); int getPartitionCount(); int getRealPartitionCount(); long long getSize(); // Size in bytes long long getSectorSize(); long long getPhysicalSectorSize(); bool isWritable(); bool isBusy(); bool isPhysicalVolume(); PhysVol *getPhysicalVolume(); }; #endif kvpm-0.8.6/kvpm/lvrename.h0000644000175000017500000000163611733530416015676 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef LVRENAME_H #define LVRENAME_H #include #include #include #include class LogVol; class LVRenameDialog : public KDialog { Q_OBJECT LogVol *m_lv; QString m_old_name; QString m_vg_name; KLineEdit *m_new_name; QRegExpValidator *m_name_validator; QString getNewMapperPath(); public: explicit LVRenameDialog(LogVol *const volume, QWidget *parent = 0); private slots: void validateName(QString name); void commitChanges(); }; #endif kvpm-0.8.6/kvpm/vgextend.cpp0000644000175000017500000001422511733530416016242 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vgextend.h" #include #include #include #include #include #include #include #include "masterlist.h" #include "misc.h" #include "progressbox.h" #include "pvgroupbox.h" #include "storagedevice.h" #include "storagepartition.h" #include "topwindow.h" #include "vgcreate.h" #include "volgroup.h" VGExtendDialog::VGExtendDialog(VolGroup *const group, QWidget *parent) : KDialog(parent), m_vg(group) { m_bailout = false; QList devices; QList partitions; const QString warning = i18n("If a device or partition is added to a volume group, " "any data currently on that device or partition will be lost."); getUsablePvs(devices, partitions); if( partitions.size() + devices.size() > 0 ){ if(KMessageBox::warningContinueCancel(NULL, warning, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) == KMessageBox::Continue){ buildDialog(devices, partitions); } else{ m_bailout = true; } } else{ m_bailout = true; KMessageBox::error(0, i18n("No unused potential physical volumes found") ); } } VGExtendDialog::VGExtendDialog(VolGroup *const group, StorageDevice *const device, StoragePartition *const partition, QWidget *parent) : KDialog(parent), m_vg(group) { m_bailout = false; QList devices; QList partitions; if(device != NULL) devices.append(device); else partitions.append(partition); QString warning = i18n("If a device or partition is added to a volume group, " "any data currently on that device or partition will be lost."); if(KMessageBox::warningContinueCancel(NULL, warning, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) == KMessageBox::Continue){ buildDialog(devices, partitions); } else{ m_bailout = true; } } void VGExtendDialog::commitChanges() { const QByteArray vg_name = m_vg->getName().toLocal8Bit(); const QStringList pv_names = m_pv_checkbox->getNames(); QByteArray pv_name; lvm_t lvm = MasterList::getLvm(); vg_t vg_dm; ProgressBox *const progress_box = TopWindow::getProgressBox(); progress_box->setRange(0, pv_names.size()); progress_box->setText("Extending VG"); hide(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); if( (vg_dm = lvm_vg_open(lvm, vg_name.data(), "w", 0 )) ){ for(int x = 0; x < pv_names.size(); x++){ progress_box->setValue(x); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); pv_name = pv_names[x].toLocal8Bit(); if( lvm_vg_extend(vg_dm, pv_name.data()) ) KMessageBox::error(0, QString(lvm_errmsg(lvm))); } if( lvm_vg_write(vg_dm) ) KMessageBox::error(0, QString(lvm_errmsg(lvm))); lvm_vg_close(vg_dm); progress_box->reset(); return; } KMessageBox::error(0, QString(lvm_errmsg(lvm))); progress_box->reset(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); return; } void VGExtendDialog::validateOK() { if( m_pv_checkbox->getRemainingSpace() ) enableButtonOk(true); else enableButtonOk(false); } void VGExtendDialog::buildDialog(QList devices, QList partitions) { setWindowTitle( i18n("Extend Volume Group") ); QWidget *const dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *const layout = new QVBoxLayout(); dialog_body->setLayout(layout); QLabel *const title = new QLabel( i18n("Extend Volume Group: %1", m_vg->getName()) ); title->setAlignment(Qt::AlignCenter); layout->addSpacing(5); layout->addWidget(title); layout->addSpacing(5); m_pv_checkbox = new PvGroupBox( devices, partitions, m_vg->getExtentSize() ); layout->addWidget(m_pv_checkbox); connect(m_pv_checkbox, SIGNAL(stateChanged()), this, SLOT(validateOK())); connect(this, SIGNAL(okClicked()), this, SLOT(commitChanges())); } void VGExtendDialog::getUsablePvs(QList &devices, QList &partitions) { QList all_dev = MasterList::getStorageDevices(); QList all_part; for(int x = 0; x < all_dev.size(); x++){ if( (all_dev[x]->getRealPartitionCount() == 0) && ( !all_dev[x]->isBusy()) && ( !all_dev[x]->isPhysicalVolume() )) { devices.append( all_dev[x] ); } else if( all_dev[x]->getRealPartitionCount() > 0 ){ all_part = all_dev[x]->getStoragePartitions(); for(int y = 0; y < all_part.size(); y++){ if( ( !all_part[y]->isBusy() ) && (! all_part[y]->isPhysicalVolume() ) && (( all_part[y]->isNormal() ) || ( all_part[y]->isLogical() ))) { partitions.append(all_part[y]); } } } } } bool VGExtendDialog::bailout() { return m_bailout; } kvpm-0.8.6/kvpm/vgchange.cpp0000644000175000017500000003507111733530416016202 0ustar benscottbenscott/* * * * Copyright (C) 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vgchange.h" #include #include #include #include "logvol.h" #include "misc.h" #include "processprogress.h" #include "volgroup.h" bool change_vg(VolGroup *const volumeGroup) { VGChangeDialog dialog(volumeGroup); dialog.exec(); if(dialog.result() == QDialog::Accepted){ ProcessProgress vgchange( dialog.arguments() ); return true; } else return false; } VGChangeDialog::VGChangeDialog(VolGroup *const volumeGroup, QWidget *parent) : KDialog(parent), m_vg(volumeGroup) { m_vg_name = m_vg->getName(); setWindowTitle( i18n("Change Volume Group Attributes") ); QWidget *dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *layout = new QVBoxLayout(); dialog_body->setLayout(layout); QLabel *name_label = new QLabel( i18n("Volume group: %1", m_vg_name) ); name_label->setAlignment(Qt::AlignCenter); layout->addWidget(name_label); QGroupBox *upper_box = new QGroupBox(); QHBoxLayout *upper_layout = new QHBoxLayout(); QVBoxLayout *upper_left_layout = new QVBoxLayout(); QGroupBox *alloc_box = new QGroupBox( i18n("Allocation policy") ); QVBoxLayout *alloc_layout = new QVBoxLayout(); upper_box->setLayout(upper_layout); alloc_box->setLayout(alloc_layout); upper_layout->addLayout(upper_left_layout); upper_left_layout->addWidget(alloc_box); layout->addWidget(upper_box); m_normal = new QRadioButton( i18nc("The usual way", "Normal") ); m_contiguous = new QRadioButton( i18n("Contiguous") ); m_anywhere = new QRadioButton( i18n("Anwhere") ); m_cling = new QRadioButton( i18n("Cling") ); QString policy = m_vg->getPolicy(); if(policy == "contiguous") m_contiguous->setChecked(true); else if(policy == "anywhere") m_anywhere->setChecked(true); else if(policy == "cling") m_cling->setChecked(true); else m_normal->setChecked(true); alloc_layout->addWidget(m_normal); alloc_layout->addWidget(m_contiguous); alloc_layout->addWidget(m_anywhere); alloc_layout->addWidget(m_cling); QGroupBox *extent_box = new QGroupBox( i18n("Extent size") ); QVBoxLayout *extent_layout = new QVBoxLayout(); extent_box->setLayout(extent_layout); extent_layout->addStretch(); upper_left_layout->addWidget(extent_box); upper_left_layout->addStretch(); m_extent_size_combo = new KComboBox(); m_extent_size_combo->insertItem(0,"1"); m_extent_size_combo->insertItem(1,"2"); m_extent_size_combo->insertItem(2,"4"); m_extent_size_combo->insertItem(3,"8"); m_extent_size_combo->insertItem(4,"16"); m_extent_size_combo->insertItem(5,"32"); m_extent_size_combo->insertItem(6,"64"); m_extent_size_combo->insertItem(7,"128"); m_extent_size_combo->insertItem(8,"256"); m_extent_size_combo->insertItem(9,"512"); m_extent_size_combo->setInsertPolicy(QComboBox::NoInsert); m_extent_size_combo->setCurrentIndex(2); m_extent_suffix_combo = new KComboBox(); m_extent_suffix_combo->insertItem(0,"KiB"); m_extent_suffix_combo->insertItem(1,"MiB"); m_extent_suffix_combo->insertItem(2,"GiB"); m_extent_suffix_combo->setInsertPolicy(QComboBox::NoInsert); m_extent_suffix_combo->setCurrentIndex(1); long current_extent_size = m_vg->getExtentSize() / 1024; if(current_extent_size <= 512) m_extent_suffix_combo->setCurrentIndex(0); else if( ((current_extent_size /= 1024)) <= 512) m_extent_suffix_combo->setCurrentIndex(1); else{ m_extent_suffix_combo->setCurrentIndex(2); current_extent_size /= 1024; } for(int x = 0; x < 10; x++){ if( current_extent_size == m_extent_size_combo->itemText(x).toLong() ) m_extent_size_combo->setCurrentIndex(x); } QHBoxLayout *combo_layout = new QHBoxLayout(); extent_layout->addLayout(combo_layout); extent_layout->addStretch(); combo_layout->addWidget(m_extent_size_combo); combo_layout->addWidget(m_extent_suffix_combo); connect(m_extent_suffix_combo, SIGNAL(activated(int)), this, SLOT(limitExtentSize(int))); QGroupBox *misc_box = new QGroupBox(); QVBoxLayout *misc_layout = new QVBoxLayout(); misc_box->setLayout(misc_layout); upper_layout->addWidget(misc_box); m_resize = new QCheckBox( i18n("Allow physical volume addition and removal") ); m_resize->setChecked( m_vg->isResizable() ); misc_layout->addWidget(m_resize); m_clustered = new QCheckBox( i18n("Cluster aware") ); m_clustered->setChecked( m_vg->isClustered() ); misc_layout->addWidget(m_clustered); m_refresh = new QCheckBox( i18n("Refresh metadata") ); misc_layout->addWidget(m_refresh); m_uuid = new QCheckBox( i18n("Generate new UUID fo group") ); if( m_vg->isActive() ) m_uuid->setEnabled(false); misc_layout->addWidget(m_uuid); QHBoxLayout *middle_layout = new QHBoxLayout(); layout->addLayout(middle_layout); m_available_box = new QGroupBox("Change group availability"); QVBoxLayout *available_layout = new QVBoxLayout(); m_available_yes = new QRadioButton( i18n("Make all logical volumes available") ); m_available_no = new QRadioButton( i18n("Make all logical volumes unavailable") ); m_available_yes->setChecked(true); available_layout->addWidget(m_available_yes); available_layout->addWidget(m_available_no); m_available_box->setLayout(available_layout); m_available_box->setCheckable(true); m_available_box->setChecked(false); middle_layout->addWidget(m_available_box); m_polling_box = new QGroupBox("Change group polling"); QVBoxLayout *polling_layout = new QVBoxLayout(); m_polling_yes = new QRadioButton( i18n("Start polling") ); m_polling_no = new QRadioButton( i18n("Stop polling") ); m_polling_yes->setChecked(true); polling_layout->addWidget(m_polling_yes); polling_layout->addWidget(m_polling_no); m_polling_box->setLayout(polling_layout); m_polling_box->setCheckable(true); m_polling_box->setChecked(false); middle_layout->addWidget(m_polling_box); QList lv_list = volumeGroup->getLogicalVolumes(); for(int x = lv_list.size() - 1;x >= 0 ;x--){ // replace snap containers with first level children if( lv_list[x]->isSnapContainer() ){ lv_list.append( lv_list[x]->getChildren() ); lv_list.removeAt(x); } } for(int x = lv_list.size() - 1;x >=0 ;x--){ if( lv_list[x]->isMounted() ){ m_available_yes->setChecked(true); m_available_no->setEnabled(false); break; } } // We don't want the limit set to less than the number already in existence! int lv_count = m_vg->getLvCount(); if(lv_count <= 0) lv_count = 1; m_limit_box = new QGroupBox( i18n("Change maximum limit for number of volumes") ); QVBoxLayout *const limit_groupbox_layout = new QVBoxLayout(); m_limit_box->setLayout(limit_groupbox_layout); QLabel *const lv_limit_label = new QLabel( i18n("Logical volumes") ); QLabel *const pv_limit_label = new QLabel( i18n("Physical volumes") ); QString lv_current_max; QString pv_current_max; if(m_vg->getLvMax() > 0) lv_current_max = QString("%1").arg(m_vg->getLvMax()); else lv_current_max = QString("unlimited"); if(m_vg->getPvMax() > 0) pv_current_max = QString("%1").arg(m_vg->getPvMax()); else pv_current_max = QString("unlimited"); QLabel *const lv_min_label = new QLabel( i18n("Currently: %1 Minimum: %2", lv_current_max, m_vg->getLvCount()) ); QLabel *const pv_min_label = new QLabel( i18n("Currently: %1 Minimum: %2", pv_current_max, m_vg->getPvCount()) ); QHBoxLayout *const lv_layout = new QHBoxLayout(); QHBoxLayout *const pv_layout = new QHBoxLayout(); m_max_pvs_spin = new QSpinBox(); m_max_lvs_spin = new QSpinBox(); if(m_vg->getFormat() == "lvm1"){ m_max_lvs_spin->setEnabled(true); m_max_lvs_spin->setRange(lv_count, 255); } else{ m_limit_box->setCheckable(true); m_limit_box->setChecked(false); m_limit_box->setEnabled(true); m_max_lvs_spin->setSpecialValueText( i18n("unlimited") ); m_max_lvs_spin->setRange(0, 32767); // does anyone need more than 32 thousand? } m_max_lvs_spin->setValue(m_vg->getLvMax()); lv_layout->addWidget(lv_limit_label); lv_layout->addWidget(m_max_lvs_spin); lv_layout->addWidget(lv_min_label); lv_layout->addStretch(); pv_layout->addWidget(pv_limit_label); pv_layout->addWidget(m_max_pvs_spin); pv_layout->addWidget(pv_min_label); pv_layout->addStretch(); limit_groupbox_layout->addLayout(lv_layout); limit_groupbox_layout->addLayout(pv_layout); // We don't want the limit set to less than the number already in existence! int pv_count = m_vg->getPvCount(); if(pv_count <= 0) pv_count = 1; if(m_vg->getFormat() == "lvm1"){ m_max_pvs_spin->setEnabled(true); m_max_pvs_spin->setRange(pv_count, 255); } else{ m_max_pvs_spin->setSpecialValueText( i18n("unlimited") ); m_max_pvs_spin->setRange(0, 32767); // does anyone need more than 32 thousand? } m_max_pvs_spin->setValue(m_vg->getPvMax()); layout->addWidget(m_limit_box); connect(m_normal, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_cling, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_anywhere, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_contiguous, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_available_yes, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_available_no, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_polling_yes, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_polling_no, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_available_box, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_polling_box, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_resize, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_clustered, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_refresh, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_uuid, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_limit_box , SIGNAL(toggled(bool)), this, SLOT(resetOkButton())); connect(m_max_lvs_spin, SIGNAL(valueChanged(int)), this, SLOT(resetOkButton())); connect(m_max_pvs_spin, SIGNAL(valueChanged(int)), this, SLOT(resetOkButton())); connect(m_extent_size_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(resetOkButton())); connect(m_extent_suffix_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(resetOkButton())); enableButtonOk(false); } QStringList VGChangeDialog::arguments() { QString new_policy; QStringList args; args << "vgchange"; if(m_contiguous->isChecked()) new_policy = "contiguous"; else if(m_anywhere->isChecked()) new_policy = "anywhere"; else if(m_cling->isChecked()) new_policy = "cling"; else new_policy = "normal"; if( m_vg->getPolicy() != new_policy ) args << "--alloc" << new_policy; if( m_available_box->isChecked() ){ if( m_available_yes->isChecked() ) args << "--available" << "y"; else args << "--available" << "n"; } if(m_limit_box->isChecked()){ if(m_max_lvs_spin->value() != m_vg->getLvMax()) args << "--logicalvolume" << QString("%1").arg(m_max_lvs_spin->value()); if(m_max_pvs_spin->value() != m_vg->getPvMax()) args << "--maxphysicalvolumes" << QString("%1").arg(m_max_pvs_spin->value()); } if( m_resize->isChecked() != m_vg->isResizable() ){ if( m_resize->isChecked() ) args << "--resizeable" << "y"; else args << "--resizeable" << "n"; } if( m_clustered->isChecked() != m_vg->isClustered() ){ if( m_clustered->isChecked() ) args << "--clustered" << "y"; else args << "--clustered" << "n"; } if( m_refresh->isChecked() ) args << "--refresh"; if( m_uuid->isChecked() ) args << "--uuid"; long new_extent_size = m_extent_size_combo->currentText().toLong(); new_extent_size *= 1024; if( m_extent_suffix_combo->currentIndex() > 0 ) new_extent_size *= 1024; if( m_extent_suffix_combo->currentIndex() > 1 ) new_extent_size *= 1024; if( new_extent_size != m_vg->getExtentSize() ){ args << "--physicalextentsize" << QString("%1b").arg(new_extent_size); } if( m_polling_box->isChecked() ){ if( m_polling_yes->isChecked() ) args << "--poll" << "y"; else args << "--poll" << "n"; } args << m_vg->getName(); return args; } void VGChangeDialog::resetOkButton(){ if( arguments().size() > 2 ) enableButtonOk(true); else enableButtonOk(false); if(m_limit_box->isChecked()){ if((m_max_lvs_spin->value() < m_vg->getLvCount()) && (m_max_lvs_spin->value() != 0)) enableButtonOk(false); if((m_max_pvs_spin->value() < m_vg->getPvCount()) && (m_max_pvs_spin->value() != 0)) enableButtonOk(false); } } void VGChangeDialog::limitExtentSize(int index){ int extent_index; if( index > 1 ){ // Gigabytes selected as suffix, more than 2Gib forbidden if( m_extent_size_combo->currentIndex() > 2 ) m_extent_size_combo->setCurrentIndex(0); m_extent_size_combo->setMaxCount(2); } else{ extent_index = m_extent_size_combo->currentIndex(); m_extent_size_combo->setMaxCount(10); m_extent_size_combo->setInsertPolicy(QComboBox::InsertAtBottom); m_extent_size_combo->insertItem(3,"4"); m_extent_size_combo->insertItem(3,"8"); m_extent_size_combo->insertItem(4,"16"); m_extent_size_combo->insertItem(5,"32"); m_extent_size_combo->insertItem(6,"64"); m_extent_size_combo->insertItem(7,"128"); m_extent_size_combo->insertItem(8,"256"); m_extent_size_combo->insertItem(9,"512"); m_extent_size_combo->setInsertPolicy(QComboBox::NoInsert); m_extent_size_combo->setCurrentIndex(extent_index); } } kvpm-0.8.6/kvpm/logvol.cpp0000644000175000017500000005061411733530416015722 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "logvol.h" #include #include "fsdata.h" #include "fsprobe.h" #include "mountentry.h" #include "mounttables.h" #include "physvol.h" #include "storagedevice.h" #include "volgroup.h" /* Some information about a logical volume pertains to the entire volume while other information only applies to a segement in the volume. The volume keeps a list of Segment structures for segment information */ struct Segment { int m_stripes; // number of stripes in segment int m_stripe_size; long long m_size; // segment size (bytes) QStringList m_device_path; // full path of physical volume QList m_starting_extent; // first extent on physical volume // for this segment }; LogVol::LogVol(lv_t lvmLV, vg_t lvmVG, VolGroup *const vg, LogVol *const lvParent, MountTables *const tables, bool orphan) : m_vg(vg), m_lv_parent(lvParent), m_orphan(orphan) { m_snap_container = false; m_tables = tables; rescan(lvmLV, lvmVG); } LogVol::~LogVol() { QList children = getChildren(); for(int x = m_mount_entries.size() - 1; x >= 0; x--) delete m_mount_entries.takeAt(x); while( children.size() ) delete children.takeAt(0); while( m_segments.size() ) delete m_segments.takeAt(0); } void LogVol::rescan(lv_t lvmLV, vg_t lvmVG) // lv_t seems to change -- why? { QString additional_state; QByteArray flags; lvm_property_value value; bool was_snap_container = m_snap_container; m_snap_container = false; m_under_conversion = false; m_is_origin = false; m_merging = false; m_mirror = false; m_mirror_leg = false; m_mirror_log = false; m_snap = false; m_pvmove = false; m_valid = true; m_virtual = false; /* The child LogVols need to be re-used, and re-parented, in the case of down converted containers flag down converting in the following code */ value = lvm_lv_get_property(lvmLV, "lv_name"); m_lv_name = QString(value.value.string).trimmed(); m_lv_full_name = m_vg->getName() + '/' + m_lv_name; processSegments(lvmLV); // sets m_mirror according to segment property "regionsize" -- total hack! value = lvm_lv_get_property(lvmLV, "lv_path"); m_lv_mapper_path = QString(value.value.string).trimmed(); value = lvm_lv_get_property(lvmLV, "mirror_log"); m_log = QString(value.value.string).trimmed(); QList lvm_child_snapshots; lvm_child_snapshots.append( getLvmSnapshots(lvmVG) ); if( ( ! lvm_child_snapshots.isEmpty() ) && m_lv_parent == NULL ){ m_snap_container = true; m_seg_total = 1; } else{ m_snap_container = false; value = lvm_lv_get_property(lvmLV, "seg_count"); m_seg_total = value.value.integer; } value = lvm_lv_get_property(lvmLV, "lv_attr"); flags.append(value.value.string); switch( flags[0] ){ case 'c': m_type = "under conversion"; m_mirror = true; m_under_conversion = true; break; case 'I': m_type = "mirror leg"; additional_state = "un-synced"; m_mirror_leg = true; break; case 'i': m_type = "mirror leg"; additional_state = "synced"; m_mirror_leg = true; break; case 'L': case 'l': m_type = "mirror log"; m_mirror_log = true; break; case 'M': // mirror logs can be mirrors themselves -- see below m_type = "mirror"; m_mirror = true; break; case 'm': m_type = "mirror"; // Origin status overides mirror status in the flags if this is both m_mirror = true; // We split it below -- snap_containers are origins and the lv is a mirror break; case 'O': m_type = "origin"; additional_state = "merging"; m_is_origin = true; m_merging = true; break; case 'o': m_type = "origin"; m_is_origin = true; break; case 'p': m_type = "pvmove"; m_pvmove = true; break; case 's': m_type = "snapshot"; m_snap = true; break; case 'S': if( flags[4] != 'I' ){ // When 'S' stops getting used for Invalid and only merging - remove this m_type = "snapshot"; additional_state = "merging"; m_snap = true; m_merging = true; } else{ m_type = "snapshot"; m_snap = true; m_merging = false; } break; case 'v': m_type = "virtual"; m_virtual = true; break; default: m_type = "linear"; break; } switch(flags[1]){ case 'w': m_writable = true; break; default: m_writable = false; } m_alloc_locked = false; switch( flags[2] ){ case 'C': m_alloc_locked = true; case 'c': m_policy = "Contiguous"; break; case 'L': m_alloc_locked = true; case 'l': m_policy = "Cling"; break; case 'N': m_alloc_locked = true; case 'n': m_policy = "Normal"; break; case 'A': m_alloc_locked = true; case 'a': m_policy = "Anywhere"; break; case 'I': m_alloc_locked = true; case 'i': m_policy = "Inherited"; break; default: m_policy = "Other"; break; } if(m_alloc_locked) m_policy.append(" (locked)"); switch(flags[3]){ case 'm': m_fixed = true; break; default: m_fixed = false; } m_active = false; switch(flags[4]){ case '-': m_state = "inactive"; break; case 'a': m_state = "active"; m_active = true; break; case 's': m_state = "suspended"; break; case 'I': m_state = "invalid"; m_valid = false; break; case 'S': m_state = "suspended"; break; case 'd': m_state = "no table"; break; case 'i': m_state = "inactive table"; break; default: m_state = "unknown"; } if( !additional_state.isEmpty() ) m_state = additional_state + " / " + m_state; switch(flags[5]){ case 'o': m_open = true; break; default: m_open = false; } if( m_lv_name.contains("_mlog", Qt::CaseSensitive) ){ m_mirror_log = true; // this needs to be here in case it is a mirrored mirror log m_lv_fs = ""; } else if( m_lv_name.contains("_mimagetmp_", Qt::CaseSensitive) ){ m_virtual = true; // This is to get lvactionsmenu to forbid doing anything to it m_lv_fs = ""; } else if( !m_mirror_log && !m_mirror_leg && !m_virtual){ m_lv_fs = fsprobe_getfstype2(m_lv_mapper_path); m_lv_fs_label = fsprobe_getfslabel(m_lv_mapper_path); m_lv_fs_uuid = fsprobe_getfsuuid(m_lv_mapper_path); } else{ m_lv_fs = ""; m_lv_fs_label = ""; m_lv_fs_uuid = ""; } value = lvm_lv_get_property(lvmLV, "lv_size"); m_size = value.value.integer; m_extents = m_size / m_vg->getExtentSize(); if(m_snap || m_merging){ value = lvm_lv_get_property(lvmLV, "origin"); m_origin = value.value.string; value = lvm_lv_get_property(lvmLV, "snap_percent"); if( value.is_valid ) m_snap_percent = (double)value.value.integer / 1.0e+6; else m_snap_percent = 0; } else m_origin = ""; if( (m_mirror_leg || m_mirror_log) ){ if(m_mirror_log && m_mirror_leg) m_type = m_type.replace("leg","log"); else if( (m_mirror || m_virtual) && !m_mirror_log ) m_mirror_leg = true; } value = lvm_lv_get_property(lvmLV, "copy_percent"); if(value.is_valid) m_copy_percent = (double)value.value.integer / 1.0e+6; else m_copy_percent = 0; value = lvm_lv_get_property(lvmLV, "lv_kernel_major"); m_major_device = value.value.integer; value = lvm_lv_get_property(lvmLV, "lv_kernel_minor"); m_minor_device = value.value.integer; /* value = lvm_lv_get_property(lvm_lv, "lv_major"); if(value.value.integer != -1) m_persistent = true; else m_persistent = false; */ if( m_snap_container && !was_snap_container ){ m_uuid = QUuid::createUuid().toString(); } else if( !m_snap_container ){ value = lvm_lv_get_property(lvmLV, "lv_uuid"); m_uuid = value.value.string; } value = lvm_lv_get_property(lvmLV, "lv_tags"); QString tag = value.value.string; m_tags = tag.split(',', QString::SkipEmptyParts); for(int x = m_mount_entries.size() - 1; x >= 0; x--) delete m_mount_entries.takeAt(x); m_mount_entries = m_tables->getMtabEntries(m_major_device, m_minor_device); m_fstab_mount_point = m_tables->getFstabMountPoint(this); m_mount_points.clear(); for(int x = 0; x < m_mount_entries.size(); x++) m_mount_points.append( m_mount_entries[x]->getMountPoint() ); m_mounted = !m_mount_points.isEmpty(); if(m_mounted){ FSData fs_data = get_fs_data( m_mount_points[0] ); if(fs_data.size > 0){ m_fs_size = fs_data.size * fs_data.block_size; m_fs_used = fs_data.used * fs_data.block_size; } else{ m_fs_size = -1; m_fs_used = -1; } } else{ m_fs_size = -1; m_fs_used = -1; } if( m_snap_container ){ m_type = "origin"; } else if( m_type.contains("origin", Qt::CaseInsensitive) && ! m_snap_container ){ if( m_mirror ) m_type = "mirror"; else m_type = "linear"; } insertChildren(lvmLV, lvmVG); countLegsAndLogs(); calculateTotalSize(); } void LogVol::insertChildren(lv_t lvmLV, vg_t lvmVG) { lv_t lvm_child; QByteArray child_name; QStringList child_name_list; QList lvm_child_snapshots; lvm_child_snapshots.append( getLvmSnapshots(lvmVG) ); while( m_lv_children.size() ) delete m_lv_children.takeAt(0); if( m_snap_container ){ for(int x = lvm_child_snapshots.size() - 1; x >= 0; x--) m_lv_children.append( new LogVol(lvm_child_snapshots[x], lvmVG, m_vg, this, m_tables) ); m_lv_children.append( new LogVol(lvmLV, lvmVG, m_vg, this, m_tables) ); } else{ child_name_list = removePvNames( getPvNamesAll() ); if( m_mirror && (! m_log.isEmpty()) ) child_name_list.append( m_log ); for(int x = child_name_list.size() - 1; x >= 0; x--){ child_name = child_name_list[x].toLocal8Bit(); lvm_child = lvm_lv_from_name(lvmVG, child_name.data()); m_lv_children.append( new LogVol(lvm_child, lvmVG, m_vg, this, m_tables) ); } } } void LogVol::countLegsAndLogs() { m_mirror_count = 0; m_log_count = 0; QList all_lvs_flat = getAllChildrenFlat(); LogVol *lv; if(m_mirror){ for(int x = all_lvs_flat.size() - 1; x >= 0; x--){ lv = all_lvs_flat[x]; if( lv->isMirrorLeg() && !lv->isMirror() && !lv->isMirrorLog() ) m_mirror_count++; if( lv->isMirrorLog() && !lv->isMirror() ) m_log_count++; } } else m_mirror_count = 1; // linear volumes count as mirror = 1; } QList LogVol::getLvmSnapshots(vg_t lvmVG) { lvm_property_value value; dm_list *lv_dm_list = lvm_vg_list_lvs(lvmVG); lvm_lv_list *lv_list; QList lvm_snapshots; if(lv_dm_list){ dm_list_iterate_items(lv_list, lv_dm_list){ value = lvm_lv_get_property(lv_list->lv, "origin"); if( QString(value.value.string).trimmed() == m_lv_name ) lvm_snapshots.append(lv_list->lv); } } return lvm_snapshots; } // Finds logical volumes that are children of this volume by // removing physical volumes from the list along with pvmove // volumes. One pvmove can be under several lvs so isn't // really a child. QStringList LogVol::removePvNames(QStringList names) { for(int x = names.size() - 1; x >= 0; x--){ if( names[x].startsWith("pvmove") ) names.removeAt(x); } QListIterator pv_itr( m_vg->getPhysicalVolumes() ); while( pv_itr.hasNext() ){ PhysVol *const pv = pv_itr.next(); for(int y = names.size() - 1; y >= 0; y--){ if( pv->getName() == names[y] ) names.removeAt(y); } } return names; } void LogVol::calculateTotalSize() { m_total_size = 0; if( m_lv_children.size() ){ for(int x = m_lv_children.size() - 1; x >= 0; x--) m_total_size += m_lv_children[x]->getTotalSize(); } else{ m_total_size = m_size; } } void LogVol::processSegments(lv_t lvmLV) { Segment *segment; QStringList devices_and_starts, temp; QString raw_paths; lvm_property_value value; dm_list* lvseg_dm_list = lvm_lv_list_lvsegs(lvmLV); lvm_lvseg_list *lvseg_list; lvseg_t lvm_lvseg; while( m_segments.size() ) delete m_segments.takeAt(0); if(lvseg_dm_list){ dm_list_iterate_items(lvseg_list, lvseg_dm_list){ lvm_lvseg = lvseg_list->lvseg; value = lvm_lvseg_get_property(lvm_lvseg, "regionsize"); if( value.is_valid ){ if( value.value.integer ) m_mirror = true; } segment = new Segment(); if(m_mirror){ segment->m_stripes = 1; segment->m_stripe_size = 1; segment->m_size = 1; } else{ value = lvm_lvseg_get_property(lvm_lvseg, "stripes"); segment->m_stripes = value.value.integer; value = lvm_lvseg_get_property(lvm_lvseg, "stripesize"); segment->m_stripe_size = value.value.integer; value = lvm_lvseg_get_property(lvm_lvseg, "seg_size"); segment->m_size = value.value.integer; } value = lvm_lvseg_get_property(lvm_lvseg, "devices"); if( value.is_valid ){ raw_paths = value.value.string; } if( raw_paths.size() ){ devices_and_starts = raw_paths.split(','); for(int x = 0; x < devices_and_starts.size(); x++){ temp = devices_and_starts[x].split('('); segment->m_device_path.append( temp[0] ); segment->m_starting_extent.append( ( temp[1].remove(')') ).toLongLong() ); } } m_segments.append(segment); } } } QList LogVol::getChildren() { return m_lv_children; } QList LogVol::takeChildren() { QList children = m_lv_children; m_lv_children.clear(); return children; } QList LogVol::getAllChildrenFlat() { QList flat_list = m_lv_children; long child_size = m_lv_children.size(); for(int x = 0; x < child_size; x++) flat_list.append( m_lv_children[x]->getAllChildrenFlat() ); return flat_list; } QList LogVol::getSnapshots() { QList snapshots; LogVol *top_lv = this; while( top_lv->getParent() != NULL) top_lv = top_lv->getParent(); if( top_lv->isSnapContainer() ){ snapshots = top_lv->getChildren(); for(int x = snapshots.size() - 1; x >= 0; x--){ // delete the 'real' lv leaving the snaps if( m_lv_name == snapshots[x]->getName() ) snapshots.removeAt(x); } } return snapshots; } LogVol *LogVol::getParent() { return m_lv_parent; } int LogVol::getSegmentCount() { return m_seg_total; } int LogVol::getSegmentStripes(const int segment) { return m_segments[segment]->m_stripes; } int LogVol::getSegmentStripeSize(const int segment) { return m_segments[segment]->m_stripe_size; } long long LogVol::getSegmentSize(const int segment) { return m_segments[segment]->m_size; } long long LogVol::getSegmentExtents(const int segment) { return (m_segments[segment]->m_size / m_vg->getExtentSize()); } QList LogVol::getSegmentStartingExtent(const int segment) { return m_segments[segment]->m_starting_extent; } QStringList LogVol::getPvNames(const int segment) { return m_segments[segment]->m_device_path; } QStringList LogVol::getPvNamesAll() { QStringList pv_names; QListIterator seg_itr(m_segments); while( seg_itr.hasNext() ) pv_names << seg_itr.next()->m_device_path; pv_names.sort(); pv_names.removeDuplicates(); return pv_names; } QStringList LogVol::getPvNamesAllFlat() { if( m_snap_container || m_mirror ){ QListIterator child_itr( getChildren() ); QStringList pv_names; while( child_itr.hasNext() ) pv_names << child_itr.next()->getPvNamesAllFlat(); pv_names.sort(); pv_names.removeDuplicates(); return pv_names; } else return getPvNamesAll(); } VolGroup* LogVol::getVg() { return m_vg; } QString LogVol::getName() { return m_lv_name; } QString LogVol::getFullName() { return m_lv_full_name; } QString LogVol::getMapperPath() { return m_lv_mapper_path; } long long LogVol::getSpaceUsedOnPv(const QString physicalVolume) { long long space_used = 0; QListIterator seg_itr(m_segments); while( seg_itr.hasNext() ){ Segment *const seg = seg_itr.next(); QListIterator path_itr(seg->m_device_path); while( path_itr.hasNext() ){ if(physicalVolume == path_itr.next()) space_used += (seg->m_size) / (seg->m_stripes) ; } } return space_used; } long long LogVol::getExtents() { return m_extents; } long long LogVol::getSize() { return m_size; } long long LogVol::getTotalSize() { return m_total_size; } QString LogVol::getFilesystem() { return m_lv_fs; } QString LogVol::getFilesystemLabel() { return m_lv_fs_label; } QString LogVol::getFilesystemUuid() { return m_lv_fs_uuid; } long long LogVol::getFilesystemSize() { return m_fs_size; } long long LogVol::getFilesystemUsed() { return m_fs_used; } unsigned long LogVol::getMinorDevice() { return m_minor_device; } unsigned long LogVol::getMajorDevice() { return m_major_device; } int LogVol::getLogCount() { return m_log_count; } int LogVol::getMirrorCount() { return m_mirror_count; } int LogVol::getSnapshotCount() { return getSnapshots().size(); } bool LogVol::isMerging() { return m_merging; } bool LogVol::isMounted() { return m_mounted; } bool LogVol::isActive() { return m_active; } bool LogVol::isMirror() { return m_mirror; } bool LogVol::isMirrorLeg() { return m_mirror_leg; } bool LogVol::isMirrorLog() { return m_mirror_log; } bool LogVol::isPersistent() { return m_persistent; } bool LogVol::isOpen() { return m_open; } bool LogVol::isLocked() { return m_alloc_locked; } bool LogVol::isUnderConversion() { return m_under_conversion; } bool LogVol::isWritable() { return m_writable; } bool LogVol::isVirtual() { return m_virtual; } bool LogVol::isSnap() { return m_snap; } bool LogVol::isSnapContainer() { return m_snap_container; } bool LogVol::isPvmove() { return m_pvmove; } bool LogVol::isOrigin() { return m_is_origin; } bool LogVol::isOrphan() { return m_orphan; } bool LogVol::isFixed() { return m_fixed; } bool LogVol::isValid() { return m_valid; } QString LogVol::getPolicy() { return m_policy; } QString LogVol::getState() { return m_state; } QString LogVol::getType() { return m_type; } QStringList LogVol::getTags() { return m_tags; } QString LogVol::getOrigin() { return m_origin; } QList LogVol::getMountEntries() { QList copy; QListIterator itr(m_mount_entries); while( itr.hasNext() ) copy.append( new MountEntry( itr.next() ) ); return copy; } QStringList LogVol::getMountPoints() { return m_mount_points; } QString LogVol::getFstabMountPoint() { return m_fstab_mount_point; } double LogVol::getSnapPercent() { if( m_snap || m_merging ) return m_snap_percent; else return 0.0; } double LogVol::getCopyPercent() { return m_copy_percent; } QString LogVol::getUuid() { return m_uuid; } bool LogVol::hasMissingVolume() { return getPvNamesAllFlat().contains("unknown device"); } kvpm-0.8.6/kvpm/devicepropertiesstack.h0000644000175000017500000000170511733530416020464 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef DEVICEPROPERTIESSTACK_H #define DEVICEPROPERTIESSTACK_H #include #include #include #include class StorageDevice; class DevicePropertiesStack : public QStackedWidget { Q_OBJECT QStringList m_device_path_list; // full path of each device on the stack, in the same order QWidget *getDefaultWidget(); public: explicit DevicePropertiesStack(QWidget *parent = 0); void loadData(QList devices); public slots: void changeDeviceStackIndex(QTreeWidgetItem *item); }; #endif kvpm-0.8.6/kvpm/pedexceptions.cpp0000644000175000017500000000172411733530416017270 0ustar benscottbenscott/* * * * Copyright (C) 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "pedexceptions.h" #include #include #include PedExceptionOption my_handler(PedException *exception) { QString error_message; qApp->restoreOverrideCursor(); // reset the cursor to not-busy if (exception->type == PED_EXCEPTION_INFORMATION) KMessageBox::information( 0, exception->message ); else{ error_message = QString( exception->message ); if( !error_message.contains( "unrecognised disk label") ) KMessageBox::error( 0, exception->message ); } return PED_EXCEPTION_UNHANDLED; } kvpm-0.8.6/kvpm/vgsplit.h0000644000175000017500000000421311733530416015547 0ustar benscottbenscott/* * * * Copyright (C) 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGSPLIT_H #define VGSPLIT_H #include #include #include #include #include #include #include class LogVol; class VolGroup; bool split_vg(VolGroup *volumeGroup); class VGSplitDialog : public KDialog { Q_OBJECT KListWidget *m_left_lv_list; KListWidget *m_right_lv_list; KListWidget *m_left_pv_list; KListWidget *m_right_pv_list; KPushButton *m_lv_add; KPushButton *m_lv_remove; KPushButton *m_pv_add; KPushButton *m_pv_remove; KLineEdit *m_new_vg_name; QRegExpValidator *m_validator; VolGroup *m_vg; QWidget *buildLvLists(const QStringList mobileLvNames, const QStringList immobileLvNames); QWidget *buildPvLists(const QStringList mobilePvNames, const QStringList immobilePvNames); void volumeMobility(QStringList &mobileLvNames, QStringList &immobileLvNames, QStringList &mobilePvNames, QStringList &immobilePvNames); void pvState(QStringList &open, QStringList &closed ); void movesWithVolume(const bool isLV, const QString name, QStringList &movingPvNames, QStringList &movingLvNames); void moveNames(const bool isLvMove, KListWidget *const lvSource, KListWidget *const lvTarget, KListWidget *const pvSource, KListWidget *const pvTarget); public: explicit VGSplitDialog(VolGroup *volumeGroup, QWidget *parent = 0); QStringList arguments(); private slots: void enableLvArrows(); void enablePvArrows(); void addPvList(); void removePvList(); void addLvList(); void removeLvList(); void validateOK(); void deactivate(); // active lvs must be deactivated before moving }; #endif kvpm-0.8.6/kvpm/snapmerge.h0000644000175000017500000000074311733530416016044 0ustar benscottbenscott/* * * * Copyright (C) 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef SNAPMERGE_H #define SNAPMERGE_H class LogVol; bool merge_snap(LogVol *const snapshot); #endif kvpm-0.8.6/kvpm/lvreduce.cpp0000644000175000017500000001412011733530416016221 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "lvreduce.h" #include #include #include #include #include "fsreduce.h" #include "logvol.h" #include "processprogress.h" #include "misc.h" #include "sizeselectorbox.h" #include "volgroup.h" LVReduceDialog::LVReduceDialog(LogVol *const volume, QWidget *parent) : KDialog(parent), m_lv(volume) { m_vg = m_lv->getVg(); setWindowTitle( i18n("Reduce Logical Volume") ); QWidget *dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *layout = new QVBoxLayout(); dialog_body->setLayout(layout); const long long extent_size = m_vg->getExtentSize(); const long long current_lv_extents = m_lv->getExtents(); const QString fs = m_lv->getFilesystem(); long long min_lv_extents; bool force = false; m_bailout = false; const QString warning_message1 = i18n("If this Inactive logical volume is reduced " "any data it contains will be lost!"); const QString warning_message2 = i18n("Only the ext2, ext3 and ext4 file systems " "are supported for file system reduction. If this " "logical volume is reduced any data it contains " "will be lost!"); if( !m_lv->isActive() && !m_lv->isSnap() ){ if(KMessageBox::warningContinueCancel(NULL, warning_message1, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) == KMessageBox::Continue){ force = true; } else{ m_bailout = true; } } else if( m_lv->isMounted() && !m_lv->isSnap() ){ KMessageBox::error(0, i18n("The filesystem must be unmounted first") ); m_bailout = true; } else if( !fs_can_reduce(fs) && !m_lv->isSnap() ){ if(KMessageBox::warningContinueCancel(NULL, warning_message2, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) == KMessageBox::Continue){ force = true; } else{ m_bailout = true; } } if( !m_bailout && !force && !m_lv->isSnap() ){ const long long min_fs_size = get_min_fs_size( m_lv->getMapperPath(), m_lv->getFilesystem() ); if( min_fs_size % extent_size ) min_lv_extents = 1 + (min_fs_size / extent_size); else min_lv_extents = min_fs_size / extent_size; if( min_fs_size == 0 || min_lv_extents >= m_lv->getExtents() ){ KMessageBox::error(0, i18n("The filesystem is already as small as it can be") ); m_bailout = true; } } else min_lv_extents = 1; if(!m_bailout){ bool use_si_units; KConfigSkeleton skeleton; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", use_si_units, false); KLocale *const locale = KGlobal::locale(); if(use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); QVBoxLayout *const label_layout = new QVBoxLayout(); QWidget *const label_widget = new QWidget(); label_widget->setLayout(label_layout); QLabel *const lv_name_label = new QLabel( i18n("Reducing Volume: %1", m_lv->getName() )); lv_name_label->setAlignment(Qt::AlignCenter); QLabel *const lv_min_label = new QLabel( i18n("Estimated minimum size: %1", locale->formatByteSize(min_lv_extents * extent_size) )); m_size_selector = new SizeSelectorBox(extent_size, min_lv_extents, current_lv_extents, current_lv_extents, true, false, true); label_layout->addWidget(lv_name_label); label_layout->addSpacing(5); label_layout->addWidget(lv_min_label); label_layout->addSpacing(5); layout->addWidget(label_widget); layout->addWidget(m_size_selector); connect(this, SIGNAL(okClicked()), this, SLOT(doShrink())); connect(m_size_selector, SIGNAL(stateChanged()), this , SLOT(resetOkButton())); } } bool LVReduceDialog::bailout() { return m_bailout; } void LVReduceDialog::doShrink() { QStringList lv_arguments; const QString fs = m_lv->getFilesystem(); const long long target_size = m_size_selector->getCurrentSize() * m_vg->getExtentSize(); long long new_size; hide(); if( m_lv->isSnap() || !m_lv->isActive() ) // never reduce the fs of a snap! new_size = target_size; else if( fs_can_reduce(fs) ) new_size = fs_reduce( m_lv->getMapperPath(), target_size, fs ); else new_size = target_size; if( new_size ){ lv_arguments << "lvreduce" << "--force" << "--size" << QString("%1K").arg( new_size / 1024 ) << m_lv->getMapperPath(); ProcessProgress reduce_lv(lv_arguments); } } void LVReduceDialog::resetOkButton() { enableButtonOk( m_size_selector->isValid() ); } kvpm-0.8.6/kvpm/devicetree.h0000644000175000017500000000233711733530416016203 0ustar benscottbenscott/* * * * Copyright (C) 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef DEVICETREE_H #define DEVICETREE_H #include #include #include #include #include #include class StorageDevice; class DeviceSizeChart; class DevicePropertiesStack; class DeviceTree : public QTreeWidget { Q_OBJECT bool m_initial_run, m_show_total, m_show_percent, m_show_both, m_expand_parts, m_use_si_units; int m_fs_warn_percent, m_pv_warn_percent; DeviceSizeChart *m_chart; DevicePropertiesStack *m_stack; void setupContextMenu(); void setViewConfig(); public: DeviceTree(DeviceSizeChart *const chart, DevicePropertiesStack *const stack, QWidget *parent = NULL); void loadData(QList devices); private slots: void popupContextMenu(QPoint point); }; #endif kvpm-0.8.6/kvpm/dualselectorbox.cpp0000644000175000017500000001174611733530416017622 0ustar benscottbenscott/* * * * Copyright (C) 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include #include #include "dualselectorbox.h" #include "sizeselectorbox.h" DualSelectorBox::DualSelectorBox(const long long sectorSize, const long long totalSpace, QWidget *parent) : QWidget(parent), m_space(totalSpace) { QVBoxLayout *const layout = new QVBoxLayout(); layout->setMargin(0); const long ONE_MIB = 0x100000 / sectorSize; m_size_selector = new SizeSelectorBox(sectorSize, 2 * ONE_MIB, m_space, m_space, false, false, true); m_offset_selector = new SizeSelectorBox(sectorSize, 0, m_space - ONE_MIB, 0, false, true); layout->addWidget(m_size_selector); layout->addWidget(m_offset_selector); setLayout(layout); connect(m_size_selector, SIGNAL(stateChanged()), this , SLOT(sizeChanged())); connect(m_offset_selector, SIGNAL(stateChanged()), this , SLOT(offsetChanged())); } DualSelectorBox::DualSelectorBox(const long long sectorSize, const long long totalSpace, const long long minSize, const long long maxSize, const long long initialSize, const long long minOffset, const long long maxOffset, const long long initialOffset, QWidget *parent) : QWidget(parent), m_space(totalSpace) { QVBoxLayout *const layout = new QVBoxLayout(); layout->setMargin(0); m_size_selector = new SizeSelectorBox(sectorSize, minSize, maxSize, initialSize, false, false, true); if(minSize == maxSize) m_offset_selector = new SizeSelectorBox(sectorSize, minOffset, maxOffset, initialOffset, false, true, false, false); else m_offset_selector = new SizeSelectorBox(sectorSize, minOffset, maxOffset, initialOffset, false, true, false, true); layout->addWidget(m_size_selector); layout->addWidget(m_offset_selector); setLayout(layout); connect(m_size_selector, SIGNAL(stateChanged()), this , SLOT(sizeChanged())); connect(m_offset_selector, SIGNAL(stateChanged()), this , SLOT(offsetChanged())); } void DualSelectorBox::sizeChanged() { const long long max = m_space; const long long current_size = m_size_selector->getCurrentSize(); const long long current_offset = m_offset_selector->getCurrentSize(); if( m_size_selector->isValid() ){ if( !m_offset_selector->isValid() ) m_offset_selector->setCurrentSize( m_offset_selector->getCurrentSize() ); // reset to last valid value if( !m_offset_selector->isLocked() ){ if( m_size_selector->isLocked() ) m_offset_selector->setConstrainedMax(max - current_size); else m_offset_selector->setConstrainedMax( max - m_size_selector->getMinimumSize() ); if( m_offset_selector->getCurrentSize() > (max - current_size) ) if( !m_offset_selector->setCurrentSize(max - current_size) ) m_size_selector->setCurrentSize(max - current_offset); } else{ m_size_selector->setConstrainedMax(max - current_offset); } } emit changed(); } void DualSelectorBox::offsetChanged() { const long long max = m_space; const long long current_size = m_size_selector->getCurrentSize(); const long long current_offset = m_offset_selector->getCurrentSize(); if( m_offset_selector->isValid() ){ if( !m_size_selector->isValid() ) m_size_selector->setCurrentSize( m_size_selector->getCurrentSize() ); // poke it to make it valid if( !m_size_selector->isLocked() ){ m_size_selector->setConstrainedMax(max); if( m_offset_selector->isLocked() ) m_size_selector->setConstrainedMax(max - current_offset); else m_size_selector->setConstrainedMax( max - m_offset_selector->getMinimumSize() ); if( m_size_selector->getCurrentSize() > (max - current_offset) ) if( !m_size_selector->setCurrentSize(max - current_offset) ) m_offset_selector->setCurrentSize(max - current_size); } else{ m_offset_selector->setConstrainedMax(max - current_size); } } emit changed(); } void DualSelectorBox::resetSelectors() { m_size_selector->resetToInitial(); m_offset_selector->resetToInitial(); } long long DualSelectorBox::getCurrentSize() { return m_size_selector->getCurrentSize(); } long long DualSelectorBox::getCurrentOffset() { return m_offset_selector->getCurrentSize(); } bool DualSelectorBox::isValid() { return ( m_size_selector->isValid() && m_offset_selector->isValid() ); } kvpm-0.8.6/kvpm/pedexceptions.h0000644000175000017500000000101311733530416016724 0ustar benscottbenscott/* * * * Copyright (C) 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PEDEXCEPTIONS_H #define PEDEXCEPTIONS_H #include PedExceptionOption my_handler(PedException *exception); #endif kvpm-0.8.6/kvpm/fsextend.cpp0000644000175000017500000001411311733530416016232 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "fsextend.h" #include #include #include #include #include #include #include #include "executablefinder.h" #include "fsck.h" #include "mountentry.h" #include "mounttables.h" #include "processprogress.h" bool do_temp_mount(const QString dev, const QString fs, const QString mp); // device path, filesystem and mount point void do_temp_unmount(const QString mp); bool fs_can_extend(const QString fs) { QString executable; if(fs == "ext2" || fs == "ext3" || fs == "ext4" || fs == "reiserfs" || fs == "ntfs" || fs == "xfs"){ if(fs == "ext2" || fs == "ext3" || fs == "ext4") executable = "resize2fs"; else if(fs == "reiserfs") executable = "resize_reiserfs"; else if(fs == "ntfs") executable = "ntfsresize"; else if(fs == "xfs") executable = "xfs_growfs"; if( !ExecutableFinder::getPath(executable).isEmpty() ) return true; else{ KMessageBox::error(NULL, i18n("Executable: '%1' not found, this filesystem cannot be extended", executable)); return false; } } else if(fs == "jfs") return true; else return false; } // dev is the full path to the device or volume, fs == filesystem, mps == mountpoints bool fs_extend(const QString dev, const QString fs, const QStringList mps, const bool isLV) { bool isMounted = true; QString mp; if( mps.isEmpty() ) isMounted = false; else mp = mps[0]; const QByteArray dev_qba = dev.toLocal8Bit(); const QByteArray fs_qba = fs.toLocal8Bit(); const QByteArray mp_qba = mp.toLocal8Bit(); QStringList args; unsigned long options = 0; if( ( !isLV ) && ( isMounted ) ){ KMessageBox::error(0, i18n("only logical volumes may be mounted during resize, not partitions")); return false; } const QString err_msg = i18n("It appears that the filesystem on the device or volume was not extended. " "It will need to be extended before the additional space can be used."); if(fs == "ext2" || fs == "ext3" || fs == "ext4"){ if( !isMounted ){ if( !fsck(dev) ) return false; } args << "resize2fs" << dev; ProcessProgress fs_grow(args); if ( fs_grow.exitCode() ){ KMessageBox::error(0, err_msg); return false; } else return true; } else if(fs == "xfs"){ if(isMounted){ args << "xfs_growfs" << mp; ProcessProgress fs_grow(args); if ( fs_grow.exitCode() ){ KMessageBox::error(0, err_msg); return false; } else return true; } else{ KTempDir temp_mp; if( do_temp_mount(dev, fs, temp_mp.name()) ){ args << "xfs_growfs" << temp_mp.name(); ProcessProgress fs_grow(args); do_temp_unmount( temp_mp.name() ); if ( fs_grow.exitCode() ){ KMessageBox::error(0, err_msg); return false; } else return true; } else{ KMessageBox::error(0, err_msg); return false; } } } else if(fs == "jfs"){ options = MS_REMOUNT; if(isMounted){ if(mount( dev_qba.data(), mp_qba.data(), NULL, options, "resize" )){ KMessageBox::error(0, i18n("Error number: %1 %2", errno, strerror(errno))); return false; } else return true; } else{ KTempDir temp_mp; const QByteArray temp_mp_qba = temp_mp.name().toLocal8Bit(); if( do_temp_mount(dev, fs, temp_mp.name()) ){ if( ! mount( dev_qba.data(), temp_mp_qba.data(), NULL, options, "resize" ) ){ do_temp_unmount( temp_mp.name() ); return true; } else{ KMessageBox::error(0, i18n("Error number: %1 %2", errno, strerror(errno))); do_temp_unmount( temp_mp.name() ); } } return false; } } else if(fs == "reiserfs"){ args << "resize_reiserfs" << "-fq" << dev; ProcessProgress fs_grow(args); if ( fs_grow.exitCode() ){ KMessageBox::error(0, err_msg); return false; } else return true; } else if(fs == "ntfs"){ args << "ntfsresize" << "--no-progress-bar" << "--force" << dev; ProcessProgress fs_grow(args); if ( fs_grow.exitCode() ){ KMessageBox::error(0, err_msg); return false; } else return true; } return false; } bool do_temp_mount(const QString dev, const QString fs, const QString mp) { const unsigned long options = 0; const QByteArray dev_qba = dev.toLocal8Bit(); const QByteArray fs_qba = fs.toLocal8Bit(); const QByteArray mp_qba = mp.toLocal8Bit(); const int error = mount( dev_qba.data(), mp_qba.data(), fs_qba.data(), options, NULL ); if( error ){ KMessageBox::error(0, QString("Error number: %1 %2").arg(errno).arg(strerror(errno))); return false; } return true; } void do_temp_unmount(const QString mp) { const QByteArray mp_qba = mp.toLocal8Bit(); umount2(mp_qba, 0); } kvpm-0.8.6/kvpm/storagepartition.h0000644000175000017500000000551511733530416017463 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef STORAGEPARTITION_H #define STORAGEPARTITION_H #include #include #include #include class PhysVol; class MountEntry; class MountTables; class StoragePartition { QList m_mount_entries; QString m_fstab_mount_point; PhysVol *m_pv; PedPartition *m_ped_partition; QString m_partition_path; QString m_partition_type; unsigned int m_ped_type; QString m_fs_type; QString m_fs_uuid; QString m_fs_label; QStringList m_flags; long long m_partition_size; // Partition size and first sector for *freespace* are aligned PedSector m_first_sector; // within the space to 1 MiB and may be different than libparted reports. PedSector m_last_sector; // Last sector is not aligned and will be the same as libarted PedSector m_true_first_sector; // unaligned first sector long long m_fs_size; long long m_fs_used; int m_major; // block dev numbers int m_minor; bool m_is_writable; bool m_is_pv; bool m_is_mounted; bool m_is_empty; bool m_is_busy; bool m_is_mountable; bool m_is_normal; bool m_is_logical; bool m_is_freespace; PedSector getAlignedStart(); PedSector getFreespaceEnd(); public: StoragePartition(PedPartition *const part, const int freespaceCount, const QList pvList, MountTables *const mountTables); ~StoragePartition(); PedPartition *getPedPartition(); QString getFilesystem(); QString getFilesystemUuid(); QString getFilesystemLabel(); QString getName(); PhysVol *getPhysicalVolume(); QString getType(); unsigned int getPedType(); QString getFstabMountPoint(); QStringList getMountPoints(); QList getMountEntries(); // These need to be deleted by the calling function! QStringList getFlags(); long long getSize(); PedSector getFirstSector(); PedSector getTrueFirstSector(); PedSector getLastSector(); long long getFilesystemSize(); long long getFilesystemUsed(); long long getFilesystemRemaining(); int getFilesystemPercentUsed(); int getMajorNumber(); int getMinorNumber(); bool isWritable(); bool isPhysicalVolume(); bool isMounted(); bool isEmpty(); bool isBusy(); bool isMountable(); bool isNormal(); bool isLogical(); bool isFreespace(); }; #endif kvpm-0.8.6/kvpm/pvmove.h0000644000175000017500000000362111733530416015375 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PVMOVE_H #define PVMOVE_H #include #include #include #include #include #include class LogVol; class NameAndRange; class NoMungeRadioButton; class PhysVol; class PvGroupBox; class VolGroup; bool restart_pvmove(); bool stop_pvmove(); class PVMoveDialog : public KDialog { Q_OBJECT VolGroup *m_vg; LogVol *m_lv; bool m_move_lv; bool m_move_segment; bool m_bailout; // if TRUE, a move is impossible so don't even call up the dialog long long m_pv_used_space; QList m_sources; QList m_target_pvs; // destination physical volumes QList m_radio_buttons; // user can select only one source pv PvGroupBox *m_pv_checkbox; // many target pvs may be selected QRadioButton *m_contiguous_button, *m_normal_button, // Radio button to chose *m_anywhere_button, *m_inherited_button, // the allocation policy *m_cling_button; void buildDialog(); void removeFullTargets(); void setupSegmentMove(int segment); void setupFullMove(); QStringList arguments(); public: explicit PVMoveDialog(PhysVol *physicalVolume, QWidget *parent = 0); explicit PVMoveDialog(LogVol *logicalVolume, int segment, QWidget *parent = 0); ~PVMoveDialog(); bool bailout(); private slots: void resetOkButton(); void disableSource(); void commitMove(); }; #endif kvpm-0.8.6/kvpm/vgreduce.h0000644000175000017500000000167211733530416015671 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGREDUCE_H #define VGREDUCE_H #include #include #include class VolGroup; class PvGroupBox; bool reduce_vg(VolGroup *volumeGroup); class VGReduceDialog : public KDialog { Q_OBJECT VolGroup *m_vg; PvGroupBox *m_pv_checkbox; bool m_unremovable_pvs_present; public: explicit VGReduceDialog(VolGroup *const volumeGroup, QWidget *parent = NULL); QStringList arguments(); private slots: void excludeOneVolume(); // one pv must remain in the vg void commitChanges(); }; #endif kvpm-0.8.6/kvpm/volumegrouptab.cpp0000644000175000017500000001047011733530416017467 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "volumegrouptab.h" #include #include #include "lvpropertiesstack.h" #include "lvsizechart.h" #include "physvol.h" #include "pvpropertiesstack.h" #include "pvtree.h" #include "vginfolabels.h" #include "vgremove.h" #include "vgtree.h" #include "volgroup.h" VolumeGroupTab::VolumeGroupTab(VolGroup *volumeGroup, QWidget *parent) : QWidget(parent), m_vg(volumeGroup) { QSplitter *const tree_splitter = new QSplitter(Qt::Vertical); QSplitter *const lv_splitter = new QSplitter(); QSplitter *const pv_splitter = new QSplitter(); m_vg_info_labels = NULL; m_lv_size_chart = NULL; m_lv_properties_stack = NULL; m_pv_properties_stack = NULL; m_layout = new QVBoxLayout; setLayout(m_layout); m_group_name = m_vg->getName(); m_vg_tree = new VGTree(m_vg); m_pv_tree = new PVTree(m_vg); m_vg_tree->setAlternatingRowColors(true); m_pv_tree->setAlternatingRowColors(true); m_layout->addWidget(tree_splitter); tree_splitter->addWidget(lv_splitter); tree_splitter->addWidget(pv_splitter); lv_splitter->addWidget(m_vg_tree); pv_splitter->addWidget(m_pv_tree); tree_splitter->setStretchFactor(0, 3); tree_splitter->setStretchFactor(1, 2); QList lv_size_list; lv_size_list << 1500 << 10 ; lv_splitter->setSizes( lv_size_list ); m_lv_properties_stack = new LVPropertiesStack(m_vg); m_lv_properties_stack->setFrameStyle( m_vg_tree->frameStyle() ); lv_splitter->addWidget(m_lv_properties_stack); QList pv_size_list; pv_size_list << 1500 << 10 ; pv_splitter->setSizes( pv_size_list ); m_pv_properties_stack = new PVPropertiesStack(m_vg); m_pv_properties_stack->setFrameStyle( m_vg_tree->frameStyle() ); pv_splitter->addWidget(m_pv_properties_stack); return; } void VolumeGroupTab::rescan() { disconnect(m_vg_tree, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), m_lv_properties_stack, SLOT(changeLVStackIndex(QTreeWidgetItem*, QTreeWidgetItem*))); disconnect(m_pv_tree, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), m_pv_properties_stack, SLOT(changePVStackIndex(QTreeWidgetItem*, QTreeWidgetItem*))); if(m_vg_info_labels){ m_layout->removeWidget(m_vg_info_labels); m_vg_info_labels->setParent(NULL); m_vg_info_labels->deleteLater(); } m_vg_info_labels = new VGInfoLabels(m_vg); m_layout->insertWidget(0, m_vg_info_labels); m_lv_properties_stack->loadData(); m_pv_properties_stack->loadData(); connect(m_vg_tree, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), m_lv_properties_stack, SLOT(changeLVStackIndex(QTreeWidgetItem*, QTreeWidgetItem*))); connect(m_pv_tree, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), m_pv_properties_stack, SLOT(changePVStackIndex(QTreeWidgetItem*, QTreeWidgetItem*))); m_vg_tree->loadData(); // This needs to be done after the lv property stack is built m_pv_tree->loadData(); // This needs to be done after the pv property stack is loaded if(m_lv_size_chart){ // This needs to be after the vgtree is loaded m_layout->removeWidget(m_lv_size_chart); m_lv_size_chart->setParent(NULL); m_lv_size_chart->deleteLater(); } m_lv_size_chart = new LVSizeChart(m_vg, m_vg_tree); m_layout->insertWidget(1, m_lv_size_chart); readConfig(); return; } VolGroup* VolumeGroupTab::getVg() { return m_vg; } void VolumeGroupTab::readConfig() { KConfigSkeleton skeleton; bool show_vg_info, show_lv_bar; skeleton.setCurrentGroup("General"); skeleton.addItemBool( "show_vg_info", show_vg_info, true ); skeleton.addItemBool( "show_lv_bar", show_lv_bar, true ); if(show_vg_info) m_vg_info_labels->show(); else m_vg_info_labels->hide(); if(show_lv_bar) m_lv_size_chart->show(); else m_lv_size_chart->hide(); } kvpm-0.8.6/kvpm/storagepartition.cpp0000644000175000017500000002463711733530416020024 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "storagepartition.h" #include #include #include #include "fsdata.h" #include "fsprobe.h" #include "misc.h" #include "mountentry.h" #include "mounttables.h" #include "physvol.h" StoragePartition::StoragePartition(PedPartition *const part, const int freespaceCount, const QList pvList, MountTables *const mountTables) : m_ped_partition (part) { PedDevice *const ped_device = m_ped_partition->disk->dev; PedGeometry const geometry = m_ped_partition->geom; long long const sector_size = ped_device->sector_size; m_true_first_sector = geometry.start; m_first_sector = geometry.start; m_last_sector = geometry.end; m_partition_size = geometry.length * sector_size; // in bytes m_ped_type = m_ped_partition->type; m_is_writable = !ped_device->read_only; m_is_pv = false; m_is_normal = false; m_is_logical = false; m_is_freespace = false; m_pv = NULL; if( m_ped_type & PED_PARTITION_FREESPACE ){ if( m_ped_type & PED_PARTITION_LOGICAL ){ m_last_sector = getFreespaceEnd(); } m_first_sector = getAlignedStart(); m_is_freespace = true; m_partition_size = (1 + m_last_sector - m_first_sector) * sector_size; } if( m_ped_type == PED_PARTITION_NORMAL ){ m_partition_type = "normal"; m_is_normal = true; m_partition_path = ped_partition_get_path(part); } else if( m_ped_type & PED_PARTITION_EXTENDED ){ m_partition_type = "extended"; m_partition_path = ped_partition_get_path(part); } else if( (m_ped_type & PED_PARTITION_LOGICAL) && !(m_ped_type & PED_PARTITION_FREESPACE) ){ m_partition_type = "logical"; m_is_logical = true; m_partition_path = ped_partition_get_path(part); } else if( (m_ped_type & PED_PARTITION_LOGICAL) && (m_ped_type & PED_PARTITION_FREESPACE) ){ m_partition_type = "freespace (logical)"; m_partition_path = ped_partition_get_path(part); m_partition_path.chop(1); m_partition_path.append( QString("%1").arg(freespaceCount) ); } else{ m_partition_type = "freespace"; m_partition_path = ped_partition_get_path(part); m_partition_path.chop(1); m_partition_path.append( QString("%1").arg(freespaceCount) ); } for(int x = 0; x < pvList.size(); x++){ if(m_partition_path == pvList[x]->getName()){ m_is_pv = true; m_pv = pvList[x]; } } KDE_struct_stat buf; QByteArray path = m_partition_path.toLocal8Bit(); if( KDE_stat(path.data(), &buf) == 0 ){ m_major = major(buf.st_rdev); m_minor = minor(buf.st_rdev); } else{ m_major = -1; m_minor = -1; } // Iterate though all the possible flags and check each one PedPartitionFlag ped_flag = PED_PARTITION_BOOT; if( !m_partition_type.contains("freespace", Qt::CaseInsensitive) ){ while( ped_flag != 0 ){ if( ped_partition_get_flag(m_ped_partition, ped_flag) ) m_flags << ped_partition_flag_get_name(ped_flag); ped_flag = ped_partition_flag_next( ped_flag ); } if( !m_flags.size() ) m_flags << ""; } if( m_partition_type == "extended" ){ m_is_mountable = false; m_fs_type = ""; } else{ m_fs_type = fsprobe_getfstype2(m_partition_path).trimmed(); m_fs_uuid = fsprobe_getfsuuid(m_partition_path).trimmed(); m_fs_label = fsprobe_getfslabel(m_partition_path).trimmed(); if( m_fs_type == "swap" || m_is_pv || m_partition_type.contains("freespace", Qt::CaseInsensitive) ) m_is_mountable = false; else m_is_mountable = true; } for(int x = m_mount_entries.size() - 1; x >= 0; x--) delete m_mount_entries.takeAt(x); m_mount_entries = mountTables->getMtabEntries(m_major, m_minor); m_fstab_mount_point = mountTables->getFstabMountPoint(this); m_is_mounted = !m_mount_entries.isEmpty(); if(m_is_mounted){ FSData fs_data = get_fs_data(m_mount_entries[0]->getMountPoint()); if(fs_data.size > 0){ m_fs_size = fs_data.size * fs_data.block_size; m_fs_used = fs_data.used * fs_data.block_size; } else{ m_fs_size = -1; m_fs_used = -1; } } else{ m_fs_size = -1; m_fs_used = -1; } if (m_is_pv) m_is_busy = m_pv->isActive(); else m_is_busy = ped_partition_is_busy(m_ped_partition); if( m_partition_type == "extended" ){ PedPartition *temp_part = NULL; PedDisk *const temp_disk = ped_disk_new(ped_device); m_is_empty = true; if( temp_disk ){ while( (temp_part = ped_disk_next_partition (temp_disk, temp_part)) ){ if( (temp_part->type == PED_PARTITION_LOGICAL) ){ m_is_empty = false; break; } } ped_disk_destroy(temp_disk); } } else m_is_empty = false; } StoragePartition::~StoragePartition() { for(int x = 0; x < m_mount_entries.size(); x++) delete m_mount_entries[x]; } QString StoragePartition::getType() { return m_partition_type.trimmed(); } unsigned int StoragePartition::getPedType() { return m_ped_type; } PedPartition* StoragePartition::getPedPartition() { return m_ped_partition; } QString StoragePartition::getFilesystem() { return m_fs_type; } QString StoragePartition::getFilesystemUuid() { return m_fs_uuid; } QString StoragePartition::getFilesystemLabel() { return m_fs_label; } PhysVol* StoragePartition::getPhysicalVolume() { return m_pv; } QString StoragePartition::getName() { return m_partition_path.trimmed(); } long long StoragePartition::getSize() { return m_partition_size; } PedSector StoragePartition::getFirstSector() { return m_first_sector; } PedSector StoragePartition::getTrueFirstSector() { return m_true_first_sector; } PedSector StoragePartition::getLastSector() { return m_last_sector; } long long StoragePartition::getFilesystemSize() { return m_fs_size; } long long StoragePartition::getFilesystemUsed() { return m_fs_used; } long long StoragePartition::getFilesystemRemaining() { return m_fs_size - m_fs_used; } int StoragePartition::getMajorNumber() { return m_major; } int StoragePartition::getMinorNumber() { return m_minor; } int StoragePartition::getFilesystemPercentUsed() { int percent; if( m_fs_used == 0 ) return 0; else if( m_fs_used == m_fs_size ) return 100; else if( m_fs_size == 0 ) return 0; else percent = qRound( 100 - ( ( ( m_fs_size - m_fs_used ) * 100.0 ) / m_fs_size ) ); return percent; } bool StoragePartition::isMounted() { return m_is_mounted; } /* function returns true if the partition is extended and has no logical partitions */ bool StoragePartition::isEmpty() { return m_is_empty; } bool StoragePartition::isWritable() { return m_is_writable; } bool StoragePartition::isNormal() { return m_is_normal; } bool StoragePartition::isLogical() { return m_is_logical; } bool StoragePartition::isBusy() { return m_is_busy; } bool StoragePartition::isMountable() { return m_is_mountable; } bool StoragePartition::isFreespace() { return m_is_freespace; } bool StoragePartition::isPhysicalVolume() { return m_is_pv; } QString StoragePartition::getFstabMountPoint() { return m_fstab_mount_point; } QStringList StoragePartition::getMountPoints() { QStringList mount_points; for(int x = 0; x < m_mount_entries.size(); x++) mount_points.append( m_mount_entries[x]->getMountPoint() ); return mount_points; } QList StoragePartition::getMountEntries() { QList copy; QListIterator itr(m_mount_entries); while( itr.hasNext() ) copy.append( new MountEntry( itr.next() ) ); return copy; } QStringList StoragePartition::getFlags() { return m_flags; } PedSector StoragePartition::getAlignedStart() { PedPartition *const free_space = m_ped_partition; PedDevice *const device = free_space->disk->dev; const PedSector ONE_MIB = 0x100000 / device->sector_size; // sectors per megabyte PedSector start = free_space->geom.start; PedSector const end = free_space->geom.length + start - 1; if( (end - start) < (ONE_MIB * 2) ) // ignore partitions less than 2 MiB, alignment may fail. return start; PedAlignment *const start_align = ped_alignment_new(0, ONE_MIB); PedAlignment *const end_align = ped_alignment_new(0, 1); PedGeometry *const start_range = ped_geometry_new(device, start, free_space->geom.length); PedGeometry *const end_range = ped_geometry_new(device, start, free_space->geom.length); PedConstraint *constraint = ped_constraint_new(start_align, end_align, start_range, end_range, 1, free_space->geom.length); PedGeometry *const aligned_geometry = ped_constraint_solve_max(constraint); start = aligned_geometry->start; ped_alignment_destroy(start_align); ped_alignment_destroy(end_align); ped_constraint_destroy(constraint); ped_geometry_destroy(start_range); ped_geometry_destroy(end_range); ped_geometry_destroy(aligned_geometry); return start; } /* The following function works *only* on logical freespace. It finds the largest partition that will fit in the freespace by adding any space in a following metadata area, if one exists */ PedSector StoragePartition::getFreespaceEnd() { PedPartition *const free_space = m_ped_partition; PedDisk *const disk = free_space->disk; PedSector end = free_space->geom.length + free_space->geom.start - 1; PedPartition *next_part = ped_disk_next_partition(disk, free_space); if(next_part){ if(next_part->type & PED_PARTITION_METADATA){ end = next_part->geom.length + next_part->geom.start - 1; } } return end; } kvpm-0.8.6/kvpm/pvchange.h0000644000175000017500000000203211733530416015647 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PVCHANGE_H #define PVCHANGE_H #include #include #include #include #include #include #include class PhysVol; class PVChangeDialog : public KDialog { Q_OBJECT KLineEdit *m_tag_edit; // new tag to add KComboBox *m_deltag_combo; // old tag to remove QGroupBox *m_tags_group; QCheckBox *m_allocation_box; QCheckBox *m_uuid_box; QCheckBox *m_mda_box; PhysVol *m_pv; public: explicit PVChangeDialog(PhysVol *physicalVolume, QWidget *parent = 0); QStringList arguments(); private slots: void resetOkButton(); }; #endif kvpm-0.8.6/kvpm/vgcreate.cpp0000644000175000017500000003134611733530416016221 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vgcreate.h" #include #include #include #include #include #include "masterlist.h" #include "progressbox.h" #include "pvgroupbox.h" #include "storagedevice.h" #include "storagepartition.h" #include "topwindow.h" VGCreateDialog::VGCreateDialog(QWidget *parent) : KDialog(parent) { m_bailout = false; QList devices; QList partitions; const QString warning = i18n("If a device or partition is added to a volume group, " "any data currently on that device or partition will be lost."); getUsablePvs(devices, partitions); if( partitions.size() + devices.size() > 0 ){ if(KMessageBox::warningContinueCancel(NULL, warning, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) == KMessageBox::Continue){ buildDialog(devices, partitions); } else{ m_bailout = true; } } else{ m_bailout = true; KMessageBox::error(0, i18n("No unused potential physical volumes found") ); } } VGCreateDialog::VGCreateDialog(StorageDevice *const device, StoragePartition *const partition, QWidget *parent) : KDialog(parent) { m_bailout = false; QList devices; QList partitions; if(device != NULL) devices.append(device); else partitions.append(partition); const QString warning = i18n("If a device or partition is added to a volume group, " "any data currently on that device or partition will be lost."); if(KMessageBox::warningContinueCancel(NULL, warning, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) == KMessageBox::Continue){ buildDialog(devices, partitions); } else{ m_bailout = true; } } void VGCreateDialog::extentSizeChanged(){ limitExtentSize(m_extent_suffix->currentIndex() ); uint32_t new_extent_size = m_extent_size->currentText().toULong(); new_extent_size *= 1024; if( m_extent_suffix->currentIndex() > 0 ) new_extent_size *= 1024; if( m_extent_suffix->currentIndex() > 1 ) new_extent_size *= 1024; m_pv_checkbox->setExtentSize(new_extent_size); } void VGCreateDialog::limitExtentSize(int index){ int extent_index; if( index > 1 ){ // Gigabytes selected as suffix, more than 2Gib forbidden if( m_extent_size->currentIndex() > 2 ) m_extent_size->setCurrentIndex(0); m_extent_size->setMaxCount(2); } else{ extent_index = m_extent_size->currentIndex(); m_extent_size->setMaxCount(10); m_extent_size->setInsertPolicy(QComboBox::InsertAtBottom); m_extent_size->insertItem(2,"4"); m_extent_size->insertItem(3,"8"); m_extent_size->insertItem(4,"16"); m_extent_size->insertItem(5,"32"); m_extent_size->insertItem(6,"64"); m_extent_size->insertItem(7,"128"); m_extent_size->insertItem(8,"256"); m_extent_size->insertItem(9,"512"); m_extent_size->setInsertPolicy(QComboBox::NoInsert); m_extent_size->setCurrentIndex(extent_index); } } void VGCreateDialog::commitChanges() { lvm_t lvm = MasterList::getLvm(); vg_t vg_dm; uint32_t new_extent_size = m_extent_size->currentText().toULong(); const QStringList pv_names = m_pv_checkbox->getNames(); const QByteArray vg_name_array = m_vg_name->text().toLocal8Bit(); ProgressBox *const progress_box = TopWindow::getProgressBox(); QByteArray pv_name_qba; hide(); new_extent_size *= 1024; if( m_extent_suffix->currentIndex() > 0 ) new_extent_size *= 1024; if( m_extent_suffix->currentIndex() > 1 ) new_extent_size *= 1024; progress_box->setRange(0, pv_names.size()); progress_box->setText("Creating VG"); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); if( (vg_dm = lvm_vg_create(lvm, vg_name_array.data())) ){ if( (lvm_vg_set_extent_size(vg_dm, new_extent_size)) ) KMessageBox::error(0, QString(lvm_errmsg(lvm))); for(int x = 0; x < pv_names.size(); x++){ progress_box->setValue(x); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); pv_name_qba = pv_names[x].toLocal8Bit(); if( lvm_vg_extend(vg_dm, pv_name_qba.data()) ) KMessageBox::error(0, QString(lvm_errmsg(lvm))); } // ****To Do... None of the following are supported by liblvm2app yet**** // if(m_clustered->isChecked()) // if(m_auto_backup->isChecked()) // if((m_max_lvs_check->isChecked()) && (m_max_lvs->text() != "")) // if((m_max_pvs_check->isChecked()) && (m_max_pvs->text() != "")) if( lvm_vg_write(vg_dm) ) KMessageBox::error(0, QString(lvm_errmsg(lvm))); lvm_vg_close(vg_dm); progress_box->reset(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); return; } KMessageBox::error(0, QString(lvm_errmsg(lvm))); progress_box->reset(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); return; } /* The allowed characters in the name are letters, numbers, periods hyphens and underscores. Also, the names ".", ".." and names starting with a hyphen are disallowed. Finally disable the OK button if no pvs are checked */ void VGCreateDialog::validateOK() { QString name = m_vg_name->text(); int pos = 0; long long space = m_pv_checkbox->getRemainingSpace(); enableButtonOk(false); if(m_validator->validate(name, pos) == QValidator::Acceptable && name != "." && name != ".."){ if(space) enableButtonOk(true); } } void VGCreateDialog::buildDialog(QList devices, QList partitions) { setWindowTitle( i18n("Create Volume Group") ); QWidget *dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *const layout = new QVBoxLayout(); dialog_body->setLayout(layout); QLabel *const title = new QLabel( i18n("Create A Volume Group") ); title->setAlignment(Qt::AlignCenter); layout->addSpacing(5); layout->addWidget(title); layout->addSpacing(5); QLabel *name_label = new QLabel( i18n("Group Name: ") ); m_vg_name = new KLineEdit(); QRegExp rx("[0-9a-zA-Z_\\.][-0-9a-zA-Z_\\.]*"); m_validator = new QRegExpValidator( rx, m_vg_name ); m_vg_name->setValidator(m_validator); QHBoxLayout *name_layout = new QHBoxLayout(); name_layout->addWidget(name_label); name_layout->addWidget(m_vg_name); QLabel *extent_label = new QLabel( i18n("Physical Extent Size: ") ); m_extent_size = new KComboBox(); m_extent_size->insertItem(0,"1"); m_extent_size->insertItem(1,"2"); m_extent_size->insertItem(2,"4"); m_extent_size->insertItem(3,"8"); m_extent_size->insertItem(4,"16"); m_extent_size->insertItem(5,"32"); m_extent_size->insertItem(6,"64"); m_extent_size->insertItem(7,"128"); m_extent_size->insertItem(8,"256"); m_extent_size->insertItem(9,"512"); m_extent_size->setInsertPolicy(QComboBox::NoInsert); m_extent_size->setCurrentIndex(2); m_extent_suffix = new KComboBox(); m_extent_suffix->insertItem(0,"KiB"); m_extent_suffix->insertItem(1,"MiB"); m_extent_suffix->insertItem(2,"GiB"); m_extent_suffix->setInsertPolicy(QComboBox::NoInsert); m_extent_suffix->setCurrentIndex(1); m_pv_checkbox = new PvGroupBox(devices, partitions, 0x400000); // 4 MiB default extent size layout->addWidget(m_pv_checkbox); connect(m_pv_checkbox, SIGNAL(stateChanged()), this, SLOT(validateOK())); QHBoxLayout *extent_layout = new QHBoxLayout(); extent_layout->addWidget(extent_label); extent_layout->addWidget(m_extent_size); extent_layout->addWidget(m_extent_suffix); extent_layout->addStretch(); /* liblvm does not support setting limits on lvs and pvs yet (will it ever?) QGroupBox *lv_box = new QGroupBox( i18n("Number of Logical Volumes") ); QVBoxLayout *lv_layout_v = new QVBoxLayout(); QHBoxLayout *lv_layout_h = new QHBoxLayout(); lv_box->setLayout(lv_layout_v); m_max_lvs_check = new QCheckBox( i18n("No Limit") ); m_max_lvs_check->setCheckState(Qt::Checked); lv_layout_v->addWidget(m_max_lvs_check); lv_layout_v->addLayout(lv_layout_h); QLabel *lv_label = new QLabel( i18n("Maximum: ") ); m_max_lvs = new KLineEdit(); QIntValidator *lv_validator = new QIntValidator(1,255,this); m_max_lvs->setValidator(lv_validator); m_max_lvs->setEnabled(false); lv_layout_h->addWidget(lv_label); lv_layout_h->addWidget(m_max_lvs); QGroupBox *pv_box = new QGroupBox( i18n("Number of Physical Volumes") ); QVBoxLayout *pv_layout_v = new QVBoxLayout(); QHBoxLayout *pv_layout_h = new QHBoxLayout(); pv_box->setLayout(pv_layout_v); m_max_pvs_check = new QCheckBox( i18n("No Limit") ); m_max_pvs_check->setCheckState(Qt::Checked); pv_layout_v->addWidget(m_max_pvs_check); pv_layout_v->addLayout(pv_layout_h); QLabel *pv_label = new QLabel( i18n("Maximum: ") ); m_max_pvs = new KLineEdit(); QIntValidator *pv_validator = new QIntValidator(1,255,this); m_max_pvs->setValidator(pv_validator); m_max_pvs->setEnabled(false); pv_layout_h->addWidget(pv_label); pv_layout_h->addWidget(m_max_pvs); */ m_clustered = new QCheckBox( i18n("Cluster Aware") ); m_clustered->setEnabled(false); m_auto_backup = new QCheckBox( i18n("Automatic Backup") ); m_auto_backup->setCheckState(Qt::Checked); m_auto_backup->setEnabled(false); layout->addLayout(name_layout); layout->addLayout(extent_layout); // layout->addWidget(lv_box); // layout->addWidget(pv_box); layout->addWidget(m_clustered); layout->addWidget(m_auto_backup); enableButtonOk(false); connect(m_vg_name, SIGNAL(textChanged(QString)), this, SLOT(validateOK())); /* connect(m_max_lvs_check, SIGNAL(stateChanged(int)), this, SLOT(limitLogicalVolumes(int))); connect(m_max_pvs_check, SIGNAL(stateChanged(int)), this, SLOT(limitPhysicalVolumes(int))); */ connect(this, SIGNAL(okClicked()), this, SLOT(commitChanges())); connect(m_extent_size, SIGNAL(activated(int)), this, SLOT(extentSizeChanged())); connect(m_extent_suffix, SIGNAL(activated(int)), this, SLOT(extentSizeChanged())); } bool VGCreateDialog::bailout() { return m_bailout; } void VGCreateDialog::getUsablePvs(QList &devices, QList &partitions) { QList all_dev = MasterList::getStorageDevices(); QList all_part; for(int x = 0; x < all_dev.size(); x++){ if( (all_dev[x]->getRealPartitionCount() == 0) && ( !all_dev[x]->isBusy()) && ( !all_dev[x]->isPhysicalVolume() )) { devices.append( all_dev[x] ); } else if( all_dev[x]->getRealPartitionCount() > 0 ){ all_part = all_dev[x]->getStoragePartitions(); for(int y = 0; y < all_part.size(); y++){ if( ( !all_part[y]->isBusy() ) && (! all_part[y]->isPhysicalVolume() ) && (( all_part[y]->isNormal() ) || ( all_part[y]->isLogical() ))) { partitions.append(all_part[y]); } } } } } /* void VGCreateDialog::limitLogicalVolumes(int boxstate) { if(boxstate == Qt::Unchecked) m_max_lvs->setEnabled(true); else m_max_lvs->setEnabled(false); } void VGCreateDialog::limitPhysicalVolumes(int boxstate) { if(boxstate == Qt::Unchecked) m_max_pvs->setEnabled(true); else m_max_pvs->setEnabled(false); } */ kvpm-0.8.6/kvpm/lvcreate.h0000644000175000017500000000701511733530416015667 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef LVCREATE_H #define LVCREATE_H #include #include #include #include #include #include #include #include #include #include #include class LogVol; class VolGroup; class PhysVol; class QRegExpValidator; class QDoubleValidator; class PvGroupBox; class SizeSelectorBox; class LVCreateDialog : public KDialog { Q_OBJECT bool m_snapshot; // TRUE if a snapshot bool m_extend; // TRUE if extending a volume bool m_name_is_valid; // TRUE if the new name is acceptable bool m_bailout; // TRUE if we should not bother to execute this dialog bool m_fs_can_extend; bool m_use_si_units; // TRUE Metric SI sizes = MB and GB, otherise use MiB, GiB etc. SizeSelectorBox *m_size_selector; VolGroup *m_vg; LogVol *m_lv; // origin for snap or lv to extend // set to NULL if creating a new logical volume QWidget *m_general_tab, // The tab labeled "general" in the dialog *m_physical_tab, // The physical tab *m_advanced_tab; // Adevanced options tab KLineEdit *m_minor_number_edit, *m_major_number_edit, *m_name_edit, *m_tag_edit; QRegExpValidator *m_name_validator, *m_tag_validator; QCheckBox *m_zero_check, *m_readonly_check, *m_skip_sync_check, // don't synchronize empty mirror images when created *m_monitor_check; // monitor with dmeventd QGroupBox *m_persistent_box, *m_mirror_box, *m_stripe_box; PvGroupBox *m_pv_checkbox; KTabWidget *m_tab_widget; KComboBox *stripe_size_combo; QSpinBox *m_mirror_count_spin, // how many mirrors we want *m_stripe_count_spin; // how many stripes we want QLabel *m_stripe_count_label, *m_max_size_label, *m_max_extents_label, *m_extend_by_label, // how much space we are adding to a volume *m_current_size_label; // if we are extending this is the existing size QRadioButton *contiguous_button, *normal_button, // Radio button to chose *anywhere_button, *inherited_button, // the allocation policy *cling_button; QRadioButton *m_mirrored_log, *m_disk_log, *m_core_log; QWidget* createGeneralTab(); QWidget* createAdvancedTab(); QWidget* createPhysicalTab(); long long getLargestVolume(); int getStripeCount(); int getMirrorCount(); void resetOkButton(); void makeConnections(); long long roundExtentsToStripes(long long extents); bool hasInitialErrors(); QStringList argumentsLV(); public: explicit LVCreateDialog(VolGroup *const group, QWidget *parent = 0); LVCreateDialog(LogVol *const volume, const bool snapshot, QWidget *parent = 0); bool bailout(); private slots: void setMaxSize(); void validateVolumeName(QString name); void zeroReadonlyCheck(int state); void enableMonitoring(bool checked); void commitChanges(); }; #endif kvpm-0.8.6/kvpm/vgsplit.cpp0000644000175000017500000004614011733530416016107 0ustar benscottbenscott/* * * * Copyright (C) 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vgsplit.h" #include #include #include #include #include #include "logvol.h" #include "masterlist.h" #include "misc.h" #include "physvol.h" #include "processprogress.h" #include "volgroup.h" bool split_vg(VolGroup *volumeGroup) { if( volumeGroup->getPhysicalVolumes().size() < 2 ){ KMessageBox::error(0, i18n("A volume group must have at least two physical volumes to split group") ); return false; } VGSplitDialog dialog(volumeGroup); dialog.exec(); if(dialog.result() == QDialog::Accepted){ ProcessProgress vgsplit( dialog.arguments() ); return true; } else return false; } VGSplitDialog::VGSplitDialog(VolGroup *volumeGroup, QWidget *parent) : KDialog(parent), m_vg(volumeGroup) { QList pv_list = m_vg->getPhysicalVolumes(); setWindowTitle( i18n("Split Volume Group") ); QWidget *dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *layout = new QVBoxLayout(); dialog_body->setLayout(layout); QLabel *name_label = new QLabel( i18n("Volume Group To Split: %1", m_vg->getName() ) ); name_label->setAlignment(Qt::AlignCenter); layout->addWidget(name_label); m_new_vg_name = new KLineEdit(); QRegExp rx("[0-9a-zA-Z_\\.][-0-9a-zA-Z_\\.]*"); m_validator = new QRegExpValidator( rx, m_new_vg_name ); m_new_vg_name->setValidator(m_validator); QLabel *new_name_label = new QLabel( i18n("New Volume Group Name") ); QHBoxLayout *new_name_layout = new QHBoxLayout(); new_name_layout->addStretch(); new_name_layout->addWidget(new_name_label); new_name_layout->addWidget(m_new_vg_name); new_name_layout->addStretch(); layout->addLayout(new_name_layout); connect(m_new_vg_name, SIGNAL(textEdited(QString)), this, SLOT(validateOK())); connect(this, SIGNAL(okClicked()), this, SLOT(deactivate())); KTabWidget *tw = new KTabWidget(); layout->addWidget(tw); QStringList mobile_lv_names, immobile_lv_names, mobile_pv_names, immobile_pv_names; volumeMobility( mobile_lv_names, immobile_lv_names, mobile_pv_names, immobile_pv_names); tw->addTab( buildLvLists(mobile_lv_names, immobile_lv_names), i18n("Logical volume view") ); tw->addTab( buildPvLists(mobile_pv_names, immobile_pv_names), i18n("Physical volume view") ); enableLvArrows(); enablePvArrows(); validateOK(); setMinimumWidth(400); } void VGSplitDialog::validateOK() { QString name = m_new_vg_name->text(); int pos = 0; bool original_vg = false; bool new_vg = false; if(m_validator->validate(name, pos) == QValidator::Acceptable && name != "." && name != ".."){ if( m_left_pv_list->count() ) // Must have at least one pv in old group and one in new group original_vg = true; if( m_right_pv_list->count() ) new_vg = true; if(new_vg && original_vg) enableButtonOk(true); else enableButtonOk(false); } else enableButtonOk(false); } QStringList VGSplitDialog::arguments() { QStringList args; args << "vgsplit" << m_vg->getName() << m_new_vg_name->text(); for(int x = m_right_pv_list->count() - 1; x >= 0; x--) args << m_right_pv_list->item(x)->data(Qt::DisplayRole).toString(); return args; } void VGSplitDialog::deactivate() { QStringList moving_lvs; const QByteArray vg_name = m_vg->getName().toLocal8Bit(); lvm_t lvm = MasterList::getLvm(); vg_t vg_dm; dm_list *lv_dm_list; lvm_lv_list *lv_list; QList lvs_to_deactivate; for(int x = m_right_lv_list->count() - 1; x >= 0; x--) moving_lvs << m_right_lv_list->item(x)->data(Qt::DisplayRole).toString(); if( (vg_dm = lvm_vg_open(lvm, vg_name.data(), "w", 0x0)) ){ for(int x = 0; x < moving_lvs.size(); x++){ lv_dm_list = lvm_vg_list_lvs(vg_dm); dm_list_iterate_items(lv_list, lv_dm_list){ if( QString( lvm_lv_get_name( lv_list->lv ) ).trimmed() == moving_lvs[x]) lvs_to_deactivate.append( lv_list->lv ); } } for(int x = 0; x < lvs_to_deactivate.size(); x++){ if( lvm_lv_is_active(lvs_to_deactivate[x]) ){ if( lvm_lv_deactivate(lvs_to_deactivate[x]) ) KMessageBox::error(0, QString(lvm_errmsg(lvm))); } } lvm_vg_close(vg_dm); return; } else{ KMessageBox::error(0, QString(lvm_errmsg(lvm))); return; } return; } QWidget *VGSplitDialog::buildLvLists(const QStringList mobileLvNames, const QStringList immobileLvNames) { QWidget *lv_list = new QWidget(); QHBoxLayout *layout = new QHBoxLayout; QVBoxLayout *left_layout = new QVBoxLayout; QVBoxLayout *center_layout = new QVBoxLayout; QVBoxLayout *right_layout = new QVBoxLayout; m_lv_add = new KPushButton( KIcon("arrow-right"), i18n("Add") ); m_lv_remove = new KPushButton( KIcon("arrow-left"), i18n("Remove") ); m_left_lv_list = new KListWidget(); m_right_lv_list = new KListWidget(); m_left_lv_list->setSelectionMode(QAbstractItemView::ExtendedSelection); m_right_lv_list->setSelectionMode(QAbstractItemView::ExtendedSelection); m_left_lv_list->setSortingEnabled(true); m_right_lv_list->setSortingEnabled(true); QListWidgetItem *lv_item; for(int x = mobileLvNames.size() - 1; x >= 0; x--){ lv_item = new QListWidgetItem(mobileLvNames[x]); lv_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); m_left_lv_list->addItem(lv_item); } for(int x = immobileLvNames.size() - 1; x >= 0; x--){ lv_item = new QListWidgetItem(immobileLvNames[x]); lv_item->setFlags(Qt::NoItemFlags); m_left_lv_list->addItem(lv_item); } QLabel *temp_label = new QLabel( i18n("Original volume group") ); temp_label->setAlignment(Qt::AlignCenter); left_layout->addWidget(temp_label); left_layout->addWidget(m_left_lv_list); layout->addLayout(left_layout); center_layout->addStretch(); center_layout->addWidget(m_lv_add); center_layout->addWidget(m_lv_remove); center_layout->addStretch(); layout->addLayout(center_layout); temp_label = new QLabel( i18n("New volume group") ); temp_label->setAlignment(Qt::AlignCenter); right_layout->addWidget(temp_label); right_layout->addWidget(m_right_lv_list); layout->addLayout(right_layout); lv_list->setLayout(layout); connect(m_left_lv_list, SIGNAL(itemSelectionChanged()), this, SLOT(enableLvArrows())); connect(m_right_lv_list, SIGNAL(itemSelectionChanged()), this, SLOT(enableLvArrows())); connect(m_lv_add, SIGNAL(clicked()), this, SLOT(addLvList())); connect(m_lv_remove, SIGNAL(clicked()), this, SLOT(removeLvList())); return lv_list; } QWidget *VGSplitDialog::buildPvLists(const QStringList mobilePvNames, const QStringList immobilePvNames) { QWidget *pv_list = new QWidget(); QHBoxLayout *layout = new QHBoxLayout; QVBoxLayout *left_layout = new QVBoxLayout; QVBoxLayout *center_layout = new QVBoxLayout; QVBoxLayout *right_layout = new QVBoxLayout; m_pv_add = new KPushButton( KIcon("arrow-right"), "Add" ); m_pv_remove = new KPushButton( KIcon("arrow-left"), "Remove" ); QList pvs = m_vg->getPhysicalVolumes(); QList lvs = m_vg->getLogicalVolumes(); QListWidgetItem *pv_item; QStringList open_pv_names; QStringList closed_pv_names; m_left_pv_list = new KListWidget(); m_right_pv_list = new KListWidget(); m_left_pv_list->setSelectionMode(QAbstractItemView::ExtendedSelection); m_right_pv_list->setSelectionMode(QAbstractItemView::ExtendedSelection); m_left_pv_list->setSortingEnabled(true); m_right_pv_list->setSortingEnabled(true); for(int x = mobilePvNames.size() - 1; x >= 0; x--){ pv_item = new QListWidgetItem( mobilePvNames[x] ); pv_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); m_left_pv_list->addItem(pv_item); } for(int x = immobilePvNames.size() - 1; x >= 0; x--){ pv_item = new QListWidgetItem( immobilePvNames[x] ); pv_item->setFlags(Qt::NoItemFlags); m_left_pv_list->addItem(pv_item); } QLabel *temp_label = new QLabel( i18n("Original volume group") ); temp_label->setAlignment(Qt::AlignCenter); left_layout->addWidget(temp_label); left_layout->addWidget(m_left_pv_list); layout->addLayout(left_layout); center_layout->addStretch(); center_layout->addWidget(m_pv_add); center_layout->addWidget(m_pv_remove); center_layout->addStretch(); layout->addLayout(center_layout); temp_label = new QLabel( i18n("New volume group") ); temp_label->setAlignment(Qt::AlignCenter); right_layout->addWidget(temp_label); right_layout->addWidget(m_right_pv_list); layout->addLayout(right_layout); pv_list->setLayout(layout); connect(m_left_pv_list, SIGNAL(itemSelectionChanged()), this, SLOT(enablePvArrows())); connect(m_right_pv_list, SIGNAL(itemSelectionChanged()), this, SLOT(enablePvArrows())); connect(m_pv_add, SIGNAL(clicked()), this, SLOT(addPvList())); connect(m_pv_remove, SIGNAL(clicked()), this, SLOT(removePvList())); return pv_list; } void VGSplitDialog::enableLvArrows() { if( m_left_lv_list->selectedItems().size() ) m_lv_add->setEnabled(true); else m_lv_add->setEnabled(false); if( m_right_lv_list->selectedItems().size() ) m_lv_remove->setEnabled(true); else m_lv_remove->setEnabled(false); } void VGSplitDialog::enablePvArrows() { if( m_left_pv_list->selectedItems().size() ) m_pv_add->setEnabled(true); else m_pv_add->setEnabled(false); if( m_right_pv_list->selectedItems().size() ) m_pv_remove->setEnabled(true); else m_pv_remove->setEnabled(false); } void VGSplitDialog::addLvList() { moveNames(true, m_left_lv_list, m_right_lv_list, m_left_pv_list, m_right_pv_list); } void VGSplitDialog::removeLvList() { moveNames(true, m_right_lv_list, m_left_lv_list, m_right_pv_list, m_left_pv_list); } void VGSplitDialog::addPvList() { moveNames(false, m_left_lv_list, m_right_lv_list, m_left_pv_list, m_right_pv_list); } void VGSplitDialog::removePvList() { moveNames(false, m_right_lv_list, m_left_lv_list, m_right_pv_list, m_left_pv_list); } void VGSplitDialog::moveNames(const bool isLvMove, KListWidget *const lvSource, KListWidget *const lvTarget, KListWidget *const pvSource, KListWidget *const pvTarget) { QList selected_items; QList moving_items; QStringList moving_pv_names, moving_lv_names; QStringList pv_names, lv_names; QString name; if(isLvMove){ selected_items = lvSource->selectedItems(); for(int x = selected_items.size() - 1; x >= 0; x--){ name = lvSource->item( lvSource->row(selected_items[x]))->data(Qt::DisplayRole).toString(); movesWithVolume(true, name, pv_names, lv_names); moving_pv_names.append(pv_names); moving_lv_names.append(lv_names); } moving_pv_names.removeDuplicates(); moving_lv_names.removeDuplicates(); } else{ selected_items = pvSource->selectedItems(); for(int x = selected_items.size() - 1; x >= 0; x--){ name = pvSource->item( pvSource->row(selected_items[x]))->data(Qt::DisplayRole).toString(); movesWithVolume(false, name, pv_names, lv_names); moving_pv_names.append(pv_names); moving_lv_names.append(lv_names); } moving_pv_names.removeDuplicates(); moving_lv_names.removeDuplicates(); } for(int x = moving_lv_names.size() - 1; x >= 0; x--){ moving_items = lvSource->findItems(moving_lv_names[x], Qt::MatchExactly); for(int y = moving_items.size() - 1; y >= 0; y--){ lvSource->takeItem( lvSource->row( moving_items[y] ) ); lvTarget->addItem( moving_items[y] ); } } for(int x = moving_pv_names.size() - 1; x >= 0; x--){ moving_items = pvSource->findItems(moving_pv_names[x], Qt::MatchExactly); for(int y = moving_items.size() - 1; y >= 0; y--){ pvSource->takeItem( pvSource->row( moving_items[y] ) ); pvTarget->addItem( moving_items[y] ); } } validateOK(); } void VGSplitDialog::volumeMobility(QStringList &mobileLvNames, QStringList &immobileLvNames, QStringList &mobilePvNames, QStringList &immobilePvNames) { QStringList all_pv_names; const QList lvs = m_vg->getLogicalVolumes(); const QList pvs = m_vg->getPhysicalVolumes(); LogVol *temp; bool movable = true; bool growing = true; int immobile_count = 0; mobileLvNames.clear(); mobilePvNames.clear(); immobileLvNames.clear(); immobilePvNames.clear(); pvState(immobilePvNames, mobilePvNames); while(growing){ for(int x = lvs.size() - 1; x >= 0 ; x--){ all_pv_names = lvs[x]->getPvNamesAllFlat(); movable = true; for(int y = all_pv_names.size() - 1; y >= 0; y--){ for(int z = immobilePvNames.size() - 1; z >= 0; z--){ if( immobilePvNames[z] == all_pv_names[y] ){ movable = false; break; } } } if( !movable ) immobileLvNames.append( lvs[x]->getName() ); } for(int x = immobileLvNames.size() - 1; x >= 0; x--){ temp = m_vg->getLvByName( immobileLvNames[x] ); if( temp->isOrigin() && ( temp->getParent() != NULL ) ) immobilePvNames.append( temp->getParent()->getPvNamesAllFlat() ); else immobilePvNames.append( temp->getPvNamesAllFlat() ); } immobilePvNames.removeDuplicates(); immobileLvNames.removeDuplicates(); if( immobileLvNames.size() > immobile_count ) growing = true; else growing = false; immobile_count = immobileLvNames.size(); } mobilePvNames.clear(); mobileLvNames.clear(); for(int x = lvs.size() - 1; x >= 0; x--) mobileLvNames.append( lvs[x]->getName() ); for(int x = mobileLvNames.size() - 1; x >= 0; x--){ for(int y = immobileLvNames.size() - 1; y >= 0; y--){ if(mobileLvNames[x] == immobileLvNames[y]){ mobileLvNames.removeAt(x); break; } } } for(int x = pvs.size() - 1; x >= 0; x--) mobilePvNames.append( pvs[x]->getName() ); for(int x = mobilePvNames.size() - 1; x >= 0; x--){ for(int y = immobilePvNames.size() - 1; y >= 0; y--){ if(mobilePvNames[x] == immobilePvNames[y]){ mobilePvNames.removeAt(x); break; } } } mobilePvNames.removeDuplicates(); mobileLvNames.removeDuplicates(); } void VGSplitDialog::pvState(QStringList &open, QStringList &closed ) { const QList pvs = m_vg->getPhysicalVolumes(); const QList lvs = m_vg->getLogicalVolumes(); QList snaps; for(int x = lvs.size() - 1; x >=0; x--){ if( lvs[x]->isOpen() ){ open.append( lvs[x]->getPvNamesAllFlat() ); } else if( lvs[x]->isSnapContainer() || lvs[x]->isOrigin() ){ snaps = lvs[x]->getSnapshots(); for(int y = snaps.size() - 1; y >= 0; y--){ if( snaps[y]->isOpen() ){ open.append( lvs[x]->getPvNamesAllFlat() ); // if any snap is open the whole container is open break; } } } } open.removeDuplicates(); QStringList all_pv_names; for(int x = pvs.size() - 1; x >= 0; x--) all_pv_names.append( pvs[x]->getName() ); for(int x = all_pv_names.size() - 1; x >= 0; x--){ for(int y = open.size() - 1; y >= 0; y--){ if( all_pv_names[x] == open[y] ){ all_pv_names.removeAt(x); break; } } } closed = all_pv_names; } void VGSplitDialog::movesWithVolume(const bool isLV, const QString name, QStringList &movingPvNames, QStringList &movingLvNames) { QStringList all_pv_names; const QList lvs = m_vg->getLogicalVolumes(); const QList pvs = m_vg->getPhysicalVolumes(); LogVol *temp; bool growing = true; bool moving = true; int moving_lv_count; int moving_pv_count; movingPvNames.clear(); movingLvNames.clear(); if(isLV){ moving_lv_count = 1; moving_pv_count = 0; temp = m_vg->getLvByName(name); if( temp->isOrigin() && ( temp->getParent() != NULL ) ) movingPvNames = temp->getParent()->getPvNamesAllFlat(); else movingPvNames = temp->getPvNamesAllFlat(); } else{ moving_lv_count = 0; moving_pv_count = 1; movingPvNames.append(name); } while(growing){ for(int x = lvs.size() - 1; x >= 0 ; x--){ if( lvs[x]->isOrigin() && ( lvs[x]->getParent() != NULL ) ) all_pv_names = lvs[x]->getParent()->getPvNamesAllFlat(); else all_pv_names = lvs[x]->getPvNamesAllFlat(); moving = false; for(int y = all_pv_names.size() - 1; y >= 0; y--){ for(int z = movingPvNames.size() - 1; z >= 0; z--){ if( movingPvNames[z] == all_pv_names[y] ){ moving = true; break; } } } if( moving ) movingLvNames.append( lvs[x]->getName() ); } for(int x = movingLvNames.size() - 1; x >= 0; x--){ temp = m_vg->getLvByName( movingLvNames[x] ); if( temp->isOrigin() && ( temp->getParent() != NULL ) ) movingPvNames.append( temp->getParent()->getPvNamesAllFlat() ); else movingPvNames.append( temp->getPvNamesAllFlat() ); } movingLvNames.removeDuplicates(); movingPvNames.removeDuplicates(); if( ( movingLvNames.size() > moving_lv_count ) || ( movingPvNames.size() > moving_pv_count ) ) growing = true; else growing = false; moving_lv_count = movingLvNames.size(); moving_pv_count = movingPvNames.size(); } } kvpm-0.8.6/kvpm/vgrename.h0000644000175000017500000000165011733530416015665 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGRENAME_H #define VGRENAME_H #include #include #include #include class VolGroup; bool rename_vg(VolGroup *volumeGroup); class VGRenameDialog : public KDialog { Q_OBJECT QString m_old_name; KLineEdit *m_new_name; QRegExpValidator *m_name_validator; public: explicit VGRenameDialog(VolGroup *volumeGroup, QWidget *parent = 0); QStringList arguments(); QString getNewName(); QString getOldName(); private slots: void validateName(QString); }; #endif kvpm-0.8.6/kvpm/lvchange.cpp0000644000175000017500000003566311733530416016216 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "lvchange.h" #include #include #include #include "logvol.h" #include "processprogress.h" LVChangeDialog::LVChangeDialog(LogVol *const volume, QWidget *parent) : KDialog(parent), m_lv(volume) { setWindowTitle( i18n("Change Logical Volume Attributes") ); QWidget *const dialog_body = new QWidget(); setMainWidget(dialog_body); QVBoxLayout *const layout = new QVBoxLayout; dialog_body->setLayout(layout); QLabel *const name = new QLabel( i18n("Change Volume: %1", m_lv->getName()) ); name->setAlignment(Qt::AlignCenter); layout->addSpacing(10); layout->addWidget(name); layout->addStretch(); KTabWidget *const tab_widget = new KTabWidget(); tab_widget->setAutomaticResizeTabs(true); layout->addWidget(tab_widget); tab_widget->addTab(buildGeneralTab(), i18nc("The standard or basic options", "General") ); tab_widget->addTab(buildAdvancedTab(), i18nc("Less used or complex options", "Advanced") ); if( m_lv->isSnap() || m_lv->isMirror() ){ if( m_lv->isMirror() ) tab_widget->insertTab(1, buildMirrorTab(), i18n("Mirror") ); else tab_widget->insertTab(1, buildMirrorTab(), i18n("Snapshot") ); connect(m_dmeventd_box, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_monitor_button, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_nomonitor_button, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_ignore_button, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_resync_check, SIGNAL(clicked()), this, SLOT(resetOkButton())); } connect(m_alloc_box, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_normal_button, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_contiguous_button, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_anywhere_button, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_cling_button, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_inherit_button, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_available_check, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_ro_check, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_refresh_check, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_udevsync_check, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_persistent_check, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_tag_group, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_deltag_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(resetOkButton())); connect(m_tag_edit, SIGNAL(userTextChanged(QString)), this, SLOT(resetOkButton())); connect(m_polling_box, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_poll_button, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_nopoll_button, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_devnum_box, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_udevsync_box, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_available_check, SIGNAL(stateChanged(int)), this ,SLOT(refreshAndAvailableCheck())); connect(m_refresh_check, SIGNAL(stateChanged(int)), this ,SLOT(refreshAndAvailableCheck())); connect(this, SIGNAL(okClicked()), this, SLOT(commitChanges())); resetOkButton(); } QWidget *LVChangeDialog::buildGeneralTab() { QWidget *const tab = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(); tab->setLayout(layout); QGroupBox *general_group = new QGroupBox(); QVBoxLayout *general_layout = new QVBoxLayout(); general_group->setLayout(general_layout); layout->addWidget(general_group); m_available_check = new QCheckBox( i18n("Make volume available for use") ); m_ro_check = new QCheckBox( i18n("Make volume read only") ); m_refresh_check = new QCheckBox( i18n("Refresh volume metadata") ); general_layout->addWidget(m_available_check); general_layout->addWidget(m_ro_check); general_layout->addWidget(m_refresh_check); if( m_lv->isActive() ) m_available_check->setChecked(true); if( m_lv->isMounted() || m_lv->isSnap() ) m_available_check->setEnabled(false); if( !m_lv->isWritable() ) m_ro_check->setChecked(true); m_tag_group = new QGroupBox( i18n("Change Volume Tags")); m_tag_group->setCheckable(true); m_tag_group->setChecked(false); layout->addWidget(m_tag_group); QHBoxLayout *add_tag_layout = new QHBoxLayout(); QHBoxLayout *del_tag_layout = new QHBoxLayout(); QVBoxLayout *tag_group_layout = new QVBoxLayout(); tag_group_layout->addLayout(add_tag_layout); tag_group_layout->addLayout(del_tag_layout); m_tag_group->setLayout(tag_group_layout); add_tag_layout->addWidget( new QLabel( i18n("Add new tag:")) ); m_tag_edit = new KLineEdit(); QRegExp rx("[0-9a-zA-Z_\\.+-]*"); QRegExpValidator *tag_validator = new QRegExpValidator( rx, m_tag_edit ); m_tag_edit->setValidator(tag_validator); add_tag_layout->addWidget(m_tag_edit); del_tag_layout->addWidget( new QLabel( i18n("Remove tag:")) ); m_deltag_combo = new KComboBox(); m_deltag_combo->setEditable(false); QStringList tags = m_lv->getTags(); for(int x = 0; x < tags.size(); x++) m_deltag_combo->addItem( tags[x] ); m_deltag_combo->insertItem(0, QString("")); m_deltag_combo->setCurrentIndex(0); del_tag_layout->addWidget(m_deltag_combo); m_alloc_box = new QGroupBox( i18n("Change Allocation Policy") ); m_alloc_box->setCheckable(true); m_alloc_box->setChecked(false); QVBoxLayout *alloc_box_layout = new QVBoxLayout; m_normal_button = new QRadioButton( i18nc("The usual way", "Normal") ); m_contiguous_button = new QRadioButton( i18n("Contiguous") ); m_anywhere_button = new QRadioButton( i18n("Anywhere") ); m_cling_button = new QRadioButton( i18n("Cling") ); m_inherit_button = new QRadioButton( i18nc("Inherited from the parent group", "Inherited") ); QString policy = m_lv->getPolicy(); if( policy == "Contiguous" ){ m_contiguous_button->setEnabled(false); m_contiguous_button->setText("Contiguous (current)"); m_normal_button->setChecked(true); } else if( policy == "Inherited"){ m_inherit_button->setEnabled(false); m_inherit_button->setText("Inherited (current)"); m_normal_button->setChecked(true); } else if( policy == "Anywhere" ){ m_anywhere_button->setEnabled(false); m_anywhere_button->setText("Anywhere (current)"); m_normal_button->setChecked(true); } else if( policy == "Cling" ){ m_cling_button->setEnabled(false); m_cling_button->setText("Cling (current)"); m_normal_button->setChecked(true); } else{ m_normal_button->setEnabled(false); m_normal_button->setText("Normal (current)"); m_cling_button->setChecked(true); } alloc_box_layout->addWidget(m_normal_button); alloc_box_layout->addWidget(m_contiguous_button); alloc_box_layout->addWidget(m_anywhere_button); alloc_box_layout->addWidget(m_cling_button); alloc_box_layout->addWidget(m_inherit_button); m_alloc_box->setLayout(alloc_box_layout); layout->addWidget(m_alloc_box); return tab; } QWidget *LVChangeDialog::buildMirrorTab() { QWidget *const tab = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(); tab->setLayout(layout); layout->addStretch(); QGroupBox *const resync_box = new QGroupBox( i18n("Mirror Sync") ); if( !m_lv->isMirror() ){ resync_box->setEnabled(false); resync_box->hide(); } layout->addWidget(resync_box); QVBoxLayout *const resync_layout = new QVBoxLayout(); resync_box->setLayout(resync_layout); m_resync_check = new QCheckBox( i18n("Re-synchronize mirrors") ); m_resync_check->setEnabled( !m_lv->isMounted() ); resync_layout->addWidget(m_resync_check); m_dmeventd_box = new QGroupBox( i18n("Monitoring With dmeventd") ); m_dmeventd_box->setCheckable(true); m_dmeventd_box->setChecked(false); QVBoxLayout *const mirror_layout = new QVBoxLayout(); m_dmeventd_box->setLayout(mirror_layout); m_monitor_button = new QRadioButton( i18n("Monitor with dmeventd") ); m_nomonitor_button = new QRadioButton( i18n("Do not monitor") ); m_ignore_button = new QRadioButton( i18n("Ignore dmeventd") ); m_monitor_button->setChecked(true); mirror_layout->addWidget(m_monitor_button); mirror_layout->addWidget(m_nomonitor_button); mirror_layout->addWidget(m_ignore_button); layout->addWidget(m_dmeventd_box); layout->addStretch(); return tab; } QWidget *LVChangeDialog::buildAdvancedTab() { QWidget *const tab = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(); tab->setLayout(layout); m_polling_box = new QGroupBox( i18n("Change Volume Polling") ); m_polling_box->setCheckable(true); m_polling_box->setChecked(false); layout->addWidget(m_polling_box); QVBoxLayout *poll_layout = new QVBoxLayout(); m_polling_box->setLayout(poll_layout); m_poll_button = new QRadioButton( i18n("Start polling") ); m_poll_button->setChecked(true); poll_layout->addWidget(m_poll_button); m_nopoll_button = new QRadioButton( i18n("Stop polling") ); poll_layout->addWidget(m_nopoll_button); m_udevsync_box = new QGroupBox( i18n("Udev Synchronizing") ); layout->addWidget(m_udevsync_box); QVBoxLayout *sync_layout = new QVBoxLayout(); m_udevsync_box->setLayout(sync_layout); m_udevsync_check = new QCheckBox( i18n("Synchronize with udev") ); m_udevsync_check->setChecked(true); sync_layout->addWidget(m_udevsync_check); m_devnum_box = new QGroupBox( i18n("Set Kernel Device Numbers") ); m_devnum_box->setCheckable(true); QVBoxLayout *devnum_layout = new QVBoxLayout(); m_devnum_box->setLayout(devnum_layout); QHBoxLayout *major_layout = new QHBoxLayout(); QHBoxLayout *minor_layout = new QHBoxLayout(); m_persistent_check = new QCheckBox( i18n("Use persistent device numbers") ); devnum_layout->addWidget(m_persistent_check); devnum_layout->addLayout(major_layout); devnum_layout->addLayout(minor_layout); m_major_edit = new KLineEdit(QString("%1").arg(m_lv->getMajorDevice())); major_layout->addWidget( new QLabel( i18n("Major number: ") ) ); major_layout->addWidget(m_major_edit); m_minor_edit = new KLineEdit(QString("%1").arg(m_lv->getMinorDevice())); minor_layout->addWidget( new QLabel( i18n("Minor number: ") ) ); minor_layout->addWidget(m_minor_edit); layout->addWidget(m_devnum_box); m_persistent_check->setChecked( m_lv->isPersistent() ); m_devnum_box->setChecked(false); return tab; } QStringList LVChangeDialog::arguments() { QStringList args, temp; args << "lvchange" << "--yes"; // answer yes to any question if( !m_lv->isSnap() ){ if( m_available_check->isChecked() && ( !m_lv->isActive() )) args << "--available" << "y"; else if( ( ! m_available_check->isChecked() ) && ( m_lv->isActive() )) args << "--available" << "n"; } if( m_ro_check->isChecked() && m_lv->isWritable() ) args << "--permission" << "r"; else if( ( ! m_ro_check->isChecked() ) && ( ! m_lv->isWritable() ) ) args << "--permission" << "rw"; if( m_refresh_check->isChecked() ) args << "--refresh"; if( m_lv->isSnap() || m_lv->isMirror() ){ // These buttons are undefined if the mirrortab isn't built! if( m_resync_check->isChecked() ) args << "--resync"; if( m_dmeventd_box->isChecked() ){ if( m_monitor_button->isChecked() ) args << "--monitor" << "y"; else if( m_nomonitor_button->isChecked() ) args << "--monitor" << "n"; else args << "--ignoremonitoring"; } } if( m_devnum_box->isChecked() ){ if( m_persistent_check->isChecked() ){ args << "--force" << "--persistent" << "y"; args << "--major" << m_major_edit->text(); args << "--minor" << m_minor_edit->text(); } else{ args << "--force" << "--persistent" << "n"; args << "--major" << m_major_edit->text(); args << "--minor" << m_minor_edit->text(); } } if(m_polling_box->isChecked()){ if(m_poll_button->isChecked()) args << "--poll" << "y"; else args << "--poll" << "y"; } if(!m_udevsync_check->isChecked()) args << "--noudevsync"; if( m_tag_group->isChecked() ){ if( m_deltag_combo->currentIndex() ) args << "--deltag" << m_deltag_combo->currentText(); if( ! (m_tag_edit->text()).isEmpty() ) args << "--addtag" << m_tag_edit->text(); } if( m_alloc_box->isChecked() ){ args << "--alloc"; if( m_contiguous_button->isChecked() ) args << "contiguous"; else if( m_anywhere_button->isChecked() ) args << "anywhere"; else if( m_cling_button->isChecked() ) args << "cling"; else if( m_inherit_button->isChecked() ) args << "inherit"; else args << "normal"; } args << m_lv->getFullName(); return args; } void LVChangeDialog::commitChanges() { hide(); ProcessProgress change_lv( arguments() ); } void LVChangeDialog::resetOkButton(){ if( m_available_check->isChecked() ) m_polling_box->setEnabled(true); else{ m_polling_box->setEnabled(false); m_polling_box->setChecked(false); } QStringList args = arguments(); args.removeAt( args.indexOf( QString("--ignoremonitoring") ) ); if( args.size() > 3 ) enableButtonOk(true); else enableButtonOk(false); } // metadata refresh and availability change can't happen at the same time void LVChangeDialog::refreshAndAvailableCheck() { if( !m_lv->isMounted() && !m_lv->isSnap() ){ if( m_lv->isActive() == m_available_check->isChecked() ){ m_refresh_check->setEnabled(true); } else{ m_refresh_check->setEnabled(false); m_refresh_check->setChecked(false); } if( m_refresh_check->isChecked() ){ m_available_check->setChecked( m_lv->isActive() ); m_available_check->setEnabled(false); } else{ m_available_check->setEnabled(true); } } } kvpm-0.8.6/kvpm/fsreduce.cpp0000644000175000017500000001130511733530416016212 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "fsreduce.h" #include #include #include #include "executablefinder.h" #include "fsblocksize.h" #include "fsck.h" #include "processprogress.h" bool fs_can_reduce(const QString fs) { const QString executable = "resize2fs"; if(fs == "ext2" || fs == "ext3" || fs == "ext4"){ if( ExecutableFinder::getPath(executable).isEmpty() ){ KMessageBox::error(NULL, i18n("Executable: '%1' not found, this filesystem cannot be reduced", executable)); return false; } else return true; } else return false; } // Returns new fs size in bytes or 0 if no shrinking was done // Returns -1 if fs isn't one of ext2, ext3 or ext4 (not shrinkable) // Takes new_size in bytes. long long fs_reduce(const QString path, const long long new_size, const QString fs) { QStringList arguments, output, success_stringlist, nothing_stringlist, nospace_stringlist; QString size_string; if( !fs_can_reduce(fs) ) return -1; if( ! fsck( path ) ) return 0; const long block_size = get_fs_block_size(path); if( block_size <= 0 ){ KMessageBox::error( 0, i18n("Shrink failed: could not determine filesystem block size")); return 0 ; } arguments << "resize2fs" << path << QString("%1K").arg( new_size / 1024 ); ProcessProgress fs_shrink(arguments); output = fs_shrink.programOutputAll(); success_stringlist = output.filter("is now"); // it worked nothing_stringlist = output.filter("is already"); // already a reduced fs, nothing to do! nospace_stringlist = output.filter("space left"); if( success_stringlist.size() > 0 ){ // Try to shrink the desired amount size_string = success_stringlist[0]; size_string = size_string.remove( 0, size_string.indexOf("now") + 3 ); size_string.truncate( size_string.indexOf("blocks") ); size_string = size_string.simplified(); return size_string.toLongLong() * block_size; } else if( nothing_stringlist.size() > 0 ){ size_string = nothing_stringlist[0]; size_string = size_string.remove( 0, size_string.indexOf("already") + 7 ); size_string.truncate( size_string.indexOf("blocks") ); size_string = size_string.simplified(); return size_string.toLongLong() * block_size; } else if( nospace_stringlist.size() > 0 ){ // Couldn't shrink that much but try again with -M arguments.clear(); success_stringlist.clear(); arguments << "resize2fs" << "-M" << path; ProcessProgress fs_shrink(arguments); output = fs_shrink.programOutput(); success_stringlist = output.filter("is now"); if( success_stringlist.size() > 0 ){ size_string = success_stringlist[0]; size_string = size_string.remove( 0, size_string.indexOf("now") + 3 ); size_string.truncate( size_string.indexOf("blocks") ); size_string = size_string.simplified(); return size_string.toLongLong() * block_size; } } // Give up and do nothing return 0; } // Returns estimated minimum size of filesystem after shrinking, in bytes // Returns 0 on failure long long get_min_fs_size(const QString path, const QString fs){ QStringList arguments, output; QString size_string; if( fs_can_reduce(fs) ){ arguments << "resize2fs" << "-P" << path; long block_size = get_fs_block_size(path); if( block_size ){ // if blocksize failed skip this part ProcessProgress fs_scan(arguments); output = fs_scan.programOutput(); if( output.size() > 0 && fs_scan.exitCode() == 0 ){ size_string = output[0]; if ( size_string.contains("Estimated", Qt::CaseInsensitive) ){ size_string = size_string.remove( 0, size_string.indexOf(":") + 1 ); size_string = size_string.simplified(); return size_string.toLongLong() * block_size; } else return 0; } } return 0; } return 0; } kvpm-0.8.6/kvpm/lvactionsmenu.cpp0000644000175000017500000005474411733530416017317 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "lvactionsmenu.h" #include #include #include #include "fsck.h" #include "logvol.h" #include "vgtree.h" #include "lvsizechartseg.h" #include "topwindow.h" #include "changemirror.h" #include "lvsizechartseg.h" #include "lvcreate.h" #include "lvchange.h" #include "lvreduce.h" #include "lvremove.h" #include "lvrename.h" #include "mkfs.h" #include "maxfs.h" #include "mount.h" #include "pvmove.h" #include "removefs.h" #include "removemirror.h" #include "removemirrorleg.h" #include "snapmerge.h" #include "unmount.h" #include "volgroup.h" LVActionsMenu::LVActionsMenu(LogVol *logicalVolume, int segment, VolGroup *volumeGroup, QWidget *parent) : KMenu(parent), m_vg(volumeGroup), m_lv(logicalVolume), m_segment(segment) { if( m_vg->getSize() == 0 ) setEnabled(false); KActionCollection *lv_actions = new KActionCollection(this); lv_actions->addAssociatedWidget(this); lv_create_action = lv_actions->addAction( "lvcreate", this, SLOT(createLogicalVolume())); lv_create_action->setText( i18n("Create logical volume...") ); lv_create_action->setIcon( KIcon("document-new") ); lv_remove_action = lv_actions->addAction("lvremove", this, SLOT(removeLogicalVolume())); lv_remove_action->setText( i18n("Remove logical volume...") ); lv_remove_action->setIcon( KIcon("cross") ); addSeparator(); lv_rename_action = lv_actions->addAction("lvrename", this, SLOT(renameLogicalVolume())); lv_rename_action->setText( i18n("Rename logical volume...")); lv_rename_action->setIcon( KIcon("edit-rename") ); snap_create_action = lv_actions->addAction("snapcreate", this, SLOT(createSnapshot())); snap_create_action->setText( i18n("Create snapshot...") ); snap_create_action->setIcon( KIcon("camera_add") ); snap_merge_action = lv_actions->addAction("snapmerge", this, SLOT(mergeSnapshot())); snap_merge_action->setText( i18n("Merge snapshot...") ); snap_merge_action->setIcon( KIcon("arrow_join") ); lv_reduce_action = lv_actions->addAction( "lvreduce", this, SLOT(reduceLogicalVolume())); lv_reduce_action->setText( i18n("Reduce logical volume...") ); lv_reduce_action->setIcon( KIcon("delete") ); lv_extend_action = lv_actions->addAction( "lvextend", this, SLOT(extendLogicalVolume())); lv_extend_action->setText( i18n("Extend logical volume...") ); lv_extend_action->setIcon( KIcon("add") ); pv_move_action = lv_actions->addAction("pvmove", this, SLOT(movePhysicalExtents())); pv_move_action->setText( i18n("Move physical extents...") ); pv_move_action->setIcon( KIcon("lorry") ); lv_change_action = lv_actions->addAction( "lvchange", this, SLOT(changeLogicalVolume())); lv_change_action->setText( i18n("Change attributes or tags...")); lv_change_action->setIcon( KIcon("wrench") ); addSeparator(); lv_mkfs_action = new KAction( KIcon("lightning_add"), i18n("Make filesystem..."), this); lv_removefs_action = new KAction( KIcon("lightning_delete"), i18n("Remove filesystem..."), this); lv_maxfs_action = new KAction( KIcon("resultset_last"), i18n("Extend filesystem to fill volume..."), this); lv_fsck_action = new KAction( i18n("Run 'fsck -fp' on filesystem..."), this); mount_filesystem_action = new KAction( KIcon("emblem-mounted"), i18n("Mount filesystem..."), this); unmount_filesystem_action = new KAction( KIcon("emblem-unmounted"), i18n("Unmount filesystem..."), this); add_mirror_legs_action = new KAction( i18n("Add mirror legs to volume..."), this); change_mirror_log_action = new KAction( i18n("Change mirror log..."), this); remove_mirror_action = new KAction( i18n("Remove mirror leg..."), this); remove_mirror_leg_action = new KAction( i18n("Remove this mirror leg..."), this); connect(lv_mkfs_action, SIGNAL(triggered()), this, SLOT(mkfsLogicalVolume())); connect(lv_fsck_action, SIGNAL(triggered()), this, SLOT(fsckLogicalVolume())); connect(lv_removefs_action, SIGNAL(triggered()), this, SLOT(removefsLogicalVolume())); connect(lv_maxfs_action, SIGNAL(triggered()), this, SLOT(maxfsLogicalVolume())); connect(add_mirror_legs_action, SIGNAL(triggered()), this, SLOT(addMirrorLegs())); connect(change_mirror_log_action, SIGNAL(triggered()), this, SLOT(changeMirrorLog())); connect(mount_filesystem_action, SIGNAL(triggered()), this, SLOT(mountFilesystem())); connect(unmount_filesystem_action, SIGNAL(triggered()), this, SLOT(unmountFilesystem())); connect(remove_mirror_action, SIGNAL(triggered()), this, SLOT(removeMirror())); connect(remove_mirror_leg_action, SIGNAL(triggered()), this, SLOT(removeMirrorLeg())); KMenu *mirror_menu = new KMenu( i18n("Mirror operations"), this); mirror_menu->setIcon( KIcon("document-multiple") ); addMenu(mirror_menu); mirror_menu->addAction(add_mirror_legs_action); mirror_menu->addAction(change_mirror_log_action); mirror_menu->addAction(remove_mirror_action); mirror_menu->addAction(remove_mirror_leg_action); filesystem_menu = new KMenu( i18n("Filesystem operations"), this); addMenu(filesystem_menu); filesystem_menu->addAction(mount_filesystem_action); filesystem_menu->addAction(unmount_filesystem_action); filesystem_menu->addSeparator(); filesystem_menu->addAction(lv_maxfs_action); filesystem_menu->addAction(lv_fsck_action); filesystem_menu->addSeparator(); filesystem_menu->addAction(lv_mkfs_action); filesystem_menu->addAction(lv_removefs_action); filesystem_menu->setEnabled(false); if( m_lv ){ // snap containers are replaced by the "real" lv before getting here, see: vg->getLvByName() if( m_lv->isSnap() && m_lv->isValid() && !m_lv->isMerging() ) snap_merge_action->setEnabled(true); else snap_merge_action->setEnabled(false); if( m_lv->isWritable() && !m_lv->isLocked() && !m_lv->isVirtual() && !m_lv->isMirrorLeg() && !m_lv->isMirrorLog() ){ if( m_lv->isMounted() ){ lv_fsck_action->setEnabled(false); lv_mkfs_action->setEnabled(false); lv_removefs_action->setEnabled(false); lv_reduce_action->setEnabled(false); lv_extend_action->setEnabled(true); lv_remove_action->setEnabled(false); unmount_filesystem_action->setEnabled(true); mount_filesystem_action->setEnabled(true); } else if( m_lv->isOpen() && m_lv->getFilesystem() == "swap" ){ lv_fsck_action->setEnabled(false); lv_mkfs_action->setEnabled(false); lv_removefs_action->setEnabled(false); lv_reduce_action->setEnabled(false); lv_extend_action->setEnabled(false); lv_remove_action->setEnabled(false); lv_maxfs_action->setEnabled(false); unmount_filesystem_action->setEnabled(false); mount_filesystem_action->setEnabled(false); } else{ lv_mkfs_action->setEnabled(true); lv_removefs_action->setEnabled(true); lv_reduce_action->setEnabled(true); lv_extend_action->setEnabled(true); lv_remove_action->setEnabled(true); unmount_filesystem_action->setEnabled(false); mount_filesystem_action->setEnabled(true); } if( m_lv->isOrigin() ){ if( m_lv->isMirror() ){ add_mirror_legs_action->setEnabled(false); change_mirror_log_action->setEnabled(true); remove_mirror_action->setEnabled(true); } else{ add_mirror_legs_action->setEnabled(true); change_mirror_log_action->setEnabled(false); remove_mirror_action->setEnabled(false); } if( m_lv->isMerging() ){ lv_extend_action->setEnabled(false); snap_create_action->setEnabled(false); add_mirror_legs_action->setEnabled(false); } else{ lv_extend_action->setEnabled(true); snap_create_action->setEnabled(true); } lv_reduce_action->setEnabled(false); pv_move_action->setEnabled(false); } else if( m_lv->isSnap() ){ add_mirror_legs_action->setEnabled(false); remove_mirror_action->setEnabled(false); change_mirror_log_action->setEnabled(false); mirror_menu->setEnabled(false); lv_maxfs_action->setEnabled(false); snap_create_action->setEnabled(false); pv_move_action->setEnabled(false); if( m_lv->isMerging() || !m_lv->isValid() ){ lv_extend_action->setEnabled(false); lv_reduce_action->setEnabled(false); mount_filesystem_action->setEnabled(false); lv_fsck_action->setEnabled(false); lv_mkfs_action->setEnabled(false); lv_removefs_action->setEnabled(false); if( m_lv->isMounted() ) filesystem_menu->setEnabled(true); if( !m_lv->isValid() ) lv_remove_action->setEnabled(true); else lv_remove_action->setEnabled(false); } else if( m_lv->isMounted() ){ lv_extend_action->setEnabled(true); lv_reduce_action->setEnabled(false); lv_fsck_action->setEnabled(false); lv_mkfs_action->setEnabled(false); lv_removefs_action->setEnabled(false); } else{ lv_extend_action->setEnabled(true); lv_reduce_action->setEnabled(true); lv_fsck_action->setEnabled(true); lv_mkfs_action->setEnabled(true); lv_removefs_action->setEnabled(true); } } else if( m_lv->isMirror() ){ remove_mirror_action->setEnabled(true); change_mirror_log_action->setEnabled(true); pv_move_action->setEnabled(false); if( m_lv->isUnderConversion() ){ add_mirror_legs_action->setEnabled(false); lv_extend_action->setEnabled(false); lv_reduce_action->setEnabled(false); } else{ add_mirror_legs_action->setEnabled(true); lv_extend_action->setEnabled(true); lv_reduce_action->setEnabled(true); } } else{ add_mirror_legs_action->setEnabled(true); remove_mirror_action->setEnabled(false); change_mirror_log_action->setEnabled(false); pv_move_action->setEnabled(true); snap_create_action->setEnabled(true); } remove_mirror_leg_action->setEnabled(false); if(m_lv->isSnap() && m_lv->isMerging()){ lv_rename_action->setEnabled(false); lv_change_action->setEnabled(false); } else{ lv_rename_action->setEnabled(true); lv_change_action->setEnabled(true); filesystem_menu->setEnabled(true); } } else if( m_lv->isOrphan() ){ lv_mkfs_action->setEnabled(false); lv_removefs_action->setEnabled(false); lv_maxfs_action->setEnabled(false); lv_remove_action->setEnabled(true); unmount_filesystem_action->setEnabled(false); mount_filesystem_action->setEnabled(false); add_mirror_legs_action->setEnabled(false); lv_change_action->setEnabled(false); lv_extend_action->setEnabled(false); lv_reduce_action->setEnabled(false); lv_rename_action->setEnabled(false); pv_move_action->setEnabled(false); remove_mirror_action->setEnabled(false); change_mirror_log_action->setEnabled(false); remove_mirror_leg_action->setEnabled(false); snap_create_action->setEnabled(false); filesystem_menu->setEnabled(false); } else if( m_lv->isPvmove() ){ lv_mkfs_action->setEnabled(false); lv_removefs_action->setEnabled(false); lv_maxfs_action->setEnabled(false); lv_remove_action->setEnabled(false); unmount_filesystem_action->setEnabled(false); mount_filesystem_action->setEnabled(false); add_mirror_legs_action->setEnabled(false); lv_change_action->setEnabled(false); lv_extend_action->setEnabled(false); lv_reduce_action->setEnabled(false); lv_rename_action->setEnabled(false); pv_move_action->setEnabled(false); remove_mirror_action->setEnabled(false); change_mirror_log_action->setEnabled(false); remove_mirror_leg_action->setEnabled(false); snap_create_action->setEnabled(false); filesystem_menu->setEnabled(false); } else if( m_lv->isMirrorLeg() || m_lv->isMirrorLog() ){ lv_mkfs_action->setEnabled(false); lv_removefs_action->setEnabled(false); lv_maxfs_action->setEnabled(false); lv_remove_action->setEnabled(false); unmount_filesystem_action->setEnabled(false); mount_filesystem_action->setEnabled(false); add_mirror_legs_action->setEnabled(false); lv_change_action->setEnabled(false); lv_extend_action->setEnabled(false); lv_reduce_action->setEnabled(false); pv_move_action->setEnabled(false); remove_mirror_action->setEnabled(false); change_mirror_log_action->setEnabled(false); lv_rename_action->setEnabled(false); snap_create_action->setEnabled(false); filesystem_menu->setEnabled(false); if( !m_lv->isMirrorLog() ) remove_mirror_leg_action->setEnabled(true); else{ remove_mirror_leg_action->setEnabled(false); mirror_menu->setEnabled(false); } } else if( !(m_lv->isWritable()) && m_lv->isLocked() ){ if( m_lv->isMounted() ) unmount_filesystem_action->setEnabled(true); else unmount_filesystem_action->setEnabled(false); mount_filesystem_action->setEnabled(true); lv_removefs_action->setEnabled(false); lv_mkfs_action->setEnabled(false); lv_maxfs_action->setEnabled(false); lv_remove_action->setEnabled(false); add_mirror_legs_action->setEnabled(false); lv_change_action->setEnabled(true); lv_extend_action->setEnabled(false); lv_reduce_action->setEnabled(false); lv_rename_action->setEnabled(false); pv_move_action->setEnabled(false); remove_mirror_action->setEnabled(false); change_mirror_log_action->setEnabled(false); remove_mirror_leg_action->setEnabled(false); snap_create_action->setEnabled(false); filesystem_menu->setEnabled(true); } else if( m_lv->isWritable() && m_lv->isLocked() ){ if( m_lv->isMounted() ) unmount_filesystem_action->setEnabled(true); else unmount_filesystem_action->setEnabled(false); mount_filesystem_action->setEnabled(true); lv_removefs_action->setEnabled(true); lv_mkfs_action->setEnabled(true); lv_remove_action->setEnabled(false); lv_rename_action->setEnabled(false); add_mirror_legs_action->setEnabled(false); lv_change_action->setEnabled(true); lv_extend_action->setEnabled(false); lv_reduce_action->setEnabled(false); pv_move_action->setEnabled(false); remove_mirror_action->setEnabled(false); change_mirror_log_action->setEnabled(false); remove_mirror_leg_action->setEnabled(false); snap_create_action->setEnabled(false); filesystem_menu->setEnabled(true); } else{ if( m_lv->isMounted() ){ lv_remove_action->setEnabled(false); unmount_filesystem_action->setEnabled(true); } else{ lv_remove_action->setEnabled(true); unmount_filesystem_action->setEnabled(false); } if( m_lv->isSnap() || m_lv->isOrigin() ){ add_mirror_legs_action->setEnabled(false); remove_mirror_action->setEnabled(false); change_mirror_log_action->setEnabled(false); pv_move_action->setEnabled(false); if( m_lv->isSnap() ) snap_create_action->setEnabled(false); else snap_create_action->setEnabled(true); mirror_menu->setEnabled(false); } else if( m_lv->isMirror() ){ add_mirror_legs_action->setEnabled(true); remove_mirror_action->setEnabled(true); change_mirror_log_action->setEnabled(true); pv_move_action->setEnabled(false); snap_create_action->setEnabled(false); } else{ add_mirror_legs_action->setEnabled(true); remove_mirror_action->setEnabled(false); change_mirror_log_action->setEnabled(false); pv_move_action->setEnabled(true); snap_create_action->setEnabled(true); } lv_removefs_action->setEnabled(false); lv_mkfs_action->setEnabled(false); lv_fsck_action->setEnabled(false); lv_maxfs_action->setEnabled(false); lv_reduce_action->setEnabled(false); lv_extend_action->setEnabled(false); remove_mirror_leg_action->setEnabled(false); if(!m_lv->isVirtual()){ filesystem_menu->setEnabled(true); mount_filesystem_action->setEnabled(true); lv_change_action->setEnabled(true); } else{ lv_rename_action->setEnabled(false); lv_remove_action->setEnabled(false); mount_filesystem_action->setEnabled(false); lv_change_action->setEnabled(false); filesystem_menu->setEnabled(false); mirror_menu->setEnabled(false); } } if( !m_lv->isActive() ){ lv_removefs_action->setEnabled(false); lv_mkfs_action->setEnabled(false); unmount_filesystem_action->setEnabled(false); mount_filesystem_action->setEnabled(false); filesystem_menu->setEnabled(false); snap_create_action->setEnabled(false); } } else{ snap_merge_action->setEnabled(false); lv_maxfs_action->setEnabled(false); lv_mkfs_action->setEnabled(false); lv_removefs_action->setEnabled(false); lv_remove_action->setEnabled(false); unmount_filesystem_action->setEnabled(false); mount_filesystem_action->setEnabled(false); add_mirror_legs_action->setEnabled(false); lv_change_action->setEnabled(false); lv_extend_action->setEnabled(false); lv_reduce_action->setEnabled(false); lv_rename_action->setEnabled(false); pv_move_action->setEnabled(false); remove_mirror_action->setEnabled(false); change_mirror_log_action->setEnabled(false); remove_mirror_leg_action->setEnabled(false); snap_create_action->setEnabled(false); filesystem_menu->setEnabled(false); } if(!add_mirror_legs_action->isEnabled() && !remove_mirror_action->isEnabled() && !remove_mirror_leg_action->isEnabled() && !change_mirror_log_action->isEnabled()) mirror_menu->setEnabled(false); } void LVActionsMenu::createLogicalVolume() { LVCreateDialog dialog(m_vg); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } void LVActionsMenu::reduceLogicalVolume() { LVReduceDialog dialog(m_lv); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } void LVActionsMenu::extendLogicalVolume() { LVCreateDialog dialog(m_lv, false); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } void LVActionsMenu::addMirrorLegs() { ChangeMirrorDialog dialog(m_lv, false); dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } void LVActionsMenu::changeMirrorLog() { ChangeMirrorDialog dialog(m_lv, true); dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } void LVActionsMenu::removeMirror() { if( remove_mirror(m_lv) ) MainWindow->reRun(); } void LVActionsMenu::removeMirrorLeg() { if( remove_mirror_leg(m_lv) ) MainWindow->reRun(); } void LVActionsMenu::mkfsLogicalVolume() { MkfsDialog dialog(m_lv); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } void LVActionsMenu::fsckLogicalVolume() { if( manual_fsck(m_lv) ) MainWindow->reRun(); } void LVActionsMenu::removefsLogicalVolume() { if( remove_fs( m_lv->getMapperPath() ) ) MainWindow->reRun(); } void LVActionsMenu::maxfsLogicalVolume() { if( max_fs(m_lv) ) MainWindow->reRun(); } void LVActionsMenu::mergeSnapshot() { if( merge_snap(m_lv) ) MainWindow->reRun(); } void LVActionsMenu::removeLogicalVolume() { LVRemoveDialog dialog(m_lv); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == KDialog::Yes) MainWindow->reRun(); } } void LVActionsMenu::renameLogicalVolume() { LVRenameDialog dialog(m_lv); dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } void LVActionsMenu::createSnapshot() { LVCreateDialog dialog(m_lv, true); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } void LVActionsMenu::changeLogicalVolume() { LVChangeDialog dialog(m_lv); dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } void LVActionsMenu::mountFilesystem() { MountDialog dialog(m_lv); dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } void LVActionsMenu::unmountFilesystem() { UnmountDialog dialog(m_lv); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted || dialog.result() == KDialog::Yes) MainWindow->reRun(); } } void LVActionsMenu::movePhysicalExtents() { PVMoveDialog dialog(m_lv, m_segment); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } kvpm-0.8.6/kvpm/partchange.cpp0000644000175000017500000006715211733530416016541 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "partchange.h" #include #include #include #include #include #include #include #include #include "dualselectorbox.h" #include "fsextend.h" #include "fsreduce.h" #include "masterlist.h" #include "pedexceptions.h" #include "partitiongraphic.h" #include "physvol.h" #include "processprogress.h" #include "progressbox.h" #include "pvextend.h" #include "pvreduce.h" #include "storagepartition.h" #include "topwindow.h" #include "volgroup.h" PartitionChangeDialog::PartitionChangeDialog(StoragePartition *const partition, QWidget *parent) : KDialog(parent), m_old_storage_part(partition) { m_bailout = false; KConfigSkeleton skeleton; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", m_use_si_units, false); setup(); const QString fs = m_old_storage_part->getFilesystem(); const long long existing_offset = m_existing_part->geom.start - m_max_part_start; const long long max_offset = m_max_part_size - m_min_shrink_size; long long max_size; if( ! ( fs_can_extend(fs) || partition->isPhysicalVolume() ) ) max_size = m_existing_part->geom.length; else max_size = m_max_part_size; setButtons( KDialog::Ok | KDialog::Cancel | KDialog::Reset ); setWindowTitle( i18n("Move or resize a partition") ); QWidget *const dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *const layout = new QVBoxLayout(); dialog_body->setLayout(layout); QLabel *const label = new QLabel( i18n("Resize Or Move A Partition") ); label->setAlignment(Qt::AlignCenter); layout->addSpacing(5); layout->addWidget(label); layout->addSpacing(5); layout->addWidget( buildInfoGroup(max_size) ); if( m_old_storage_part->isPhysicalVolume() ){ if( m_old_storage_part->getPhysicalVolume()->isActive() ){ max_size -= existing_offset; m_dual_selector = new DualSelectorBox(m_sector_size, m_max_part_size, m_min_shrink_size, max_size, m_existing_part->geom.length, existing_offset, existing_offset, existing_offset); } else{ m_dual_selector = new DualSelectorBox(m_sector_size, m_max_part_size, m_min_shrink_size, max_size, m_existing_part->geom.length, 0, max_offset, existing_offset); } } else{ m_dual_selector = new DualSelectorBox(m_sector_size, m_max_part_size, m_min_shrink_size, max_size, m_existing_part->geom.length, 0, max_offset, existing_offset); } layout->addWidget(m_dual_selector); m_dual_selector->resetSelectors(); validateChange(); connect(m_dual_selector, SIGNAL(changed()), this, SLOT(validateChange())); connect(this, SIGNAL(resetClicked()), m_dual_selector, SLOT(resetSelectors())); connect(this, SIGNAL(okClicked()), this, SLOT(commitPartition())); setDefaultButton(KDialog::Cancel); } void PartitionChangeDialog::commitPartition() { long long new_size = m_dual_selector->getCurrentSize(); long long new_offset = m_dual_selector->getCurrentOffset(); bool grow = false; bool shrink = false; bool move = ( (m_max_part_start + new_offset) != m_existing_part->geom.start ); if( new_size < m_existing_part->geom.length ) { grow = false; shrink = true; } else if( new_size > m_existing_part->geom.length ){ grow = true; shrink = false; } else{ grow = false; shrink = false; } qApp->processEvents(QEventLoop::ExcludeUserInputEvents); if( grow && move ){ if( movePartition() ){ qApp->processEvents(QEventLoop::ExcludeUserInputEvents); growPartition(); } } else if( grow ){ growPartition(); } else if( shrink && move ){ if( shrinkPartition() ){ qApp->processEvents(QEventLoop::ExcludeUserInputEvents); movePartition(); } } else if( shrink ){ shrinkPartition(); } else if( move ){ movePartition(); } qApp->processEvents(QEventLoop::ExcludeUserInputEvents); } void PartitionChangeDialog::validateChange() { if( !m_dual_selector->isValid() ){ button(KDialog::Ok)->setEnabled(false); return; } const long long preceding_sectors = m_dual_selector->getCurrentOffset(); const long long following_sectors = m_max_part_size - ( m_dual_selector->getCurrentOffset() + m_dual_selector->getCurrentSize() ); if( preceding_sectors < 0 || following_sectors < 0 ){ (button(KDialog::Ok))->setEnabled(false); return; } updateGraphicAndLabels(); button(KDialog::Ok)->setEnabled(true); } void PartitionChangeDialog::setup() { const QString fs = m_old_storage_part->getFilesystem(); m_existing_part = m_old_storage_part->getPedPartition(); m_ped_disk = m_existing_part->disk; PedDevice *const ped_device = m_ped_disk->dev; QString message = i18n("Currently only the ext2, ext3 and ext4 file systems " "are supported for file system shrinking. " "Growing is supported for ext2/3/4, jfs, xfs, ntfs and Reiserfs. " "Moving a partition is supported for any filesystem. " "Physical volumes may also be grown, shrunk or moved"); if( ! ( fs == "ext2" || fs == "ext3" || fs == "ext4" || m_old_storage_part->isPhysicalVolume() ) ) KMessageBox::information(0, message); message = i18n("This partition is on the same device with partitions that are busy or mounted. " "If at all possible they should be unmounted before proceeding. Otherise " "changes to the partition table may not be recognized by the kernel."); if( ped_device_is_busy(ped_device) ){ if(KMessageBox::warningContinueCancel(NULL, message, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) != KMessageBox::Continue){ m_bailout = true; } } /* Switch off cylinder alignment */ PedDiskFlag cylinder_flag = ped_disk_flag_get_by_name("cylinder_alignment"); if( ped_disk_is_flag_available(m_ped_disk, cylinder_flag) ) ped_disk_set_flag(m_ped_disk, cylinder_flag, 0); PedSector max_part_end; getMaximumPartition(m_max_part_start, max_part_end, m_sector_size); m_max_part_size = max_part_end - m_max_part_start + 1; if( m_existing_part->type & PED_PARTITION_LOGICAL ) m_logical = true; else m_logical = false; if( m_old_storage_part->isPhysicalVolume() ){ PhysVol *const pv = m_old_storage_part->getPhysicalVolume(); const long mda_count = pv->getMdaCount(); const long long extent_size = pv->getVg()->getExtentSize(); const long long mda_extents = (pv->getMdaSize() / extent_size) + 1; m_min_shrink_size = 1 + (mda_extents * mda_count) + pv->getLastUsedExtent(); m_min_shrink_size *= extent_size; m_min_shrink_size /= m_sector_size; } else{ if( fs_can_reduce(fs) ) m_min_shrink_size = get_min_fs_size(ped_partition_get_path(m_existing_part), fs) / m_sector_size; else m_min_shrink_size = m_existing_part->geom.length; } const PedSector TWO_MIB = 0x200000 / m_sector_size; if( m_min_shrink_size == 0 ) // 0 means we can't shrink it m_min_shrink_size = m_existing_part->geom.length; else if( m_min_shrink_size <= TWO_MIB ){ // Don't allow shrinking below 2 MiB if(m_existing_part->geom.length > TWO_MIB ) m_min_shrink_size = TWO_MIB; else m_min_shrink_size = m_existing_part->geom.length; } } bool PartitionChangeDialog::movefs(long long from_start, long long to_start, long long length) { const long long blocksize = 8000; // sectors moved in each block PedDevice *const device = m_ped_disk->dev; char *const buff = static_cast( malloc( blocksize * m_sector_size ) ) ; const long long blockcount = length / blocksize; const long long extra = length % blocksize; ped_device_open(device); ProgressBox *const progress_box = TopWindow::getProgressBox(); progress_box->setRange(0, blockcount); progress_box->setText( i18n("Moving data") ); int event_timer = 0; qApp->setOverrideCursor(Qt::WaitCursor); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); if( to_start < from_start ){ // moving left for(long long x = 0; x < blockcount; x++){ event_timer++; if(event_timer > 5){ qApp->processEvents(QEventLoop::ExcludeUserInputEvents); event_timer = 0; } if( ! ped_device_read( device, buff, from_start + (x * blocksize), blocksize) ){ qApp->restoreOverrideCursor(); KMessageBox::error( 0, i18n("Move failed: could not read from device") ); return false; } if( ! ped_device_write(device, buff, to_start + (x * blocksize), blocksize) ){ qApp->restoreOverrideCursor(); KMessageBox::error( 0, i18n("Move failed: could not write to device") ); return false; } progress_box->setValue(x); } if( ! ped_device_read(device, buff, from_start + (blockcount * blocksize), extra) ){ qApp->restoreOverrideCursor(); KMessageBox::error( 0, i18n("Move failed: could not read from device") ); return false; } if( ! ped_device_write(device, buff, to_start + (blockcount * blocksize), extra) ){ qApp->restoreOverrideCursor(); KMessageBox::error( 0, i18n("Move failed: could not write to device") ); return false; } } else{ // moving right if( ! ped_device_read(device, buff, from_start + (blockcount * blocksize), extra) ){ qApp->restoreOverrideCursor(); KMessageBox::error( 0, i18n("Move failed: could not read from device") ); return false; } if( ! ped_device_write(device, buff, to_start + (blockcount * blocksize), extra) ){ qApp->restoreOverrideCursor(); KMessageBox::error( 0, i18n("Move failed: could not write to device") ); return false; } for(long long x = blockcount - 1; x >= 0 ; x--){ event_timer++; if(event_timer > 5){ qApp->processEvents(QEventLoop::ExcludeUserInputEvents); event_timer = 0; } if( ! ped_device_read( device, buff, from_start + (x * blocksize), blocksize) ){ qApp->restoreOverrideCursor(); KMessageBox::error( 0, i18n("Move failed: could not read from device") ); return false; } if( ! ped_device_write(device, buff, to_start + (x * blocksize), blocksize) ){ qApp->restoreOverrideCursor(); KMessageBox::error( 0, i18n("Move failed: could not write to device") ); return false; } progress_box->setValue( blockcount - x ); } } qApp->processEvents(QEventLoop::ExcludeUserInputEvents); ped_device_sync( device ); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); ped_device_close( device ); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); qApp->restoreOverrideCursor(); return true; } bool PartitionChangeDialog::shrinkPartition() { hide(); const PedSector ONE_MIB = 0x100000 / m_sector_size; // sectors per megabyte PedDevice *const device = m_ped_disk->dev; const PedSector current_start = m_existing_part->geom.start; const PedSector current_size = m_existing_part->geom.length; const QString fs = m_old_storage_part->getFilesystem(); const QString path = ped_partition_get_path(m_existing_part); const bool is_pv = m_old_storage_part->isPhysicalVolume(); PedSector new_size = m_dual_selector->getCurrentSize(); if( new_size >= current_size ) return false; if( new_size < m_min_shrink_size) new_size = m_min_shrink_size; else if( new_size > m_max_part_size ) new_size = m_max_part_size; PedSector reduced_size = m_dual_selector->getCurrentSize(); if( is_pv ) reduced_size = pv_reduce( path, new_size * m_sector_size ); else reduced_size = fs_reduce( path, new_size * m_sector_size, fs ); new_size = reduced_size / m_sector_size; if( reduced_size % m_sector_size ) new_size++; if( new_size == 0 ) // The shrink failed return false; // This constraint assures we have a new partition at least as long as the fs can shrink it // We allow up to an extra 1MiB sectors for the end of the partition PedAlignment *const start_alignment = ped_alignment_new(0, 1); PedGeometry *const start_range = ped_geometry_new(device, current_start, 1); PedGeometry *end_range; PedAlignment *end_alignment; PedSector maximum_size; PedSector minimum_size = new_size; if( current_start + new_size - 1 > m_max_part_start + m_max_part_size - 1 ){ end_alignment = ped_alignment_new(0, 1); end_range = ped_geometry_new(device, current_start + new_size - 1, 1); maximum_size = minimum_size; } else{ end_alignment = ped_alignment_new(-1, ONE_MIB); end_range = ped_geometry_new(device, current_start + new_size - 1, ONE_MIB); maximum_size = new_size + ONE_MIB - 1; } qApp->setOverrideCursor(Qt::WaitCursor); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); PedConstraint *constraint = ped_constraint_new( start_alignment, end_alignment, start_range, end_range, minimum_size, maximum_size ); int success = ped_disk_set_partition_geom( m_ped_disk, m_existing_part, constraint, current_start, current_start + maximum_size ); qApp->restoreOverrideCursor(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); ped_constraint_destroy(constraint); ped_alignment_destroy(start_alignment); ped_alignment_destroy(end_alignment); ped_geometry_destroy(start_range); ped_geometry_destroy(end_range); if( !success ){ KMessageBox::error( 0, i18n("Partition shrink failed") ); return false; } else{ pedCommitAndWait(m_ped_disk); return true; } } bool PartitionChangeDialog::growPartition() { hide(); const PedSector ONE_MIB = 0x100000 / m_sector_size; // sectors per megabyte PedDevice *const device = m_ped_disk->dev; const QString fs = m_old_storage_part->getFilesystem(); const bool is_pv = m_old_storage_part->isPhysicalVolume(); const PedSector current_start = m_existing_part->geom.start; const PedSector current_size = m_existing_part->geom.length; const PedSector max_end = m_max_part_start + m_max_part_size - 1; const PedSector max_start = m_max_part_start; int success; PedSector min_new_size, max_new_size; // max desired size PedSector proposed_new_size = m_dual_selector->getCurrentSize(); if( proposed_new_size - 1 + current_start > max_end ) proposed_new_size = 1 + max_end - current_start; if( proposed_new_size <= current_size ) return true; if( proposed_new_size < current_size + ONE_MIB ){ if( current_start + ONE_MIB <= max_end){ max_new_size = current_size + ONE_MIB; min_new_size = current_size; } else{ max_new_size = max_end - current_start + 1; min_new_size = current_size; } } else if( proposed_new_size + ONE_MIB >= 1 + max_end - current_start ){ max_new_size = 1 + max_end - current_start; min_new_size = max_new_size - ONE_MIB; } else{ max_new_size = proposed_new_size + ONE_MIB - 1; min_new_size = proposed_new_size; } qApp->processEvents(QEventLoop::ExcludeUserInputEvents); qApp->setOverrideCursor(Qt::WaitCursor); PedGeometry *start_range = ped_geometry_new(device, current_start, 1); PedGeometry *end_range = ped_geometry_new(device, current_start, max_new_size); PedConstraint *constraint = ped_constraint_new( ped_alignment_new(0, 1), ped_alignment_new(-1, ONE_MIB), start_range, end_range, min_new_size, max_new_size); /* if constraint solves to NULL then the new part will fail, so just bail out */ if( ped_constraint_solve_max( constraint ) == NULL ){ qApp->processEvents(QEventLoop::ExcludeUserInputEvents); qApp->restoreOverrideCursor(); KMessageBox::error( 0, i18n("Partition extension failed") ); ped_constraint_destroy(constraint); ped_geometry_destroy(start_range); ped_geometry_destroy(end_range); return false; } success = ped_disk_set_partition_geom(m_ped_disk, m_existing_part, constraint, max_start, max_end); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); qApp->restoreOverrideCursor(); ped_constraint_destroy(constraint); ped_geometry_destroy(start_range); ped_geometry_destroy(end_range); if( !success ){ KMessageBox::error( 0, i18n("Partition extension failed") ); return false; } else { // Here we wait for linux and udev to re-read the partition table before doing anything else. // Otherwise the resize program will fail. pedCommitAndWait(m_ped_disk); if(is_pv){ if( pv_extend( ped_partition_get_path(m_existing_part) ) ) return true; else return false; } else{ if( fs_extend( ped_partition_get_path(m_existing_part), fs, m_old_storage_part->getMountPoints() ) ) return true; else return false; } } } void PartitionChangeDialog::updateGraphicAndLabels() { const PedSector preceding_sectors = m_dual_selector->getCurrentOffset(); const PedSector following_sectors = m_max_part_size - ( preceding_sectors + m_dual_selector->getCurrentSize() ); const long long change_size = m_sector_size * (m_dual_selector->getCurrentSize() - m_existing_part->geom.length ); m_display_graphic->setPrecedingSectors(preceding_sectors); m_display_graphic->setPartitionSectors(m_dual_selector->getCurrentSize()); m_display_graphic->setFollowingSectors(following_sectors); m_display_graphic->repaint(); KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); if(change_size < 0){ QString change = locale->formatByteSize( qAbs(change_size) ); m_change_by_label->setText( i18n("Shrink by : -%1", change) ); } else{ QString change = locale->formatByteSize(change_size); m_change_by_label->setText( i18n("Grow by : %1", change) ); } QString preceding_bytes_string = locale->formatByteSize(preceding_sectors * m_sector_size); m_preceding_label->setText( i18n("Preceding space: %1", preceding_bytes_string) ); long long following_space = following_sectors * m_sector_size; if(following_space < 0) following_space = 0; QString following_bytes_string = locale->formatByteSize(following_space); m_following_label->setText( i18n("Following space: %1", following_bytes_string) ); } bool PartitionChangeDialog::movePartition() { hide(); PedDevice *const device = m_ped_disk->dev; const PedSector ONE_MIB = 0x100000 / m_sector_size; // sectors per megabyte const PedSector max_start = m_max_part_start; const PedSector max_end = max_start + m_max_part_size - 1; const PedSector current_size = m_existing_part->geom.length; PedSector current_start = m_existing_part->geom.start; PedSector new_start = m_max_part_start + m_dual_selector->getCurrentOffset(); // don't move if the move is less than 1 megabyte // and check that we have at least 1 meg to spare if( fabs(current_start - new_start) < ONE_MIB ) return true; // pretend we moved since it wasn't worth doing else if( new_start < current_start ){ // moving left if( ( current_start - max_start ) < ONE_MIB ) return false; else if( ( new_start - max_start ) < ( ONE_MIB / 2 ) ) new_start = max_start + ( ONE_MIB / 2); } else { // moving right if( ( max_end - (current_start + current_size - 1) ) < ONE_MIB ) return false; else if( ( new_start + current_size - 1 ) > ( max_end - ONE_MIB / 2 ) ) new_start = 2 + max_end - ( ( ONE_MIB / 2 ) + current_size ); } qApp->processEvents(QEventLoop::ExcludeUserInputEvents); qApp->setOverrideCursor(Qt::WaitCursor); PedAlignment *start_alignment = ped_alignment_new(0, ONE_MIB); PedAlignment *end_alignment = ped_alignment_new(0, 1); PedGeometry *start_range = ped_geometry_new(device, new_start - ( ONE_MIB / 2 ), ONE_MIB); PedGeometry *end_range = ped_geometry_new(device, new_start + ( current_size - 1 ) - ( ONE_MIB / 2 ), ONE_MIB); PedConstraint *constraint_1MiB = ped_constraint_new( start_alignment, end_alignment, start_range, end_range, current_size, current_size ); PedSector old_start = current_start; PedSector old_size = current_size; int success = 0; if(constraint_1MiB){ success = ped_disk_set_partition_geom( m_ped_disk, m_existing_part, constraint_1MiB, max_start, max_end ); } ped_exception_set_handler(my_handler); current_start = m_existing_part->geom.start; qApp->restoreOverrideCursor(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); ped_constraint_destroy(constraint_1MiB); ped_alignment_destroy(start_alignment); ped_alignment_destroy(end_alignment); ped_geometry_destroy(start_range); ped_geometry_destroy(end_range); if( !success ){ KMessageBox::error( 0, i18n("Repartitioning failed: data not moved") ); return false; } else { if( !movefs(old_start, current_start, old_size) ){ return false; } else{ pedCommitAndWait(m_ped_disk); return true; } } } /* The following function waits for udev to acknowledge the partion changes before exiting */ bool PartitionChangeDialog::pedCommitAndWait(PedDisk *disk) { QStringList args; qApp->setOverrideCursor(Qt::WaitCursor); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); if( !ped_disk_commit(disk) ){ qApp->restoreOverrideCursor(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); return false; } else{ args << "udevadm" << "settle"; ProcessProgress wait_settle(args); qApp->restoreOverrideCursor(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); return true; } } QGroupBox *PartitionChangeDialog::buildInfoGroup(const long long maxSize) { QGroupBox *const group = new QGroupBox(); QVBoxLayout *const layout = new QVBoxLayout(); group->setLayout(layout); layout->addSpacing(10); m_display_graphic = new PartitionGraphic(); layout->addWidget(m_display_graphic, 0, Qt::AlignCenter); QLabel *const path = new QLabel( i18n("Device: %1", m_old_storage_part->getName()) ); path->setAlignment( Qt::AlignHCenter ); layout->addWidget(path); layout->addSpacing(10); KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); m_change_by_label = new QLabel(); m_preceding_label = new QLabel(); m_following_label = new QLabel(); layout->addWidget( m_preceding_label ); layout->addWidget( m_following_label ); layout->addStretch(); layout->addWidget( new QLabel( i18n("Minimum size: %1", locale->formatByteSize( m_min_shrink_size * m_sector_size )))); layout->addWidget( new QLabel( i18n("Maximum size: %1", locale->formatByteSize( maxSize * m_sector_size ) ))); layout->addStretch(); layout->addSpacing(10); layout->addWidget( m_change_by_label ); return group; } bool PartitionChangeDialog::bailout() { return m_bailout; } void PartitionChangeDialog::getMaximumPartition(PedSector &start, PedSector &end, PedSector §orSize) { PedPartition *const part = m_old_storage_part->getPedPartition(); PedDevice *const device = part->disk->dev; PedDisk *const disk = part->disk; sectorSize = device->sector_size; const PedSector ONE_MIB = 0x100000 / m_sector_size; PedSector const old_start = part->geom.start; PedSector const old_end = part->geom.length + old_start - 1; PedDiskFlag cylinder_flag = ped_disk_flag_get_by_name("cylinder_alignment"); if( ped_disk_is_flag_available(disk, cylinder_flag) ) ped_disk_set_flag(disk, cylinder_flag, 0); PedConstraint *constraint = ped_constraint_any(device); PedGeometry *max_geometry = ped_disk_get_max_partition_geometry(disk, part, constraint); start = max_geometry->start; end = max_geometry->length + max_geometry->start - 1; ped_constraint_destroy(constraint); PedAlignment *const start_align = ped_alignment_new( 0, ONE_MIB); PedAlignment *const end_align = ped_alignment_new(-1, ONE_MIB); PedGeometry *const start_range = ped_geometry_new(device, start, max_geometry->length); PedGeometry *const end_range = ped_geometry_new(device, start, max_geometry->length); constraint = ped_constraint_new(start_align, end_align, start_range, end_range, 1, max_geometry->length); ped_geometry_destroy(max_geometry); max_geometry = ped_disk_get_max_partition_geometry(disk, part, constraint); start = max_geometry->start; end = max_geometry->length + max_geometry->start - 1; // Don't return a size smaller than partition already is if(start > old_start) start = old_start; if(end < old_end) end = old_end; ped_constraint_destroy(constraint); ped_geometry_destroy(max_geometry); } kvpm-0.8.6/kvpm/deviceactionsmenu.cpp0000644000175000017500000003512511733530416020125 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "deviceactionsmenu.h" #include #include #include #include "fsck.h" #include "devicesizechartseg.h" #include "mkfs.h" #include "maxfs.h" #include "mount.h" #include "unmount.h" #include "masterlist.h" #include "partremove.h" #include "partadd.h" #include "partchange.h" #include "physvol.h" #include "storagedevice.h" #include "storagepartition.h" #include "tablecreate.h" #include "topwindow.h" #include "removefs.h" #include "vgreduce.h" #include "vgreduceone.h" #include "vgcreate.h" #include "vgextend.h" DeviceActionsMenu::DeviceActionsMenu( QTreeWidgetItem *item, QWidget *parent) : KMenu(parent) { setup(item); connect(m_fsck_action, SIGNAL(triggered()), this, SLOT(fsckPartition())); connect(m_maxfs_action, SIGNAL(triggered()), this, SLOT(maxfsPartition())); connect(m_maxpv_action, SIGNAL(triggered()), this, SLOT(maxfsPartition())); connect(m_mkfs_action, SIGNAL(triggered()), this, SLOT(mkfsPartition())); connect(m_partremove_action, SIGNAL(triggered()), this, SLOT(removePartition())); connect(m_partadd_action, SIGNAL(triggered()), this, SLOT(addPartition())); connect(m_partchange_action, SIGNAL(triggered()), this, SLOT(changePartition())); connect(m_removefs_action, SIGNAL(triggered()), this, SLOT(removefsPartition())); connect(m_vgcreate_action, SIGNAL(triggered()), this, SLOT(vgcreatePartition())); connect(m_tablecreate_action,SIGNAL(triggered()), this, SLOT(tablecreatePartition())); connect(m_vgreduce_action, SIGNAL(triggered()), this, SLOT(vgreducePartition())); connect(m_mount_action, SIGNAL(triggered()), this, SLOT(mountPartition())); connect(m_unmount_action, SIGNAL(triggered()), this, SLOT(unmountPartition())); connect(m_vgextend_menu, SIGNAL(triggered(QAction*)), this, SLOT(vgextendPartition(QAction*))); } void DeviceActionsMenu::setup(QTreeWidgetItem *item) { KMenu *const filesystem_menu = new KMenu( i18n("Filesystem operations"), this); m_vgextend_menu = new KMenu( i18n("Extend volume group"), this); m_vgextend_menu->setIcon( KIcon("add") ); m_partchange_action = new KAction( i18n("Move or resize disk partition"), this); m_maxpv_action = new KAction( KIcon("resultset_last"), i18n("Extend physical volume to fill device"), this); m_partadd_action = new KAction( i18n("Add disk partition"), this); m_partremove_action = new KAction( i18n("Remove disk partition"), this); m_vgcreate_action = new KAction( KIcon("document-new"), i18n("Create volume group"), this); m_tablecreate_action = new KAction( KIcon("exclamation"), i18n("Create or remove a partition table"), this); m_vgreduce_action = new KAction( KIcon("delete"), i18n("Remove from volume group"), this); m_mount_action = new KAction( KIcon("emblem-mounted"), i18n("Mount filesystem"), this); m_unmount_action = new KAction( KIcon("emblem-unmounted"), i18n("Unmount filesystem"), this); m_maxfs_action = new KAction( KIcon("resultset_last"), i18n("Extend filesystem to fill partition"), this); m_fsck_action = new KAction( i18n("Run 'fsck -fp' on filesystem"), this); m_mkfs_action = new KAction( KIcon("lightning_add"), i18n("Make filesystem"), this); m_removefs_action = new KAction( KIcon("lightning_delete"), i18n("Remove filesystem"), this); addAction(m_tablecreate_action); addSeparator(); addAction(m_partremove_action); addAction(m_partadd_action); addAction(m_partchange_action); addAction(m_maxpv_action); addSeparator(); addAction(m_vgcreate_action); addAction(m_vgreduce_action); addMenu(m_vgextend_menu); addSeparator(); addMenu(filesystem_menu); filesystem_menu->addAction(m_mount_action); filesystem_menu->addAction(m_unmount_action); filesystem_menu->addSeparator(); filesystem_menu->addAction(m_maxfs_action); filesystem_menu->addAction(m_fsck_action); filesystem_menu->addSeparator(); filesystem_menu->addAction(m_mkfs_action); filesystem_menu->addAction(m_removefs_action); m_vg_name = item->data(5, Qt::DisplayRole).toString(); // only set if this is a pv in a vg const QStringList group_names = MasterList::getVgNames(); for(int x = 0; x < group_names.size(); x++){ vgextend_actions.append(new QAction(group_names[x], this)); m_vgextend_menu->addAction(vgextend_actions[x]); } if(item){ setEnabled(true); m_dev = (StorageDevice *) (( item->data(1, Qt::UserRole)).value() ); filesystem_menu->setEnabled(false); if( ( item->data(0, Qt::UserRole)).canConvert() ){ // its a partition m_part = (StoragePartition *) (( item->data(0, Qt::UserRole)).value() ); m_maxpv_action->setText( i18n("Extend physical volume to fill partition") ); m_tablecreate_action->setEnabled(false); m_mount_action->setEnabled( m_part->isMountable() ); m_unmount_action->setEnabled( m_part->isMounted() ); m_fsck_action->setEnabled( !m_part->isMounted() && !m_part->isBusy() ); if( m_part->getPedType() & 0x04 ){ // freespace m_maxfs_action->setEnabled(false); m_maxpv_action->setEnabled(false); m_mkfs_action->setEnabled(false); m_partremove_action->setEnabled(false); m_partchange_action->setEnabled(false); m_partadd_action->setEnabled(true); m_removefs_action->setEnabled(false); m_vgcreate_action->setEnabled(false); m_vgextend_menu->setEnabled(false); m_vgreduce_action->setEnabled(false); } else if( m_part->getPedType() & 0x02 ){ // extended partition m_maxfs_action->setEnabled(false); m_maxpv_action->setEnabled(false); m_mkfs_action->setEnabled(false); if( m_part->isEmpty() ) m_partadd_action->setEnabled(true); else m_partadd_action->setEnabled(false); m_partchange_action->setEnabled(false); m_removefs_action->setEnabled(false); m_vgcreate_action->setEnabled(false); m_vgextend_menu->setEnabled(false); m_vgreduce_action->setEnabled(false); if( m_part->isEmpty() ) m_partremove_action->setEnabled(true); else m_partremove_action->setEnabled(false); } else if( m_part->isPhysicalVolume() ){ m_maxfs_action->setEnabled(false); m_mkfs_action->setEnabled(false); m_partremove_action->setEnabled(false); if( m_part->getPhysicalVolume()->isActive() ){ m_partchange_action->setEnabled(false); m_maxpv_action->setEnabled(false); } else{ m_partchange_action->setEnabled(true); m_maxpv_action->setEnabled(true); } m_partadd_action->setEnabled(false); m_removefs_action->setEnabled(false); m_vgcreate_action->setEnabled(false); m_vgextend_menu->setEnabled(false); if( m_part->getPhysicalVolume()->getPercentUsed() == 0 ) m_vgreduce_action->setEnabled(true); else m_vgreduce_action->setEnabled(false); } else if( m_part->isNormal() || m_part->isLogical() ){ filesystem_menu->setEnabled(true); m_maxpv_action->setEnabled(false); if( m_part->isMounted() || m_part->isBusy() ){ m_partremove_action->setEnabled(false); m_partchange_action->setEnabled(false); m_mkfs_action->setEnabled(false); m_maxfs_action->setEnabled(false); m_removefs_action->setEnabled(false); m_vgcreate_action->setEnabled(false); m_vgextend_menu->setEnabled(false); } else{ // not mounted or busy m_partremove_action->setEnabled(true); m_partchange_action->setEnabled(true); m_maxfs_action->setEnabled(true); m_mkfs_action->setEnabled(true); m_removefs_action->setEnabled(true); m_vgcreate_action->setEnabled(true); if(group_names.size()) m_vgextend_menu->setEnabled(true); else m_vgextend_menu->setEnabled(false); } m_partadd_action->setEnabled(false); m_vgreduce_action->setEnabled(false); } } else { // its a whole device m_part = NULL; m_mount_action->setEnabled(false); m_unmount_action->setEnabled(false); m_maxfs_action->setEnabled(false); m_fsck_action->setEnabled(false); if( m_dev->isPhysicalVolume() ){ m_tablecreate_action->setEnabled(false); m_mkfs_action->setEnabled(false); if( m_dev->getPhysicalVolume()->isActive() ) m_maxpv_action->setEnabled(false); else m_maxpv_action->setEnabled(true); m_partremove_action->setEnabled(false); m_partchange_action->setEnabled(false); m_partadd_action->setEnabled(false); m_removefs_action->setEnabled(false); m_vgcreate_action->setEnabled(false); m_vgextend_menu->setEnabled(false); if( m_dev->getPhysicalVolume()->getPercentUsed() == 0 ) m_vgreduce_action->setEnabled(true); else m_vgreduce_action->setEnabled(false); } else{ // not a pv m_partremove_action->setEnabled(false); m_partchange_action->setEnabled(false); m_partadd_action->setEnabled(false); m_mkfs_action->setEnabled(false); m_removefs_action->setEnabled(false); m_vgreduce_action->setEnabled(false); m_maxpv_action->setEnabled(false); if( m_dev->isBusy() || m_dev->getRealPartitionCount() != 0 ){ m_tablecreate_action->setEnabled(false); m_vgcreate_action->setEnabled(false); m_vgextend_menu->setEnabled(false); } else{ m_tablecreate_action->setEnabled(true); m_vgcreate_action->setEnabled(true); m_vgextend_menu->setEnabled(true); } } } } else setEnabled(false); // if item points to NULL, do nothing } void DeviceActionsMenu::mkfsPartition() { MkfsDialog dialog(m_part); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } void DeviceActionsMenu::fsckPartition() { if( manual_fsck(m_part) ) MainWindow->reRun(); } void DeviceActionsMenu::maxfsPartition() { if(m_part){ // m_part == NULL if not a partition if( max_fs(m_part) ) MainWindow->reRun(); } else{ if( max_fs(m_dev) ) MainWindow->reRun(); } } void DeviceActionsMenu::removePartition() { if( remove_partition(m_part) ) MainWindow->reRun(); } void DeviceActionsMenu::addPartition() { PartitionAddDialog dialog(m_part); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } void DeviceActionsMenu::changePartition() { PartitionChangeDialog dialog(m_part); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } void DeviceActionsMenu::vgcreatePartition() { if(m_part){ VGCreateDialog dialog(NULL, m_part); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } else{ // whole device, not partition VGCreateDialog dialog(m_dev, NULL); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } } void DeviceActionsMenu::tablecreatePartition() { if( create_table( m_dev->getName() ) ) MainWindow->reRun(); } void DeviceActionsMenu::vgreducePartition() // pvs can also be whole devices { QString name; if(m_part) name = m_part->getName(); else name = m_dev->getName(); if( reduce_vg_one(m_vg_name, name) ) MainWindow->reRun(); } void DeviceActionsMenu::vgextendPartition(QAction *action) { QString vg_name = action->text(); VolGroup *vg = MasterList::getVgByName( vg_name.remove(QChar('&')) ); if(m_part){ VGExtendDialog dialog(vg, NULL, m_part); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } else{ // whole device, not partition VGExtendDialog dialog(vg, m_dev, NULL); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } } } void DeviceActionsMenu::mountPartition() { MountDialog dialog(m_part); dialog.exec(); if(dialog.result() == QDialog::Accepted) MainWindow->reRun(); } void DeviceActionsMenu::removefsPartition() { if( remove_fs( m_part->getName() ) ) MainWindow->reRun(); } void DeviceActionsMenu::unmountPartition() { UnmountDialog dialog(m_part); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted || dialog.result() == KDialog::Yes) MainWindow->reRun(); } } kvpm-0.8.6/kvpm/lvsizechartseg.h0000644000175000017500000000150311733530416017113 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef LVSIZECHARTSEG_H #define LVSIZECHARTSEG_H #include #include #include #include class VolGroup; class LogVol; class LVChartSeg : public QFrame { Q_OBJECT VolGroup *m_vg; LogVol *m_lv; KMenu *m_context_menu; public: LVChartSeg(VolGroup *const group, LogVol *const volume, const QString use, QWidget *parent); private slots: void popupContextMenu(QPoint); }; #endif kvpm-0.8.6/kvpm/lvsizechartseg.cpp0000644000175000017500000000770411733530416017457 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "lvsizechartseg.h" #include #include #include #include #include "logvol.h" #include "lvactionsmenu.h" #include "volgroup.h" /* This should be passed *lv = 0 if it is really free space on a volume group that is being displayed */ LVChartSeg::LVChartSeg(VolGroup *const group, LogVol *const volume, const QString use, QWidget *parent) : QFrame(parent), m_vg(group), m_lv(volume) { setFrameStyle( QFrame::Sunken | QFrame::Panel ); setLineWidth(2); QVBoxLayout *const layout = new QVBoxLayout(); QWidget *const color_widget = new QWidget(); layout->addWidget(color_widget); layout->setSpacing(0); layout->setMargin(0); setLayout(layout); QPalette colorset; KConfigSkeleton skeleton; QColor ext2_color, ext3_color, ext4_color, reiser_color, reiser4_color, msdos_color, jfs_color, xfs_color, none_color, free_color, swap_color, hfs_color, btrfs_color, ntfs_color; skeleton.setCurrentGroup("FilesystemColors"); skeleton.addItemColor("ext2", ext2_color, Qt::blue); skeleton.addItemColor("ext3", ext3_color, Qt::darkBlue); skeleton.addItemColor("ext4", ext4_color, Qt::cyan); skeleton.addItemColor("btrfs", btrfs_color, Qt::yellow); skeleton.addItemColor("reiser", reiser_color, Qt::red); skeleton.addItemColor("reiser4", reiser4_color, Qt::darkRed); skeleton.addItemColor("msdos", msdos_color, Qt::darkYellow); skeleton.addItemColor("jfs", jfs_color, Qt::magenta); skeleton.addItemColor("xfs", xfs_color, Qt::darkCyan); skeleton.addItemColor("hfs", hfs_color, Qt::darkMagenta); skeleton.addItemColor("ntfs", ntfs_color, Qt::darkGray); skeleton.addItemColor("none", none_color, Qt::black); skeleton.addItemColor("free", free_color, Qt::green); skeleton.addItemColor("swap", swap_color, Qt::lightGray); if(use == "ext2") colorset.setColor(QPalette::Window, ext2_color); else if(use == "ext3") colorset.setColor(QPalette::Window, ext3_color); else if(use == "ext4") colorset.setColor(QPalette::Window, ext4_color); else if(use == "btrfs") colorset.setColor(QPalette::Window, btrfs_color); else if(use == "reiserfs") colorset.setColor(QPalette::Window, reiser_color); else if(use == "reiser4") colorset.setColor(QPalette::Window, reiser4_color); else if(use == "hfs") colorset.setColor(QPalette::Window, hfs_color); else if(use == "vfat") colorset.setColor(QPalette::Window, msdos_color); else if(use == "jfs") colorset.setColor(QPalette::Window, jfs_color); else if(use == "xfs") colorset.setColor(QPalette::Window, xfs_color); else if(use == "ntfs") colorset.setColor(QPalette::Window, ntfs_color); else if(use == "swap") colorset.setColor(QPalette::Window, swap_color); else if(use == "freespace") colorset.setColor(QPalette::Window, free_color); else colorset.setColor(QPalette::Window, none_color); color_widget->setPalette(colorset); color_widget->setAutoFillBackground(true); if( !m_vg->isExported() ){ setContextMenuPolicy(Qt::CustomContextMenu); if( m_lv ) setToolTip( m_lv->getName() ); else setToolTip( i18n("free space") ); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(popupContextMenu(QPoint)) ); } } void LVChartSeg::popupContextMenu(QPoint) { m_context_menu = new LVActionsMenu(m_lv, -1, m_vg, this); m_context_menu->exec(QCursor::pos()); m_context_menu->deleteLater(); } kvpm-0.8.6/kvpm/removemissing.cpp0000644000175000017500000000402111733530416017276 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "removemissing.h" #include #include #include #include "processprogress.h" #include "volgroup.h" bool remove_missing_pv(VolGroup *volumeGroup) { RemoveMissingDialog dialog(volumeGroup); dialog.exec(); if(dialog.result() == QDialog::Accepted){ ProcessProgress remove_missing( dialog.arguments() ); return true; } return false; } RemoveMissingDialog::RemoveMissingDialog(VolGroup *volumeGroup, QWidget *parent) : KDialog(parent), m_vg(volumeGroup) { setWindowTitle( i18n("Remove missing physical volumes") ); QWidget *dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *layout = new QVBoxLayout(); dialog_body->setLayout(layout); QLabel *message = new QLabel( i18n("Removing missing physical volumes may result in data loss! Use with extreme care.") ); message->setWordWrap(true); layout->addWidget(message); QGroupBox *radio_box = new QGroupBox(); QVBoxLayout *radio_box_layout = new QVBoxLayout(); radio_box->setLayout(radio_box_layout); layout->addWidget(radio_box); m_empty_button = new QRadioButton("Remove only empty physical volumes"); m_all_button = new QRadioButton("Remove all missing physical volumes"); m_empty_button->setChecked(true); radio_box_layout->addWidget(m_empty_button); radio_box_layout->addWidget(m_all_button); } QStringList RemoveMissingDialog::arguments() { QStringList args; args << "vgreduce" << "--removemissing"; if( m_all_button->isChecked() ) args << "--force"; args << m_vg->getName(); return args; } kvpm-0.8.6/kvpm/vgreduceone.cpp0000644000175000017500000000175311733530416016726 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vgreduceone.h" #include #include #include #include "processprogress.h" bool reduce_vg_one(QString volumeGroupName, QString physicalVolumeName) { QStringList args; const QString message = i18n( "Remove physical volume: %1 from volume " "group: %2", physicalVolumeName, volumeGroupName ); if(KMessageBox::questionYesNo(0, message) == KMessageBox::Yes){ args << "vgreduce" << volumeGroupName << physicalVolumeName; ProcessProgress remove(args); return true; } else{ return false; } } kvpm-0.8.6/kvpm/vgimport.h0000644000175000017500000000075511733530416015735 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGIMPORT_H #define VGIMPORT_H class VolGroup; bool export_vg(VolGroup *const volumeGroup); #endif kvpm-0.8.6/kvpm/topwindow.cpp0000644000175000017500000004100611733530416016445 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "topwindow.h" #include #include #include #include #include #include #include #include "kvpmconfigdialog.h" #include "devicetab.h" #include "executablefinder.h" #include "logvol.h" #include "masterlist.h" #include "maintabwidget.h" #include "physvol.h" #include "processprogress.h" #include "progressbox.h" #include "pvmove.h" #include "removemissing.h" #include "vgcreate.h" #include "vgchange.h" #include "vgexport.h" #include "vgextend.h" #include "vgimport.h" #include "vgmerge.h" #include "vgremove.h" #include "vgrename.h" #include "vgreduce.h" #include "vgsplit.h" #include "volgroup.h" #include "volumegrouptab.h" ProgressBox* TopWindow::m_progress_box = NULL; // Static initializing TopWindow::TopWindow(MasterList *const masterList, ExecutableFinder *const executableFinder, QWidget *parent) : KMainWindow(parent), m_master_list(masterList), m_executable_finder(executableFinder) { m_tab_widget = NULL; m_progress_box = new ProgressBox(); // make sure this stays *before* master_list->rescan() gets called! menuBar()->addMenu( buildFileMenu() ); menuBar()->addMenu( buildGroupsMenu() ); menuBar()->addMenu( buildToolsMenu() ); menuBar()->addMenu( buildSettingsMenu() ); menuBar()->addMenu( buildHelpMenu() ); m_tab_widget = new MainTabWidget(this); setCentralWidget(m_tab_widget); m_device_tab = new DeviceTab(); m_tab_widget->appendDeviceTab(m_device_tab, i18n("Storage Devices") ); menuBar()->setCornerWidget(m_progress_box); connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(cleanUp())); reRun(); // reRun also does the initial run } void TopWindow::reRun() { m_master_list->rescan(); // loads the list with new data updateTabs(); } ProgressBox* TopWindow::getProgressBox() { return m_progress_box; } void TopWindow::updateTabs() { VolumeGroupTab *tab; QList groups; bool vg_exists; disconnect(m_tab_widget, SIGNAL(currentIndexChanged()), this, SLOT(setupMenus())); m_device_tab->rescan( MasterList::getStorageDevices() ); groups = MasterList::getVolGroups(); // if there is a tab for a deleted vg then delete the tab for(int x = 1; x < m_tab_widget->getCount(); x++){ vg_exists = false; for(int y = 0; y < groups.size(); y++){ if( m_tab_widget->getUnmungedText(x) == groups[y]->getName() ) vg_exists = true; } if( !vg_exists ){ m_tab_widget->deleteTab(x); } } // if there is a new vg and no tab then create one for( int y = 0; y < groups.size(); y++){ vg_exists = false; for(int x = 1; x < m_tab_widget->getCount(); x++){ if( m_tab_widget->getUnmungedText(x) == groups[y]->getName() ){ vg_exists = true; if(groups[y]->isPartial()) m_tab_widget->setIcon( x, KIcon("exclamation") ); else m_tab_widget->setIcon( x, KIcon() ); } } if( !vg_exists ){ tab = new VolumeGroupTab(groups[y]); if( groups[y]->isPartial() ) m_tab_widget->appendVolumeGroupTab( tab, KIcon("exclamation"),groups[y]->getName() ); else m_tab_widget->appendVolumeGroupTab( tab, KIcon(), groups[y]->getName() ); } } for(int x = 0; x < (m_tab_widget->getCount() - 1); x++) m_tab_widget->getVolumeGroupTab(x)->rescan(); setupMenus(); connect(m_tab_widget, SIGNAL(currentIndexChanged()), this, SLOT(setupMenus())); } void TopWindow::setupMenus() { int index = m_tab_widget->getCurrentIndex(); bool has_active = false; QList lvs; if(index){ m_vg = MasterList::getVgByName( m_tab_widget->getUnmungedText(index) ); if( m_vg != NULL ){ lvs = m_vg->getLogicalVolumes(); for( int x = lvs.size() - 1; x >= 0 ;x-- ){ if( lvs[x]->isActive() ) has_active = true; } } } else m_vg = NULL; // only enable group removal if the tab is // a volume group with no logical volumes if(m_vg){ if( lvs.size() || m_vg->isPartial() || m_vg->isExported() ) m_remove_vg_action->setEnabled(false); else m_remove_vg_action->setEnabled(true); if( m_vg->isPartial() ) m_remove_missing_action->setEnabled(true); else m_remove_missing_action->setEnabled(false); if( m_vg->isExported() ){ m_split_vg_action->setEnabled(false); m_merge_vg_action->setEnabled(false); m_import_vg_action->setEnabled(true); m_export_vg_action->setEnabled(false); m_reduce_vg_action->setEnabled(false); m_extend_vg_action->setEnabled(false); } else if( !m_vg->isPartial() ){ m_import_vg_action->setEnabled(false); m_reduce_vg_action->setEnabled(true); m_split_vg_action->setEnabled(true); m_merge_vg_action->setEnabled(true); m_extend_vg_action->setEnabled(true); if(has_active) m_export_vg_action->setEnabled(false); else m_export_vg_action->setEnabled(true); } else{ m_split_vg_action->setEnabled(false); m_merge_vg_action->setEnabled(false); m_import_vg_action->setEnabled(false); m_export_vg_action->setEnabled(false); m_reduce_vg_action->setEnabled(false); m_extend_vg_action->setEnabled(false); } m_rename_vg_action->setEnabled(true); m_change_vg_action->setEnabled(true); } else{ m_reduce_vg_action->setEnabled(false); m_rename_vg_action->setEnabled(false); m_remove_vg_action->setEnabled(false); m_remove_missing_action->setEnabled(false); m_change_vg_action->setEnabled(false); m_import_vg_action->setEnabled(false); m_split_vg_action->setEnabled(false); m_merge_vg_action->setEnabled(false); m_export_vg_action->setEnabled(false); m_extend_vg_action->setEnabled(false); } } void TopWindow::changeVolumeGroup() { if( change_vg(m_vg) ) reRun(); } void TopWindow::createVolumeGroup() { VGCreateDialog dialog; if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) reRun(); } } void TopWindow::removeVolumeGroup() { if( remove_vg(m_vg) ) reRun(); } void TopWindow::renameVolumeGroup() { if( rename_vg(m_vg) ) reRun(); } void TopWindow::reduceVolumeGroup() { if( reduce_vg(m_vg) ) reRun(); } void TopWindow::exportVolumeGroup() { if( export_vg(m_vg) ) reRun(); } void TopWindow::extendVolumeGroup() { VGExtendDialog dialog(m_vg); if( !dialog.bailout() ){ dialog.exec(); if(dialog.result() == QDialog::Accepted) reRun(); } } void TopWindow::importVolumeGroup() { if( import_vg(m_vg) ) reRun(); } void TopWindow::splitVolumeGroup() { if( split_vg(m_vg) ) reRun(); } void TopWindow::mergeVolumeGroup() { if( merge_vg(m_vg) ) reRun(); } void TopWindow::removeMissingVolumes() { if( remove_missing_pv(m_vg) ) reRun(); } void TopWindow::restartPhysicalVolumeMove() { if( restart_pvmove() ) reRun(); } void TopWindow::stopPhysicalVolumeMove() { if( stop_pvmove() ) reRun(); } void TopWindow::configKvpm() { KvpmConfigDialog *const dialog = new KvpmConfigDialog( this, "settings", new KConfigSkeleton(), m_executable_finder ); connect(dialog, SIGNAL(applyClicked()), this, SLOT(updateTabs())); dialog->exec(); disconnect(dialog, SIGNAL(applyClicked()), this, SLOT(updateTabs())); dialog->deleteLater(); updateTabs(); } void TopWindow::cleanUp() { delete m_master_list; // This calls lvm_quit() on destruct } void TopWindow::closeEvent(QCloseEvent *) { qApp->quit(); } void TopWindow::showVolumeGroupInfo(bool show) { KConfigSkeleton skeleton; bool show_vg_info; skeleton.setCurrentGroup("General"); skeleton.addItemBool("show_vg_info", show_vg_info, true); show_vg_info = show; skeleton.writeConfig(); updateTabs(); } void TopWindow::showVolumeGroupBar(bool show) { KConfigSkeleton skeleton; bool show_lv_bar; skeleton.setCurrentGroup("General"); skeleton.addItemBool("show_lv_bar", show_lv_bar, true); show_lv_bar = show; skeleton.writeConfig(); updateTabs(); } void TopWindow::useSiUnits(bool use) { KConfigSkeleton skeleton; bool use_si_units; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", use_si_units, false); use_si_units = use; skeleton.writeConfig(); updateTabs(); } KMenu *TopWindow::buildSettingsMenu() { KMenu *const settings_menu = new KMenu( i18n("Settings") ); KToggleAction *const show_vg_info_action = new KToggleAction( KIcon("preferences-other"), i18n("Show Volume Group Information"), this); KToggleAction *const show_lv_bar_action = new KToggleAction( KIcon("preferences-other"), i18n("Show Volume Group Bar Graph"), this); KToggleAction *const use_si_units_action = new KToggleAction( KIcon("preferences-other"), i18n("Use Metric SI Units"), this); KAction *const config_kvpm_action = new KAction( KIcon("configure"), i18n("Configure kvpm..."), this); settings_menu->addAction(show_vg_info_action); settings_menu->addAction(show_lv_bar_action); settings_menu->addAction(use_si_units_action); settings_menu->addSeparator(); settings_menu->addAction(config_kvpm_action); KConfigSkeleton skeleton; bool show_vg_info, show_lv_bar, use_si_units; skeleton.setCurrentGroup("General"); skeleton.addItemBool( "show_vg_info", show_vg_info, true ); skeleton.addItemBool( "show_lv_bar", show_lv_bar, true ); skeleton.addItemBool( "use_si_units", use_si_units, false ); // This must be *before* the following connect() statements show_vg_info_action->setChecked(show_vg_info); show_lv_bar_action->setChecked(show_lv_bar); use_si_units_action->setChecked(use_si_units); connect(config_kvpm_action, SIGNAL(triggered()), this, SLOT(configKvpm())); connect(show_vg_info_action, SIGNAL(toggled(bool)), this, SLOT(showVolumeGroupInfo(bool))); connect(show_lv_bar_action, SIGNAL(toggled(bool)), this, SLOT(showVolumeGroupBar(bool))); connect(use_si_units_action, SIGNAL(toggled(bool)), this, SLOT(useSiUnits(bool))); return settings_menu; } KMenu *TopWindow::buildToolsMenu() { KMenu *const tools_menu = new KMenu( i18n("Tools") ); KAction *const rescan_action = new KAction( KIcon("view-refresh"), i18n("Rescan System"), this); KAction *const restart_pvmove_action = new KAction( KIcon("system-reboot"), i18n("Restart interrupted pvmove"), this); KAction *const stop_pvmove_action = new KAction( KIcon("process-stop"), i18n("Abort pvmove"), this); tools_menu->addAction(rescan_action); tools_menu->addSeparator(); tools_menu->addAction(restart_pvmove_action); tools_menu->addAction(stop_pvmove_action); connect(rescan_action, SIGNAL(triggered()), this, SLOT(reRun())); connect(restart_pvmove_action, SIGNAL(triggered()), this, SLOT(restartPhysicalVolumeMove())); connect(stop_pvmove_action, SIGNAL(triggered()), this, SLOT(stopPhysicalVolumeMove())); return tools_menu; } KMenu *TopWindow::buildHelpMenu() { KAboutData *const about_data = new KAboutData( QByteArray("kvpm"), QByteArray(""), ki18n("kvpm"), QByteArray("0.8.6"), ki18n("Linux volume and partition manager for KDE.\n" "This program is still under development,\n" "bug reports and any comments are welcomed.\n \n" "Additional icons taken from the Silk icon set by\n" "Mark James http://www.famfamfam.com/lab/icons/silk/\n" "under the Creative Commons Attribution 3.0 License"), KAboutData::License_GPL_V3, ki18n("(c) 2008, 2009, 2010, 2011, 2012 Benjamin Scott"), ki18n(" "), QByteArray("http://sourceforge.net/projects/kvpm/"), QByteArray("benscott@nwlink.com") ); KHelpMenu *const help_menu = new KHelpMenu(this, about_data); return help_menu->menu(); } KMenu *TopWindow::buildFileMenu() { KMenu *const file_menu = new KMenu( i18n("File") ); KAction *const quit_action = KStandardAction::quit(qApp, SLOT( quit() ), file_menu ); file_menu->addAction(quit_action); return file_menu; } KMenu *TopWindow::buildGroupsMenu() { KMenu *const groups_menu = new KMenu( i18n("Volume Groups") ); m_remove_vg_action = new KAction( KIcon("cross"), i18n("Delete Volume Group..."), this); m_reduce_vg_action = new KAction( KIcon("delete"), i18n("Reduce Volume Group..."), this); m_extend_vg_action = new KAction( KIcon("add"), i18n("Extend Volume Group..."), this); m_rename_vg_action = new KAction( KIcon("edit-rename"), i18n("Rename Volume Group..."), this); m_remove_missing_action = new KAction( KIcon("error_go"), i18n("Remove Missing Physcial Volumes..."), this); m_merge_vg_action = new KAction( KIcon("arrow_join"), i18n("Merge Volume Group..."), this); m_split_vg_action = new KAction( KIcon("arrow_divide"), i18n("Split Volume Group..."), this); m_change_vg_action = new KAction( KIcon("wrench"), i18n("Change Volume Group Attributes..."), this); m_create_vg_action = new KAction( KIcon("document-new"), i18n("Create Volume Group..."), this); m_export_vg_action = new KAction( KIcon("document-export"), i18n("Export Volume Group..."), this); m_import_vg_action = new KAction( KIcon("document-import"), i18n("Import Volume Group..."), this); groups_menu->addAction(m_create_vg_action); groups_menu->addAction(m_remove_vg_action); groups_menu->addAction(m_rename_vg_action); groups_menu->addSeparator(); groups_menu->addAction(m_remove_missing_action); groups_menu->addAction(m_extend_vg_action); groups_menu->addAction(m_reduce_vg_action); groups_menu->addAction(m_split_vg_action); groups_menu->addAction(m_merge_vg_action); groups_menu->addSeparator(); groups_menu->addAction(m_export_vg_action); groups_menu->addAction(m_import_vg_action); groups_menu->addAction(m_change_vg_action); connect(m_change_vg_action, SIGNAL(triggered()), this, SLOT(changeVolumeGroup())); connect(m_create_vg_action, SIGNAL(triggered()), this, SLOT(createVolumeGroup())); connect(m_remove_vg_action, SIGNAL(triggered()), this, SLOT(removeVolumeGroup())); connect(m_rename_vg_action, SIGNAL(triggered()), this, SLOT(renameVolumeGroup())); connect(m_export_vg_action, SIGNAL(triggered()), this, SLOT(exportVolumeGroup())); connect(m_extend_vg_action, SIGNAL(triggered()), this, SLOT(extendVolumeGroup())); connect(m_import_vg_action, SIGNAL(triggered()), this, SLOT(importVolumeGroup())); connect(m_split_vg_action, SIGNAL(triggered()), this, SLOT(splitVolumeGroup())); connect(m_merge_vg_action, SIGNAL(triggered()), this, SLOT(mergeVolumeGroup())); connect(m_remove_missing_action, SIGNAL(triggered()), this, SLOT(removeMissingVolumes())); connect(m_reduce_vg_action, SIGNAL(triggered()), this, SLOT(reduceVolumeGroup())); return groups_menu; } kvpm-0.8.6/kvpm/lvproperties.cpp0000644000175000017500000002446111733530416017157 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "lvproperties.h" #include #include #include #include #include "logvol.h" #include "misc.h" #include "mountentry.h" #include "volgroup.h" /* if segment = -1 we have a multi segement logical volume but we are not focused on any one segment. Therefor stripes and stripe size have no meaning */ LVProperties::LVProperties(LogVol *const volume, const int segment, QWidget *parent) : QWidget(parent), m_lv(volume) { QVBoxLayout *layout = new QVBoxLayout(); layout->setSpacing(2); layout->setMargin(2); KConfigSkeleton skeleton; bool show_mount, show_fsuuid, show_fslabel, show_uuid; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", m_use_si_units, false); skeleton.setCurrentGroup("LogicalVolumeProperties"); skeleton.addItemBool("lp_mount", show_mount, true); skeleton.addItemBool("lp_fsuuid", show_fsuuid, false); skeleton.addItemBool("lp_fslabel", show_fslabel, false); skeleton.addItemBool("lp_uuid", show_uuid, false); layout->addWidget( generalFrame(segment) ); if( !m_lv->isMirrorLeg() && !m_lv->isMirrorLog() && !m_lv->isPvmove() && !m_lv->isVirtual() && !m_lv->isSnapContainer() && ( (m_lv->getSegmentCount() == 1) || (segment == -1) ) ){ if(show_mount) layout->addWidget( mountPointsFrame() ); layout->addWidget( physicalVolumesFrame(segment) ); if(show_fsuuid || show_fslabel) layout->addWidget( fsFrame(show_fsuuid, show_fslabel) ); } else layout->addWidget( physicalVolumesFrame(segment) ); if( show_uuid && !m_lv->isSnapContainer() && ( (m_lv->getSegmentCount() == 1) || (segment == -1) ) ) layout->addWidget( uuidFrame() ); layout->addStretch(); setLayout(layout); } QFrame *LVProperties::mountPointsFrame() { QLabel *label; QFrame *const frame = new QFrame(); QVBoxLayout *const layout = new QVBoxLayout(); frame->setLayout(layout); frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); const QList entries = m_lv->getMountEntries(); if(entries.size() > 1){ label = new QLabel( i18n("Mount points") ); label->setAlignment(Qt::AlignCenter); layout->addWidget(label); } else{ label = new QLabel( i18n("Mount point") ) ; label->setAlignment(Qt::AlignCenter); layout->addWidget(label); } if(entries.size() == 0){ label = new QLabel( i18n("not mounted") ) ; label->setAlignment(Qt::AlignLeft); layout->addWidget(label); } else{ for(int x = 0; x < entries.size(); x++){ const int pos = entries[x]->getMountPosition(); const QString mp = entries[x]->getMountPoint(); if(pos > 0){ label = new QLabel( QString("%1 <%2>").arg(mp).arg(pos) ); label->setToolTip( QString("%1 <%2>").arg(mp).arg(pos) ); layout->addWidget(label); } else{ label = new QLabel(mp); label->setToolTip(mp); layout->addWidget(label); } } } QListIterator entry_itr(entries); while( entry_itr.hasNext() ) delete entry_itr.next(); return frame; } QFrame *LVProperties::uuidFrame() { QStringList uuid; QFrame *frame = new QFrame(); QVBoxLayout *layout = new QVBoxLayout(); frame->setLayout(layout); frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); QLabel *label = new QLabel( i18n("Logical volume UUID") ); label->setAlignment(Qt::AlignCenter); layout->addWidget( label ); uuid = splitUuid( m_lv->getUuid() ); label = new QLabel( uuid[0] ); label->setToolTip( m_lv->getUuid() ); layout->addWidget( label ); label = new QLabel( uuid[1] ); label->setToolTip( m_lv->getUuid() ); layout->addWidget( label ); return frame; } QFrame *LVProperties::fsFrame(const bool showFsUuid, const bool showFsLabel) { QStringList uuid; QFrame *frame = new QFrame(); QVBoxLayout *layout = new QVBoxLayout(); frame->setLayout(layout); frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); QLabel *label; if(showFsLabel){ label = new QLabel( i18n("Filesystem label") ); label->setAlignment(Qt::AlignCenter); layout->addWidget( label ); label = new QLabel( m_lv->getFilesystemLabel() ); label->setToolTip( m_lv->getFilesystemLabel() ); label->setWordWrap(true); layout->addWidget( label ); } if(showFsUuid){ label = new QLabel( i18n("Filesystem UUID") ); label->setAlignment(Qt::AlignCenter); layout->addWidget( label ); uuid = splitUuid( m_lv->getFilesystemUuid() ); label = new QLabel( uuid[0] ); label->setToolTip( m_lv->getFilesystemUuid() ); layout->addWidget( label ); label = new QLabel( uuid[1] ); label->setToolTip( m_lv->getFilesystemUuid() ); layout->addWidget( label ); } return frame; } QFrame *LVProperties::generalFrame(int segment) { const long long extent_size = m_lv->getVg()->getExtentSize(); const int segment_count = m_lv->getSegmentCount(); long long extents, total_size, total_extents; int stripes, stripe_size; QStringList pv_list; QFrame *const frame = new QFrame(); QVBoxLayout *const layout = new QVBoxLayout(); frame->setLayout(layout); frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); if((segment >= 0) && (segment_count > 1)){ extents = m_lv->getSegmentExtents(segment); stripes = m_lv->getSegmentStripes(segment); stripe_size = m_lv->getSegmentStripeSize(segment); layout->addWidget(new QLabel( i18n("Extents: %1", extents) ) ); if( !m_lv->isMirror() ){ QHBoxLayout *const stripe_layout = new QHBoxLayout(); if( stripes != 1 ){ stripe_layout->addWidget(new QLabel( i18n("Stripes: %1", stripes) )); stripe_layout->addWidget(new QLabel( i18n("Stripe size: %1", locale->formatByteSize(stripe_size)) )); } else{ stripe_layout->addWidget(new QLabel( i18n("Stripes: none") )); } layout->addLayout(stripe_layout); } } else if((segment >= 0) && (segment_count == 1)){ extents = m_lv->getSegmentExtents(segment); total_size = m_lv->getTotalSize(); total_extents = total_size / extent_size; stripes = m_lv->getSegmentStripes(segment); stripe_size = m_lv->getSegmentStripeSize(segment); if( !m_lv->isSnapContainer() ) layout->addWidget(new QLabel( i18n("Extents: %1", extents) )); if( !m_lv->isMirror() ){ QHBoxLayout *const stripe_layout = new QHBoxLayout(); if( stripes != 1 ){ stripe_layout->addWidget(new QLabel( i18n("Stripes: %1", stripes) )); stripe_layout->addWidget(new QLabel( i18n("Stripe size: %1", locale->formatByteSize(stripe_size)) )); } else{ stripe_layout->addWidget(new QLabel( i18n("Stripes: none") )); } layout->addLayout(stripe_layout); } else if( !m_lv->isMirrorLog() || ( m_lv->isMirrorLog() && m_lv->isMirror() ) ){ layout->addWidget(new QLabel( i18n("Total extents: %1", total_extents) )); layout->addWidget(new QLabel( i18n("Total size: %1", locale->formatByteSize(total_size)) )); } if( !( m_lv->isMirrorLeg() || m_lv->isMirrorLog() )){ layout->addWidget(new QLabel( i18n("Filesystem: %1", m_lv->getFilesystem() ) )); if(m_lv->isWritable()) layout->addWidget(new QLabel( i18n("Access: r/w") )); else layout->addWidget(new QLabel( i18n("Access: r/o") )); layout->addWidget(new QLabel( i18n("Allocation policy: %1", m_lv->getPolicy() ) )); } } else{ extents = m_lv->getExtents(); layout->addWidget(new QLabel( i18n("Extents: %1", extents) )); if( !( m_lv->isMirrorLeg() || m_lv->isMirrorLog() )){ layout->addWidget(new QLabel( i18n("Filesystem: %1", m_lv->getFilesystem()) )); if(m_lv->isWritable()) layout->addWidget(new QLabel( i18n("Access: r/w") )); else layout->addWidget(new QLabel( i18n("Access: r/o") )); layout->addWidget(new QLabel( i18n("Allocation policy: %1", m_lv->getPolicy()))); } } if(m_lv->isSnap()) layout->addWidget(new QLabel( i18n("Origin: %1", m_lv->getOrigin()) )); return frame; } QFrame *LVProperties::physicalVolumesFrame(int segment) { QStringList pv_list; QFrame *frame = new QFrame(); QVBoxLayout *layout = new QVBoxLayout(); frame->setLayout(layout); frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); QLabel *label = new QLabel( i18n("Physical volumes") ); label->setAlignment(Qt::AlignCenter); layout->addWidget(label); if( m_lv->isMirror() || m_lv->isSnapContainer() ){ pv_list = m_lv->getPvNamesAllFlat(); for(int pv = 0; pv < pv_list.size(); pv++){ label = new QLabel( pv_list[pv] ); label->setToolTip( pv_list[pv] ); layout->addWidget( label ); } } else if(segment > -1){ pv_list = m_lv->getPvNames(segment); for(int pv = 0; pv < pv_list.size(); pv++){ label = new QLabel( pv_list[pv] ); label->setToolTip( pv_list[pv] ); layout->addWidget( label ); } } else{ pv_list = m_lv->getPvNamesAll(); for(int pv = 0; pv < pv_list.size(); pv++){ label = new QLabel( pv_list[pv] ); label->setToolTip( pv_list[pv] ); layout->addWidget( label ); } } return frame; } kvpm-0.8.6/kvpm/dualselectorbox.h0000644000175000017500000000243211733530416017257 0ustar benscottbenscott/* * * * Copyright (C) 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef DUALSELECTORBOX_H #define DUALSELECTORBOX_H #include class SizeSelectorBox; class DualSelectorBox : public QWidget { Q_OBJECT SizeSelectorBox *m_size_selector; SizeSelectorBox *m_offset_selector; long long m_space; signals: void changed(); public: DualSelectorBox(const long long sectorSize, const long long totalSpace, const long long minSize, const long long maxSize, const long long initialSize, const long long minOffset, const long long maxOffset, const long long initialOffset, QWidget *parent = 0); DualSelectorBox(const long long sectorSize, const long long totalSpace, QWidget *parent = 0); long long getCurrentSize(); long long getCurrentOffset(); bool isValid(); public slots: void resetSelectors(); private slots: void offsetChanged(); void sizeChanged(); }; #endif kvpm-0.8.6/kvpm/mkfs.h0000644000175000017500000000505511733530416015024 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef MKFS_H #define MKFS_H #include #include #include #include #include #include #include #include #include #include class VolGroup; class LogVol; class StoragePartition; class MkfsDialog : public KDialog { Q_OBJECT KTabWidget *m_tab_widget; QGroupBox *m_stripe_box, *m_base_options_box, *m_ext4_options_box, *m_misc_options_box; QRadioButton *ext2, *ext3, *ext4, *reiser, *reiser4, *ntfs, *jfs, *xfs, *vfat, *swap, *btrfs; KComboBox *m_block_combo; // blocksize KComboBox *m_inode_combo; // inode size KLineEdit *m_name_edit; // volume name KLineEdit *m_inode_edit; // bytes / inode KLineEdit *m_total_edit; // total inode count KLineEdit *m_stride_edit; // stride size KLineEdit *m_count_edit; // strides per stripe QSpinBox *m_reserved_spin; QCheckBox *m_extent_check; QCheckBox *m_ext_attr_check; QCheckBox *m_resize_inode_check; QCheckBox *m_dir_index_check; QCheckBox *m_filetype_check; QCheckBox *m_sparse_super_check; QCheckBox *m_clobber_fs_check; QCheckBox *m_flex_bg_check; QCheckBox *m_huge_file_check; QCheckBox *m_uninit_bg_check; QCheckBox *m_dir_nlink_check; QCheckBox *m_extra_isize_check; QCheckBox *m_lazy_itable_init_check; QString m_path; bool m_bailout; QWidget *generalTab(); QWidget *advancedTab(const long strideSize, const long strideCount); QWidget *ext4Tab(); QGroupBox *miscOptionsBox(); QGroupBox *baseOptionsBox(); QGroupBox *ext4OptionsBox(); QGroupBox *stripeBox(const long strideSize, const long strideCount); void clobberFilesystem(); bool hasInitialErrors(const bool mounted); void buildDialog(const long strideSize, const long strideCount); private slots: void enableOptions(bool); void commitFilesystem(); public: explicit MkfsDialog(LogVol *const volume, QWidget *parent = 0); explicit MkfsDialog(StoragePartition *const partition, QWidget *parent = 0); bool bailout(); }; #endif kvpm-0.8.6/kvpm/unmount.h0000644000175000017500000000207011733530416015563 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef UNMOUNT_H #define UNMOUNT_H #include #include #include class LogVol; class MountEntry; class NoMungeCheck; class StoragePartition; class UnmountDialog : public KDialog { Q_OBJECT bool m_bailout; bool m_single; QString m_mp; QList m_check_list; // one check box for each mount point public: UnmountDialog(StoragePartition *const partition, QWidget *parent = NULL); UnmountDialog(LogVol *const volume, QWidget *parent = NULL); void buildDialog(QString const device, const QList entries); bool bailout(); private slots: void resetOkButton(); void commitChanges(); }; #endif kvpm-0.8.6/kvpm/partremove.cpp0000644000175000017500000000350611733530416016602 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include #include #include #include #include "partremove.h" #include "storagepartition.h" bool remove_partition(StoragePartition *const partition) { PedPartition *const ped_partition = partition->getPedPartition(); if( partition->getType() != "extended" ){ const QString warning = i18n("Remove partition: %1? Any data on that partition will be lost.", partition->getName() ); if(KMessageBox::warningYesNo(NULL, warning, QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QString(), KMessageBox::Dangerous) == KMessageBox::Yes){ if( ped_disk_delete_partition( ped_partition->disk, ped_partition ) ) ped_disk_commit( ped_partition->disk ); return true; } } else{ const QString question = i18n("Remove partition: %1?", partition->getName() ); if(KMessageBox::questionYesNo(0, question) == KMessageBox::Yes){ if( ped_disk_delete_partition( ped_partition->disk, ped_partition ) ) ped_disk_commit( ped_partition->disk ); return true; } } return false; } kvpm-0.8.6/kvpm/deviceactionsmenu.h0000644000175000017500000000327211733530416017570 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef DEVICEACTIONSMENU_H #define DEVICEACTIONSMENU_H #include #include #include #include #include class StoragePartition; class StorageDevice; class DeviceActionsMenu : public KMenu { Q_OBJECT KMenu *m_vgextend_menu; KAction *m_mkfs_action, *m_fsck_action, *m_maxfs_action, *m_maxpv_action, *m_partremove_action, *m_partadd_action, *m_partchange_action, *m_removefs_action, *m_vgcreate_action, *m_tablecreate_action, *m_vgreduce_action, *m_mount_action, *m_unmount_action; QList vgextend_actions; StoragePartition *m_part; StorageDevice *m_dev; QString m_vg_name; void setup(QTreeWidgetItem *const item); public: explicit DeviceActionsMenu(QTreeWidgetItem *const item, QWidget *parent = 0); private slots: void fsckPartition(); void mkfsPartition(); void maxfsPartition(); void addPartition(); void changePartition(); void removePartition(); void removefsPartition(); void vgcreatePartition(); void tablecreatePartition(); void vgreducePartition(); void vgextendPartition(QAction *action); void mountPartition(); void unmountPartition(); }; #endif kvpm-0.8.6/kvpm/lvreduce.h0000644000175000017500000000162511733530416015674 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef LVREDUCEDIALOG_H #define LVREDUCEDIALOG_H #include #include #include #include class LogVol; class SizeSelectorBox; class VolGroup; class LVReduceDialog : public KDialog { Q_OBJECT LogVol *m_lv; VolGroup *m_vg; SizeSelectorBox *m_size_selector; bool m_bailout; public: explicit LVReduceDialog(LogVol *const volume, QWidget *parent = 0); bool bailout(); private slots: void doShrink(); void resetOkButton(); }; #endif kvpm-0.8.6/kvpm/vgtree.cpp0000644000175000017500000004760111733530416015716 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vgtree.h" #include #include #include #include #include #include "lvactionsmenu.h" #include "logvol.h" #include "volgroup.h" VGTree::VGTree(VolGroup *const group) : QTreeWidget(), m_vg(group) { m_init = true; const QStringList headers = QStringList() << i18n("Volume") << i18n("type") << i18n("Size") << i18n("Remaining") << i18n("Filesystem") << i18n("Stripes") << i18n("Stripe size") << i18n("Snap/Copy") << i18n("State") << i18n("Access") << i18n("Tags") << i18n("Mount points"); QTreeWidgetItem *item = new QTreeWidgetItem((QTreeWidgetItem *)0, headers); for(int column = 0; column < item->columnCount() ; column++) item->setTextAlignment(column, Qt::AlignCenter); item->setToolTip(0, i18n("Logical volume name")); item->setToolTip(1, i18n("Type of logical volume")); item->setToolTip(2, i18n("Total size of the logical volume")); item->setToolTip(3, i18n("Free space on logical volume")); item->setToolTip(4, i18n("Filesystem type on logical volume, if any")); item->setToolTip(5, i18n("Number of stripes if the volume is striped")); item->setToolTip(6, i18n("Size of stripes if the volume is striped")); item->setToolTip(7, i18n("Percentage of pvmove completed, of mirror synced or of snapshot used up")); item->setToolTip(8, i18n("Logical volume state")); item->setToolTip(9, i18n("Read and write or Read Only")); item->setToolTip(10, i18n("Optional tags for physical volume")); item->setToolTip(11, i18n("Filesystem mount points, if mounted")); setHeaderItem(item); sortByColumn(0, Qt::AscendingOrder); } void VGTree::loadData() { QList logical_volumes = m_vg->getLogicalVolumes(); LogVol *lv = NULL; QTreeWidgetItem *new_item; disconnect(this, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(adjustColumnWidth(QTreeWidgetItem *))); disconnect(this, SIGNAL(itemCollapsed(QTreeWidgetItem *)), this, SLOT(adjustColumnWidth(QTreeWidgetItem *))); setSortingEnabled(false); setViewConfig(); for(int x = 0; x < m_vg->getLvCount(); x++){ lv = logical_volumes[x]; new_item = NULL; for(int y = topLevelItemCount() - 1; y >= 0; y--){ if(topLevelItem(y)->data(0, Qt::DisplayRole).toString() == lv->getName()) new_item = loadItem(lv, topLevelItem(y)); } if(new_item == NULL){ new_item = new QTreeWidgetItem((QTreeWidgetItem *)0); addTopLevelItem(new_item); loadItem(lv, new_item); } } for(int y = topLevelItemCount() - 1; y >= 0; y--){ // remove top level lv items of deleted lvs bool match = false; for(int x = 0; x < logical_volumes.size(); x++){ if(topLevelItem(y)->data(0, Qt::DisplayRole).toString() == logical_volumes[x]->getName() ) match = true; } if( !match ){ delete takeTopLevelItem(y); } } connect(this, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(adjustColumnWidth(QTreeWidgetItem *))); connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem *)), this, SLOT(adjustColumnWidth(QTreeWidgetItem *))); setSortingEnabled(true); if( currentItem() == NULL && topLevelItemCount() > 0 ) setCurrentItem( topLevelItem(0) ); if( currentItem() != NULL ){ setCurrentItem( currentItem() ); scrollToItem(currentItem(), QAbstractItemView::EnsureVisible); } for(int x = 0; x < 10; x++) resizeColumnToContents(x); setupContextMenu(); m_init = false; emit currentItemChanged(currentItem(), currentItem()); return; } QTreeWidgetItem *VGTree::loadItem(LogVol *lv, QTreeWidgetItem *item) { const QString old_type = item->data(1, Qt::DisplayRole).toString(); // lv type before reload or "" if new item const QString lv_name = lv->getName(); const bool was_sc = old_type.contains("origin", Qt::CaseInsensitive); const bool is_sc = lv->isSnapContainer(); const int old_child_count = item->childCount(); bool was_expanded = false; KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); QList temp_kids; long long fs_remaining; // remaining space on fs -- if known int fs_percent; // percentage of space remaining /* UserRole definitions: setData(0, Qt::UserRole, lv->getName()); setData(1, Qt::UserRole, segment); // segment number setData(2, Qt::UserRole, lv->getUuid()); setData(3, Qt::UserRole, QString("segment")); // "" if not a segment item setData(4, Qt::UserRole, bool); // true if it is a snap container */ if(is_sc && !was_sc) was_expanded = item->isExpanded(); if(!is_sc && was_sc){ for(int x = 0; x < old_child_count; x++){ if( lv_name == item->child(x)->data(0, Qt::DisplayRole).toString() ) was_expanded = item->child(x)->isExpanded(); } } if(is_sc) item->setData(4, Qt::UserRole, true); else item->setData(4, Qt::UserRole, false); item->setData(0, Qt::DisplayRole, lv_name); if( lv->hasMissingVolume() ){ item->setIcon(0, KIcon("exclamation")); item->setToolTip(0, i18n("one or more physical volumes are missing")); } else if( !lv->isSnapContainer() && lv->isOrigin() ){ item->setIcon(0, KIcon("bullet_star")); item->setToolTip(0, i18n("origin")); } else{ item->setIcon(0, KIcon()); item->setToolTip(0, QString()); } item->setData(1, Qt::DisplayRole, lv->getType()); if(lv->isSnapContainer()){ item->setData(2, Qt::DisplayRole, locale->formatByteSize(lv->getTotalSize())); for(int x = 3; x < 12; x++) item->setData(x, Qt::DisplayRole, QVariant()); item->setIcon(3, KIcon()); item->setToolTip(3, QString()); item->setIcon(8, KIcon()); item->setToolTip(8, QString()); } else{ item->setData(2, Qt::DisplayRole, locale->formatByteSize(lv->getSize())); if( lv->getFilesystemSize() > -1 && lv->getFilesystemUsed() > -1 ){ fs_remaining = lv->getFilesystemSize() - lv->getFilesystemUsed(); fs_percent = qRound( ((double)fs_remaining / (double)lv->getFilesystemSize()) * 100 ); if(m_show_total) item->setData(3, Qt::DisplayRole, locale->formatByteSize(fs_remaining)); else if(m_show_percent) item->setData(3, Qt::DisplayRole, QString("%%1").arg(fs_percent) ); else if(m_show_both) item->setData(3, Qt::DisplayRole, QString(locale->formatByteSize(fs_remaining) + " (%%1)").arg(fs_percent) ); if( fs_percent <= m_fs_warn_percent ){ item->setIcon(3, KIcon("exclamation")); item->setToolTip(3, i18n("This filesystem is running out of space")); } else{ item->setIcon(3, KIcon()); item->setToolTip(3, QString()); } } else{ item->setData(3, Qt::DisplayRole, QString("")); item->setIcon(3, KIcon()); item->setToolTip(3, QString()); } item->setData(4, Qt::DisplayRole, lv->getFilesystem() ); if( (lv->isPvmove() || lv->isMirror()) && !lv->isSnapContainer() ) item->setData(7, Qt::DisplayRole, QString("%%1").arg(lv->getCopyPercent(), 1, 'f', 2)); else if( lv->isSnap() || lv->isMerging() ) item->setData(7, Qt::DisplayRole, QString("%%1").arg(lv->getSnapPercent(), 1, 'f', 2)); else item->setData(7, Qt::DisplayRole, QString("")); item->setData(8, Qt::DisplayRole, lv->getState()); if( !lv->isSnapContainer() && !lv->isMirrorLog() && !lv->isMirrorLeg() && !lv->isVirtual() ){ if( lv->isMounted() ){ item->setIcon( 8, KIcon("emblem-mounted") ); item->setToolTip( 8, i18n("mounted filesystem") ); } else if( lv->getFilesystem() == "swap" ){ if( lv->isOpen() ){ item->setIcon(8, KIcon("task-recurring")); item->setToolTip(8, i18n("Active swap area") ); } else{ item->setIcon(8, KIcon("emblem-unmounted")); item->setToolTip(8, i18n("Inactive swap area")); } } else{ item->setIcon( 8, KIcon("emblem-unmounted") ); item->setToolTip( 8, i18n("unmounted filesystem") ); } } if(lv->isWritable()) item->setData(9, Qt::DisplayRole, QString("r/w")); else item->setData(9, Qt::DisplayRole, QString("r/o")); item->setData(10, Qt::DisplayRole, lv->getTags().join(",")); item->setData(11, Qt::DisplayRole, lv->getMountPoints().join(",")); } item->setData(0, Qt::UserRole, lv_name); item->setData(2, Qt::UserRole, lv->getUuid()); if( lv->getSegmentCount() == 1 ) { item->setData(1, Qt::UserRole, 0); // 0 means segment 0 data present if( lv->isMirror() ){ item->setData(5, Qt::DisplayRole, QString("")); item->setData(6, Qt::DisplayRole, QString("")); } else{ item->setData(5, Qt::DisplayRole, QString("%1").arg(lv->getSegmentStripes(0)) ); item->setData(6, Qt::DisplayRole, locale->formatByteSize(lv->getSegmentStripeSize(0)) ); } if( !is_sc && old_type.contains("origin", Qt::CaseInsensitive) ){ for(int x = 0; x < old_child_count; x++){ if( item->child(x)->data(0, Qt::DisplayRole) == lv->getName() ) item->setExpanded( item->child(x)->isExpanded() ); } } insertChildItems(lv, item); } else { item->setData(1, Qt::UserRole, -1); // -1 means not segment data item->setData(5, Qt::DisplayRole, QString("")); item->setData(6, Qt::DisplayRole, QString("")); insertSegmentItems(lv, item); } const int new_child_count = item->childCount(); if(is_sc){ // expand the item if it is a new snap container or snap count is different if(m_init){ item->setExpanded(true); } else{ if( !was_sc || old_child_count != new_child_count ){ item->setExpanded(true); if( !was_sc ){ for(int x = 0; x < new_child_count; x++){ if( item->child(x)->data(0, Qt::DisplayRole) == lv_name ) item->child(x)->setExpanded(was_expanded); } } } } } else if( was_sc && !is_sc ){ // if it was formerly a snap container, set expanded to what the "real" lv was if( indexOfTopLevelItem(item) >= 0 ) addTopLevelItem( takeTopLevelItem( indexOfTopLevelItem(item) ) ); // next line doesn't work without this! item->setExpanded(was_expanded); } for(int column = 1; column < item->columnCount() ; column++) item->setTextAlignment(column, Qt::AlignRight); return item; } void VGTree::setupContextMenu() { setContextMenuPolicy(Qt::CustomContextMenu); // disconnect the last connect, otherwise the following connect get repeated // and piles up. disconnect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(popupContextMenu(QPoint)) ); if( !m_vg->isExported() ){ connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(popupContextMenu(QPoint)) ); } return; } void VGTree::insertChildItems(LogVol *parentVolume, QTreeWidgetItem *parentItem) { LogVol *child_volume; const QList immediate_children = parentVolume->getChildren(); for(int x = 0; x < immediate_children.size(); x++){ QTreeWidgetItem *child_item = NULL; child_volume = immediate_children[x]; for(int y = parentItem->childCount() - 1; y >= 0; y--){ if(parentItem->child(y)->data(0, Qt::DisplayRole).toString() == child_volume->getName() ) child_item = loadItem(child_volume, parentItem->child(y)); } if(child_item == NULL) child_item = loadItem(child_volume, new QTreeWidgetItem(parentItem)); for(int column = 1; column < child_item->columnCount() ; column++) child_item->setTextAlignment(column, Qt::AlignRight); } // Remove child items for logical volumes that no longer exist for(int y = parentItem->childCount() - 1; y >= 0; y--){ bool match = false; for(int x = 0; x < immediate_children.size(); x++){ child_volume = immediate_children[x]; if(parentItem->child(y)->data(0, Qt::DisplayRole).toString() == child_volume->getName() ) match = true; } if( !match ) delete parentItem->takeChild(y); } return; } void VGTree::popupContextMenu(QPoint point) { KMenu *context_menu = NULL; const QTreeWidgetItem *const item = itemAt(point); if(item){ //item = 0 if there is no item a that point LogVol *const lv = m_vg->getLvByName( QVariant(item->data(0, Qt::UserRole)).toString() ); int segment; // segment = -1 means whole lv if( QVariant(item->data(3, Qt::UserRole) ).toString() == "segment" ) segment = QVariant(item->data(1, Qt::UserRole)).toInt(); else segment = -1; context_menu = new LVActionsMenu(lv, segment, m_vg, this); context_menu->exec(QCursor::pos()); } else{ context_menu = new LVActionsMenu(NULL, 0, m_vg, this); context_menu->exec(QCursor::pos()); } if(context_menu != NULL) context_menu->deleteLater(); } void VGTree::setViewConfig() { KConfigSkeleton skeleton; bool changed = false; bool volume, size, remaining, filesystem, type, stripes, stripesize, snapmove, state, access, tags, mountpoints; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", m_use_si_units, false); skeleton.setCurrentGroup("AllTreeColumns"); skeleton.addItemBool( "show_percent", m_show_percent, false ); skeleton.addItemBool( "show_total", m_show_total, false ); skeleton.addItemBool( "show_both", m_show_both, true ); skeleton.addItemInt( "fs_warn", m_fs_warn_percent, 10 ); skeleton.setCurrentGroup("VolumeTreeColumns"); skeleton.addItemBool( "vt_volume", volume, true ); skeleton.addItemBool( "vt_size", size, true ); skeleton.addItemBool( "vt_remaining", remaining, true ); skeleton.addItemBool( "vt_type", type, true ); skeleton.addItemBool( "vt_filesystem", filesystem, false ); skeleton.addItemBool( "vt_stripes", stripes, false ); skeleton.addItemBool( "vt_stripesize", stripesize, false ); skeleton.addItemBool( "vt_snapmove", snapmove, true ); skeleton.addItemBool( "vt_state", state, true ); skeleton.addItemBool( "vt_access", access, false ); skeleton.addItemBool( "vt_tags", tags, true ); skeleton.addItemBool( "vt_mountpoints", mountpoints, false ); if( !( !volume == isColumnHidden(0) && !size == isColumnHidden(1) && !remaining == isColumnHidden(2) && !filesystem == isColumnHidden(3) && !type == isColumnHidden(4) && !stripes == isColumnHidden(5) && !stripesize == isColumnHidden(6) && !snapmove == isColumnHidden(7) && !state == isColumnHidden(8) && !access == isColumnHidden(9) && !tags == isColumnHidden(10) && !mountpoints == isColumnHidden(11) ) ) changed = true; if(changed){ setColumnHidden( 0, !volume ); setColumnHidden( 1, !type ); setColumnHidden( 2, !size ); setColumnHidden( 3, !remaining ); setColumnHidden( 4, !filesystem ); setColumnHidden( 5, !stripes ); setColumnHidden( 6, !stripesize ); setColumnHidden( 7, !snapmove ); setColumnHidden( 8, !state ); setColumnHidden( 9, !access ); setColumnHidden( 10, !tags ); setColumnHidden( 11, !mountpoints ); for(int column = 0; column < 11; column++){ if( !isColumnHidden(column) ) resizeColumnToContents(column); } } } void VGTree::adjustColumnWidth(QTreeWidgetItem *) { resizeColumnToContents(0); resizeColumnToContents(1); resizeColumnToContents(6); } void VGTree::insertSegmentItems(LogVol *lv, QTreeWidgetItem *item) { const int segment_count = lv->getSegmentCount(); const int child_count = item->childCount(); KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); QTreeWidgetItem *child_item; QList segment_children; for(int x = 0; x < child_count ; x++) // segments can never have children segment_children.append( item->child(x)->takeChildren() ); for(int x = segment_children.size() - 1; x >= 0 ;x--) delete segment_children[x]; // so delete them if( segment_count > child_count ){ for(int x = 0; x < segment_count - child_count; x++) new QTreeWidgetItem(item); } else if( segment_count < child_count ){ for(int x = child_count - 1; x >= segment_count ; x--) delete (item->takeChild(x)); } for(int segment = 0; segment < segment_count; segment++){ child_item = item->child(segment); if( lv->getPvNames(segment).contains("unknown device") ){ child_item->setIcon(0, KIcon("exclamation") ); child_item->setToolTip( 0, i18n("one or more physical volumes are missing") ); } else{ child_item->setIcon(0, KIcon() ); child_item->setToolTip( 0, QString() ); } child_item->setData(0, Qt::DisplayRole, QString("Seg# %1").arg(segment)); child_item->setData(1, Qt::DisplayRole, QString("")); child_item->setData(2, Qt::DisplayRole, locale->formatByteSize(lv->getSegmentSize(segment))); child_item->setData(3, Qt::DisplayRole, QString("")); child_item->setData(4, Qt::DisplayRole, QString("")); child_item->setData(5, Qt::DisplayRole, QString("%1").arg(lv->getSegmentStripes(segment))); child_item->setData(6, Qt::DisplayRole, locale->formatByteSize(lv->getSegmentStripeSize(segment))); child_item->setData(0, Qt::UserRole, lv->getName()); child_item->setData(1, Qt::UserRole, segment); child_item->setData(2, Qt::UserRole, lv->getUuid()); child_item->setData(3, Qt::UserRole, QString("segment")); for(int column = 7; column < 12; column++) child_item->setData(column, Qt::DisplayRole, QString("")); for(int column = 1; column < child_item->columnCount() ; column++) child_item->setTextAlignment(column, Qt::AlignRight); } } kvpm-0.8.6/kvpm/fsprobe.cpp0000644000175000017500000000335411733530416016057 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "fsprobe.h" #include #include #define BLKID_EMPTY_CACHE "/dev/null" QString fsprobe_getfstype2(const QString devicePath) { static blkid_cache blkid2; const QByteArray path = devicePath.toLocal8Bit(); blkid2 = NULL; if( blkid_get_cache(&blkid2, BLKID_EMPTY_CACHE) < 0){ return QString(); } else{ char *tag = blkid_get_tag_value(blkid2, "TYPE", path.data()); QString type(tag); free (tag); blkid_put_cache(blkid2); return type; } } QString fsprobe_getfsuuid(const QString devicePath) { static blkid_cache blkid2; const QByteArray path = devicePath.toLocal8Bit(); blkid2 = NULL; if( blkid_get_cache(&blkid2, BLKID_EMPTY_CACHE) < 0){ return QString(); } else{ char *tag = blkid_get_tag_value(blkid2, "UUID", path.data()); QString uuid(tag); free (tag); blkid_put_cache(blkid2); return uuid; } } QString fsprobe_getfslabel(const QString devicePath) { static blkid_cache blkid2; const QByteArray path = devicePath.toLocal8Bit(); blkid2 = NULL; if( blkid_get_cache(&blkid2, BLKID_EMPTY_CACHE) < 0){ return QString(); } else{ char *tag = blkid_get_tag_value(blkid2, "LABEL", path.data()); QString label(tag); free (tag); blkid_put_cache(blkid2); return label; } } kvpm-0.8.6/kvpm/topwindow.h0000644000175000017500000000457011733530416016117 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef TOPWINDOW_H #define TOPWINDOW_H #include #include #include #include #include #include class DeviceTab; class ExecutableFinder; class MainTabWidget; class MasterList; class ProgressBox; class StorageDevice; class TopWindow; class VolGroup; class VolumeGroupTab; extern TopWindow *MainWindow; class TopWindow : public KMainWindow { Q_OBJECT MainTabWidget *m_tab_widget; // The current tab widget we are using KAction *m_remove_vg_action, *m_rename_vg_action, *m_reduce_vg_action, *m_create_vg_action, *m_extend_vg_action, *m_change_vg_action, *m_remove_missing_action, *m_merge_vg_action, *m_export_vg_action, *m_import_vg_action, *m_split_vg_action; DeviceTab *m_device_tab; VolGroup *m_vg; MasterList *m_master_list; ExecutableFinder *m_executable_finder; static ProgressBox *m_progress_box; void closeEvent(QCloseEvent *); KMenu *buildFileMenu(); KMenu *buildGroupsMenu(); KMenu *buildHelpMenu(); KMenu *buildSettingsMenu(); KMenu *buildToolsMenu(); public: TopWindow(MasterList *const masterList, ExecutableFinder *const executableFinder, QWidget *parent = NULL); static ProgressBox *getProgressBox(); public slots: void reRun(); void updateTabs(); private slots: void cleanUp(); void setupMenus(); void showVolumeGroupInfo(bool show); void showVolumeGroupBar(bool show); void useSiUnits(bool use); void changeVolumeGroup(); void createVolumeGroup(); void removeVolumeGroup(); void renameVolumeGroup(); void removeMissingVolumes(); void reduceVolumeGroup(); void exportVolumeGroup(); void importVolumeGroup(); void splitVolumeGroup(); void mergeVolumeGroup(); void extendVolumeGroup(); void restartPhysicalVolumeMove(); void stopPhysicalVolumeMove(); void configKvpm(); }; #endif kvpm-0.8.6/kvpm/removemirror.h0000644000175000017500000000166311733530416016615 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef LVREMOVEMIRROR_H #define LVREMOVEMIRROR_H #include #include #include #include class LogVol; class NoMungeCheck; class VolGroup; bool remove_mirror(LogVol *logicalVolume); class RemoveMirrorDialog : public KDialog { Q_OBJECT LogVol *m_lv; VolGroup *m_vg; QList m_mirror_leg_checks; public: explicit RemoveMirrorDialog(LogVol *logicalVolume, QWidget *parent = 0); QStringList arguments(); private slots: void validateCheckStates(int); }; #endif kvpm-0.8.6/kvpm/vgextend.h0000644000175000017500000000230711733530416015705 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGEXTEND_H #define VGEXTEND_H #include #include #include #include class PvGroupBox; class StorageDevice; class StoragePartition; class VolGroup; class VGExtendDialog : public KDialog { Q_OBJECT bool m_bailout; PvGroupBox *m_pv_checkbox; VolGroup *m_vg; public: VGExtendDialog(VolGroup *const group, QWidget *parent = NULL); VGExtendDialog(VolGroup *const group, StorageDevice *const device, StoragePartition *const partition, QWidget *parent = NULL); void buildDialog(QList devices, QList partitions); void getUsablePvs(QList &devices, QList &partitions); bool bailout(); private slots: void commitChanges(); void validateOK(); }; #endif kvpm-0.8.6/kvpm/fsdata.cpp0000644000175000017500000000232411733530416015655 0ustar benscottbenscott/* * * * Copyright (C) 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "fsdata.h" #include #include #include #include #include "misc.h" // Gets basic stats on mounted filesystems like amount used up. FSData get_fs_data(const QString path){ struct statvfs buff; const QByteArray path_qba = path.toLocal8Bit(); const int error = statvfs(path_qba.data(), &buff); FSData fs_data; if(error){ fs_data.block_size = -1; fs_data.size = -1; fs_data.used = -1; } else{ const long long block_size = buff.f_bsize; const long long frag_size = buff.f_frsize; const long long total_blocks = (frag_size * buff.f_blocks) / block_size; fs_data.block_size = block_size; fs_data.size = total_blocks; fs_data.used = total_blocks - buff.f_bavail; } return fs_data; } kvpm-0.8.6/kvpm/vgexport.cpp0000644000175000017500000000161411733530416016272 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vgexport.h" #include #include #include #include "logvol.h" #include "processprogress.h" #include "volgroup.h" bool export_vg(VolGroup *const volumeGroup) { QStringList args; const QString message = i18n("Export volume group: %1?", volumeGroup->getName() ); if(KMessageBox::questionYesNo(0, message) == KMessageBox::Yes){ args << "vgexport" << volumeGroup->getName(); ProcessProgress remove(args); return true; } else return false; } kvpm-0.8.6/kvpm/lvsizechart.h0000644000175000017500000000220311733530416016412 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef LVSIZECHART_H #define LVSIZECHART_H #include #include #include #include class VolGroup; class LogVol; class LVSizeChart : public QFrame { Q_OBJECT VolGroup *m_vg; LogVol *m_lv; QTreeWidget *m_vg_tree; QHBoxLayout *m_layout; QList m_widgets; // These are segments of the bar chart QList m_ratios; // These are the relative size of each segment // to the whole chart. The total should be about 1. public: LVSizeChart(VolGroup *const group, QTreeWidget *const vgTree, QWidget *parent = 0); void populateChart(); void resizeEvent(QResizeEvent *event); private slots: void vgtreeClicked(); }; #endif kvpm-0.8.6/kvpm/pvproperties.h0000644000175000017500000000140311733530416016617 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PVPROPERTIES_H #define PVPROPERTIES_H #include class LVSegmentExtent; class PhysVol; class PVProperties : public QWidget { PhysVol *m_pv; QList sortByExtent(); QFrame *buildMdaBox(); QFrame *buildLvBox(); QFrame *buildUuidBox(); public: explicit PVProperties(PhysVol *const volume, QWidget *parent = 0); }; #endif kvpm-0.8.6/kvpm/tablecreate.cpp0000644000175000017500000000754111733530416016674 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "tablecreate.h" #include #include #include #include // Creates or deletes a partition table "disk label" on a device. bool create_table(const QString devicePath) { const QString warning_message = i18n("Writing a new partition table to this device, " "or removing the old one, will cause " "any existing data on it to be permanently lost"); if(KMessageBox::warningContinueCancel(NULL, warning_message, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) != KMessageBox::Continue){ return false; } else{ TableCreateDialog dialog(devicePath); dialog.exec(); if(dialog.result() == QDialog::Accepted) return true; else return false; } } TableCreateDialog::TableCreateDialog(const QString devicePath, QWidget *parent) : KDialog(parent), m_device_path(devicePath) { setWindowTitle( i18n("Create Partition Table") ); QWidget *const dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *const layout = new QVBoxLayout(); dialog_body->setLayout(layout); layout->addWidget( new QLabel( i18n("Create partition table on:") ) ); QLabel *const device_label = new QLabel("" + m_device_path + ""); device_label->setAlignment(Qt::AlignHCenter); layout->addWidget(device_label); QGroupBox *const radio_box = new QGroupBox("Table Types"); QVBoxLayout *const radio_box_layout = new QVBoxLayout(); radio_box->setLayout(radio_box_layout); layout->addWidget(radio_box); m_msdos_button = new QRadioButton("MS-DOS"); m_gpt_button = new QRadioButton("GPT"); m_destroy_button = new QRadioButton("Remove table"); m_msdos_button->setChecked(true); radio_box_layout->addWidget(m_msdos_button); radio_box_layout->addWidget(m_gpt_button); radio_box_layout->addWidget(m_destroy_button); connect(this, SIGNAL(okClicked()), this, SLOT(commitTable())); } void TableCreateDialog::commitTable() { QByteArray path = m_device_path.toLocal8Bit(); PedDevice *const ped_device = ped_device_get( path.data() ); PedDiskType *ped_disk_type = NULL; PedDisk *ped_disk = NULL; char *buff = NULL; if( m_msdos_button->isChecked() ){ ped_disk_type = ped_disk_type_get("msdos"); ped_disk = ped_disk_new_fresh (ped_device, ped_disk_type); ped_disk_commit(ped_disk); } else if( m_gpt_button->isChecked() ){ ped_disk_type = ped_disk_type_get("gpt"); ped_disk = ped_disk_new_fresh (ped_device, ped_disk_type); ped_disk_commit(ped_disk); } else{ ped_disk_clobber(ped_device); // This isn't enough for lvm ped_device_open(ped_device); buff = static_cast( malloc( 2 * ped_device->sector_size ) ); for( int x = 0; x < 2 * ped_device->sector_size; x++) buff[x] = 0; if( ! ped_device_write(ped_device, buff, 0, 2) ) // clobber first 2 sectors KMessageBox::error( 0, "Destroying table failed: could not write to device"); ped_device_close(ped_device); } } kvpm-0.8.6/kvpm/devicetree.cpp0000644000175000017500000004242711733530416016542 0ustar benscottbenscott/* * * * Copyright (C) 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "devicetree.h" #include #include #include #include #include #include "deviceactionsmenu.h" #include "devicepropertiesstack.h" #include "devicesizechart.h" #include "physvol.h" #include "storagedevice.h" #include "storagepartition.h" #include "volgroup.h" DeviceTree::DeviceTree(DeviceSizeChart *const chart, DevicePropertiesStack *const stack, QWidget *parent) : QTreeWidget(parent), m_chart(chart), m_stack(stack) { const QStringList headers = QStringList() << "Device" << "Type" << "Capacity" << "Remaining" << "Usage" << "Group" << "Flags" << "Mount point" ; QTreeWidgetItem *item = new QTreeWidgetItem((QTreeWidgetItem *)0, headers); for(int column = 0; column < item->columnCount() ; column++) item->setTextAlignment(column, Qt::AlignCenter); item->setToolTip(0, i18n("The device name")); item->setToolTip(1, i18n("The type of partition")); item->setToolTip(2, i18n("The amount of storage space")); item->setToolTip(3, i18n("The remaining storage space")); item->setToolTip(4, i18n("How the device is being used")); item->setToolTip(5, i18n("The Name of the volume group if the device is a physical volume")); item->setToolTip(6, i18n("Any flags associated with device")); item->setToolTip(7, i18n("Mount points of the filesystem if it is mounted")); m_initial_run = true; setHeaderItem(item); setAlternatingRowColors(true); setAllColumnsShowFocus(true); setExpandsOnDoubleClick(true); setSelectionBehavior(QAbstractItemView::SelectRows); setupContextMenu(); } void DeviceTree::loadData(QList devices) { QTreeWidgetItem *parent, *child, *extended = 0; StoragePartition *part; StorageDevice *dev; PhysVol *pv; QStringList data, expanded_items; QString dev_name, part_name, type, current_device; QVariant part_variant, dev_variant; /* item->data(x, Qt::UserRole) x = 0: pointer to storagepartition if partition, else "" x = 1: pointer to storagedevice */ setSortingEnabled(false); disconnect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem *)), m_chart, SLOT(setNewDevice(QTreeWidgetItem*))); disconnect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), m_stack, SLOT(changeDeviceStackIndex(QTreeWidgetItem*))); setViewConfig(); KLocale *const locale = KGlobal::locale(); if(m_use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); if( currentItem() ) current_device = currentItem()->data(0, Qt::DisplayRole).toString(); for(int x = topLevelItemCount() - 1; x >= 0; x--){ parent = topLevelItem(x); if( parent->isExpanded() ) expanded_items << parent->data(0, Qt::DisplayRole).toString(); for(int y = parent->childCount() - 1; y >= 0; y--){ if( parent->child(y)->isExpanded() ) expanded_items << parent->child(y)->data(0, Qt::DisplayRole).toString(); } delete takeTopLevelItem(x); } for(int x = 0; x < devices.size(); x++){ data.clear(); dev = devices[x]; dev_variant.setValue( (void *) dev); dev_name = dev->getName(); if(dev->isPhysicalVolume()){ pv = dev->getPhysicalVolume(); data << dev_name << "" << locale->formatByteSize(dev->getSize()); if(m_show_total && !m_show_percent) data << locale->formatByteSize( pv->getRemaining() ); else if(!m_show_total && m_show_percent) data << QString("%%1").arg( 100 - pv->getPercentUsed() ); else if(m_show_both) data << QString("%1 (%%2) ").arg( locale->formatByteSize( pv->getRemaining() )).arg( 100 - pv->getPercentUsed() ); data << "PV" << pv->getVg()->getName(); } else{ data << dev_name << "" << locale->formatByteSize(dev->getSize()); } parent = new QTreeWidgetItem(data); parent->setData(1, Qt::UserRole, dev_variant); parent->setTextAlignment(2, Qt::AlignRight); parent->setTextAlignment(3, Qt::AlignRight); if( dev->isPhysicalVolume() ){ if( m_pv_warn_percent && ( m_pv_warn_percent >= (100 - dev->getPhysicalVolume()->getPercentUsed() ) ) ){ parent->setIcon(3, KIcon("exclamation")); parent->setToolTip(3, i18n("Physical volume that is running out of space")); } if( dev->getPhysicalVolume()->isActive() ){ parent->setIcon(4, KIcon("lightbulb")); parent->setToolTip(4, i18n("Physical volume with active logical volumes")); } else{ parent->setIcon(4, KIcon("lightbulb_off")); parent->setToolTip(4, i18n("Physical volume without active logical volumes")); } } addTopLevelItem(parent); for(int n = expanded_items.size() - 1; n >= 0; n--){ // if the old item for this dev was expanded, expand new one if(expanded_items[n] == dev_name){ parent->setExpanded(true); expanded_items.removeAt(n); break; } } for(int y = 0; y < dev->getPartitionCount(); y++){ data.clear(); part = dev->getStoragePartitions()[y]; part_variant.setValue( (void *) part); type = part->getType(); data << part->getName() << type << locale->formatByteSize(part->getSize()); if(part->isPhysicalVolume()){ pv = part->getPhysicalVolume(); if(m_show_total && !m_show_percent) data << locale->formatByteSize( pv->getRemaining() ); else if(!m_show_total && m_show_percent) data << QString("%%1").arg( 100 - pv->getPercentUsed() ); else data << QString("%1 (%%2) ").arg( locale->formatByteSize( pv->getRemaining() )).arg( 100 - pv->getPercentUsed() ); data << "PV" << pv->getVg()->getName() << (part->getFlags()).join(", ") << ""; } else{ if(part->getFilesystemSize() > -1 && part->getFilesystemUsed() > -1){ if(m_show_total && !m_show_percent) data << locale->formatByteSize( part->getFilesystemRemaining() ); else if(!m_show_total && m_show_percent) data << QString("%%1").arg( 100 - part->getFilesystemPercentUsed() ); else data << QString("%1 (%%2) ").arg( locale->formatByteSize( part->getFilesystemRemaining() )).arg( 100 - part->getFilesystemPercentUsed() ); } else data << ""; data << part->getFilesystem() << "" << (part->getFlags()).join(", "); if(part->isMounted()) data << (part->getMountPoints())[0]; else if( part->isBusy() && ( part->getFilesystem() == "swap" ) ) data << "swapping"; else data << ""; } if(type == "extended"){ extended = new QTreeWidgetItem(data); extended->setData(0, Qt::UserRole, part_variant); extended->setData(1, Qt::UserRole, dev_variant); extended->setTextAlignment(2, Qt::AlignRight); extended->setTextAlignment(3, Qt::AlignRight); parent->addChild(extended); part_name = part->getName(); for(int n = expanded_items.size() - 1; n >= 0; n--){ // if the old item for this part was expanded, expand new one if(expanded_items[n] == part_name){ extended->setExpanded(true); expanded_items.removeAt(n); break; } } } else if( !( (type == "logical") || (type == "freespace (logical)") ) ){ child = new QTreeWidgetItem(data); child->setData(0, Qt::UserRole, part_variant); child->setData(1, Qt::UserRole, dev_variant); child->setTextAlignment(2, Qt::AlignRight); child->setTextAlignment(3, Qt::AlignRight); if( part->isPhysicalVolume() ){ if( m_pv_warn_percent && ( m_pv_warn_percent >= (100 - part->getPhysicalVolume()->getPercentUsed() ) ) ){ child->setIcon(3, KIcon("exclamation")); child->setToolTip(3, i18n("Physical volume that is running out of space")); } if( part->getPhysicalVolume()->isActive() ){ child->setIcon(4, KIcon("lightbulb")); child->setToolTip(4, i18n("Physical volume with active logical volumes")); } else{ child->setIcon(4, KIcon("lightbulb_off")); child->setToolTip(4, i18n("Physical volume without active logical volumes")); } } else if( part->isMountable() ){ if( part->isMounted() ){ if( m_fs_warn_percent && ( m_fs_warn_percent >= (100 - part->getFilesystemPercentUsed() ) ) ){ child->setIcon(3, KIcon("exclamation")); child->setToolTip(3, i18n("Filesystem that is running out of space")); } child->setIcon(4, KIcon("emblem-mounted")); child->setToolTip(4, i18n("mounted filesystem") ); } else{ child->setIcon(4, KIcon("emblem-unmounted")); child->setToolTip(4, i18n("unmounted filesystem")); } } else if( part->getFilesystem() == "swap" ){ if( part->isBusy() ){ child->setIcon(4, KIcon("task-recurring")); child->setToolTip(4, i18n("Active swap area") ); } else{ child->setIcon(4, KIcon("emblem-unmounted")); child->setToolTip(4, i18n("Inactive swap area")); } } parent->addChild(child); } else if(extended){ child = new QTreeWidgetItem(data); child->setData(0, Qt::UserRole, part_variant); child->setData(1, Qt::UserRole, dev_variant); child->setTextAlignment(2, Qt::AlignRight); child->setTextAlignment(3, Qt::AlignRight); if( part->isPhysicalVolume() ){ if( m_pv_warn_percent && ( m_pv_warn_percent >= (100 - part->getPhysicalVolume()->getPercentUsed() ) ) ){ child->setIcon(3, KIcon("exclamation")); child->setToolTip(3, i18n("Physical volume that is running out of space")); } if( part->getPhysicalVolume()->isActive() ){ child->setIcon(4, KIcon("lightbulb")); child->setToolTip(4, i18n("Physical volume with active logical volumes")); } else{ child->setIcon(4, KIcon("lightbulb_off")); child->setToolTip(4, i18n("Physical volume without active logical volumes")); } } else if( part->isMountable() ){ if( part->isMounted() ){ if( m_fs_warn_percent && ( m_fs_warn_percent >= (100 - part->getFilesystemPercentUsed() ) ) ){ child->setIcon(3, KIcon("exclamation")); child->setToolTip(3, i18n("Filesystem that is running out of space")); } child->setIcon(4, KIcon("emblem-mounted")); child->setToolTip(4, i18n("mounted filesystem") ); } else{ child->setIcon(4, KIcon("emblem-unmounted")); child->setToolTip(4, i18n("unmounted filesystem")); } } else if( part->getFilesystem() == "swap" ){ if( part->isBusy() ){ child->setIcon(4, KIcon("task-recurring")); child->setToolTip(4, i18n("Active swap area") ); } else{ child->setIcon(4, KIcon("emblem-unmounted")); child->setToolTip(4, i18n("Inactive swap area")); } } extended->addChild(child); } } } bool match = false; connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem *)), m_chart, SLOT(setNewDevice(QTreeWidgetItem*))); connect(this, SIGNAL( currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*) ), m_stack, SLOT( changeDeviceStackIndex(QTreeWidgetItem*) )); if(m_initial_run){ m_initial_run = false; if(m_expand_parts) expandAll(); } else{ for(int x = topLevelItemCount() - 1; x >= 0; x--){ parent = topLevelItem(x); if( parent->data(0, Qt::DisplayRole).toString() == current_device ){ match = true; setCurrentItem(parent); break; } for(int y = parent->childCount() - 1; y >= 0; y--){ child = parent->child(y); if( child->data(0, Qt::DisplayRole).toString() == current_device ){ match = true; setCurrentItem(child); break; } for(int z = child->childCount() - 1; z >= 0; z--){ if( child->child(z)->data(0, Qt::DisplayRole).toString() == current_device ){ match = true; setCurrentItem(child->child(z)); break; } } if(match) break; } if(match) break; } } resizeColumnToContents(0); resizeColumnToContents(3); resizeColumnToContents(5); if( topLevelItemCount() && ( currentItem() == NULL ) ) setCurrentItem( topLevelItem(0) ); return; } void DeviceTree::setupContextMenu() { setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(popupContextMenu(QPoint)) ); return; } void DeviceTree::popupContextMenu(QPoint point) { QTreeWidgetItem *const item = itemAt(point); KMenu *context_menu; if(item){ context_menu = new DeviceActionsMenu(item, this); context_menu->exec(QCursor::pos()); } } void DeviceTree::setViewConfig() { KConfigSkeleton skeleton; bool device, partition, capacity, remaining, usage, group, flags, mount; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", m_use_si_units, false); skeleton.setCurrentGroup("DeviceTreeColumns"); skeleton.addItemBool( "dt_device", device, true ); skeleton.addItemBool( "dt_partition", partition, true ); skeleton.addItemBool( "dt_capacity", capacity, true ); skeleton.addItemBool( "dt_remaining", remaining, true ); skeleton.addItemBool( "dt_usage", usage, true ); skeleton.addItemBool( "dt_group", group, true ); skeleton.addItemBool( "dt_flags", flags, true ); skeleton.addItemBool( "dt_mount", mount, true ); skeleton.addItemBool( "dt_expandparts", m_expand_parts, true ); skeleton.setCurrentGroup("AllTreeColumns"); skeleton.addItemBool( "show_total", m_show_total, false ); skeleton.addItemBool( "show_percent", m_show_percent, false ); skeleton.addItemBool( "show_both", m_show_both, true ); skeleton.addItemInt( "fs_warn", m_fs_warn_percent, 10 ); skeleton.addItemInt( "pv_warn", m_pv_warn_percent, 0 ); setColumnHidden( 0, !device ); setColumnHidden( 1, !partition ); setColumnHidden( 2, !capacity ); setColumnHidden( 3, !remaining ); setColumnHidden( 4, !usage ); setColumnHidden( 5, !group ); setColumnHidden( 6, !flags ); setColumnHidden( 7, !mount ); } kvpm-0.8.6/kvpm/snapmerge.cpp0000644000175000017500000000240711733530416016376 0ustar benscottbenscott/* * * * Copyright (C) 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "snapmerge.h" #include #include #include #include "logvol.h" #include "processprogress.h" bool merge_snap(LogVol *const snapshot) { QStringList args; const QString warning = i18n("Merge snapshot: %1 with origin: %2?", snapshot->getName(), snapshot->getOrigin()); if(KMessageBox::warningYesNo(NULL, warning, QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QString(), KMessageBox::Dangerous) == KMessageBox::Yes){ args << "lvconvert" << "--merge" << "--background" << snapshot->getFullName(); ProcessProgress merge(args); return true; } else{ return false; } } kvpm-0.8.6/kvpm/masterlist.h0000644000175000017500000000227411733530416016253 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef MASTERLIST_H #define MASTERLIST_H #include #include #include #include #include class VolGroup; class LogVol; class MountTables; class PhysVol; class StorageDevice; class MasterList : public QObject { Q_OBJECT MountTables *m_mount_tables; static QList m_volume_groups; static QList m_storage_devices; static lvm_t m_lvm; void scanVolumeGroups(); void scanStorageDevices(); public: MasterList(); ~MasterList(); void rescan(); static lvm_t getLvm(); static QList getVolGroups(); static QList getStorageDevices(); static int getVgCount(); static VolGroup *getVgByName(QString name); static QStringList getVgNames(); }; #endif kvpm-0.8.6/kvpm/removemissing.h0000644000175000017500000000152111733530416016745 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef REMOVEMISSING_H #define REMOVEMISSING_H #include #include #include class VolGroup; bool remove_missing_pv(VolGroup *volumeGroup); class RemoveMissingDialog : public KDialog { Q_OBJECT VolGroup *m_vg; QRadioButton *m_empty_button, *m_all_button; public: explicit RemoveMissingDialog(VolGroup *volumeGroup, QWidget *parent = 0); QStringList arguments(); }; #endif kvpm-0.8.6/kvpm/physvol.h0000644000175000017500000000404011733530416015561 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PHYSVOL_H #define PHYSVOL_H #include #include #include #include class VolGroup; class PhysVol { QString m_device; // eg: /dev/hde4 QString m_format; // e.g. lvm1 or lvm2 QString m_uuid; QStringList m_tags; VolGroup *m_vg; // all pvs now must be in a vg bool m_active; bool m_allocatable; uint64_t m_mda_count; // number of metadata areas uint64_t m_mda_used; // number of metadata areas in use uint64_t m_mda_size; long long m_size; // size in bytes of physical volume long long m_device_size; // size in bytes of underlying device long long m_unused; // free space in bytes long long m_last_used_extent; public: PhysVol(pv_t lvm_pv, VolGroup *vg); void rescan(pv_t pv); QString getName(); // eg: /dev/hde4 QString getUuid(); QStringList getTags(); VolGroup* getVg(); bool isAllocatable(); void setActive(); // If any lv is active on the pv, the pv is active bool isActive(); long long getSize(); // size of the physical volume in bytes long long getDeviceSize(); // the physical volume might not take up all the device! long long getRemaining(); // free space in bytes long long getLastUsedExtent(); // needed for minimum shrink size determination void setLastUsedExtent(const long long last); int getPercentUsed(); // 0 - 100 long getMdaCount(); long getMdaUsed(); // Meta Data areas in use long long getMdaSize(); // Meta Data Area size in bytes }; #endif kvpm-0.8.6/kvpm/deviceproperties.h0000644000175000017500000000215011733530416017431 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef DEVICEPROPERTIES_H #define DEVICEPROPERTIES_H #include class StorageDevice; class StoragePartition; class PhysVol; class DeviceProperties : public QWidget { QFrame *hardwareFrame(StorageDevice *const device); QFrame *pvFrame(PhysVol *const pv); QFrame *mpFrame(StoragePartition *const partition); QFrame *fsFrame(StoragePartition *const partition, const bool showFsUuid, const bool showFsLabel); QFrame *generalFrame(StorageDevice *const device); QFrame *generalFrame(StoragePartition *const partition); public: explicit DeviceProperties(StorageDevice *const device, QWidget *parent = 0); explicit DeviceProperties(StoragePartition *const partition, QWidget *parent = 0); }; #endif kvpm-0.8.6/kvpm/devicesizechartseg.cpp0000644000175000017500000001264111733530416020271 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "devicesizechartseg.h" #include #include #include #include "deviceactionsmenu.h" #include "storagepartition.h" DeviceChartSeg::DeviceChartSeg(QTreeWidgetItem *storageItem, QWidget *parent) : QFrame(parent), m_item(storageItem) { QStringList group_names; QString use; QPalette colorset; KConfigSkeleton skeleton; QColor ext2_color, ext3_color, ext4_color, reiser_color, reiser4_color, msdos_color, jfs_color, xfs_color, none_color, free_color, swap_color, hfs_color, physical_color, ntfs_color , btrfs_color; skeleton.setCurrentGroup("FilesystemColors"); skeleton.addItemColor("ext2", ext2_color, Qt::blue); skeleton.addItemColor("ext3", ext3_color, Qt::darkBlue); skeleton.addItemColor("ext4", ext4_color, Qt::cyan); skeleton.addItemColor("btrfs", btrfs_color, Qt::yellow); skeleton.addItemColor("reiser", reiser_color, Qt::red); skeleton.addItemColor("reiser4", reiser4_color, Qt::darkRed); skeleton.addItemColor("msdos", msdos_color, Qt::darkYellow); skeleton.addItemColor("jfs", jfs_color, Qt::magenta); skeleton.addItemColor("xfs", xfs_color, Qt::darkCyan); skeleton.addItemColor("hfs", hfs_color, Qt::darkMagenta); skeleton.addItemColor("ntfs", ntfs_color, Qt::darkGray); skeleton.addItemColor("none", none_color, Qt::black); skeleton.addItemColor("free", free_color, Qt::green); skeleton.addItemColor("swap", swap_color, Qt::lightGray); skeleton.addItemColor("physvol", physical_color, Qt::darkGreen); use = (m_item->data(4, Qt::DisplayRole)).toString(); m_partition = NULL; if( (m_item->data(0, Qt::UserRole)).canConvert() ){ m_partition = (StoragePartition *) (( m_item->data(0, Qt::UserRole)).value() ); if ( m_partition->getPedType() & 0x02 ){ // extended setFrameStyle(QFrame::Sunken | QFrame::Panel); setLineWidth(2); colorset.setColor(QPalette::Window, Qt::green); } else if( m_partition->getPedType() & 0x04 ){ // freespace setFrameStyle(QFrame::Sunken | QFrame::Panel); setLineWidth(2); colorset.setColor(QPalette::Window, Qt::green); } else{ setFrameStyle(QFrame::Sunken | QFrame::Panel); setLineWidth(2); if(use == "ext2") colorset.setColor(QPalette::Window, ext2_color); else if(use == "ext3") colorset.setColor(QPalette::Window, ext3_color); else if(use == "ext4") colorset.setColor(QPalette::Window, ext4_color); else if(use == "reiserfs") colorset.setColor(QPalette::Window, reiser_color); else if(use == "reiser4") colorset.setColor(QPalette::Window, reiser4_color); else if(use == "hfs") colorset.setColor(QPalette::Window, hfs_color); else if(use == "ntfs") colorset.setColor(QPalette::Window, ntfs_color); else if(use == "vfat") colorset.setColor(QPalette::Window, msdos_color); else if(use == "jfs") colorset.setColor(QPalette::Window, jfs_color); else if(use == "xfs") colorset.setColor(QPalette::Window, xfs_color); else if(use == "btrfs") colorset.setColor(QPalette::Window, btrfs_color); else if(use == "swap") colorset.setColor(QPalette::Window, swap_color); else if(use == "freespace") colorset.setColor(QPalette::Window, free_color); else if(use == "PV") colorset.setColor(QPalette::Window, physical_color); else colorset.setColor(QPalette::Window, none_color); } } else{ // whole device, not a partition setFrameStyle( QFrame::Sunken | QFrame::Panel ); setLineWidth( 2 ); if(use == "PV") colorset.setColor(QPalette::Window, physical_color); else colorset.setColor(QPalette::Window, none_color); } setToolTip( i18n("Device: %1", m_item->data(0, Qt::DisplayRole).toString()) ); setPalette(colorset); setAutoFillBackground(true); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(popupContextMenu(QPoint)) ); } void DeviceChartSeg::popupContextMenu(QPoint point) { (void)point; KMenu *context_menu; if( (m_item->data(0, Qt::UserRole)).canConvert() ) m_partition = (StoragePartition *) (( m_item->data(0, Qt::UserRole)).value() ); if(m_item){ // m_item = 0 if there is no item a that point context_menu = new DeviceActionsMenu(m_item, this); context_menu->exec(QCursor::pos()); } else{ context_menu = new DeviceActionsMenu(NULL, this); context_menu->exec(QCursor::pos()); } } kvpm-0.8.6/kvpm/pvproperties.cpp0000644000175000017500000002045211733530416017157 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "pvproperties.h" #include #include #include #include #include #include "masterlist.h" #include "physvol.h" #include "logvol.h" #include "volgroup.h" struct LVSegmentExtent { QString lv_name; long long first_extent; long long last_extent; }; bool isLessThan( const LVSegmentExtent * lv1 , const LVSegmentExtent * lv2 ) { return lv1->first_extent < lv2->first_extent; } PVProperties::PVProperties(PhysVol *const volume, QWidget *parent) : QWidget(parent), m_pv(volume) { setBackgroundRole(QPalette::Base); setAutoFillBackground(true); KConfigSkeleton skeleton; bool mda, uuid; skeleton.setCurrentGroup("PhysicalVolumeProperties"); skeleton.addItemBool("pp_mda", mda, true); skeleton.addItemBool("pp_uuid", uuid, false); QVBoxLayout *const top_layout = new QVBoxLayout; top_layout->setSpacing(2); top_layout->setMargin(2); top_layout->addWidget( buildLvBox() ); if(mda) top_layout->addWidget( buildMdaBox() ); if(uuid) top_layout->addWidget( buildUuidBox() ); top_layout->addStretch(); setLayout(top_layout); } QList PVProperties::sortByExtent() { const QString pv_name = m_pv->getName(); QList first_extent_list; QStringList pv_name_list; QList lv_extents; LVSegmentExtent *temp; LogVol *lv; QListIterator lv_itr( m_pv->getVg()->getLogicalVolumesFlat() ); while( lv_itr.hasNext() ){ lv = lv_itr.next(); if( !lv->isSnapContainer() ){ for(int segment = lv->getSegmentCount() - 1; segment >= 0; segment--){ pv_name_list = lv->getPvNames(segment); first_extent_list = lv->getSegmentStartingExtent(segment); for(int y = pv_name_list.size() - 1; y >= 0; y--){ if( pv_name_list[y] == pv_name ){ temp = new LVSegmentExtent; temp->lv_name = lv->getName(); temp->first_extent = first_extent_list[y]; temp->last_extent = temp->first_extent - 1 + (lv->getSegmentExtents(segment) / (lv->getSegmentStripes(segment))); lv_extents.append(temp); } } } } } qSort( lv_extents.begin() , lv_extents.end(), isLessThan ); return lv_extents; } QFrame *PVProperties::buildMdaBox() { bool use_si_units; KConfigSkeleton skeleton; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", use_si_units, false); KLocale *const locale = KGlobal::locale(); if(use_si_units) locale->setBinaryUnitDialect(KLocale::MetricBinaryDialect); else locale->setBinaryUnitDialect(KLocale::IECBinaryDialect); QLabel *label; QHBoxLayout *const layout = new QHBoxLayout; QFrame *const frame = new QFrame; frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); frame->setLayout(layout); label = new QLabel( "MDA" ); label->setAlignment( Qt::AlignVCenter | Qt::AlignLeft ); layout->addWidget(label); label = new QLabel( QString("Total: %1").arg( m_pv->getMdaCount() ) ); layout->addWidget(label); label = new QLabel( QString("In use: %1").arg( m_pv->getMdaUsed() ) ); layout->addWidget(label); label = new QLabel( QString("Size: %1").arg( locale->formatByteSize( m_pv->getMdaSize() ) ) ); layout->addWidget(label); return frame; } QFrame *PVProperties::buildLvBox() { QGridLayout *const layout = new QGridLayout; QLabel *label; long long first_extent; long long last_extent; QFrame *const frame = new QFrame; frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); frame->setLayout(layout); label = new QLabel( i18n("Volume name") ); label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); layout->addWidget(label, 1, 0); label = new QLabel( i18n("Start") ); label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); layout->addWidget(label, 1, 1); label = new QLabel( i18n("End") ); label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); layout->addWidget(label, 1, 2); label = new QLabel( i18n("Extents") ); label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); layout->addWidget(label, 1, 3); const QList lv_extents = sortByExtent(); int row = 2; for(int x = 0; x < lv_extents.size() ; x++){ first_extent = lv_extents[x]->first_extent; last_extent = lv_extents[x]->last_extent; if( row % 2 == 0 ){ label = new QLabel( lv_extents[x]->lv_name ); label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter ); label->setBackgroundRole(QPalette::Base); label->setAutoFillBackground(true); layout->addWidget(label,row, 0); label = new QLabel( i18n("%1", first_extent) ); label->setAlignment(Qt::AlignRight | Qt::AlignVCenter ); label->setBackgroundRole(QPalette::Base); label->setAutoFillBackground(true); layout->addWidget(label,row, 1); label = new QLabel( i18n("%1", last_extent) ); label->setAlignment(Qt::AlignRight | Qt::AlignVCenter ); label->setBackgroundRole(QPalette::Base); label->setAutoFillBackground(true); layout->addWidget(label, row, 2); label = new QLabel( i18n("%1", last_extent - first_extent + 1 ) ); label->setBackgroundRole(QPalette::Base); label->setAutoFillBackground(true); label->setAlignment(Qt::AlignRight | Qt::AlignVCenter ); layout->addWidget(label, row, 3); } else{ label = new QLabel( lv_extents[x]->lv_name ); label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter ); label->setBackgroundRole(QPalette::AlternateBase); label->setAutoFillBackground(true); layout->addWidget(label,row, 0); label = new QLabel( i18n("%1", first_extent) ); label->setAlignment(Qt::AlignRight | Qt::AlignVCenter ); label->setBackgroundRole(QPalette::AlternateBase); label->setAutoFillBackground(true); layout->addWidget(label,row, 1); label = new QLabel( i18n("%1", last_extent) ); label->setAlignment(Qt::AlignRight | Qt::AlignVCenter ); label->setBackgroundRole(QPalette::AlternateBase); label->setAutoFillBackground(true); layout->addWidget(label, row, 2); label = new QLabel( i18n("%1", last_extent - first_extent + 1 ) ); label->setAlignment(Qt::AlignRight | Qt::AlignVCenter ); label->setBackgroundRole(QPalette::AlternateBase); label->setAutoFillBackground(true); layout->addWidget(label, row, 3); } row++; } label = new QLabel(); layout->addWidget(label, row + 1, 0, 1, -1 ); label = new QLabel( i18n("Total extents: %1", m_pv->getSize() / m_pv->getVg()->getExtentSize() ) ); layout->addWidget(label, row + 2, 0, 1, -1 ); for(int x = 0; x < lv_extents.size(); x++) delete lv_extents[x]; return frame; } QFrame *PVProperties::buildUuidBox() { QLabel *label; QHBoxLayout *const layout = new QHBoxLayout; QFrame *const frame = new QFrame; frame->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); frame->setLineWidth(2); frame->setLayout(layout); label = new QLabel( "UUID" ); label->setAlignment( Qt::AlignVCenter | Qt::AlignLeft ); layout->addWidget(label); label = new QLabel( m_pv->getUuid() ); label->setAlignment( Qt::AlignCenter ); label->setWordWrap(true); layout->addWidget(label); return frame; } kvpm-0.8.6/kvpm/fsck.h0000644000175000017500000000114311733530416015004 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef FSCK_H #define FSCK_H class LogVol; class StoragePartition; #include bool fsck(const QString path); bool manual_fsck(LogVol *const logicalVolume); bool manual_fsck(StoragePartition *const partition); #endif kvpm-0.8.6/kvpm/lvproperties.h0000644000175000017500000000155411733530416016622 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef LVPROPERTIES_H #define LVPROPERTIES_H #include #include class LogVol; class LVProperties : public QWidget { bool m_use_si_units; LogVol *m_lv; QFrame *mountPointsFrame(); QFrame *uuidFrame(); QFrame *fsFrame(const bool showFsUuid, const bool showFsLabel); QFrame *generalFrame(int segment); QFrame *physicalVolumesFrame(int segment); public: LVProperties(LogVol *const volume, const int segment, QWidget *parent = 0); }; #endif kvpm-0.8.6/kvpm/partadd.h0000644000175000017500000000374011733530416015502 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef PARTADD_H #define PARTADD_H #include #include #include #include #include #include #include #include #include class QDoubleValidator; class PartitionGraphic; class DualSelectorBox; class StoragePartition; class PartitionAddDialog : public KDialog { Q_OBJECT StoragePartition *m_partition; PedConstraint *m_ped_constraints; DualSelectorBox *m_dual_selector; bool m_use_si_units, m_bailout; PedSector m_max_part_start, // first available sector of free space m_max_part_end, // last available sector of free space m_max_part_size; // sectors available of free space long long m_sector_size; // bytes per logical sector PartitionGraphic *m_display_graphic; // The color bar that shows the relative // size of the partition graphically QLabel *m_remaining_label, // space left past the end of the proposed partition *m_preceding_label; // ditto for the preceding space KComboBox *m_type_combo; void updatePartition(); long long convertSizeToSectors(int index, double size); void getMaximumPartition(PedSector &start, PedSector &end, PedSector §orSize); QGroupBox *buildInfoGroup(); KComboBox *buildTypeCombo(); bool hasInitialErrors(); public: explicit PartitionAddDialog(StoragePartition *const partition, QWidget *parent = 0); bool bailout(); private slots: void commitPartition(); void validateChange(); }; #endif kvpm-0.8.6/kvpm/lvactionsmenu.h0000644000175000017500000000350211733530416016746 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef LVACTIONSMENU_H #define LVACTIONSMENU_H #include #include #include class VolGroup; class LogVol; class LVChartSeg; class VGTree; class LVActionsMenu : public KMenu { Q_OBJECT KAction *lv_remove_action, *lv_create_action, *pv_move_action, *lv_rename_action, *lv_reduce_action, *lv_extend_action, *lv_change_action, *add_mirror_legs_action, *change_mirror_log_action, *remove_mirror_action, *remove_mirror_leg_action, *snap_create_action, *snap_merge_action, *mount_filesystem_action, *unmount_filesystem_action, *lv_removefs_action, *lv_mkfs_action, *lv_maxfs_action, *lv_fsck_action; KMenu *filesystem_menu; VolGroup *m_vg; LogVol *m_lv; int m_segment; public: LVActionsMenu(LogVol *logicalVolume, int segment, VolGroup *volumeGroup, QWidget *parent); private slots: void createLogicalVolume(); void extendLogicalVolume(); void changeLogicalVolume(); void reduceLogicalVolume(); void removeLogicalVolume(); void renameLogicalVolume(); void addMirrorLegs(); void changeMirrorLog(); void removefsLogicalVolume(); void removeMirror(); void removeMirrorLeg(); void createSnapshot(); void mkfsLogicalVolume(); void fsckLogicalVolume(); void maxfsLogicalVolume(); void mergeSnapshot(); void movePhysicalExtents(); void mountFilesystem(); void unmountFilesystem(); }; #endif kvpm-0.8.6/kvpm/fsblocksize.h0000644000175000017500000000074711733530416016405 0ustar benscottbenscott/* * * * Copyright (C) 2009 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef FSBLOCKSIZE_H #define FSBLOCKSIZE_H #include long get_fs_block_size(QString path); #endif kvpm-0.8.6/kvpm/maintabwidget.h0000644000175000017500000000246211733530416016702 0ustar benscottbenscott/* * * * Copyright (C) 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef MAINTABWIDGET_H #define MAINTABWIDGET_H #include #include #include class VolumeGroupTab; class DeviceTab; class MainTabWidget : public QWidget { Q_OBJECT QStringList m_unmunged_text; // Tab labels without amperands KTabWidget *m_tab_widget; QList m_vg_tabs; signals: void currentIndexChanged(); public: MainTabWidget(QWidget *parent = 0); QString getUnmungedText(const int index); void appendVolumeGroupTab(VolumeGroupTab *const page, const QIcon &icon, const QString &label); void appendDeviceTab(DeviceTab *const page, const QString & label); void deleteTab(const int index); QWidget *getWidget(const int index); int getCount(); int getCurrentIndex(); VolumeGroupTab *getVolumeGroupTab(const int index); void setIcon(const int index, const QIcon &icon); public slots: void indexChanged(int index); }; #endif kvpm-0.8.6/kvpm/vgremove.cpp0000644000175000017500000000401211733530416016241 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "vgremove.h" #include #include #include #include #include "masterlist.h" #include "topwindow.h" #include "progressbox.h" #include "volgroup.h" bool remove_vg(VolGroup *volumeGroup) { const QByteArray vg_name = volumeGroup->getName().toLocal8Bit(); lvm_t lvm = MasterList::getLvm(); vg_t vg_dm = NULL; ProgressBox *const progress_box = TopWindow::getProgressBox(); bool success = true; const QString message = i18n("Are you certain you want to delete volume group: %1?", volumeGroup->getName()); if(KMessageBox::questionYesNo( 0, message) == KMessageBox::Yes){ progress_box->setRange(0,3); progress_box->setValue(1); qApp->processEvents(); if( (vg_dm = lvm_vg_open(lvm, vg_name.data(), "w", 0)) ){ progress_box->setValue(2); qApp->processEvents(); if( lvm_vg_remove(vg_dm) ){ KMessageBox::error(0, QString(lvm_errmsg(lvm))); success = false; } else{ if( lvm_vg_write(vg_dm) ){ KMessageBox::error(0, QString(lvm_errmsg(lvm))); success = false; } } lvm_vg_close(vg_dm); progress_box->setValue(3); qApp->processEvents(); } else{ KMessageBox::error(0, QString(lvm_errmsg(lvm))); success = false; } } else{ KMessageBox::error(0, QString(lvm_errmsg(lvm))); success = false; } progress_box->setValue(3); qApp->processEvents(); return success; } kvpm-0.8.6/kvpm/sizeselectorbox.cpp0000644000175000017500000003403211733530416017640 0ustar benscottbenscott/* * * * Copyright (C) 2011, 2012 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "sizeselectorbox.h" #include #include #include #include SizeSelectorBox::SizeSelectorBox(long long unitSize, long long minSize, long long maxSize, long long initialSize, bool isVolume, bool isOffset, bool isNew, bool startLocked, QWidget *parent) : QGroupBox(parent), m_max_size(maxSize), m_min_size(minSize), m_unit_size(unitSize), m_is_volume(isVolume), m_is_offset(isOffset), m_is_new(isNew), m_start_locked(startLocked) { m_initial_size = initialSize; m_current_size = initialSize; m_is_valid = true; m_constrained_max = m_max_size; m_constrained_min = m_min_size; KConfigSkeleton skeleton; skeleton.setCurrentGroup("General"); skeleton.addItemBool("use_si_units", m_use_si_units, false); QVBoxLayout *const layout = new QVBoxLayout(); QHBoxLayout *const upper_layout = new QHBoxLayout(); m_size_slider = new QSlider(Qt::Horizontal); m_size_slider->setRange(0, 100); m_size_edit = new KLineEdit(); m_size_edit->setAlignment(Qt::AlignRight); m_suffix_combo = new KComboBox(); if(m_use_si_units){ m_suffix_combo->insertItem(0,"MB"); m_suffix_combo->insertItem(1,"GB"); m_suffix_combo->insertItem(2,"TB"); } else{ m_suffix_combo->insertItem(0,"MiB"); m_suffix_combo->insertItem(1,"GiB"); m_suffix_combo->insertItem(2,"TiB"); } if(m_is_volume){ m_suffix_combo->insertItem(0, i18n("Extents")); m_suffix_combo->setCurrentIndex(2); } else{ m_suffix_combo->setCurrentIndex(1); } m_suffix_combo->setInsertPolicy(KComboBox::NoInsert); if(m_is_new){ if( !m_is_offset ) m_current_size = m_constrained_max; else m_current_size = 0; } m_size_validator = new QDoubleValidator(m_size_edit); m_size_edit->setValidator(m_size_validator); m_size_validator->setBottom(0); m_size_box = NULL; if(m_is_volume){ setTitle( i18n("Volume Size") ); if( !m_is_new ){ m_size_box = new QCheckBox( i18n("Lock selected size") ); m_size_box->setChecked(false); layout->addWidget(m_size_box); setConstraints(false); connect(m_size_box, SIGNAL(toggled(bool)), this, SLOT(lock(bool))); } } else if(m_is_offset){ setTitle( i18n("Partition Start") ); m_offset_box = new QCheckBox( i18n("Lock partition start") ); setConstraints(false); layout->addWidget(m_offset_box); connect(m_offset_box, SIGNAL(toggled(bool)), this, SLOT(lock(bool))); } else{ setTitle( i18n("Partition Size") ); m_size_box = new QCheckBox( i18n("Lock selected size") ); layout->addWidget(m_size_box); connect(m_size_box, SIGNAL(toggled(bool)), this, SLOT(lock(bool))); if( !m_is_new ){ m_shrink_box = new QCheckBox( i18n("Prevent shrinking") ); m_shrink_box->setChecked(false); setConstraints(false); layout->addWidget(m_shrink_box); connect(m_shrink_box, SIGNAL(toggled(bool)), this, SLOT(lockShrink(bool))); connect(m_size_box, SIGNAL(toggled(bool)), this, SLOT(disableLockShrink(bool))); } } QLabel *const edit_label = new QLabel(); if(m_is_offset) edit_label->setText( i18n("New start:") ); else edit_label->setText( i18n("New size:") ); edit_label->setBuddy(m_size_edit); upper_layout->addWidget(edit_label); upper_layout->addWidget(m_size_edit); upper_layout->addWidget(m_suffix_combo); layout->addLayout(upper_layout); layout->addWidget(m_size_slider); setLayout(layout); updateSlider(); updateEdit(); updateValidator(); connect(m_size_edit, SIGNAL(textEdited(QString)), this, SLOT(setToEdit(QString))); connect(m_size_slider, SIGNAL(sliderMoved(int)), this, SLOT(setToSlider(int))); connect(m_suffix_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateEdit())); if( m_min_size == m_max_size ){ setCurrentSize(m_min_size); if(m_is_offset){ m_offset_box->setChecked(true); m_offset_box->setEnabled(false); } else{ m_size_box->setChecked(true); m_size_box->setEnabled(false); } } } void SizeSelectorBox::resetToInitial() { if( m_size_box != NULL ){ if( !m_size_box->isEnabled() ) return; } m_current_size = m_initial_size; if(m_is_volume){ if( !m_is_new ){ m_shrink_box->setChecked(false); } } else if(m_is_offset){ lock(m_start_locked); m_offset_box->setChecked(m_start_locked); } else{ lock(m_start_locked); m_size_box->setChecked(m_start_locked); if( !m_is_new ){ m_shrink_box->setChecked(false); } } updateEdit(); updateSlider(); } void SizeSelectorBox::setToSlider(int value) { long long new_size = ((m_constrained_max - m_constrained_min) * ( (double)value / 100 )) + m_constrained_min; m_size_slider->setValue(value); if( (value >= 100 ) || ( new_size > m_constrained_max ) ) m_current_size = m_constrained_max; else if( ( value <= 0 ) || ( new_size < m_constrained_min ) ) m_current_size = m_constrained_min; else m_current_size = new_size; updateEdit(); } bool SizeSelectorBox::setCurrentSize(long long size) { if( isLocked() ){ return false; } else if( size > m_constrained_max ){ return false; } else if( size < m_constrained_min ){ return false; } else{ m_current_size = size; updateEdit(); updateSlider(); return true; } } void SizeSelectorBox::updateSlider() { int percent = qRound(100.0 * ((double)(m_current_size - m_constrained_min)/(m_constrained_max - m_constrained_min))); m_size_slider->setValue(percent); } void SizeSelectorBox::setConstrainedMax(long long max) { if( isLocked() || !m_size_edit->isEnabled() ) return; if( ( max < 0 ) || ( max > m_max_size ) ) max = m_max_size; m_constrained_max = max; if( m_constrained_min > m_constrained_max ) m_constrained_min = m_constrained_max; if( m_current_size > m_constrained_max ) setCurrentSize(m_constrained_max); updateValidator(); updateSlider(); } long long SizeSelectorBox::getCurrentSize() { return m_current_size; } long long SizeSelectorBox::getMaximumSize() { return m_constrained_max; } long long SizeSelectorBox::getMinimumSize() { return m_constrained_min; } void SizeSelectorBox::lock(bool lock) { setConstraints(lock); emit stateChanged(); } void SizeSelectorBox::disableLockShrink(bool disable) { m_shrink_box->setEnabled( !disable ); } void SizeSelectorBox::setConstraints(bool lock) { if(lock){ if( !m_is_valid ){ if(m_current_size > m_constrained_max) m_current_size = m_constrained_max; if(m_current_size < m_constrained_min) m_current_size = m_constrained_min; updateEdit(); } if(m_is_offset){ m_constrained_min = m_min_size; m_constrained_max = m_max_size; } else{ m_constrained_min = m_current_size; m_constrained_max = m_current_size; } m_size_edit->setEnabled(false); m_size_slider->setEnabled(false); m_suffix_combo->setEnabled(false); } else{ if( !m_is_valid ){ if(m_current_size > m_constrained_max) m_current_size = m_constrained_max; if(m_current_size < m_constrained_min) m_current_size = m_constrained_min; updateEdit(); } m_constrained_min = m_min_size; m_size_edit->setEnabled(true); m_size_slider->setEnabled(true); m_suffix_combo->setEnabled(true); m_constrained_max = m_max_size; updateValidator(); updateSlider(); } } void SizeSelectorBox::lockShrink(bool lock) { if(lock){ if( !m_is_valid ){ if(m_current_size > m_constrained_max) m_current_size = m_constrained_max; if(m_current_size < m_constrained_min) m_current_size = m_constrained_min; updateEdit(); } m_constrained_min = m_initial_size; if(m_current_size < m_constrained_min){ m_current_size = m_constrained_min; updateValidator(); updateSlider(); } } else{ if( !m_is_valid ){ if(m_current_size > m_constrained_max) m_current_size = m_constrained_max; if(m_current_size < m_constrained_min) m_current_size = m_constrained_min; updateEdit(); } m_constrained_min = m_min_size; updateValidator(); updateSlider(); } emit stateChanged(); } void SizeSelectorBox::setToEdit(QString size) { long long proposed_size; int x = 0; if(m_size_validator->validate(size, x) == QValidator::Acceptable){ if(m_suffix_combo->currentIndex() == 0 && m_is_volume) proposed_size = size.toLongLong(); else proposed_size = convertSizeToUnits(m_suffix_combo->currentIndex(), size.toDouble()); if( (proposed_size >= m_constrained_min) && (proposed_size <= m_constrained_max)){ m_current_size = proposed_size; m_is_valid = true; emit stateChanged(); return; } } m_is_valid = false; emit stateChanged(); } long long SizeSelectorBox::convertSizeToUnits(int index, double size) { if(m_is_volume) index -= 1; long double partition_size = size; if(m_use_si_units){ if(index == 0) partition_size *= (long double)1.0E6; else if(index == 1) partition_size *= (long double)1.0E9; else{ partition_size *= (long double)1.0E6; partition_size *= (long double)1.0E6; } } else{ if(index == 0) partition_size *= (long double)0x100000; else if(index == 1) partition_size *= (long double)0x40000000; else{ partition_size *= (long double)0x100000; partition_size *= (long double)0x100000; } } partition_size /= m_unit_size; if (partition_size < 0) partition_size = 0; return qRound64(partition_size); } void SizeSelectorBox::updateEdit() { long double sized = (long double)m_current_size * m_unit_size; int index = m_suffix_combo->currentIndex(); updateValidator(); if(m_is_volume) index -= 1; if(m_use_si_units){ if(index == 0) sized /= (long double)1.0E6; else if(index == 1) sized /= (long double)1.0E9; else{ sized /= (long double)1.0E6; sized /= (long double)1.0E6; } } else{ if(index == 0) sized /= (long double)0x100000; else if(index == 1) sized /= (long double)0x40000000; else{ sized /= (long double)0x100000; sized /= (long double)0x100000; } } if( index == -1 ) m_size_edit->setText( QString("%1").arg( (double)m_current_size, 0, 'g', 5) ); else m_size_edit->setText( QString("%1").arg( (double)sized, 0, 'g', 5) ); if( (m_current_size >= m_constrained_min) && (m_current_size <= m_constrained_max)) m_is_valid = true; else m_is_valid = false; emit stateChanged(); } void SizeSelectorBox::updateValidator() { long double valid_topd = (long double)m_constrained_max * m_unit_size; long double valid_bottomd = (long double)m_constrained_min * m_unit_size; int index = m_suffix_combo->currentIndex(); if(m_is_volume) index -= 1; if(m_use_si_units){ if(index == 0){ valid_topd /= (long double)1.0E6; valid_bottomd /= (long double)1.0E6; } else if(index == 1){ valid_topd /= (long double)1.0E9; valid_bottomd /= (long double)1.0E9; } else{ valid_topd /= (long double)1.0E6; valid_topd /= (long double)1.0E6; valid_bottomd /= (long double)1.0E6; valid_bottomd /= (long double)1.0E6; } } else{ if(index == 0){ valid_topd /= (long double)0x100000; valid_bottomd /= (long double)0x100000; } else if(index == 1){ valid_topd /= (long double)0x40000000; valid_bottomd /= (long double)0x40000000; } else{ valid_topd /= (long double)0x100000; valid_topd /= (long double)0x100000; valid_bottomd /= (long double)0x100000; valid_bottomd /= (long double)0x100000; } } if( valid_bottomd < 0 ) valid_bottomd = 0; if( index != -1 ){ m_size_validator->setTop( (double)valid_topd ); m_size_validator->setBottom( (double)valid_bottomd ); } else{ m_size_validator->setTop( (double)m_constrained_max ); m_size_validator->setBottom( (double)m_constrained_min ); } } bool SizeSelectorBox::isValid() { return m_is_valid; } bool SizeSelectorBox::isLocked() { if(m_is_offset){ return m_offset_box->isChecked(); } else return m_size_box->isChecked(); } kvpm-0.8.6/kvpm/mount.cpp0000644000175000017500000004444611733530416015570 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "mount.h" #include #include #include #include #include #include #include #include #include #include #include #include "logvol.h" #include "mountentry.h" #include "mounttables.h" #include "storagepartition.h" const int BUFF_LEN = 2000; // Enough? MountDialog::MountDialog(LogVol *const volume, QWidget *parent) : KDialog(parent) { m_device_to_mount = volume->getMapperPath(); m_filesystem_type = volume->getFilesystem(); m_is_writable = volume->isWritable(); m_mount_point = volume->getFstabMountPoint(); buildDialog(); } MountDialog::MountDialog(StoragePartition *const partition, QWidget *parent) : KDialog(parent) { m_device_to_mount = partition->getName(); m_filesystem_type = partition->getFilesystem(); m_is_writable = partition->isWritable(); m_mount_point = partition->getFstabMountPoint(); buildDialog(); } void MountDialog::buildDialog() { setCaption("Mount a Filesystem"); QWidget *const dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *const layout = new QVBoxLayout(); dialog_body->setLayout(layout); QLabel *const device_label = new QLabel( "" + i18n("Device To Mount: ") + m_device_to_mount + ""); device_label->setAlignment(Qt::AlignCenter); layout->addWidget(device_label); QHBoxLayout *const main_layout = new QHBoxLayout(); layout->addLayout(main_layout); QVBoxLayout *const left_layout = new QVBoxLayout(); main_layout->addLayout(left_layout); left_layout->addWidget( filesystemBox() ); left_layout->addWidget( mountPointBox() ); main_layout->addWidget( optionsBox() ); connect(this, SIGNAL( accepted() ), this, SLOT( mountFilesystem() )); } QWidget* MountDialog::filesystemBox() { QGroupBox *const filesystem_box = new QGroupBox( i18n("Filesystem Type"), this); QVBoxLayout *const layout_left = new QVBoxLayout(); QVBoxLayout *const layout_center = new QVBoxLayout(); QVBoxLayout *const layout_right = new QVBoxLayout(); QHBoxLayout *const upper_layout = new QHBoxLayout(); QHBoxLayout *const lower_layout = new QHBoxLayout(); QVBoxLayout *const filesystem_layout = new QVBoxLayout(); upper_layout->addLayout(layout_left); upper_layout->addLayout(layout_center); upper_layout->addLayout(layout_right); filesystem_layout->addLayout(upper_layout); filesystem_layout->addLayout(lower_layout); filesystem_box->setLayout(filesystem_layout); ext2_button = new QRadioButton("ext2", this); ext3_button = new QRadioButton("ext3", this); ext4_button = new QRadioButton("ext4", this); btrfs_button = new QRadioButton("btrfs",this); reiserfs3_button = new QRadioButton("reiser3", this); reiserfs4_button = new QRadioButton("reiser4", this); xfs_button = new QRadioButton("xfs", this); jfs_button = new QRadioButton("jfs", this); vfat_button = new QRadioButton("vfat",this); udf_button = new QRadioButton("udf", this); iso9660_button = new QRadioButton("iso9660", this); hfs_button = new QRadioButton("hfs", this); ntfs_button = new QRadioButton("ntfs", this); specify_button = new QRadioButton( i18n("Specify another fileystem:"), this); if( m_filesystem_type == "ext2" ) ext2_button->setChecked(true); else if( m_filesystem_type == "ext3" ) ext3_button->setChecked(true); else if( m_filesystem_type == "ext4" ) ext4_button->setChecked(true); else if( m_filesystem_type == "btrfs" ) btrfs_button->setChecked(true); else if( m_filesystem_type == "reiserfs" ) reiserfs3_button->setChecked(true); else if( m_filesystem_type == "reiser4" ) reiserfs4_button->setChecked(true); else if( m_filesystem_type == "xfs" ) xfs_button->setChecked(true); else if( m_filesystem_type == "jfs" ) jfs_button->setChecked(true); else if( m_filesystem_type == "vfat" ) vfat_button->setChecked(true); else if( m_filesystem_type == "udf" ) udf_button->setChecked(true); else if( m_filesystem_type == "iso9660" ) iso9660_button->setChecked(true); else if( m_filesystem_type == "hfs" ) hfs_button->setChecked(true); else if( m_filesystem_type == "ntfs" ) ntfs_button->setChecked(true); else specify_button->setChecked(true); m_filesystem_edit = new KLineEdit(m_filesystem_type); if( specify_button->isChecked() && (m_filesystem_edit->text()).isEmpty() ) (button(KDialog::Ok))->setEnabled(false); layout_left->addWidget(ext2_button); layout_left->addWidget(ext3_button); layout_left->addWidget(ext4_button); layout_left->addWidget(btrfs_button); layout_left->addWidget(ntfs_button); layout_left->addStretch(); layout_center->addWidget(reiserfs3_button); layout_center->addWidget(reiserfs4_button); layout_center->addWidget(xfs_button); layout_center->addWidget(jfs_button); layout_center->addStretch(); layout_right->addWidget(udf_button); layout_right->addWidget(iso9660_button); layout_right->addWidget(hfs_button); layout_right->addWidget(vfat_button); layout_right->addStretch(); lower_layout->addWidget(specify_button); lower_layout->addWidget(m_filesystem_edit); connect(m_filesystem_edit, SIGNAL( textEdited(const QString) ), this, SLOT( toggleOKButton(const QString) )); connect(specify_button, SIGNAL( toggled(bool) ), this, SLOT( toggleOKButton(bool) )); return filesystem_box; } QWidget* MountDialog::optionsBox() { QWidget *options_box = new QGroupBox( i18n("Mount Options") ); QVBoxLayout *options_layout = new QVBoxLayout(); options_box->setLayout(options_layout); QGroupBox *common_options_box = new QGroupBox( i18n("Common Options"), this); QVBoxLayout *layout_left = new QVBoxLayout(); QVBoxLayout *layout_right = new QVBoxLayout(); QHBoxLayout *common_options_layout = new QHBoxLayout(); common_options_layout->addLayout(layout_left); common_options_layout->addLayout(layout_right); common_options_box->setLayout(common_options_layout); sync_check = new QCheckBox("sync"); dirsync_check = new QCheckBox("dirsync"); rw_check = new QCheckBox("rw"); suid_check = new QCheckBox("suid"); dev_check = new QCheckBox("dev"); exec_check = new QCheckBox("exec"); mand_check = new QCheckBox("mand"); acl_check = new QCheckBox("acl"); user_xattr_check = new QCheckBox("user xattr"); if(m_is_writable) rw_check->setChecked(true); else{ rw_check->setChecked(false); rw_check->setEnabled(false); } suid_check->setChecked(true); dev_check->setChecked(true); exec_check->setChecked(true); sync_check->setToolTip( i18n("Always use synchronous I/O") ); rw_check->setToolTip( i18n("Allow writing in addition to reading") ); suid_check->setToolTip( i18n("Allow the suid bit to have effect") ); dev_check->setToolTip( i18n("Allow the use of block and special devices") ); exec_check->setToolTip( i18n("Allow the execution of binary files") ); mand_check->setToolTip( i18n("Allow manditory file locks") ); acl_check->setToolTip( i18n("Allow use of access control lists") ); layout_left->addWidget(rw_check); layout_left->addWidget(suid_check); layout_left->addWidget(sync_check); layout_left->addWidget(dirsync_check); layout_left->addWidget(acl_check); layout_left->addStretch(); layout_right->addWidget(dev_check); layout_right->addWidget(exec_check); layout_right->addWidget(mand_check); layout_right->addWidget(user_xattr_check); layout_right->addStretch(); QHBoxLayout *options_atime_layout = new QHBoxLayout(); options_layout->addLayout(options_atime_layout); QGroupBox *atime_box = new QGroupBox( i18n("Update atime"), this); QVBoxLayout *atime_layout = new QVBoxLayout(); atime_box->setLayout(atime_layout); options_atime_layout->addWidget(common_options_box); QVBoxLayout *atime_journal_layout = new QVBoxLayout(); options_atime_layout->addLayout(atime_journal_layout); m_filesystem_journal_box = new QGroupBox( i18n("Journaling") ); QVBoxLayout *journaling_layout = new QVBoxLayout; m_filesystem_journal_box->setLayout(journaling_layout); data_journal_button = new QRadioButton("data=journal"); data_ordered_button = new QRadioButton("data=ordered"); data_ordered_button->setChecked(true); data_writeback_button = new QRadioButton("data=writeback"); journaling_layout->addWidget(data_journal_button); journaling_layout->addWidget(data_ordered_button); journaling_layout->addWidget(data_writeback_button); atime_journal_layout->addWidget(m_filesystem_journal_box); atime_button = new QRadioButton("atime"); noatime_button = new QRadioButton("noatime"); nodiratime_button = new QRadioButton("nodiratime"); relatime_button = new QRadioButton("relatime"); relatime_button->setChecked(true); atime_layout->addWidget(atime_button); atime_layout->addWidget(noatime_button); atime_layout->addWidget(nodiratime_button); atime_layout->addWidget(relatime_button); atime_button->setToolTip( i18n("Always update atime, this is the default") ); noatime_button->setToolTip( i18n("Do not update atime") ); nodiratime_button->setToolTip( i18n("Do not update atime for directory access") ); relatime_button->setToolTip( i18n("Access time is only updated if the previous " "access time was earlier than the current modify " "or change time") ); atime_journal_layout->addWidget(atime_box); QGroupBox *filesystem_options_box = new QGroupBox( i18n("Filesystem specific mount options") ); m_fs_specific_edit = new KLineEdit(); m_fs_specific_edit->setPlaceholderText( i18n("comma separated list of additional mount options") ); options_layout->addWidget(filesystem_options_box); QVBoxLayout *filesystem_options_box_layout = new QVBoxLayout(); filesystem_options_box->setLayout(filesystem_options_box_layout); filesystem_options_box_layout->addWidget(m_fs_specific_edit); toggleAdditionalOptions(true); connect(ext2_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(ext3_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(ext4_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(reiserfs3_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(reiserfs4_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(btrfs_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(xfs_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(jfs_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(vfat_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(ntfs_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(specify_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(udf_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(iso9660_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); connect(hfs_button, SIGNAL( toggled(bool) ), this, SLOT( toggleAdditionalOptions(bool))); return options_box; } QWidget* MountDialog::mountPointBox() { QGroupBox *const mount_point_box = new QGroupBox( i18n("Mount Point") ); QHBoxLayout *const mount_point_layout = new QHBoxLayout(); mount_point_box->setLayout(mount_point_layout); m_mount_point_edit = new KLineEdit(m_mount_point); if( (m_mount_point_edit->text()).isEmpty() ) (button(KDialog::Ok))->setEnabled(false); mount_point_layout->addWidget(m_mount_point_edit); QPushButton *const browse_button = new QPushButton( i18n("Browse"), this); mount_point_layout->addWidget(browse_button); connect(m_mount_point_edit, SIGNAL( textChanged(const QString) ), this, SLOT( toggleOKButton(const QString) )); connect(browse_button, SIGNAL( clicked(bool) ), this, SLOT( selectMountPoint(bool) )); return mount_point_box; } void MountDialog::selectMountPoint(bool) { const QString filter = "*"; QString start; if( !m_mount_point.isEmpty() ) start = QString("file://" + m_mount_point); else start = QString("file:///"); const KUrl url(start); KFileDialog dialog( url, filter, 0 ); dialog.setModal(true); dialog.setMode(KFile::Directory); dialog.exec(); m_mount_point = dialog.selectedFile(); if( m_mount_point.length() > 1 && m_mount_point.endsWith('/') ) m_mount_point.chop(1); // remove trailing "/" character m_mount_point_edit->setText(m_mount_point); } void MountDialog::mountFilesystem() { unsigned long options = 0; QStringList standard_options, additional_options; QString all_options; if( ext2_button->isChecked() ) m_filesystem_type = "ext2"; else if( ext3_button->isChecked() ) m_filesystem_type = "ext3"; else if( ext4_button->isChecked() ) m_filesystem_type = "ext4"; else if( btrfs_button->isChecked() ) m_filesystem_type = "btrfs"; else if( reiserfs3_button->isChecked() ) m_filesystem_type = "reiserfs"; else if( reiserfs4_button->isChecked() ) m_filesystem_type = "reiser4"; else if( xfs_button->isChecked() ) m_filesystem_type = "xfs"; else if( jfs_button->isChecked() ) m_filesystem_type = "jfs"; else if( vfat_button->isChecked() ) m_filesystem_type = "vfat"; else if( ntfs_button->isChecked() ) m_filesystem_type = "ntfs"; else m_filesystem_type = m_filesystem_edit->text(); m_mount_point = m_mount_point_edit->text(); if( m_mount_point.length() > 1 && m_mount_point.endsWith('/') ) // remove trailing slash m_mount_point.chop(1); if( sync_check->isChecked() ){ standard_options.append("sync"); options |= MS_SYNCHRONOUS; } if( dirsync_check->isChecked() ){ standard_options.append("dirsync"); options |= MS_DIRSYNC; } if( rw_check->isChecked() ){ standard_options.append("rw"); } else{ standard_options.append("ro"); options |= MS_RDONLY; } if( !suid_check->isChecked() ){ standard_options.append("nosuid"); options |= MS_NOSUID; } if( !dev_check->isChecked() ){ standard_options.append("nodev"); options |= MS_NODEV; } if( !exec_check->isChecked() ){ standard_options.append("noexec"); options |= MS_NOEXEC; } if( mand_check->isChecked() ){ standard_options.append("mand"); options |= MS_MANDLOCK; } if( atime_button->isChecked() ){ standard_options.append("atime"); options |= MS_STRICTATIME; } else if( noatime_button->isChecked() ){ standard_options.append("noatime"); options |= MS_NOATIME; } else if( nodiratime_button->isChecked() ){ standard_options.append("nodiratime"); options |= MS_NODIRATIME; } else if( relatime_button->isChecked() ){ standard_options.append("relatime"); options |= MS_RELATIME; } /* "data=ordered" is the default so we ignore that button */ if( m_filesystem_journal_box->isEnabled() ){ if( data_journal_button->isChecked() ) additional_options.append("data=journal"); else if( data_writeback_button->isChecked() ) additional_options.append("data=writeback"); } if( acl_check->isEnabled() ){ if( acl_check->isChecked() ) additional_options.append("acl"); else additional_options.append("noacl"); } if( user_xattr_check->isEnabled() ){ if( user_xattr_check->isChecked() ) additional_options.append("user_xattr"); else additional_options.append("nouser_xattr"); } if( !m_fs_specific_edit->text().trimmed().isEmpty() ) additional_options.append( (m_fs_specific_edit->text()).trimmed() ); all_options = standard_options.join(","); if( additional_options.size() ){ all_options.append(","); all_options.append( additional_options.join(",") ); } const QByteArray device = m_device_to_mount.toLocal8Bit(); const QByteArray mount_point = m_mount_point.toLocal8Bit(); const QByteArray fs_type = m_filesystem_type.toLocal8Bit(); const QByteArray fs_options = additional_options.join(",").toLocal8Bit(); int error = mount( device.data(), mount_point.data(), fs_type.data(), options, fs_options.data() ); if( !error ) MountTables::addEntry(m_device_to_mount, m_mount_point, m_filesystem_type, all_options, 0, 0); else KMessageBox::error(0, QString("Error number: %1 %2").arg(errno).arg(strerror(errno))); } void MountDialog::toggleOKButton() { if ( (specify_button->isChecked() && (m_filesystem_edit->text()).isEmpty()) || (m_mount_point_edit->text()).isEmpty()) (button(KDialog::Ok))->setEnabled(false); else (button(KDialog::Ok))->setEnabled(true); } void MountDialog::toggleOKButton(const QString) { toggleOKButton(); } void MountDialog::toggleOKButton(bool) { toggleOKButton(); } void MountDialog::toggleAdditionalOptions(bool) { if( ext3_button->isChecked() ) m_filesystem_journal_box->setEnabled(true); else m_filesystem_journal_box->setEnabled(false); if( ext2_button->isChecked() || ext3_button->isChecked() || ext4_button->isChecked() || reiserfs3_button->isChecked() || reiserfs4_button->isChecked() ){ acl_check->setEnabled(true); acl_check->setChecked(true); user_xattr_check->setEnabled(true); user_xattr_check->setChecked(true); } else{ acl_check->setEnabled(false); acl_check->setChecked(false); user_xattr_check->setEnabled(false); user_xattr_check->setChecked(false); } } kvpm-0.8.6/kvpm/changemirror.cpp0000644000175000017500000003560711733530416017105 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2009, 2010, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "changemirror.h" #include #include #include #include #include #include #include "logvol.h" #include "misc.h" #include "physvol.h" #include "pvgroupbox.h" #include "processprogress.h" #include "volgroup.h" ChangeMirrorDialog::ChangeMirrorDialog(LogVol *logicalVolume, bool changeLog, QWidget *parent): KDialog(parent), m_change_log(changeLog), m_lv(logicalVolume) { QList children; const QString lv_name = m_lv->getName(); setWindowTitle( i18n("Change Mirror") ); m_tab_widget = new KTabWidget(); setMainWidget(m_tab_widget); m_tab_widget->addTab(buildGeneralTab(), i18nc("Common user options", "General") ); m_tab_widget->addTab(buildPhysicalTab(), i18n("Physical layout") ); resetOkButton(); connect(this, SIGNAL(okClicked()), this, SLOT(commitChanges())); connect(m_pv_box, SIGNAL(stateChanged()), this, SLOT(resetOkButton())); } QWidget *ChangeMirrorDialog::buildGeneralTab() { QWidget *const general = new QWidget; QLabel *const lv_name_label = new QLabel( i18n("Volume: %1", m_lv->getName() )); QLabel *const current_mirrors_label = new QLabel(); QHBoxLayout *const general_layout = new QHBoxLayout; QVBoxLayout *const center_layout = new QVBoxLayout; const bool is_mirror = m_lv->isMirror(); lv_name_label->setAlignment(Qt::AlignCenter); center_layout->addWidget(lv_name_label); general_layout->addStretch(); general_layout->addLayout(center_layout); general_layout->addStretch(); if( !m_change_log ){ QGroupBox *const add_mirror_box = new QGroupBox( i18n("Add mirror legs") ); QVBoxLayout *const add_mirror_box_layout = new QVBoxLayout; add_mirror_box->setLayout(add_mirror_box_layout); center_layout->addStretch(); center_layout->addWidget(add_mirror_box); if( is_mirror ){ current_mirrors_label->setText( i18n("Existing mirror legs: %1", m_lv->getMirrorCount() ) ); add_mirror_box_layout->addWidget(current_mirrors_label); } QHBoxLayout *const spin_box_layout = new QHBoxLayout(); QLabel *const add_mirrors_label = new QLabel( i18n("Add mirror legs: ") ); m_add_mirrors_spin = new KIntSpinBox(1, 10, 1, 1, this); spin_box_layout->addWidget(add_mirrors_label); spin_box_layout->addWidget(m_add_mirrors_spin); add_mirror_box_layout->addLayout(spin_box_layout); add_mirror_box_layout->addStretch(); connect(m_add_mirrors_spin, SIGNAL(valueChanged(int)), this, SLOT(resetOkButton())); } if( !is_mirror || m_change_log ){ QGroupBox *const log_box = new QGroupBox( i18n("Mirror logging") ); QVBoxLayout *const log_box_layout = new QVBoxLayout; m_core_log_button = new QRadioButton( i18n("Memory based log") ); m_disk_log_button = new QRadioButton( i18n("Disk based log") ); m_mirrored_log_button = new QRadioButton( i18n("Mirrored disk based log") ); if( is_mirror ){ if( m_lv->getLogCount() == 0 ) m_core_log_button->setChecked(true); else if( m_lv->getLogCount() == 1 ) m_disk_log_button->setChecked(true); else m_mirrored_log_button->setChecked(true); } else m_disk_log_button->setChecked(true); log_box_layout->addWidget(m_mirrored_log_button); log_box_layout->addWidget(m_disk_log_button); log_box_layout->addWidget(m_core_log_button); log_box->setLayout(log_box_layout); center_layout->addWidget(log_box); connect(m_disk_log_button, SIGNAL(toggled(bool)), this, SLOT(resetOkButton())); connect(m_core_log_button, SIGNAL(toggled(bool)), this, SLOT(resetOkButton())); connect(m_mirrored_log_button, SIGNAL(toggled(bool)), this, SLOT(resetOkButton())); } QGroupBox *const alloc_box = new QGroupBox( i18n("Allocation policy") ); QVBoxLayout *const alloc_box_layout = new QVBoxLayout; m_normal_button = new QRadioButton( i18nc("The usual way", "Normal") ); m_contiguous_button = new QRadioButton( i18n("Contiguous") ); m_anywhere_button = new QRadioButton( i18n("Anywhere") ); m_inherited_button = new QRadioButton( i18nc("Inherited from the parent group", "Inherited") ); m_cling_button = new QRadioButton( i18n("Cling") ); m_normal_button->setChecked(true); alloc_box_layout->addWidget(m_normal_button); alloc_box_layout->addWidget(m_contiguous_button); alloc_box_layout->addWidget(m_anywhere_button); alloc_box_layout->addWidget(m_inherited_button); alloc_box_layout->addWidget(m_cling_button); alloc_box->setLayout(alloc_box_layout); center_layout->addWidget(alloc_box); center_layout->addStretch(); general->setLayout(general_layout); return general; } QWidget *ChangeMirrorDialog::buildPhysicalTab() { QWidget *const physical = new QWidget; QList unused_pvs = m_lv->getVg()->getPhysicalVolumes(); const QStringList pvs_in_use = getPvsInUse(); // pvs with a mirror leg or log already on them aren't // suitable for another so we remove those here for(int x = unused_pvs.size() - 1; x >= 0; x--){ if( !unused_pvs[x]->isAllocatable() || unused_pvs[x]->getRemaining() <= 0 ){ unused_pvs.removeAt(x); } else{ for(int y = 0; y < pvs_in_use.size() ; y++){ if( unused_pvs[x]->getName() == pvs_in_use[y] ){ unused_pvs.removeAt(x); break; } } } } QVBoxLayout *const physical_layout = new QVBoxLayout(); m_pv_box = new PvGroupBox(unused_pvs); physical_layout->addWidget(m_pv_box); physical_layout->addStretch(); QHBoxLayout *const h_layout = new QHBoxLayout(); QVBoxLayout *const lower_layout = new QVBoxLayout(); physical_layout->addLayout(h_layout); h_layout->addStretch(); h_layout->addLayout(lower_layout); h_layout->addStretch(); if( !m_change_log ){ m_stripe_box = new QGroupBox( i18n("Disk striping") ); QVBoxLayout *const striped_layout = new QVBoxLayout(); m_stripe_box->setCheckable(true); m_stripe_box->setChecked(false); m_stripe_box->setLayout(striped_layout); m_stripe_size_combo = new KComboBox(); for(int n = 2; (pow(2, n) * 1024) <= m_lv->getVg()->getExtentSize() ; n++){ m_stripe_size_combo->addItem(QString("%1").arg(pow(2, n)) + " KiB"); m_stripe_size_combo->setItemData(n - 2, QVariant( (int) pow(2, n) ), Qt::UserRole ); } QLabel *const stripe_size = new QLabel( i18n("Stripe Size: ") ); m_stripes_number_spin = new KIntSpinBox(); m_stripes_number_spin->setMinimum(2); m_stripes_number_spin->setMaximum(m_lv->getVg()->getPvCount()); QHBoxLayout *const stripe_size_layout = new QHBoxLayout(); stripe_size_layout->addWidget(stripe_size); stripe_size_layout->addWidget(m_stripe_size_combo); QLabel *const stripes_number = new QLabel( i18n("Number of stripes: ") ); QHBoxLayout *const stripes_number_layout = new QHBoxLayout(); stripes_number_layout->addWidget(stripes_number); stripes_number_layout->addWidget(m_stripes_number_spin); striped_layout->addLayout(stripe_size_layout); striped_layout->addLayout(stripes_number_layout); m_error_stack = new QStackedWidget(); QWidget *const error_widget = new QWidget(); QWidget *const blank_widget = new QWidget(); m_error_stack->addWidget(error_widget); m_error_stack->addWidget(blank_widget); QHBoxLayout *const error_layout = new QHBoxLayout(); QVBoxLayout *const error_right_layout = new QVBoxLayout(); QLabel *const stripe_error1 = new QLabel(""); stripe_error1->setPixmap( KIcon("dialog-warning").pixmap(32, 32) ); QLabel *const stripe_error2 = new QLabel( i18n("The number of extents: %1 must be evenly divisible by the number of stripes", m_lv->getExtents()) ); stripe_error2->setWordWrap(true); error_layout->addWidget(stripe_error1); error_right_layout->addWidget(stripe_error2); error_layout->addLayout(error_right_layout); error_widget->setLayout(error_layout); striped_layout->addWidget(m_error_stack); lower_layout->addWidget(m_stripe_box); connect(m_stripes_number_spin, SIGNAL(valueChanged(int)), this, SLOT(resetOkButton())); connect(m_stripe_box, SIGNAL(toggled(bool)), this, SLOT(resetOkButton())); } lower_layout->addStretch(); setLogRadioButtons(); resetOkButton(); physical->setLayout(physical_layout); return physical; } /* The next function returns a list of physical volumes in use by the mirror as legs or logs. */ QStringList ChangeMirrorDialog::getPvsInUse() { QList mirror_legs = m_lv->getAllChildrenFlat(); QStringList pvs_in_use; if( m_lv->isMirror() ){ for(int x = mirror_legs.size() - 1; x >= 0; x--){ if( (!mirror_legs[x]->isMirrorLeg() && !mirror_legs[x]->isMirrorLog()) ) mirror_legs.removeAt(x); else pvs_in_use << mirror_legs[x]->getPvNamesAll(); } } else{ pvs_in_use << m_lv->getPvNamesAll(); } return pvs_in_use; } /* Here we create a string based on all the options that the user chose in the dialog and feed that to "lvconvert" */ QStringList ChangeMirrorDialog::arguments() { QStringList args; args << "lvconvert"; if( !m_change_log ) args << "--mirrors" << QString("+%1").arg( m_add_mirrors_spin->value() ); else args << "--mirrors" << QString("+0"); if( m_change_log || !m_lv->isMirror() ){ if( m_core_log_button->isChecked() ) args << "--mirrorlog" << "core"; else if( m_mirrored_log_button->isChecked() ) args << "--mirrorlog" << "mirrored"; else args << "--mirrorlog" << "disk"; } if( !m_change_log ){ if( m_stripe_box->isChecked() ){ args << "--stripes" << QString("%1").arg( m_stripes_number_spin->value()); args << "--stripesize" << (m_stripe_size_combo->currentText()).remove("KiB").trimmed(); } } if ( !m_inherited_button->isChecked() ){ // "inherited" is what we get if args << "--alloc"; // we don't pass "--alloc" at all if ( m_contiguous_button->isChecked() ) // passing "--alloc" "inherited" args << "contiguous" ; // doesn't work else if ( m_anywhere_button->isChecked() ) args << "anywhere" ; else if ( m_cling_button->isChecked() ) args << "cling" ; else args << "normal" ; } args << "--background" << m_lv->getFullName() << m_pv_box->getNames(); return args; } /* Enable or disable the OK button based on having enough physical volumes checked. At least one pv for each mirror leg and one or two for the log(s) are needed. We also total up the space needed. */ void ChangeMirrorDialog::resetOkButton() { QList available_pv_bytes = m_pv_box->getRemainingSpaceList();; QList stripe_pv_bytes; int new_stripe_count = 1; int total_stripes = 0; // stripes per mirror * added mirrors int new_log_count = m_lv->getLogCount(); if( !m_change_log ){ if( !validateStripeSpin() ){ enableButtonOk(false); return; } if( m_stripe_box->isChecked() ) new_stripe_count = m_stripes_number_spin->value(); total_stripes = m_add_mirrors_spin->value() * new_stripe_count; } for(int x = 0; x < total_stripes; x++) stripe_pv_bytes.append(0); if( m_change_log || !m_lv->isMirror() ){ if( m_disk_log_button->isChecked() ) new_log_count = 1; else if( m_mirrored_log_button->isChecked() ) new_log_count = 2; else new_log_count = 0; } if( m_lv->isMirror() ){ if( m_change_log && ( m_lv->getLogCount() == new_log_count ) ){ enableButtonOk(false); return; } else if( !m_change_log && !(total_stripes > 0) ){ enableButtonOk(false); return; } } else if( !m_lv->isMirror() && !( (total_stripes > 0) || (m_lv->getLogCount() != new_log_count) ) ){ enableButtonOk(false); return; } qSort(available_pv_bytes); for(int x = m_lv->getLogCount(); x < new_log_count; x++){ if( available_pv_bytes.size() ) available_pv_bytes.removeFirst(); else{ enableButtonOk(false); return; } } if(total_stripes){ while( available_pv_bytes.size() ){ qSort(available_pv_bytes); qSort(stripe_pv_bytes); stripe_pv_bytes[0] += available_pv_bytes.takeLast(); } qSort(stripe_pv_bytes); if( stripe_pv_bytes[0] >= ( m_lv->getSize() / new_stripe_count ) ) enableButtonOk(true); else enableButtonOk(false); return; } else{ enableButtonOk(true); return; } enableButtonOk(false); return; } void ChangeMirrorDialog::setLogRadioButtons() { if( m_change_log ){ if( m_lv->getLogCount() == 2 ) m_mirrored_log_button->setChecked(true); else if( m_lv->getLogCount() == 1 ) m_disk_log_button->setChecked(true); else m_core_log_button->setChecked(true); } else if( !m_lv->isMirror() ) m_disk_log_button->setChecked(true); resetOkButton(); } void ChangeMirrorDialog::commitChanges() { hide(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); ProcessProgress add_mirror( arguments() ); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); } bool ChangeMirrorDialog::validateStripeSpin() { if( m_stripe_box->isChecked() ){ if( m_lv->getExtents() % m_stripes_number_spin->value() ){ m_error_stack->setCurrentIndex(0); // unworkable stripe count return false; } else{ m_error_stack->setCurrentIndex(1); // valid stripe count return true; } } else{ m_error_stack->setCurrentIndex(1); return true; } } kvpm-0.8.6/kvpm/pvchange.cpp0000644000175000017500000001146711733530416016216 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "pvchange.h" #include #include #include "physvol.h" #include "volgroup.h" PVChangeDialog::PVChangeDialog(PhysVol *physicalVolume, QWidget *parent): KDialog(parent), m_pv(physicalVolume) { setWindowTitle( i18n("Change physical volume attributes") ); QWidget *dialog_body = new QWidget(this); setMainWidget(dialog_body); QVBoxLayout *layout = new QVBoxLayout; dialog_body->setLayout(layout); QString pv_name = m_pv->getName(); QLabel *label = new QLabel( i18n("%1", pv_name) ); label->setAlignment(Qt::AlignCenter); layout->addWidget(label); QGroupBox *attrib_box = new QGroupBox( i18n("Attributes") ); QVBoxLayout *attrib_box_layout = new QVBoxLayout; attrib_box->setLayout(attrib_box_layout); layout->addWidget(attrib_box); m_allocation_box = new QCheckBox( i18n("Enable allocation of extents") ); if( m_pv->isAllocatable() ) m_allocation_box->setChecked(true); attrib_box_layout->addWidget(m_allocation_box); m_mda_box = new QCheckBox( i18n("Use metadata areas on this volume") ); if( m_pv->getMdaUsed() ) m_mda_box->setChecked(true); attrib_box_layout->addWidget(m_mda_box); m_uuid_box = new QCheckBox( i18n("Generate new UUID for this volume") ); m_uuid_box->setChecked(false); if( m_pv->getVg()->isActive() ) m_uuid_box->setEnabled(false); attrib_box_layout->addWidget(m_uuid_box); m_tags_group = new QGroupBox( i18n("Change tags") ); m_tags_group->setCheckable(true); m_tags_group->setChecked(false); layout->addWidget(m_tags_group); QHBoxLayout *add_tag_layout = new QHBoxLayout(); QHBoxLayout *del_tag_layout = new QHBoxLayout(); QVBoxLayout *tag_group_layout = new QVBoxLayout(); tag_group_layout->addLayout(add_tag_layout); tag_group_layout->addLayout(del_tag_layout); m_tags_group->setLayout(tag_group_layout); add_tag_layout->addWidget( new QLabel( i18n("Add new tag:")) ); m_tag_edit = new KLineEdit(); QRegExp rx("[0-9a-zA-Z_\\.+-]*"); QRegExpValidator *tag_validator = new QRegExpValidator( rx, m_tag_edit ); m_tag_edit->setValidator(tag_validator); add_tag_layout->addWidget(m_tag_edit); del_tag_layout->addWidget( new QLabel( i18n("Remove tag:")) ); m_deltag_combo = new KComboBox(); m_deltag_combo->setEditable(false); QStringList tags = m_pv->getTags(); for(int x = 0; x < tags.size(); x++) m_deltag_combo->addItem( tags[x] ); m_deltag_combo->insertItem(0, QString("")); m_deltag_combo->setCurrentIndex(0); del_tag_layout->addWidget(m_deltag_combo); layout->addWidget(m_tags_group); enableButtonOk(false); connect(m_allocation_box, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_mda_box, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_uuid_box, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_tags_group, SIGNAL(clicked()), this, SLOT(resetOkButton())); connect(m_deltag_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(resetOkButton())); connect(m_tag_edit, SIGNAL(userTextChanged(QString)), this, SLOT(resetOkButton())); } void PVChangeDialog::resetOkButton() { enableButtonOk(false); if( m_allocation_box->isChecked() != m_pv->isAllocatable() ) enableButtonOk(true); if( m_mda_box->isChecked() != m_pv->getMdaUsed() ) enableButtonOk(true); if( m_uuid_box->isChecked() ) enableButtonOk(true); if( ( m_deltag_combo->currentIndex() || ( !m_tag_edit->text().isEmpty() ) ) && m_tags_group->isChecked() ) enableButtonOk(true); } QStringList PVChangeDialog::arguments() { QStringList args; args << "pvchange"; if( m_allocation_box->isChecked() && !m_pv->isAllocatable() ) args << "--allocatable" << "y"; else if( !m_allocation_box->isChecked() && m_pv->isAllocatable() ) args << "--allocatable" << "n"; if( m_mda_box->isChecked() && !m_pv->getMdaUsed() ) args << "--metadataignore" << "n"; else if( !m_mda_box->isChecked() && m_pv->getMdaUsed() ) args << "--metadataignore" << "y"; if( m_uuid_box->isChecked() ) args << "--uuid"; if( m_tags_group->isChecked() ){ if( m_deltag_combo->currentIndex() ) args << "--deltag" << m_deltag_combo->currentText(); if( !m_tag_edit->text().isEmpty() ) args << "--addtag" << m_tag_edit->text(); } args << m_pv->getName(); return args; } kvpm-0.8.6/kvpm/removemirror.cpp0000644000175000017500000000624611733530416017152 0ustar benscottbenscott/* * * * Copyright (C) 2008, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "removemirror.h" #include #include #include "logvol.h" #include "misc.h" #include "processprogress.h" #include "volgroup.h" bool remove_mirror(LogVol *logicalVolume) { RemoveMirrorDialog dialog(logicalVolume); dialog.exec(); if(dialog.result() == QDialog::Accepted){ ProcessProgress remove_mirror( dialog.arguments() ); return true; } else{ return false; } } RemoveMirrorDialog::RemoveMirrorDialog(LogVol *logicalVolume, QWidget *parent): KDialog(parent), m_lv(logicalVolume) { NoMungeCheck *temp_check; QStringList pv_names; m_vg = m_lv->getVg(); QList lvs = m_lv->getChildren(); setWindowTitle( i18n("Remove mirrors") ); QWidget *dialog_body = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout; dialog_body->setLayout(layout); setMainWidget(dialog_body); QLabel *message = new QLabel( i18n("Select the mirror legs to remove:") ); layout->addWidget(message); for(int x = lvs.size() - 1; x >= 0 ;x--){ if( lvs[x]->isMirrorLeg() ){ temp_check = new NoMungeCheck( lvs[x]->getName() ); pv_names = lvs[x]->getPvNamesAll(); temp_check->setAlternateTextList( pv_names ); m_mirror_leg_checks.append(temp_check); layout->addWidget(temp_check); connect(temp_check, SIGNAL(stateChanged(int)), this ,SLOT(validateCheckStates(int))); } } } /* Here we create a string based on all the options that the user chose in the dialog and feed that to "lvconvert" */ QStringList RemoveMirrorDialog::arguments() { int mirror_count = m_mirror_leg_checks.size(); QStringList args; QStringList legs; // mirror legs (actually pv names) being deleted for(int x = 0; x < m_mirror_leg_checks.size(); x++){ if( m_mirror_leg_checks[x]->isChecked() ){ legs << m_mirror_leg_checks[x]->getAlternateTextList(); mirror_count--; } } args << "lvconvert" << "--mirrors" << QString("%1").arg(mirror_count - 1) << m_lv->getFullName() << legs; return args; } /* One leg of the mirror must always be left intact, so we make certain at least one check box is left unchecked. The unchecked one is disabled. */ void RemoveMirrorDialog::validateCheckStates(int) { int check_box_count = m_mirror_leg_checks.size(); int checked_count = 0; for(int x = 0; x < check_box_count; x++){ if( m_mirror_leg_checks[x]->isChecked() ){ checked_count++; } } if( checked_count == (check_box_count - 1) ){ for(int x = 0; x < check_box_count; x++){ if( !m_mirror_leg_checks[x]->isChecked() ){ m_mirror_leg_checks[x]->setEnabled(false); } } } else{ for(int x = 0; x < check_box_count; x++){ m_mirror_leg_checks[x]->setEnabled(true); } } } kvpm-0.8.6/kvpm/maxfs.h0000644000175000017500000000111711733530416015175 0ustar benscottbenscott/* * * * Copyright (C) 2011 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef MAXFS_H #define MAXFS_H class LogVol; class StoragePartition; class StorageDevice; bool max_fs(LogVol *logicalVolume); bool max_fs(StoragePartition *partition); bool max_fs(StorageDevice *device); #endif kvpm-0.8.6/kvpm/vgchange.h0000644000175000017500000000251011733530416015637 0ustar benscottbenscott/* * * * Copyright (C) 2011, 2012 Benjamin Scott * * This file is part of the Kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #ifndef VGCHANGE_H #define VGCHANGE_H #include #include #include #include #include #include #include class VolGroup; bool change_vg(VolGroup *const VolumeGroup); class VGChangeDialog : public KDialog { Q_OBJECT VolGroup *m_vg; QString m_vg_name; QRadioButton *m_normal, *m_contiguous, *m_anywhere, *m_cling, *m_available_yes, *m_available_no, *m_polling_yes, *m_polling_no; QCheckBox *m_resize, *m_clustered, *m_refresh, *m_uuid; KComboBox *m_extent_size_combo, *m_extent_suffix_combo; QGroupBox *m_limit_box, *m_lvlimit_box, *m_pvlimit_box, *m_available_box, *m_polling_box; QSpinBox *m_max_lvs_spin, *m_max_pvs_spin; public: explicit VGChangeDialog(VolGroup *const volumeGroup, QWidget *parent = 0); QStringList arguments(); private slots: void limitExtentSize(int index); void resetOkButton(); }; #endif kvpm-0.8.6/kvpm/pvreduce.cpp0000644000175000017500000000174011733530416016231 0ustar benscottbenscott/* * * * Copyright (C) 2009, 2011 Benjamin Scott * * This file is part of the kvpm project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3, as * published by the Free Software Foundation. * * See the file "COPYING" for the exact licensing terms. */ #include "pvreduce.h" #include #include #include #include "processprogress.h" // Returns new pv size in bytes or 0 if no shrinking was done // Takes new_size in bytes. long long pv_reduce(QString path, long long new_size) { QStringList arguments; QString size_string; arguments << "pvresize" << "--setphysicalvolumesize" << QString("%1m").arg( new_size / ( 1024 * 1024 ) ) << path; ProcessProgress pv_shrink(arguments); if( pv_shrink.exitCode() ) return 0; else return new_size; } kvpm-0.8.6/COPYING0000644000175000017500000010451311733530416013770 0ustar benscottbenscott GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . kvpm-0.8.6/kvpm.desktop0000644000175000017500000000027111733530416015301 0ustar benscottbenscott[Desktop Entry] GenericName=LVM Frontend Name=KVPM Comment=KDE Volume Partition Manager Exec=/usr/sbin/kvpm X-KDE-SubstituteUID=true Type=Application Categories=Qt;KDE;System; Icon=kvpmkvpm-0.8.6/icons/0000755000175000017500000000000011733530416014044 5ustar benscottbenscottkvpm-0.8.6/icons/app/0000755000175000017500000000000011733530416014624 5ustar benscottbenscottkvpm-0.8.6/icons/app/hi32-app-kvpm.png0000644000175000017500000000446311733530416017637 0ustar benscottbenscottPNG  IHDR szzsRGBbKGD pHYs  tIME3m3IDATXGW}lݻegll q$PpLJ"(D(MIUBBDZZD*%䏦%AiSJ-IhJ|R@}>w{{ۙ9󡐦R4v߾fo~ܸݻJ:}[8ArA>eMb``*.*۪ u _ՀTXg( b1xWw){jeYf6cqȄ It@ZU8(). |f%EQyg9zO[-S+EKj  ,Ri` ̘WUM#ը~¡;xF.g`_;'h|T&6cnܰw^mk͑b+uR~0(2Ťia:Vnff6>iJmzuGGU=7KKs3TWV§AEfDI,#0dĀL8y|Zx ]ؚUb>LQ{yXӲP6Aӎ ]e@x7+kJ?8ADoe1n `yLȢb(ΠI:\BrFSq7U{LƣfXW ^Tjd3fx ]$sY-$#'MQށùٜ3 0TZA[_' lb|6YKD|݄n^O.={,珄 @%xD$|I$QeBQh$,1Gu]Ǽ4HG+Hb4Dt-\YDS$b::H'$c5\쪈X1'J6 )TBp#n"P(*lGXQa,t3.##xY% m˖ڧk_ڊKäJHuzN=M6icp aGsC^ ݔ;.\em|r n#B=+(f7@¨f4mKw?@04MSܷҬ~P kwzδ5[R8ѯ)v|!ZZ vybB)%#@unh8*"njC~5kyaƚAЀˌF[^):8$c` ssr> S?H 0nBTS_?`ޫrxM/̽+qm_rzg vXWY\{,$d,kf;OSyaJC%TGzploNrO}.7k2`‡jܹ+WrޖșɔZt) ē3}=ݶU[uMn3mw?ގo͙]̰S:s\1йb)n^_zUKʔj Ϫ[*ZKtmK<`JޫtYiKfWɘIENDB`kvpm-0.8.6/icons/app/CMakeLists.txt0000644000175000017500000000005211733530416017361 0ustar benscottbenscottkde4_install_icons( ${ICON_INSTALL_DIR} ) kvpm-0.8.6/icons/app/hi16-app-kvpm.png0000644000175000017500000000165611733530416017642 0ustar benscottbenscottPNG  IHDRasRGBbKGD pHYs  tIME6f].IDAT8OS[hWLvg/&lŸ AM%hU" ">(BK-ŴBR$E}CҾXVCT[^fggfwgֶtsLOf` DR(@4PJ'O'Xz]]ou,r\wղ ^` &B0_)5 !" }}tO3xoO ghrBn*}/l´m=r++vѨ84=GSo_t)AnAm.gq,  ^Cf#dܺQ!d!8s¡^[F B(Ղy(J,۫j<~Z3LZU4 a:+l:&KV8_yZ\%#+'A]{^P!H)Xzn|SE出d3nVQ&`b* DALaIEBNb7jqmbZkeվ3}[waxg'蕓 9dmv h-_GPqFIENDB`kvpm-0.8.6/icons/app/hi64-app-kvpm.png0000644000175000017500000001267111733530416017644 0ustar benscottbenscottPNG  IHDR@@iqsRGBbKGD pHYs  tIME2`9IDATx{Uy̞=L&L2rar+ I$ArQ\ǣmss,==BVmmJiU܅ *@@`B21 dZ3$83I 1|ٟ~ky< 8;'pP{5_s>8kA dhxBD \&Ri=S)RZC)==tpPȣA3:p(<&p ARs[NKߕI!ry5kÐC󼺃[r~;y(f}߯9>иSloYqٖHkc| /oyqR38>\sL紫O;ycR>(a3Q#wrz³/:Duz2ZX>Xzljy闞wSr"3kkqڵ8@Nh2`ɚO} .e89ӳ M(|1w@~ɥ}W\xz{N?NvZpƸJM~ Ƌ182X، !w @]u'xYΜypa CԖw%;n/ :lJp t嵟VG;c{M8|0]w{^MS}826x7X3pQkyg!R.29w8wu@͌իtڷNZ͔ZsKaX[l!G֕O=㡅f1aX91 ydRdEK׵NmtB)8wxɎ[MGF>8/>iZG%(xq|ғV-d<1~JXw8&1v-"81H!pΔ2SSbVLr':D&%"P$X* qlsrPB%Jgjhiir#,>\vل>Zk]dB([S[|~S #X4,G|ss Ŕ+o|`ݑH'3uyN.Wu129sk ֍pad3 n|)y Щ{6sϽwF\-u"SijVeR R`ZzhBI 5ZjҤd"<4Pң&fW \օ%dq9LlD2q`f% (Rh 5J LO`U[KH@}m8㔓@+Ny(Z!U %TT*ah<3qF6ISGNV.@ 3BD{QHBkO{Ac> ĆWX}{7PŨbz\WzxHl""m&Dj)%RDF<iQiƟ,>k#-<l1XpBf!ضMI`%}I‘Lh6ruu# ێGhҺ[a RUi+ 3IAI8 kv'g?NԾu/ lWO_u<;BP0n:F,uQtpVP ?BNGRb`4e~.~_Lh 6-|u{EPqRv_<9.vۉ nb, 6ɷd0Tm4ڤ~m&}I@? soH?Cn}wxj3)?ܲkOW0~*٣y.đKc/3uƔb"MjIo$Ì>Ǎc~ϼIEDA̮.nuW_{՝n3ƭɾB2^OW=s?amXpդd=]@ua) 2891e߮Zi6aI$ShoXF8'hnʓ̚=\Q͍_Psտw~܅īNݤy_?OYt: J1&."Y5vtP屚OCkf5CGFlq\m$c =dT*OSc=u1s]|7kKq֥o冋 S.~oo_*?o"/n߻p=P8.?ſlvGW T S(Jyo"!<5(dIj΂1f='DGGY0D+>#29ڧ6j޳bL-P(6w?ȭK=⫟/?AwtJ}ߞ=ۊ}ݝ5./μ/>K]sXnJAp$t e%)ۯ^f%+28B d5%36Y$g %ʼV3{J6=,]M 9aA6`b[Ƕ^ m⸹M/Vb#66z6o0=?W?'/Z\<ſ0A&W~?fg~֮x wxb N]Mw 4sw>m1[i̴<[j@Ho뗷ӻahji|ccG6Bzh}aY=}žO]fϒٹPD}9'+c?ﶤ񵛦;z2ݬ\|B5g:^i櫟=͛.Mo',LL&|SK`~g)rBwyqgG}x.z-w4ؔM{G/kֻbحvKoz}^շIf6OhfMkgl:h7EvB -d=5/w+՜Ʋ7kaIENDB`kvpm-0.8.6/icons/local/0000755000175000017500000000000011733530416015136 5ustar benscottbenscottkvpm-0.8.6/icons/local/hi16-action-lightning_add.png0000644000175000017500000000135211733530416022460 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<|IDAT8˥RKSa>¶!QL!yQZE&VPQ#N헔]Xh%5'HDs in͹휭Cv/<>ϗ ò1gȆw`a[<_=po>ת1_ bK//>Gt ~:7X, \)ê|pBĮT%;"Řgn5Eƈ0t9\d)q 3ļ-Ok:Mp*xpie&YራD.Bc fҽXZ0g,"Ƽ  9y$;5{yGw?׵Om;7)H@11mN/qa YIU*m>~]#2R=E EUZMA4xܣ/[W{.-kygY NvtK0יrn>ƶh\$ibb*OwAW&vvԌDXA]%0 IENDB`kvpm-0.8.6/icons/local/hi16-action-arrow_divide.png0000644000175000017500000000124511733530416022344 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<7IDAT8˥MHqƟuU}_) #<)H.iQJ:`y5 (:54CJ62wFfDp8a 3<b-2JzU"l78e+ng㍽@Lq6o$B1U9U%Si$Tճ*ZTFLBB@U f#р uv*}[+`kA+ #=U>r.)8UȝвZCHJP cBB,A avJ\]_@HY0s P^Z~uwP*@%fR)3X2RCͤ TTb oރQXPALMOB9:q|sf$ٺ|[R > m9>X{.ڱ.7ob1x&kx>2}K.b`V}fU(4/`CC#Jo ]IENDB`kvpm-0.8.6/icons/local/LICENSE0000644000175000017500000005042611733530416016152 0ustar benscottbenscottAttribution-ShareAlike 3.0 Unported License THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. 1. Definitions "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License. "Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; to Distribute and Publicly Perform the Work including as incorporated in Collections; and, to Distribute and Publicly Perform Adaptations. For the avoidance of doubt: Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. 5. Representations, Warranties and Disclaimer UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. Termination This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 8. Miscellaneous Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. Creative Commons Notice Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License. Creative Commons may be contacted at http://creativecommons.org/. kvpm-0.8.6/icons/local/hi16-action-resultset_last.png0000644000175000017500000000101411733530416022735 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8œkQ7; *,E/A^TI%XX,,?H6ɨA QL&q>X&_q81)%v]j1W٫G ;E)}z>wOu{G%lj٘=*l__v$>lIENDB`kvpm-0.8.6/icons/local/hi16-action-delete.png0000644000175000017500000000131311733530416021124 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<]IDAT8˥KSa[nQP2wܦγL[,biaA\Cv_2MlZFjסNMjmkʷ`&.#z<ϓ bVPT3%I{GqRivȅ tz#E6EddJ`DR2<]N ;4Ѿ;m>78ɀQe6LIt殷cq!z |v j/Xi@ %1|hl !|! Y#uUNw]˼ H3u t]E>k%IfoRD:0`~ | (r on3oG0!$V *[W0_-+ dW&2ZfMFVJpiF&B > Rg- ~ CmڴER ឫ p5ްy+21Kawh` #aZ񽞆TZoLѓ`"(?'ˎJvKކ|:G9[aw82 Jw f'ymzsӘTsw__ιIrIENDB`kvpm-0.8.6/icons/local/hi16-action-camera_add.png0000644000175000017500000000144011733530416021723 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˥S_HSQ{kst+Rp(?0ꡠ-(z^"꥚`=hO0B(jl,ԩsss۝\Qz+|}0]?KԽrVuZU*(f5McEFyHӛ, E1 _cP\ײc"\z#]]]v˅T*UlllӁ+DOi0ưrY,ː$3u=jD: (p:FBp8HrFN,;.ĵ5466Z9Y g[a/H~\%'& nߊ"Z[[1MEE f5_1dUUA䀹xrvLD@E6U7&dyPUK׵]2P*?W@¬aG! *P!\CnX.VpGZ1[)8~Gb6PyT Rj6$ޝwqdhk3r ~8ĉ?ZC{~ĠϏd|uM&I-..2^ {kĬyPvg)MlLF1o(W.wv?ěE ^) 4]{DJFg3:ٿ>[n Ʌ'㉦:IENDB`kvpm-0.8.6/icons/local/hi16-action-bullet_star.png0000644000175000017500000000051311733530416022203 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8c?%aRӷsnNdogOd?moq=KQk|@nN" HFzݦr23 p=hJ hB?nf&* ~~{ެv]_Agcoz@A^9!HŎĤL +PeIENDB`kvpm-0.8.6/icons/local/hi16-action-lorry.png0000644000175000017500000000110611733530416021031 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8œKa(F}")UD!6@A BQP.B ZTza@HBi_-u Zn=yfFrz 3LPTSEӧ0ޮc#DaNn)aTwe"a& ԄkbSNtq05LTttfVDw?jS\=?yP[Zm5d M%C׬lAEI+3͇qq K e }[jCtnX́]e$ Mz1(ARIV'%LmݍĵH޵؀*hD]* @vpܝ' JDf_?J/|yQzS]}#t>§mS/>g/3 e@T*a` KE?*{7lߏi?lCIENDB`kvpm-0.8.6/icons/local/CMakeLists.txt0000644000175000017500000000006711733530416017701 0ustar benscottbenscottkde4_install_icons( ${DATA_INSTALL_DIR}/kvpm/icons ) kvpm-0.8.6/icons/local/hi16-action-error_go.png0000644000175000017500000000133611733530416021505 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<pIDAT8ˍS]Ha~}fY*٢Y,,7gDЯ7ݔ0n4B$4HB ,S(Q("Ĵ[m..7}EÁ<<103!ܵĊ?2,:ңXqM1皬Vt+Et)a1KþɸIuvohfyʊW ]2QX-bEIb9ZX_Lt߲H2myzO#袉g.oJ˙b`*]SM*pY0@A$@fY&@SS3g0fC\Cm'jY$D1d_ ۸7ABJd(׃WՈܾnfX[ZjטP$ rb"Dan9" zI?*4m(*Mϯ9Kń0tCW:QQ}?Fhԭfq- tC*vHRPLLjɷES0VBΰ""$r Aq)9Q8w=|FgXƮzchApXj5l1R)_2vDIENDB`kvpm-0.8.6/icons/local/hi16-status-lightbulb_off.png0000644000175000017500000000127411733530416022544 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<NIDAT8˕OPƽ?tW{rf좭V.nb˙EZ+Դ2 KKL$OTDDRAD^< ,9;{s%rό-|r}ˉdjvLz\.7j+1cIDHsօ6s5vFH&7iP*<4Ҏ]v&RXŚHg32)x$ ӝy@ݸݹP|kkDwt#<@7=ōC ˎ+,AIA&fze#1Ыwƍ/*Za2hJf,vHpmwq; Юrb8^)qE~G' y5P ~QСҠsP)jD3oMI:߳eo?7Z a 9/ZE_"4( ^~k5u?n*ME'1'jx*;nPv%T/j es U-V6RF]}=xebE+==XB %U'eKK%cG$՗ A (>hEIENDB`kvpm-0.8.6/icons/local/hi16-status-exclamation.png0000644000175000017500000000127511733530416022243 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<OIDAT8˥Ka SAoc6.PDC Dբ@Zxh1N"CN:vf}󶰾6uvyy1=!4ИvCv$ HS8d*P. M`+Sذnv~cHVr}ފOKW55Bb `l`yj`yk1AjE @EoqS2FE64(l=$6#1![?@Dw$-WfW&D$Fn>SRJuZNWc#di@% b_s Rۆ^t&:?!DmSQeJWeJÈqMT 'DB:RE_as3ȯC2Vz9W[9ŢwU*B4!B|zPJ fVcdEmZVw!Opřzp!MuS>x9f0Uއx8GHv=}uʡGBy=-Ka J8K+${?`vLЉ37ӿѯLjIENDB`kvpm-0.8.6/icons/local/hi16-action-cross.png0000644000175000017500000000121711733530416021016 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<!IDAT8˕NQɉϠVȑB[( &^M6M| Dްҙv:官J-%Nd LB>w_3:*WrlNC/-좕B'{ u_a46ҽbߡE%D47;ٻƩ;8ˣ}>6[ӕS@*Z Qk>~͵hB\9uxZvYb J Cيٽ?BYvn&kft$,d9Zap\^ Y7 QJF 9=Q4 ؜Io SBpsI) Fv(@yՎވc\@ %% Z2h'@d(<|áaJuM@O⤁LGjd!X8Af 5J i K->w62ƾWH}:mP]XB0QX=ib_g=!Ftt…clrIENDB`kvpm-0.8.6/icons/local/hi16-action-lightning_delete.png0000644000175000017500000000135111733530416023171 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<{IDAT8˥SKHTQ;w&s,u6:**Z$d""m$*hodJ1zZ#|̨=)::sN:8Ms$!g)m)*88Y3n"n`lCtkp kIv9WM2#+=m3:own? }v$s%y{GE, ȹ)EK<#Y(=JQÃXpધai)HUɪ{U|npYP<Zt'SVs-_)[uUCWGְJ B%M0t48KC/uvԚ: oi3l}Dm {,y mp,46V^-)M@oޮSՊZHYycQFEM#}ėwXpb͖1Ep#4 ߅@r";{!u g%DIڵ*C806Bb{/Pe KJ9v(e-SC<)}OD;$S/`H \9Ew@| B yڎ;^+x߼ڋZyx^+_7FIENDB`kvpm-0.8.6/icons/local/hi16-status-lightbulb.png0000644000175000017500000000141611733530416021710 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕KSq}ћHoLL܋"솒!PRtR&$^22p31RiSy7񶔍m^v6n9~.ZZ;MIENDB`kvpm-0.8.6/icons/local/hi16-action-add.png0000644000175000017500000000133511733530416020416 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<oIDAT8˥Ka[/Y()%X(olNۖskn.-h;8fEP"jïMGˈ}yພ羹$I.tulu AX:𼂒ZHh1DnZJOJB{Z?`2`S=N$ő=;a &jw qJG#<"N2h8޵`6xցn_+ ~Zto}`x%XЛ͈ hXѿƻ/}BJ_G&|Qr-6Aރ EL⬡\U3:WUh[C6+ 6.f *K͸ܝFq ou4܄?d|XҥMvD` *_[ #A20liR|xq`4w=\uQ m+G|%$5Թ5RO*YGMUO Gqj4ְ(X& s1c˭(LVf RdjQ '-1ATA>U j4,pV"4L$e@.ArBY a~myY])Q8tNLܞt2"I o=CSd)__AF(IENDB`kvpm-0.8.6/icons/local/hi16-action-wrench.png0000644000175000017500000000114211733530416021150 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕oQ_[c+W.]хi5  g mJ[Fq#_)qո;,@{;upLTʙH$(Z XMɤni"\nt:}yD 0 \ ږIU4(r HMk̈_4_ziy'"[ n1rM_A`b=$Ik_p-qS~=li~3Bv"qZAԧ̸r[G]<&e!'ڸ67 yq$OX!=_~1Gs~EZQx&qWK3!ޤunkzGrjQnIENDB`kvpm-0.8.6/icons/local/hi16-action-arrow_join.png0000644000175000017500000000116211733530416022035 0ustar benscottbenscottPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˥RKq~ fkr[[ I.˓%];'"ک? F}+XMwV)Q}yep|'87>;z{.-E$n Ǻ:Fz- {@%3J𺽠}tp ?dw6֊ҏٱWZy#ʣT\73'|nG ' 1qTLL<{(hu K%$ZS[wH޿;A["D`G 93G(o l&޻pR!k _ϋDvG] \(bJ(}4UcA j"%\|k g璙l@0V;HJRһ]C)NT}*ʼn\Ά@@(ZC(BNɗK][v@ YDbF"$aM\3~TZ4r>~'Kg>IENDB`kvpm-0.8.6/icons/svn-commit.tmp~0000644000175000017500000000010511733530416017054 0ustar benscottbenscott --This line, and those below, will be ignored-- A local/LICENSE kvpm-0.8.6/kvpm.10000644000175000017500000000236611733530416013777 0ustar benscottbenscott.TH KVPM "1" "March 2012" KDE "KDE Application" .SH NAME kvpm \- Graphical user interface for LVM based on KDE .SH SYNOPSIS .B kvpm [\fIQt-options\fR] [\fIKDE-options\fR] .SH DESCRIPTION This manual page briefly documents the .B KVPM KDE Application. .P .B KVPM is a graphical user interface for the Linux Volume Manager (LVM) and libparted. It uses the standard LVM tools and programs to manipulate logical volumes, such as resizing, deleting or creating them. It can also format volumes and mount or unmount them. Creating, deleting and resizing partitions is also supported. .SS "Generic options:" .TP \fB\-\-help\fR Show help about options .TP \fB\-\-help\-qt\fR Show Qt specific options .TP \fB\-\-help\-kde\fR Show KDE specific options .TP \fB\-\-help\-all\fR Show all options .TP \fB\-\-author\fR Show author information .TP \fB\-v\fR, \fB\-\-version\fR Show version information .TP \fB\-\-license\fR Show license information .TP \fB\-\-\fR End of options .SH SEE ALSO Full user documentation is available through the KDE Help Center. You can also enter the URL .BR help:/kvpm/ directly into konqueror or you can run .BR "`khelpcenter help:/kvpm/'" from the command-line. .br .SH AUTHOR KVPM is copyright by .nh Benjamin Scott .hy .br kvpm-0.8.6/po/0000755000175000017500000000000011733530416013347 5ustar benscottbenscottkvpm-0.8.6/po/kvpm.pot0000644000175000017500000012756311733530416015066 0ustar benscottbenscott# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-03-17 22:15-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: changemirror.cpp:42 msgid "Change Mirror" msgstr "" #: changemirror.cpp:47 msgctxt "Common user options" msgid "General" msgstr "" #: changemirror.cpp:48 lvcreate.cpp:60 lvcreate.cpp:94 msgid "Physical layout" msgstr "" #: changemirror.cpp:63 msgid "Volume: %1" msgstr "" #: changemirror.cpp:76 msgid "Add mirror legs" msgstr "" #: changemirror.cpp:83 msgid "Existing mirror legs: %1" msgstr "" #: changemirror.cpp:89 msgid "Add mirror legs: " msgstr "" #: changemirror.cpp:101 msgid "Mirror logging" msgstr "" #: changemirror.cpp:103 lvcreate.cpp:377 msgid "Memory based log" msgstr "" #: changemirror.cpp:104 lvcreate.cpp:376 msgid "Disk based log" msgstr "" #: changemirror.cpp:105 lvcreate.cpp:375 msgid "Mirrored disk based log" msgstr "" #: changemirror.cpp:134 vgchange.cpp:61 msgid "Allocation policy" msgstr "" #: changemirror.cpp:136 lvchange.cpp:151 lvcreate.cpp:244 pvmove.cpp:293 #: vgchange.cpp:69 msgctxt "The usual way" msgid "Normal" msgstr "" #: changemirror.cpp:137 lvchange.cpp:152 lvcreate.cpp:245 pvmove.cpp:294 #: vgchange.cpp:70 msgid "Contiguous" msgstr "" #: changemirror.cpp:138 lvchange.cpp:153 lvcreate.cpp:246 pvmove.cpp:295 msgid "Anywhere" msgstr "" #: changemirror.cpp:139 lvchange.cpp:155 lvcreate.cpp:247 msgctxt "Inherited from the parent group" msgid "Inherited" msgstr "" #: changemirror.cpp:140 lvchange.cpp:154 lvcreate.cpp:248 pvmove.cpp:298 #: vgchange.cpp:72 msgid "Cling" msgstr "" #: changemirror.cpp:193 lvcreate.cpp:299 msgid "Disk striping" msgstr "" #: changemirror.cpp:205 lvcreate.cpp:314 msgid "Stripe Size: " msgstr "" #: changemirror.cpp:212 lvcreate.cpp:320 msgid "Number of stripes: " msgstr "" #: changemirror.cpp:229 msgid "" "The number of extents: %1 must be evenly divisible by the number of stripes" msgstr "" #: deviceactionsmenu.cpp:69 lvactionsmenu.cpp:137 msgid "Filesystem operations" msgstr "" #: deviceactionsmenu.cpp:70 msgid "Extend volume group" msgstr "" #: deviceactionsmenu.cpp:72 msgid "Move or resize disk partition" msgstr "" #: deviceactionsmenu.cpp:73 msgid "Extend physical volume to fill device" msgstr "" #: deviceactionsmenu.cpp:74 msgid "Add disk partition" msgstr "" #: deviceactionsmenu.cpp:75 msgid "Remove disk partition" msgstr "" #: deviceactionsmenu.cpp:76 msgid "Create volume group" msgstr "" #: deviceactionsmenu.cpp:77 msgid "Create or remove a partition table" msgstr "" #: deviceactionsmenu.cpp:78 pvtree.cpp:210 msgid "Remove from volume group" msgstr "" #: deviceactionsmenu.cpp:80 msgid "Mount filesystem" msgstr "" #: deviceactionsmenu.cpp:81 msgid "Unmount filesystem" msgstr "" #: deviceactionsmenu.cpp:82 msgid "Extend filesystem to fill partition" msgstr "" #: deviceactionsmenu.cpp:83 msgid "Run 'fsck -fp' on filesystem" msgstr "" #: deviceactionsmenu.cpp:84 msgid "Make filesystem" msgstr "" #: deviceactionsmenu.cpp:85 msgid "Remove filesystem" msgstr "" #: deviceactionsmenu.cpp:124 msgid "Extend physical volume to fill partition" msgstr "" #: deviceproperties.cpp:95 msgid "First sector: %1" msgstr "" #: deviceproperties.cpp:98 msgid "First aligned: %1 (to 1 MiB)" msgstr "" #: deviceproperties.cpp:101 msgid "Last sector: %1" msgstr "" #: deviceproperties.cpp:106 msgid "Flags: %1" msgstr "" #: deviceproperties.cpp:125 lvproperties.cpp:100 msgid "Mount point" msgstr "" #: deviceproperties.cpp:127 lvproperties.cpp:95 msgid "Mount points" msgstr "" #: deviceproperties.cpp:150 msgid "Not mounted" msgstr "" #: deviceproperties.cpp:175 msgid "Partition table: %1" msgstr "" #: deviceproperties.cpp:177 msgid "Logical sector size: %1" msgstr "" #: deviceproperties.cpp:178 msgid "Physical sector size: %1" msgstr "" #: deviceproperties.cpp:179 msgid "Sectors: %1" msgstr "" #: deviceproperties.cpp:182 msgctxt "May be read and not written" msgid "Read only" msgstr "" #: deviceproperties.cpp:184 msgid "Read/write" msgstr "" #: deviceproperties.cpp:187 msgid "Busy: Yes" msgstr "" #: deviceproperties.cpp:189 msgid "Busy: No" msgstr "" #: deviceproperties.cpp:204 msgid "Filesystem LABEL" msgstr "" #: deviceproperties.cpp:213 lvproperties.cpp:181 msgid "Filesystem UUID" msgstr "" #: deviceproperties.cpp:234 msgid "Physical volume" msgstr "" #: deviceproperties.cpp:239 msgid "State: active" msgstr "" #: deviceproperties.cpp:241 msgid "State: inactive" msgstr "" #: deviceproperties.cpp:244 msgid "UUID" msgstr "" #: deviceproperties.cpp:264 msgid "Hardware" msgstr "" #: devicesizechartseg.cpp:122 msgid "Device: %1" msgstr "" #: devicetree.cpp:46 msgid "The device name" msgstr "" #: devicetree.cpp:47 msgid "The type of partition" msgstr "" #: devicetree.cpp:48 msgid "The amount of storage space" msgstr "" #: devicetree.cpp:49 msgid "The remaining storage space" msgstr "" #: devicetree.cpp:50 msgid "How the device is being used" msgstr "" #: devicetree.cpp:51 msgid "The Name of the volume group if the device is a physical volume" msgstr "" #: devicetree.cpp:52 msgid "Any flags associated with device" msgstr "" #: devicetree.cpp:53 msgid "Mount points of the filesystem if it is mounted" msgstr "" #: devicetree.cpp:144 devicetree.cpp:241 devicetree.cpp:291 msgid "Physical volume that is running out of space" msgstr "" #: devicetree.cpp:149 devicetree.cpp:246 devicetree.cpp:296 msgid "Physical volume with active logical volumes" msgstr "" #: devicetree.cpp:153 devicetree.cpp:250 devicetree.cpp:300 msgid "Physical volume without active logical volumes" msgstr "" #: devicetree.cpp:257 devicetree.cpp:307 msgid "Filesystem that is running out of space" msgstr "" #: devicetree.cpp:261 devicetree.cpp:311 vgtree.cpp:247 msgid "mounted filesystem" msgstr "" #: devicetree.cpp:265 devicetree.cpp:315 vgtree.cpp:261 msgid "unmounted filesystem" msgstr "" #: devicetree.cpp:271 devicetree.cpp:321 vgtree.cpp:252 msgid "Active swap area" msgstr "" #: devicetree.cpp:275 devicetree.cpp:325 vgtree.cpp:256 msgid "Inactive swap area" msgstr "" #: fsck.cpp:46 msgid "Run 'fsck -fp' to check the filesystem on volume %1?" msgstr "" #: fsck.cpp:65 msgid "Run 'fsck -fp' to check the filesystem on partition %1?" msgstr "" #: fsextend.cpp:57 msgid "Executable: '%1' not found, this filesystem cannot be extended" msgstr "" #: fsextend.cpp:85 msgid "only logical volumes may be mounted during resize, not partitions" msgstr "" #: fsextend.cpp:89 msgid "" "It appears that the filesystem on the device or volume was not extended. It " "will need to be extended before the additional space can be used." msgstr "" #: fsextend.cpp:153 fsextend.cpp:170 msgid "Error number: %1 %2" msgstr "" #: fsreduce.cpp:35 msgid "Executable: '%1' not found, this filesystem cannot be reduced" msgstr "" #: fsreduce.cpp:70 msgid "Shrink failed: could not determine filesystem block size" msgstr "" #: kvpmconfigdialog.cpp:46 lvcreate.cpp:59 lvcreate.cpp:93 msgctxt "The standard common options" msgid "General" msgstr "" #: kvpmconfigdialog.cpp:47 msgid "Colors" msgstr "" #: kvpmconfigdialog.cpp:48 msgid "Programs" msgstr "" #: kvpmconfigdialog.cpp:59 msgid "Tree Views" msgstr "" #: kvpmconfigdialog.cpp:60 msgid "Property Panels" msgstr "" #: kvpmconfigdialog.cpp:69 msgid "Set columns to show in tables and tree views" msgstr "" #: kvpmconfigdialog.cpp:89 msgid "Set information to show in property panels" msgstr "" #: kvpmconfigdialog.cpp:142 msgid "Volume and Partition Colors" msgstr "" #: kvpmconfigdialog.cpp:145 msgid "" "These are the colors used to show the filesystem type \n" "on a partition or volume in any graphical display. Colors\n" "may also be selected for swap and unpartitioned space." msgstr "" #: kvpmconfigdialog.cpp:268 msgid "Program name" msgstr "" #: kvpmconfigdialog.cpp:268 msgid "Full path" msgstr "" #: kvpmconfigdialog.cpp:293 msgid "Set the search path for support programs" msgstr "" #: kvpmconfigdialog.cpp:366 msgid "Device Tree" msgstr "" #: kvpmconfigdialog.cpp:391 msgid "Device name" msgstr "" #: kvpmconfigdialog.cpp:392 msgid "Partition type" msgstr "" #: kvpmconfigdialog.cpp:393 msgid "Capacity" msgstr "" #: kvpmconfigdialog.cpp:394 kvpmconfigdialog.cpp:468 kvpmconfigdialog.cpp:541 msgid "Remaining space" msgstr "" #: kvpmconfigdialog.cpp:395 msgid "Usage of device" msgstr "" #: kvpmconfigdialog.cpp:396 msgid "Volume group" msgstr "" #: kvpmconfigdialog.cpp:397 msgid "Partition flags" msgstr "" #: kvpmconfigdialog.cpp:398 kvpmconfigdialog.cpp:550 msgid "Mount point" msgstr "" #: kvpmconfigdialog.cpp:399 msgid "Expand device tree" msgstr "" #: kvpmconfigdialog.cpp:411 kvpmconfigdialog.cpp:484 msgid "Show the path to the device, /dev/sda1 for example." msgstr "" #: kvpmconfigdialog.cpp:412 msgid "Show the type of partition, 'extended' for example." msgstr "" #: kvpmconfigdialog.cpp:413 kvpmconfigdialog.cpp:485 kvpmconfigdialog.cpp:566 msgid "Show the storage capacity in megabytes, gigabytes or terabytes." msgstr "" #: kvpmconfigdialog.cpp:414 kvpmconfigdialog.cpp:486 kvpmconfigdialog.cpp:567 msgid "Show the remaining storage in megabytes, gigabytes or terabytes." msgstr "" #: kvpmconfigdialog.cpp:415 msgid "" "Show how the partition is being used. Usually the type of filesystem, such " "as ext4, \n" "swap space or as a physical volume." msgstr "" #: kvpmconfigdialog.cpp:417 msgid "" "If the partition is a physical volume this column shows the volume group it " "is in." msgstr "" #: kvpmconfigdialog.cpp:418 msgid "Show any flags, such a 'boot.'" msgstr "" #: kvpmconfigdialog.cpp:419 kvpmconfigdialog.cpp:578 msgid "Show the mount point if the partition has a mounted filesystem." msgstr "" #: kvpmconfigdialog.cpp:420 msgid "" "This determines if all partitions of all devices get shown at start up. \n" " The user can still expand or collapse the items by clicking on them." msgstr "" #: kvpmconfigdialog.cpp:432 msgid "At start up:" msgstr "" #: kvpmconfigdialog.cpp:443 msgid "Physical Volume Table" msgstr "" #: kvpmconfigdialog.cpp:466 kvpmconfigdialog.cpp:539 pvproperties.cpp:153 msgid "Volume name" msgstr "" #: kvpmconfigdialog.cpp:467 kvpmconfigdialog.cpp:540 pvtree.cpp:48 #: vgtree.cpp:32 msgid "Size" msgstr "" #: kvpmconfigdialog.cpp:469 msgid "Used space" msgstr "" #: kvpmconfigdialog.cpp:470 pvtree.cpp:50 vgtree.cpp:34 msgid "State" msgstr "" #: kvpmconfigdialog.cpp:471 pvtree.cpp:50 msgid "Allocatable" msgstr "" #: kvpmconfigdialog.cpp:472 kvpmconfigdialog.cpp:549 pvtree.cpp:51 #: vgtree.cpp:35 msgid "Tags" msgstr "" #: kvpmconfigdialog.cpp:473 msgid "Logical Volumes" msgstr "" #: kvpmconfigdialog.cpp:487 msgid "Show the used storage in megabytes, gigabytes or terabytes." msgstr "" #: kvpmconfigdialog.cpp:488 msgid "Show the state, either 'active' or 'inactive.'" msgstr "" #: kvpmconfigdialog.cpp:489 msgid "" "Shows whether or not the physical volume can have its extents allocated." msgstr "" #: kvpmconfigdialog.cpp:490 msgid "List any tags associated with the physical volume." msgstr "" #: kvpmconfigdialog.cpp:491 msgid "List any logical volumes associated with the physical volume." msgstr "" #: kvpmconfigdialog.cpp:508 msgid "Logical volume tree" msgstr "" #: kvpmconfigdialog.cpp:542 msgid "Volume type" msgstr "" #: kvpmconfigdialog.cpp:543 msgid "Filesystem type" msgstr "" #: kvpmconfigdialog.cpp:544 msgid "Stripe count" msgstr "" #: kvpmconfigdialog.cpp:545 vgtree.cpp:34 msgid "Stripe size" msgstr "" #: kvpmconfigdialog.cpp:546 msgid "(\\%)Snap/Copy" msgstr "" #: kvpmconfigdialog.cpp:547 msgid "Volume state" msgstr "" #: kvpmconfigdialog.cpp:548 msgid "Volume access" msgstr "" #: kvpmconfigdialog.cpp:565 msgid "Show the volume name." msgstr "" #: kvpmconfigdialog.cpp:568 msgid "Show the type of volume, 'mirror' or 'linear,' for example." msgstr "" #: kvpmconfigdialog.cpp:569 msgid "Show the filesystem type on the volume, 'ext3' or 'swap,' for example." msgstr "" #: kvpmconfigdialog.cpp:570 msgid "Show the number of stripes on the volume, if it is striped" msgstr "" #: kvpmconfigdialog.cpp:571 msgid "Show the size of the stripes, if it is striped" msgstr "" #: kvpmconfigdialog.cpp:572 msgid "" "For a mirror, show the percentage of the mirror synced. \n" "For a snapshot, show the percentage of the snapshot used. \n" "For a pvmove, show the percentage of the move completed." msgstr "" #: kvpmconfigdialog.cpp:575 msgid "Show the state, 'active' or 'invalid,' for example." msgstr "" #: kvpmconfigdialog.cpp:576 msgid "Show access, either read only or read and write." msgstr "" #: kvpmconfigdialog.cpp:577 msgid "List any tags associated with the volume" msgstr "" #: kvpmconfigdialog.cpp:599 msgid "All trees and tables" msgstr "" #: kvpmconfigdialog.cpp:600 msgid "Remaining and used space" msgstr "" #: kvpmconfigdialog.cpp:620 msgid "Show percentage" msgstr "" #: kvpmconfigdialog.cpp:621 msgid "Show total" msgstr "" #: kvpmconfigdialog.cpp:622 msgid "Show both" msgstr "" #: kvpmconfigdialog.cpp:636 msgid "Show warning icon" msgstr "" #: kvpmconfigdialog.cpp:641 msgid "when space falls to or below:" msgstr "" #: kvpmconfigdialog.cpp:648 kvpmconfigdialog.cpp:658 msgid "Never" msgstr "" #: kvpmconfigdialog.cpp:650 msgid "on a filesystem" msgstr "" #: kvpmconfigdialog.cpp:660 msgid "on a physical volume" msgstr "" #: kvpmconfigdialog.cpp:673 msgid "Device Properties Panel" msgstr "" #: kvpmconfigdialog.cpp:685 kvpmconfigdialog.cpp:747 vgtree.cpp:35 msgid "Mount points" msgstr "" #: kvpmconfigdialog.cpp:686 kvpmconfigdialog.cpp:748 msgid "Show the filesystem mount points for the device" msgstr "" #: kvpmconfigdialog.cpp:688 kvpmconfigdialog.cpp:750 msgid "Filesystem uuid" msgstr "" #: kvpmconfigdialog.cpp:689 kvpmconfigdialog.cpp:751 msgid "Show the filesytem UUID" msgstr "" #: kvpmconfigdialog.cpp:691 kvpmconfigdialog.cpp:753 msgid "Filesystem label" msgstr "" #: kvpmconfigdialog.cpp:692 kvpmconfigdialog.cpp:754 msgid "Show the filesystem label" msgstr "" #: kvpmconfigdialog.cpp:706 msgid "Physical Volume Properties Panel" msgstr "" #: kvpmconfigdialog.cpp:716 msgid "Metadata areas" msgstr "" #: kvpmconfigdialog.cpp:717 msgid "Show information about physical volume metadata" msgstr "" #: kvpmconfigdialog.cpp:719 msgid "Physical volume uuid" msgstr "" #: kvpmconfigdialog.cpp:720 msgid "Show the physical volume UUID" msgstr "" #: kvpmconfigdialog.cpp:733 msgid "Logical Volume Properties Panel" msgstr "" #: kvpmconfigdialog.cpp:756 msgid "Logical volume uuid" msgstr "" #: kvpmconfigdialog.cpp:757 msgid "Show the logical volume UUID" msgstr "" #: lvactionsmenu.cpp:59 msgid "Create logical volume..." msgstr "" #: lvactionsmenu.cpp:62 msgid "Remove logical volume..." msgstr "" #: lvactionsmenu.cpp:66 msgid "Rename logical volume..." msgstr "" #: lvactionsmenu.cpp:69 msgid "Create snapshot..." msgstr "" #: lvactionsmenu.cpp:72 msgid "Merge snapshot..." msgstr "" #: lvactionsmenu.cpp:75 msgid "Reduce logical volume..." msgstr "" #: lvactionsmenu.cpp:78 msgid "Extend logical volume..." msgstr "" #: lvactionsmenu.cpp:81 msgid "Move physical extents..." msgstr "" #: lvactionsmenu.cpp:84 msgid "Change attributes or tags..." msgstr "" #: lvactionsmenu.cpp:88 msgid "Make filesystem..." msgstr "" #: lvactionsmenu.cpp:89 msgid "Remove filesystem..." msgstr "" #: lvactionsmenu.cpp:90 msgid "Extend filesystem to fill volume..." msgstr "" #: lvactionsmenu.cpp:91 msgid "Run 'fsck -fp' on filesystem..." msgstr "" #: lvactionsmenu.cpp:92 msgid "Mount filesystem..." msgstr "" #: lvactionsmenu.cpp:93 msgid "Unmount filesystem..." msgstr "" #: lvactionsmenu.cpp:94 msgid "Add mirror legs to volume..." msgstr "" #: lvactionsmenu.cpp:95 msgid "Change mirror log..." msgstr "" #: lvactionsmenu.cpp:96 msgid "Remove mirror leg..." msgstr "" #: lvactionsmenu.cpp:97 msgid "Remove this mirror leg..." msgstr "" #: lvactionsmenu.cpp:129 msgid "Mirror operations" msgstr "" #: lvchange.cpp:31 msgid "Change Logical Volume Attributes" msgstr "" #: lvchange.cpp:38 msgid "Change Volume: %1" msgstr "" #: lvchange.cpp:48 msgctxt "The standard or basic options" msgid "General" msgstr "" #: lvchange.cpp:49 msgctxt "Less used or complex options" msgid "Advanced" msgstr "" #: lvchange.cpp:54 msgid "Mirror" msgstr "" #: lvchange.cpp:56 msgid "Snapshot" msgstr "" #: lvchange.cpp:105 msgid "Make volume available for use" msgstr "" #: lvchange.cpp:106 msgid "Make volume read only" msgstr "" #: lvchange.cpp:107 msgid "Refresh volume metadata" msgstr "" #: lvchange.cpp:121 msgid "Change Volume Tags" msgstr "" #: lvchange.cpp:131 pvchange.cpp:67 msgid "Add new tag:" msgstr "" #: lvchange.cpp:137 pvchange.cpp:73 msgid "Remove tag:" msgstr "" #: lvchange.cpp:147 msgid "Change Allocation Policy" msgstr "" #: lvchange.cpp:203 msgid "Mirror Sync" msgstr "" #: lvchange.cpp:212 msgid "Re-synchronize mirrors" msgstr "" #: lvchange.cpp:216 msgid "Monitoring With dmeventd" msgstr "" #: lvchange.cpp:222 lvcreate.cpp:451 msgid "Monitor with dmeventd" msgstr "" #: lvchange.cpp:223 msgid "Do not monitor" msgstr "" #: lvchange.cpp:224 msgid "Ignore dmeventd" msgstr "" #: lvchange.cpp:241 msgid "Chnage Volume Polling" msgstr "" #: lvchange.cpp:247 vgchange.cpp:177 msgid "Start polling" msgstr "" #: lvchange.cpp:250 vgchange.cpp:178 msgid "Stop polling" msgstr "" #: lvchange.cpp:253 msgid "Udev Synchronizing" msgstr "" #: lvchange.cpp:257 msgid "Synchronize with udev" msgstr "" #: lvchange.cpp:261 msgid "Set Kernel Device Numbers" msgstr "" #: lvchange.cpp:267 msgid "Use persistant device numbers" msgstr "" #: lvchange.cpp:273 msgid "Major number: " msgstr "" #: lvchange.cpp:276 msgid "Minor number: " msgstr "" #: lvcreate.cpp:53 msgid "Create Logical Volume" msgstr "" #: lvcreate.cpp:61 lvcreate.cpp:95 msgctxt "Less used, dangerous or complex options" msgid "Advanced options" msgstr "" #: lvcreate.cpp:85 msgid "Create Snapshot Volume" msgstr "" #: lvcreate.cpp:87 msgid "Extend Logical Volume" msgstr "" #: lvcreate.cpp:168 msgid "Extending volume: %1" msgstr "" #: lvcreate.cpp:171 msgid "Current size: %1" msgstr "" #: lvcreate.cpp:177 msgid "Creating snapshot of: %1" msgstr "" #: lvcreate.cpp:179 msgid "Create new logical volume" msgstr "" #: lvcreate.cpp:188 msgid "Volume name: " msgstr "" #: lvcreate.cpp:199 msgid "Optional tag: " msgstr "" #: lvcreate.cpp:230 msgid "Maximum volume size" msgstr "" #: lvcreate.cpp:242 pvmove.cpp:291 msgid "Allocation Policy" msgstr "" #: lvcreate.cpp:368 msgid "Disk mirrors" msgstr "" #: lvcreate.cpp:388 msgid "Number of mirror legs: " msgstr "" #: lvcreate.cpp:422 msgid "Device numbering" msgstr "" #: lvcreate.cpp:427 msgid "Set read only" msgstr "" #: lvcreate.cpp:430 msgid "Write zeros at volume start" msgstr "" #: lvcreate.cpp:452 msgid "Skip initial synchronization of mirror" msgstr "" #: lvcreate.cpp:482 msgid "Device minor number: " msgstr "" #: lvcreate.cpp:483 msgid "Device major number: " msgstr "" #: lvcreate.cpp:518 msgid "Size: %1" msgstr "" #: lvcreate.cpp:519 lvproperties.cpp:223 lvproperties.cpp:249 #: lvproperties.cpp:284 pvgroupbox.cpp:310 msgid "Extents: %1" msgstr "" #: lvcreate.cpp:524 lvcreate.cpp:536 msgid "(with %1 mirror legs)" msgstr "" #: lvcreate.cpp:526 lvcreate.cpp:538 msgid "(with %1 stripes)" msgstr "" #: lvcreate.cpp:528 lvcreate.cpp:540 msgid "" "(with %1 mirror legs\n" "and %2 stripes)" msgstr "" #: lvcreate.cpp:530 lvcreate.cpp:542 msgid "(linear volume)" msgstr "" #: lvcreate.cpp:533 msgid "Extend by: %1" msgstr "" #: lvcreate.cpp:830 msgid "There is no free space left in the volume group" msgstr "" #: lvcreate.cpp:836 msgid "" "If this volume has a filesystem or data, it will need to be extended " "separately. Currently, only the ext2, ext3, ext4, xfs, jfs, ntfs and " "reiserfs file systems are supported for extension. The correct executable " "for extention must also be present. " msgstr "" #: lvcreate.cpp:842 msgid "Snapshot origins cannot be extended while open or mounted" msgstr "" #: lvcreate.cpp:850 msgid "Volumes cannot be extended with open or mounted snapshots" msgstr "" #: lvcreate.cpp:897 msgid "Volume deactivation failed, volume not extended" msgstr "" #: lvcreate.cpp:903 msgid "Volume extention failed" msgstr "" #: lvcreate.cpp:911 msgid "Volume activation failed, filesystem not extended" msgstr "" #: lvproperties.cpp:106 msgid "not mounted" msgstr "" #: lvproperties.cpp:144 msgid "Logical volume UUID" msgstr "" #: lvproperties.cpp:171 msgid "Filesystem label" msgstr "" #: lvproperties.cpp:230 lvproperties.cpp:256 msgid "Stripes: %1" msgstr "" #: lvproperties.cpp:231 lvproperties.cpp:257 msgid "Stripe size: %1" msgstr "" #: lvproperties.cpp:234 lvproperties.cpp:260 msgid "Stripes: none" msgstr "" #: lvproperties.cpp:266 pvproperties.cpp:230 msgid "Total extents: %1" msgstr "" #: lvproperties.cpp:267 msgid "Total size: %1" msgstr "" #: lvproperties.cpp:272 lvproperties.cpp:288 msgid "Filesystem: %1" msgstr "" #: lvproperties.cpp:275 lvproperties.cpp:291 msgid "Access: r/w" msgstr "" #: lvproperties.cpp:277 lvproperties.cpp:293 msgid "Access: r/o" msgstr "" #: lvproperties.cpp:279 lvproperties.cpp:295 msgid "Allocation policy: %1" msgstr "" #: lvproperties.cpp:300 msgid "Origin: %1" msgstr "" #: lvproperties.cpp:315 msgid "Physical volumes" msgstr "" #: lvpropertiesstack.cpp:85 msgid "segment" msgstr "" #: lvpropertiesstack.cpp:90 msgid "Snapshots" msgstr "" #: lvreduce.cpp:36 msgid "Reduce Logical Volume" msgstr "" #: lvreduce.cpp:50 msgid "" "If this Inactive logical volume is reduced any data it " "contains will be lost!" msgstr "" #: lvreduce.cpp:53 msgid "" "Only the ext2, ext3 and ext4 file systems are supported for file system " "reduction. If this logical volume is reduced any data it contains " "will be lost!" msgstr "" #: lvreduce.cpp:73 msgid "The filesystem must be unmounted first" msgstr "" #: lvreduce.cpp:101 msgid "The filesystem is already as small as it can be" msgstr "" #: lvreduce.cpp:124 msgid "Reducing Volume: %1" msgstr "" #: lvreduce.cpp:126 msgid "Estimated minimum size: %1" msgstr "" #: lvremove.cpp:31 msgid "Delete Volume" msgstr "" #: lvremove.cpp:56 msgid "The volume: %1 has snapshots." msgstr "" #: lvremove.cpp:57 msgid "The following volumes will all be deleted:" msgstr "" #: lvremove.cpp:58 msgid "Delete the volume named: %1?" msgstr "" #: lvremove.cpp:59 msgid "Any data on it will be lost." msgstr "" #: lvremove.cpp:87 msgid "Are you certain you want to delete these volumes?" msgstr "" #: lvremove.cpp:88 msgid "Any data on them will be lost." msgstr "" #: lvremove.cpp:102 msgid "A snapshot of this origin is busy or mounted. It can not be deleted." msgstr "" #: lvrename.cpp:30 msgid "Rename Logical Volume" msgstr "" #: lvrename.cpp:40 msgid "Rename Logical Volume" msgstr "" #: lvrename.cpp:45 msgid "Current volume name: %1" msgstr "" #: lvrename.cpp:53 msgid "New volume name: " msgstr "" #: lvsizechartseg.cpp:112 msgid "free space" msgstr "" #: main.cpp:40 topwindow.cpp:450 msgid "kvpm" msgstr "" #: main.cpp:41 msgid "Linux volume and partition manager for KDE" msgstr "" #: main.cpp:43 msgid "Copyright (c) 2008, 2009, 2010, 2011, 2012 Benjamin Scott" msgstr "" #: main.cpp:52 msgid "This program must be run as root (uid = 0) " msgstr "" #: main.cpp:53 msgid "Insufficient Privilege" msgstr "" #: maxfs.cpp:37 msgid "Extend the filesystem on: %1 to fill the entire volume?" msgstr "" #: maxfs.cpp:38 msgid "" "Extending is only supported for ext2, ext3, ext4, jfs, xfs, ntfs and " "Reiserfs. The correct executables for file system extention must also be " "present" msgstr "" #: maxfs.cpp:64 msgid "" "Filesystem extending is only supported for ext2, ext3, ext4, jfs, xfs, ntfs " "and Reiserfs. Physical volumes can also be extended. The correct executables " "for file system extention must be present" msgstr "" #: maxfs.cpp:71 maxfs.cpp:102 msgid "Extend the physical volume on: %1 to fill the entire partition?" msgstr "" #: maxfs.cpp:73 msgid "Extend the filesystem on: %1 to fill the entire partition?" msgstr "" #: mkfs.cpp:62 msgid "" "Writing a new file system on %1 will delete any existing data on it." msgstr "" #: mkfs.cpp:65 msgid "" "The volume: %1 is mounted. It must be unmounted before a new " "filesystem can be written on it" msgstr "" #: mkfs.cpp:93 msgid "Write filesystem on: %1" msgstr "" #: mkfs.cpp:100 mount.cpp:88 msgid "Filesystem Type" msgstr "" #: mkfs.cpp:101 msgid "Standard Ext Options" msgstr "" #: mkfs.cpp:102 msgid "Additional Ext4 Options" msgstr "" #: mkfs.cpp:106 msgid "Write Filesystem" msgstr "" #: mkfs.cpp:123 msgid "Select Filesystem" msgstr "" #: mkfs.cpp:135 msgid "Linux swap" msgstr "" #: mkfs.cpp:158 msgid "Optional name or label: " msgstr "" #: mkfs.cpp:192 msgid "" "If enabled, these options override the mkfs defaults for ext2, ext3 and ext4 " "filesystems." msgstr "" #: mkfs.cpp:245 msgid "" "If enabled, these options override the mkfs defaults for settings that only " "apply to ext4 filesystems." msgstr "" #: mkfs.cpp:290 msgid "Reserved space: " msgstr "" #: mkfs.cpp:301 msgid "Block size: " msgstr "" #: mkfs.cpp:305 mkfs.cpp:319 mkfs.cpp:332 mkfs.cpp:343 msgctxt "Let the program decide" msgid "default" msgstr "" #: mkfs.cpp:315 msgid "Inode size: " msgstr "" #: mkfs.cpp:329 msgid "Bytes / inode: " msgstr "" #: mkfs.cpp:340 msgid "Total inodes: " msgstr "" #: mkfs.cpp:363 msgid "Use extents" msgstr "" #: mkfs.cpp:364 msgid "Flexible block group layout" msgstr "" #: mkfs.cpp:365 msgid "Enable files over 2TB" msgstr "" #: mkfs.cpp:366 msgid "Don't init all block groups" msgstr "" #: mkfs.cpp:367 msgid "Don't init all inodes" msgstr "" #: mkfs.cpp:368 msgid "Unlimited subdirectories" msgstr "" #: mkfs.cpp:369 msgid "Nanosecond timestamps" msgstr "" #: mkfs.cpp:385 msgid "Striping" msgstr "" #: mkfs.cpp:394 msgid "Stride size in blocks: " msgstr "" #: mkfs.cpp:406 msgid "Strides per stripe: " msgstr "" #: mkfs.cpp:427 msgid "Extended attributes" msgstr "" #: mkfs.cpp:428 msgid "Resize inode" msgstr "" #: mkfs.cpp:430 msgid "Directory B-Tree index" msgstr "" #: mkfs.cpp:431 msgid "Store filetype in inode" msgstr "" #: mkfs.cpp:432 msgid "Sparse superblock" msgstr "" #: mount.cpp:69 msgid "Device To Mount: " msgstr "" #: mount.cpp:116 msgid "Specify another fileystem:" msgstr "" #: mount.cpp:183 msgid "Mount Options" msgstr "" #: mount.cpp:187 msgid "Common Options" msgstr "" #: mount.cpp:216 msgid "Always use synchronous I/O" msgstr "" #: mount.cpp:217 msgid "Allow writing in addition to reading" msgstr "" #: mount.cpp:218 msgid "Allow the suid bit to have effect" msgstr "" #: mount.cpp:219 msgid "Allow the use of block and special devices" msgstr "" #: mount.cpp:220 msgid "Allow the execution of binary files" msgstr "" #: mount.cpp:221 msgid "Allow manditory file locks" msgstr "" #: mount.cpp:222 msgid "Allow use of access control lists" msgstr "" #: mount.cpp:239 msgid "Update atime" msgstr "" #: mount.cpp:247 msgid "Journaling" msgstr "" #: mount.cpp:269 msgid "Always update atime, this is the default" msgstr "" #: mount.cpp:270 msgid "Do not update atime" msgstr "" #: mount.cpp:271 msgid "Do not update atime for directory access" msgstr "" #: mount.cpp:272 msgid "" "Access time is only updated if the previous access time was earlier than the " "current modify or change time" msgstr "" #: mount.cpp:278 msgid "Filesystem specific mount options" msgstr "" #: mount.cpp:280 msgid "comma separated list of additional mount options" msgstr "" #: mount.cpp:337 msgid "Mount Point" msgstr "" #: mount.cpp:348 msgid "Browse" msgstr "" #: partadd.cpp:51 msgid "Create A New Partition" msgstr "" #: partadd.cpp:58 msgid "Create A New Partition" msgstr "" #: partadd.cpp:78 msgid "Not enough usable space for a new partition" msgstr "" #: partadd.cpp:101 msgid "Partitions less than two MiB are not supported" msgstr "" #: partadd.cpp:199 partchange.cpp:568 msgid "Preceding space: %1" msgstr "" #: partadd.cpp:211 partchange.cpp:576 msgid "Following space: %1" msgstr "" #: partadd.cpp:296 partchange.cpp:694 msgid "Device: %1" msgstr "" #: partadd.cpp:314 partchange.cpp:712 msgid "Maximum size: %1" msgstr "" #: partadd.cpp:318 msgid "Select type: " msgstr "" #: partadd.cpp:356 msgid "Primary" msgstr "" #: partadd.cpp:357 msgid "Extended" msgstr "" #: partadd.cpp:360 msgid "Logical" msgstr "" #: partadd.cpp:381 msgid "This disk already has %1 primary partitions, the maximum" msgstr "" #: partadd.cpp:385 msgid "" "This should not happen. Try selecting the freespace and not the partiton " "itself" msgstr "" #: partchange.cpp:68 msgid "Move or resize a partition" msgstr "" #: partchange.cpp:75 msgid "Resize Or Move A Partition" msgstr "" #: partchange.cpp:196 msgid "" "Currently only the ext2, ext3 and ext4 file systems are supported for file " "system shrinking. Growing is supported for ext2/3/4, jfs, xfs, ntfs and " "Reiserfs. Moving a partition is supported for any filesystem. Physical " "volumes may also be grown, shrunk or moved" msgstr "" #: partchange.cpp:205 msgid "" "This partition is on the same device with partitions that are busy or " "mounted. If at all possible they should be unmounted before proceeding. " "Otherise changes to the partition table may not be recognized by the kernel." msgstr "" #: partchange.cpp:280 msgid "Moving data" msgstr "" #: partchange.cpp:294 partchange.cpp:306 partchange.cpp:318 partchange.cpp:334 msgid "Move failed: could not read from device" msgstr "" #: partchange.cpp:299 partchange.cpp:311 partchange.cpp:323 partchange.cpp:339 msgid "Move failed: could not write to device" msgstr "" #: partchange.cpp:432 msgid "Partition shrink failed" msgstr "" #: partchange.cpp:499 partchange.cpp:517 msgid "Partition extension failed" msgstr "" #: partchange.cpp:560 msgid "Shrink by : -%1" msgstr "" #: partchange.cpp:564 msgid "Grow by : %1" msgstr "" #: partchange.cpp:648 msgid "Repartitioning failed: data not moved" msgstr "" #: partchange.cpp:711 msgid "Minimum size: %1" msgstr "" #: partremove.cpp:31 msgid "Remove partition: %1? Any data on that partition will be lost." msgstr "" #: partremove.cpp:48 msgid "Remove partition: %1?" msgstr "" #: processprogress.cpp:58 msgid "progress" msgstr "" #: processprogress.cpp:59 msgid "Running program: %1" msgstr "" #: processprogress.cpp:96 msgid "Executable: '%1' not found" msgstr "" #: processprogress.cpp:123 msgid "%1 produced this output: %2" msgstr "" #: processprogress.cpp:125 msgid "%1 crashed with this output: %2" msgstr "" #: processprogress.cpp:148 msgid "Really kill process %1" msgstr "" #: processprogress.cpp:161 msgid "Waiting for process to finish" msgstr "" #: pvchange.cpp:28 pvtree.cpp:211 msgid "Change physical volume attributes" msgstr "" #: pvchange.cpp:36 msgid "%1" msgstr "" #: pvchange.cpp:39 msgid "Attributes" msgstr "" #: pvchange.cpp:43 msgid "Enable allocation of extents" msgstr "" #: pvchange.cpp:47 msgid "Use metadata areas on this volume" msgstr "" #: pvchange.cpp:51 msgid "Generate new UUID for this volume" msgstr "" #: pvchange.cpp:57 msgid "Change tags" msgstr "" #: pvgroupbox.cpp:39 pvgroupbox.cpp:103 msgid "Available Physical Volumes" msgstr "" #: pvgroupbox.cpp:56 msgid "No suitable volumes found" msgstr "" #: pvgroupbox.cpp:105 msgid "Physical Volume" msgstr "" #: pvgroupbox.cpp:125 msgid "none found" msgstr "" #: pvgroupbox.cpp:305 msgid "Selected space: %1" msgstr "" #: pvgroupbox.cpp:306 msgid "Selected extents: %1" msgstr "" #: pvgroupbox.cpp:309 msgid "Space: %1" msgstr "" #: pvgroupbox.cpp:357 msgid "Select all" msgstr "" #: pvgroupbox.cpp:358 msgid "Clear all" msgstr "" #: pvmove.cpp:47 msgid "Do you wish to restart all interrupted physical volume moves?" msgstr "" #: pvmove.cpp:63 msgid "Do you wish to abort all physical volume moves currently in progress?" msgstr "" #: pvmove.cpp:187 msgid "There are no available physical volumes with space to move to" msgstr "" #: pvmove.cpp:214 msgid "Move Physical Volume Extents" msgstr "" #: pvmove.cpp:221 msgid "Move only physical extents on:" msgstr "" #: pvmove.cpp:229 msgid "Source Physical Volumes" msgstr "" #: pvmove.cpp:296 msgctxt "Inherited from the group" msgid "Inherited" msgstr "" #: pvproperties.cpp:156 msgid "Start" msgstr "" #: pvproperties.cpp:159 msgid "End" msgstr "" #: pvproperties.cpp:162 sizeselectorbox.cpp:66 msgid "Extents" msgstr "" #: pvproperties.cpp:182 pvproperties.cpp:188 pvproperties.cpp:194 #: pvproperties.cpp:207 pvproperties.cpp:213 pvproperties.cpp:219 msgid "%1" msgstr "" #: pvtree.cpp:48 msgctxt "The name of the device" msgid "Name" msgstr "" #: pvtree.cpp:49 msgctxt "Unused space" msgid "Remaining" msgstr "" #: pvtree.cpp:49 msgctxt "Space used up" msgid "Used" msgstr "" #: pvtree.cpp:52 vgchange.cpp:213 msgid "Logical volumes" msgstr "" #: pvtree.cpp:61 msgid "Physical volume device" msgstr "" #: pvtree.cpp:62 msgid "Total size of physical volume" msgstr "" #: pvtree.cpp:63 msgid "Free space on physical volume" msgstr "" #: pvtree.cpp:64 msgid "Space used on physical volume" msgstr "" #: pvtree.cpp:65 msgid "A physcial volume is active if it has logical volumes that are active" msgstr "" #: pvtree.cpp:66 msgid "If physical volume allows more extents to be allocated" msgstr "" #: pvtree.cpp:67 vgtree.cpp:52 msgid "Optional tags for physical volume" msgstr "" #: pvtree.cpp:68 msgid "Logical volumes on physical volume" msgstr "" #: pvtree.cpp:159 msgid "Active" msgstr "" #: pvtree.cpp:163 msgid "Inactive" msgstr "" #: pvtree.cpp:209 msgid "Move physical extents" msgstr "" #: removefs.cpp:32 msgid "" "Are you sure you want delete the filesystem on %1? Any data on it " "will be lost." msgstr "" #: removefs.cpp:35 msgid "Error writing to device %1" msgstr "" #: removemirror.cpp:51 msgid "Remove mirrors" msgstr "" #: removemirror.cpp:58 msgid "Select the mirror legs to remove:" msgstr "" #: removemirrorleg.cpp:41 msgid "Remove mirror leg: %1 ?" msgstr "" #: removemissing.cpp:43 msgid "Remove missing physical volumes" msgstr "" #: removemissing.cpp:48 msgid "" "Removing missing physical volumes may result in data loss! Use with " "extreme care." msgstr "" #: sizeselectorbox.cpp:87 msgid "Volume Size" msgstr "" #: sizeselectorbox.cpp:89 sizeselectorbox.cpp:109 msgid "Lock selected size" msgstr "" #: sizeselectorbox.cpp:99 msgid "Partition Start" msgstr "" #: sizeselectorbox.cpp:100 msgid "Lock partition start" msgstr "" #: sizeselectorbox.cpp:108 msgid "Partition Size" msgstr "" #: sizeselectorbox.cpp:116 msgid "Prevent shrinking" msgstr "" #: sizeselectorbox.cpp:132 msgid "New start:" msgstr "" #: sizeselectorbox.cpp:134 msgid "New size:" msgstr "" #: snapmerge.cpp:29 msgid "Merge snapshot: %1 with origin: %2?" msgstr "" #: tablecreate.cpp:31 msgid "" "Writing a new partition table to this device, or removing the old one, will " "cause any existing data on it to be permanently lost" msgstr "" #: tablecreate.cpp:60 msgid "Create Partition Table" msgstr "" #: tablecreate.cpp:67 msgid "Create partition table on:" msgstr "" #: topwindow.cpp:73 msgid "Storage Devices" msgstr "" #: topwindow.cpp:382 msgid "Settings" msgstr "" #: topwindow.cpp:383 msgid "Show Volume Group Information" msgstr "" #: topwindow.cpp:384 msgid "Show Volume Group Bar Graph" msgstr "" #: topwindow.cpp:385 msgid "Use Metric SI Units" msgstr "" #: topwindow.cpp:386 msgid "Configure kvpm..." msgstr "" #: topwindow.cpp:424 msgid "Tools" msgstr "" #: topwindow.cpp:425 msgid "Rescan System" msgstr "" #: topwindow.cpp:426 msgid "Restart interrupted pvmove" msgstr "" #: topwindow.cpp:427 msgid "Abort pvmove" msgstr "" #: topwindow.cpp:452 msgid "" "Linux volume and partition manager for KDE.\n" "This program is still under development,\n" "bug reports and any comments are welcomed.\n" " \n" "Additional icons taken from the Silk icon set by\n" "Mark James http://www.famfamfam.com/lab/icons/silk/\n" "under the Creative Commons Attribution 2.5 License" msgstr "" #: topwindow.cpp:459 msgid "(c) 2008, 2009, 2010, 2011, 2012 Benjamin Scott" msgstr "" #: topwindow.cpp:460 msgid " " msgstr "" #: topwindow.cpp:472 msgid "File" msgstr "" #: topwindow.cpp:481 msgid "Volume Groups" msgstr "" #: topwindow.cpp:483 msgid "Delete Volume Group..." msgstr "" #: topwindow.cpp:484 msgid "Reduce Volume Group..." msgstr "" #: topwindow.cpp:485 msgid "Extend Volume Group..." msgstr "" #: topwindow.cpp:486 msgid "Rename Volume Group..." msgstr "" #: topwindow.cpp:487 msgid "Remove Missing Physcial Volumes..." msgstr "" #: topwindow.cpp:488 msgid "Merge Volume Group..." msgstr "" #: topwindow.cpp:489 msgid "Split Volume Group..." msgstr "" #: topwindow.cpp:490 msgid "Change Volume Group Attributes..." msgstr "" #: topwindow.cpp:491 msgid "Create Volume Group..." msgstr "" #: topwindow.cpp:492 msgid "Export Volume Group..." msgstr "" #: topwindow.cpp:493 msgid "Import Volume Group..." msgstr "" #: unmount.cpp:64 msgid "Can not unmount: %1, it does not seem to be mounted" msgstr "" #: unmount.cpp:72 unmount.cpp:88 msgid "" "Can not unmount: %1, another volume or device is mounted over the " "same mount point and must be unmounted first" msgstr "" #: unmount.cpp:95 msgid "Unmount Filesystem" msgstr "" #: unmount.cpp:106 msgid "Unmount Filesystem" msgstr "" #: unmount.cpp:114 msgid "%1 is mounted on: %2" msgstr "" #: unmount.cpp:115 msgid "Do you wish to unmount it?" msgstr "" #: unmount.cpp:121 msgid "%1 is mounted at multiple locatations." msgstr "" #: unmount.cpp:123 msgid "Select the ones to unmount:" msgstr "" #: unmount.cpp:140 msgid "" "Some selections have been disabled. Another device or volume is mounted over " "the same mount point and must be unmounted first" msgstr "" #: unmount.cpp:186 unmount.cpp:200 msgid "Unmounting %1 failed with error number: %2 %3" msgstr "" #: vgchange.cpp:47 msgid "Change Volume Group Attributes" msgstr "" #: vgchange.cpp:54 msgid "Volume group: %1" msgstr "" #: vgchange.cpp:71 msgid "Anwhere" msgstr "" #: vgchange.cpp:87 msgid "Extent size" msgstr "" #: vgchange.cpp:146 msgid "Allow physical volume addition and removal" msgstr "" #: vgchange.cpp:149 msgid "Cluster aware" msgstr "" #: vgchange.cpp:152 msgid "Refresh metadata" msgstr "" #: vgchange.cpp:155 msgid "Generate new UUID fo group" msgstr "" #: vgchange.cpp:165 msgid "Make all logical volumes available" msgstr "" #: vgchange.cpp:166 msgid "Make all logical volumes unavailable" msgstr "" #: vgchange.cpp:209 msgid "Change maximum limit for number of volumes" msgstr "" #: vgchange.cpp:214 msgid "Physical volumes" msgstr "" #: vgchange.cpp:229 vgchange.cpp:230 msgid "Currently: %1 Minimum: %2" msgstr "" #: vgchange.cpp:246 vgchange.cpp:274 msgid "unlimited" msgstr "" #: vgcreate.cpp:40 vgcreate.cpp:77 vgextend.cpp:46 vgextend.cpp:85 msgid "" "If a device or partition is added to a volume group, any data currently on " "that device or partition will be lost." msgstr "" #: vgcreate.cpp:61 vgextend.cpp:68 msgid "No unused potential physical volumes found" msgstr "" #: vgcreate.cpp:213 msgid "Create Volume Group" msgstr "" #: vgcreate.cpp:220 msgid "Create A Volume Group" msgstr "" #: vgcreate.cpp:226 msgid "Group Name: " msgstr "" #: vgcreate.cpp:236 msgid "Physical Extent Size: " msgstr "" #: vgcreate.cpp:305 msgid "Cluster Aware" msgstr "" #: vgcreate.cpp:308 msgid "Automatic Backup" msgstr "" #: vgexport.cpp:31 msgid "Export volume group: %1?" msgstr "" #: vgextend.cpp:152 msgid "Extend Volume Group" msgstr "" #: vgextend.cpp:159 msgid "Extend Volume Group: %1" msgstr "" #: vgimport.cpp:31 msgid "Import volume group: %1?" msgstr "" #: vginfolabels.cpp:102 msgid "Warning: Partial Volume Group" msgstr "" #: vginfolabels.cpp:107 msgid "Exported Volume Group" msgstr "" #: vginfolabels.cpp:123 msgctxt "Space used up" msgid "Used: %1" msgstr "" #: vginfolabels.cpp:124 msgctxt "Space not used" msgid "Free: %1" msgstr "" #: vginfolabels.cpp:125 msgctxt "Total space on device" msgid "Total: %1" msgstr "" #: vginfolabels.cpp:126 msgid "Format: %1" msgstr "" #: vginfolabels.cpp:127 msgid "Policy: %1" msgstr "" #: vginfolabels.cpp:128 msgid "Resizable: %1" msgstr "" #: vginfolabels.cpp:129 msgid "Clustered: %1" msgstr "" #: vginfolabels.cpp:130 msgid "Allocatable: %1" msgstr "" #: vginfolabels.cpp:131 msgid "Extent size: %1" msgstr "" #: vginfolabels.cpp:132 msgid "MDA count: %1" msgstr "" #: vginfolabels.cpp:133 msgid "UUID: %1" msgstr "" #: vginfolabels.cpp:156 msgid "Max pvs: %1" msgstr "" #: vginfolabels.cpp:158 msgid "Max pvs: Unlimited" msgstr "" #: vginfolabels.cpp:161 msgid "Max lvs: %1" msgstr "" #: vginfolabels.cpp:163 msgid "Max lvs: Unlimited" msgstr "" #: vgmerge.cpp:35 msgid "There is no other volume group to merge with" msgstr "" #: vgmerge.cpp:41 msgid "The volume group to merge must not have active logical volumes" msgstr "" #: vgmerge.cpp:58 msgid "Merge Volume Group" msgstr "" #: vgmerge.cpp:64 msgid "Volume Group: %1" msgstr "" #: vgmerge.cpp:68 msgid "Merge Volume Group With:" msgstr "" #: vgreduce.cpp:44 msgid "Reduce Volume Group" msgstr "" #: vgreduce.cpp:66 msgid "Select physical volumes to remove them from volume group %1" msgstr "" #: vgreduce.cpp:70 msgid "" "Select physical volumes excluding one to remove them from volume " "group %1" msgstr "" #: vgreduce.cpp:79 msgid "Unused physical volumes" msgstr "" #: vgreduceone.cpp:28 msgid "Remove physical volume: %1 from volume group: %2" msgstr "" #: vgremove.cpp:37 msgid "Are you certain you want to delete volume group: %1?" msgstr "" #: vgrename.cpp:62 msgid "Rename volume group" msgstr "" #: vgrename.cpp:70 msgid "Old volume group name: %1" msgstr "" #: vgrename.cpp:73 msgid "New volume group name: " msgstr "" #: vgsplit.cpp:37 msgid "A volume group must have at least two physical volumes to split group" msgstr "" #: vgsplit.cpp:55 msgid "Split Volume Group" msgstr "" #: vgsplit.cpp:61 msgid "Volume Group To Split: %1" msgstr "" #: vgsplit.cpp:69 msgid "New Volume Group Name" msgstr "" #: vgsplit.cpp:90 msgid "Logical volume view" msgstr "" #: vgsplit.cpp:91 msgid "Physical volume view" msgstr "" #: vgsplit.cpp:183 msgid "Add" msgstr "" #: vgsplit.cpp:184 msgid "Remove" msgstr "" #: vgsplit.cpp:206 vgsplit.cpp:274 msgid "Original volume group" msgstr "" #: vgsplit.cpp:216 vgsplit.cpp:286 msgid "New volume group" msgstr "" #: vgtree.cpp:32 msgid "Volume" msgstr "" #: vgtree.cpp:32 msgid "type" msgstr "" #: vgtree.cpp:33 msgid "Remaining" msgstr "" #: vgtree.cpp:33 msgid "Filesystem" msgstr "" #: vgtree.cpp:33 msgid "Stripes" msgstr "" #: vgtree.cpp:34 msgid "Snap/Copy" msgstr "" #: vgtree.cpp:35 msgid "Access" msgstr "" #: vgtree.cpp:42 msgid "Logical volume name" msgstr "" #: vgtree.cpp:43 msgid "Type of logical volume" msgstr "" #: vgtree.cpp:44 msgid "Total size of the logical volume" msgstr "" #: vgtree.cpp:45 msgid "Free space on logical volume" msgstr "" #: vgtree.cpp:46 msgid "Filesystem type on logical volume, if any" msgstr "" #: vgtree.cpp:47 msgid "Number of stripes if the volume is striped" msgstr "" #: vgtree.cpp:48 msgid "Size of stripes if the volume is striped" msgstr "" #: vgtree.cpp:49 msgid "Percentage of pvmove completed, of mirror synced or of snapshot used up" msgstr "" #: vgtree.cpp:50 msgid "Logical volume state" msgstr "" #: vgtree.cpp:51 msgid "Read and write or Read Only" msgstr "" #: vgtree.cpp:53 msgid "Filesystem mount points, if mounted" msgstr "" #: vgtree.cpp:179 vgtree.cpp:534 msgid "one or more physical volumes are missing" msgstr "" #: vgtree.cpp:183 msgid "origin" msgstr "" #: vgtree.cpp:220 msgid "This filesystem is running out of space" msgstr "" #: rc.cpp:1 msgctxt "NAME OF TRANSLATORS" msgid "Your names" msgstr "" #: rc.cpp:2 msgctxt "EMAIL OF TRANSLATORS" msgid "Your emails" msgstr "" kvpm-0.8.6/images/0000755000175000017500000000000011733530416014176 5ustar benscottbenscottkvpm-0.8.6/images/splash.png0000644000175000017500000002434411733530416016205 0ustar benscottbenscottPNG  IHDRJTƊsRGBbKGD pHYs  tIME  ̣tEXtCommentCreated with GIMPW IDATx^]TOޗeq)KsAE 5E3/y3ĒƊ+;*UiR,˖>ww,܏e~~{N1Y`Jʧ6ڀ8_ `|b ~ƣh |Jٰ@ kS2`5*k@#_N ׋fnjBsK mm& P^Vke֎b70w5#88D!wNߝxB"+Ew{;| aRU>`0GokĨ8H pP(1짜p|>#H )دɉ͛ch¯ N = "'L zlle밋8rJTVjKj2c|"`Gc'[O:M:61.v/^}MnHLO5H"I 6JXnBS&C$`bݞR]8X)56n2 -@̀6:?i0du+:mfP7qX:p +* 9U[dj!ںsJRf̟mB5׸n]"Mb?t>=}!Y55-ޫr/>x!0J2;rY+qY0&Wt^YdOV+ޮ't=ѧ;}xE 7nOb0//}x)t_:6'vuFYW~vmo@cc m. q1.| +-[{^'? ߍ߽|F8LjVc3̀s^~Ymeڊf? v +ŻqgѮڟy _}xMCڴ)Ov/B9Ӎ5k QYIotbQ):<~{/"^?>g{0yrV>D2gtҜs׷sQ5 ݞ0\m!{CS?Iq#!r+O{IFt 0.M_`3z("@R.,at``A`H>^pp.D 7@0^?lH.N?]Y Y^q(ٳ7-?IX{alª_w]k1bC)fABii'E,g!1`'OGYQ. ܭN }=!dz8dzyćGVaŗc_XhBuF0& )VgQu"& UO=qg‘G9;B>?tcO9J}0M7S,iH!>oX]UՂCi~VPd]ONWL¼y9D ?}8M wjLɥE?]wyѯ_ Zp.˜9>يf]N,[[ .q?= i8dvkE8D%캌>w CRf϶s2[E!a|MlX_fq1Ecw-U<\/HsRHmB vp[?|xe?,)$bd$*ӟJH  +ǣqfPl#F8Ə~d+cR~g)꥗1cF|ݐ+pmsA#WR|Їo /w~vRTS'p㩧>ĭ8^Lj/Go6,Wjs Q]=Oy|~.^{,\B6b4 *Kr`U{.$"e6>A2l UkHm^R\ `*^d$$<]s睌A\D_zկ< "BR;Ϊ)A9L{4ʓ᯹ND5+'=ذP )o{Nq=y$݉I)f|9-p˽0lH?SjG e\K#㧍0[@f\q/QY ZV6)"*"LJ%ݎՏ^WʊKڪkhskisFQ{tRa&sL*֯~PʓOZ#YwՐMOVO[1VbEwv<"̺b4҄n]~fͲ+)56I~[ Q)S2bcM=i]Z#t(-D:XȢ:=c?|ґܰ)],%sq:SM AMT>ΓNJfFNU)/M7MCCl+V_Wp5mZzis !RUe J#}p)oAc,㣙/#;z F#}.RWvSS 8E,T:iWTژT>yN]#Q"GD{jjgUw&~.H6۷^KK_O|Q݋?ݓ_C"IxEKG~ZpnU1Z,V%kؿ8 +gG)Smj;}Jvȯ>XWdD]V|&[2p2R͞FQ_5+hUB:E\E};UdEt0r^zH.* 7puy]@t畕&lV߉aH*\QӼқ[p oiFھj7C@pY>hExD\E4@P Tn%r@i%K/5d]]C]&E ƍij "(IJKK}Ek;Tg>~  W/Mb#JH)%%&rO;T},m͐"پ*)q}{HK9/j^ŷ'J2O6.Y?¸:ҍI8&r1efv}-|d;H'a|(?5_{ENNlUMEdw-y.nZu]9xc;sh@s9OVd_hc͚妦C8 #Yɮ+~fS&}Y~*-jLU#Y9hR"}u'[>}Ir>6% GyezRvE_ ^rv 'sq6'*ȏѦAtpal`j.؆ŋ UxE>ܕ+C1 S"q^"ҥR2c $ ^'+?yrP[kR+饗S"MvsߐGĵa`3E,aX qb?ot}6e⋠T^>Ix ([~% +:Մ;WNN"!&Kb!>âQ!F.GA! PV^bTVb#?Z?aAec_1 qgر4y|M[yΦ`X[4貪w j]S"DK*7~ax݃ v_sM8}Ls#H=LA|{F4ѡT ;Hȱc7~ \HHrKsp^O >*w~^:yhjۦ]p>BEd# 8<My7;B&Cw^;M-%֝-L$Sx8se'AyL?+P60-xk.=L2E1 @y~%…(8er+6 6"R8{1Nv݆C\P(ӷHvʣNDB0Ϫa^ ;a0.crR! ۄpf2J PܗzاfĐZBt>.g+h7o ;+eYVE/h$(.r7cph:iBW2[oh~9v!*f5+.cZ"%19gq&Uܵ~5&36:ܩ+'9KK[Tkm玽D3áb6%E0vɺ/(5Ubr5oO֋5oS]B H:~)qxYZ{ ,}W͊by-9A,ň1W!Bh""&:\!=]q"!vnCxOUfAιSimj)p'f]|>n 5;@.z%6:OYTb B1f|)'{Ð`G?g13m}Yswk矏Ob%d(Ou5z@6j/f7A|8@]! V_$ڱvVCcp]5To"r;02 q5jҁ>@ # ((00ԗ'L]N@?iJX{cXFM-F*!-t;6 0z%xW^VcnrbJb/~?uN[lHHzl^~Xz+e^V/.su~An!"e%CjH0 xeV} rKȦH/’ɖ B))nXh!,s.@q ꐿ%柕^ +-\q  s0=|ᤓ*\g=u|e.8NכT0w `4M ы/Dޚ%$)mWU Aȹ>_ɴ!m(ʉwXQ )Ft eǓ<SILì "$g .d T!?<mXZba%f0[_ĝwa[i`53m*7cĺG;} D?Nζ*. ҫ~4Ve'ZȂs ob-Ol'~JjԎ1WD:1mCsUo`у0dHdQ8X+oљ-spTܒ&XU!<9cʔodL!R5dՋd:dJg T!%D"?'D3omB%hk{Ifz)H*tGk!6,YecQR>AcYiHO'(X5H$PFA z\ܕ3? jq.i&dsR$6J:9ħiݺB#I:,ܐ(?(:%?pEϏ'K8#m@R͙kܹ~t$^O եmVU{!fqeuoʯʒ~ 53hlH2tjÐlI@&;=jÒ e%ᆣ~'W^CG7DlgcN>okI%?^&N / U2FO[Zi,W SfLţ[]~ 4$]ښ`yv,ZU#"9.<" 0.Bث์)@U2t()*Κqw}Zq̱3pauʛp1w}4yc'1sf|{>GݜcD3IDATh:ǕXRm'){Nj5duCE/ea睷֣0#O_/y:^#ȮV_! ly 0q090&Z Æs'j1u;n$sMh/ec.KTJTK&2S[KkCx)?N8t/JO{Tm9]wbo.)q-񓏆P\>Lz W<.mDxrDzL$a؆5T";LF>aqq͍MIoMΘ~$f}<݉`W@;*{U2Dy0 ӧǑ3o//@fRsbfx/,>2}̙m3L"J6q *5AJ DJKن+Vyf}7svX򜒛ڨ]))f8\U_|3& CAq^)C{gpH $gI~1>Xy cW+5N-qVsͬk;v`{] c@mΚ>_y*d1ڌW*썦x_a>vtx{omsrK.$? /1J9Le#?`2{]7 2vZ|o9t?ު#GoĊA|l7ۤ#5WD[r\00_% }m1[41U/x59[ha'Ȩ7 / .U3f&Q(=_5h57q9fW44=(w:\&i\&v"׀7.SG:".` 0i56]wS_QFvn/6LYwlZ@f MB(_P5 ؉c(ć桚G:g >Eiu.&;TxwU j*ȦsV/v48e[6nݎ/Vm:(knnAIe\PH>mxT=F9 CFaC1Cm2 ։K7ۅ˗wnW%- zR[kr(k+taVTW BI(Th~.w!nWۜE/`A!=fW@,zV.X-v|kQE-]TF^=ru7a{nC/G"ېZ!3|qK ^_Σ?> Em]D|6(tjae?Ϸ0A~^ lXb)6>AAқA?BZw.ٟ ikd&jأݭ̀Z!u:iq .>*J hg8 Aa/I6^56ckj+G6zlf0fˣg"I;sH-,GQA\:0wTr$c|=}8ٶv4X1wj О/J=4g! <$(@B>߃=Ft C Z"ŀ=N4fa@@,jXP1 $0A4  ΀A$/xL `XP1 $RWnBIENDB`kvpm-0.8.6/images/CMakeLists.txt0000644000175000017500000000010611733530416016733 0ustar benscottbenscottinstall(FILES splash.png DESTINATION ${DATA_INSTALL_DIR}/kvpm/images )