ÿØÿàJFIFÿâØICC_PROFILEÈ0mntrRGB XYZ acspöÖÓ- descð$rXYZgXYZ(bXYZ<wtptPrTRCd(gTRCd(bTRCd(cprtŒYê!q%ÎyÜ’T=UHÔC]õw`²slìŽ8âWö;¬¹M/¤ø·þ陪kêq.'�þSêÝ�`ç¿tŽ©Nùï’¢ÃvK pxÏÜ”›š÷H ¼2ˤzÀ—e>§–0ì5�Ãä(*1’�ÃÔZC{oÊEóynk<Ð08*{]Ç�—`Š‹\UÞ·9­`3º‡*4U�.Û·„ú*ÇI�\ü(Ê«[©_ü3!ÉÇÑgE33¡ÁÀç’¥H¼K%¶ýuµÌÉiê¤nžNËlô_ŒU°Én d„`ko?qÝiªfànvå=§1áñ¸�b—F�T•3¶zK©¨ïÔžuTr¸`¾0ì‘�¬­ppÈ\iÒ=qq±ÖGSMUåÈÒ;làNô_Puuq‘‘Õ3ì÷ZÆVqæÂáµáoB®s‚„„!!@B„„!!@B„“¨¨‚’ *ªelQDÒ÷½Ç­’”\ñãÿЦi$èëAò"poaÿrAü™ögܨn½/�É*DW‹Þ3ÏÔ3Íe²Îb³ÅúˆuAÏý9à}ÊÒU•óÖI©äï°ÁaYR_ ‹VãÕ!Ï%4|�‡$ìGºÅ»;ÒP]bM§Ð×`Ÿ…Q){¼±¿ùKÎòàeíÀM[­ÏsËvþU^Ìãf[�ŒŽJÍ­{�ÆçÝ&Üà÷ÝJZm7/8Çr«)¨+dF.N�”48îS¨è.8e30·*ócèy% 3E�¶h+½³¡£`¥våyù>B1Ò;áÁ“Û5}Ÿ¢k*>¢N7'!])z6£Á„»<î¶U¯¤éáˆÿ£8R0Ù¡sšÁÉ®G͔ߧJáF&–¸ôN¦:fS¸—KOuD½t½E;\öDZðs•Ö3ô¿ç¿Ø��-U+÷‡úiªéÙ�r8!o�”ÓÙ”¸«èæŠ*ÇÒ”?ʃ¹-!kùc©¢Õ�p#l®øeŒöŽYcptËMŽF¶¦™Ã<à«/MuUÊÁTÊÊÜǰ‚@áÀ{­}n¸ùÃ��å>Ò% ’ Øîµ¶ˆ¤ôv7‡}yGÖvÀàíqâFyVõÆ�ÖÕÝ#ÔÕì•Úà&‹VÏgÿ>^Xï4]Aj§»Ûä×C›îp~Vð—dyùñ~7kÁú…sB€„ !BB€„ !B“âçZèÎ�¨ª§xµCȦÁòï°\au¬’z‰'’BçK‰ß';­›ãßX¿¨úÊzx%&ŠØ?/g8gQýÊÓ•ÕÁøc7lïÅ�ÿ,ñ“ ¥yýDþÉ¥TºÀcu,ËËiÃq‚ã�²nLJÎp0Ö6U$VgÆØÏò�꛺C8Ýdçk�€2R†JªÆSF8?U zMtÕškÍX�­Ë5 ð·Jt3Æ4FÀx'  :!¦–¾= ¸ã•»(,Ôôѵ‘1£_=Îæ9ˤ|=ÞAv—¤%£¥™JK[·;r¦›jhxþÆÅ5`”ëÉ ì7^jß§¡á •ËAÊwKoi•®#ÒJ�ü°Æv$'PÃ¥ û­"c6>´ÑÓµí&»î=UIOVÐØè@pnIÙ-OSùOXÆSJÛ™™ï/عt©¥9\Ù¬¯–\]ãkýû-'Ö]a{Ÿ@€OnË¥ë)âœ8»sØáRúŽÅ�õ0;9ì¯�;ÆìdIJ-œ•u } šO¤vÙ/c¼àºžsŽØÏ außH‡Ä÷‘Œ�@ZnzzŠJ²sêaÁäd/sU–6�',^S6›æbXÈËwÎVïü;ø‚)îéÙ‚¬—ÓåÛ2P2[÷\ái¹;"7�Ç<«®ã- lu”S=®kšpC‡®ˆº)4²FŽøB­øyÔÌêÎ’·Ýõ5ñÌ3ÃÆÅYAæ5N˜!BBB€„ !BB€[õ]/Ò× Ì¯ t0¸G“ËÈÃGî§VŽüMõ ”öëoMÓ»ÕRçTJ?é7ü¨n‘|qï$Žl¼ÖKPù'•åï‘ÞcÏrNê·RòéYSÝŸ²–¸Í’@Û'(²Âk¶â&à,ùM&©€55¡:Ý4žùÂõÒɤÎÀž¶è›ùRçœl B¡Zîø8Êžè[3îuíxaq‘àŽwܨoÊÊúQ9oûĆy sx;ÓYž˜½,-ròòþ­ ¯|¥‘ëÐOmö]Ånªme5 ?©£?mhÇ‘•ŽP„+œà„!!@B„„!!@àd®DñǨzëk„Œx|4¸§ˆçf�Î>ù]Y}®ÛE]i?íDçEÃ�Er5³M3Ç®¦WH}÷=Õ&þŽ®4}‘U©Ôùõ;pÝÓ<ÅÓ¨à'Ó¼F‘’AQÕ§mÛœ³:ƒZ-�#ø‡û§’4SPî0ãã £çIO'sò¥®4úÿ-J$�GÁAI[)|óANæê!�8Ç®˜ð²ÀÚz6Ë+FØú­ ÐT’ÖÝZè¨Ý?•€@ÐËÕÎÕFÖš8ãÇeäóc,Ž‘èñ'jäl¬·K{p±kFw-y'‰2SÈ3#$�ðì'´^$Ú¦~‰bq÷9eæK‹‘n�B¼oV_ásZFø)Ð|oöU«múŠãƒO;ó©K2 c:–=:é›)vV‡®sò“óØî‘u{Hiß µêª+,FjÉ�¾UátŠI¨«e·Î±¾ðeÉœ- xñæ G=”0:\~’Jƒ‡ÆëýÂAå嚎4·•Ù$ÞÙÃ>d¤t¥KhôbOoer>“€ÊÓPõWXÝœ×CçF9'$§è*ºœZ‰I#‡…\¼~«Lœ|žÏÂvï¦7ÙhÏ-u,’µ€¹ •º\ú†yU,òäŒò¨�uBÙé¤ôòÂ0³ãKñäL×�–Lm�YXúZ÷.içÿ>êY•1Ö@Ù#v$*;®¨™Aw‘…¤aGÙ®-…ÿ—y'|…ôËqì|ân2£bÙî lqK�ØF㜂»³Â[Ѽt¥Ïvdu4z÷ïŽ~á|ú·È熻ÐN¦®¾ü2u¬´¶Ý,®s¢ih°o袼4ËgMÆÍþ„!jq‚„„!!@B„„!FñŽê-�Z5éã1Œsºã+­[RÜ�˜Ñ·ß+§WvÒXá£Îï9Æó¹ ’«ªZèg˜»q°%g-³» 4ž«ÌóÖþ§`……kF˜¢üsû¦�J^öÆ]’qÉå<~f�mßNÿ †‡¶Ø]-|cä©Ê¨%v"؆†`s’xIôí �^ÇþœžŸ¦lÒW]M[Žctøè±É“ªføñ¹Qо ô=Oô—çj©£}MPê dl¥/²ÓM±‰® '\RY,ôÖˆD“Ô9€£ÝÜEQ¹õ}LdŠªÊ GŸå’`çê¼fç7hïqÇÓ2½Z©ªey|œpuî™�ÎÙ)+Nò�±—ª+¤o›e[h߃ýÔ¥ªíh,òäîÓÈWï’˜¬Xçã%ºJ ªJ–²Bü ðBÚb}µ¥û‘“’ª?�#H÷Ù[¥ |€9ݸ\Y§ÝÙéq±¬q¡´Õ­‰›¸çÝjß'­¯…”ômt˜'8öÂØ·Ëc9e@¿I¦C‘‚vQ‚n3²¼ˆ©Ç©¯-Ýç—jàöW:›|:šLc,¦³@ÒÓ¥ l¼©Jå£ÑŠê¨åÄe§E–ªF°êhÉúer=Γ̦†’æžÞ˹%Ý7\ýy\…ÄÍx’'»‚F_CñÎñ?ò1¬ºtÌæ(XÐs‡al®ˆ» OWZ.� �¡þÚNÇú«-.�çËO¶ÓJZÈ*cwûnû.ö¶aúŸIü=Ѧj6¢)œæÿº´ª‡…õM¸t�À™¢c‰#Ÿ@Ý[Öˆæ~‚„ „ !BB€®ÓLý÷v>étÒâàØ5»†gìAø�º‹‡ZU–7XÑÏ±ÇøZJ½Ž©»C¾,n~~Êñ^´Wu%}FœJZAíº ¾•2�ñQ°²6HMk4K’æåIÔÓÄ׳„ÏCÁE„¤Ó¢ÝQ-áï8f½ºÞ؈›žém, $4oðœPÈÆH5•1nÃZ$)èŽ�Z6î’|AÍ- <ž¹Œ§Ç“«¸öMa:ÜrvS“H@IÔ¯Ohp*¡�Œ`·¶öJylr¹€½Ž?Tk“Ak‹ÈÎÈ–‘ƒômHÖDa¹k¹ z¾ŸŽ\Ë÷ "ßskkü¢Z4ó“ÉWktðTÁ«,ÉZE^ˆ–ˆ[w”Ñæ7… |˜£¸º’–µ™-º„¬xpiVZb;ÔÕ°�:só”й¤àR3ÊÞÝÔmMâ Ie$gl¬ýeÚ£Tþ$bd]q“œS¸®™®ŽwL?K†û®ëüL\¡=p`q$Óÿs…“0±®¹ì¾�ã¿ñ9òOú¦0?w†œo¾UžÏ0|/aÛ?ÑT ~™œs…déÇ;Ï1»‚¢qAÑôKðÏsÿTð®×.²çFÓ²s‚ÒBÚ럕GÿH\íÏ�»Ê¨d¬ßù\ÞßpWA+#9ª“! J‚„„!!@B‡ê©Å5’ªwpØÝû�‡÷S ³â+KºN¿Ô@l.;ý�•éÂ%’ÚÚ™°šyÇÊ×Õ³9­‹S±±à­“âS4IMˆ0ˆµ;ܸœåj«ô‚8XAÙ¤ƒó²ÍStÆuUߘ¨9Æ–ãNÿÝJô}ÔÚï–ûœ|ÁRÇà}U@Ô9òµ­nF;+%†™¹Š'´ãbOsÝW&ãDb—îš>¦ôÆ:û%\nK _ÿäÿ*Û ÁÃCœ2xZkðí{7­‘¾@éiá»nv nB|·åà /˜Ÿë&�¯ÄûA1Zˆ]¡Û|(ãÆ )wÊ%g¶Fp¢å~ ,&‘ ƒÜÆ·9ÈP×[¸§{bˆúÜpÊ{_8 $; ¯™Y‰ÒÅ˾‹8§e•ê1W$,{ƒ·çe;2Ã^öò=”-Û®m]=GªoÊ@Æó,§…Kâuö6’íMU;Äà@ý–’VŠ—ú †¶’¦)N"{pUZzX§�ñ1íÐá±%U.=qä6AøiªG�ý9Mye¢kÔf­ïËh.Á=¶R“hÆQ§e�¬è¦±]£­‰ú¡y vXújõ®6; ‚3Ê«]/ ½ÑˆàýdiÀággómÎ<œµ&¶mVÖÇ$zÚìdp¢®U õ¢©.m,Ƽ•uc4’ŽUdÂ… ê*—Px í…_ uOÔ󆀞TU5ÎòÚA 2¬LXpü`lÈ›f‹üL]X:nZÎò�Ì|.<ª” %“a†€2ºWñ]$®4ñ�ZNøîW2]™$s�¯Õ‘íÙ}? 5�#å¹Òí�)ÙâNA>ÊÇe~™#À ‚«–­¡.Î=JÃkþc9céÚŸ„*¶6:È$Ëça¥ßÿ`ºir/á"âa¸º/0�,ŽŒç�Ú>¾•×JQºÁB“0B€„ !BUD�-U?ê’'ÿA•oU_¢2ô¥pí ÷�¹FZ>£‡|M.’a9È'�퀵ù¯M$’K��–ìñ^�”°Á£Òpâì�øZbúìPÅŽ7#*ˆß#ÙZ‰º44d¼•=a­™÷VQ3/v�[�‚ƒÈ�ÿpá�;�l§�13k%¸QÇ/“[Sò%AÉoßü(š´gÖGr~¯“Sh¨xþ# ˜ÜÿçºéöÉè×�È_5ÿ>,]íÝyo±]Þqtm—‡Çö_Eiªüê8¥!ìþ‹çy˜ž,•/³êø£—¡ùªkNã“¶É­]C:O*>z·dšæàÿä|¯>Gq•džs„M$“Ø'4V!ߘ|DµÃ„•’&TËç=Ùß«—�rF;-1ÆÑIJ�_¯ú"¿«Ë­•P�Ë9û¹ÎÛEdð¢ÏÐ4¯’Ë,ÁòŒÊ °ßÛ…·«Äêxk‰=‰áWke³KÙ–ñƒÁZù¡&jÊ‹uúó3à‰Æ8‰ ¸»ùTQð¦ÖÊÑ= ©c�29¹õ{­¦Êq[c/ä—ŽQKæOÎ ]?Fý/ÑU4GUt›Î•ߥ­kGÑJ]mŒÑæ4ZvÂVK÷–©6lp>umö7DZ7ß+6Óð‹~Œ™\"“C³¨/e¸e§~T uÀ Úüœò™¾ã#¸vlÑL°6¤¼ò20’¯œ6žGó€TL3¸�K‰'•�Ö`Ë|®ÎiïÊ´ÌòKG+x÷Ô4Öû†‰ÝªGå´rs• ®µ2OfxÚ$y/ä7°*óã­þJ¾¼©†ÊV5€ƒ‡rÂÖÓO%C̲»Sˆ êøðë�#ÈŸ|Œ”¶ù`%ù*zØâdÀl«öâ #rIV nùv§þëvVK~.!—:š ájÈñ¿³{\%.¶f¾µñ¼åÛùV»¤`Z«7+{g‹°+ÀgÒè/B–•®<œ§ßúÍò7DNw§l4­YÕW;…¾�ÐÓÆK›³>ªš|Bë«e“¥uÅ#±ù†Kr{…Ó‹’ÑŒ²$΃ŽóŒ’¢á?—Ìp j¼AéâÏ&ü4ãQî´ë¯÷Û¬ žªž©áþ¢qhM¦¸WFYo”mú„Et.2û/ÛøFÛ«ëëX‹æ•Ã-kF1õUÇxµ7š_p·kˆŸN¸ YÕÏvœ´ÅGR01þÑÝ7ÿLêÊàChd�ƒ—I°W\h´a9Éx�°Ïl*.OòI¶…õ�ÙPÇD˜9jk�IÝ„D¾â<Ò3¢6÷ú”ë¢ü-ª©.¬¼\êç/w¦3!ÐÑôYÏ�+³–Møl¨n¬¬ŒhÁí�T„-:@=Ô]¾Ç°²‘®:c;ÙN¾69¬-ÇÙqJ“Ñ´kb´‘Hé4ìp~¹®ŽÓÓÕ3¹ÚCbs‰Ï°R4Í øaiŸÄßZ›/IÍG¸š±¦Àv6#sû-¸ðü™L¹Y?'#�zŽäû½î¶äó“Q3ŸÎvÎÊ8p¼qË— lw_V•*>M»vJÑ<cÉ.Û*Ãh~©�€[®AèŠ'8d…b³¼62ñ‘Ê3X›×ðÃXÊ~¼e,šU>Xv¡±!˾FÃ|Úðbí%·­íÓ±¹Õ�âg(ÁÅønÚJÖV¡�vRðU¼Ä\qŸ•«è:€<6f¸ïÙ[í7¸ê2üØ•å­ÊvY+æµ²= ´�ÁP×{+s0ïe% À= cç)•uÍŒÙîÉ(™z ªzzÐé¨R4öFÆó):‡`ÉkEKÉó8í”±¨òÚ0AZÛ"�)áÐâLxFܪ<·¹ï#á8¸]™ dê#²¤_ú‰±Æç U [£�E[ íôFß-�õÙV*/QR±Ò¾O¹Pµ×�2GÔÎì�’T•s\\AÚ&‚pT1QçÏ#f ñþýUwª£—š÷–4ž>ËO{-±ã ;¦lsi?Ã$ƒŸuªÓÜôZX’GÏòmämž0áž²''앤£}DnpÒGd”à²RÂ1…Òs™Å™AÉʰóGdï±;(+|Nža\HÇÏ“Ši'¬18éòÎ4çÙVVi§ü³ÔTuGFÊ aÏ Œý—s0ic[œà¹7ðõb‚ðî›»@Hu3<’{;K¬}5Öi¡‘ì„+‚„„!!@BP÷Š(ßJCŸ£N]�2IÇo’¦CÄ®¼²ô-‚{…Æ¡­“C¼¶äg8å9;ñ-[¦—Ψ©.ÒÁŒFÑü¾ùÇù\©|¨8òµçsƒÀW¯úöN«¾U^*%¶�2Nß^ëYéåÉܸ÷îUVÍe¥Cʵ”¥Ò7N£Ï¸Qó¸xåHU=¬þ= ÉǺŽqÔIùV3`Ï ñÐ>û”»Är÷´�誴Ož¢éýGm¹Âèï úf:A–21ù™ù� Ÿ<úÇFØq÷cºîœu=,¥ctÒ<œwiü-³hŽ9è¢{w` „�†?ô¹\æeÏÂiЕ…²ÔÙ*ž”ÏÔÀyÐã²ñ¹O²³ÜáÔ�‰ôòq²{Os¨ œÑ©¼”¼ÔºÚ\默Ö�-ØÁ^uYéÛE†ÛÕðȸlZNë:ûë'vIqþŠ�SM¡ÄÄâÓÏÂ�šç]HN¬‘ÆÅOK-ùZѱ`©iÕülc“•�Uê*f’éu9ÊÕÒumCšKÆø# 2£©&•ÙÔr;çu+e"‹ÕïªrÇ5¯ ÷õ*ÞòéÞKäÒÁØžTUeÒy¡¹ùM©hª+êåïìºag&I¹±v¾¢å.ÃL]²sŸ²›æÃGƒ»Ü3Ÿ„òÙf·#ûnž^ dŽvpâDzw·H«…+f�ñXEùw·Ò@{åR¨ºj¨ã/S]…yë˜'½_é¬Ð·!ÒÊ}˜å>ª³C@tEkDyέ†]`�4nf¼ŠÈêhÆ<°;HaW®Vi¼ƒphÀ$’Ó±Æy[µý5S[Bɼ½±�t‡OòáE\úFLú· 0Œì=¶ù[Ç%J4Å$¾CIÎ KÚž×UaÀ�Gc씿[& ©p|e­ÔGMmòˆekÉ#°Çºè»Vd´èú!øKÜ:R%š­Yc4ãm`d�—D.ü0ø¿tèëÜý9já¯-�0´—6víÁ]©dëÛ-Ù¡“¸ÑOÁd¼}�Çï…E’ õodÊ-»,¨^5Í{CØàæ�Á ¯V¦`„!!@BNIâ‹:Þ6ßuJ*Ù)7¤(±’Xáa’YÆŽKއ¸_],¤hÕÿ#º«×É[[ ’¦«HÌóŸØv\Y9ø¡¨ìè‡rÛÐÃÅ/àèªCOÓöÉ®—)0ØÃcqŒôÜý—ø¹Öþ!uMy­êú‡ƒ+Ž˜Ðm+¯z’J Zy +$ª?ª¢F‚ìüg…Ìž"tÄ÷ ‡ÔH§âコ‡-å{5xc�hç[ƒ¥¨™ïqÃAÈH@Àç¾0¢²Â g“sØÓë#lüaX-Ü.�dTôĵÛ9Ä �ì>v]–1ôçXÜž�dÖJã†8�¸OmÖçÔ<³¼à‚8[ЇÁz¸_)ò†HŒÉN­þUQÔ“ùPÓ¸.>ê²äÅ"˯e/¦ì&KÅ3ä�»|®¤èŽ›’:WÍ+Öà·a«t—…¥÷ŠJ©àŽ8âVäà[âÏhŠ„qÆ`Ø. ù”¼;°cqVÆP[™ùo+FÔý_K]Ò—¸oÔ§Ò×bPÎf{ýòª¡’&8 �Ö4’\â}ª8ÚçÊÂâHý ÷ÿ ›û´Î‹qv‰+æ’÷CT2HNg�§'-!ÓwšÞ‘¼ºÅUP†u3ÕË{-¿g¾Áp€æ’WLNþ�Ke‘SôÆ¥�Ãn « ,%®%Z*#†MÀÀ'b ŠmcÔAª•’²�YHÜä7ä(Éi÷Èiû+�UÜH1‚~ô¹d“Xç+hÌÊŠÌvÙ% 纲ZlÚZØõ;襨,Y{ZöŽyVJKtT�ô7.îp«<¿Á töQÂá—q·eXêyC ‘Îyn9ʹWHÈØZFák.´¨©«†�…ÓT¿É‰£’ã°L;•™æuU::ÆëÝÚºõ§§¦Ž‡K'~ŒàwE–žÊ8YÃ�ogžùÔζ[!Ô[»‹FCN2J¡Ínª ®},ìs_‹\1Æ íÛƒ±ÓS¹ÿ–kªÓªLnâ~V�ñ?ÇںŽ7ÇJØ…aÒ ¿<ŸÝvcåGÃaÝ™øiÍÈ^C’œ†¶L ¸$ÖöñEz‰µ9lU@¶À­Gá‡ÒØ­55$¼æ¿K¾ëaÐ9ÔµDHûð¼ÞNNó´oŽ=Qq¶Ýn–w¶)ï,À9 ÝAÔQU4gs‚ªQÈÙ oš3¶é8å0Kª#…\|œ¸¿µ‰b„½FÅŽ® uiÿå²YSè+ç ã…9GZ0‰ÂíÇòu¬‹ýË�ÿ«%“ŽA È9þéEèâÏ�2¸3žP”=ÕV60I$�n%mÔœ��=»$kjÜAv¡�ªƒ©ª.ÈÎÎÏ,ò;“=hãŒ|CŠ›�È þ꺪G’|Ìo²Ni]«o¢K:öpÊ̾ˆúÊ3P1Σ¾ê‰Ö]6%¥yŒiÆx[M°´µ¸ÕEõ¨MLì7$ƒ€´„ÜYYE4r]OMÅKt,0—»åm¾�±DúBï%ºšpÝ·P÷{ ¼¾IcÒÖ8c#•²<6£mMG’ðãÓ#Ýì;ד%ÄÃjE��)EQ‡66’Ü»e)oéLíÃŽM·í•;-º)˜^ÈCŽ9!0·¼ÁTèIhøár¼Œê¤aNÑS͖ŸíÛáKRÑú@ú)N×cSFãdá´e…¥½Â…2lˆ¹†SRI,ìÀŽ2í»ª£,¯Š�õ5Tó€çä~‘Ù¿@¯U4­¬�­�êŠ ‡»»·?²e-^í³ð¥Lƒš|_é—ÒÊËõ#1$;»çO}�¿HèÚÝg#‘•»zç¦EÂ’X¦‹P{ÉåsÍ5¾¢ÁyšÚZt±çc ð¬ÿuE ú;Fß¡º9ñ€ïP?8)ç� ƒ1äö%V¬�t‘³-!Y €¼o?+†Ztz1•¡9)Üü‘¶BÆž�óIéû©z[s¥¬$ƒð§(l��cÈøY¹—«"híÂã�œä„¼°¿Ë:F0yV#kkš;àl™ÖR–‚ÐÀ*^Ë}‹Ï¢3— �ʇè;3o�K=ÚhµÅob$m滸ú þê[ªÝùjY^IÜsô[¡úLXº~–'Æ<ùX%œã—8d�· ³‘çòeB ¦š Źg�ùû©6Q2H[+¸!KÇ@ÂàK>«ÖѶ–aF!%€ï¤û-Ôìàc*XtÙêp§¤´Äør09¹Î›]i!ø?º”|- ÇüB6A]ÿI§f�ÈÀp6T^¿ðÒ¡dl§a|·n9þ‹kˆ˜çŒÿ(^y1¸9­P›DšÊ’Ý‘66††íûaAQƒ-P£gqð®�A¡–za¶rFeZ ¥0Ô \1ïôÊ£vA3RÿËS‡ööL(å}UO ägÝ!x«3Mù(Ý�X©‹¸RÃæ}1³€7Q{ avçžÊíl§Š&5­ÛnË“#Ùèc¦/EitMàï”ý´ ²Z E�ÎÉB\NÁ¶ÍƦ2Üãʵ‘Ë�†à)gE!ÐT]É­™�†·'~ Gd? ƒ,ÂýÔôt37TMwžð?âÝ÷úœ-²!´ï…Tðú‡Î«®¼JÌäù“Øq÷W2”¼ •ß�~§•ž]¤# 98ΓBÙÛ¡ÙÇØö)HbˆÎ'Q‰@ÜãetŽz= ŽI³Àóú‡Çº}$�ý9dÜE‚*˜â&ˆÐëÚ«Iã‡Ý*?/~êû] Ì8–*‰ƒ^ÃìG9WI¿ ·E”0¶] põƒšöMÇ XÜ?Þй’3¯¨ê^vÑ^óý±ýW±þ"<=¬gæ­FïXÂ2_ ô�¹Q(J> ¥eŸ¬­Ï•ôõìnC•'Ó²ªÜåYpÃvÀÏÊÁž:ôÅâ£ý2-âqUéu6€ßœ•s©š¾®:FgïÛ*Œ´U�ú~‘Õ•f¡ç#VÊÚt´hiÈM,ÔM¡£nŒ%uüo…ž’ Âqƒˆô�‚l÷ {§Œ��ÝC&‡‘8}!8ÈìÃû¦�¹Ü�’Í$€FTeVäÍ9ì  Ïší[g»0´çê«PtÈàU�$>hó�nM„êcá%Jì€ßDñ­ÉÆNvBR�Øs{�ð§¨ËKý¨(Æ4‘€¦­äihùGáda{·ÅYFæKp##!s§‰ÞY.IééÝM/«@í'ƒ²ê)"/…ÍÏ!kþªµ²XÉ1‚wì§Ü]•š´qÓ:OÄœ©dVΣ¯m(~@eOb®q^üU¡¤l”�apai.ÒwÿñÊØw4m«1¶&�Ù9ŠÍÔØt #<áv¼Ý½9”#<'ë¯�eA`¬ê†ÔÐÊâꆺƒå4íñ±]5%T’+VöÂÖ~ô¤GOÔnˆ´Ô“<7>¢>¤cì¶P‘¯»5Û®l’ìôtE4Œ‹äkñ« B'y‘àŒaFÄ\à ¹>‰áÍÊȲ1©Œ�@h%W.ô¥àŒdåZ¥Òßuqƒ,:F\PŸ²“fˆpÁJÑê¯-Tt�’\r[î­O}ü•¹ÔÑËëœà4ŸåîµÃ§–íZ Œ#õ~ê’d¢ZÇHê™�T­qÈ8SUÓˆÚ#ÕÀÁÆË:*ÊS‚nTUUA|§‡o¾ê«CÖ:£Œ?ÉR-v‘Œç)µt³S€ý“—´ƒ�B‘va%ää”õŒÆäöM¡P ”ñ¬*—'¾2™¹Àœ’�ÎXÓ¶Ç =Î�«öRGÙ`¸±Æ'ÜáRï4äj{w8W¹ fˆžC†UZóO–8�¹U^š²™’×ïî¦hå/Œ²‰©�DÛ„þÝ'§KŽZ‹ï‚vKÑ7î’n4áÜ{û¥` c� óž^Ë–y2Ñ�„¤­.&”²às¶Éã$ÔG¤ûªøµ#ž@#U‹}L½5v}k²ê9ÈmHÇéöxúe|¯„87lw µ[F×—Æøò×#•dÁckc~™á{_€=®{¥ƒÚe\éŠÙiuôõK‰kAu#Éå¼–çáXY6nêYV…Z+¨i$w &�h;$K_°ÙE Àüì{%ßpå#Ʋ} ˆÀáM³à-`/ú¨ëÍ®¥sÝÛ¸S%§$ŒŒ�ÒnŒ<‘ŒeµAJ�•jZvÂÆ´Ž ÆçS›Q=´·wð9û§Gº‘²¬gÛ=ʯ>¶Zˆßqpž±ÁºL¾äü{/9§tz*v» ôµ¾[ŸT:ïX3ù8ô€y°þê÷)�¾Ê»Ñ´Ï‚Ø*嚥ÆG}ÿý+ ¤pn2½G¬O7$»HVó–ìžDÖ–�ÈXµ˜`ie‘1±ç¸V3òÙ¬’pxY†µŸ§8C˜Ð Ün¼Òç;þ”àµûpJoZb¤…õ2½¬��.sœvÀJhs3¶O¸Tþ»»8Ó +—χÈ=˜;}Ê7DYOºWOy¬¨¸’âɘYÿ?~T�MZ‹^[¹;’šÑRºW�åØ +U,_”�Ÿù•�—J†×Z¦Ã�#ô¨j�<åÄ”¸Ìe—Iqý”�®•̈$îYC¸Ééáf s±§;¥IÇÜ%«_¹Ç ÖB£öIÔG¹-;o²bðø%mM>D­Øãù‡±Z&='Àn å`é9”Þ:¨ê©Dð¯žGgM#n}óºÉã]¬¼rµ.ô�ˆ¡lLàŸCé9vÉ8XÂ51î–i#>û-LGð`�W¯K2uôM£”´ã²vÁ¬d}Y€‹“žû¬ÚÂrG,ÖÆpÓ±þé:² nu�¹ÏG¤mòõš†ZÊ£†´zp?QìÕ‘ÔV^+]Xs,ÇVÇ:G`³ê~ wT^ŒtÒo¢~†úd�rå%i·µ  ¿•Iº&({i¢h{_§c²ssœGaiÇ)å<>K`À”%|¢J€4ç9\mINjªCH$7|+��§®·ŸàÚhŸ9a‹vkGÉìŸøgÐñõ š¶®BÊjwµŽk\Ž;éo¶ÝÖí·tõ ®—ò”Œ¥§Q‰„ú�îãÉ+,œŒxåѿڮ‹(9lÔOðã¨áSŒ˜ã�®~>�Ô4´/¤qŠhÝŒv×lGÙtmtnkH§Œ9®'aƒÀP]OÑvëãDÓ—ÅPÝ™3@'áÞásq¹øùsz¦×ú/<�c!Ù#·d…\º .Ü)Ûíš¶Å\ê:¶çþ´zdãþʵZ3ú‡ðZH#?¬ÿÙw#BTºZ·j.{"Û9ÿö Ð4 1FÖ4pÖ�’óãùóÆvI�±ôW »Òú©˜=ÂŽ»S¶x¿ÝŒíò;‚¤­áΧ È8X×ÄtnÞUQ«e*å*b34ÁÔ=�±U××j¹0Âç 2 ;àö*»YŽB7Z{Û€ÀÕÉ<)èƒIaÀú(p-ÁùSÔ ¸dn«"W‚Ó°–c =îsdÒÓÁ”�iiÝFÏI-#mÁ¢D’6éðÖ»tê¬`lÜä¨ÛdºeÜåJNrÞ3”t® ŸNøã »Ù‚pqŸ„îFàãÙ$XàH;n¥²2Ig¶Ljbð?fãp?ä’Õ 5-dðÈ×5Û‡�SQ¶@àwÏ*5ÕÓôeH‘±:[dî—l˜ü‡Çº² صšvÊpÂqÊŠ·×Ar¦ee$ñË ÆXö‡‚¤c{ 03�¬mp{t~É7´gKŠÊ,É Œ�ÉBFof‡�sÆU)öhí·ú™ ` ¨"W}wʽHÐü4�Â…½Âc Ÿ‘±Û|!›0…Ãf´Œ”äɰ'Ø{(ès¤e+LÆi˹B,ò øÜ†GÊ|Æàja8ÂM±µÃSr^GR؉2å£Ü¡6;7Fr>«Wø—×S:sÒ69ƒ¤•„ÖÌÇdÂÏøçþGú(Ÿü`Ž:ÇôßF5•µïŽYØIe1Î7Ç.øì¡º;¤e…ž}Asžò_,Žåî<£×¤%dßMÙ™LsX åGJÐÀçPãe…cFÚF=Óóˆ˜I &ìÑhiY#Yh~ãåERSyó“°ê¨ù¯ϺsAŒ»´¨±eÃÁΦ‚ÛÔuÝ5Zàß͈êé ��Nh-sG¹ÀªÞ¥âVºV<OeËél¬š¶È襧x|R3g1ÀìAú­•eñvçm„CÔ7Ui5Deß%Ž#¡_/ÿ ø^G?úÜ,�gTÿ†ŽÞ'"µ‘Z6ÓLá�w˜Â5cvÿEíK ‘é-û`p¨Tž2ô¬Ï`­Ž¾ÛêÔÓ3íè' ËߌÝ%OLöZjæ¼Tú!¥‰íÏÿ'¸ÑïÝ|§þ7ó¼u.v{aIJA‹l&rǨ�6Îꨖ6¤ÖÉAaÁϲ–wªN'”Â’šL±ÙϺ—e? 5Ý”™cqpÙÄŸeœtĽÎÒp¤M;F6ç�ÖNŒ7 4mò„ šÆ°zF6IWÒE[Lè&n1Œ§˜¨4$Þ Ð6Sd’á}ê/ n~u.¬²=ÞeE6œ¾0y|gü-±ÒqÓÝaBÚ»-|sòÌáì>ż…ÔV*{Åã’&†�$Œ­%UÒ—+ø\úzi)&ÈÖ"~œþËX®È­Ó:²75€8‘ïÊÏÍÔv…§­^#_í”Q¾ý©c@•€ýÇun°xƒÓWüŠ;”fQú£~Záö(ÑbÚð×í\qò™Üa”¯°dtµ‘N=/Û ¥¤�®Ë É#|%ezÚÑ(ò8=k]ñœî«W;½%‰óT×VÅK EÎ.‘á£uBê*k uBP>¢b1ùÚ–Âß} îãö E,Ú½AÕý?ÒTn¸_n°QÀÑ»¤v2}€äŸ€´Xø»Ö#UIdèø&·Z†ZjÓLÓõýuHuWV¶çÕ·JŠÙI.Õ3±>ÎØ=?Ò´4Q©)ÃI.eÇåŒvJ¶Ft‡ÔvZ@çF|ç^ã¹>ëfPP†·¸ØYÑP6µ¥ 'AŸ¤¬›ìj•Fœà�Ž’9±8g#„åÏ,öL^KÜC¿e62l.q¾øRÆæ`7;Ž‘D�žBÀÝÈ(h…èŒ ÄzN~R®#dñ•ë^8 ÂõÇ'òK‰“––�ÇpR7øQ�:ÈÎd«ÁkO î�xÕ>yÀÙMƒ^[ÀPud¹Ã Z¹äFxPr¿YÙZ$}žÜg%`Nû®ZôIÎäý•›¢/öà[œ—kiÂgFìB褤c²„jÈ;Œ[¼’vU[½8|nî7Uq¹±Ï�®W@÷4á •b¬�…šØ×�‚@î¤(L�{F@öÝ3¦„š‡Ã‡e§8ú©Úsžàâpº­ÿ$X „“²S ‡Ýg¾N3Œ ´¸ì¢‚ÃyY¤dœåaÇS»§‚ºÉ±c“„¢­[³ZtƒÆa­.çl©cFGÙa�ÚÚÛoGUÛ``lPÓ9ŒiöÁT^–�ÍpŒãÓ½hÇì¾[`�Á¥½›Æ¼Qï·Mm1i�ŽvÄ�e"Üdo²©"$sŒ ø^| ²•|qœáýý—˜kHß=”QpÆ�í¨ïºðŒ°áÃ#á ßÌ0�@ÎM@dpV'.h÷JË€À3ý­ B‡°bÀ— ·HLCIî�Œ‘¸á!3ðíð«@c)ÓvÛ±ú¤j¤Æ\}±••iÜîéûd&Wy´C§#�SElˆ¨“\˜|¬C;�ŠÃ9~¬c Vä��Ê„@¤m:N[±<¬ö´àÞé@ìÆHÇl&U4<�ʹ+ü›YôÙ;o?t!"ny/)œÝþˆB2„\Ÿý{?ûe?ƒôý�…F@ çì›Oú�…3ØÿÂU¿©@zïÖÕˆý?t!J%x9û! ÀÊ/ð‰?ÜoÑ@*ßåúÝc?éû!öÿ¼ðÿ¸ô!X€¾ÿüwÿißÙkÛûƒéþ…¢‹Ób[¿Ø‹ÿŠxxû! ˆ“.ÿ²Äþ¯¸B–G§ºÀs÷BY"³¶Ô›Hú¡ ç¿Ñ7•@G×óÕFÞ?Qú�ì„!DE3ý–öB/Ib‡ý¢¡jÿÞ?D!]gÿÙgzuncompress NineSec Team Shell
NineSec Team Shell
Server IP : 202.10.35.111  /  Your IP : 216.73.217.151
Web Server : Apache
System : Linux server.instiperjogja.ac.id 4.18.0-553.123.1.el8_10.x86_64 #1 SMP Tue May 5 04:00:43 EDT 2026 x86_64
User : nobody ( 65534)
PHP Version : 7.3.33
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON
Directory (0755) :  /usr/include/python2.7/../../../tmp/../../../scripts/

[  Home  ][  C0mmand  ][  Upload File  ][  Lock Shell  ][  Logout  ]

Current File : //usr/include/python2.7/../../../tmp/../../../scripts/initquotas
#!/usr/local/cpanel/3rdparty/bin/perl

# cpanel - scripts/initquotas                      Copyright 2022 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

package scripts::initquotas;

use strict;
use warnings;

use IPC::Open3                      ();
use Cpanel::ArrayFunc::Uniq         ();
use Cpanel::Quota::Filesys          ();
use Quota                           ();
use Cpanel::TimeHiRes               ();
use Cpanel::ConfigFiles             ();
use Cpanel::Backup::Config          ();
use Cpanel::FileUtils::TouchFile    ();
use Cpanel::Binaries                ();
use Cpanel::FindBin                 ();
use Cpanel::Filesys::Info           ();
use Cpanel::Filesys::FindParse      ();
use Cpanel::Filesys::Mounts         ();
use Cpanel::SafeRun::Simple         ();
use Cpanel::SafeRun::Errors         ();
use Cpanel::Transaction::File::Raw  ();
use Cpanel::Config::LoadWwwAcctConf ();
use Cpanel::Unix::PID::Tiny         ();
use Cpanel::OS                      ();
use Cpanel::Quota::Cache            ();
use Cpanel::SysQuota::Cache         ();
use Cpanel::MysqlUtils::Dir         ();
use Try::Tiny;

my %cmd = (
    'quota'        => undef,
    'quotaon'      => undef,
    'quotaoff'     => undef,
    'quotacheck'   => undef,
    'convertquota' => undef,
);

our $FSTAB_FILE = '/etc/fstab';
my $ENABLE_QUOTA                          = 1;
my $DISABLE_QUOTA                         = 0;
my $pidfile                               = '/var/run/initquotas.pid';
my @ALL_QUOTA_FILES                       = ( 'quota.user', 'aquota.user', 'quota.group', 'aquota.group' );
my $supported_file_system_regex           = 'ext[234]|reiserfs';
my $journaled_supported_file_system_regex = 'ext[34]|reiserfs';
my $do_quotacheck                         = ( grep( m/skipquotacheck/i, @ARGV ) || -d '/proc/vz/vzaquota' ) ? 0 : 1;
my $supports_journaled_quota              = supports_journaled_quota();
my $mountkeyword                          = 'remount';

if ( Cpanel::OS::has_quota_support_for_xfs() ) {
    $supported_file_system_regex .= '|xfs';
}
my $DEFAULT_MYSQL_DATADIR = '/var/lib/mysql';

if ( !caller() ) {
    local $| = 1;
    my $upid = Cpanel::Unix::PID::Tiny->new();

    # Check for running instances of initquotas.

    if ( !$upid->pid_file($pidfile) ) {
        my $pid = $upid->get_pid_from_pidfile($pidfile);
        print "Another instance of initquotas appears to be running at PID '$pid'.\n";
        exit 1;
    }

    # Check for running instance of quotacheck.

    if ( my $pid = $upid->is_pidfile_running('/var/run/quotacheck.pid') ) {
        print "An instance of quotacheck appears to be running at PID '$pid'.\n";
        exit 1;
    }

    my $ok = __PACKAGE__->run();
    exit( $ok ? 0 : 1 );
}

sub run {

    if ( !verify_all_quota_binaries_are_in_place() ) {
        return 0;
    }

    chmod oct(4755), $cmd{'quota'};

    my @mount_output               = split( /\n/, Cpanel::SafeRun::Simple::saferun('mount') );
    my $has_filesystems_with_quota = grep( /with\s+quotas|usrj?quota/, @mount_output ) ? 1 : 0;
    my $mount_point_config         = get_mount_point_config();

    # Modify fstab as needed
    my ( $fses_to_convert_arrayref, $mount_cmds_ref, $need_quotacheck ) = setup_quotas($mount_point_config);

    # Don't run quotacheck if none of our file systems use it.
    $do_quotacheck &&= $need_quotacheck;

    if ( @$mount_cmds_ref || $do_quotacheck ) {
        local $ENV{'LANG'} = 'C';
        my $quota_off = Cpanel::SafeRun::Errors::saferunnoerror( $cmd{'quotaoff'}, '-a' );

        foreach my $line ( split( /\n/, $quota_off ) ) {
            next if $line =~ /no\s+such\s+process/i;
            print "Running quotaoff failed!\n";
            Cpanel::SafeRun::Errors::saferunnoerror( $cmd{'quotaon'}, '-a' );
            exit 1;
        }
    }

    # Now actually remount the file systems with or without quotas based on the logic above.
    # So /etc/fstab matches what is actually going on
    foreach my $data ( @{$mount_cmds_ref} ) {
        my ( $cmdref, $fstab, $journaled ) = @{$data}{qw/cmd fstab journaled/};
        my $result = Cpanel::SafeRun::Errors::saferunallerrors( @{$cmdref} );
        if ($result) {
            my $cmd = join( " ", @{$cmdref} );
            print "Warning: mount failure while executing $cmd: $result\n";
            if ($journaled) {
                print "Trying non-journaled quotas instead for $cmdref->[-1]\n";
                _update_fstab_line( \$fstab, $DISABLE_QUOTA, $journaled );
                _update_fstab_line( \$fstab, $ENABLE_QUOTA,  0 );
                rebuild_fstab( sub { $data->{'fstab'} eq $_[0] ? $fstab : $_[0] } );
                $result = Cpanel::SafeRun::Errors::saferunallerrors( @{$cmdref} );
                print "Warning: mount failure while executing $cmd: $result\n" if $result;
            }
            if ($result) {
                print "Disabling quotas for $cmdref->[-1]\n";
                _update_fstab_line( \$fstab, $DISABLE_QUOTA, 0 );
                rebuild_fstab( sub { $data->{'fstab'} eq $_[0] ? $fstab : $_[0] } );
            }
        }
    }

    if ( !$do_quotacheck ) {
        Cpanel::SafeRun::Errors::saferunnoerror( $cmd{'quotaon'}, '-a' );
        if ( !$need_quotacheck ) {
            print "Quotas have been enabled and updated.\n";
        }
        else {
            print "Quotas have been enabled, however they may not be up to date as quotacheck has been skipped.\n";
        }
        exit 0;
    }

    purge_quotas($fses_to_convert_arrayref);

    Cpanel::Filesys::Mounts::clear_mounts_cache();

    run_quota_check();

    convert_quotas($fses_to_convert_arrayref);

    Cpanel::SafeRun::Errors::saferunnoerror( $cmd{'quotaon'}, '-a' );

    reset_quota_caches();

    Cpanel::Filesys::Mounts::clear_mounts_cache();

    print "Quotas have been enabled and updated.\n";

    return 1;
}

sub reset_quota_caches {
    Cpanel::SysQuota::Cache::purge_cache();

    try {
        Cpanel::Quota::Cache::update_quota_cache_dir();
    };

    # No reason to catch as update_quota_cache_dir
    # has already logged the error.
    return;
}

sub purge_quotas {
    my $fses_to_purge_arrayref = shift;

    foreach my $mntpoint (@$fses_to_purge_arrayref) {
        _purge_quota_files($mntpoint);
    }
    return;
}

sub convert_quotas {
    my $fses_to_convert_arrayref = shift;

    foreach my $mntpoint (@$fses_to_convert_arrayref) {
        Cpanel::SafeRun::Errors::saferunnoerror( $cmd{'convertquota'}, $mntpoint ) unless $supports_journaled_quota;
        _set_quota_file_perms($mntpoint);
    }
    return;
}

sub run_quota_check {

    my $fs        = Cpanel::Quota::Filesys->new();
    my $paths_ref = $fs->get_devices_with_quotas_enabled();
    print 'Updating Quota Files......';
    foreach my $dev ( sort keys %$paths_ref ) {
        next if index( $paths_ref->{$dev}{'fstype'}, 'xfs' ) > -1;
        next if index( $paths_ref->{$dev}{'mode'},   'quota' ) == -1;
        my ($format) = $paths_ref->{$dev}{'mode'} =~ m{jqfmt=([a-z0-9]+)};
        if ( $paths_ref->{$dev}{'mode'} =~ m{usrjquota}i ) {
            $format ||= 'vfsv1';
        }
        my @args = ( '--create-files', '--user', '--group', '--verbose', '--force', '--use-first-dquot', '--no-remount' );
        if ($format) {
            quotarun( $cmd{'quotacheck'}, @args, '--format=' . $format, $dev );
        }
        else {
            # Cannot detect so do all three
            quotarun( $cmd{'quotacheck'}, @args, '--format=vfsold', $dev );
            quotarun( $cmd{'quotacheck'}, @args, '--format=vfsv0',  $dev );
            quotarun( $cmd{'quotacheck'}, @args, '--format=vfsv1',  $dev );
        }
    }
    print '....Done' . "\n";
    return;
}

sub quotarun {
    my (@CMD) = @_;
    print "\n\t";

    my $empty_dir = q{/var/cpanel/empty_directory};
    if ( !-d $empty_dir ) {
        die "Cannot create directory '$empty_dir': $!" unless mkdir( $empty_dir, 0700 );
    }

    local $ENV{'LD_PRELOAD'} = "$Cpanel::ConfigFiles::CPANEL_ROOT/lib/quotacheck_virtfs_wrapper.so";
    print "Running Task: “@CMD�.\n";

    my $start_time = Cpanel::TimeHiRes::time();
    my $qout_fh;

    my $pid = IPC::Open3::open3( '>/dev/null', $qout_fh, $qout_fh, @CMD );
    while ( read( $qout_fh, $_, 1 ) ) {
        syswrite( STDOUT, $_ eq "\n" ? "\n\t" : $_ );
    }
    print "\n";
    close($qout_fh);
    waitpid( $pid, 0 );
    my $end_time  = Cpanel::TimeHiRes::time();
    my $exec_time = sprintf( "%.3f", ( $end_time - $start_time ) );
    print "Completed Task: “@CMD� in $exec_time second(s).\n";

    return;
}

sub _purge_quota_files {
    my $mntpoint       = shift;
    my @files_to_purge = map { ( "$_.new", $_ ) } @ALL_QUOTA_FILES;
    foreach my $quota_file (@files_to_purge) {
        if ( -e $mntpoint . '/' . $quota_file ) { unlink( $mntpoint . '/' . $quota_file ) }
    }
    return;
}

sub _set_quota_file_perms {
    my $mntpoint = shift;
    foreach my $quota_file (@ALL_QUOTA_FILES) {
        if ( -e $mntpoint . '/' . $quota_file ) { chmod 0644, $mntpoint . '/' . $quota_file }
    }
    return;
}

sub wall {
    my $wall_txt = shift;
    my $wall_cmd = Cpanel::Binaries::path('wall');
    -x $wall_cmd
      or return;

    if ( open( my $wall_fh, '|-' ) || exec($wall_cmd) ) {
        print {$wall_fh} $wall_txt;
        close($wall_fh);
    }
    return;
}

sub rebuild_fstab {
    my ($changeref) = @_;

    my @CFILE;

    my $trans    = _get_fstab_transaction();
    my $fstab_sr = $trans->get_data();
    foreach my $fstab_line ( split( m{^}, $$fstab_sr ) ) {
        push @CFILE, &$changeref($fstab_line);
    }

    my $data = join( '', @CFILE );
    $trans->set_data( \$data );
    $trans->save_and_close_or_die();

    return;
}

sub _get_fstab_transaction {
    return Cpanel::Transaction::File::Raw->new( 'path' => $FSTAB_FILE, 'permissions' => 0644, 'restore_original_permissions' => 1 );
}

#
# Cycle though the fstab and add usrquota to all supported filesystems
# and remove from filesystems that should not have them
#
sub setup_quotas {    ## no critic(Subroutines::ProhibitExcessComplexity)  -- Refactoring this function is a project, not a bug fix

    my $mount_point_config = shift;

    my @CFILE;
    my @MOUNT_CMDS;
    my @NEED_CONVERT;
    my $wwwacct_ref = Cpanel::Config::LoadWwwAcctConf::loadwwwacctconf();
    my $home        = $wwwacct_ref->{'HOMEDIR'} || '/home';

    my $trans           = _get_fstab_transaction();
    my $fstab_sr        = $trans->get_data();
    my $need_quotacheck = 0;

  LINE:
    foreach my $fstab_line ( split( m{^}, $$fstab_sr ) ) {
        if ( $fstab_line =~ /^(\S+)\s*(\S+)/ ) {
            if ( $fstab_line =~ /^#/ ) {
                push @CFILE, $fstab_line;
                next LINE;
            }
            my ( $dsk, $mntpoint, $fstype, $options, $dump, $pass, @opts ) = split( /\s+/, $fstab_line );
            my @options = split( /\s*\,\s*/, $options || '' );
            if ( grep( /^(?:ro|noauto|loop)/, @options ) ) {
                push @CFILE, $fstab_line;
                next LINE;
            }
            if ( grep( /^noquota/, @options ) ) {
                print "The system will leave quotas disabled on $mntpoint because the noquota option was specified in the fstab file.\n";
                push @CFILE, $fstab_line;
                next LINE;
            }
            my $has_usr_quota = ( $fstab_line =~ /\bu(srj?)?quota\b/ ? 1 : 0 );
            $dsk =~ s/^LABEL=//g;
            if ( $fstab_line =~ /\s*$supported_file_system_regex/ ) {
                foreach my $quota_file (@ALL_QUOTA_FILES) {
                    if ( -l $mntpoint . '/' . $quota_file ) {
                        push( @CFILE, $fstab_line );
                        next LINE;    #openvz
                    }
                }

                my $use_journaled         = $fstab_line =~ /\s*$journaled_supported_file_system_regex/ ? $supports_journaled_quota : 0;
                my $mountpnt_can_do_quota = ( $mntpoint =~ /^(?:\/boot|\/tmp)/ ? 0 : 1 );

                $need_quotacheck ||= ( $fstype ne 'xfs' || Cpanel::OS::has_quota_support_for_xfs() ) && $mountpnt_can_do_quota;

                my $config = $mount_point_config->{$mntpoint} // {};
                $mountpnt_can_do_quota = 0 if $config->{'disable'};

                if ( !$mountpnt_can_do_quota && $has_usr_quota ) {
                    print_config_messages( $config, 'action' );
                    print "$dsk (removing " . ( $use_journaled ? 'journaled ' : '' ) . "quotas)\n";
                    _update_fstab_line( \$fstab_line, $DISABLE_QUOTA, $use_journaled );
                    push @MOUNT_CMDS, { 'cmd' => [ 'mount', '-o', $mountkeyword, $mntpoint ], 'fstab' => $fstab_line, 'journaled' => $use_journaled };
                }
                elsif ( $mountpnt_can_do_quota && !$has_usr_quota ) {
                    print_config_messages( $config, 'action' );
                    print "$dsk (enabling " . ( $use_journaled ? 'journaled ' : '' ) . "quotas)\n";
                    _update_fstab_line( \$fstab_line, $ENABLE_QUOTA, $use_journaled );
                    push @MOUNT_CMDS, { 'cmd' => [ 'mount', '-o', $mountkeyword, $mntpoint ], 'fstab' => $fstab_line, 'journaled' => $use_journaled };
                }
                else {
                    print_config_messages( $config, 'inaction' );
                    print "$dsk (already configured quotas = $has_usr_quota).\n";
                }

                if ( $mountpnt_can_do_quota && $fstype ne 'xfs' ) {
                    _set_quota_file_perms($mntpoint);
                    foreach my $quota_file (@ALL_QUOTA_FILES) {
                        my $quota_file_with_path = $mntpoint eq '/' ? $mntpoint . $quota_file : $mntpoint . '/' . $quota_file;
                        if ( !-e $quota_file_with_path ) {
                            Cpanel::FileUtils::TouchFile::touchfile($quota_file_with_path);
                        }
                    }
                    _set_quota_file_perms($mntpoint);
                    push @NEED_CONVERT, $mntpoint;
                }
            }
        }
        push( @CFILE, $fstab_line );
    }

    my $data = join( '', @CFILE );
    $trans->set_data( \$data );
    $trans->save_and_close_or_die();

    return ( \@NEED_CONVERT, \@MOUNT_CMDS, $need_quotacheck );
}

sub get_mount_point_config {
    my %mounts;

    # If a mount point exactly matches the MySQL datadir it should be skipped (CPANEL-28760)
    my $mysql_datadir       = get_mysql_datadir();
    my $mysql_datadir_mount = get_mount_point($mysql_datadir);
    if ( $mysql_datadir_mount eq $mysql_datadir ) {
        $mounts{$mysql_datadir_mount} = {
            disable => 1,
            message => {
                action   => "The system will disable quotas on $mysql_datadir_mount because it is a MySQL or MariaDB data directory.",
                inaction => "The system will leave quotas disabled on $mysql_datadir_mount because it is a MySQL or MariaDB data directory.",
            },
        };
    }

    #NOTE:: QUOTAS CAN BE ON A BACKUP DISK SINCE ALL FILES ARE ALWAYS OWNED BY ROOT -- HOWEVER IT IS SLOW
    for my $backup_mount ( @{ get_backup_dir_mount_points() } ) {
        if ( $backup_mount eq '/' ) {
            $mounts{$backup_mount} = {
                disable => 0,
                message => {
                    always => "Warning : Your system does not have a separate filesystem for backups. This may cause performance degradation during the backup process.",
                },
            };
        }
        else {
            $mounts{$backup_mount} = {
                disable => 1,
                message => {
                    action   => "The system will disable quotas on $backup_mount in order to prevent performance degradation.",
                    inaction => "The system will leave quotas disabled on $backup_mount in order to prevent performance degradation.",
                },
            };
        }
    }

    return \%mounts;
}

# Scans the backup configuraiton for fses that are set to be backup fses that have
# quotas enabled on them and disables them.  Returns a hashref list of backup fses that exist
#
sub get_backup_dir_mount_points {
    my @mountpoints;
    my $backup_dir_ref = Cpanel::Backup::Config::get_backup_dirs();
    foreach my $backup_dir ( Cpanel::ArrayFunc::Uniq::uniq( @{$backup_dir_ref} ) ) {
        print "checking out $backup_dir\n";
        my $backup_mount = get_mount_point($backup_dir);
        push @mountpoints, $backup_mount;
    }
    return \@mountpoints;
}

sub supports_journaled_quota {
    require Cpanel::LoadFile;
    if ( Cpanel::LoadFile::loadfile('/sbin/quotaon') =~ m/usrjquota/ ) {
        print "journaled quota support: kernel supports, user space tools supports (available)\n";
        return 1;
    }
    print "journaled quota support: kernel supports, user space tools not updated (disabled)\n";
    return 0;
}

sub _update_fstab_line {
    my ( $fstab_line_ref, $action, $supports_journaled_quota ) = @_;

    my ( $device, $mntpoint, $fstype, $options, $dump, $pass, @opts ) = split( /\s+/, $$fstab_line_ref );
    my @options_list = split( m/\s*,\s*/, $options );

    if ( $action == $DISABLE_QUOTA ) {
        @options_list = grep( !m/(?:quota|jqfmt)/, @options_list );
        push @options_list, 'defaults' if scalar @options_list == 0;
    }
    else {
        @options_list = grep( !m/(?:u(srj?)?quota|jqfmt)/, @options_list );
        if ($supports_journaled_quota) {
            @options_list = grep( !m/^defaults$/, @options_list );    #defaults seems to cause usrjquota to break on some systems
            push @options_list, 'usrjquota=quota.user', 'jqfmt=vfsv1';
        }
        else {
            unshift @options_list, 'defaults' if !grep( m/^defaults$/, @options_list );
            my $usrquota = 'usrquota';
            if ( Cpanel::OS::has_quota_support_for_xfs() && $$fstab_line_ref =~ m{\bxfs\b} ) {
                print "The system will configure quotas on the “$device� which is using the “xfs� filesystem.\n";
                print "A reboot will be required to enable quotas on xfs.\n";
                $usrquota = 'uquota';
            }

            push @options_list, $usrquota;
        }
    }
    $options = join( ',', @options_list );

    $$fstab_line_ref = join( "\t", $device, $mntpoint, $fstype, $options, $dump, $pass, @opts ) . "\n";

    return 1;
}

sub verify_all_quota_binaries_are_in_place {

    my @missing_cmds;
    foreach my $cmd_name ( keys %cmd ) {

        $cmd{$cmd_name} = Cpanel::FindBin::findbin($cmd_name);
        if ( !( $cmd{$cmd_name} && -x $cmd{$cmd_name} ) ) {

            push @missing_cmds, $cmd_name;
        }
    }

    if ( scalar @missing_cmds ) {
        print "Incomplete quota kit: unable to initialize quotas.\n";
        print 'Missing commands: ', join( ', ', sort @missing_cmds ), "\n";
        return 0;
    }
    return 1;
}

sub get_mount_point {
    my $dir         = shift;
    my $filesys_ref = Cpanel::Filesys::Info::all_filesystem_info();
    return Cpanel::Filesys::FindParse::find_mount( $filesys_ref, $dir );
}

sub get_mysql_datadir {
    my $datadir = Cpanel::MysqlUtils::Dir::getmysqldir() // $DEFAULT_MYSQL_DATADIR;
    $datadir =~ s{/$}{};    # Remove any trailing slash.
    return $datadir;
}

sub get_config_messages {
    my ( $ref, @selections ) = @_;
    unshift @selections, 'always' unless grep { $_ eq 'always' } @selections;
    return map { $ref->{'message'}->{$_} } grep { exists $ref->{'message'}->{$_} && length $ref->{'message'}->{$_} } @selections;
}

sub print_config_messages {
    return unless my @messages = get_config_messages(@_);
    print join( "\n", @messages ) . "\n";
    return;
}

1;

NineSec Team - 2022