123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624 |
- /*##############################################################################
- HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ############################################################################## */
- #define da_decl DECL_EXPORT
- #include "platform.h"
- #include "jlib.hpp"
- #include "jfile.hpp"
- #include "jlzw.hpp"
- #include "jmisc.hpp"
- #include "jtime.hpp"
- #include "jregexp.hpp"
- #include "jexcept.hpp"
- #include "jsort.hpp"
- #include "jptree.hpp"
- #include "jbuff.hpp"
- #include "dafdesc.hpp"
- #include "dasds.hpp"
- #include "dasess.hpp"
- #include "daclient.hpp"
- #include "daserver.hpp"
- #include "dautils.hpp"
- #include "danqs.hpp"
- #include "mputil.hpp"
- #include "dadfs.hpp"
- #include "eclhelper.hpp"
- #include "seclib.hpp"
- #ifdef _DEBUG
- //#define EXTRA_LOGGING
- //#define TRACE_LOCKS
- #endif
- #define SDS_CONNECT_TIMEOUT (1000*60*60*2) // better than infinite
- #define SDS_SUB_LOCK_TIMEOUT (10000)
- #define SDS_TRANSACTION_RETRY (60000)
- #define SDS_UPDATEFS_TIMEOUT (10000)
- #define DEFAULT_NUM_DFS_THREADS 30
- #define TIMEOUT_ON_CLOSEDOWN 120000 // On closedown, give up on trying to join a thread in CDaliDFSServer after two minutes
- #define MAX_PHYSICAL_DELETE_THREADS 1000
- #if _INTERNAL_EDITION == 1
- #ifndef _MSC_VER
- #warning Disabling Sub-file compatibility checking
- #endif
- #else
- #define SUBFILE_COMPATIBILITY_CHECKING
- #endif
- //#define PACK_ECL
- #define SDS_GROUPSTORE_ROOT "Groups" // followed by name
- class CDistributedFile;
- enum MDFSRequestKind
- {
- MDFS_ITERATE_FILES,
- MDFS_UNUSED1,
- MDFS_GET_FILE_TREE,
- MDFS_GET_GROUP_TREE,
- MDFS_SET_FILE_ACCESSED,
- MDFS_ITERATE_RELATIONSHIPS,
- MDFS_SET_FILE_PROTECT,
- MDFS_ITERATE_FILTEREDFILES,
- MDFS_ITERATE_FILTEREDFILES2,
- MDFS_MAX
- };
- // Mutex for physical operations (remove/rename)
- static CriticalSection physicalChange;
- #define MDFS_GET_FILE_TREE_V2 ((unsigned)1)
- static int strcompare(const void * left, const void * right)
- {
- const char * l = (const char *)left;
- const char * r = (const char *)right;
- return stricmp(l,r);
- }
- inline unsigned ipGroupDistance(const IpAddress &ip,const IGroup *grp)
- {
- if (!grp)
- return (unsigned)-1;
- return grp->distance(ip);
- }
- inline unsigned groupDistance(IGroup *grp1,IGroup *grp2)
- {
- if (grp1==grp2)
- return 0;
- if (!grp1||!grp2)
- return (unsigned)-1;
- return grp1->distance(grp2);
- }
- static StringBuffer &normalizeFormat(StringBuffer &in)
- {
- in.toLowerCase();
- for (unsigned i = 0;i<in.length();)
- {
- switch (in.charAt(i)) {
- case '-':
- case '_':
- case ' ':
- in.remove(i,1);
- break;
- default:
- i++;
- break;
- }
- }
- return in;
- }
- static StringBuffer &getAttrQueryStr(StringBuffer &str,const char *sub,const char *key,const char *name)
- {
- assertex(key[0]=='@');
- str.appendf("%s[%s=\"%s\"]",sub,key,name);
- return str;
- }
- static IPropertyTree *getNamedPropTree(IPropertyTree *parent,const char *sub,const char *key,const char *name,bool preload)
- { // no create
- if (!parent)
- return NULL;
- StringBuffer query;
- getAttrQueryStr(query,sub,key,name);
- if (preload)
- return parent->getBranch(query.str());
- return parent->getPropTree(query.str());
- }
- static IPropertyTree *addNamedPropTree(IPropertyTree *parent,const char *sub,const char *key,const char *name, IPropertyTree* init=NULL)
- {
- IPropertyTree* ret = init?createPTreeFromIPT(init):createPTree(sub);
- assertex(key[0]=='@');
- ret->setProp(key,name);
- ret = parent->addPropTree(sub,ret);
- return LINK(ret);
- }
- const char *normalizeLFN(const char *s,StringBuffer &tmp)
- {
- CDfsLogicalFileName dlfn;
- dlfn.set(s);
- return dlfn.get(tmp).str();
- }
- static IPropertyTree *getEmptyAttr()
- {
- return createPTree("Attr");
- }
- RemoteFilename &constructPartFilename(IGroup *grp,unsigned partno,unsigned partmax,const char *name,const char *partmask,const char *partdir,unsigned copy,ClusterPartDiskMapSpec &mspec,RemoteFilename &rfn)
- {
- partno--;
- StringBuffer tmp;
- if (!name||!*name) {
- if (!partmask||!*partmask) {
- partmask = "!ERROR!._$P$_of_$N$"; // could use logical tail name if I had it
- IERRLOG("No partmask for constructPartFilename");
- }
- name = expandMask(tmp,partmask,partno,partmax).str();
- }
- StringBuffer fullname;
- if (findPathSepChar(name)==NULL)
- addPathSepChar(fullname.append(partdir));
- fullname.append(name);
- unsigned n;
- unsigned d;
- mspec.calcPartLocation(partno,partmax,copy,grp?grp->ordinality():partmax,n,d);
- setReplicateFilename(fullname,d);
- SocketEndpoint ep;
- if (grp)
- ep=grp->queryNode(n).endpoint();
- rfn.setPath(ep,fullname.toLowerCase().str());
- return rfn;
- }
- inline void LOGPTREE(const char *title,IPropertyTree *pt)
- {
- StringBuffer buf;
- if (pt) {
- toXML(pt,buf);
- PROGLOG("%s:\n%s\n",title,buf.str());
- }
- else
- PROGLOG("%s : NULL",title);
- }
- inline void LOGFDESC(const char *title,IFileDescriptor *fdesc)
- {
- if (fdesc) {
- Owned<IPropertyTree> pt = fdesc->getFileTree();
- LOGPTREE(title,pt);
- }
- else
- PROGLOG("%s : NULL",title);
- }
- class DECL_EXCEPTION CDFS_Exception: implements IDFS_Exception, public CInterface
- {
- int errcode;
- StringAttr errstr;
- public:
- CDFS_Exception(int _errcode, const char *_errstr)
- : errstr(_errstr)
- {
- errcode = _errcode;
- }
- int errorCode() const { return errcode; }
- StringBuffer & errorMessage(StringBuffer &str) const
- {
- if (errcode==DFSERR_ok)
- return str;
- str.append("DFS Exception: ").append(errcode);
- switch(errcode) {
- case DFSERR_LogicalNameAlreadyExists:
- return str.append(": logical name ").append(errstr).append(" already exists");
- case DFSERR_CannotFindPartFileSize:
- return str.append(": Cannot find physical file size for ").append(errstr);
- case DFSERR_CannotFindPartFileCrc:
- return str.append(": Cannot find physical file crc for ").append(errstr);
- case DFSERR_LookupAccessDenied:
- {
- StringBuffer ip;
- queryMyNode()->endpoint().getIpText(ip);
- return str.appendf(" Lookup access denied for scope %s at Dali %s", errstr.str(), ip.str());
- }
- case DFSERR_CreateAccessDenied:
- return str.append(" Create access denied for scope ").append(errstr);
- case DFSERR_PhysicalPartAlreadyExists:
- return str.append(": physical part ").append(errstr).append(" already exists");
- case DFSERR_PhysicalPartDoesntExist:
- return str.append(": physical part ").append(errstr).append(" doesnt exist");
- case DFSERR_ForeignDaliTimeout:
- return str.append(": Timeout connecting to Dali Server on ").append(errstr);
- case DFSERR_ClusterNotFound:
- return str.append(": Cluster not found: ").append(errstr);
- case DFSERR_ClusterAlreadyExists:
- return str.append(": Cluster already exists: ").append(errstr);
- case DFSERR_LookupConnectionTimout:
- return str.append(": Lookup connection timeout: ").append(errstr);
- case DFSERR_FailedToDeleteFile:
- return str.append(": Failed to delete file: ").append(errstr);
- }
- return str.append("Unknown DFS Exception");
- }
- MessageAudience errorAudience() const { return MSGAUD_user; }
- IMPLEMENT_IINTERFACE;
- };
- class CConnectLock
- {
- public:
- Owned<IRemoteConnection> conn;
- CConnectLock(const char *caller, const char *name, bool write, bool preload, bool hold, unsigned timeout)
- {
- unsigned start = msTick();
- bool first = true;
- for (;;)
- {
- try
- {
- unsigned mode = write ? RTM_LOCK_WRITE : RTM_LOCK_READ;
- if (preload) mode |= RTM_SUB;
- if (hold) mode |= RTM_LOCK_HOLD;
- conn.setown(querySDS().connect(name, queryCoven().inCoven() ? 0 : myProcessSession(), mode, (timeout==INFINITE)?1000*60*5:timeout));
- #ifdef TRACE_LOCKS
- PROGLOG("%s: LOCKGOT(%x) %s %s",caller,(unsigned)(memsize_t)conn.get(),name,write?"WRITE":"");
- LogRemoteConn(conn);
- PrintStackReport();
- #endif
- break;
- }
- catch (ISDSException *e)
- {
- if (SDSExcpt_LockTimeout == e->errorCode())
- {
- #ifdef TRACE_LOCKS
- PROGLOG("%s: LOCKFAIL %s %s",caller,name,write?"WRITE":"");
- LogRemoteConn(conn);
- #endif
- unsigned tt = msTick()-start;
- if (timeout!=INFINITE)
- throw;
- IWARNLOG("CConnectLock on %s waiting for %ds",name,tt/1000);
- if (first)
- {
- PrintStackReport();
- first = false;
- }
- if (tt>SDS_CONNECT_TIMEOUT)
- throw;
- e->Release();
- }
- else
- throw;
- }
- catch (IException *e)
- {
- StringBuffer tmp("CConnectLock ");
- tmp.append(caller).append(' ').append(name);
- EXCLOG(e, tmp.str());
- throw;
- }
- }
- }
- IRemoteConnection *detach()
- {
- #ifdef TRACE_LOCKS
- if (conn.get()) {
- PROGLOG("LOCKDETACH(%x)",(unsigned)(memsize_t)conn.get());
- LogRemoteConn(conn);
- }
- #endif
- return conn.getClear();
- }
- #ifdef TRACE_LOCKS
- ~CConnectLock()
- {
- if (conn.get()) {
- PROGLOG("LOCKDELETE(%x)",(unsigned)(memsize_t)conn.get());
- LogRemoteConn(conn);
- }
- }
- #endif
- };
- void ensureFileScope(const CDfsLogicalFileName &dlfn,unsigned timeout)
- {
- CConnectLock connlock("ensureFileScope",querySdsFilesRoot(),true,false,false,timeout);
- StringBuffer query;
- IPropertyTree *r = connlock.conn->getRoot();
- StringBuffer scopes;
- const char *s=dlfn.getScopes(scopes,true).str();
- for (;;) {
- IPropertyTree *nr;
- const char *e = strstr(s,"::");
- query.clear();
- if (e)
- query.append(e-s,s);
- else
- query.append(s);
- nr = getNamedPropTree(r,queryDfsXmlBranchName(DXB_Scope),"@name",query.trim().toLowerCase().str(),false);
- if (!nr)
- nr = addNamedPropTree(r,queryDfsXmlBranchName(DXB_Scope),"@name",query.str());
- r->Release();
- if (!e) {
- ::Release(nr);
- break;
- }
- r = nr;
- s = e+2;
- }
- }
- void removeFileEmptyScope(const CDfsLogicalFileName &dlfn,unsigned timeout)
- {
- CConnectLock connlock("removeFileEmptyScope",querySdsFilesRoot(),true,false,false,timeout); //*1
- IPropertyTree *root = connlock.conn.get()?connlock.conn->queryRoot():NULL;
- if (!root)
- return;
- StringBuffer query;
- dlfn.makeScopeQuery(query.clear(),false);
- StringBuffer head;
- for (;;) {
- if (query.length()) {
- const char *tail = splitXPath(query.str(),head.clear());
- if (!tail||!*tail)
- break;
- IPropertyTree *pt;
- if (head.length()) {
- query.set(head);
- pt = root->queryPropTree(query.str());
- }
- else
- pt = root;
- IPropertyTree *t = pt?pt->queryPropTree(tail):NULL;
- if (t) {
- if (t->hasChildren())
- break;
- pt->removeTree(t);
- if (root==pt)
- break;
- }
- else
- break;
- }
- else
- break;
- }
- }
- class CFileLockBase
- {
- IRemoteConnection *conn;
- protected:
- Owned<IRemoteConnection> lock;
- bool init(const char *lockPath, unsigned mode, IRemoteConnection *_conn, unsigned timeout, const char *msg)
- {
- conn = NULL;
- lock.clear();
- CTimeMon tm(timeout);
- for (;;)
- {
- try
- {
- lock.setown(querySDS().connect(lockPath, myProcessSession(), mode, timeout>60000 ? 60000 : timeout));
- if (lock.get())
- {
- conn = _conn;
- return true;
- }
- return false;
- }
- catch (ISDSException *e)
- {
- if (SDSExcpt_LockTimeout != e->errorCode() || tm.timedout())
- throw;
- IWARNLOG("CFileAttrLockBase(%s) blocked for %ds", msg, tm.elapsed()/1000);
- e->Release();
- }
- }
- }
- public:
- CFileLockBase()
- {
- conn = NULL;
- }
- ~CFileLockBase()
- {
- // if conn provided, 'lock' was just a surrogate for the owner connection, commit now to conn if write lock
- if (conn && lock)
- conn->commit();
- }
- IRemoteConnection *detach()
- {
- return lock.getClear();
- }
- void clear()
- {
- lock.clear();
- conn = NULL;
- }
- void commit() { if (conn) conn->commit(); }
- IPropertyTree *queryRoot() const
- {
- return lock.get() ? lock->queryRoot() : NULL;
- }
- };
- class CFileLock : protected CFileLockBase
- {
- protected:
- DfsXmlBranchKind kind;
- public:
- CFileLock()
- {
- kind = DXB_Internal;
- }
- bool init(const CDfsLogicalFileName &logicalName, DfsXmlBranchKind bkind, unsigned mode, unsigned timeout, const char *msg)
- {
- StringBuffer lockPath;
- logicalName.makeFullnameQuery(lockPath, bkind, true);
- if (CFileLockBase::init(lockPath, mode, NULL, timeout, msg))
- {
- kind = bkind;
- return true;
- }
- kind = DXB_Internal;
- return false;
- }
- bool init(const CDfsLogicalFileName &logicalName, unsigned mode, unsigned timeout, const char *msg)
- {
- StringBuffer lockPath;
- logicalName.makeFullnameQuery(lockPath, DXB_File, true);
- if (CFileLockBase::init(lockPath, mode, NULL, timeout, msg))
- {
- kind = DXB_File;
- return true;
- }
- // try super
- logicalName.makeFullnameQuery(lockPath.clear(), DXB_SuperFile, true);
- if (CFileLockBase::init(lockPath, mode, NULL, timeout, msg))
- {
- kind = DXB_SuperFile;
- return true;
- }
- kind = DXB_Internal;
- return false;
- }
- IRemoteConnection *detach() { return CFileLockBase::detach(); }
- IPropertyTree *queryRoot() const { return CFileLockBase::queryRoot(); }
- IRemoteConnection *queryConnection() const
- {
- return lock;
- }
- void clear()
- {
- CFileLockBase::clear();
- kind = DXB_Internal;
- }
- DfsXmlBranchKind getKind() const { return kind; }
- };
- class CFileSubLock : protected CFileLockBase
- {
- public:
- bool init(const CDfsLogicalFileName &logicalName, DfsXmlBranchKind bkind, unsigned mode, const char *subLock, IRemoteConnection *conn, unsigned timeout, const char *msg)
- {
- StringBuffer lockPath;
- logicalName.makeFullnameQuery(lockPath, bkind, true);
- lockPath.appendf("/%s", subLock);
- return CFileLockBase::init(lockPath, mode, conn, timeout, msg);
- }
- bool init(const CDfsLogicalFileName &logicalName, unsigned mode, const char *subLock, IRemoteConnection *conn, unsigned timeout, const char *msg)
- {
- StringBuffer lockPath;
- logicalName.makeFullnameQuery(lockPath, DXB_File, true);
- lockPath.appendf("/%s", subLock);
- if (CFileLockBase::init(lockPath, mode, conn, timeout, msg))
- return true;
- // try super
- logicalName.makeFullnameQuery(lockPath.clear(), DXB_SuperFile, true);
- return CFileLockBase::init(lockPath, mode, conn, timeout, msg);
- }
- };
- class CFileAttrLock : protected CFileSubLock
- {
- public:
- bool init(const CDfsLogicalFileName &logicalName, DfsXmlBranchKind bkind, unsigned mode, IRemoteConnection *conn, unsigned timeout, const char *msg)
- {
- return CFileSubLock::init(logicalName, bkind, mode, "Attr", conn, timeout, msg);
- }
- bool init(const CDfsLogicalFileName &logicalName, unsigned mode, IRemoteConnection *conn, unsigned timeout, const char *msg)
- {
- return CFileSubLock::init(logicalName, mode, "Attr", conn, timeout, msg);
- }
- IPropertyTree *queryRoot() const { return CFileSubLock::queryRoot(); }
- void commit() { CFileSubLock::commit(); }
- };
- class CFileLockCompound : protected CFileLockBase
- {
- public:
- bool init(const CDfsLogicalFileName &logicalName, unsigned mode, IRemoteConnection *conn, const char *subLock, unsigned timeout, const char *msg)
- {
- StringBuffer lockPath;
- if (subLock)
- lockPath.appendf("/_Locks/%s/", subLock);
- logicalName.makeXPathLName(lockPath);
- return CFileLockBase::init(lockPath, mode, conn, timeout, msg);
- }
- };
- class CFileSuperOwnerLock : protected CFileLockCompound
- {
- public:
- bool init(const CDfsLogicalFileName &logicalName, IRemoteConnection *conn, unsigned timeout, const char *msg)
- {
- return CFileLockCompound::init(logicalName, RTM_CREATE_QUERY | RTM_LOCK_WRITE | RTM_DELETE_ON_DISCONNECT, conn, "SuperOwnerLock", timeout, msg);
- }
- IRemoteConnection *detach()
- {
- return CFileLockCompound::detach();
- }
- bool initWithFileLock(const CDfsLogicalFileName &logicalName, unsigned timeout, const char *msg, CFileLock &fcl, unsigned fclmode)
- {
- // SuperOwnerLock while holding fcl
- IRemoteConnection *fclConn = fcl.queryConnection();
- if (!fclConn)
- return false; // throw ?
- CTimeMon tm(timeout);
- unsigned remaining = timeout;
- for (;;)
- {
- try
- {
- if (init(logicalName, NULL, 0, msg))
- return true;
- else
- return false; // throw ?
- }
- catch (ISDSException *e)
- {
- if (SDSExcpt_LockTimeout != e->errorCode() || tm.timedout(&remaining))
- throw;
- e->Release();
- }
- // release lock
- {
- fclConn->changeMode(RTM_NONE, remaining);
- }
- tm.timedout(&remaining);
- unsigned stime = 1000 * (2+getRandom()%15); // 2-15 sec
- if (stime > remaining)
- stime = remaining;
- // let another get excl lock
- Sleep(stime);
- tm.timedout(&remaining);
- // get lock again (waiting for other to release excl)
- {
- fclConn->changeMode(fclmode, remaining);
- fclConn->reload();
- }
- }
- }
- };
- class CScopeConnectLock
- {
- CConnectLock *lock;
- public:
- CScopeConnectLock()
- {
- lock = NULL;
- }
- CScopeConnectLock(const char *caller, const CDfsLogicalFileName &lname, bool write, bool preload, bool hold, unsigned timeout)
- {
- lock = NULL;
- init(caller, lname, write, preload, hold, timeout);
- }
- ~CScopeConnectLock()
- {
- delete lock;
- }
- bool init(const char *caller, const CDfsLogicalFileName &lname, bool write, bool preload, bool hold, unsigned timeout)
- {
- delete lock;
- StringBuffer query;
- lname.makeScopeQuery(query,true);
- lock = new CConnectLock(caller, query.str(), write, preload,hold, timeout);
- if (lock->conn.get()==NULL)
- {
- delete lock;
- lock = NULL;
- ensureFileScope(lname);
- lock = new CConnectLock(caller, query.str(), write, preload, hold, timeout);
- }
- return lock->conn.get()!=NULL;
- }
- IRemoteConnection *detach()
- {
- return lock?lock->detach():NULL;
- }
- IRemoteConnection *conn()
- {
- return lock?lock->conn:NULL;
- }
- IPropertyTree *queryRoot()
- {
- return (lock&&lock->conn.get())?lock->conn->queryRoot():NULL;
- }
- void remove()
- {
- if (lock&&lock->conn.get())
- lock->conn->close(true);
- }
- IPropertyTree *queryFileRoot(const CDfsLogicalFileName &dlfn,DfsXmlBranchKind &bkind)
- {
- bool external;
- bool foreign;
- external = dlfn.isExternal();
- foreign = dlfn.isForeign();
- if (external||foreign)
- return NULL;
- IPropertyTree *sroot = queryRoot();
- if (!sroot)
- return NULL;
- StringBuffer tail;
- dlfn.getTail(tail);
- StringBuffer query;
- getAttrQueryStr(query,queryDfsXmlBranchName(DXB_File),"@name",tail.str());
- IPropertyTree *froot = sroot->queryPropTree(query.str());
- bkind = DXB_File;
- if (!froot) {
- // check for super file
- getAttrQueryStr(query.clear(),queryDfsXmlBranchName(DXB_SuperFile),"@name",tail.str());
- froot = sroot->queryPropTree(query.str());
- if (froot)
- bkind = DXB_SuperFile;
- }
- return froot;
- }
- };
- class CClustersLockedSection
- {
- Owned<IRemoteConnection> conn;
- public:
- CClustersLockedSection(CDfsLogicalFileName &dlfn, bool exclusive)
- {
- StringBuffer xpath;
- dlfn.makeFullnameQuery(xpath,DXB_File,true).append("/ClusterLock");
- /* Avoid RTM_CREATE_QUERY connect() if possible by making 1st call without. This is to avoid write contention caused by RTM_CREATE*
- * NB: RTM_CREATE_QUERY should probably only gain exclusive access in Dali if node is missing.
- */
- conn.setown(querySDS().connect(xpath.str(), myProcessSession(), exclusive ? RTM_LOCK_WRITE : RTM_LOCK_READ, SDS_CONNECT_TIMEOUT));
- if (!conn.get()) // NB: ClusterLock is now created at File create time, so this can only be true for pre-existing File's
- {
- conn.setown(querySDS().connect(xpath.str(), myProcessSession(), RTM_CREATE_QUERY | RTM_LOCK_WRITE, SDS_CONNECT_TIMEOUT));
- assertex(conn.get());
- if (!exclusive)
- conn->changeMode(RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
- }
- }
- };
- static void checkDfsReplyException(MemoryBuffer &mb)
- {
- if (mb.length()<=sizeof(int))
- return;
- if ((*(int *)mb.bufferBase()) == -1) { // exception indicator
- int i;
- mb.read(i);
- throw deserializeException(mb);
- }
- }
- static void foreignDaliSendRecv(const INode *foreigndali,CMessageBuffer &mb, unsigned foreigndalitimeout)
- {
- SocketEndpoint ep = foreigndali->endpoint();
- if (ep.port==0)
- ep.port = DALI_SERVER_PORT;
- Owned<IGroup> grp = createIGroup(1,&ep);
- Owned<ICommunicator> comm = createCommunicator(grp,true);
- if (!comm->verifyConnection(0,foreigndalitimeout)) {
- StringBuffer tmp;
- IDFS_Exception *e = new CDFS_Exception(DFSERR_ForeignDaliTimeout, foreigndali->endpoint().getUrlStr(tmp).str());
- throw e;
- }
- comm->sendRecv(mb,0,MPTAG_DFS_REQUEST);
- }
- static bool isLocalDali(const INode *foreigndali)
- {
- if (!foreigndali)
- return true;
- Owned<INode> node;
- SocketEndpoint ep = foreigndali->endpoint();
- if (ep.port==0) {
- ep.port = DALI_SERVER_PORT;
- node.setown(createINode(ep));
- foreigndali = node.get();
- }
- return queryCoven().inCoven((INode *)foreigndali);
- }
- class FileClusterInfoArray: public IArrayOf<IClusterInfo>
- {
- ClusterPartDiskMapSpec defaultmapping;
- bool singleclusteroverride;
- public:
- FileClusterInfoArray()
- {
- singleclusteroverride = false;
- }
- void clear()
- {
- IArrayOf<IClusterInfo>::kill();
- }
- unsigned getNames(StringArray &clusternames)
- {
- StringBuffer name;
- ForEachItem(i) {
- clusternames.append(item(i).getClusterLabel(name.clear()).str());
- if (singleclusteroverride)
- break;
- }
- return clusternames.ordinality();
- }
- unsigned find(const char *_clusterName)
- {
- StringAttr clusterName = _clusterName;
- clusterName.toLowerCase();
- StringBuffer name;
- ForEachItem(i) {
- if (strcmp(item(i).getClusterLabel(name.clear()).str(),clusterName)==0)
- return i;
- if (singleclusteroverride)
- break;
- }
- return NotFound;
- }
- IGroup *queryGroup(unsigned clusternum)
- {
- if (clusternum>=ordinality())
- return NULL;
- if (singleclusteroverride&&clusternum)
- return NULL;
- return item(clusternum).queryGroup();
- }
- IGroup *getGroup(unsigned clusternum)
- {
- IGroup *ret = queryGroup(clusternum);
- return LINK(ret);
- }
- unsigned copyNum(unsigned part,unsigned copy,unsigned maxparts, unsigned *replicate)
- {
- ForEachItem(i) {
- IGroup *g = queryGroup(i);
- unsigned cw = g?g->ordinality():1;
- unsigned mc = item(i).queryPartDiskMapping().numCopies(part,cw,maxparts);
- if (copy<mc) {
- if (replicate)
- *replicate = copy;
- return i;
- }
- copy -= mc;
- if (singleclusteroverride)
- break;
- }
- return NotFound;
- }
- ClusterPartDiskMapSpec &queryPartDiskMapping(unsigned clusternum)
- {
- if (clusternum>=ordinality()||(singleclusteroverride&&clusternum))
- return defaultmapping;
- return item(clusternum).queryPartDiskMapping();
- }
- void updatePartDiskMapping(unsigned clusternum,const ClusterPartDiskMapSpec &spec)
- {
- if (clusternum<ordinality())
- item(clusternum).queryPartDiskMapping() = spec;
- }
- StringBuffer &getName(unsigned clusternum,StringBuffer &name)
- {
- if (clusternum<ordinality())
- item(clusternum).getClusterLabel(name);
- return name;
- }
- void setPreferred(const char *clusters,CDfsLogicalFileName &lfname)
- {
- unsigned nc = ordinality();
- if (nc<=1)
- return;
- StringBuffer cname;
- StringArray clustlist;
- if (lfname.getCluster(cname).length())
- clustlist.append(cname.str());
- unsigned i;
- if (clusters) {
- for (;;) {
- const char *s = clusters;
- while (*s&&(*s!=','))
- s++;
- if (s!=clusters) {
- cname.clear().append(s-clusters,clusters);
- for (i=0;i<clustlist.ordinality();i++)
- if (strcmp(clustlist.item(i),cname.str())==0)
- break;
- if (i==clustlist.ordinality())
- clustlist.append(cname.str());
- }
- if (!*s)
- break;
- clusters = s+1;
- }
- }
- if (clustlist.ordinality()==0) {
- // sort by closest to this node
- const IpAddress &myip = queryMyNode()->endpoint();
- unsigned *d=new unsigned[nc];
- for (i=0;i<nc;i++)
- d[i] = ipGroupDistance(myip,item(i).queryGroup());
- // bubble sort it - only a few
- for (i=0;i+1<nc;i++)
- for (unsigned j=0;j+i+1<nc;j++)
- if (d[j+1]<d[j]) {
- unsigned bd = d[j+1];
- d[j+1] = d[j];
- d[j] = bd;
- swap(j,j+1);
- }
- delete [] d;
- return;
- }
- Owned<IGroup> firstgrp;
- unsigned done = 0;
- StringBuffer name;
- StringBuffer name2;
- ForEachItemIn(ci,clustlist) {
- const char *cls = clustlist.item(ci);
- Owned<IGroup> grp = queryNamedGroupStore().lookup(cls);
- if (!grp) {
- IERRLOG("IDistributedFile::setPreferred - cannot find cluster %s",cls);
- return;
- }
- if (!firstgrp.get())
- firstgrp.set(grp);
- for (i=done;i<nc;i++) {
- IClusterInfo &info=item(i);
- if (stricmp(info.getClusterLabel(name2.clear()).str(),name.str())==0)
- break;
- IGroup *grp2 = info.queryGroup();
- if (grp2&&(grp->compare(grp2)!=GRdisjoint))
- break;
- }
- if (i<nc) {
- if (i) {
- Linked<IClusterInfo> tmp = &item(i);
- remove(i);
- add(*tmp.getClear(),done);
- }
- done++;
- if (done+1>=nc)
- break;
- }
- }
- if (done+1<nc) { // sort remaining by nearest to first group
- unsigned *d=new unsigned[nc]; // only use done to nc
- for (i=done;i<nc;i++)
- d[i] = groupDistance(firstgrp,item(i).queryGroup());
- // bubble sort it - only a few
- for (i=done;i+1<nc;i++)
- for (unsigned j=done;j+i+1<nc;j++)
- if (d[j+1]<d[j]) {
- unsigned bd = d[j+1];
- d[j+1] = d[j];
- d[j] = bd;
- swap(j,j+1);
- }
- delete [] d;
- }
- }
- void setSingleClusterOnly(bool set=true)
- {
- singleclusteroverride = set;
- }
- unsigned numCopies(unsigned part,unsigned maxparts)
- {
- unsigned ret = 0;
- ForEachItem(i) {
- IGroup *g = queryGroup(i);
- unsigned cw = g?g->ordinality():1;
- ret += item(i).queryPartDiskMapping().numCopies(part,cw,maxparts);
- if (singleclusteroverride)
- break;
- }
- return ret;
- }
- };
- // Internal extension of transaction interface, used to manipulate and track transaction
- interface IDistributedFileTransactionExt : extends IDistributedFileTransaction
- {
- virtual IUserDescriptor *queryUser()=0;
- virtual void descend()=0; // descend into a recursive call (can't autoCommit if depth is not zero)
- virtual void ascend()=0; // ascend back from the deep, one step at a time
- virtual void autoCommit()=0; // if transaction not active, commit straight away
- virtual void addAction(CDFAction *action)=0;
- virtual void addFile(IDistributedFile *file)=0;
- virtual void ensureFile(IDistributedFile *file)=0;
- virtual void clearFile(IDistributedFile *file)=0;
- virtual void clearFiles()=0;
- virtual void noteAddSubFile(IDistributedSuperFile *super, const char *superName, IDistributedFile *sub) = 0;
- virtual void noteRemoveSubFile(IDistributedSuperFile *super, IDistributedFile *sub) = 0;
- virtual void noteSuperSwap(IDistributedSuperFile *super1, IDistributedSuperFile *super2) = 0;
- virtual void clearSubFiles(IDistributedSuperFile *super) = 0;
- virtual void noteRename(IDistributedFile *file, const char *newName) = 0;
- virtual void validateAddSubFile(IDistributedSuperFile *super, IDistributedFile *sub, const char *subName) = 0;
- virtual bool isSubFile(IDistributedSuperFile *super, const char *subFile, bool sub) = 0;
- virtual bool addDelayedDelete(CDfsLogicalFileName &lfn,unsigned timeoutms=INFINITE)=0; // used internally to delay deletes until commit
- virtual bool prepareActions()=0;
- virtual void retryActions()=0;
- virtual void runActions()=0;
- virtual void commitAndClearup()=0;
- virtual ICodeContext *queryCodeContext()=0;
- };
- class CDistributedFileDirectory: implements IDistributedFileDirectory, public CInterface
- {
- Owned<IUserDescriptor> defaultudesc;
- Owned<IDFSredirection> redirection;
- void resolveForeignFiles(IPropertyTree *tree,const INode *foreigndali);
- protected: friend class CDistributedFile;
- StringAttr defprefclusters;
- unsigned defaultTimeout;
- public:
- IMPLEMENT_IINTERFACE;
- CDistributedFileDirectory()
- {
- defaultTimeout = INFINITE;
- defaultudesc.setown(createUserDescriptor());
- redirection.setown(createDFSredirection());
- }
- unsigned queryDefaultTimeout() const { return defaultTimeout; }
- IDistributedFile *dolookup(CDfsLogicalFileName &logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, unsigned timeout);
- IDistributedFile *lookup(const char *_logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, unsigned timeout);
- IDistributedFile *lookup(CDfsLogicalFileName &logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, unsigned timeout);
- /* createNew always creates an unnamed unattached distributed file
- * The caller must associated it with a name and credentials when it is attached (attach())
- */
- IDistributedFile *createNew(IFileDescriptor * fdesc);
- IDistributedFile *createExternal(IFileDescriptor *desc, const char *name);
- IDistributedSuperFile *createSuperFile(const char *logicalname,IUserDescriptor *user,bool interleaved,bool ifdoesnotexist,IDistributedFileTransaction *transaction=NULL);
- IDistributedSuperFile *createNewSuperFile(IPropertyTree *tree, const char *optionalName=nullptr);
- void removeSuperFile(const char *_logicalname, bool delSubs, IUserDescriptor *user, IDistributedFileTransaction *transaction);
- IDistributedFileIterator *getIterator(const char *wildname, bool includesuper,IUserDescriptor *user);
- IDFAttributesIterator *getDFAttributesIterator(const char *wildname, IUserDescriptor *user, bool recursive, bool includesuper,INode *foreigndali,unsigned foreigndalitimeout);
- IPropertyTreeIterator *getDFAttributesTreeIterator(const char *filters, DFUQResultField* localFilters, const char *localFilterBuf,
- IUserDescriptor *user, bool recursive, bool& allMatchingFilesReceived, INode *foreigndali,unsigned foreigndalitimeout);
- IDFAttributesIterator *getForeignDFAttributesIterator(const char *wildname, IUserDescriptor *user, bool recursive=true, bool includesuper=false, const char *foreigndali="", unsigned foreigndalitimeout=FOREIGN_DALI_TIMEOUT)
- {
- Owned<INode> foreign;
- if (foreigndali&&*foreigndali) {
- SocketEndpoint ep(foreigndali);
- foreign.setown(createINode(ep));
- }
- return getDFAttributesIterator(wildname, user, recursive, includesuper,foreign,foreigndalitimeout);
- }
- IDFScopeIterator *getScopeIterator(IUserDescriptor *user, const char *subscope,bool recursive,bool includeempty);
- bool loadScopeContents(const char *scopelfn,StringArray *scopes, StringArray *supers,StringArray *files, bool includeemptyscopes);
- IPropertyTree *getFileTree(const char *lname,IUserDescriptor *user,const INode *foreigndali,unsigned foreigndalitimeout,bool expandnodes=false, bool appendForeign=true);
- void setFileAccessed(CDfsLogicalFileName &dlfn, IUserDescriptor *user,const CDateTime &dt,const INode *foreigndali=NULL,unsigned foreigndalitimeout=FOREIGN_DALI_TIMEOUT);
- IFileDescriptor *getFileDescriptor(const char *lname,IUserDescriptor *user,const INode *foreigndali=NULL,unsigned foreigndalitimeout=FOREIGN_DALI_TIMEOUT);
- IDistributedFile *getFile(const char *lname,IUserDescriptor *user,const INode *foreigndali=NULL,unsigned foreigndalitimeout=FOREIGN_DALI_TIMEOUT);
- bool exists(const char *_logicalname,IUserDescriptor *user,bool notsuper=false,bool superonly=false);
- bool existsPhysical(const char *_logicalname,IUserDescriptor *user);
- void addEntry(CDfsLogicalFileName &lfn,IPropertyTree *root,bool superfile, bool ignoreexists);
- bool removeEntry(const char *name, IUserDescriptor *user, IDistributedFileTransaction *transaction=NULL, unsigned timeoutms=INFINITE, bool throwException=false);
- void renamePhysical(const char *oldname,const char *newname,IUserDescriptor *user,IDistributedFileTransaction *transaction);
- void removeEmptyScope(const char *name);
- IDistributedSuperFile *lookupSuperFile(const char *logicalname,IUserDescriptor *user,IDistributedFileTransaction *transaction,unsigned timeout=INFINITE);
- SecAccessFlags getFilePermissions(const char *lname,IUserDescriptor *user,unsigned auditflags);
- SecAccessFlags getNodePermissions(const IpAddress &ip,IUserDescriptor *user,unsigned auditflags);
- SecAccessFlags getFDescPermissions(IFileDescriptor *,IUserDescriptor *user,unsigned auditflags=0);
- void setDefaultUser(IUserDescriptor *user);
- IUserDescriptor* queryDefaultUser();
- DistributedFileCompareResult fileCompare(const char *lfn1,const char *lfn2,DistributedFileCompareMode mode,StringBuffer &errstr,IUserDescriptor *user);
- bool filePhysicalVerify(const char *lfn1,IUserDescriptor *user,bool includecrc,StringBuffer &errstr);
- void setDefaultPreferredClusters(const char *clusters);
- void fixDates(IDistributedFile *fil);
- GetFileClusterNamesType getFileClusterNames(const char *logicalname,StringArray &out); // returns 0 for normal file, 1 for
- bool isSuperFile( const char *logicalname, IUserDescriptor *user=NULL, INode *foreigndali=NULL, unsigned timeout=0);
- void promoteSuperFiles(unsigned numsf,const char **sfnames,const char *addsubnames,bool delsub,bool createonlyonesuperfile,IUserDescriptor *user, unsigned timeout, StringArray &outunlinked);
- ISimpleSuperFileEnquiry * getSimpleSuperFileEnquiry(const char *logicalname,const char *title,IUserDescriptor *udesc,unsigned timeout);
- bool getFileSuperOwners(const char *logicalname, StringArray &owners);
- IDFSredirection & queryRedirection() { return *redirection; }
- static StringBuffer &getFileRelationshipXPath(StringBuffer &xpath, const char *primary, const char *secondary,const char *primflds,const char *secflds,
- const char *kind, const char *cardinality, const bool *payload
- );
- void doRemoveFileRelationship( IRemoteConnection *conn, const char *primary,const char *secondary,const char *primflds,const char *secflds, const char *kind);
- void removeFileRelationships(const char *primary,const char *secondary, const char *primflds, const char *secflds, const char *kind);
- void addFileRelationship(const char *kind,const char *primary,const char *secondary,const char *primflds, const char *secflds,const char *cardinality,bool payload,IUserDescriptor *user,const char *description);
- IFileRelationshipIterator *lookupFileRelationships(const char *primary,const char *secondary,const char *primflds,const char *secflds,
- const char *kind,const char *cardinality,const bool *payload,
- const char *foreigndali,unsigned foreigndalitimeout);
- void removeAllFileRelationships(const char *filename);
- IFileRelationshipIterator *lookupAllFileRelationships(const char *filenames);
- void renameFileRelationships(const char *oldname,const char *newname,IFileRelationshipIterator *reliter, IUserDescriptor *user);
- bool publishMetaFileXML(const CDfsLogicalFileName &logicalname,IUserDescriptor *user);
- IFileDescriptor *createDescriptorFromMetaFile(const CDfsLogicalFileName &logicalname,IUserDescriptor *user);
- bool isProtectedFile(const CDfsLogicalFileName &logicalname, unsigned timeout) ;
- unsigned queryProtectedCount(const CDfsLogicalFileName &logicalname, const char *owner);
- bool getProtectedInfo(const CDfsLogicalFileName &logicalname, StringArray &names, UnsignedArray &counts);
- IDFProtectedIterator *lookupProtectedFiles(const char *owner=NULL,bool notsuper=false,bool superonly=false);
- IDFAttributesIterator* getLogicalFilesSorted(IUserDescriptor* udesc, DFUQResultField *sortOrder, const void *filterBuf, DFUQResultField *specialFilters,
- const void *specialFilterBuf, unsigned startOffset, unsigned maxNum, __int64 *cacheHint, unsigned *total, bool *allMatchingFilesReceived);
- IDFAttributesIterator* getLogicalFiles(IUserDescriptor* udesc, DFUQResultField *sortOrder, const void *filterBuf, DFUQResultField *specialFilters,
- const void *specialFilterBuf, unsigned startOffset, unsigned maxNum, __int64 *cacheHint, unsigned *total, bool *allMatchingFilesReceived, bool recursive, bool sorted);
- void setFileProtect(CDfsLogicalFileName &dlfn,IUserDescriptor *user, const char *owner, bool set, const INode *foreigndali=NULL,unsigned foreigndalitimeout=FOREIGN_DALI_TIMEOUT);
- unsigned setDefaultTimeout(unsigned timems)
- {
- unsigned ret = defaultTimeout;
- defaultTimeout = timems;
- return ret;
- }
- virtual bool removePhysicalPartFiles(const char *logicalName, IFileDescriptor *fileDesc, IMultiException *mexcept, unsigned numParallelDeletes=0) override;
- };
- // === Transactions
- class CDFAction: public CInterface
- {
- unsigned locked;
- protected:
- IDistributedFileTransactionExt *transaction;
- IArrayOf<IDistributedFile> lockedFiles;
- DFTransactionState state;
- void addFileLock(IDistributedFile *file)
- {
- // derived's prepare must call this before locking
- lockedFiles.append(*LINK(file));
- }
- bool lock()
- {
- // Files most have been acquired already by derived's class prepare
- ForEachItemIn(i,lockedFiles)
- {
- try
- {
- lockedFiles.item(i).lockProperties(0);
- }
- catch (ISDSException *e)
- {
- if (SDSExcpt_LockTimeout != e->errorCode())
- throw;
- e->Release();
- PROGLOG("CDFAction lock timed out on %s",lockedFiles.item(i).queryLogicalName());
- return false;
- }
- locked++;
- }
- return true;
- }
- void unlock()
- {
- for(unsigned i=0; i<locked; i++)
- lockedFiles.item(i).unlockProperties(state);
- locked = 0;
- lockedFiles.kill();
- }
- public:
- CDFAction() : locked(0), state(TAS_NONE)
- {
- transaction = NULL;
- }
- // Clear all locked files (when re-using transaction on auto-commit mode)
- virtual ~CDFAction()
- {
- if (transaction)
- unlock();
- }
- void setTransaction(IDistributedFileTransactionExt *_transaction)
- {
- assertex(_transaction);
- assertex(!transaction);
- transaction = _transaction;
- }
- virtual bool prepare()=0; // should call lock
- virtual void run()=0; // must override this
- // If some lock fails, call this
- virtual void retry()
- {
- state = TAS_RETRY;
- unlock();
- }
- // MORE: In the rare event of a commit failure, not all actions can be rolled back.
- // Since all actions today occur during "run", and since commit phases does very little,
- // this chance is minimal and will probably be caused by corrupted file descriptors.
- // The problem is that the state of the sub removals and the order in which they occur might not
- // be trivial on such a low level error, and there's no way to atomically do operations in SDS
- // at present time. We need more thought about this.
- virtual void commit()
- {
- state = TAS_SUCCESS;
- unlock();
- }
- virtual void rollback()
- {
- state = TAS_FAILURE;
- unlock();
- }
- };
- static void setUserDescriptor(Linked<IUserDescriptor> &udesc,IUserDescriptor *user)
- {
- if (!user)
- {
- #ifdef NULL_DALIUSER_STACKTRACE
- StringBuffer sb;
- if (user)
- user->getUserName(sb);
- if (sb.length()==0)
- {
- IERRLOG("UNEXPECTED USER (NULL) in dadfs.cpp setUserDescriptor() %d",__LINE__);
- //following debug code to be removed
- PrintStackReport();
- }
- #endif
- user = queryDistributedFileDirectory().queryDefaultUser();
- }
- udesc.set(user);
- }
- static bool scopePermissionsAvail = true;
- static SecAccessFlags getScopePermissions(const char *scopename,IUserDescriptor *user,unsigned auditflags)
- { // scope must be normalized already
- SecAccessFlags perms = SecAccess_Full;
- if (scopePermissionsAvail && scopename && *scopename) {
- if (!user)
- {
- #ifdef NULL_DALIUSER_STACKTRACE
- IERRLOG("UNEXPECTED USER (NULL) in dadfs.cpp getScopePermissions() line %d",__LINE__);
- //following debug code to be removed
- PrintStackReport();
- #endif
- user = queryDistributedFileDirectory().queryDefaultUser();
- }
- //Create signature
- CDateTime now;
- StringBuffer b64sig;
- createDaliSignature(scopename, user, now, b64sig);
- perms = querySessionManager().getPermissionsLDAP(queryDfsXmlBranchName(DXB_Scope),scopename,user,auditflags, b64sig.str(), now);
- if (perms<0) {
- if (perms == SecAccess_Unavailable) {
- scopePermissionsAvail=false;
- perms = SecAccess_Full;
- }
- else
- perms = SecAccess_None;
- }
- }
- return perms;
- }
- static void checkLogicalScope(const char *scopename,IUserDescriptor *user,bool readreq,bool createreq)
- {
- // scope must be normalized already
- if (!readreq&&!createreq)
- return;
- unsigned auditflags = 0;
- if (readreq)
- auditflags |= (DALI_LDAP_AUDIT_REPORT|DALI_LDAP_READ_WANTED);
- if (createreq)
- auditflags |= (DALI_LDAP_AUDIT_REPORT|DALI_LDAP_WRITE_WANTED);
- #ifdef NULL_DALIUSER_STACKTRACE
- if (!user)
- {
- IERRLOG("UNEXPECTED USER (NULL) in dadfs.cpp checkLogicalScope() line %d",__LINE__);
- PrintStackReport();
- }
- #endif
- SecAccessFlags perm = getScopePermissions(scopename,user,auditflags);
- IDFS_Exception *e = NULL;
- if (readreq&&!HASREADPERMISSION(perm))
- e = new CDFS_Exception(DFSERR_LookupAccessDenied,scopename);
- else if (createreq&&!HASWRITEPERMISSION(perm))
- e = new CDFS_Exception(DFSERR_CreateAccessDenied,scopename);
- if (e)
- throw e;
- }
- bool checkLogicalName(CDfsLogicalFileName &dlfn,IUserDescriptor *user,bool readreq,bool createreq,bool allowquery,const char *specialnotallowedmsg)
- {
- bool ret = true;
- if (dlfn.isMulti()) { //is temporary superFile?
- if (specialnotallowedmsg)
- throw MakeStringException(-1,"cannot %s a multi file name (%s)",specialnotallowedmsg,dlfn.get());
- if (!dlfn.isExpanded())
- dlfn.expand(user);//expand wildcards
- unsigned i = dlfn.multiOrdinality();
- while (--i)//continue looping even when ret is false, in order to check for illegal elements (foreigns/externals), and to check each scope permission
- ret = checkLogicalName((CDfsLogicalFileName &)dlfn.multiItem(i),user,readreq,createreq,allowquery,specialnotallowedmsg)&&ret;
- }
- else {
- if (specialnotallowedmsg) {
- if (dlfn.isExternal()) {
- if (dlfn.isQuery()&&allowquery)
- ret = false;
- else
- throw MakeStringException(-1,"cannot %s an external file name (%s)",specialnotallowedmsg,dlfn.get());
- }
- if (dlfn.isForeign()) {
- throw MakeStringException(-1,"cannot %s a foreign file name (%s)",specialnotallowedmsg,dlfn.get());
- }
- }
- StringBuffer scopes;
- dlfn.getScopes(scopes);
- checkLogicalScope(scopes.str(),user,readreq,createreq);
- }
- return ret;
- }
- bool checkLogicalName(const char *lfn,IUserDescriptor *user,bool readreq,bool createreq,bool allowquery,const char *specialnotallowedmsg)
- {
- CDfsLogicalFileName dlfn;
- dlfn.set(lfn);
- return checkLogicalName(dlfn, user, readreq, createreq, allowquery, specialnotallowedmsg);
- }
- /*
- * This class removes all files marked for deletion during transactions.
- *
- * TODO: the doDelete method re-acquires the lock to remove the files, and
- * that creates a window (between end of commit and deletion) where other
- * processes can acquire locks and blow things up. To fix this, you'd have
- * to be selective on what files you unlock during commit, so that you
- * can still keep an unified cache AND release the deletions later on.
- */
- class CDelayedDelete: public CInterface
- {
- CDfsLogicalFileName lfn;
- Linked<IUserDescriptor> user;
- unsigned timeoutms;
- public:
- CDelayedDelete(CDfsLogicalFileName &_lfn,IUserDescriptor *_user,unsigned _timeoutms)
- : user(_user), timeoutms(_timeoutms)
- {
- lfn.set(_lfn);
- }
- void doDelete() // Throw on error!
- {
- const char *logicalname = lfn.get();
- if (!lfn.isExternal() && !checkLogicalName(lfn,user,true,true,true,"remove"))
- ThrowStringException(-1, "Logical Name fails for removal on %s", lfn.get());
- for (;;)
- {
- // Transaction files have already been unlocked at this point, delete all remaining files
- Owned<IDistributedFile> file = queryDistributedFileDirectory().lookup(lfn, user, true, false, true, NULL, SDS_SUB_LOCK_TIMEOUT);
- if (!file.get())
- return;
- StringBuffer reason;
- if (!file->canRemove(reason, false))
- ThrowStringException(-1, "Can't remove %s: %s", lfn.get(), reason.str());
- // This will do the right thing for either super-files and logical-files.
- try
- {
- file->detach(0, NULL); // 0 == timeout immediately if cannot get exclusive lock
- return;
- }
- catch (ISDSException *e)
- {
- switch (e->errorCode())
- {
- case SDSExcpt_LockTimeout:
- case SDSExcpt_LockHeld:
- e->Release();
- break;
- default:
- throw;
- }
- }
- file.clear();
- PROGLOG("CDelayedDelete: pausing due to locked file = %s", logicalname);
- Sleep(SDS_TRANSACTION_RETRY/2+(getRandom()%SDS_TRANSACTION_RETRY));
- }
- }
- };
- class CDistributedFileTransaction: implements IDistributedFileTransactionExt, public CInterface
- {
- class CTransactionFile : public CSimpleInterface
- {
- class HTMapping : public CInterface
- {
- IDistributedFile *file;
- StringAttr name;
- public:
- HTMapping(const char *_name, IDistributedFile *_file) : name(_name), file(_file) { }
- IDistributedFile &query() { return *file; }
- const char *queryFindString() const { return name; }
- const void *queryFindParam() const { return &file; }
- };
- class CSubFileIter : protected SuperHashIteratorOf<HTMapping>, implements IDistributedFileIterator
- {
- typedef SuperHashIteratorOf<HTMapping> PARENT;
- public:
- IMPLEMENT_IINTERFACE_USING(PARENT);
- CSubFileIter(OwningStringSuperHashTableOf<HTMapping> &table) : PARENT(table)
- {
- }
- // IDistributedFileIterator impl.
- virtual IDistributedFile &query()
- {
- HTMapping &map = PARENT::query();
- return map.query();
- }
- virtual bool first() { return PARENT::first(); }
- virtual bool isValid() { return PARENT::isValid(); }
- virtual bool next() { return PARENT::next(); }
- virtual StringBuffer &getName(StringBuffer &name)
- {
- HTMapping &map = PARENT::query();
- return name.append(map.queryFindString());
- }
- };
- CDistributedFileTransaction &owner;
- OwningStringSuperHashTableOf<HTMapping> subFilesByName;
- StringAttr name;
- Linked<IDistributedFile> file;
- public:
- CTransactionFile(CDistributedFileTransaction &_owner, const char *_name, IDistributedFile *_file) : owner(_owner), name(_name), file(_file)
- {
- }
- const char *queryName() const { return name; }
- IDistributedFile *queryFile() { return file; }
- IDistributedFileIterator *getSubFiles()
- {
- IDistributedSuperFile *super = file->querySuperFile();
- if (!super)
- return NULL;
- return new CSubFileIter(subFilesByName);
- }
- void clearSubs()
- {
- subFilesByName.kill();
- }
- unsigned numSubFiles() const { return subFilesByName.count(); }
- void noteAddSubFile(IDistributedFile *sub)
- {
- if (NULL == subFilesByName.find(sub->queryLogicalName()))
- {
- Owned<HTMapping> map = new HTMapping(sub->queryLogicalName(), sub);
- subFilesByName.replace(*map.getLink());
- }
- }
- void noteRemoveSubFile(IDistributedFile *sub)
- {
- HTMapping *map = subFilesByName.find(sub->queryLogicalName());
- if (map)
- verifyex(subFilesByName.removeExact(map));
- }
- bool find(const char *subFile, bool sub)
- {
- StringBuffer tmp;
- subFile = normalizeLFN(subFile, tmp);
- HTMapping *match = subFilesByName.find(subFile);
- if (match)
- return true;
- else if (sub)
- {
- SuperHashIteratorOf<HTMapping> iter(subFilesByName);
- ForEach(iter)
- {
- HTMapping &map = iter.query();
- IDistributedFile &file = map.query();
- IDistributedSuperFile *super = file.querySuperFile();
- if (super)
- {
- if (owner.isSubFile(super, subFile, sub))
- return true;
- }
- }
- }
- return false;
- }
- const void *queryFindParam() const { return &file; }
- const char *queryFindString() const { return name; }
- };
- CIArrayOf<CDFAction> actions;
- OwningSimpleHashTableOf<CTransactionFile, IDistributedFile *> trackedFiles;
- OwningStringSuperHashTableOf<CTransactionFile> trackedFilesByName;
- bool isactive;
- Linked<IUserDescriptor> udesc;
- CIArrayOf<CDelayedDelete> delayeddelete;
- // auto-commit only works at depth zero (for recursive calls)
- // MORE: Maybe this needs a context object (descend on c-tor, ascend on d-tor)
- // But we need all actions within transactions first to find out if there is
- // any exception to the rule used by addSubFile / removeSubFile
- unsigned depth;
- unsigned prepared;
- ICodeContext *codeCtx;
- /* 'owner' is set if, transaction object is implicitly created, because none provided
- * The owner cannot be release or unlocked. The transaction can still retry if other files are locked,
- * so need to ensure 'owner' remains in tracked file cache.
- */
- IDistributedSuperFile *owner;
- CTransactionFile *queryCreate(const char *name, IDistributedFile *file, bool recreate=false)
- {
- Owned<CTransactionFile> trackedFile;
- if (!recreate)
- trackedFile.set(trackedFiles.find(file));
- if (!trackedFile)
- {
- StringBuffer tmp;
- name = normalizeLFN(name, tmp);
- trackedFile.setown(new CTransactionFile(*this, tmp.str(), file));
- trackedFiles.replace(*trackedFile.getLink());
- trackedFilesByName.replace(*trackedFile.getLink());
- }
- return trackedFile;
- }
- CTransactionFile *lookupTrackedFile(IDistributedFile *file)
- {
- return trackedFiles.find(file);
- }
- void commitActions()
- {
- while (actions.ordinality()) // if we get here everything should work!
- {
- Owned<CDFAction> action = &actions.popGet();
- action->commit();
- }
- }
- IDistributedFile *findFile(const char *name)
- {
- StringBuffer tmp;
- name = normalizeLFN(name, tmp);
- CTransactionFile *trackedFile = trackedFilesByName.find(tmp.str());
- if (!trackedFile)
- return NULL;
- return trackedFile->queryFile();
- }
- void deleteFiles() // no rollback at this point
- {
- Owned<IMultiException> me = MakeMultiException("Transaction");
- ForEachItemIn(i,delayeddelete) {
- try {
- delayeddelete.item(i).doDelete();
- } catch (IException *e) {
- me->append(*e);
- }
- }
- delayeddelete.kill();
- if (me->ordinality())
- throw me.getClear();
- }
- public:
- IMPLEMENT_IINTERFACE;
- CDistributedFileTransaction(IUserDescriptor *user, IDistributedSuperFile *_owner=NULL, ICodeContext *_codeCtx=NULL)
- : isactive(false), depth(0), prepared(0), owner(_owner), codeCtx(_codeCtx)
- {
- setUserDescriptor(udesc,user);
- }
- ~CDistributedFileTransaction()
- {
- // New files should be removed automatically if not committed
- // MORE - refactor cCreateSuperFileAction to avoid this
- if (isactive)
- rollback();
- assert(depth == 0);
- }
- // IDistributedFileTransaction impl.
- virtual void start()
- {
- if (isactive)
- throw MakeStringException(-1,"Transaction already started");
- clearFiles();
- actions.kill();
- isactive = true;
- prepared = 0;
- assertex(actions.ordinality()==0);
- }
- virtual void commit()
- {
- if (!isactive)
- return;
- IException *rete=NULL;
- // =============== PREPARE AND RETRY UNTIL READY
- try
- {
- for (;;)
- {
- if (prepareActions())
- break;
- else
- retryActions();
- PROGLOG("CDistributedFileTransaction: Transaction pausing");
- Sleep(SDS_TRANSACTION_RETRY/2+(getRandom()%SDS_TRANSACTION_RETRY));
- }
- runActions();
- commitAndClearup();
- return;
- }
- catch (IException *e)
- {
- rete = e;
- }
- rollback();
- throw rete;
- }
- virtual void rollback()
- {
- // =============== ROLLBACK AND CLEANUP
- while (actions.ordinality())
- {
- try
- {
- // we don't want to unlock what hasn't been locked
- // if an exception was thrown while locking, but we
- // do want to pop them all
- Owned<CDFAction> action = &actions.popGet();
- if (actions.ordinality()<prepared)
- action->rollback();
- }
- catch (IException *e)
- {
- e->Release();
- }
- }
- actions.kill(); // should be empty
- clearFiles(); // release locks
- if (!isactive)
- return;
- isactive = false;
- // this we only want to do if active
- delayeddelete.kill();
- }
- virtual bool active()
- {
- return isactive;
- }
- virtual IDistributedFile *lookupFile(const char *name,unsigned timeout)
- {
- IDistributedFile *ret = findFile(name);
- if (ret)
- return LINK(ret);
- else
- {
- ret = queryDistributedFileDirectory().lookup(name, udesc, false, false, false, this, timeout);
- if (ret)
- queryCreate(name, ret, true);
- return ret;
- }
- }
- virtual IDistributedSuperFile *lookupSuperFile(const char *name, unsigned timeout)
- {
- IDistributedFile *f = findFile(name);
- if (f)
- return LINK(f->querySuperFile());
- else
- {
- IDistributedSuperFile *ret = queryDistributedFileDirectory().lookupSuperFile(name,udesc,this,timeout);
- if (ret)
- addFile(ret);
- return ret;
- }
- }
- // IDistributedFileTransactionExt impl.
- virtual IUserDescriptor *queryUser()
- {
- return udesc;
- }
- virtual void descend() // Call this when you're recurring
- {
- depth++;
- }
- virtual void ascend() // Call this at the end of the block that started recursion
- {
- assertex(depth);
- depth--;
- }
- virtual void autoCommit()
- {
- // Recursive calls to transaction will not commit until
- // all calls have finished (gone back to zero depth)
- if (!depth && !isactive) {
- try {
- isactive = true;
- commit();
- }
- catch (IException *) {
- rollback();
- throw;
- }
- }
- }
- virtual void addAction(CDFAction *action)
- {
- actions.append(*action); // takes ownership
- action->setTransaction(this);
- }
- virtual void addFile(IDistributedFile *file)
- {
- CTransactionFile *trackedFile = queryCreate(file->queryLogicalName(), file, false);
- // Also add subfiles to cache
- IDistributedSuperFile *sfile = file->querySuperFile();
- if (sfile)
- {
- Owned<IDistributedFileIterator> iter = sfile->getSubFileIterator();
- ForEach(*iter)
- {
- IDistributedFile *f = &iter->query();
- trackedFile->noteAddSubFile(f);
- addFile(f);
- }
- }
- }
- virtual void ensureFile(IDistributedFile *file)
- {
- if (!trackedFiles.find(file))
- addFile(file);
- }
- virtual void clearFile(IDistributedFile *file)
- {
- CTransactionFile *trackedFile = lookupTrackedFile(file);
- IDistributedSuperFile *sfile = file->querySuperFile();
- if (trackedFile)
- {
- Owned<IDistributedFileIterator> iter = trackedFile->getSubFiles();
- if (iter)
- {
- ForEach(*iter)
- clearFile(&iter->query());
- }
- trackedFiles.removeExact(trackedFile);
- trackedFilesByName.removeExact(trackedFile);
- }
- }
- virtual void clearFiles()
- {
- trackedFiles.kill();
- trackedFilesByName.kill();
- if (owner)
- addFile(owner); // ensure remains tracked
- }
- virtual void noteAddSubFile(IDistributedSuperFile *super, const char *superName, IDistributedFile *sub)
- {
- CTransactionFile *trackedSuper = queryCreate(superName, super);
- trackedSuper->noteAddSubFile(sub);
- }
- virtual void noteRemoveSubFile(IDistributedSuperFile *super, IDistributedFile *sub)
- {
- CTransactionFile *trackedSuper = lookupTrackedFile(super);
- if (trackedSuper)
- trackedSuper->noteRemoveSubFile(sub);
- }
- virtual void noteSuperSwap(IDistributedSuperFile *super1, IDistributedSuperFile *super2)
- {
- CTransactionFile *trackedSuper1 = lookupTrackedFile(super1);
- CTransactionFile *trackedSuper2 = lookupTrackedFile(super2);
- assertex(trackedSuper1 && trackedSuper2);
- ICopyArrayOf<IDistributedFile> super1Subs, super2Subs;
- Owned<IDistributedFileIterator> iter = trackedSuper1->getSubFiles();
- ForEach(*iter)
- super1Subs.append(iter->query());
- trackedSuper1->clearSubs();
- iter.setown(trackedSuper2->getSubFiles());
- ForEach(*iter)
- super2Subs.append(iter->query());
- trackedSuper2->clearSubs();
- ForEachItemIn(s, super2Subs)
- trackedSuper1->noteAddSubFile(&super2Subs.item(s));
- ForEachItemIn(s2, super1Subs)
- trackedSuper2->noteAddSubFile(&super1Subs.item(s2));
- }
- virtual void clearSubFiles(IDistributedSuperFile *super)
- {
- CTransactionFile *trackedSuper = lookupTrackedFile(super);
- if (trackedSuper)
- trackedSuper->clearSubs();
- }
- virtual void noteRename(IDistributedFile *file, const char *newName)
- {
- CTransactionFile *trackedFile = lookupTrackedFile(file);
- if (trackedFile)
- {
- trackedFiles.removeExact(trackedFile);
- trackedFilesByName.removeExact(trackedFile);
- trackedFile = queryCreate(newName, file);
- }
- }
- virtual void validateAddSubFile(IDistributedSuperFile *super, IDistributedFile *sub, const char *subName);
- virtual bool isSubFile(IDistributedSuperFile *super, const char *subFile, bool sub)
- {
- CTransactionFile *trackedSuper = lookupTrackedFile(super);
- if (!trackedSuper)
- return false;
- return trackedSuper->find(subFile, sub);
- }
- virtual bool addDelayedDelete(CDfsLogicalFileName &lfn,unsigned timeoutms)
- {
- delayeddelete.append(*new CDelayedDelete(lfn,udesc,timeoutms));
- return true;
- }
- virtual bool prepareActions()
- {
- prepared = 0;
- unsigned toPrepare = actions.ordinality();
- ForEachItemIn(i0,actions)
- {
- if (actions.item(i0).prepare())
- ++prepared;
- else
- break;
- }
- return prepared == toPrepare;
- }
- virtual void retryActions()
- {
- clearFiles(); // clear all previously tracked pending file changes, e.g. renames, super file additions/removals
- while (prepared) // unlock for retry
- actions.item(--prepared).retry();
- }
- virtual void runActions()
- {
- ForEachItemIn(i,actions)
- actions.item(i).run();
- }
- virtual void commitAndClearup()
- {
- // =============== COMMIT and CLEANUP
- commitActions();
- clearFiles();
- isactive = false;
- actions.kill();
- deleteFiles();
- }
- virtual ICodeContext *queryCodeContext()
- {
- return codeCtx;
- }
- };
- static bool recursiveCheckEmptyScope(IPropertyTree &ct)
- {
- Owned<IPropertyTreeIterator> iter = ct.getElements("*");
- ForEach(*iter) {
- IPropertyTree &item = iter->query();
- const char *n = item.queryName();
- if (!n||(strcmp(n,queryDfsXmlBranchName(DXB_Scope))!=0))
- return false;
- if (!recursiveCheckEmptyScope(item))
- return false;
- }
- return true;
- }
- class CDFScopeIterator: implements IDFScopeIterator, public CInterface
- {
- PointerArray scopes;
- unsigned index;
- IDistributedFileDirectory *dir;
- bool includeempty;
- void add(IPropertyTree &t, bool recursive, StringBuffer &name)
- {
- name.trim();
- size32_t nl = name.length();
- size32_t l=nl;
- if (nl) {
- name.append("::");
- l+=2;
- }
- Owned<IPropertyTreeIterator> iter = t.getElements(queryDfsXmlBranchName(DXB_Scope));
- ForEach(*iter) {
- IPropertyTree &ct = iter->query();
- if (includeempty||!recursiveCheckEmptyScope(ct)) {
- name.append(ct.queryProp("@name"));
- scopes.append(strdup(name.str()));
- if (recursive)
- add(ct,recursive,name);
- name.setLength(l);
- }
- }
- name.setLength(nl);
- }
- public:
- IMPLEMENT_IINTERFACE;
- CDFScopeIterator(IDistributedFileDirectory *_dir,const char *base,bool recursive, bool _includeempty,unsigned timeout) // attrib not yet implemented
- {
- includeempty = _includeempty;
- dir = _dir;
- StringBuffer lockPath;
- if (!isEmptyString(base))
- {
- CDfsLogicalFileName dlfn;
- dlfn.set(base, "dummyfilename"); // makeScopeQuery expects a lfn to a file, 'dummyfilename' will not be used
- dlfn.makeScopeQuery(lockPath, true);
- }
- else
- lockPath.append(querySdsFilesRoot());
- {
- CConnectLock connlock("CDFScopeIterator", lockPath, false, false, false, timeout);
- if (connlock.conn)
- {
- StringBuffer name;
- add(*connlock.conn->queryRoot(),recursive,name);
- }
- }
- if (scopes.ordinality()>1)
- qsortvec(scopes.getArray(),scopes.ordinality(),strcompare);
- index = 0;
- }
- ~CDFScopeIterator()
- {
- ForEachItemIn(i,scopes) {
- free(scopes.item(i));
- }
- }
- bool first()
- {
- index = 0;
- return isValid();
- }
- bool next()
- {
- index++;
- return isValid();
- }
- bool isValid()
- {
- return (index<scopes.ordinality());
- }
- const char *query()
- {
- return (const char *)scopes.item(index);
- }
- };
- struct SerializeFileAttrOptions
- {
- bool includeSuperOwner;
- //Add more as needed
- SerializeFileAttrOptions()
- {
- includeSuperOwner = false;
- }
- };
- class CDFAttributeIterator: implements IDFAttributesIterator, public CInterface
- {
- unsigned index;
- IArrayOf<IPropertyTree> attrs;
- public:
- IMPLEMENT_IINTERFACE;
- static MemoryBuffer &serializeFileAttributes(MemoryBuffer &mb, IPropertyTree &root, const char *name, bool issuper, SerializeFileAttrOptions& option)
- {
- StringBuffer buf;
- mb.append(name);
- if (issuper) {
- mb.append("!SF");
- mb.append(root.getPropInt("@numsubfiles",0));
- mb.append("");
- }
- else {
- mb.append(root.queryProp("@directory"));
- mb.append(root.getPropInt("@numparts",0));
- mb.append(root.queryProp("@partmask"));
- }
- mb.append(root.queryProp("@modified"));
- Owned<IPropertyTree> attrs = root.getPropTree("Attr");;
- Owned<IAttributeIterator> attriter;
- if (attrs)
- attriter.setown(attrs->getAttributes());
- unsigned count=0;
- size32_t countpos = mb.length();
- mb.append(count);
- if (attriter.get()&&attriter->first()) {
- do {
- mb.append(attriter->queryName());
- mb.append(attriter->queryValue());
- count++;
- } while (attriter->next());
- }
- const char *ps = root.queryProp("@group");
- if (ps&&*ps) {
- count++;
- mb.append("@group");
- mb.append(ps);
- }
- // add protected
- if (attrs) {
- Owned<IPropertyTreeIterator> piter = attrs->getElements("Protect");
- StringBuffer plist;
- ForEach(*piter) {
- const char *name = piter->get().queryProp("@name");
- if (name&&*name) {
- unsigned count = piter->get().getPropInt("@count");
- if (count) {
- if (plist.length())
- plist.append(',');
- plist.append(name);
- if (count>1)
- plist.append(':').append(count);
- }
- }
- }
- if (plist.length()) {
- count++;
- mb.append("@protect");
- mb.append(plist.str());
- }
- }
- if (option.includeSuperOwner) {
- //add superowners
- StringBuffer soList;
- Owned<IPropertyTreeIterator> superOwners = root.getElements("SuperOwner");
- ForEach(*superOwners) {
- IPropertyTree &superOwner = superOwners->query();
- const char *name = superOwner.queryProp("@name");
- if (name&&*name) {
- if (soList.length())
- soList.append(",");
- soList.append(name);
- }
- }
- if (soList.length()) {
- count++;
- mb.append("@superowners");
- mb.append(soList.str());
- }
- }
- mb.writeDirect(countpos,sizeof(count),&count);
- return mb;
- }
- CDFAttributeIterator(MemoryBuffer &mb) // attrib not yet implemented
- {
- unsigned numfiles;
- mb.read(numfiles);
- while (numfiles--) {
- IPropertyTree *attr = getEmptyAttr();
- StringAttr val;
- unsigned n;
- mb.read(val);
- attr->setProp("@name",val.get());
- mb.read(val);
- if (stricmp(val,"!SF")==0) {
- mb.read(n);
- attr->setPropInt("@numsubfiles",n);
- mb.read(val); // not used currently
- }
- else {
- attr->setProp("@directory",val.get());
- mb.read(n);
- attr->setPropInt("@numparts",n);
- mb.read(val);
- attr->setProp("@partmask",val.get());
- }
- mb.read(val);
- attr->setProp("@modified",val.get());
- unsigned count;
- mb.read(count);
- StringAttr at;
- while (count--) {
- mb.read(at);
- mb.read(val);
- attr->setProp(at.get(),val.get());
- }
- attrs.append(*attr);
- }
- index = 0;
- }
- CDFAttributeIterator(IArrayOf<IPropertyTree>& trees)
- {
- ForEachItemIn(t, trees)
- attrs.append(*LINK(&trees.item(t)));
- index = 0;
- }
- ~CDFAttributeIterator()
- {
- attrs.kill();
- }
- bool first()
- {
- index = 0;
- return (attrs.ordinality()!=0);
- }
- bool next()
- {
- index++;
- return (index<attrs.ordinality());
- }
- bool isValid()
- {
- return (index<attrs.ordinality());
- }
- IPropertyTree & query()
- {
- return attrs.item(index);
- }
- };
- class CDFProtectedIterator: implements IDFProtectedIterator, public CInterface
- {
- StringAttr owner;
- StringAttr fn;
- unsigned count;
- bool issuper;
- Owned<IRemoteConnection> conn;
- Owned<IPropertyTreeIterator> fiter;
- Owned<IPropertyTreeIterator> piter;
- unsigned defaultTimeout;
- bool notsuper;
- bool superonly;
- void fill()
- {
- IPropertyTree &t = fiter->query();
- fn.set(t.queryProp("OrigName"));
- IPropertyTree &pt = piter->query();
- owner.set(pt.queryProp("@name"));
- count = pt.getPropInt("@count");
- }
- void clear()
- {
- piter.clear();
- fiter.clear();
- conn.clear();
- issuper = false;
- }
- public:
- IMPLEMENT_IINTERFACE;
- CDFProtectedIterator(const char *_owner,bool _notsuper,bool _superonly,unsigned _defaultTimeout)
- : owner(_owner)
- {
- count = 0;
- issuper = false;
- notsuper=_notsuper;
- superonly=_superonly;
- defaultTimeout = _defaultTimeout;
- }
- ~CDFProtectedIterator()
- {
- clear();
- }
- bool first()
- {
- clear();
- conn.setown(querySDS().connect("Files",myProcessSession(),0, defaultTimeout));
- if (!conn)
- return false;
- IPropertyTree *t = conn->queryRoot();
- if (!superonly) {
- fiter.setown(t->getElements("//File[Attr/Protect]", iptiter_remote));
- if (fiter->first()) {
- piter.setown(fiter->query().getElements("Attr/Protect"));
- if (piter->first()) {
- fill();
- return true;
- }
- }
- }
- if (!notsuper) {
- issuper = true;
- fiter.clear();
- fiter.setown(t->getElements("//SuperFile[Attr/Protect]", iptiter_remote));
- if (fiter->first()) {
- piter.setown(fiter->query().getElements("Attr/Protect"));
- if (piter->first()) {
- fill();
- return true;
- }
- }
- }
- clear();
- return false;
- }
- bool next()
- {
- if (!fiter.get())
- return false;
- if (piter->next()) {
- fill();
- return true;
- }
- for (;;) {
- if (fiter->next()) {
- piter.setown(fiter->query().getElements("Attr/Protect"));
- if (piter->first()) {
- fill();
- return true;
- }
- }
- else if (!notsuper&&!issuper) {
- issuper = true;
- fiter.clear();
- fiter.setown(conn->queryRoot()->getElements("//SuperFile[Attr/Protect]", iptiter_remote));
- if (fiter->first()) {
- piter.setown(fiter->query().getElements("Attr/Protect"));
- if (piter->first()) {
- fill();
- return true;
- }
- }
- else
- break;
- }
- else
- break;
- }
- clear();
- return false;
- }
- bool isValid()
- {
- return fiter.get()!=NULL;
- }
- const char *queryFilename()
- {
- return fn;
- }
- const char *queryOwner()
- {
- return owner;
- }
- unsigned getCount()
- {
- return count;
- }
- bool isSuper()
- {
- return issuper;
- }
- };
- // --------------------------------------------------------
- class CDistributedFilePart: public CInterface, implements IDistributedFilePart
- {
- unsigned partIndex;
- CDistributedFile &parent;
- Owned<IPropertyTree> attr;
- CriticalSection sect;
- StringAttr overridename; // may or not be relative to directory
- bool dirty; // whether needs updating in tree
- offset_t getSize(bool checkCompressed);
- public:
- virtual void Link(void) const;
- virtual bool Release(void) const;
- void set(IPropertyTree *pt,FileClusterInfoArray &clusters,unsigned maxcluster);
- RemoteFilename &getFilename(RemoteFilename &ret,unsigned copy);
- void renameFile(IFile *file);
- IPropertyTree &queryAttributes();
- bool lockProperties(unsigned timems);
- void unlockProperties(DFTransactionState state);
- bool isHost(unsigned copy);
- offset_t getFileSize(bool allowphysical,bool forcephysical);
- offset_t getDiskSize(bool allowphysical,bool forcephysical);
- bool getModifiedTime(bool allowphysical,bool forcephysical,CDateTime &dt);
- bool getCrc(unsigned &crc);
- unsigned getPhysicalCrc();
- IPartDescriptor *getPartDescriptor();
- unsigned numCopies();
- INode *queryNode(unsigned copy);
- unsigned queryDrive(unsigned copy);
- StringBuffer &getPartName(StringBuffer &name);
- StringBuffer &getPartDirectory(StringBuffer &name,unsigned copy);
- const char *queryOverrideName() { return overridename; }
- void clearOverrideName()
- {
- if (overridename.get()&&overridename.length()) {
- dirty = true;
- overridename.clear();
- }
- }
- unsigned bestCopyNum(const IpAddress &ip,unsigned rel=0);
- unsigned copyClusterNum(unsigned copy,unsigned *replicate=NULL);
- void childLink(void) { CInterface::Link(); }
- bool childRelease(void) { return CInterface::Release(); }
- CDistributedFilePart(CDistributedFile &_parent,unsigned _part,IPartDescriptor *pd);
- unsigned getPartIndex()
- {
- return partIndex;
- }
- INode *getNode(unsigned copy)
- {
- INode *ret = queryNode(copy);
- if (ret)
- return LINK(ret);
- return NULL;
- }
- void setAttr(IPropertyTree &pt)
- {
- attr.setown(createPTreeFromIPT(&pt)); // take a copy
- dirty = false;
- }
- IPropertyTree *queryAttr()
- {
- return attr;
- }
- inline CDistributedFile &queryParent()
- {
- return parent;
- }
- inline bool isDirty()
- {
- return dirty;
- }
- inline bool clearDirty()
- {
- bool ret=dirty;
- dirty = false;
- return ret;
- }
- };
- // --------------------------------------------------------
- class CDistributedFilePartArray: public CIArrayOf<CDistributedFilePart>
- {
- public:
- virtual ~CDistributedFilePartArray() // this shouldn't be needed - points to problem in CIArrayOf?
- {
- kill();
- }
- void kill(bool nodel = false)
- {
- if (nodel)
- CIArrayOf<CDistributedFilePart>::kill(true);
- else {
- while (ordinality()) {
- CDistributedFilePart &part = popGet();
- part.Release();
- }
- }
- }
- };
- // --------------------------------------------------------
- /**
- * Base Iterator class for all iterator types. Implements basic iteration
- * logic and forces all iterators to behave similarly. This will simplify
- * future compatibility with STL containers/algorithms.
- *
- * INTERFACE needs to be extended from IIteratorOf<>
- * ARRAYTY need to be extended from IArrayOf<>
- */
- template <class INTERFACE, class ARRAYTY>
- class CDistributedFileIteratorBase: implements INTERFACE, public CInterface
- {
- protected:
- unsigned index;
- ARRAYTY list;
- virtual bool set() { return isValid(); }
- public:
- IMPLEMENT_IINTERFACE;
- CDistributedFileIteratorBase()
- : index(0)
- {
- }
- virtual ~CDistributedFileIteratorBase()
- {
- list.kill();
- }
- bool first()
- {
- if (list.ordinality() == 0)
- return false;
- index = 0;
- return set();
- }
- bool next()
- {
- index++;
- set();
- return isValid();
- }
- bool isValid()
- {
- return (index < list.ordinality());
- }
- };
- /**
- * FilePart Iterator, used by files to manipulate its parts.
- */
- class CDistributedFilePartIterator: public CDistributedFileIteratorBase<IDistributedFilePartIterator, CDistributedFilePartArray>
- {
- public:
- CDistributedFilePartIterator(CDistributedFilePartArray &parts, IDFPartFilter *filter)
- {
- ForEachItemIn(i,parts) {
- if (!filter||filter->includePart(i))
- list.append(*LINK(&parts.item(i)));
- }
- }
- CDistributedFilePartIterator()
- {
- }
- IDistributedFilePart & query()
- {
- return list.item(index);
- }
- CDistributedFilePartArray &queryParts()
- {
- return list;
- }
- };
- /**
- * File Iterator, used by directory to list file search results.
- */
- class CDistributedFileIterator: public CDistributedFileIteratorBase<IDistributedFileIterator, PointerArray>
- {
- Owned<IDistributedFile> cur;
- IDistributedFileDirectory *parent;
- Linked<IUserDescriptor> udesc;
- Linked<IDistributedFileTransaction> transaction;
- bool set()
- {
- while (isValid()) {
- cur.setown(parent->lookup(queryName(),udesc));
- if (cur)
- return true;
- index++;
- }
- return false;
- }
- public:
- CDistributedFileIterator(IDistributedFileDirectory *_dir,const char *wildname,bool includesuper,IUserDescriptor *user,IDistributedFileTransaction *_transaction=NULL)
- : transaction(_transaction)
- {
- setUserDescriptor(udesc,user);
- if (!wildname||!*wildname)
- wildname = "*";
- parent = _dir;
- bool recursive = (stricmp(wildname,"*")==0);
- Owned<IDFAttributesIterator> attriter = parent->getDFAttributesIterator(wildname,user,recursive,includesuper,NULL);
- ForEach(*attriter) {
- IPropertyTree &pt = attriter->query();
- list.append(strdup(pt.queryProp("@name")));
- }
- index = 0;
- if (list.ordinality()>1)
- qsortvec(list.getArray(),list.ordinality(),strcompare);
- }
- const char *queryName()
- {
- return (const char *)list.item(index);
- }
- StringBuffer & getName(StringBuffer &name)
- {
- return name.append(queryName());
- }
- IDistributedFile & query()
- {
- return *cur;
- }
- };
- /**
- * SuperFile Iterator, used by CDistributedFile to list all its super-owners by name.
- */
- class CDistributedSuperFileIterator: public CDistributedFileIteratorBase<IDistributedSuperFileIterator, StringAttrArray>
- {
- CDistributedFileDirectory *parent;
- Linked<IUserDescriptor> udesc;
- Linked<IDistributedFileTransaction> transaction;
- Owned<IDistributedSuperFile> cur;
- Linked<IDistributedFile> owner;
- public:
- CDistributedSuperFileIterator(IDistributedFile *_owner, CDistributedFileDirectory *_parent,IPropertyTree *root,IUserDescriptor *user, IDistributedFileTransaction *_transaction)
- : owner(_owner), transaction(_transaction)
- {
- setUserDescriptor(udesc,user);
- parent = _parent;
- if (root)
- {
- Owned<IPropertyTreeIterator> iter = root->getElements("SuperOwner");
- StringBuffer pname;
- ForEach(*iter)
- {
- iter->query().getProp("@name",pname.clear());
- if (pname.length())
- list.append(* new StringAttrItem(pname.str()));
- }
- }
- }
- IDistributedSuperFile & query()
- {
- // NOTE: This used to include a do/while (!cur.get()&&next()) loop
- // this should never be needed but match previous semantics
- // throwing an exception now, to catch the error early on
- if (transaction.get())
- cur.setown(transaction->lookupSuperFile(queryName()));
- else
- cur.setown(parent->lookupSuperFile(queryName(),udesc,NULL));
- if (!cur.get())
- throw MakeStringException(-1,"superFileIter: invalid super-file on query at %s", queryName());
- return *cur;
- }
- virtual const char *queryName()
- {
- if (isValid())
- return list.item(index).text.get();
- return NULL;
- }
- };
- //-----------------------------------------------------------------------------
- inline void dfCheckRoot(const char *trc,Owned<IPropertyTree> &root,IRemoteConnection *conn)
- {
- if (root.get()!=conn->queryRoot()) {
- DBGLOG("%s - root changed",trc);
- #ifdef _DEBUG
- PrintStackReport();
- #endif
- root.setown(conn->getRoot());
- }
- }
- static bool setFileProtectTree(IPropertyTree &p,const char *owner, bool protect)
- {
- bool ret = false;
- CDateTime dt;
- dt.setNow();
- if (owner&&*owner) {
- Owned<IPropertyTree> t = getNamedPropTree(&p,"Protect","@name",owner,false);
- if (t) {
- unsigned c = t->getPropInt("@count");
- if (protect)
- c++;
- else {
- if (c>=1) {
- p.removeTree(t);
- c = 0;
- }
- else
- c--;
- }
- if (c) {
- t->setPropInt("@count",c);
- StringBuffer str;
- t->setProp("@modified",dt.getString(str).str());
- }
- }
- else if (protect) {
- t.setown(addNamedPropTree(&p,"Protect","@name",owner));
- t->setPropInt("@count",1);
- StringBuffer str;
- t->setProp("@modified",dt.getString(str).str());
- }
- ret = true;
- }
- else if (!protect) {
- unsigned n=0;
- IPropertyTree *pt;
- while ((pt=p.queryPropTree("Protect[1]"))!=NULL) {
- p.removeTree(pt);
- n++;
- }
- if (n)
- ret = true;
- }
- return ret;
- }
- static bool checkProtectAttr(const char *logicalname,IPropertyTree *froot,StringBuffer &reason)
- {
- Owned<IPropertyTreeIterator> wpiter = froot->getElements("Attr/Protect");
- bool prot = false;
- ForEach(*wpiter) {
- IPropertyTree &t = wpiter->query();
- if (t.getPropInt("@count")) {
- const char *wpname = t.queryProp("@name");
- if (!wpname||!*wpname)
- wpname = "<Unknown>";
- if (prot)
- reason.appendf(", %s",wpname);
- else {
- reason.appendf("file %s protected by %s",logicalname,wpname);
- prot = true;
- }
- }
- }
- return prot;
- }
- /**
- * A template class which implements the common methods of an IDistributedFile interface.
- * The actual interface (extended from IDistributedFile) is provided as a template argument.
- */
- template <class INTERFACE>
- class CDistributedFileBase : implements INTERFACE, public CInterface
- {
- protected:
- Owned<IPropertyTree> root;
- Owned<IRemoteConnection> conn; // kept connected during lifetime for attributes
- CDfsLogicalFileName logicalName;
- CriticalSection sect;
- CDistributedFileDirectory *parent;
- unsigned proplockcount;
- unsigned transactionnest;
- Linked<IUserDescriptor> udesc;
- unsigned defaultTimeout;
- bool dirty;
- bool external = false;
- Owned<IRemoteConnection> superOwnerLock;
- public:
- IPropertyTree *queryRoot() { return root; }
- CDistributedFileBase<INTERFACE>()
- {
- parent = NULL;
- proplockcount = 0;
- transactionnest = 0;
- defaultTimeout = INFINITE;
- dirty = false;
- }
- ~CDistributedFileBase<INTERFACE>()
- {
- root.clear();
- }
- void setSuperOwnerLock(IRemoteConnection *_superOwnerLock)
- {
- superOwnerLock.setown(_superOwnerLock);
- }
- unsigned setPropLockCount(unsigned _propLockCount)
- {
- unsigned prevPropLockCount = proplockcount;
- proplockcount = _propLockCount;
- return prevPropLockCount;
- }
- bool isCompressed(bool *blocked)
- {
- return ::isCompressed(queryAttributes(),blocked);
- }
- StringBuffer &getLogicalName(StringBuffer &lname)
- {
- lname.append(logicalName.get());
- return lname;
- }
- void setLogicalName(const char *lname)
- {
- logicalName.set(lname);
- }
- const char *queryLogicalName()
- {
- return logicalName.get();
- }
- IPropertyTree &queryAttributes()
- {
- IPropertyTree *t = root->queryPropTree("Attr");
- if (!t)
- t = root->setPropTree("Attr",createPTree("Attr")); // takes ownership
- return *t;
- }
- IPropertyTree *queryHistory() const
- {
- IPropertyTree *attr = root->queryPropTree("Attr");
- if (attr)
- return attr->queryPropTree("History");
- return nullptr;
- }
- void resetHistory()
- {
- DistributedFilePropertyLock lock(this);
- queryAttributes().removeTree(queryHistory());
- }
- protected:
- class CFileChangeWriteLock
- {
- IRemoteConnection *conn;
- unsigned timeoutMs, prevMode;
- public:
- CFileChangeWriteLock(IRemoteConnection *_conn, unsigned _timeoutMs)
- : conn(_conn), timeoutMs(_timeoutMs)
- {
- if (conn)
- {
- prevMode = conn->queryMode();
- unsigned newMode = (prevMode & ~RTM_LOCKBASIC_MASK) | RTM_LOCK_WRITE;
- conn->changeMode(RTM_LOCK_WRITE, timeoutMs);
- }
- else
- prevMode = RTM_NONE;
- }
- ~CFileChangeWriteLock()
- {
- if (conn)
- conn->changeMode(prevMode, timeoutMs);
- }
- void clear() { conn = NULL; }
- };
- IPropertyTree *closeConnection(bool removeFile)
- {
- Owned<IPropertyTree> detachedRoot = createPTreeFromIPT(root);
- root.clear();
- if (conn)
- {
- conn->close(removeFile);
- conn.clear();
- }
- return detachedRoot.getClear();
- }
- IPropertyTree *resetFileAttr(IPropertyTree *prop=NULL)
- {
- if (prop)
- return root->setPropTree("Attr", prop);
- root->removeProp("Attr");
- return NULL;
- }
- void updateFS(const CDfsLogicalFileName &lfn, unsigned timeoutMs)
- {
- // Update the file system
- removeFileEmptyScope(lfn, timeoutMs);
- // MORE: We shouldn't have to update all relationships if we had done a better job making sure
- // that all correct relationships were properly cleaned up
- queryDistributedFileDirectory().removeAllFileRelationships(lfn.get());
- }
- public:
- bool isAnon()
- {
- return !logicalName.isSet();
- }
- /*
- * Change connection to write-mode, allowing multiple writers only on the same instance.
- * Returns true if the lock was lost at least once before succeeding, hinting that some
- * resources might need reload (like sub-files list, etc).
- *
- * WARN: This is not thread-safe
- *
- * @deprecated : use DistributedFilePropertyLock instead, when possible
- */
- bool lockProperties(unsigned timeoutms)
- {
- bool reload = false;
- if (timeoutms==INFINITE)
- timeoutms = defaultTimeout;
- if (proplockcount++==0)
- {
- if (conn)
- {
- conn->rollback(); // changes chouldn't be done outside lock properties
- #ifdef TRACE_LOCKS
- PROGLOG("lockProperties: pre safeChangeModeWrite(%x)",(unsigned)(memsize_t)conn.get());
- #endif
- try
- {
- if (0 == timeoutms)
- conn->changeMode(RTM_LOCK_WRITE, 0, true); // 0 timeout, test and fail immediately if contention
- else
- safeChangeModeWrite(conn,queryLogicalName(),reload,timeoutms);
- }
- catch(IException *)
- {
- proplockcount--;
- dfCheckRoot("lockProperties",root,conn);
- if (reload)
- dirty = true; // safeChangeModeWrite unlocked, and reload will be need if retried
- throw;
- }
- if (dirty) // a previous attempt unlocked and did not reload
- {
- dirty = false;
- if (!reload) // if reload=true, safeChangeModeWrite has just reloaded, so no need to again here
- {
- conn->reload();
- reload = true;
- }
- }
- #ifdef TRACE_LOCKS
- PROGLOG("lockProperties: done safeChangeModeWrite(%x)",(unsigned)(memsize_t)conn.get());
- LogRemoteConn(conn);
- #endif
- dfCheckRoot("lockProperties",root,conn);
- }
- }
- return reload;
- }
- /*
- * Change connection back to read mode on the last unlock. There should never be
- * an uneven number of locks/unlocks, since that will leave the connection with
- * the DFS locked until the instance's destruction.
- *
- * WARN: This is not thread-safe
- *
- * @deprecated : use DistributedFilePropertyLock instead, when possible
- */
- void unlockProperties(DFTransactionState state=TAS_NONE)
- {
- savePartsAttr();
- if (--proplockcount==0) {
- if (conn) {
- // Transactional logic, if any
- switch(state) {
- case TAS_SUCCESS:
- conn->commit();
- break;
- case TAS_FAILURE:
- conn->rollback();
- break;
- case TAS_RETRY:
- conn->changeMode(RTM_NONE,defaultTimeout,true);
- return;
- // TAS_NONE, do nothing
- }
- #ifdef TRACE_LOCKS
- PROGLOG("unlockProperties: pre changeMode(%x)",(unsigned)(memsize_t)conn.get());
- #endif
- conn->changeMode(RTM_LOCK_READ,defaultTimeout,true);
- #ifdef TRACE_LOCKS
- PROGLOG("unlockProperties: post changeMode(%x)",(unsigned)(memsize_t)conn.get());
- LogRemoteConn(conn);
- #endif
- dfCheckRoot("unlockProperties",root,conn);
- }
- }
- }
- bool getModificationTime(CDateTime &dt)
- {
- StringBuffer str;
- if (!root->getProp("@modified",str))
- return false;
- dt.setString(str.str());
- return true;
- }
- void setModificationTime(const CDateTime &dt)
- {
- DistributedFilePropertyLock lock(this);
- if (dt.isNull())
- root->removeProp("@modified");
- else {
- StringBuffer str;
- root->setProp("@modified",dt.getString(str).str());
- }
- root->removeProp("@verified");
- }
- void setModified()
- {
- CDateTime dt;
- dt.setNow();
- setModificationTime(dt);
- }
- virtual StringBuffer &getECL(StringBuffer &buf)
- {
- MemoryBuffer mb;
- if (queryAttributes().getPropBin("ECLbin",mb))
- buf.deserialize(mb);
- else
- queryAttributes().getProp("ECL",buf);
- return buf;
- }
- virtual void setECL(const char *ecl)
- {
- DistributedFilePropertyLock lock(this);
- IPropertyTree &p = queryAttributes();
- #ifdef PACK_ECL
- p.removeProp("ECL");
- if (!ecl||!*ecl)
- p.removeProp("ECLbin");
- else {
- MemoryBuffer mb; // could be better
- StringBuffer buf(ecl);
- buf.serialize(mb);
- root->setPropBin("ECLbin",mb.length(),mb.toByteArray());
- }
- #else
- p.setProp("ECL",ecl);
- #endif
- }
- void setProtect(const char *owner, bool protect, unsigned timems)
- {
- if (logicalName.isForeign()) {
- parent->setFileProtect(logicalName,udesc,owner,protect);
- }
- else {
- bool ret=false;
- if (conn) {
- DistributedFilePropertyLock lock(this);
- IPropertyTree &p = queryAttributes();
- CDateTime dt;
- dt.setNow();
- if (setFileProtectTree(p,owner,protect))
- conn->commit();
- dfCheckRoot("setProtect.1",root,conn);
- }
- else
- IERRLOG("setProtect - cannot protect %s (no connection in file)",owner?owner:"");
- }
- }
- virtual IDistributedSuperFileIterator *getOwningSuperFiles(IDistributedFileTransaction *_transaction)
- {
- CriticalBlock block(sect);
- return new CDistributedSuperFileIterator(this,parent,root,udesc,_transaction);
- }
- virtual void checkFormatAttr(IDistributedFile *sub, const char* exprefix="")
- {
- // check file has same (or similar) format
- IPropertyTree &superProp = queryAttributes();
- IPropertyTree &subProp = sub->queryAttributes();
- if (!exprefix)
- exprefix = "CheckFormatAttr";
- bool superBlocked = false;
- bool superComp = ::isCompressed(superProp,&superBlocked);
- bool subBlocked = false;
- bool subComp = ::isCompressed(subProp,&subBlocked);
- // FIXME: this may fail if an empty superfile added to a compressed superfile
- if (superComp != subComp)
- throw MakeStringException(-1,"%s: %s's compression setting (%s) is different than %s's (%s)",
- exprefix, sub->queryLogicalName(), (subComp?"compressed":"uncompressed"),
- queryLogicalName(), (superComp?"compressed":"uncompressed"));
- if (superBlocked != subBlocked)
- throw MakeStringException(-1,"%s: %s's blocked setting (%s) is different than %s's (%s)",
- exprefix, sub->queryLogicalName(), (subBlocked?"blocked":"unblocked"),
- queryLogicalName(), (superBlocked?"blocked":"unblocked"));
- #ifdef SUBFILE_COMPATIBILITY_CHECKING
- // MORE - this first check looks completely useless to me
- bool subSoft = subProp.hasProp("_record_layout");
- bool superSoft = superProp.hasProp("_record_layout");
- if (superSoft != subSoft)
- throw MakeStringException(-1,"%s: %s's record layout (%s) is different than %s's (%s)",
- exprefix, sub->queryLogicalName(), (subSoft?"dynamic":"fixed"),
- queryLogicalName(), (superSoft?"dynamic":"fixed"));
- // If they don't, they must have the same size
- if (!superSoft) {
- unsigned superSize = superProp.getPropInt("@recordSize",0);
- unsigned subSize = subProp.getPropInt("@recordSize",0);
- // Variable length files (CSV, etc) have zero record size
- if (superSize && subSize && (superSize != subSize))
- throw MakeStringException(-1,"%s: %s's record size (%d) is different than %s's (%d)",
- exprefix, sub->queryLogicalName(), subSize, queryLogicalName(), superSize);
- }
- StringBuffer superFmt;
- bool superHasFmt = superProp.getProp("@format",superFmt);
- StringBuffer subFmt;
- bool subHasFmt = subProp.getProp("@format",subFmt);
- if (subHasFmt && superHasFmt)
- if (strcmp(normalizeFormat(superFmt).str(),normalizeFormat(subFmt).str()) != 0)
- throw MakeStringException(-1,"%s: %s's format (%s) is different than %s's (%s)",
- exprefix, sub->queryLogicalName(), superFmt.str(),
- queryLogicalName(), subFmt.str());
- #endif
- bool superLocal = superProp.getPropBool("@local",false);
- bool subLocal = subProp.getPropBool("@local",false);
- if (subLocal != superLocal)
- throw MakeStringException(-1,"%s: %s's local setting (%s) is different than %s's (%s)",
- exprefix, sub->queryLogicalName(), (subLocal?"local":"global"),
- queryLogicalName(), (superLocal?"local":"global"));
- int superRepO = superProp.getPropInt("@replicateOffset",1);
- int subRepO = subProp.getPropInt("@replicateOffset",1);
- if (subRepO != superRepO)
- throw MakeStringException(-1,"%s: %s's replication offset (%d) is different than %s's (%d)",
- exprefix, sub->queryLogicalName(), subRepO,
- queryLogicalName(), superRepO);
- }
- void getSuperOwners(StringArray &owners)
- {
- if (root)
- {
- StringBuffer owner;
- Owned<IPropertyTreeIterator> iter = root->getElements("SuperOwner");
- ForEach (*iter)
- {
- iter->query().getProp("@name", owner.clear());
- if (owner.length())
- {
- if (NotFound == owners.find(owner))
- owners.append(owner);
- }
- }
- }
- }
- void linkSuperOwner(const char *superfile,bool link)
- {
- if (!superfile||!*superfile)
- return;
- if (conn)
- {
- CFileSuperOwnerLock attrLock;
- if (0 == proplockcount)
- verifyex(attrLock.init(logicalName, conn, defaultTimeout, "CDistributedFile::linkSuperOwner"));
- Owned<IPropertyTree> t = getNamedPropTree(root,"SuperOwner","@name",superfile,false);
- if (t && !link)
- root->removeTree(t);
- else if (!t && link)
- t.setown(addNamedPropTree(root,"SuperOwner","@name",superfile));
- }
- else
- IERRLOG("linkSuperOwner - cannot link to %s (no connection in file)",superfile);
- }
- void setAccessed()
- {
- CDateTime dt;
- dt.setNow();
- setAccessedTime(dt);
- }
- virtual StringBuffer &getColumnMapping(StringBuffer &mapping)
- {
- queryAttributes().getProp("@columnMapping",mapping);
- return mapping;
- }
- virtual void setColumnMapping(const char *mapping)
- {
- DistributedFilePropertyLock lock(this);
- if (!mapping||!*mapping)
- queryAttributes().removeProp("@columnMapping");
- else
- queryAttributes().setProp("@columnMapping",mapping);
- }
- unsigned setDefaultTimeout(unsigned timems)
- {
- unsigned ret = defaultTimeout;
- defaultTimeout = timems;
- return ret;
- }
- // MORE - simplify this, after removing CLightWeightSuperFileConn
- bool canModify(StringBuffer &reason)
- {
- return !checkProtectAttr(logicalName.get(),root,reason);
- }
- bool checkOwned(StringBuffer &error)
- {
- Owned<IPropertyTreeIterator> iter = root->getElements("SuperOwner");
- if (iter->first())
- {
- error.append("Cannot remove file ").append(logicalName.get()).append(" as owned by SuperFile(s): ");
- for (;;)
- {
- error.append(iter->query().queryProp("@name"));
- if (!iter->next())
- break;
- error.append(", ");
- }
- return true;
- }
- return false;
- }
- bool canRemove(StringBuffer &reason,bool ignoresub=false)
- {
- CriticalBlock block(sect);
- if (!canModify(reason))
- return false;
- const char *logicalname = logicalName.get();
- if (!logicalname||!*logicalname) {
- reason.appendf("empty filename");
- return false;
- }
- if (logicalName.isQuery())
- {
- reason.appendf("%s is query",logicalname);
- return false;
- }
- if (logicalName.isForeign())
- {
- reason.appendf("%s is foreign",logicalname);
- return false;
- }
- if (logicalName.isMulti())
- {
- reason.appendf("%s is multi",logicalname);
- return false;
- }
- if (!ignoresub)
- {
- if (checkOwned(reason))
- return false;
- }
- return true;
- }
- virtual const char *queryDefaultDir() = 0;
- virtual unsigned numParts() = 0;
- virtual IDistributedFilePart &queryPart(unsigned idx) = 0;
- virtual IDistributedFilePart* getPart(unsigned idx) = 0;
- virtual void savePartsAttr(bool force=false) = 0;
- virtual IDistributedFilePartIterator *getIterator(IDFPartFilter *filter=NULL) = 0;
- virtual IDistributedSuperFile *querySuperFile() = 0;
- virtual ClusterPartDiskMapSpec &queryPartDiskMapping(unsigned clusternum)=0;
- virtual void updatePartDiskMapping(const char *clustername,const ClusterPartDiskMapSpec &spec)=0;
- virtual void enqueueReplicate()=0;
- virtual bool getAccessedTime(CDateTime &dt) = 0; // get date and time last accessed (returns false if not set)
- virtual void setAccessedTime(const CDateTime &dt) = 0; // set date and time last accessed
- virtual bool isExternal() const { return external; }
- };
- class CDistributedFile: public CDistributedFileBase<IDistributedFile>
- {
- protected:
- CDistributedFilePartArray parts; // use queryParts to access
- CriticalSection sect;
- StringAttr directory;
- StringAttr partmask;
- FileClusterInfoArray clusters;
- void savePartsAttr(bool force) override
- {
- CriticalBlock block (sect);
- IPropertyTree *pt;
- if (parts.ordinality()==1) { // single part saved as part
- if (parts.item(0).clearDirty()||force) {
- CDistributedFilePart &part = parts.item(0);
- while ((pt=root->queryPropTree("Part[1]"))!=NULL)
- root->removeTree(pt);
- pt = createPTreeFromIPT(part.queryAttr());
- pt->setPropInt("@num",1);
- const char *grp = root->queryProp("@group");
- if (!grp||!*grp) {
- StringBuffer eps;
- pt->addProp("@node",part.queryNode(0)->endpoint().getUrlStr(eps).str()); // legacy
- }
- const char *override = part.queryOverrideName();
- if (override&&*override)
- pt->setProp("@name",override);
- else {
- pt->removeProp("@name");
- const char *mask=queryPartMask();
- if (mask&&*mask) {
- StringBuffer tmp;
- expandMask(tmp,mask,0,1);
- pt->setProp("@name",tmp.str());
- }
- }
- root->setPropTree("Part",pt);
- }
- }
- else {
- unsigned n = parts.ordinality();
- unsigned i1;
- for (i1=0;i1<n;i1++) {
- if (parts.item(i1).clearDirty()||force) {
- MemoryBuffer mb;
- CriticalBlock block (sect);
- ForEachItemIn(i2,parts)
- serializePartAttr(mb,parts.item(i2).queryAttr());
- root->setPropBin("Parts",mb.length(),mb.toByteArray());
- while ((pt=root->queryPropTree("Part[1]"))!=NULL)
- root->removeTree(pt);
- break;
- }
- }
- while (i1<n)
- parts.item(i1++).clearDirty();
- }
- }
- void detach(unsigned timeoutMs, bool removePhysicals, ICodeContext *ctx)
- {
- // Removes either a cluster in case of multi cluster file or the whole File entry from DFS
- assert(proplockcount == 0 && "CDistributedFile detach: Some properties are still locked");
- assertex(!isAnon()); // not attached!
- if (removePhysicals)
- {
- // Avoid removing physically when there is no physical representation
- if (logicalName.isMulti())
- removePhysicals = false;
- }
- StringBuffer clusterName;
- Owned<IFileDescriptor> fileDescCopy;
- #ifdef EXTRA_LOGGING
- PROGLOG("CDistributedFile::detach(%s)",logicalName.get());
- LOGPTREE("CDistributedFile::detach root.1",root);
- #endif
- {
- CriticalBlock block(sect); // JCSMORE - not convinced this is still necessary
- CFileChangeWriteLock writeLock(conn, timeoutMs);
- logicalName.getCluster(clusterName);
- // copy file descriptor before altered, used by physical file removal routines
- if (removePhysicals)
- {
- MemoryBuffer mb;
- Owned<IFileDescriptor> fdesc = getFileDescriptor(clusterName);
- fdesc->serialize(mb);
- fileDescCopy.setown(deserializeFileDescriptor(mb));
- }
- bool removeFile=true;
- if (clusterName.length())
- {
- // Remove just cluster specified, unless it's the last, in which case detach below will remove File entry.
- if (clusters.ordinality()>1)
- {
- if (removeCluster(clusterName.str()))
- removeFile=false;
- else
- ThrowStringException(-1, "Cluster %s not present in file %s", clusterName.str(), logicalName.get());
- }
- }
- if (removeFile)
- {
- // check can remove, e.g. cannot if this is a subfile of a super
- StringBuffer reason;
- if (!canRemove(reason))
- throw MakeStringException(-1,"detach: %s", reason.str());
- }
- // detach this IDistributeFile
- /* JCSMORE - In 'removeFile=true' case, this should really delete before release exclusive lock.
- */
- writeLock.clear();
- root.setown(closeConnection(removeFile));
- // NB: The file is now unlocked
- if (removeFile && !logicalName.isExternal())
- updateFS(logicalName, parent->queryDefaultTimeout());
- logicalName.clear();
- }
- // NB: beyond unlock
- if (removePhysicals)
- {
- CriticalBlock block(physicalChange);
- Owned<IMultiException> exceptions = MakeMultiException("CDistributedFile::detach");
- removePhysicalPartFiles(fileDescCopy, exceptions);
- if (exceptions->ordinality())
- throw exceptions.getClear();
- }
- }
- bool removePhysicalPartFiles(IFileDescriptor *fileDesc, IMultiException *mexcept, unsigned numParallelDeletes=0)
- {
- if (logicalName.isExternal())
- {
- if (logicalName.isQuery())
- return false;
- }
- if (logicalName.isForeign())
- throw MakeStringException(-1,"cannot remove a foreign file (%s)",logicalName.get());
- return parent->removePhysicalPartFiles(logicalName.get(), fileDesc, mexcept, numParallelDeletes);
- }
- protected: friend class CDistributedFilePart;
- CDistributedFilePartArray &queryParts()
- {
- return parts;
- }
- public:
- IMPLEMENT_IINTERFACE_O;
- CDistributedFile(CDistributedFileDirectory *_parent, IRemoteConnection *_conn,const CDfsLogicalFileName &lname,IUserDescriptor *user) // takes ownership of conn
- {
- setUserDescriptor(udesc,user);
- logicalName.set(lname);
- parent = _parent;
- conn.setown(_conn);
- CClustersLockedSection sect(logicalName, false);
- root.setown(conn->getRoot());
- root->queryBranch("."); // load branch
- #ifdef EXTRA_LOGGING
- LOGPTREE("CDistributedFile.a root",root);
- #endif
- Owned<IFileDescriptor> fdesc = deserializeFileDescriptorTree(root,&queryNamedGroupStore(),0);
- #ifdef EXTRA_LOGGING
- LOGFDESC("CDistributedFile.a fdesc",fdesc);
- #endif
- setFileAttrs(fdesc,false);
- setClusters(fdesc);
- setPreferredClusters(_parent->defprefclusters);
- setParts(fdesc,false);
- //shrinkFileTree(root); // enable when safe!
- }
- CDistributedFile(CDistributedFileDirectory *_parent, IFileDescriptor *fdesc, IUserDescriptor *user, bool _external)
- {
- #ifdef EXTRA_LOGGING
- LOGFDESC("CDistributedFile.b fdesc",fdesc);
- #endif
- parent = _parent;
- root.setown(createPTree(queryDfsXmlBranchName(DXB_File)));
- root->setPropTree("ClusterLock", createPTree());
- // fdesc->serializeTree(*root,IFDSF_EXCLUDE_NODES);
- setFileAttrs(fdesc,true);
- setClusters(fdesc);
- setPreferredClusters(_parent->defprefclusters);
- saveClusters();
- setParts(fdesc,true);
- udesc.set(user);
- external = _external;
- #ifdef EXTRA_LOGGING
- LOGPTREE("CDistributedFile.b root.1",root);
- #endif
- offset_t totalsize=0;
- unsigned checkSum = ~0;
- bool useableCheckSum = true;
- MemoryBuffer pmb;
- unsigned n = fdesc->numParts();
- for (unsigned i=0;i<n;i++) {
- IPropertyTree *partattr = &fdesc->queryPart(i)->queryProperties();
- if (!partattr)
- {
- totalsize = (unsigned)-1;
- useableCheckSum = false;
- }
- else
- {
- offset_t psz;
- if (totalsize!=(offset_t)-1) {
- psz = (offset_t)partattr->getPropInt64("@size", -1);
- if (psz==(offset_t)-1)
- totalsize = psz;
- else
- totalsize += psz;
- }
- if (useableCheckSum) {
- unsigned crc;
- if (fdesc->queryPart(i)->getCrc(crc))
- checkSum ^= crc;
- else
- useableCheckSum = false;
- }
- }
- }
- shrinkFileTree(root);
- if (totalsize!=(offset_t)-1)
- queryAttributes().setPropInt64("@size", totalsize);
- if (useableCheckSum)
- queryAttributes().setPropInt64("@checkSum", checkSum);
- setModified();
- #ifdef EXTRA_LOGGING
- LOGPTREE("CDistributedFile.b root.2",root);
- #endif
- }
- void killParts()
- {
- ForEachItemIn(i,parts)
- parts.item(i).childRelease();
- parts.kill(true);
- }
- ~CDistributedFile()
- {
- assert(proplockcount == 0 && "CDistributedFile destructor: Some properties are still locked");
- if (conn)
- conn->rollback(); // changes should always be done in locked properties
- killParts();
- clusters.kill();
- }
- IFileDescriptor *getFileDescriptor(const char *_clusterName) override
- {
- CriticalBlock block (sect);
- Owned<IFileDescriptor> fdesc = deserializeFileDescriptorTree(root,&queryNamedGroupStore(),0);
- fdesc->setTraceName(logicalName.get());
- StringArray cnames;
- if (_clusterName&&*_clusterName)
- {
- StringAttr clusterName = _clusterName;
- clusterName.toLowerCase();
- cnames.append(clusterName);
- }
- else
- getClusterNames(cnames);
- fdesc->setClusterOrder(cnames,_clusterName&&*_clusterName);
- return fdesc.getClear();
- }
- void setFileAttrs(IFileDescriptor *fdesc,bool save)
- {
- directory.set(fdesc->queryDefaultDir());
- partmask.set(fdesc->queryPartMask());
- const char *lfn = logicalName.get();
- if (lfn&&*lfn) {
- if (partmask.isEmpty()) {
- StringBuffer mask;
- getPartMask(mask,lfn,0);
- partmask.set(mask);
- }
- }
- if (!save)
- return;
- if (directory.isEmpty())
- root->removeProp("@directory");
- else
- root->setProp("@directory",directory);
- if (partmask.isEmpty())
- root->removeProp("@partmask");
- else
- root->setProp("@partmask",partmask);
- IPropertyTree *t = &fdesc->queryProperties();
- if (isEmptyPTree(t))
- resetFileAttr();
- else
- resetFileAttr(createPTreeFromIPT(t));
- }
- void setClusters(IFileDescriptor *fdesc)
- {
- clusters.clear();
- unsigned nc = fdesc->numClusters();
- if (nc) {
- for (unsigned i=0;i<nc;i++) {
- StringBuffer cname;
- StringBuffer clabel;
- IClusterInfo &cluster = *createClusterInfo(
- fdesc->getClusterGroupName(i,cname,NULL).str(),
- fdesc->queryClusterGroup(i),
- fdesc->queryPartDiskMapping(i),
- &queryNamedGroupStore()
- );
- #ifdef EXTRA_LOGGING
- PROGLOG("setClusters(%d,%s)",i,cname.str());
- #endif
- if (!cluster.queryGroup(&queryNamedGroupStore())) {
- IERRLOG("IDistributedFileDescriptor cannot set cluster for %s",logicalName.get());
- }
- clusters.append(cluster);
- }
- }
- else
- IERRLOG("No cluster specified for %s",logicalName.get());
- }
- virtual unsigned numClusters() override
- {
- return clusters.ordinality();
- }
- virtual unsigned findCluster(const char *clustername) override
- {
- return clusters.find(clustername);
- }
- virtual unsigned getClusterNames(StringArray &clusternames) override
- {
- return clusters.getNames(clusternames);
- }
- void reloadClusters()
- {
- // called from CClustersLockedSection
- if (!CDistributedFileBase<IDistributedFile>::conn)
- return;
- assertex(CDistributedFileBase<IDistributedFile>::proplockcount==0); // cannot reload clusters if properties locked
- CDistributedFileBase<IDistributedFile>::conn->reload(); // should only be cluster changes but a bit dangerous
- IPropertyTree *t = CDistributedFileBase<IDistributedFile>::conn->queryRoot(); // NB not CDistributedFileBase<IDistributedFile>::queryRoot();
- if (!t)
- return;
- clusters.clear();
- getClusterInfo(*t,&queryNamedGroupStore(),0,clusters);
- }
- void saveClusters()
- {
- // called from CClustersLockedSection
- IPropertyTree *t;
- if (CDistributedFileBase<IDistributedFile>::conn)
- t = CDistributedFileBase<IDistributedFile>::conn->queryRoot();
- else
- t = CDistributedFileBase<IDistributedFile>::queryRoot(); //cache
- if (!t)
- return;
- IPropertyTree *pt;
- IPropertyTree *tc = CDistributedFileBase<IDistributedFile>::queryRoot(); //cache
- IPropertyTree *t0 = t;
- StringBuffer grplist;
- // the following is complicated by fact there is a cache of the file branch
- for (;;) {
- while ((pt=t->queryPropTree("Cluster[1]"))!=NULL)
- t->removeTree(pt);
- ForEachItemIn(i,clusters) {
- IPropertyTree *pt = createPTree("Cluster");
- clusters.item(i).serializeTree(pt,IFDSF_EXCLUDE_GROUPS);
- if (!isEmptyPTree(pt)) {
- t->addPropTree("Cluster",pt);
- if (t==t0) {
- StringBuffer clabel;
- clusters.item(i).getClusterLabel(clabel);
- if (clabel.length()) {
- if (grplist.length())
- grplist.append(',');
- grplist.append(clabel);
- }
- }
- }
- else
- DBGLOG("CFileClusterOwner::saveClusters - empty cluster");
- }
- if (grplist.length())
- t->setProp("@group",grplist.str());
- else
- t->removeProp("@group");
- t->setPropInt("@numclusters",clusters.ordinality());
- t->setProp("@directory", directory);
- if (t==tc)
- break;
- t = tc; // now fix cache
- }
- if (CDistributedFileBase<IDistributedFile>::conn)
- CDistributedFileBase<IDistributedFile>::conn->commit(); // should only be cluster changes but a bit dangerous
- }
- virtual void addCluster(const char *clustername,const ClusterPartDiskMapSpec &mspec) override
- {
- if (!clustername&&!*clustername)
- return;
- CClustersLockedSection cls(CDistributedFileBase<IDistributedFile>::logicalName, true);
- reloadClusters();
- if (findCluster(clustername)!=NotFound) {
- IDFS_Exception *e = new CDFS_Exception(DFSERR_ClusterAlreadyExists,clustername);
- throw e;
- }
- Owned<IClusterInfo> cluster = createClusterInfo(clustername,NULL,mspec,&queryNamedGroupStore());
- if (cluster->queryGroup(&queryNamedGroupStore())) {
- clusters.append(*cluster.getClear());
- }
- else {
- IDFS_Exception *e = new CDFS_Exception(DFSERR_ClusterNotFound,clustername);
- throw e;
- }
- saveClusters();
- }
- virtual bool removeCluster(const char *clustername) override
- {
- CClustersLockedSection cls(CDistributedFileBase<IDistributedFile>::logicalName, true);
- reloadClusters();
- unsigned i = findCluster(clustername);
- if (i!=NotFound) {
- if (clusters.ordinality()==1)
- throw MakeStringException(-1,"CFileClusterOwner::removeCluster cannot remove sole cluster %s",clustername);
- // If the cluster is the 'default' one we need to update the directory too
- StringBuffer oldBaseDir;
- char pathSepChar = getPathSepChar(directory.get());
- DFD_OS os = SepCharBaseOs(pathSepChar);
- clusters.item(i).getBaseDir(oldBaseDir, os);
- unsigned oldLen = oldBaseDir.length();
- clusters.remove(i);
- if (oldLen && strncmp(directory, oldBaseDir, oldLen)==0 && (directory[oldLen]==pathSepChar || directory[oldLen]=='\0'))
- {
- StringBuffer newBaseDir;
- clusters.item(0).getBaseDir(newBaseDir, os);
- newBaseDir.append(directory.get() + oldBaseDir.length());
- directory.set(newBaseDir);
- }
- saveClusters();
- return true;
- }
- return false;
- }
- virtual void setPreferredClusters(const char *clusterlist) override
- {
- clusters.setPreferred(clusterlist,CDistributedFileBase<IDistributedFile>::logicalName);
- }
- INode *queryNode(unsigned idx,unsigned copy)
- {
- unsigned rep;
- unsigned cluster = copyClusterNum(idx,copy,&rep);
- if (cluster==NotFound)
- return queryNullNode();
- unsigned nn;
- unsigned dn;
- IGroup *grp = clusters.queryGroup(cluster);
- if (!grp)
- return queryNullNode();
- if (!clusters.item(cluster).queryPartDiskMapping().calcPartLocation (idx, numParts(),rep, grp->ordinality(), nn, dn))
- return queryNullNode();
- return &grp->queryNode(nn);
- }
- unsigned queryDrive(unsigned idx,unsigned copy,const char *dir)
- {
- // this is odd routine
- unsigned dn = dir?getPathDrive(dir):0;
- if (dn)
- return dn;
- unsigned rep;
- unsigned cluster = copyClusterNum(idx,copy,&rep);
- if (cluster==NotFound)
- return 0;
- unsigned nn;
- IGroup *grp = clusters.queryGroup(cluster);
- if (!grp)
- return 0;
- if (!clusters.item(cluster).queryPartDiskMapping().calcPartLocation (idx, numParts(),rep, grp->ordinality(), nn, dn))
- return 0;
- return dn;
- }
- virtual StringBuffer &getClusterName(unsigned clusternum,StringBuffer &name) override
- {
- return clusters.getName(clusternum,name);
- }
- unsigned copyClusterNum(unsigned part, unsigned copy,unsigned *replicate)
- {
- return clusters.copyNum(part,copy, numParts(),replicate);
- }
- virtual ClusterPartDiskMapSpec &queryPartDiskMapping(unsigned clusternum) override
- {
- assertex(clusternum<clusters.ordinality());
- return clusters.queryPartDiskMapping(clusternum);
- }
- virtual void updatePartDiskMapping(const char *clustername,const ClusterPartDiskMapSpec &spec) override
- {
- CClustersLockedSection cls(CDistributedFileBase<IDistributedFile>::logicalName, true);
- reloadClusters();
- unsigned i = findCluster(clustername);
- if (i!=NotFound) {
- clusters.updatePartDiskMapping(i,spec);
- saveClusters();
- }
- }
- virtual IGroup *queryClusterGroup(unsigned clusternum) override
- {
- return clusters.queryGroup(clusternum);
- }
- virtual StringBuffer &getClusterGroupName(unsigned clusternum, StringBuffer &name) override
- {
- return clusters.item(clusternum).getGroupName(name, &queryNamedGroupStore());
- }
- virtual unsigned numCopies(unsigned partno) override
- {
- return clusters.numCopies(partno,numParts());
- }
- virtual void setSingleClusterOnly() override
- {
- clusters.setSingleClusterOnly();
- }
- unsigned numClusterCopies(unsigned cnum,unsigned partnum)
- {
- IClusterInfo &cluster = clusters.item(cnum);
- IGroup *grp = cluster.queryGroup();
- return cluster.queryPartDiskMapping().numCopies(partnum,grp?grp->ordinality():1,numParts());
- }
- void adjustClusterDir(unsigned partno,unsigned copy, StringBuffer &path)
- {
- // this corrects the directory for a copy
- // assumes default dir matches one of clusters
- unsigned rep=0;
- unsigned cluster = NotFound;
- const char *ds = path.str();
- unsigned nc = clusters.ordinality();
- if (nc>1) {
- StringAttr matched;
- StringAttr toadd;
- unsigned i=0;
- bool c = 0;
- int cp = (int)copy;
- while (i<nc) {
- StringBuffer dcmp;
- clusters.item(i).getBaseDir(dcmp,SepCharBaseOs(getPathSepChar(ds))); // no trailing sep
- const char *t = dcmp.str();
- const char *d = ds;
- while (*d&&(*t==*d)) {
- d++;
- t++;
- }
- if (!*t&&(!*d||isPathSepChar(*d))&&(dcmp.length()>matched.length()))
- matched.set(dcmp);
- unsigned mc = numClusterCopies(i,partno);
- if ((cp>=0)&&(cp<(int)mc)) {
- toadd.set(dcmp);
- rep = (unsigned)cp;
- cluster = i;
- }
- cp -= mc;
- i++;
- }
- if (!matched.isEmpty()&&!toadd.isEmpty()&&(strcmp(matched,toadd)!=0)) {
- StringBuffer tmp(toadd);
- tmp.append(ds+matched.length());
- path.swapWith(tmp);
- }
- }
- else {
- rep = copy;
- cluster = 0;
- }
- // now set replicate
- if (cluster!=NotFound) {
- unsigned n;
- unsigned d;
- ClusterPartDiskMapSpec& mspec = clusters.item(cluster).queryPartDiskMapping();
- mspec.calcPartLocation(partno,numParts(),rep,clusters.queryGroup(cluster)?clusters.queryGroup(cluster)->ordinality():numParts(),n,d);
- if ((d==1) && (mspec.flags&CPDMSF_overloadedConfig) && mspec.defaultReplicateDir.length())
- path.set(mspec.defaultReplicateDir.get());
- else
- setReplicateFilename(path,d);
- }
- }
- void setParts(IFileDescriptor *fdesc,bool save)
- {
- unsigned np = fdesc->numParts();
- for (unsigned i = 0;i<np;i++) {
- CDistributedFilePart &part = *new CDistributedFilePart(*this,i,fdesc->queryPart(i));
- parts.append(part);
- }
- if (save) {
- root->setPropInt("@numparts",parts.ordinality());
- savePartsAttr(true);
- }
- }
- virtual unsigned numParts() override
- {
- return parts.ordinality();
- }
- virtual IDistributedFilePart &queryPart(unsigned idx) override
- {
- if (idx<parts.ordinality())
- return queryParts().item(idx);
- return *(IDistributedFilePart *)NULL;
- }
- virtual IDistributedFilePart* getPart(unsigned idx) override
- {
- if (idx>=parts.ordinality())
- return NULL;
- IDistributedFilePart *ret = &queryParts().item(idx);
- return LINK(ret);
- }
- virtual IDistributedFilePartIterator *getIterator(IDFPartFilter *filter=NULL) override
- {
- return new CDistributedFilePartIterator(queryParts(),filter);
- }
- virtual void rename(const char *_logicalname,IUserDescriptor *user) override
- {
- StringBuffer prevname;
- Owned<IFileRelationshipIterator> reliter;
- // set prevname
- if (!isAnon()) {
- getLogicalName(prevname);
- try {
- IFileRelationshipIterator *iter = parent->lookupAllFileRelationships(prevname.str());
- reliter.setown(iter);
- }
- catch (IException *e) {
- EXCLOG(e,"CDistributedFile::rename");
- e->Release();
- }
- detachLogical();
- }
- attach(_logicalname,user);
- if (prevname.length()) {
- DistributedFilePropertyLock lock(this);
- IPropertyTree &pt = queryAttributes();
- StringBuffer list;
- if (pt.getProp("@renamedFrom",list)&&list.length())
- list.append(',');
- pt.setProp("@renamedFrom",list.append(prevname).str());
- }
- if (reliter.get()) {
- // add back any relationships with new name
- parent->renameFileRelationships(prevname.str(),_logicalname,reliter,user);
- }
- }
- virtual const char *queryDefaultDir() override
- {
- CriticalBlock block (sect);
- return directory.get();
- }
- virtual const char *queryPartMask() override
- {
- CriticalBlock block (sect);
- if (partmask.isEmpty()) {
- assertex(root);
- partmask.set(root->queryProp("@partmask"));
- }
- return partmask.get();
- }
- bool isAnon()
- {
- return (!logicalName.isSet());
- }
- virtual void attach(const char *_logicalname,IUserDescriptor *user) override
- {
- CriticalBlock block (sect);
- assertex(isAnon()); // already attached!
- logicalName.set(_logicalname);
- if (!checkLogicalName(logicalName,user,true,true,true,"attach"))
- return; // query
- #ifdef EXTRA_LOGGING
- PROGLOG("CDistributedFile::attach(%s)",_logicalname);
- LOGPTREE("CDistributedFile::attach root.1",root);
- #endif
- parent->addEntry(logicalName,root.getClear(),false,false);
- killParts();
- clusters.kill();
- CFileLock fcl;
- verifyex(fcl.init(logicalName, DXB_File, RTM_LOCK_READ, defaultTimeout, "CDistributedFile::attach"));
- conn.setown(fcl.detach());
- root.setown(conn->getRoot());
- root->queryBranch("."); // load branch
- Owned<IFileDescriptor> fdesc = deserializeFileDescriptorTree(root,&queryNamedGroupStore(),0);
- setFileAttrs(fdesc,false);
- setClusters(fdesc);
- setParts(fdesc,false);
- setUserDescriptor(udesc, user);
- #ifdef EXTRA_LOGGING
- LOGFDESC("CDistributedFile::attach fdesc",fdesc);
- LOGPTREE("CDistributedFile::attach root.2",root);
- #endif
- }
- /*
- * Internal method (not in IDistributedFile interface) that is used
- * when renaming files (so don't delete the physical representation).
- *
- * This is also used during CPPUINT tests, so we need to make them public
- * only when tests are enabled (ie. non-production mode).
- *
- * See removeLogical()
- */
- public:
- void detachLogical(unsigned timeoutms=INFINITE)
- {
- detach(timeoutms, false, NULL);
- }
- public:
- virtual void detach(unsigned timeoutMs=INFINITE, ICodeContext *ctx=NULL) override
- {
- detach(timeoutMs, true, ctx);
- }
- virtual bool existsPhysicalPartFiles(unsigned short port) override
- {
- unsigned width = numParts();
- CriticalSection errcrit;
- class casyncfor: public CAsyncFor
- {
- IDistributedFile *file;
- unsigned short port;
- CriticalSection &errcrit;
- unsigned width;
- public:
- bool ok;
- casyncfor(IDistributedFile *_file,unsigned _width,unsigned short _port,CriticalSection &_errcrit)
- : errcrit(_errcrit)
- {
- file = _file;
- port = _port;
- ok = true;
- width = _width;
- ok = true;
- }
- void Do(unsigned i)
- {
- {
- CriticalBlock block(errcrit);
- if (!ok)
- return;
- }
- Owned<IDistributedFilePart> part = file->getPart(i);
- unsigned nc = part->numCopies();
- for (unsigned copy = 0; copy < nc; copy++)
- {
- RemoteFilename rfn;
- part->getFilename(rfn,copy);
- if (port)
- rfn.setPort(port); // if daliservix
- Owned<IFile> partfile = createIFile(rfn);
- try
- {
- if (partfile->exists())
- return;
- }
- catch (IException *e)
- {
- CriticalBlock block(errcrit);
- StringBuffer s("Failed to find file part ");
- s.append(partfile->queryFilename()).append(" on ");
- rfn.queryEndpoint().getUrlStr(s);
- EXCLOG(e, s.str());
- e->Release();
- }
- }
- CriticalBlock block(errcrit);
- ok = false;
- }
- } afor(this,width,port,errcrit);
- afor.For(width,10,false,true);
- return afor.ok;
- }
- // This method takes an existing physical directory path for a logical file
- // and a constructed path to the same logical file created in this context
- // and deduces the original base path e.g. /var/lib/HPCCSystems/hpcc-data/thor
- // This is necessary, because there is no not enough context to directly fetch the
- // original base path to construct new paths for the rename
- bool getBase(const char *oldPath, const char *thisPath, StringBuffer &baseDir)
- {
- const char *oldEnd = oldPath+strlen(oldPath)-1;
- const char *thisEnd = thisPath+strlen(thisPath)-1;
- if (isPathSepChar(*oldEnd))
- oldEnd--;
- if (isPathSepChar(*thisEnd))
- thisEnd--;
- const char *oldP = oldEnd, *thisP = thisEnd;
- for (;;) {
- if (oldP==oldPath || thisP==thisPath)
- break;
- if (*oldP != *thisP) {
- // unless last was separator, continue until find one
- if (isPathSepChar(*(oldP+1)))
- oldP++;
- else {
- while (oldP != oldPath && (!isPathSepChar(*oldP)))
- oldP--;
- }
- baseDir.append(oldP-oldPath, oldPath);
- return true;
- }
- --oldP;
- --thisP;
- }
- return false;
- }
- virtual bool renamePhysicalPartFiles(const char *newname,
- const char *cluster,
- IMultiException *mexcept,
- const char *newbasedir) override
- {
- // cluster TBD
- unsigned width = numParts();
- StringBuffer newdir;
- StringBuffer newmask;
- const char *diroverride = NULL;
- char psc = getPathSepChar(directory.get());
- DFD_OS os = SepCharBaseOs(psc);
- StringBuffer basedir;
- const char *myBase;
- if (newbasedir)
- {
- diroverride = newbasedir;
- myBase = newbasedir;
- }
- else
- myBase = queryBaseDirectory(grp_unknown, 0, os);
- StringBuffer baseDir, newPath;
- makePhysicalPartName(logicalName.get(), 0, 0, newPath, false, os, diroverride);
- if (!getBase(directory, newPath, baseDir))
- baseDir.append(myBase); // getBase returns false, if directory==newPath, so have common base
- getPartMask(newmask,newname,width);
- if (newmask.length()==0)
- return false;
- makePhysicalPartName(newname, 0, 0, newPath.clear(), false, os, diroverride);
- if (newPath.length()==0)
- return false;
- if (isPathSepChar(newPath.charAt(newPath.length()-1)))
- newPath.setLength(newPath.length()-1);
- newPath.remove(0, strlen(myBase));
- newdir.append(baseDir).append(newPath);
- StringBuffer fullname;
- CIArrayOf<CIStringArray> newNames;
- unsigned i;
- for (i=0;i<width;i++) {
- newNames.append(*new CIStringArray);
- CDistributedFilePart &part = parts.item(i);
- for (unsigned copy=0; copy<part.numCopies(); copy++) {
- makePhysicalPartName(newname, i+1, width, newPath.clear(), false, os, myBase);
- newPath.remove(0, strlen(myBase));
- StringBuffer copyDir(baseDir);
- adjustClusterDir(i, copy, copyDir);
- fullname.clear().append(copyDir).append(newPath);
- newNames.item(i).append(fullname);
- }
- }
- // NB: the code below, specifically deals with 1 primary + 1 replicate
- // it will need refactoring if it's to deal with multiple clusters/copies
- // first check file doestn't exist for any new part
- CriticalSection crit;
- class casyncforbase: public CAsyncFor
- {
- protected:
- CriticalSection &crit;
- CIArrayOf<CIStringArray> &newNames;
- IDistributedFile *file;
- unsigned width;
- IMultiException *mexcept;
- bool *ignoreprim;
- bool *ignorerep;
- public:
- bool ok;
- bool * doneprim;
- bool * donerep;
- IException *except;
- casyncforbase(IDistributedFile *_file,CIArrayOf<CIStringArray> &_newNames,unsigned _width,IMultiException *_mexcept,CriticalSection &_crit,bool *_ignoreprim,bool *_ignorerep)
- : newNames(_newNames),crit(_crit)
- {
- width = _width;
- file = _file;
- ok = true;
- mexcept = _mexcept;
- doneprim = (bool *)calloc(sizeof(bool),width);
- donerep = (bool *)calloc(sizeof(bool),width);
- except = NULL;
- ignoreprim = _ignoreprim;
- ignorerep = _ignorerep;
- }
- ~casyncforbase()
- {
- free(doneprim);
- free(donerep);
- }
- virtual bool doPart(IDistributedFilePart *,bool,RemoteFilename &,RemoteFilename &, bool &)
- #ifdef _WIN32
- {
- assertex(!"doPart"); // stupid microsoft error
- return false;
- }
- #else
- = 0;
- #endif
- void Do(unsigned idx)
- {
- {
- CriticalBlock block(crit);
- if (!ok)
- return;
- }
- Owned<IDistributedFilePart> part = file->getPart(idx);
- unsigned copies = part->numCopies();
- for (int copy = copies-1; copy>=0; copy--)
- {
- if ((copy==0)&&ignoreprim&&ignoreprim[idx])
- continue;
- if ((copy!=0)&&ignorerep&&ignorerep[idx])
- continue;
- bool pok=false;
- IException *ex = NULL;
- RemoteFilename oldrfn;
- part->getFilename(oldrfn,(unsigned)copy);
- const char *newfn = newNames.item(idx).item(copy);
- if (!newfn||!*newfn)
- continue;
- RemoteFilename newrfn;
- newrfn.setPath(part->queryNode(copy)->endpoint(),newfn);
- try {
- pok = doPart(part,copy!=0,oldrfn,newrfn,(copy==0)?doneprim[idx]:donerep[idx]);
- }
- catch (IException *e) {
- EXCLOG(e, NULL);
- ex = e;
- }
- CriticalBlock block(crit);
- if (!pok||ex) {
- ok = false;
- if (ex) {
- StringBuffer s("renamePhysicalPartFiles ");
- s.append(file->queryLogicalName()).append(" part ").append(newfn);
- EXCLOG(ex, s.str());
- if (mexcept)
- mexcept->append(*ex);
- else {
- if (except)
- ex->Release();
- else
- except = ex;
- }
- }
- }
- }
- }
- };
- class casyncfor1: public casyncforbase
- {
- public:
- casyncfor1(IDistributedFile *_file,CIArrayOf<CIStringArray> &_newNames,unsigned _width,IMultiException *_mexcept,CriticalSection &_crit,bool *_ignoreprim,bool *_ignorerep)
- : casyncforbase(_file,_newNames,_width,_mexcept,_crit,_ignoreprim,_ignorerep)
- {
- }
- bool doPart(IDistributedFilePart *part,bool isrep,RemoteFilename &oldrfn,RemoteFilename &newrfn, bool &done)
- {
- done = false;
- Owned<IFile> src = createIFile(oldrfn);
- if (src->exists())
- done = true;
- else {
- StringBuffer s;
- oldrfn.getRemotePath(s);
- DBGLOG("renamePhysicalPartFiles: %s doesn't exist",s.str());
- return true;
- }
- Owned<IFile> dest = createIFile(newrfn);
- StringBuffer newname;
- newrfn.getRemotePath(newname);
- if (dest->exists()) {
- IDFS_Exception *e = new CDFS_Exception(DFSERR_PhysicalPartAlreadyExists,newname.str());
- throw e;
- }
- // check destination directory exists
- StringBuffer newdir;
- splitDirTail(newname.str(),newdir);
- Owned<IFile> destdir = createIFile(newdir.str());
- destdir->createDirectory();
- return true;
- }
- } afor1 (this,newNames,width,mexcept,crit,NULL,NULL);
- afor1.For(width,10,false,true);
- if (afor1.except)
- throw afor1.except; // no recovery needed
- if (!afor1.ok)
- return false; // no recovery needed
- MemoryAttr ignorebuf;
- bool *ignoreprim = (bool *)ignorebuf.allocate(width*sizeof(bool)*2);
- bool *ignorerep = ignoreprim+width;
- for (i=0;i<width;i++) {
- if (afor1.donerep[i]) {
- ignorerep[i] = false;
- ignoreprim[i] = !afor1.doneprim[i];
- }
- else if (afor1.doneprim[i]) {
- ignorerep[i] = true;
- ignoreprim[i] = false;
- }
- else {
- StringBuffer s(queryLogicalName());
- s.append(" Part ").append(i+1);
- IDFS_Exception *e = new CDFS_Exception(DFSERR_PhysicalPartDoesntExist,s.str());
- throw e;
- }
- }
- // now do the rename
- class casyncfor2: public casyncforbase
- {
- public:
- casyncfor2(IDistributedFile *_file,CIArrayOf<CIStringArray> &_newNames,unsigned _width,IMultiException *_mexcept,CriticalSection &_crit,bool *_ignoreprim,bool *_ignorerep)
- : casyncforbase(_file,_newNames,_width,_mexcept,_crit,_ignoreprim,_ignorerep)
- {
- }
- bool doPart(IDistributedFilePart *part,bool isrep,RemoteFilename &oldrfn,RemoteFilename &newrfn, bool &done)
- {
- done = false;
- StringBuffer oldfn;
- oldrfn.getRemotePath(oldfn);
- StringBuffer newfn;
- newrfn.getRemotePath(newfn);
- Owned<IFile> f = createIFile(oldrfn);
- if (!isrep||f->exists()) { // ignore non-existant replicates
- f->move(newfn.str());
- PROGLOG("Succeeded rename %s to %s",oldfn.str(),newfn.str());
- }
- done = true;
- return true;;
- }
- } afor2 (this,newNames,width,mexcept,crit,ignoreprim,ignorerep);
- afor2.For(width,10,false,true);
- if (afor2.ok) {
- // now rename directory and partmask
- DistributedFilePropertyLock lock(this);
- root->setProp("@directory",newdir.str());
- root->setProp("@partmask",newmask.str());
- partmask.set(newmask.str());
- directory.set(newdir.str());
- StringBuffer mask;
- for (unsigned i=0;i<width;i++) {
- mask.appendf("Part[%d]/@name",i+1);
- parts.item(i).clearOverrideName();
- }
- savePartsAttr(false);
- }
- else {
- // attempt recovery
- // do this synchronously to maximize chance of success (I don't expect many to have been done)
- for (i=0;i<width;i++) {
- Owned<IDistributedFilePart> part = getPart(i);
- unsigned copies = part->numCopies();
- for (int copy = copies-1; copy>=0; copy--) {
- bool done = (copy==0)?afor2.doneprim[i]:afor2.donerep[i];
- if (done) {
- RemoteFilename oldrfn;
- part->getFilename(oldrfn,(unsigned)copy);
- const char *newfn = newNames.item(i).item(copy);
- if (!newfn||!*newfn)
- continue;
- RemoteFilename newrfn;
- newrfn.setPath(part->queryNode(copy)->endpoint(),newfn);
- for (unsigned t=1;t<3;t++) { // 3 goes
- try {
- StringBuffer oldfn;
- oldrfn.getRemotePath(oldfn);
- StringBuffer newfn;
- newrfn.getRemotePath(newfn);
- Owned<IFile> f = createIFile(newrfn);
- f->move(oldfn.str());
- PROGLOG("Succeeded rename %s back to %s",newfn.str(),oldfn.str());
- break;
- }
- catch (IException *e) {
- if (!afor2.except)
- afor2.except = e;
- else
- e->Release();
- }
- }
- }
- }
- }
- }
- if (afor2.except)
- throw afor2.except;
- return afor2.ok;
- }
- IPropertyTree *queryRoot() { return root; }
- virtual __int64 getFileSize(bool allowphysical,bool forcephysical) override
- {
- __int64 ret = (__int64)(forcephysical?-1:queryAttributes().getPropInt64("@size",-1));
- if (ret==-1)
- {
- ret = 0;
- unsigned n = numParts();
- for (unsigned i=0;i<n;i++)
- {
- Owned<IDistributedFilePart> part = getPart(i);
- __int64 ps = part->getFileSize(allowphysical,forcephysical);
- if (ps == -1)
- {
- ret = ps;
- break;
- }
- ret += ps;
- }
- }
- return ret;
- }
- virtual __int64 getDiskSize(bool allowphysical,bool forcephysical) override
- {
- if (!isCompressed(NULL))
- return getFileSize(allowphysical, forcephysical);
- __int64 ret = (__int64)(forcephysical?-1:queryAttributes().getPropInt64("@compressedSize",-1));
- if (ret==-1)
- {
- ret = 0;
- unsigned n = numParts();
- for (unsigned i=0;i<n;i++)
- {
- Owned<IDistributedFilePart> part = getPart(i);
- __int64 ps = part->getDiskSize(allowphysical,forcephysical);
- if (ps == -1)
- {
- ret = ps;
- break;
- }
- ret += ps;
- }
- }
- return ret;
- }
- virtual bool getFileCheckSum(unsigned &checkSum) override
- {
- if (queryAttributes().hasProp("@checkSum"))
- checkSum = (unsigned)queryAttributes().getPropInt64("@checkSum");
- else
- {
- checkSum = ~0;
- unsigned n = numParts();
- for (unsigned i=0;i<n;i++) {
- Owned<IDistributedFilePart> part = getPart(i);
- unsigned crc;
- if (!part->getCrc(crc))
- return false;
- checkSum ^= crc;
- }
- }
- return true;
- }
- virtual bool getFormatCrc(unsigned &crc) override
- {
- if (queryAttributes().hasProp("@formatCrc")) {
- // NB pre record_layout CRCs are not valid
- crc = (unsigned)queryAttributes().getPropInt("@formatCrc");
- return true;
- }
- return false;
- }
- virtual bool getRecordLayout(MemoryBuffer &layout, const char *attrname) override
- {
- return queryAttributes().getPropBin(attrname, layout);
- }
- virtual bool getRecordSize(size32_t &rsz) override
- {
- if (queryAttributes().hasProp("@recordSize")) {
- rsz = (size32_t)queryAttributes().getPropInt("@recordSize");
- return true;
- }
- return false;
- }
- virtual unsigned getPositionPart(offset_t pos, offset_t &base) override
- {
- unsigned n = numParts();
- base = 0;
- for (unsigned i=0;i<n;i++) {
- Owned<IDistributedFilePart> part = getPart(i);
- offset_t ps = part->getFileSize(true,false);
- if (ps==(offset_t)-1)
- break;
- if (ps>pos)
- return i;
- pos -= ps;
- base += ps;
- }
- return NotFound;
- }
- IDistributedSuperFile *querySuperFile() override
- {
- return NULL; // i.e. this isn't super file
- }
- virtual bool checkClusterCompatible(IFileDescriptor &fdesc, StringBuffer &err) override
- {
- unsigned n = numParts();
- if (fdesc.numParts()!=n) {
- err.appendf("Different cluster width (%d/%d)",n,fdesc.numParts());
- return false;
- }
- if (fdesc.numClusters()!=1) {
- err.append("Cannot merge more than one cluster");
- return false;
- }
- StringBuffer cname;
- fdesc.getClusterLabel(0,cname);
- if (cname.length()&&(findCluster(cname.str())!=NotFound)) {
- err.append("File already contains cluster");
- err.append(cname.str());
- return false;
- }
- StringBuffer pname;
- StringBuffer fdtail;
- for (unsigned pn=0;pn<n;pn++) {
- IDistributedFilePart &part = queryPart(pn);
- part.getPartName(pname.clear());
- fdesc.queryPart(pn)->getTail(fdtail.clear());
- if (strcmp(pname.str(),fdtail.str())!=0) {
- err.appendf("Part name mismatch (%s,%s)",pname.str(),fdtail.str());
- return false;
- }
- RemoteFilename fdrfn;
- fdesc.getFilename(pn,0,fdrfn);
- unsigned nc = numCopies(pn);
- for (unsigned c = 0;c<nc;c++) {
- RemoteFilename rfn;
- part.getFilename(rfn,c);
- if (rfn.equals(fdrfn)) {
- err.appendf("Parts overlap %s and %s",pname.str(),fdtail.str());
- return false;
- }
- }
- }
- return true;
- }
- virtual void enqueueReplicate() override
- {
- MemoryBuffer mb;
- mb.append((byte)DRQ_REPLICATE).append(queryLogicalName());
- udesc->serialize(mb);
- CDateTime filedt;
- getModificationTime(filedt);
- filedt.serialize(mb);
- Owned<INamedQueueConnection> qconn = createNamedQueueConnection(0);
- Owned<IQueueChannel> qchannel = qconn->open(DFS_REPLICATE_QUEUE);
- qchannel->put(mb);
- }
- virtual bool getAccessedTime(CDateTime &dt) override
- {
- StringBuffer str;
- if (!root->getProp("@accessed",str))
- return false;
- dt.setString(str.str());
- return true;
- }
- virtual void setAccessedTime(const CDateTime &dt) override
- {
- if (logicalName.isForeign())
- parent->setFileAccessed(logicalName,udesc,dt);
- else
- {
- CFileAttrLock attrLock;
- if (conn)
- {
- if (!attrLock.init(logicalName, DXB_File, RTM_LOCK_WRITE, conn, defaultTimeout, "CDistributedFile::setAccessedTime"))
- {
- // In unlikely event File/Attr doesn't exist, must ensure created, commited and root connection is reloaded.
- verifyex(attrLock.init(logicalName, DXB_File, RTM_LOCK_WRITE|RTM_CREATE_QUERY, conn, defaultTimeout, "CDistributedFile::setAccessedTime"));
- attrLock.commit();
- conn->commit();
- conn->reload();
- root.setown(conn->getRoot());
- }
- }
- if (dt.isNull())
- queryAttributes().removeProp("@accessed");
- else
- {
- StringBuffer str;
- queryAttributes().setProp("@accessed",dt.getString(str).str());
- }
- }
- }
- virtual void setAccessed() override
- {
- CDateTime dt;
- dt.setNow();
- setAccessedTime(dt);
- }
- virtual void validate() override
- {
- if (!existsPhysicalPartFiles(0))
- {
- const char * logicalName = queryLogicalName();
- throw MakeStringException(-1, "Some physical parts do not exists, for logical file : %s",(isEmptyString(logicalName) ? "[unattached]" : logicalName));
- }
- }
- };
- static unsigned findSubFileOrd(const char *name)
- {
- if (*name=='#') {
- const char *n = name+1;
- if (*n) {
- do { n++; } while (*n&&isdigit(*n));
- if (!*n)
- return atoi(name+1)-1;
- }
- }
- return NotFound;
- }
- class CDistributedSuperFile: public CDistributedFileBase<IDistributedSuperFile>
- {
- void checkNotForeign()
- {
- if (!conn)
- throw MakeStringException(-1,"Operation not allowed on foreign file");
- }
- CDistributedFilePartArray partscache;
- FileClusterInfoArray clusterscache;
- /**
- * Adds a sub-file to a super-file within a transaction.
- */
- class cAddSubFileAction: public CDFAction
- {
- StringAttr parentlname;
- Owned<IDistributedSuperFile> parent;
- Owned<IDistributedFile> sub;
- StringAttr subfile;
- bool before;
- StringAttr other;
- public:
- cAddSubFileAction(const char *_parentlname,const char *_subfile,bool _before,const char *_other)
- : parentlname(_parentlname), subfile(_subfile), before(_before), other(_other)
- {
- }
- virtual bool prepare()
- {
- parent.setown(transaction->lookupSuperFile(parentlname));
- if (!parent)
- throw MakeStringException(-1,"addSubFile: SuperFile %s cannot be found",parentlname.get());
- if (!subfile.isEmpty())
- {
- try
- {
- sub.setown(transaction->lookupFile(subfile,SDS_SUB_LOCK_TIMEOUT));
- if (!sub)
- throw MakeStringException(-1,"cAddSubFileAction: sub file %s not found", subfile.str());
- // Must validate before locking for update below, to check sub is not already in parent (and therefore locked already)
- transaction->validateAddSubFile(parent, sub, subfile);
- }
- catch (ISDSException *e)
- {
- if (e->errorCode()!=SDSExcpt_LockTimeout)
- throw;
- e->Release();
- return false;
- }
- if (!sub.get())
- throw MakeStringException(-1,"addSubFile: File %s cannot be found to add",subfile.get());
- }
- // Try to lock all files
- addFileLock(parent);
- if (lock())
- {
- transaction->noteAddSubFile(parent, parentlname, sub);
- return true;
- }
- unlock();
- parent.clear();
- sub.clear();
- return false;
- }
- virtual void run()
- {
- if (!sub)
- throw MakeStringException(-1,"addSubFile(2): File %s cannot be found to add",subfile.get());
- CDistributedSuperFile *sf = QUERYINTERFACE(parent.get(),CDistributedSuperFile);
- if (sf)
- sf->doAddSubFile(LINK(sub),before,other,transaction);
- }
- virtual void commit()
- {
- CDistributedSuperFile *sf = QUERYINTERFACE(parent.get(),CDistributedSuperFile);
- if (sf)
- sf->updateParentFileAttrs(transaction);
- CDFAction::commit();
- }
- virtual void retry()
- {
- parent.clear();
- sub.clear();
- CDFAction::retry();
- }
- };
- /**
- * Removes a sub-file of a super-file within a transaction.
- */
- class cRemoveSubFileAction: public CDFAction
- {
- StringAttr parentlname;
- Owned<IDistributedSuperFile> parent;
- Owned<IDistributedFile> sub;
- StringAttr subfile;
- bool remsub;
- public:
- cRemoveSubFileAction(const char *_parentlname,const char *_subfile,bool _remsub=false)
- : parentlname(_parentlname), subfile(_subfile), remsub(_remsub)
- {
- }
- virtual bool prepare()
- {
- parent.setown(transaction->lookupSuperFile(parentlname));
- if (!parent)
- throw MakeStringException(-1,"removeSubFile: SuperFile %s cannot be found",parentlname.get());
- if (!subfile.isEmpty())
- {
- try
- {
- sub.setown(transaction->lookupFile(subfile,SDS_SUB_LOCK_TIMEOUT));
- }
- catch (ISDSException *e)
- {
- if (e->errorCode()!=SDSExcpt_LockTimeout)
- throw;
- e->Release();
- return false;
- }
- if (!transaction->isSubFile(parent, subfile, true))
- {
- IWARNLOG("removeSubFile: File %s is not a subfile of %s", subfile.get(), parent->queryLogicalName());
- parent.clear();
- sub.clear();
- return true; // NB: sub was not a member of super, issue warning and continue without locking
- }
- }
- // Try to lock all files
- addFileLock(parent);
- if (sub && remsub) // NB: I only need to lock (for exclusivity, if going to delete
- addFileLock(sub);
- if (lock())
- {
- if (sub)
- transaction->noteRemoveSubFile(parent, sub);
- else
- transaction->clearSubFiles(parent);
- return true;
- }
- unlock();
- parent.clear();
- sub.clear();
- return false;
- }
- virtual void run()
- {
- CDistributedSuperFile *sf = QUERYINTERFACE(parent.get(),CDistributedSuperFile);
- if (sf) {
- // Delay the deletion of the subs until commit
- if (remsub) {
- if (subfile) {
- CDfsLogicalFileName lname;
- lname.set(subfile.get());
- transaction->addDelayedDelete(lname, SDS_SUB_LOCK_TIMEOUT);
- } else { // Remove all subfiles
- Owned<IDistributedFileIterator> iter = parent->getSubFileIterator(false);
- ForEach (*iter) {
- CDfsLogicalFileName lname;
- IDistributedFile *f = &iter->query();
- lname.set(f->queryLogicalName());
- transaction->addDelayedDelete(lname, SDS_SUB_LOCK_TIMEOUT);
- }
- }
- }
- // Now we clean the subs
- if (subfile.get())
- sf->doRemoveSubFile(subfile.get(), transaction);
- else
- sf->doRemoveSubFiles(transaction);
- }
- }
- virtual void retry()
- {
- parent.clear();
- sub.clear();
- CDFAction::retry();
- }
- };
- /**
- * Removes all subfiles exclusively owned by named superfile within a transaction.
- */
- class cRemoveOwnedSubFilesAction: public CDFAction
- {
- StringAttr parentlname;
- Owned<IDistributedSuperFile> parent;
- bool remsub;
- public:
- cRemoveOwnedSubFilesAction(IDistributedFileTransaction *_transaction, const char *_parentlname,bool _remsub=false)
- : parentlname(_parentlname), remsub(_remsub)
- {
- }
- virtual bool prepare()
- {
- parent.setown(transaction->lookupSuperFile(parentlname));
- if (!parent)
- throw MakeStringException(-1,"removeOwnedSubFiles: SuperFile %s cannot be found", parentlname.get());
- // Try to lock all files
- addFileLock(parent);
- if (lock())
- return true;
- unlock();
- parent.clear();
- return false;
- }
- virtual void run()
- {
- CDistributedSuperFile *sf = QUERYINTERFACE(parent.get(),CDistributedSuperFile);
- if (sf)
- {
- StringArray toRemove;
- Owned<IDistributedFileIterator> iter = parent->getSubFileIterator(false);
- ForEach (*iter)
- {
- IDistributedFile *file = &iter->query();
- IDistributedSuperFile *super = file->querySuperFile();
- StringArray owners;
- if (super)
- {
- CDistributedSuperFile *_super = QUERYINTERFACE(super, CDistributedSuperFile);
- if (_super)
- _super->getSuperOwners(owners);
- }
- else
- {
- CDistributedFile *_file = QUERYINTERFACE(file, CDistributedFile);
- if (_file)
- _file->getSuperOwners(owners);
- }
- if (NotFound == owners.find(parentlname))
- ThrowStringException(-1, "removeOwnedSubFiles: SuperFile %s, subfile %s - subfile not owned by superfile", parentlname.get(), file->queryLogicalName());
- if (1 == owners.ordinality()) // just me
- {
- const char *logicalName = file->queryLogicalName();
- toRemove.append(logicalName);
- // Delay the deletion of the subs until commit
- if (remsub)
- {
- CDfsLogicalFileName lname;
- lname.set(logicalName);
- transaction->addDelayedDelete(lname, SDS_SUB_LOCK_TIMEOUT);
- }
- }
- }
- // Now we clean the subs
- if (sf->numSubFiles(false) == toRemove.ordinality())
- sf->doRemoveSubFiles(transaction); // remove all
- else
- {
- ForEachItemIn(r, toRemove)
- sf->doRemoveSubFile(toRemove.item(r), transaction);
- }
- }
- }
- virtual void retry()
- {
- parent.clear();
- CDFAction::retry();
- }
- };
- /**
- * Swaps sub-files between two super-files within a transaction.
- */
- class cSwapFileAction: public CDFAction
- {
- Linked<IDistributedSuperFile> super1, super2;
- StringAttr super1Name, super2Name;
- public:
- cSwapFileAction(const char *_super1Name, const char *_super2Name)
- : super1Name(_super1Name), super2Name(_super2Name)
- {
- }
- virtual bool prepare()
- {
- super1.setown(transaction->lookupSuperFile(super1Name));
- if (!super1)
- throw MakeStringException(-1,"swapSuperFile: SuperFile %s cannot be found", super1Name.get());
- super2.setown(transaction->lookupSuperFile(super2Name));
- if (!super2)
- {
- super1.clear();
- throw MakeStringException(-1,"swapSuperFile: SuperFile %s cannot be found", super2Name.get());
- }
- // Try to lock all files
- addFileLock(super1);
- for (unsigned i=0; i<super1->numSubFiles(); i++)
- addFileLock(&super1->querySubFile(i));
- addFileLock(super2);
- for (unsigned i=0; i<super2->numSubFiles(); i++)
- addFileLock(&super2->querySubFile(i));
- if (lock())
- {
- transaction->noteSuperSwap(super1, super2);
- return true;
- }
- unlock();
- super1.clear();
- super2.clear();
- return false;
- }
- virtual void run()
- {
- CDistributedSuperFile *sf = QUERYINTERFACE(super1.get(),CDistributedSuperFile);
- if (sf)
- sf->doSwapSuperFile(super2,transaction);
- }
- virtual void retry()
- {
- super1.clear();
- super2.clear();
- CDFAction::retry();
- }
- };
- /**
- * SubFile Iterator, used only to list sub-files of a super-file.
- */
- class cSubFileIterator: public CDistributedFileIteratorBase< IDistributedFileIterator, IArrayOf<IDistributedFile> >
- {
- public:
- cSubFileIterator(IArrayOf<IDistributedFile> &_subfiles, bool supersub)
- {
- ForEachItemIn(i,_subfiles) {
- IDistributedSuperFile* super = supersub?_subfiles.item(i).querySuperFile():NULL;
- if (super) {
- Owned<IDistributedFileIterator> iter = super->getSubFileIterator(true);
- ForEach(*iter)
- list.append(iter->get());
- }
- else
- list.append(*LINK(&_subfiles.item(i)));
- }
- }
- StringBuffer & getName(StringBuffer &name)
- {
- return list.item(index).getLogicalName(name);
- }
- IDistributedFile & query()
- {
- return list.item(index);
- }
- };
- void checkModify(const char *title)
- {
- StringBuffer reason;
- if (!canModify(reason)) {
- #ifdef EXTRA_LOGGING
- PROGLOG("CDistributedSuperFile::%s(canModify) %s",title,reason.str());
- #endif
- if (reason.length())
- throw MakeStringException(-1,"CDistributedSuperFile::%s %s",title,reason.str());
- }
- }
- protected:
- int interleaved; // 0 not interleaved, 1 interleaved old, 2 interleaved new
- IArrayOf<IDistributedFile> subfiles;
- void clearSuperOwners(unsigned timeoutMs, ICodeContext *ctx)
- {
- /* JCSMORE - Why on earth is this doing this way?
- * We are in a super file, we already have [read] locks to sub files (in 'subfiles' array)
- * This should iterate through those and call unlinkSubFile I think.
- */
- Owned<IPropertyTreeIterator> iter = root->getElements("SubFile");
- StringBuffer oquery;
- oquery.append("SuperOwner[@name=\"").append(logicalName.get()).append("\"]");
- ForEach(*iter)
- {
- const char *name = iter->query().queryProp("@name");
- if (name&&*name)
- {
- CDfsLogicalFileName subfn;
- subfn.set(name);
- CFileLock fconnlockSub;
- // JCSMORE - this is really not right, but consistent with previous version
- // MORE: Use CDistributedSuperFile::linkSuperOwner(false) - ie. unlink
- if (fconnlockSub.init(subfn, RTM_LOCK_READ, timeoutMs, "CDistributedFile::doRemoveEntry"))
- {
- IPropertyTree *subfroot = fconnlockSub.queryRoot();
- if (subfroot)
- {
- if (!subfroot->removeProp(oquery.str()))
- {
- VStringBuffer s("SubFile %s is not owned by SuperFile %s", name, logicalName.get());
- if (ctx)
- ctx->addWuException(s.str(), 0, SeverityWarning, "DFS[clearSuperOwner]");
- else
- {
- Owned<IException> e = makeStringException(-1, s.str());
- EXCLOG(e, "DFS[clearSuperOwner]");
- }
- }
- }
- }
- }
- }
- }
- static StringBuffer &getSubPath(StringBuffer &path,unsigned idx)
- {
- return path.append("SubFile[@num=\"").append(idx+1).append("\"]");
- }
- void loadSubFiles(IDistributedFileTransaction *transaction, unsigned timeout, bool link=false)
- {
- partscache.kill();
- StringBuffer path;
- StringBuffer subname;
- subfiles.kill();
- unsigned n = root->getPropInt("@numsubfiles");
- if (n == 0)
- return;
- try
- {
- // Find all reported indexes and bail on bad range (before we lock any file)
- Owned<IPropertyTreeIterator> subit = root->getElements("SubFile");
- // Adding a sub 'before' another get the list out of order (but still valid)
- OwnedMalloc<IPropertyTree *> orderedSubFiles(n, true);
- ForEach (*subit)
- {
- IPropertyTree &sub = subit->query();
- unsigned sn = sub.getPropInt("@num",0);
- if (sn == 0)
- ThrowStringException(-1, "CDistributedSuperFile: SuperFile %s: bad subfile part number %d of %d", logicalName.get(), sn, n);
- if (sn > n)
- ThrowStringException(-1, "CDistributedSuperFile: SuperFile %s: out-of-range subfile part number %d of %d", logicalName.get(), sn, n);
- if (orderedSubFiles[sn-1])
- ThrowStringException(-1, "CDistributedSuperFile: SuperFile %s: duplicated subfile part number %d of %d", logicalName.get(), sn, n);
- orderedSubFiles[sn-1] = ⊂
- }
- for (unsigned i=0; i<n; i++)
- {
- if (!orderedSubFiles[i])
- ThrowStringException(-1, "CDistributedSuperFile: SuperFile %s: missing subfile part number %d of %d", logicalName.get(), i+1, n);
- }
- // Now try to resolve them all (file/superfile)
- for (unsigned f=0; f<n; f++)
- {
- IPropertyTree &sub = *(orderedSubFiles[f]);
- sub.getProp("@name",subname.clear());
- Owned<IDistributedFile> subfile;
- subfile.setown(transaction?transaction->lookupFile(subname.str(),timeout):parent->lookup(subname.str(), udesc, false, false, false, transaction, timeout));
- if (!subfile.get())
- subfile.setown(transaction?transaction->lookupSuperFile(subname.str(),timeout):parent->lookupSuperFile(subname.str(),udesc,transaction,timeout));
- // Some files are ok not to exist
- if (!subfile.get())
- {
- CDfsLogicalFileName cdfsl;
- cdfsl.set(subname);
- if (cdfsl.isForeign())
- {
- IWARNLOG("CDistributedSuperFile: SuperFile %s's sub-file file '%s' is foreign, but missing", logicalName.get(), subname.str());
- // Create a dummy empty superfile as a placeholder for the missing foreign file
- Owned<IPropertyTree> dummySuperRoot = createPTree();
- dummySuperRoot->setPropInt("@interleaved", 0);
- subfile.setown(queryDistributedFileDirectory().createNewSuperFile(dummySuperRoot, subname));
- if (transaction)
- {
- IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
- _transaction->ensureFile(subfile);
- }
- }
- else
- ThrowStringException(-1, "CDistributedSuperFile: SuperFile %s: corrupt subfile file '%s' cannot be found", logicalName.get(), subname.str());
- }
- subfiles.append(*subfile.getClear());
- if (link)
- linkSubFile(f);
- }
- // This is *only* due to foreign files
- if (subfiles.ordinality() != n)
- {
- IWARNLOG("CDistributedSuperFile: SuperFile %s's number of sub-files updated to %d", logicalName.get(), subfiles.ordinality());
- root->setPropInt("@numsubfiles", subfiles.ordinality());
- }
- }
- catch (IException *)
- {
- partscache.kill();
- subfiles.kill(); // one out, all out
- throw;
- }
- }
- void addItem(unsigned pos,IDistributedFile *_file)
- {
- Owned<IDistributedFile> file = _file;
- partscache.kill();
- // first renumber all above
- StringBuffer path;
- IPropertyTree *sub;
- for (unsigned i=subfiles.ordinality();i>pos;i--)
- {
- sub = root->queryPropTree(getSubPath(path.clear(),i-1).str());
- if (!sub)
- throw MakeStringException(-1,"C(2): Corrupt subfile file part %d cannot be found",i);
- sub->setPropInt("@num",i+1);
- }
- sub = createPTree();
- sub->setPropInt("@num",pos+1);
- sub->setProp("@name",file->queryLogicalName());
- if (pos==0)
- {
- Owned<IPropertyTree> superAttrs = createPTreeFromIPT(&file->queryAttributes());
- superAttrs->removeProp("Protect"); // do not automatically inherit protected status
- resetFileAttr(superAttrs.getClear());
- }
- root->addPropTree("SubFile",sub);
- subfiles.add(*file.getClear(),pos);
- root->setPropInt("@numsubfiles",subfiles.ordinality());
- }
- void removeItem(unsigned pos)
- {
- partscache.kill();
- StringBuffer path;
- IPropertyTree* sub = root->queryPropTree(getSubPath(path,pos).str());
- if (!sub)
- throw MakeStringException(-1,"CDistributedSuperFile(3): Corrupt subfile file part %d cannot be found",pos+1);
- root->removeTree(sub);
- // now renumber all above
- for (unsigned i=pos+1; i<subfiles.ordinality(); i++)
- {
- sub = root->queryPropTree(getSubPath(path.clear(),i).str());
- if (!sub)
- throw MakeStringException(-1,"CDistributedSuperFile(2): Corrupt subfile file part %d cannot be found",i+1);
- sub->setPropInt("@num",i);
- }
- subfiles.remove(pos);
- if (pos==0)
- {
- if (subfiles.ordinality())
- {
- Owned<IPropertyTree> superAttrs = createPTreeFromIPT(&subfiles.item(0).queryAttributes());
- superAttrs->removeProp("Protect"); // do not automatically inherit protected status
- resetFileAttr(superAttrs.getClear());
- }
- else
- resetFileAttr(getEmptyAttr());
- }
- root->setPropInt("@numsubfiles",subfiles.ordinality());
- }
- void loadParts(CDistributedFilePartArray &partsret, IDFPartFilter *filter)
- {
- unsigned p = 0;
- if (interleaved) { // a bit convoluted but should work
- IArrayOf<IDistributedFile> allsubfiles;
- ForEachItemIn(i,subfiles) {
- // if old format keep original interleaving
- IDistributedSuperFile* super = (interleaved==1)?NULL:subfiles.item(i).querySuperFile();
- if (super) {
- Owned<IDistributedFileIterator> iter = super->getSubFileIterator(true);
- ForEach(*iter)
- allsubfiles.append(iter->get());
- }
- else
- allsubfiles.append(*LINK(&subfiles.item(i)));
- }
- unsigned *pn = new unsigned[allsubfiles.ordinality()];
- ForEachItemIn(j,allsubfiles)
- pn[j] = allsubfiles.item(j).numParts();
- unsigned f=0;
- bool found=false;
- for (;;) {
- if (f==allsubfiles.ordinality()) {
- if (!found)
- break; // no more
- found = false;
- f = 0;
- }
- if (pn[f]) {
- found = true;
- if (!filter||filter->includePart(p)) {
- IDistributedFile &subfile = allsubfiles.item(f);
- IDistributedFilePart *part = subfile.getPart(subfile.numParts()-pn[f]);
- partsret.append(*QUERYINTERFACE(part,CDistributedFilePart)); // bit kludgy
- }
- p++;
- pn[f]--;
- }
- f++;
- }
- delete [] pn;
- }
- else { // sequential
- ForEachItemIn(i,subfiles) { // not wonderfully quick
- IDistributedFile &subfile = subfiles.item(i);
- unsigned n = subfile.numParts();
- unsigned j = 0;
- while (n--) {
- if (!filter||filter->includePart(p)) {
- IDistributedFilePart *part = subfile.getPart(j++);
- partsret.append(*QUERYINTERFACE(part,CDistributedFilePart)); // bit kludgy
- }
- p++;
- }
- }
- }
- }
- void linkSubFile(unsigned pos, bool link=true)
- {
- IDistributedFile *subfile = &subfiles.item(pos);
- IDistributedSuperFile *ssub = subfile->querySuperFile();
- if (ssub) {
- CDistributedSuperFile *cdsuper = QUERYINTERFACE(ssub,CDistributedSuperFile);
- cdsuper->linkSuperOwner(queryLogicalName(),link);
- }
- else {
- CDistributedFile *cdfile = QUERYINTERFACE(subfile,CDistributedFile);
- cdfile->linkSuperOwner(queryLogicalName(),link);
- }
- }
- void unlinkSubFile(unsigned pos)
- {
- linkSubFile(pos, false);
- }
- void checkSubFormatAttr(IDistributedFile *sub, const char* exprefix="")
- {
- // empty super files now pass
- ForEachItemIn(i,subfiles) {
- IDistributedSuperFile* super = subfiles.item(i).querySuperFile();
- if (super) {
- CDistributedSuperFile *cdsuper = QUERYINTERFACE(super,CDistributedSuperFile);
- if (cdsuper)
- cdsuper->checkSubFormatAttr(sub,exprefix);
- return;
- }
- CDistributedFile *cdfile = QUERYINTERFACE(&subfiles.item(0),CDistributedFile);
- if (cdfile)
- cdfile->checkFormatAttr(sub,exprefix); // any file will do
- }
- }
- void addPropIfCommon(IPropertyTree &target, const char *prop, const char *value)
- {
- bool ok = true;
- // add attributes that are common
- for (unsigned i=1; i<subfiles.ordinality(); i++)
- {
- IDistributedFile &file = subfiles.item(i);
- IDistributedSuperFile *sFile = file.querySuperFile();
- if (!sFile || sFile->numSubFiles(true)) // skip empty super files
- {
- const char *otherValue = file.queryAttributes().queryProp(prop);
- if (!otherValue || !streq(otherValue, value))
- {
- ok = false;
- break;
- }
- }
- }
- if (ok)
- target.setProp(prop, value);
- }
- public:
- virtual void checkFormatAttr(IDistributedFile *sub, const char* exprefix="") override
- {
- IDistributedSuperFile *superSub = sub->querySuperFile();
- if (superSub && (0 == superSub->numSubFiles(true)))
- return;
- // only check sub files not siblings, which is excessive (format checking is really only debug aid)
- checkSubFormatAttr(sub,exprefix);
- }
- unsigned findSubFile(const char *name)
- {
- StringBuffer lfn;
- normalizeLFN(name,lfn);
- ForEachItemIn(i,subfiles)
- if (stricmp(subfiles.item(i).queryLogicalName(),lfn.str())==0)
- return i;
- return NotFound;
- }
- IMPLEMENT_IINTERFACE_O;
- void commonInit(CDistributedFileDirectory *_parent, IPropertyTree *_root)
- {
- parent = _parent;
- root.set(_root);
- const char *val = root->queryProp("@interleaved");
- if (val&&isdigit(*val))
- interleaved = atoi(val);
- else
- interleaved = strToBool(val)?1:0;
- }
- void init(CDistributedFileDirectory *_parent, IPropertyTree *_root, const CDfsLogicalFileName &_name, IUserDescriptor* user, IDistributedFileTransaction *transaction, unsigned timeout=INFINITE)
- {
- assertex(_name.isSet());
- setUserDescriptor(udesc,user);
- logicalName.set(_name);
- commonInit(_parent, _root);
- loadSubFiles(transaction,timeout);
- }
- CDistributedSuperFile(CDistributedFileDirectory *_parent, IPropertyTree *_root,const CDfsLogicalFileName &_name,IUserDescriptor* user)
- {
- init(_parent,_root,_name,user,NULL);
- }
- CDistributedSuperFile(CDistributedFileDirectory *_parent, IRemoteConnection *_conn,const CDfsLogicalFileName &_name,IUserDescriptor* user, IDistributedFileTransaction *transaction,unsigned timeout)
- {
- conn.setown(_conn);
- init(_parent,conn->queryRoot(),_name,user,transaction,timeout);
- }
- CDistributedSuperFile(CDistributedFileDirectory *_parent, CDfsLogicalFileName &_name, IUserDescriptor* user, IDistributedFileTransaction *transaction)
- {
- // temp super file
- assertex(_name.isMulti());
- if (!_name.isExpanded())
- _name.expand(user);//expand wildcards
- Owned<IPropertyTree> tree = _name.createSuperTree();
- init(_parent,tree,_name,user,transaction);
- updateFileAttrs();
- }
- CDistributedSuperFile(CDistributedFileDirectory *_parent, IPropertyTree *_root, const char *optionalName)
- {
- commonInit(_parent, _root);
- if (optionalName)
- logicalName.set(optionalName);
- }
- ~CDistributedSuperFile()
- {
- partscache.kill();
- subfiles.kill();
- }
- virtual StringBuffer &getClusterName(unsigned clusternum,StringBuffer &name) override
- {
- // returns the cluster name if all the same
- CriticalBlock block (sect);
- if (subfiles.ordinality()==1)
- return subfiles.item(0).getClusterName(clusternum,name);
- size32_t rl = name.length();
- StringBuffer test;
- ForEachItemIn(i,subfiles) {
- if (i) {
- subfiles.item(i).getClusterName(clusternum,test.clear());
- if (strcmp(name.str(),test.str())!=0) {
- name.setLength(rl);
- break;
- }
- }
- else
- subfiles.item(i).getClusterName(clusternum,name);
- }
- return name;
- }
- virtual IFileDescriptor *getFileDescriptor(const char *clustername) override
- {
- CriticalBlock block (sect);
- if (subfiles.ordinality()==1)
- return subfiles.item(0).getFileDescriptor(clustername);
- // superfiles assume consistant replication & compression
- UnsignedArray subcounts;
- bool mixedwidth = false;
- unsigned width = 0;
- bool first = true;
- Owned<IPropertyTree> at = getEmptyAttr();
- Owned<IDistributedFileIterator> fiter = getSubFileIterator(true);
- ForEach(*fiter)
- {
- IDistributedFile &file = fiter->query();
- if (first)
- {
- first = false;
- Owned<IAttributeIterator> ait = file.queryAttributes().getAttributes();
- ForEach(*ait)
- {
- const char *name = ait->queryName();
- if ((stricmp(name,"@size")!=0)&&(stricmp(name,"@recordCount")!=0))
- {
- const char *v = ait->queryValue();
- if (!v)
- continue;
- addPropIfCommon(*at, name, v);
- }
- }
- MemoryBuffer mb;
- if (getRecordLayout(mb, "_record_layout"))
- at->setPropBin("_record_layout", mb.length(), mb.bufferBase());
- if (getRecordLayout(mb, "_rtlType"))
- at->setPropBin("_rtlType", mb.length(), mb.bufferBase());
- const char *ecl = file.queryAttributes().queryProp("ECL");
- if (!isEmptyString(ecl))
- addPropIfCommon(*at, "ECL", ecl);
- }
- unsigned np = file.numParts();
- if (0 == width)
- width = np;
- else if (np!=width)
- mixedwidth = true;
- subcounts.append(np);
- }
- // need common attributes
- Owned<ISuperFileDescriptor> fdesc=createSuperFileDescriptor(at.getClear());
- if (interleaved&&(interleaved!=2))
- IWARNLOG("getFileDescriptor: Unsupported interleave value (1)");
- fdesc->setSubMapping(subcounts,interleaved!=0);
- fdesc->setTraceName(logicalName.get());
- Owned<IDistributedFilePartIterator> iter = getIterator(NULL);
- unsigned n = 0;
- SocketEndpointArray reps;
- ForEach(*iter) {
- IDistributedFilePart &part = iter->query();
- CDistributedFilePart *cpart = (clustername&&*clustername)?QUERYINTERFACE(&part,CDistributedFilePart):NULL;
- unsigned copy = 0;
- if (cpart) {
- IDistributedFile &f = cpart->queryParent();
- unsigned cn = f.findCluster(clustername);
- if (cn!=NotFound) {
- for (unsigned i = 0;i<cpart->numCopies();i++)
- if (cpart->copyClusterNum(i,NULL)==cn) {
- copy = i;
- break;
- }
- }
- }
- if (mixedwidth) {
- SocketEndpoint rep;
- if (copy+1<part.numCopies())
- rep = part.queryNode(copy+1)->endpoint();
- reps.append(rep);
- }
- RemoteFilename rfn;
- fdesc->setPart(n,part.getFilename(rfn,copy),&part.queryAttributes());
- n++;
- }
- ClusterPartDiskMapSpec mspec;
- if (subfiles.ordinality()) {
- mspec = subfiles.item(0).queryPartDiskMapping(0);
- }
- mspec.interleave = numSubFiles(true);
- fdesc->endCluster(mspec);
- if (mixedwidth) { // bleah - have to add replicate node numbers
- Owned<IGroup> group = fdesc->getGroup();
- unsigned gw = group->ordinality();
- for (unsigned pn=0;pn<reps.ordinality();pn++) {
- const SocketEndpoint &ep=reps.item(pn);
- if (!ep.isNull()) {
- unsigned gn = pn;
- if (gn<gw) {
- do {
- gn++;
- if (gn==gw)
- gn = 0;
- if (ep.equals(group->queryNode((rank_t)gn).endpoint())) {
- IPartDescriptor *part = fdesc->queryPart(pn);
- if (part)
- part->queryProperties().setPropInt("@rn",(unsigned)gn);
- break;
- }
- } while (gn!=pn);
- }
- }
- }
- }
- return fdesc.getClear();
- }
- virtual unsigned numParts() override
- {
- CriticalBlock block(sect);
- unsigned ret=0;
- ForEachItemIn(i,subfiles)
- ret += subfiles.item(i).numParts();
- return ret;
- }
- virtual IDistributedFilePart &queryPart(unsigned idx) override
- {
- CriticalBlock block(sect);
- if (subfiles.ordinality()==1)
- return subfiles.item(0).queryPart(idx);
- if (partscache.ordinality()==0)
- loadParts(partscache,NULL);
- if (idx>=partscache.ordinality())
- return *(IDistributedFilePart *)NULL;
- return partscache.item(idx);
- }
- virtual IDistributedFilePart* getPart(unsigned idx) override
- {
- IDistributedFilePart* ret = &queryPart(idx);
- return LINK(ret);
- }
- virtual IDistributedFilePartIterator *getIterator(IDFPartFilter *filter=NULL) override
- {
- CriticalBlock block(sect);
- if (subfiles.ordinality()==1)
- return subfiles.item(0).getIterator(filter);
- CDistributedFilePartIterator *ret = new CDistributedFilePartIterator();
- loadParts(ret->queryParts(),filter);
- return ret;
- }
- virtual void rename(const char *_logicalname,IUserDescriptor *user) override
- {
- StringBuffer prevname;
- Owned<IFileRelationshipIterator> reliter;
- // set prevname
- if (!isAnon()) {
- getLogicalName(prevname);
- try {
- IFileRelationshipIterator *iter = parent->lookupAllFileRelationships(prevname.str());
- reliter.setown(iter);
- }
- catch (IException *e) {
- EXCLOG(e,"CDistributedFileDirectory::rename");
- e->Release();
- }
- detach();
- }
- attach(_logicalname,user);
- if (reliter.get()) {
- // add back any relationships with new name
- parent->renameFileRelationships(prevname.str(),_logicalname,reliter,user);
- }
- }
- virtual const char *queryDefaultDir() override
- {
- // returns the directory if all the same
- const char *ret = NULL;
- CriticalBlock block (sect);
- ForEachItemIn(i,subfiles) {
- if (subfiles.item(i).numParts())
- {
- const char *s = subfiles.item(i).queryDefaultDir();
- if (!s)
- return NULL;
- if (!ret)
- ret = s;
- else if (strcmp(ret,s)!=0)
- return NULL;
- }
- }
- return ret;
- }
- virtual const char *queryPartMask() override
- {
- // returns the part mask if all the same
- const char *ret = NULL;
- CriticalBlock block (sect);
- ForEachItemIn(i,subfiles) {
- const char *s = subfiles.item(i).queryPartMask();
- if (!s)
- return NULL;
- if (!ret)
- ret = s;
- else if (stricmp(ret,s)!=0)
- return NULL;
- }
- return ret;
- }
- virtual void attach(const char *_logicalname,IUserDescriptor *user) override
- {
- assertex(!conn.get()); // already attached
- CriticalBlock block (sect);
- StringBuffer tail;
- StringBuffer lfn;
- logicalName.set(_logicalname);
- checkLogicalName(logicalName,user,true,true,false,"attach");
- parent->addEntry(logicalName,root.getClear(),true,false);
- conn.clear();
- CFileLock fcl;
- verifyex(fcl.init(logicalName, DXB_SuperFile, RTM_LOCK_READ, defaultTimeout, "CDistributedSuperFile::attach"));
- conn.setown(fcl.detach());
- assertex(conn.get()); // must have been attached
- root.setown(conn->getRoot());
- loadSubFiles(NULL, 0, true);
- }
- virtual void detach(unsigned timeoutMs=INFINITE, ICodeContext *ctx=NULL) override
- {
- assertex(conn.get()); // must be attached
- CriticalBlock block(sect);
- checkModify("CDistributedSuperFile::detach");
- StringBuffer reason;
- if (checkOwned(reason))
- throw MakeStringException(-1, "detach: %s", reason.str());
- subfiles.kill();
- // Remove from SDS
- /* JCSMORE - this looks very kludgy...
- * We have readlock, this code is doing
- * 1) change to write lock (not using lockProperties or DistributedFilePropertyLock to do so) [using CFileChangeWriteLock]
- * CFileChangeWriteLock doesn't preserve lock mode quite right.. (see 'newMode')
- * 2) manually deleting SuperOwner from subfiles (in clearSuperOwners)
- * 3) Using the connection to delete the SuperFile from Dali (clones to 'root' in process)
- * 4) ~CFileChangeWriteLock() [writeLock.clear()], restores read lock from write to read
- * 5) updateFS (housekeeping of empty scopes, relationships) - ok
- */
- CFileChangeWriteLock writeLock(conn, timeoutMs);
- clearSuperOwners(timeoutMs, ctx);
- writeLock.clear();
- root.setown(closeConnection(true));
- updateFS(logicalName, parent->queryDefaultTimeout());
- logicalName.clear();
- }
- virtual bool existsPhysicalPartFiles(unsigned short port) override
- {
- CriticalBlock block (sect);
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- if (!f.existsPhysicalPartFiles(port))
- return false;
- }
- return true;
- }
- virtual bool renamePhysicalPartFiles(const char *newlfn,const char *cluster,IMultiException *mexcept,const char *newbasedir) override
- {
- throw MakeStringException(-1,"renamePhysicalPartFiles not supported for SuperFiles");
- return false;
- }
- void serialize(MemoryBuffer &mb)
- {
- UNIMPLEMENTED; // not yet needed
- }
- virtual unsigned numCopies(unsigned partno) override
- {
- unsigned ret = (unsigned)-1;
- CriticalBlock block (sect);
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- unsigned fnc = f.numCopies(partno);
- if (fnc<ret)
- ret = fnc;
- }
- return (ret==(unsigned)-1)?1:ret;
- }
- virtual __int64 getFileSize(bool allowphysical,bool forcephysical) override
- {
- __int64 ret = (__int64)(forcephysical?-1:queryAttributes().getPropInt64("@size",-1));
- if (ret==-1)
- {
- ret = 0;
- ForEachItemIn(i,subfiles)
- {
- __int64 ps = subfiles.item(i).getFileSize(allowphysical,forcephysical);
- if (ps == -1)
- return -1; // i.e. if cannot determine size of any part, total is unknown
- ret += ps;
- }
- }
- return ret;
- }
- virtual __int64 getDiskSize(bool allowphysical,bool forcephysical) override
- {
- if (!isCompressed(NULL))
- return getFileSize(allowphysical, forcephysical);
- __int64 ret = (__int64)(forcephysical?-1:queryAttributes().getPropInt64("@compressedSize",-1));
- if (ret==-1)
- {
- ret = 0;
- ForEachItemIn(i,subfiles)
- {
- __int64 ps = subfiles.item(i).getDiskSize(allowphysical,forcephysical);
- if (ps == -1)
- return -1; // i.e. if cannot determine size of any part, total is unknown
- ret += ps;
- }
- }
- return ret;
- }
- __int64 getRecordCount()
- {
- __int64 ret = queryAttributes().getPropInt64("@recordCount",-1);
- if (ret==-1) {
- ret = 0;
- ForEachItemIn(i,subfiles) {
- __int64 rc = subfiles.item(i).queryAttributes().getPropInt64("@recordCount",-1);
- if (rc == -1) {
- ret = rc;
- break;
- }
- ret += rc;
- }
- }
- return ret;
- }
- virtual bool getFileCheckSum(unsigned &checkSum) override
- {
- if (queryAttributes().hasProp("@checkSum"))
- checkSum = (unsigned)queryAttributes().getPropInt64("@checkSum");
- else
- {
- checkSum = ~0;
- ForEachItemIn(i,subfiles) {
- unsigned cs;
- if (!subfiles.item(i).getFileCheckSum(cs))
- return false;
- checkSum ^= cs;
- }
- }
- return true;
- }
- virtual IDistributedSuperFile *querySuperFile() override
- {
- return this;
- }
- virtual IDistributedFile &querySubFile(unsigned idx,bool sub) override
- {
- CriticalBlock block (sect);
- if (sub) {
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- IDistributedSuperFile *super = f.querySuperFile();
- if (super) {
- unsigned ns = super->numSubFiles(true);
- if (ns>idx)
- return super->querySubFile(idx,true);
- idx -= ns;
- }
- else if (idx--==0)
- return f;
- }
- // fall through to error
- }
- return subfiles.item(idx);
- }
- virtual IDistributedFile *querySubFileNamed(const char *name, bool sub) override
- {
- CriticalBlock block (sect);
- unsigned idx=findSubFileOrd(name);
- if ((idx!=NotFound)&&(idx<subfiles.ordinality()))
- return &subfiles.item(idx);
- idx=findSubFile(name);
- if (idx!=NotFound)
- return &subfiles.item(idx);
- if (sub) {
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- IDistributedSuperFile *super = f.querySuperFile();
- if (super) {
- IDistributedFile *ret = super->querySubFileNamed(name);
- if (ret)
- return ret;
- }
- }
- }
- return NULL;
- }
- virtual IDistributedFile *getSubFile(unsigned idx,bool sub) override
- {
- CriticalBlock block (sect);
- return LINK(&querySubFile(idx,sub));
- }
- virtual unsigned numSubFiles(bool sub) override
- {
- CriticalBlock block (sect);
- unsigned ret = 0;
- if (sub) {
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- IDistributedSuperFile *super = f.querySuperFile();
- if (super)
- ret += super->numSubFiles(sub);
- else
- ret++;
- }
- }
- else
- ret = subfiles.ordinality();
- return ret;
- }
- virtual bool getFormatCrc(unsigned &crc) override
- {
- if (queryAttributes().hasProp("@formatCrc")) {
- crc = (unsigned)queryAttributes().getPropInt("@formatCrc");
- return true;
- }
- bool found = false;
- ForEachItemIn(i,subfiles) {
- unsigned c;
- if (subfiles.item(i).getFormatCrc(c)) {
- if (found&&(c!=crc))
- return false;
- found = true;
- crc = c;
- }
- }
- return found;
- }
- virtual bool getRecordLayout(MemoryBuffer &layout, const char *attrname) override
- {
- layout.clear();
- if (queryAttributes().getPropBin(attrname, layout))
- return true;
- bool found = false;
- ForEachItemIn(i,subfiles) {
- MemoryBuffer b;
- if (subfiles.item(i).getRecordLayout(found?b:layout, attrname)) {
- if (found) {
- if ((b.length()!=layout.length())||(memcmp(b.bufferBase(),layout.bufferBase(),b.length())!=0))
- return false;
- }
- else
- found = true;
- }
- }
- return found;
- }
- virtual bool getRecordSize(size32_t &rsz) override
- {
- if (queryAttributes().hasProp("@recordSize")) {
- rsz = (size32_t)queryAttributes().getPropInt("@recordSize");
- return true;
- }
- bool found = false;
- ForEachItemIn(i,subfiles) {
- size32_t sz;
- if (subfiles.item(i).getRecordSize(sz)) {
- if (found&&(sz!=rsz))
- return false;
- found = true;
- rsz = sz;
- }
- }
- return found;
- }
- virtual bool isInterleaved() override
- {
- return interleaved!=0;
- }
- virtual IDistributedFile *querySubPart(unsigned partidx,unsigned &subfileidx) override
- {
- CriticalBlock block (sect);
- subfileidx = 0;
- Owned<IDistributedFilePart> part = getPart(partidx);
- if (!part)
- return NULL;
- CDistributedFilePart *cpart = QUERYINTERFACE(part.get(),CDistributedFilePart);
- if (!cpart)
- return NULL;
- IDistributedFile &ret = cpart->queryParent();
- unsigned n = ret.numParts();
- for (unsigned i=0;i<n;i++) {
- Owned<IDistributedFilePart> spart = ret.getPart(i);
- if (spart.get()==part.get()) {
- subfileidx = i;
- return &ret;
- }
- }
- return NULL;
- }
- virtual unsigned getPositionPart(offset_t pos, offset_t &base) override
- { // not very quick!
- CriticalBlock block (sect);
- unsigned n = numParts();
- base = 0;
- for (unsigned i=0;i<n;i++) {
- Owned<IDistributedFilePart> part = getPart(i);
- offset_t ps = part->getFileSize(true,false);
- if (ps==(offset_t)-1)
- break;
- if (ps>pos)
- return i;
- pos -= ps;
- base += ps;
- }
- return NotFound;
- }
- virtual IDistributedFileIterator *getSubFileIterator(bool supersub=false) override
- {
- CriticalBlock block (sect);
- return new cSubFileIterator(subfiles,supersub);
- }
- void updateFileAttrs()
- {
- if (subfiles.ordinality()==0) {
- StringBuffer desc;
- root->getProp("Attr/@description",desc);
- root->removeProp("Attr"); // remove all other attributes if superfile empty
- IPropertyTree *t=resetFileAttr(getEmptyAttr());
- if (desc.length())
- t->setProp("@description",desc.str());
- return;
- }
- root->removeProp("Attr/@size");
- root->removeProp("Attr/@compressedSize");
- root->removeProp("Attr/@checkSum");
- root->removeProp("Attr/@recordCount"); // recordCount not currently supported by superfiles
- root->removeProp("Attr/@formatCrc"); // formatCrc set if all consistant
- root->removeProp("Attr/@recordSize"); // record size set if all consistant
- root->removeProp("Attr/_record_layout"); // legacy info - set if all consistent
- root->removeProp("Attr/_rtlType"); // new info - set if all consistent
- __int64 fs = getFileSize(false,false);
- if (fs!=-1)
- root->setPropInt64("Attr/@size",fs);
- if (isCompressed(NULL))
- {
- fs = getDiskSize(false,false);
- if (fs!=-1)
- root->setPropInt64("Attr/@compressedSize",fs);
- }
- unsigned checkSum;
- if (getFileCheckSum(checkSum))
- root->setPropInt64("Attr/@checkSum", checkSum);
- __int64 rc = getRecordCount();
- if (rc!=-1)
- root->setPropInt64("Attr/@recordCount",rc);
- unsigned fcrc;
- if (getFormatCrc(fcrc))
- root->setPropInt("Attr/@formatCrc", fcrc);
- size32_t rsz;
- if (getRecordSize(rsz))
- root->setPropInt("Attr/@recordSize", rsz);
- MemoryBuffer mb;
- if (getRecordLayout(mb, "_record_layout"))
- root->setPropBin("Attr/_record_layout", mb.length(), mb.bufferBase());
- if (getRecordLayout(mb, "_rtlType"))
- root->setPropBin("Attr/_rtlType", mb.length(), mb.bufferBase());
- const char *kind = nullptr;
- Owned<IDistributedFileIterator> subIter = getSubFileIterator(true);
- ForEach(*subIter)
- {
- IDistributedFile &file = subIter->query();
- const char *curKind = file.queryAttributes().queryProp("@kind");
- if (!kind)
- kind = curKind;
- else if (!strsame(kind, curKind))
- {
- kind = nullptr;
- break;
- }
- }
- if (kind)
- root->setProp("Attr/@kind", kind);
- }
- void updateParentFileAttrs(IDistributedFileTransaction *transaction)
- {
- Owned<IPropertyTreeIterator> iter = root->getElements("SuperOwner");
- StringBuffer pname;
- ForEach(*iter) {
- iter->query().getProp("@name",pname.clear());
- Owned<IDistributedSuperFile> psfile = transaction?transaction->lookupSuperFile(pname.str()):
- queryDistributedFileDirectory().lookupSuperFile(pname.str(),udesc,NULL);
- CDistributedSuperFile *file = QUERYINTERFACE(psfile.get(),CDistributedSuperFile);
- if (file) {
- {
- DistributedFilePropertyLock lock(file);
- file->setModified();
- file->updateFileAttrs();
- }
- file->updateParentFileAttrs(transaction);
- }
- }
- }
- void validateAddSubFile(IDistributedFile *sub)
- {
- if (strcmp(sub->queryLogicalName(),queryLogicalName())==0)
- throw MakeStringException(-1,"addSubFile: Cannot add file %s to itself", queryLogicalName());
- if (subfiles.ordinality())
- checkFormatAttr(sub,"addSubFile");
- if (NotFound!=findSubFile(sub->queryLogicalName()))
- throw MakeStringException(-1,"addSubFile: File %s is already a subfile of %s", sub->queryLogicalName(),queryLogicalName());
- }
- virtual void validate() override
- {
- unsigned numSubfiles = root->getPropInt("@numsubfiles",0);
- if (numSubfiles)
- {
- Owned<IPropertyTreeIterator> treeIter = root->getElements("SubFile");
- unsigned subFileCount = 0;
- ForEach(*treeIter)
- {
- IPropertyTree & st = treeIter->query();
- StringBuffer subfilename;
- st.getProp("@name", subfilename);
- if (!parent->exists(subfilename.str(), NULL))
- throw MakeStringException(-1, "Logical subfile '%s' doesn't exists!", subfilename.str());
- if (!parent->isSuperFile(subfilename.str()))
- if (!parent->existsPhysical(subfilename.str(), NULL))
- {
- const char * logicalName = queryLogicalName();
- throw MakeStringException(-1, "Some physical parts do not exists, for logical file : %s",(isEmptyString(logicalName) ? "[unattached]" : logicalName));
- }
- subFileCount++;
- }
- if (numSubfiles != subFileCount)
- throw MakeStringException(-1, "The value of @numsubfiles (%d) is not equal to the number of SubFile items (%d)!",numSubfiles, subFileCount);
- }
- }
- private:
- void doAddSubFile(IDistributedFile *_sub,bool before,const char *other,IDistributedFileTransactionExt *transaction) // takes ownership of sub
- {
- Owned<IDistributedFile> sub = _sub;
- validateAddSubFile(sub); // shouldn't really be necessary, was validated in transaction before here
- unsigned pos;
- if (other&&*other) {
- pos = findSubFileOrd(other);
- if (pos==NotFound)
- pos = findSubFile(other);
- if (pos==NotFound)
- pos = before?0:subfiles.ordinality();
- else if (!before&&(pos<subfiles.ordinality()))
- pos++;
- }
- else
- pos = before?0:subfiles.ordinality();
- if (pos > subfiles.ordinality())
- throw MakeStringException(-1,"addSubFile: Insert position %d out of range for file %s in superfile %s", pos+1, sub->queryLogicalName(), queryLogicalName());
- addItem(pos,sub.getClear()); // remove if failure TBD?
- setModified();
- updateFileAttrs();
- linkSubFile(pos);
- }
- bool doRemoveSubFiles(IDistributedFileTransactionExt *transaction)
- {
- // have to be quite careful here
- unsigned pos = subfiles.ordinality();
- if (pos)
- {
- DistributedFilePropertyLock lock(this);
- if (lock.needsReload())
- loadSubFiles(transaction,1000*60*10);
- pos = subfiles.ordinality();
- if (pos)
- {
- do
- {
- pos--;
- unlinkSubFile(pos);
- removeItem(pos);
- } while (pos);
- setModified();
- updateFileAttrs();
- lock.unlock();
- updateParentFileAttrs(transaction);
- }
- }
- return true;
- }
- bool doRemoveSubFile(const char *subfile,
- IDistributedFileTransactionExt *transaction)
- {
- // have to be quite careful here
- unsigned pos=findSubFileOrd(subfile);
- if ((pos==NotFound)||(pos>=subfiles.ordinality()))
- pos = findSubFile(subfile);
- if (pos==NotFound)
- return false;
- {
- DistributedFilePropertyLock lock(this);
- // don't reload subfiles here
- pos=findSubFileOrd(subfile);
- if ((pos==NotFound)||(pos>=subfiles.ordinality()))
- pos = findSubFile(subfile);
- if (pos==NotFound)
- return false;
- unlinkSubFile(pos);
- removeItem(pos);
- setModified();
- updateFileAttrs();
- }
- updateParentFileAttrs(transaction);
- return true;
- }
- bool doSwapSuperFile(IDistributedSuperFile *_file,
- IDistributedFileTransactionExt *transaction)
- {
- assertex(transaction);
- CDistributedSuperFile *file = QUERYINTERFACE(_file,CDistributedSuperFile);
- if (!file)
- return false;
- // Cache names (so we can delete without problems)
- StringArray subnames1;
- StringArray subnames2;
- for (unsigned i=0; i<this->numSubFiles(false); i++)
- subnames1.append(querySubFile(i, false).queryLogicalName());
- for (unsigned i=0; i<file->numSubFiles(false); i++)
- subnames2.append(file->querySubFile(i, false).queryLogicalName());
- // Delete all files
- ForEachItemIn(d1,subnames1) {
- Owned<IDistributedFile> sub = transaction->lookupFile(subnames1.item(d1));
- if (!doRemoveSubFile(sub->queryLogicalName(), transaction))
- return false;
- }
- ForEachItemIn(d2,subnames2) {
- Owned<IDistributedFile> sub = transaction->lookupFile(subnames2.item(d2));
- if (!file->doRemoveSubFile(sub->queryLogicalName(), transaction))
- return false;
- }
- // Add files swapped
- ForEachItemIn(a1,subnames1) {
- Owned<IDistributedFile> sub = transaction->lookupFile(subnames1.item(a1));
- file->doAddSubFile(LINK(sub), false, NULL, transaction);
- }
- ForEachItemIn(a2,subnames2) {
- Owned<IDistributedFile> sub = transaction->lookupFile(subnames2.item(a2));
- doAddSubFile(LINK(sub), false, NULL, transaction);
- }
- return true;
- }
- public:
- virtual void addSubFile(const char * subfile,
- bool before=false, // if true before other
- const char *other=NULL, // in NULL add at end (before=false) or start(before=true)
- bool addcontents=false,
- IDistributedFileTransaction *transaction=NULL
- ) override
- {
- CriticalBlock block (sect);
- if (!subfile||!*subfile)
- return;
- checkModify("addSubFile");
- partscache.kill();
- // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
- Linked<IDistributedFileTransactionExt> localtrans;
- if (transaction)
- {
- IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
- localtrans.set(_transaction);
- }
- else
- localtrans.setown(new CDistributedFileTransaction(udesc, this));
- localtrans->ensureFile(this);
- if (addcontents)
- {
- localtrans->descend();
- StringArray subs;
- Owned<IDistributedSuperFile> sfile = localtrans->lookupSuperFile(subfile);
- if (sfile)
- {
- Owned<IDistributedFileIterator> iter = sfile->getSubFileIterator();
- ForEach(*iter)
- subs.append(iter->query().queryLogicalName());
- }
- sfile.clear();
- ForEachItemIn(i,subs)
- addSubFile(subs.item(i),before,other,false,localtrans);
- localtrans->ascend();
- }
- else
- {
- cAddSubFileAction *action = new cAddSubFileAction(queryLogicalName(),subfile,before,other);
- localtrans->addAction(action); // takes ownership
- }
- localtrans->autoCommit();
- }
- virtual bool removeSubFile(const char *subfile, // if NULL removes all
- bool remsub, // if true removes subfiles from DFS
- bool remcontents, // if true, recurse super-files
- IDistributedFileTransaction *transaction) override
- {
- CriticalBlock block (sect);
- if (subfile&&!*subfile)
- return false;
- checkModify("removeSubFile");
- partscache.kill();
- // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
- Linked<IDistributedFileTransactionExt> localtrans;
- if (transaction)
- {
- IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
- localtrans.set(_transaction);
- }
- else
- localtrans.setown(new CDistributedFileTransaction(udesc, this));
- // Make sure this file is in cache (reuse below)
- localtrans->ensureFile(this);
- // If recurring, traverse super-file subs (if super)
- if (remcontents)
- {
- localtrans->descend();
- CDfsLogicalFileName logicalname;
- logicalname.set(subfile);
- IDistributedFile *sub = querySubFileNamed(logicalname.get(),false);
- if (!sub)
- return false;
- IDistributedSuperFile *sfile = sub->querySuperFile();
- if (sfile)
- {
- Owned<IDistributedFileIterator> iter = sfile->getSubFileIterator(true);
- bool ret = true;
- StringArray toremove;
- ForEach(*iter)
- toremove.append(iter->query().queryLogicalName());
- iter.clear();
- ForEachItemIn(i,toremove)
- {
- if (!sfile->removeSubFile(toremove.item(i),remsub,false,localtrans))
- ret = false;
- }
- if (!ret||!remsub)
- return ret;
- }
- localtrans->ascend();
- }
- cRemoveSubFileAction *action = new cRemoveSubFileAction(queryLogicalName(),subfile,remsub);
- localtrans->addAction(action); // takes ownership
- localtrans->autoCommit();
- // MORE - auto-commit will throw an exception, change this to void
- return true;
- }
- virtual bool removeOwnedSubFiles(bool remsub, // if true removes subfiles from DFS
- IDistributedFileTransaction *transaction) override
- {
- CriticalBlock block (sect);
- checkModify("removeOwnedSubFiles");
- partscache.kill();
- // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
- Linked<IDistributedFileTransactionExt> localtrans;
- if (transaction)
- {
- IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
- localtrans.set(_transaction);
- }
- else
- localtrans.setown(new CDistributedFileTransaction(udesc, this));
- // Make sure this file is in cache (reuse below)
- localtrans->addFile(this);
- cRemoveOwnedSubFilesAction *action = new cRemoveOwnedSubFilesAction(localtrans, queryLogicalName(), remsub);
- localtrans->addAction(action); // takes ownership
- localtrans->autoCommit();
- // MORE - auto-commit will throw an exception, change this to void
- return true;
- }
- virtual bool swapSuperFile( IDistributedSuperFile *_file,
- IDistributedFileTransaction *transaction) override
- {
- CriticalBlock block (sect);
- if (!_file)
- return false;
- checkModify("swapSuperFile");
- partscache.kill();
- // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
- Linked<IDistributedFileTransactionExt> localtrans;
- if (transaction)
- {
- IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
- localtrans.set(_transaction);
- }
- else
- localtrans.setown(new CDistributedFileTransaction(udesc, this));
- // Make sure this file is in cache
- localtrans->ensureFile(this);
- cSwapFileAction *action = new cSwapFileAction(queryLogicalName(),_file->queryLogicalName());
- localtrans->addAction(action); // takes ownership
- localtrans->autoCommit();
- return true;
- }
- virtual void savePartsAttr(bool force) override
- {
- }
- void fillClustersCache()
- {
- if (clusterscache.ordinality()==0) {
- StringBuffer name;
- ForEachItemIn(i,subfiles) {
- StringArray clusters;
- IDistributedFile &f=subfiles.item(i);
- unsigned nc = f.numClusters();
- for(unsigned j=0;j<nc;j++) {
- f.getClusterName(j,name.clear());
- if (clusterscache.find(name.str())==NotFound) {
- IClusterInfo &cluster = *createClusterInfo(name.str(),f.queryClusterGroup(j),f.queryPartDiskMapping(j),&queryNamedGroupStore());
- clusterscache.append(cluster);
- }
- }
- }
- }
- }
- virtual unsigned getClusterNames(StringArray &clusters) override
- {
- CriticalBlock block (sect);
- fillClustersCache();
- return clusterscache.getNames(clusters);
- }
- virtual unsigned numClusters() override
- {
- CriticalBlock block (sect);
- fillClustersCache();
- return clusterscache.ordinality();
- }
- virtual unsigned findCluster(const char *clustername) override
- {
- CriticalBlock block (sect);
- fillClustersCache();
- return clusterscache.find(clustername);
- }
- virtual ClusterPartDiskMapSpec &queryPartDiskMapping(unsigned clusternum) override
- {
- CriticalBlock block (sect);
- fillClustersCache();
- return clusterscache.queryPartDiskMapping(clusternum);
- }
- virtual void updatePartDiskMapping(const char *clustername,const ClusterPartDiskMapSpec &spec) override
- {
- if (!clustername||!*clustername)
- return;
- CriticalBlock block (sect);
- fillClustersCache();
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- f.updatePartDiskMapping(clustername,spec);
- }
- }
- virtual IGroup *queryClusterGroup(unsigned clusternum) override
- {
- CriticalBlock block (sect);
- fillClustersCache();
- return clusterscache.queryGroup(clusternum);
- }
- virtual StringBuffer &getClusterGroupName(unsigned clusternum, StringBuffer &name) override
- {
- CriticalBlock block (sect);
- fillClustersCache();
- return clusterscache.item(clusternum).getGroupName(name, &queryNamedGroupStore());
- }
- virtual void addCluster(const char *clustername,const ClusterPartDiskMapSpec &mspec) override
- {
- if (!clustername||!*clustername)
- return;
- CriticalBlock block (sect);
- clusterscache.clear();
- subfiles.item(0).addCluster(clustername,mspec);
- }
- virtual bool removeCluster(const char *clustername) override
- {
- bool clusterRemoved=false;
- CriticalBlock block (sect);
- clusterscache.clear();
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- clusterRemoved |= f.removeCluster(clustername);
- }
- return clusterRemoved;
- }
- virtual void setPreferredClusters(const char *clusters) override
- {
- CriticalBlock block (sect);
- clusterscache.clear();
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- f.setPreferredClusters(clusters);
- }
- }
- virtual bool checkClusterCompatible(IFileDescriptor &fdesc, StringBuffer &err) override
- {
- CriticalBlock block (sect);
- if (subfiles.ordinality()!=1) {
- err.append("only singleton superfiles allowed");
- return false;
- }
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- if (!f.checkClusterCompatible(fdesc,err))
- return false;
- }
- return true;
- }
- virtual void setSingleClusterOnly() override
- {
- CriticalBlock block (sect);
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- f.setSingleClusterOnly();
- }
- }
- virtual void enqueueReplicate() override
- {
- CriticalBlock block (sect);
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- f.enqueueReplicate();
- }
- }
- virtual bool getAccessedTime(CDateTime &dt) override
- {
- bool set=false;
- CriticalBlock block (sect);
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- if (!set)
- set = f.getAccessedTime(dt);
- else {
- CDateTime cmp;
- if (f.getAccessedTime(cmp)) {
- if (cmp.compare(dt)>0)
- dt.set(cmp);
- }
- }
- }
- return set;
- }
- virtual void setAccessedTime(const CDateTime &dt) override
- {
- {
- CriticalBlock block (sect);
- ForEachItemIn(i,subfiles) {
- IDistributedFile &f=subfiles.item(i);
- f.setAccessedTime(dt);
- }
- }
- }
- };
- // --------------------------------------------------------
- void CDistributedFileTransaction::validateAddSubFile(IDistributedSuperFile *super, IDistributedFile *sub, const char *subName)
- {
- CTransactionFile *trackedSuper = lookupTrackedFile(super);
- if (!trackedSuper)
- return;
- const char *superName = trackedSuper->queryName();
- if (strcmp(subName, superName)==0)
- throw MakeStringException(-1,"addSubFile: Cannot add file %s to itself", superName);
- if (trackedSuper->numSubFiles())
- {
- CDistributedSuperFile *sf = dynamic_cast<CDistributedSuperFile *>(super);
- sf->checkFormatAttr(sub, "addSubFile");
- if (trackedSuper->find(subName, false))
- throw MakeStringException(-1,"addSubFile: File %s is already a subfile of %s", subName, superName);
- }
- }
- // --------------------------------------------------------
- CDistributedFilePart::CDistributedFilePart(CDistributedFile &_parent,unsigned _part,IPartDescriptor *pd)
- : parent(_parent)
- {
- partIndex = _part;
- dirty = false;
- if (pd) {
- if (pd->isMulti())
- IERRLOG("Multi filenames not supported in Dali DFS Part %d of %s",_part+1,_parent.queryLogicalName());
- overridename.set(pd->queryOverrideName());
- setAttr(*pd->getProperties());
- }
- else
- IERRLOG("CDistributedFilePart::CDistributedFilePart no IPartDescriptor for part");
- }
- void CDistributedFilePart::Link(void) const
- {
- parent.Link();
- CInterface::Link();
- }
- bool CDistributedFilePart::Release(void) const
- {
- parent.Release();
- return CInterface::Release();
- }
- offset_t CDistributedFilePart::getSize(bool checkCompressed)
- {
- offset_t ret = (offset_t)-1;
- StringBuffer firstname;
- bool compressed = ::isCompressed(parent.queryAttributes());
- unsigned nc=parent.numCopies(partIndex);
- for (unsigned copy=0;copy<nc;copy++)
- {
- RemoteFilename rfn;
- try
- {
- Owned<IFile> partfile = createIFile(getFilename(rfn,copy));
- if (checkCompressed && compressed)
- {
- Owned<ICompressedFileIO> compressedIO = createCompressedFileReader(partfile);
- if (compressedIO)
- ret = compressedIO->size();
- }
- else
- ret = partfile->size();
- if (ret!=(offset_t)-1)
- return ret;
- }
- catch (IException *e)
- {
- StringBuffer s("CDistributedFilePart::getSize ");
- rfn.getRemotePath(s);
- EXCLOG(e, s.str());
- e->Release();
- }
- if (copy==0)
- rfn.getRemotePath(firstname);
- }
- throw new CDFS_Exception(DFSERR_CannotFindPartFileSize,firstname.str());;
- }
- StringBuffer & CDistributedFilePart::getPartName(StringBuffer &partname)
- {
- if (!overridename.isEmpty()) {
- if (isSpecialPath(overridename)) {
- // bit of a kludge
- if (isPathSepChar(*overridename)&&partname.length()&&isPathSepChar(partname.charAt(partname.length()-1)))
- partname.setLength(partname.length()-1);
- return partname.append(overridename);
- }
- return partname.append(pathTail(overridename));
- }
- const char *mask=parent.queryPartMask();
- if (!mask||!*mask) {
- const char *err ="CDistributedFilePart::getPartName cannot determine part name (no mask)";
- IERRLOG("%s", err);
- throw MakeStringExceptionDirect(-1, err);
- }
- expandMask(partname,mask,partIndex,parent.numParts());
- return partname;
- }
- unsigned CDistributedFilePart::bestCopyNum(const IpAddress &ip,unsigned rel)
- {
- unsigned n = numCopies();
- unsigned *dist = new unsigned[n];
- unsigned *idx = new unsigned[n];
- for (unsigned c=0;c<n;c++) {
- dist[c] = ip.ipdistance(queryNode(c)->endpoint());
- idx[c] = c;
- }
- if (rel>=n)
- rel = n-1;
- // do bubble sort as not that many!
- for (unsigned i=0; i<n-1; i++)
- for (unsigned j=0; j<n-1-i; j++)
- if (dist[idx[j+1]] < dist[idx[j]]) {
- unsigned t = idx[j];
- idx[j] = idx[j+1];
- idx[j+1] = t;
- }
- unsigned ret = idx[rel];
- delete [] idx;
- delete [] dist;
- return ret;
- }
- unsigned CDistributedFilePart::copyClusterNum(unsigned copy,unsigned *replicate)
- {
- return parent.copyClusterNum(partIndex,copy,replicate);
- }
- StringBuffer &CDistributedFilePart::getPartDirectory(StringBuffer &ret,unsigned copy)
- {
- const char *defdir = parent.queryDefaultDir();
- StringBuffer dir;
- const char *pn;
- if (overridename.isEmpty())
- pn = parent.queryPartMask();
- else {
- pn = overridename.get();
- if (isSpecialPath(pn)) // its a query
- return ret; // ret.append('/'); // not sure if really need '/' here
- }
- if (pn&&*pn) {
- StringBuffer odir;
- splitDirTail(pn,odir);
- if (odir.length()) {
- if (isAbsolutePath(pn))
- dir.append(odir);
- else if (defdir&&*defdir)
- addPathSepChar(dir.append(defdir)).append(odir);
- }
- else
- dir.append(defdir);
- }
- if (dir.length()==0)
- IERRLOG("IDistributedFilePart::getPartDirectory unable to determine part directory");
- else {
- parent.adjustClusterDir(partIndex,copy,dir);
- ret.append(dir);
- }
- return ret;
- }
- unsigned CDistributedFilePart::numCopies()
- {
- return parent.numCopies(partIndex);
- }
- INode *CDistributedFilePart::queryNode(unsigned copy)
- {
- return parent.queryNode(partIndex,copy);
- }
- unsigned CDistributedFilePart::queryDrive(unsigned copy)
- {
- return parent.queryDrive(partIndex,copy,parent.directory);
- }
- bool CDistributedFilePart::isHost(unsigned copy)
- {
- return (queryNode(copy)->isHost());
- }
- IPropertyTree &CDistributedFilePart::queryAttributes()
- {
- CriticalBlock block (sect); // avoid nested blocks
- if (attr)
- return *attr;
- DBGLOG("CDistributedFilePart::queryAttributes missing part attributes");
- attr.setown(getEmptyAttr());
- return *attr;
- }
- RemoteFilename &CDistributedFilePart::getFilename(RemoteFilename &ret,unsigned copy)
- {
- // this is probably not as efficient as could be
- StringBuffer fullpath;
- getPartDirectory(fullpath,copy);
- addPathSepChar(fullpath);
- getPartName(fullpath);
- SocketEndpoint ep;
- INode *node=queryNode(copy);
- if (node)
- ep = node->endpoint();
- ret.setPath(ep,fullpath.str());
- return ret;
- }
- bool CDistributedFilePart::getCrc(unsigned &crc)
- {
- return getCrcFromPartProps(parent.queryAttributes(),queryAttributes(), crc);
- }
- unsigned CDistributedFilePart::getPhysicalCrc()
- {
- StringBuffer firstname;
- unsigned nc=parent.numCopies(partIndex);
- for (unsigned copy=0;copy<nc;copy++) {
- RemoteFilename rfn;
- try {
- Owned<IFile> partfile = createIFile(getFilename(rfn,copy));
- if (partfile&&partfile->exists())
- return partfile->getCRC();
- }
- catch (IException *e)
- {
- StringBuffer s("CDistributedFilePart::getPhysicalCrc ");
- rfn.getRemotePath(s);
- EXCLOG(e, s.str());
- e->Release();
- }
- if (copy==0)
- rfn.getRemotePath(firstname);
- }
- IDFS_Exception *e = new CDFS_Exception(DFSERR_CannotFindPartFileCrc,firstname.str());
- throw e;
- }
- // TODO: Create DistributedFilePropertyLock for parts
- bool CDistributedFilePart::lockProperties(unsigned timeoutms)
- {
- dirty = true;
- return parent.lockProperties(timeoutms);
- }
- // TODO: Create DistributedFilePropertyLock for parts
- void CDistributedFilePart::unlockProperties(DFTransactionState state=TAS_NONE)
- {
- parent.unlockProperties(state);
- }
- offset_t CDistributedFilePart::getFileSize(bool allowphysical,bool forcephysical)
- {
- offset_t ret = (offset_t)((forcephysical&&allowphysical)?-1:queryAttributes().getPropInt64("@size", -1));
- if (allowphysical&&(ret==(offset_t)-1))
- ret = getSize(true);
- return ret;
- }
- offset_t CDistributedFilePart::getDiskSize(bool allowphysical,bool forcephysical)
- {
- if (!::isCompressed(parent.queryAttributes()))
- return getFileSize(allowphysical, forcephysical);
- if (forcephysical && allowphysical)
- return getSize(false); // i.e. only if force, because all compressed should have @compressedSize attribute
- // NB: compressSize is disk size
- return queryAttributes().getPropInt64("@compressedSize", -1);
- }
- bool CDistributedFilePart::getModifiedTime(bool allowphysical,bool forcephysical, CDateTime &dt)
- {
- StringBuffer s;
- if (!forcephysical&&queryAttributes().getProp("@modified", s)) {
- dt.setString(s.str());
- if (!dt.isNull())
- return true;
- }
- if (allowphysical) {
- unsigned nc=parent.numCopies(partIndex);
- for (unsigned copy=0;copy<nc;copy++) {
- RemoteFilename rfn;
- try {
- Owned<IFile> partfile = createIFile(getFilename(rfn,copy));
- if (partfile->getTime(NULL,&dt,NULL))
- return true;
- }
- catch (IException *e)
- {
- StringBuffer s("CDistributedFilePart::getFileTime ");
- rfn.getRemotePath(s);
- EXCLOG(e, s.str());
- e->Release();
- }
- }
- }
- return false;
- }
- unsigned getSuperFileSubs(IDistributedSuperFile *super, IArrayOf<IDistributedFile> &subFiles, bool superSub)
- {
- unsigned numSubs = super->numSubFiles(superSub);
- for (unsigned s=0; s<numSubs; s++)
- {
- IDistributedFile &subFile = super->querySubFile(s, superSub);
- subFiles.append(*LINK(&subFile));
- }
- return numSubs;
- }
- // --------------------------------------------------------
- class CNamedGroupIterator: implements INamedGroupIterator, public CInterface
- {
- Owned<IPropertyTreeIterator> pe;
- Linked<IRemoteConnection> conn;
- Linked<IGroup> matchgroup;
- bool exactmatch;
- bool match();
- public:
- IMPLEMENT_IINTERFACE;
- CNamedGroupIterator(IRemoteConnection *_conn,IGroup *_matchgroup=NULL,bool _exactmatch=false)
- : conn(_conn), matchgroup(_matchgroup)
- {
- exactmatch = _exactmatch;
- if (matchgroup.get()) {
- StringBuffer query;
- query.append("Group[Node/@ip=\"");
- matchgroup->queryNode(0).endpoint().getUrlStr(query);
- query.append("\"]");
- pe.setown(conn->getElements(query.str()));
- }
- else
- pe.setown(conn->queryRoot()->getElements("Group"));
- }
- bool first()
- {
- if (!pe->first())
- return false;
- if (match())
- return true;
- return next();
- }
- bool next()
- {
- while (pe->next())
- if (match())
- return true;
- return false;
- }
- bool isValid()
- {
- return pe->isValid();
- }
- StringBuffer &get(StringBuffer &name)
- {
- pe->query().getProp("@name",name);
- return name;
- }
- StringBuffer &getdir(StringBuffer &dir)
- {
- pe->query().getProp("@dir",dir);
- return dir;
- }
- bool isCluster()
- {
- return pe->query().getPropBool("@cluster");
- }
- };
- // --------------------------------------------------------
- #define GROUP_CACHE_INTERVAL (1000*60)
- #define GROUP_EXCEPTION_CACHE_INTERVAL (1000*60*10)
- static GroupType translateGroupType(const char *groupType)
- {
- if (!groupType)
- return grp_unknown;
- if (strieq(groupType, "Thor"))
- return grp_thor;
- else if (strieq(groupType, "Roxie"))
- return grp_roxie;
- else if (strieq(groupType, "hthor"))
- return grp_hthor;
- else
- return grp_unknown;
- }
- class CNamedGroupCacheEntry: public CInterface
- {
- public:
- Linked<IGroup> group;
- StringAttr name;
- StringAttr groupDir;
- GroupType groupType;
- Linked<IException> exception;
- CNamedGroupCacheEntry(IGroup *_group, const char *_name, const char *_dir, GroupType _groupType)
- : group(_group), name(_name), groupDir(_dir), groupType(_groupType)
- {
- cachedtime = msTick();
- }
- CNamedGroupCacheEntry(IException *_exception, const char *_name)
- : exception(_exception), name(_name), groupType(grp_unknown)
- {
- cachedtime = msTick();
- }
- bool expired(unsigned timeNow)
- {
- if (exception)
- return timeNow-cachedtime > GROUP_EXCEPTION_CACHE_INTERVAL;
- else
- return timeNow-cachedtime > GROUP_CACHE_INTERVAL;
- }
- protected:
- unsigned cachedtime;
- };
- class CNamedGroupStore: implements INamedGroupStore, public CInterface
- {
- CriticalSection cachesect;
- CIArrayOf<CNamedGroupCacheEntry> cache;
- unsigned defaultTimeout;
- unsigned defaultRemoteTimeout;
- public:
- IMPLEMENT_IINTERFACE;
- CNamedGroupStore()
- {
- defaultTimeout = INFINITE;
- defaultRemoteTimeout = FOREIGN_DALI_TIMEOUT;
- }
- IGroup *dolookup(const char *logicalgroupname,IRemoteConnection *conn, StringBuffer *dirret, GroupType &groupType)
- {
- SocketEndpointArray epa;
- StringBuffer gname(logicalgroupname);
- gname.trim();
- groupType = grp_unknown;
- if (!gname.length())
- return nullptr;
- gname.toLowerCase();
- logicalgroupname = gname.str();
- bool isiprange = (*logicalgroupname!=0);
- for (const char *s1=logicalgroupname;*s1;s1++)
- if (isalpha(*s1)) {
- isiprange = false;
- break;
- }
- if (isiprange) {
- // allow IP or IP list instead of group name
- // I don't think this is a security problem as groups not checked
- // NB ports not allowed here
- char *buf = strdup(logicalgroupname);
- char *s = buf;
- while (*s) {
- char *next = strchr(s,',');
- if (next)
- *next = 0;
- SocketEndpoint ep;
- unsigned n = ep.ipsetrange(s);
- for (unsigned i=0;i<n;i++) {
- if (ep.isNull()) { // failed
- epa.kill();
- break;
- }
- epa.append(ep);
- ep.ipincrement(1);
- }
- if (!next)
- break;
- s = next+1;
- }
- free(buf);
- if (epa.ordinality())
- return createIGroup(epa);
- }
- StringBuffer range;
- StringBuffer parent;
- if (decodeChildGroupName(gname.str(),parent,range)) {
- gname.clear().append(parent);
- logicalgroupname = gname.str();
- }
- StringAttr groupdir;
- GroupType type = grp_unknown;
- bool cached = false;
- unsigned timeNow = msTick();
- {
- CriticalBlock block(cachesect);
- ForEachItemInRev(idx, cache)
- {
- CNamedGroupCacheEntry &entry = cache.item(idx);
- if (entry.expired(timeNow))
- {
- cache.remove(idx);
- }
- else if (strcmp(gname.str(),entry.name.get())==0)
- {
- cached = true;
- if (entry.exception)
- throw LINK(entry.exception);
- if (!entry.group) //cache entry of a deleted groupname
- return nullptr;
- if (range.length()==0)
- {
- if (dirret)
- dirret->append(entry.groupDir);
- groupType = entry.groupType;
- return entry.group.getLink();
- }
- // there is a range so copy to epa
- entry.group->getSocketEndpoints(epa);
- groupdir.set(entry.groupDir);
- type = entry.groupType;
- break;
- }
- }
- }
- try
- {
- if ((gname.length()>9)&&(memcmp(logicalgroupname,"foreign::",9)==0))
- {
- StringBuffer eps;
- const char *s = logicalgroupname+9;
- while (*s&&((*s!=':')||(s[1]!=':')))
- eps.append(*(s++));
- if (*s)
- {
- s+=2;
- if (*s)
- {
- Owned<INode> dali = createINode(eps.str());
- if (!dali || !getRemoteGroup(dali, s, defaultRemoteTimeout, groupdir, type, epa))
- {
- if (!cached)
- {
- CriticalBlock block(cachesect);
- cache.append(*new CNamedGroupCacheEntry(NULL, gname, NULL, grp_unknown));
- }
- return nullptr;
- }
- }
- }
- }
- else if (epa.ordinality()==0) {
- struct sLock
- {
- sLock() { lock = NULL; };
- ~sLock() { delete lock; };
- CConnectLock *lock;
- } slock;
- if (!conn)
- {
- slock.lock = new CConnectLock("CNamedGroup::lookup", SDS_GROUPSTORE_ROOT, false, false, false, defaultTimeout);
- conn = slock.lock->conn;
- if (!conn)
- {
- if (!cached)
- {
- CriticalBlock block(cachesect);
- cache.append(*new CNamedGroupCacheEntry(NULL, gname, NULL, grp_unknown));
- }
- return nullptr;
- }
- }
- Owned<IPropertyTree> pt = getNamedPropTree(conn->queryRoot(),"Group","@name",gname.str(),true);
- if (!pt)
- return nullptr;
- type = translateGroupType(pt->queryProp("@kind"));
- groupdir.set(pt->queryProp("@dir"));
- if (groupdir.isEmpty())
- groupdir.set(queryBaseDirectory(type));
- Owned<IPropertyTreeIterator> pe2 = pt->getElements("Node");
- ForEach(*pe2)
- {
- SocketEndpoint ep(pe2->query().queryProp("@ip"));
- epa.append(ep);
- }
- }
- }
- catch (IException *E)
- {
- // cache the exception
- CriticalBlock block(cachesect);
- cache.append(*new CNamedGroupCacheEntry(E, gname));
- throw;
- }
- Owned<IGroup> ret = createIGroup(epa);
- if (!cached)
- {
- CriticalBlock block(cachesect);
- cache.append(*new CNamedGroupCacheEntry(ret, gname, groupdir, type));
- }
- if (range.length())
- {
- SocketEndpointArray epar;
- const char *s = range.str();
- while (*s)
- {
- unsigned start = 0;
- while (isdigit(*s))
- {
- start = start*10+*s-'0';
- s++;
- }
- if (!start)
- break;
- unsigned end;
- if (*s=='-')
- {
- s++;
- end = 0;
- while (isdigit(*s))
- {
- end = end*10+*s-'0';
- s++;
- }
- if (!end)
- end = epa.ordinality();
- }
- else
- end = start;
- if ((start>epa.ordinality())||(end>epa.ordinality()))
- {
- s = range.str();
- break;
- }
- if (*s==',')
- s++;
- unsigned i=start-1;
- do { // allow 400-1 etc
- i++;
- if (i>epa.ordinality())
- i = 1;
- epar.append(epa.item(i-1));
- } while (i!=end);
- }
- if (*s)
- throw MakeStringException(-1,"Invalid group range %s",range.str());
- ret.setown(createIGroup(epar));
- }
- if (dirret)
- dirret->append(groupdir);
- groupType = type;
- return ret.getClear();
- }
- IGroup *lookup(const char *logicalgroupname)
- {
- GroupType dummy;
- return dolookup(logicalgroupname, NULL, NULL, dummy);
- }
- IGroup *lookup(const char *logicalgroupname, StringBuffer &dir, GroupType &groupType)
- {
- return dolookup(logicalgroupname, NULL, &dir, groupType);
- }
- INamedGroupIterator *getIterator()
- {
- CConnectLock connlock("CNamedGroup::getIterator", SDS_GROUPSTORE_ROOT, false, true, false, defaultTimeout);
- return new CNamedGroupIterator(connlock.conn); // links connection
- }
- INamedGroupIterator *getIterator(IGroup *match,bool exact)
- {
- CConnectLock connlock("CNamedGroup::getIterator", SDS_GROUPSTORE_ROOT, false, false, false, defaultTimeout);
- return new CNamedGroupIterator(connlock.conn,match,exact); // links connection
- }
- void doadd(CConnectLock &connlock,const char *name,IGroup *group,bool cluster,const char *dir)
- {
- if (!group)
- return;
- IPropertyTree *val = createPTree("Group");
- val->setProp("@name",name);
- if (cluster)
- val->setPropBool("@cluster", true);
- if (dir)
- val->setProp("@dir",dir);
- INodeIterator &gi = *group->getIterator();
- StringBuffer str;
- ForEach(gi) {
- IPropertyTree *n = createPTree("Node");
- n = val->addPropTree("Node",n);
- gi.query().endpoint().getIpText(str.clear());
- n->setProp("@ip",str.str());
- }
- gi.Release();
- connlock.conn->queryRoot()->addPropTree("Group",val);
- }
- void addUnique(IGroup *group,StringBuffer &lname, const char *dir)
- {
- if (group->ordinality()==1)
- {
- group->getText(lname);
- return;
- }
- CConnectLock connlock("CNamedGroup::addUnique", SDS_GROUPSTORE_ROOT, true, false, false, defaultTimeout);
- StringBuffer name;
- StringBuffer prop;
- unsigned scale = 16;
- for (;;) {
- name.clear();
- if (lname.length()) { // try suggested name
- name.append(lname);
- name.toLowerCase();
- lname.clear();
- }
- else
- name.append("__anon").append(getRandom()%scale);
- prop.clear().appendf("Group[@name=\"%s\"]",name.str());
- if (!connlock.conn->queryRoot()->hasProp(prop.str()))
- break;
- scale*=2;
- }
- doadd(connlock,name.str(),group,false,dir);
- lname.append(name);
- }
- void add(const char *logicalgroupname, IGroup *group, bool cluster, const char *dir, GroupType groupType)
- {
- StringBuffer name(logicalgroupname);
- name.toLowerCase();
- name.trim();
- StringBuffer prop;
- prop.appendf("Group[@name=\"%s\"]",name.str());
- CConnectLock connlock("CNamedGroup::add", SDS_GROUPSTORE_ROOT, true, false, false, defaultTimeout);
- connlock.conn->queryRoot()->removeProp(prop.str());
- doadd(connlock,name.str(),group,cluster,dir);
- {
- CriticalBlock block(cachesect);
- cache.kill();
- if (group)
- cache.append(*new CNamedGroupCacheEntry(group, name.str(), dir, groupType));
- }
- }
- void remove(const char *logicalgroupname)
- {
- add(logicalgroupname, NULL, false, NULL, grp_unknown);
- }
- bool find(IGroup *grp, StringBuffer &gname, bool add)
- {
- // gname on entry is suggested name for add if set
- unsigned n = grp->ordinality();
- if (!grp||!n)
- return false;
- Owned<INamedGroupIterator> iter=getIterator(grp,(n==1)); // one node clusters must be exact match
- StringAttr bestname;
- StringBuffer name;
- ForEach(*iter) {
- bool iscluster = iter->isCluster();
- if (iscluster||(bestname.isEmpty())) {
- iter->get(name.clear());
- if (name.length()) {
- bestname.set(name);
- if (iscluster)
- break;
- }
- }
- }
- iter.clear();
- if (bestname.isEmpty()) {
- if (add||(n==1)) // single-nodes always have implicit group of IP
- addUnique(grp,gname,NULL);
- return false;
- }
- gname.clear().append(bestname);
- return true;
- }
- void swapNode(const IpAddress &from, const IpAddress &to)
- {
- if (from.ipequals(to))
- return;
- CConnectLock connlock("CNamedGroup::swapNode", SDS_GROUPSTORE_ROOT, true, false, false, defaultTimeout);
- StringBuffer froms;
- from.getIpText(froms);
- StringBuffer tos;
- to.getIpText(tos);
- Owned<IPropertyTreeIterator> pe = connlock.conn->queryRoot()->getElements("Group");
- ForEach(*pe) {
- IPropertyTree &group = pe->query();
- const char *kind = group.queryProp("@kind");
- if (kind && streq("Spare", kind))
- continue;
- StringBuffer name;
- group.getProp("@name",name);
- StringBuffer xpath("Node[@ip = \"");
- xpath.append(froms).append("\"]");
- for (unsigned guard=0; guard<1000; guard++) {
- Owned<IPropertyTreeIterator> ne = group.getElements(xpath.str());
- if (!ne->first())
- break;
- ne->query().setProp("@ip",tos.str());
- PROGLOG("swapNode swapping %s for %s in group %s",froms.str(),tos.str(),name.str());
- unsigned nodesSwapped = group.getPropInt("@nodesSwapped");
- group.setPropInt("@nodesSwapped", nodesSwapped+1);
- }
- }
- CriticalBlock block(cachesect);
- cache.kill();
- }
- unsigned setDefaultTimeout(unsigned timems)
- {
- unsigned ret = defaultTimeout;
- defaultTimeout = timems;
- return ret;
- }
- unsigned setRemoteTimeout(unsigned timems)
- {
- unsigned ret = defaultRemoteTimeout;
- defaultRemoteTimeout = timems;
- return ret;
- }
- void resetCache()
- {
- CriticalBlock block(cachesect);
- cache.kill();
- }
- private:
- bool getRemoteGroup(const INode *foreigndali, const char *gname, unsigned foreigndalitimeout,
- StringAttr &groupdir, GroupType &type, SocketEndpointArray &epa)
- {
- StringBuffer lcname(gname);
- gname = lcname.trim().toLowerCase().str();
- CMessageBuffer mb;
- mb.append((int)MDFS_GET_GROUP_TREE).append(gname);
- size32_t mbsz = mb.length();
- foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
- checkDfsReplyException(mb);
- if (mb.length()==0)
- return false;
- byte ok;
- mb.read(ok);
- if (ok!=1) {
- // kludge for prev bug
- if ((ok==(byte)MDFS_GET_GROUP_TREE)&&mb.length()>mbsz) {
- mb.skip(mbsz-1);
- mb.read(ok);
- if (ok!=1)
- return false;
- }
- else
- return false;
- }
- Owned<IPropertyTree> pt = createPTree(mb);
- Owned<IPropertyTreeIterator> pe = pt->getElements("Node");
- groupdir.set(pt->queryProp("@dir"));
- type = translateGroupType(pt->queryProp("@kind"));
- ForEach(*pe) {
- SocketEndpoint ep(pe->query().queryProp("@ip"));
- epa.append(ep);
- }
- return epa.ordinality() > 0;
- }
- };
- static CNamedGroupStore *groupStore = NULL;
- static CriticalSection groupsect;
- bool CNamedGroupIterator::match()
- {
- if (conn.get()) {
- if (matchgroup.get()) {
- if (!groupStore)
- return false;
- const char *name = pe->query().queryProp("@name");
- if (!name||!*name)
- return false;
- GroupType dummy;
- Owned<IGroup> lgrp = groupStore->dolookup(name, conn, NULL, dummy);
- if (lgrp) {
- if (exactmatch)
- return lgrp->equals(matchgroup);
- GroupRelation gr = matchgroup->compare(lgrp);
- return (gr==GRidentical)||(gr==GRbasesubset)||(gr==GRwrappedsuperset);
- }
- }
- else
- return true;
- }
- return false;
- }
- INamedGroupStore &queryNamedGroupStore()
- {
- if (!groupStore) {
- CriticalBlock block(groupsect);
- if (!groupStore)
- groupStore = new CNamedGroupStore();
- }
- return *groupStore;
- }
- // --------------------------------------------------------
- IDistributedFile *CDistributedFileDirectory::lookup(const char *_logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, unsigned timeout)
- {
- CDfsLogicalFileName logicalname;
- logicalname.set(_logicalname);
- return lookup(logicalname, user, writeattr, hold, lockSuperOwner, transaction, timeout);
- }
- IDistributedFile *CDistributedFileDirectory::dolookup(CDfsLogicalFileName &_logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, unsigned timeout)
- {
- CDfsLogicalFileName *logicalname = &_logicalname;
- if (logicalname->isMulti())
- // don't bother checking because the sub file creation will
- return new CDistributedSuperFile(this,*logicalname,user,transaction); // temp superfile
- if (strchr(logicalname->get(), '*')) // '*' only wildcard supported. NB: This is a temporary fix (See: HPCC-12523)
- throw MakeStringException(-1, "Invalid filename lookup: %s", logicalname->get());
- Owned<IDfsLogicalFileNameIterator> redmatch;
- for (;;)
- {
- checkLogicalName(*logicalname,user,true,writeattr,true,NULL);
- if (logicalname->isExternal()) {
- Owned<IFileDescriptor> fDesc = getExternalFileDescriptor(logicalname->get());
- if (!fDesc)
- return NULL;
- return queryDistributedFileDirectory().createExternal(fDesc, logicalname->get());
- }
- if (logicalname->isForeign()) {
- IDistributedFile * ret = getFile(logicalname->get(),user,NULL);
- if (ret)
- return ret;
- }
- else {
- unsigned start = 0;
- for (;;) {
- CFileLock fcl;
- unsigned mode = RTM_LOCK_READ | RTM_SUB;
- if (hold) mode |= RTM_LOCK_HOLD;
- CTimeMon tm(timeout);
- if (!fcl.init(*logicalname, mode, timeout, "CDistributedFileDirectory::lookup"))
- break;
- CFileSuperOwnerLock superOwnerLock;
- if (lockSuperOwner)
- {
- unsigned remaining;
- tm.timedout(&remaining);
- verifyex(superOwnerLock.initWithFileLock(*logicalname, remaining, "CDistributedFileDirectory::dolookup(SuperOwnerLock)", fcl, mode));
- }
- if (fcl.getKind() == DXB_File)
- {
- StringBuffer cname;
- if (logicalname->getCluster(cname).length())
- {
- IPropertyTree *froot=fcl.queryRoot();
- if (froot)
- {
- StringBuffer query;
- query.appendf("Cluster[@name=\"%s\"]",cname.str());
- if (!froot->hasProp(query.str()))
- break;
- }
- }
- CDistributedFile *ret = new CDistributedFile(this,fcl.detach(),*logicalname,user); // found
- ret->setSuperOwnerLock(superOwnerLock.detach());
- return ret;
- }
- // now super file
- if (fcl.getKind() != DXB_SuperFile)
- break;
- if (start==0)
- start = msTick();
- unsigned elapsed;
- try
- {
- CDistributedSuperFile *ret = new CDistributedSuperFile(this,fcl.detach(),*logicalname,user,transaction,SDS_SUB_LOCK_TIMEOUT);
- ret->setSuperOwnerLock(superOwnerLock.detach());
- return ret;
- }
- catch (ISDSException *e)
- {
- elapsed = msTick()-start;
- if ((e->errorCode()!=SDSExcpt_LockTimeout)||(elapsed>((timeout==INFINITE)?SDS_CONNECT_TIMEOUT:timeout)))
- throw;
- EXCLOG(e,"Superfile lookup");
- e->Release();
- }
- PROGLOG("CDistributedSuperFile connect timeout (%dms) pausing",elapsed);
- Sleep(SDS_TRANSACTION_RETRY/2+(getRandom()%SDS_TRANSACTION_RETRY));
- }
- }
- if (redmatch.get()) {
- if (!redmatch->next())
- break;
- }
- else {
- redmatch.setown(queryRedirection().getMatch(logicalname->get()));
- if (!redmatch.get())
- break;
- if (!redmatch->first())
- break;
- }
- logicalname = &redmatch->query();
- }
- return NULL;
- }
- IDistributedFile *CDistributedFileDirectory::lookup(CDfsLogicalFileName &logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, unsigned timeout)
- {
- return dolookup(logicalname, user, writeattr, hold, lockSuperOwner, transaction, timeout);
- }
- IDistributedSuperFile *CDistributedFileDirectory::lookupSuperFile(const char *_logicalname,IUserDescriptor *user,IDistributedFileTransaction *transaction, unsigned timeout)
- {
- CDfsLogicalFileName logicalname;
- logicalname.set(_logicalname);
- IDistributedFile *file = dolookup(logicalname, user, false, false, false, transaction, timeout);
- if (file) {
- IDistributedSuperFile *sf = file->querySuperFile();
- if (sf)
- return sf;
- file->Release();
- }
- return NULL;
- }
- bool CDistributedFileDirectory::isSuperFile( const char *logicalname,
- IUserDescriptor *user,
- INode *foreigndali,
- unsigned timeout)
- {
- Owned<IPropertyTree> tree = getFileTree(logicalname, user, foreigndali,timeout, false);
- return tree.get()&&(strcmp(tree->queryName(),queryDfsXmlBranchName(DXB_SuperFile))==0);
- }
- bool CDistributedFileDirectory::exists(const char *_logicalname,IUserDescriptor *user,bool notsuper,bool superonly)
- {
- // (currently) no check on scope permissions for exists
- bool external;
- bool foreign;
- CDfsLogicalFileName dlfn;
- dlfn.set(_logicalname);
- const char *logicalname = dlfn.get();
- external = dlfn.isExternal();
- foreign = dlfn.isForeign();
- if (foreign) {
- Owned<IDistributedFile> file = lookup(_logicalname, user, false, false, false, NULL, defaultTimeout);
- if (file.get()==NULL)
- return false;
- if (file->querySuperFile()) {
- if (notsuper)
- return false;
- }
- else
- if (superonly)
- return false;
- }
- else if (external) {
- if (!existsPhysical(_logicalname,user))
- return false;
- }
- else {
- StringBuffer str;
- if (!superonly) {
- dlfn.makeFullnameQuery(str,DXB_File,true);
- CConnectLock connlockfile("CDistributedFileDirectory::exists",str.str(),false,false,false,defaultTimeout);
- if (connlockfile.conn.get())
- return true;
- }
- if (notsuper)
- return false;
- dlfn.makeFullnameQuery(str.clear(),DXB_SuperFile,true);
- CConnectLock connlocksuper("CDistributedFileDirectory::exists",str.str(),false,false,false,defaultTimeout);
- if (!connlocksuper.conn.get())
- return false;
- }
- return true;
- }
- bool CDistributedFileDirectory::existsPhysical(const char *_logicalname, IUserDescriptor *user)
- {
- Owned<IDistributedFile> file = lookup(_logicalname, user, false, false, false, NULL, defaultTimeout);
- if (!file)
- return false;
- return file->existsPhysicalPartFiles(0);
- }
- IDistributedFile *CDistributedFileDirectory::createNew(IFileDescriptor *fdesc)
- {
- return new CDistributedFile(this, fdesc, NULL, false);
- }
- IDistributedFile *CDistributedFileDirectory::createExternal(IFileDescriptor *fdesc, const char *name)
- {
- CDistributedFile *dFile = new CDistributedFile(this, fdesc, NULL, true);
- dFile->setLogicalName(name);
- return dFile;
- }
- ////////////////////////////////////
- class DistributedFilePropertyLockFree
- {
- unsigned lockedCount;
- CDistributedFile *file;
- CDistributedSuperFile *sfile;
- public:
- DistributedFilePropertyLockFree(IDistributedFile *_file)
- {
- file = dynamic_cast<CDistributedFile *>(_file);
- sfile = NULL;
- if (file)
- lockedCount = file->setPropLockCount(0);
- else
- {
- sfile = dynamic_cast<CDistributedSuperFile *>(_file);
- lockedCount = sfile->setPropLockCount(0);
- }
- }
- ~DistributedFilePropertyLockFree()
- {
- if (file)
- verifyex(0 == file->setPropLockCount(lockedCount));
- else if (sfile)
- verifyex(0 == sfile->setPropLockCount(lockedCount));
- }
- };
- /**
- * Creates a super-file within a transaction.
- */
- class CCreateSuperFileAction: public CDFAction
- {
- CDfsLogicalFileName logicalname;
- CDistributedFileDirectory *parent;
- Linked<IDistributedSuperFile> super;
- IUserDescriptor *user;
- bool interleaved, created;
- void clearSuper()
- {
- if (created)
- {
- created = false;
- super->detach();
- }
- super.clear();
- }
- public:
- CCreateSuperFileAction(CDistributedFileDirectory *_parent,
- IUserDescriptor *_user,
- const char *_flname,
- bool _interleaved)
- : parent(_parent), user(_user), created(false), interleaved(_interleaved)
- {
- logicalname.set(_flname);
- }
- IDistributedSuperFile *getSuper()
- {
- if (!super)
- {
- Owned<IDistributedSuperFile> _super = transaction->lookupSuperFile(logicalname.get(), SDS_SUB_LOCK_TIMEOUT);
- if (_super)
- super.setown(_super.getClear());
- else
- {
- /* No super, create one if necessary.
- * This really shouldn't have to work this way, looking up super early, or creating super stub now,
- * because other super file transactions are based on
- * TBD: There should be a way to obtain lock independently of actually attaching.
- */
- Owned<IPropertyTree> root = createPTree();
- root->setPropInt("@interleaved",interleaved?2:0); // this is ill placed
- super.setown(new CDistributedSuperFile(parent, root, logicalname, user));
- created = true;
- transaction->addFile(super);
- }
- }
- return super.getLink();
- }
- bool prepare()
- {
- Owned<IDistributedFile> _super = getSuper();
- // Attach the file to DFS, if wasn't there already
- if (created)
- super->attach(logicalname.get(), user);
- addFileLock(super);
- if (lock())
- return true;
- unlock();
- return false;
- }
- void run()
- {
- // Do nothing, file is already created
- }
- void retry()
- {
- // on retry, we need to remove the file so next lock doesn't fail
- clearSuper();
- CDFAction::retry();
- }
- void rollback()
- {
- state = TAS_FAILURE;
- clearSuper();
- CDFAction::rollback();
- }
- };
- /**
- * Removes a super-file within a transaction.
- */
- class CRemoveSuperFileAction: public CDFAction
- {
- CDfsLogicalFileName logicalname;
- Linked<IDistributedSuperFile> super;
- IUserDescriptor *user;
- bool delSub;
- Owned<IDistributedFileTransactionExt> nestedTransaction;
- class CNestedTransaction : public CDistributedFileTransaction
- {
- IDistributedFileTransactionExt *transaction;
- CIArrayOf<CDFAction> actions;
- public:
- CNestedTransaction(IDistributedFileTransactionExt *_transaction, IUserDescriptor *user)
- : CDistributedFileTransaction(user), transaction(_transaction)
- {
- if (transaction->active())
- start();
- }
- // wrap rest of calls into parent transaction calls
- virtual IDistributedFile *lookupFile(const char *lfn,unsigned timeout=INFINITE)
- {
- return transaction->lookupFile(lfn, timeout);
- }
- virtual IDistributedSuperFile *lookupSuperFile(const char *slfn,unsigned timeout=INFINITE)
- {
- return transaction->lookupSuperFile(slfn, timeout);
- }
- virtual IUserDescriptor *queryUser() { return transaction->queryUser(); }
- virtual void descend() { transaction->descend(); }
- virtual void ascend() { transaction->ascend(); }
- virtual void addFile(IDistributedFile *file) { transaction->addFile(file); }
- virtual void ensureFile(IDistributedFile *file) { transaction->ensureFile(file); }
- virtual void clearFile(IDistributedFile *file) { transaction->clearFile(file); }
- virtual void noteAddSubFile(IDistributedSuperFile *super, const char *superName, IDistributedFile *sub)
- {
- transaction->noteAddSubFile(super, superName, sub);
- }
- virtual void noteRemoveSubFile(IDistributedSuperFile *super, IDistributedFile *sub)
- {
- transaction->noteRemoveSubFile(super, sub);
- }
- virtual void noteSuperSwap(IDistributedSuperFile *super1, IDistributedSuperFile *super2)
- {
- transaction->noteSuperSwap(super1, super2);
- }
- virtual void clearSubFiles(IDistributedSuperFile *super)
- {
- transaction->clearSubFiles(super);
- }
- virtual void noteRename(IDistributedFile *file, const char *newName)
- {
- transaction->noteRename(file, newName);
- }
- virtual void validateAddSubFile(IDistributedSuperFile *super, IDistributedFile *sub, const char *subName)
- {
- transaction->validateAddSubFile(super, sub, subName);
- }
- virtual bool isSubFile(IDistributedSuperFile *super, const char *subFile, bool sub)
- {
- return transaction->isSubFile(super, subFile, sub);
- }
- virtual bool addDelayedDelete(CDfsLogicalFileName &lfn,unsigned timeoutms=INFINITE)
- {
- return transaction->addDelayedDelete(lfn, timeoutms);
- }
- };
- public:
- CRemoveSuperFileAction(IUserDescriptor *_user,
- const char *_flname,
- bool _delSub)
- : user(_user), delSub(_delSub)
- {
- logicalname.set(_flname);
- }
- virtual bool prepare()
- {
- // We *have* to make sure the file exists here
- super.setown(transaction->lookupSuperFile(logicalname.get(), SDS_SUB_LOCK_TIMEOUT));
- if (!super)
- ThrowStringException(-1, "Super File %s doesn't exist in the file system", logicalname.get());
- addFileLock(super);
- // Adds actions to transactions before this one and gets executed only on commit
- if (delSub)
- {
- // As 'delSub' means additional actions, handle them in their own transaction context
- // Wrap lookups and record of removed/added subs to parent transaction, for common cache purposes
- nestedTransaction.setown(new CNestedTransaction(transaction, user));
- super->removeSubFile(NULL, true, false, nestedTransaction);
- }
- if (lock())
- {
- if (nestedTransaction)
- {
- if (nestedTransaction->prepareActions())
- return true;
- }
- else
- return true;
- }
- unlock();
- super.clear();
- return false;
- }
- virtual void retry()
- {
- super.clear();
- if (nestedTransaction)
- nestedTransaction->retryActions();
- CDFAction::retry();
- }
- virtual void run()
- {
- if (nestedTransaction)
- nestedTransaction->runActions();
- super->detach(INFINITE, transaction->queryCodeContext());
- }
- virtual void commit()
- {
- if (nestedTransaction)
- nestedTransaction->commitAndClearup();
- CDFAction::commit();
- }
- };
- /**
- * Renames a file within a transaction.
- */
- class CRenameFileAction: public CDFAction
- {
- CDfsLogicalFileName fromName, toName;
- CDistributedFileDirectory *parent;
- Linked<IDistributedFile> file;
- IUserDescriptor *user;
- bool renamed;
- enum RenameAction { ra_regular, ra_splitfrom, ra_mergeinto } ra;
- public:
- CRenameFileAction(CDistributedFileDirectory *_parent,
- IUserDescriptor *_user,
- const char *_flname,
- const char *_newname)
- : user(_user), parent(_parent)
- {
- fromName.set(_flname);
- // Basic consistency checking
- toName.set(_newname);
- if (fromName.isExternal() || toName.isExternal())
- throw MakeStringException(-1,"rename: cannot rename external files"); // JCSMORE perhaps you should be able to?
- if (fromName.isForeign() || toName.isForeign())
- throw MakeStringException(-1,"rename: cannot rename foreign files");
- // Make sure files are not the same
- if (0 == strcmp(fromName.get(), toName.get()))
- ThrowStringException(-1, "rename: cannot rename file %s to itself", toName.get());
- ra = ra_regular;
- renamed = false;
- }
- virtual bool prepare()
- {
- // We *have* to make sure the source file exists and can be renamed
- file.setown(transaction->lookupFile(fromName.get(), SDS_SUB_LOCK_TIMEOUT));
- if (!file)
- ThrowStringException(-1, "rename: file %s doesn't exist in the file system", fromName.get());
- if (file->querySuperFile())
- ThrowStringException(-1,"rename: cannot rename file %s as is SuperFile", fromName.get()); // Why not
- StringBuffer reason;
- if (!file->canRemove(reason))
- ThrowStringException(-1,"rename: %s",reason.str());
- addFileLock(file);
- renamed = false;
- if (lock())
- {
- StringBuffer oldcluster, newcluster;
- fromName.getCluster(oldcluster);
- toName.getCluster(newcluster);
- Owned<IDistributedFile> newFile = transaction->lookupFile(toName.get(), SDS_SUB_LOCK_TIMEOUT);
- if (newFile)
- {
- if (newcluster.length())
- {
- if (oldcluster.length())
- ThrowStringException(-1,"rename: cannot specify both source and destination clusters on rename");
- if (newFile->findCluster(newcluster.str())!=NotFound)
- ThrowStringException(-1,"rename: cluster %s already part of file %s",newcluster.str(),toName.get());
- if (file->numClusters()!=1)
- ThrowStringException(-1,"rename: source file %s has more than one cluster",fromName.get());
- // check compatible here ** TBD
- ra = ra_mergeinto;
- }
- else
- ThrowStringException(-1, "rename: file %s already exist in the file system", toName.get());
- }
- else if (oldcluster.length())
- {
- if (newcluster.length())
- ThrowStringException(-1,"rename: cannot specify both source and destination clusters on rename");
- if (file->numClusters()==1)
- ThrowStringException(-1,"rename: cannot rename sole cluster %s",oldcluster.str());
- if (file->findCluster(oldcluster.str())==NotFound)
- ThrowStringException(-1,"rename: cannot find cluster %s",oldcluster.str());
- ra = ra_splitfrom;
- }
- else
- {
- // TODO: something should check that file being renamed is not a subfile of a super where both created in transaction
- transaction->noteRename(file, toName.get());
- ra = ra_regular;
- }
- return true;
- }
- unlock();
- file.clear();
- return false;
- }
- virtual void run()
- {
- doRename(fromName, toName, ra);
- renamed = true;
- }
- virtual void retry()
- {
- file.clear();
- CDFAction::retry();
- }
- virtual void rollback()
- {
- // Only roll back if already renamed
- if (renamed)
- {
- switch (ra)
- {
- case ra_regular:
- doRename(toName, fromName, ra_regular);
- break;
- case ra_splitfrom:
- doRename(toName, fromName, ra_mergeinto);
- break;
- case ra_mergeinto:
- doRename(toName, fromName, ra_splitfrom);
- break;
- default:
- throwUnexpected();
- }
- renamed = false;
- }
- CDFAction::rollback();
- }
- private:
- void doRename(CDfsLogicalFileName &from, CDfsLogicalFileName &to, RenameAction ra)
- {
- CriticalBlock block(physicalChange);
- StringBuffer oldcluster, newcluster;
- fromName.getCluster(oldcluster);
- toName.getCluster(newcluster);
- Owned<IDistributedFile> oldfile;
- if (ra_splitfrom == ra)
- {
- oldfile.setown(file.getClear());
- Owned<IFileDescriptor> newdesc = oldfile->getFileDescriptor(oldcluster.str());
- file.setown(parent->createNew(newdesc));
- }
- // Physical Rename
- Owned<IMultiException> exceptions = MakeMultiException();
- if (!file->renamePhysicalPartFiles(to.get(),newcluster,exceptions))
- {
- unlock();
- StringBuffer errors;
- exceptions->errorMessage(errors);
- ThrowStringException(-1, "rename: could not rename logical file %s to %s: %s", fromName.get(), to.get(), errors.str());
- }
- // Logical rename and cleanup
- switch (ra)
- {
- case ra_splitfrom:
- {
- unlock();
- oldfile->removeCluster(oldcluster.str());
- file->attach(to.get(), user);
- lock();
- break;
- }
- case ra_mergeinto:
- {
- Owned<IDistributedFile> newFile = transaction->lookupFile(to.get(), SDS_SUB_LOCK_TIMEOUT);
- ClusterPartDiskMapSpec mspec = file->queryPartDiskMapping(0);
- // Unlock the old file
- unlock();
- CDistributedFile *_file = dynamic_cast<CDistributedFile *>(file.get());
- _file->detachLogical(INFINITE); // don't delete physicals, now used by newFile
- transaction->clearFile(file); // no long used in transaction
- newFile->addCluster(newcluster.str(),mspec);
- parent->fixDates(newFile);
- // need to clear and re-lookup as changed outside of transaction
- // TBD: Allow 'addCluster' 'fixDates' etc. to be delayed/work inside transaction
- transaction->clearFile(newFile);
- newFile.clear();
- file.setown(transaction->lookupFile(to.get(), SDS_SUB_LOCK_TIMEOUT));
- addFileLock(file);
- lock();
- break;
- }
- case ra_regular:
- {
- /* It is not enough to unlock this actions locks on the file being renamed,
- * because other actions, before and after may hold locks to the same file.
- * For now, IDistributeFile::rename, needs to work on a lock free instance.
- * TBD: Allow IDistributedFile::rename to work properly within transaction.
- */
- DistributedFilePropertyLockFree unlock(file);
- file->rename(to.get(), user);
- break;
- }
- default:
- throwUnexpected();
- }
- // MORE: If the logical rename fails, we should roll back the physical renaming
- // What if the physical renaming-back fails?!
- // For now, leaving as it was, since physical renaming is more prone to errors than logical
- // And checks were made earlier to make sure it was safe to rename
- }
- };
- // MORE: This should be implemented in DFSAccess later on
- IDistributedSuperFile *CDistributedFileDirectory::createSuperFile(const char *_logicalname,IUserDescriptor *user, bool _interleaved,bool ifdoesnotexist,IDistributedFileTransaction *transaction)
- {
- CDfsLogicalFileName logicalname;
- logicalname.set(_logicalname);
- checkLogicalName(logicalname,user,true,true,false,"have a superfile with");
- // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
- Linked<IDistributedFileTransactionExt> localtrans;
- if (transaction)
- {
- IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
- localtrans.set(_transaction);
- }
- else
- localtrans.setown(new CDistributedFileTransaction(user));
- IDistributedSuperFile *sfile = localtrans->lookupSuperFile(logicalname.get());
- if (sfile)
- {
- if (ifdoesnotexist)
- return sfile;
- else
- throw MakeStringException(-1,"createSuperFile: SuperFile %s already exists",logicalname.get());
- }
- Owned<CCreateSuperFileAction> action = new CCreateSuperFileAction(this,user,_logicalname,_interleaved);
- localtrans->addAction(action.getLink()); // takes ownership
- localtrans->autoCommit();
- return action->getSuper();
- }
- // MORE: This should be implemented in DFSAccess later on
- IDistributedSuperFile *CDistributedFileDirectory::createNewSuperFile(IPropertyTree *tree, const char *optionalName)
- {
- return new CDistributedSuperFile(this, tree, optionalName);
- }
- // MORE: This should be implemented in DFSAccess later on
- void CDistributedFileDirectory::removeSuperFile(const char *_logicalname, bool delSubs, IUserDescriptor *user, IDistributedFileTransaction *transaction)
- {
- CDfsLogicalFileName logicalname;
- logicalname.set(_logicalname);
- checkLogicalName(logicalname,user,true,true,false,"have a superfile with");
- // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
- Linked<IDistributedFileTransactionExt> localtrans;
- if (transaction)
- {
- IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
- localtrans.set(_transaction);
- }
- else
- localtrans.setown(new CDistributedFileTransaction(user));
- CRemoveSuperFileAction *action = new CRemoveSuperFileAction(user, _logicalname, delSubs);
- localtrans->addAction(action); // takes ownership
- localtrans->autoCommit();
- }
- bool CDistributedFileDirectory::removeEntry(const char *name, IUserDescriptor *user, IDistributedFileTransaction *transaction, unsigned timeoutms, bool throwException)
- {
- CDfsLogicalFileName logicalname;
- logicalname.set(name);
- if (!logicalname.isExternal())
- checkLogicalName(logicalname,user,true,true,false,"delete");
- // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
- Linked<IDistributedFileTransactionExt> localtrans;
- if (transaction)
- {
- IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
- localtrans.set(_transaction);
- }
- else
- localtrans.setown(new CDistributedFileTransaction(user));
- // Action will be executed at the end of the transaction (commit)
- localtrans->addDelayedDelete(logicalname, timeoutms);
- try
- {
- localtrans->autoCommit();
- }
- catch (IException *e)
- {
- // TODO: Transform removeEntry into void
- StringBuffer msg(logicalname.get());
- msg.append(" - cause: ");
- e->errorMessage(msg);
- IERRLOG("%s", msg.str());
- if (throwException)
- throw new CDFS_Exception(DFSERR_FailedToDeleteFile, msg.str());
- e->Release();
- return false;
- }
- return true;
- }
- void CDistributedFileDirectory::removeEmptyScope(const char *scope)
- {
- if (scope&&*scope) {
- StringBuffer fn(scope);
- fn.append("::x");
- CDfsLogicalFileName dlfn;
- dlfn.set(fn.str());
- removeFileEmptyScope(dlfn,defaultTimeout);
- }
- }
- void CDistributedFileDirectory::renamePhysical(const char *oldname,const char *newname,IUserDescriptor *user,IDistributedFileTransaction *transaction)
- {
- if (!user)
- {
- #ifdef NULL_DALIUSER_STACKTRACE
- DBGLOG("UNEXPECTED USER (NULL) in dadfs.cpp CDistributedFileDirectory::renamePhysical %d",__LINE__);
- //following debug code to be removed
- PrintStackReport();
- #endif
- user = defaultudesc.get();
- }
- CDfsLogicalFileName oldlogicalname;
- oldlogicalname.set(oldname);
- checkLogicalName(oldlogicalname,user,true,true,false,"rename");
- // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
- Linked<IDistributedFileTransactionExt> localtrans;
- if (transaction)
- {
- IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
- localtrans.set(_transaction);
- }
- else
- localtrans.setown(new CDistributedFileTransaction(user));
- CRenameFileAction *action = new CRenameFileAction(this, user, oldname, newname);
- localtrans->addAction(action); // takes ownership
- localtrans->autoCommit();
- }
- void CDistributedFileDirectory::fixDates(IDistributedFile *file)
- {
- // should do in parallel
- unsigned width = file->numParts();
- CriticalSection crit;
- class casyncfor: public CAsyncFor
- {
- IDistributedFile *file;
- CriticalSection &crit;
- unsigned width;
- public:
- bool ok;
- casyncfor(IDistributedFile *_file,unsigned _width,CriticalSection &_errcrit)
- : crit(_errcrit)
- {
- file = _file;
- ok = true;
- width = _width;
- ok = true;
- }
- void Do(unsigned i)
- {
- CriticalBlock block(crit);
- Owned<IDistributedFilePart> part = file->getPart(i);
- CDateTime dt;
- if (!part->getModifiedTime(false,false,dt))
- return;
- unsigned nc = part->numCopies();
- for (unsigned copy = 0; copy < nc; copy++) {
- RemoteFilename rfn;
- part->getFilename(rfn,copy);
- Owned<IFile> partfile = createIFile(rfn);
- try {
- CriticalUnblock unblock(crit);
- CDateTime dt2;
- if (partfile->getTime(NULL,&dt2,NULL)) {
- if (!dt.equals(dt2)) {
- partfile->setTime(NULL,&dt,NULL);
- }
- }
- }
- catch (IException *e) {
- CriticalBlock block(crit);
- StringBuffer s("Failed to find file part ");
- s.append(partfile->queryFilename()).append(" on ");
- rfn.queryEndpoint().getUrlStr(s);
- EXCLOG(e, s.str());
- e->Release();
- }
- }
- }
- } afor(file,width,crit);
- afor.For(width,10,false,true);
- }
- void CDistributedFileDirectory::addEntry(CDfsLogicalFileName &dlfn,IPropertyTree *root,bool superfile, bool ignoreexists)
- {
- // add bit awkward
- bool external;
- bool foreign;
- external = dlfn.isExternal();
- foreign = dlfn.isForeign();
- if (external) {
- root->Release();
- return; // ignore attempts to add external
- }
- CScopeConnectLock sconnlock("CDistributedFileDirectory::addEntry", dlfn, true, false, false, defaultTimeout);
- if (!sconnlock.conn()) {// warn?
- root->Release();
- return;
- }
- IPropertyTree* sroot = sconnlock.conn()->queryRoot();
- StringBuffer tail;
- dlfn.getTail(tail);
- IPropertyTree *prev = getNamedPropTree(sroot,superfile?queryDfsXmlBranchName(DXB_SuperFile):queryDfsXmlBranchName(DXB_File),"@name",tail.str(),false);
- if (!prev) // check super/file doesn't exist
- prev = getNamedPropTree(sroot,superfile?queryDfsXmlBranchName(DXB_File):queryDfsXmlBranchName(DXB_SuperFile),"@name",tail.str(),false);
- if (prev!=nullptr)
- {
- prev->Release();
- root->Release();
- if (ignoreexists)
- return;
- throw new CDFS_Exception(DFSERR_LogicalNameAlreadyExists,dlfn.get());
- }
- root->setProp("@name",tail.str());
- root->setProp("OrigName",dlfn.get());
- if (superfile)
- sroot->addPropTree(queryDfsXmlBranchName(DXB_SuperFile), root); // now owns root
- else
- {
- IPropertyTree *file = sroot->addPropTree(queryDfsXmlBranchName(DXB_File), root); // now owns root
- file->setPropTree("ClusterLock", createPTree());
- }
- }
- IDistributedFileIterator *CDistributedFileDirectory::getIterator(const char *wildname, bool includesuper, IUserDescriptor *user)
- {
- return new CDistributedFileIterator(this,wildname,includesuper,user);
- }
- GetFileClusterNamesType CDistributedFileDirectory::getFileClusterNames(const char *_logicalname,StringArray &out)
- {
- CDfsLogicalFileName logicalname;
- logicalname.set(_logicalname);
- if (logicalname.isForeign())
- return GFCN_Foreign;
- if (logicalname.isExternal())
- return GFCN_External;
- CScopeConnectLock sconnlock("CDistributedFileDirectory::getFileClusterList", logicalname, false, false, false, defaultTimeout);
- DfsXmlBranchKind bkind;
- IPropertyTree *froot = sconnlock.queryFileRoot(logicalname,bkind);
- if (froot) {
- if (bkind==DXB_File) {
- getFileGroups(froot,out);
- return GFCN_Normal;
- }
- if (bkind==DXB_SuperFile)
- return GFCN_Super;
- }
- return GFCN_NotFound;
- }
- // --------------------------------------------------------
- static CDistributedFileDirectory *DFdir = NULL;
- static CriticalSection dfdirCrit;
- /**
- * Public method to control DistributedFileDirectory access
- * as a singleton. This is the only way to get directories,
- * files, super-files and logic-files.
- */
- IDistributedFileDirectory &queryDistributedFileDirectory()
- {
- if (!DFdir) {
- CriticalBlock block(dfdirCrit);
- if (!DFdir)
- DFdir = new CDistributedFileDirectory();
- }
- return *DFdir;
- }
- /**
- * Shutdown distributed file system (root directory).
- */
- void closedownDFS() // called by dacoven
- {
- CriticalBlock block(dfdirCrit);
- try {
- delete DFdir;
- }
- catch (IMP_Exception *e) {
- if (e->errorCode()!=MPERR_link_closed)
- throw;
- PrintExceptionLog(e,"closedownDFS");
- e->Release();
- }
- catch (IDaliClient_Exception *e) {
- if (e->errorCode()!=DCERR_server_closed)
- throw;
- e->Release();
- }
- DFdir = NULL;
- CriticalBlock block2(groupsect);
- ::Release(groupStore);
- groupStore = NULL;
- }
- class CDFPartFilter : implements IDFPartFilter, public CInterface
- {
- protected:
- bool *partincluded;
- unsigned max;
- public:
- IMPLEMENT_IINTERFACE;
- CDFPartFilter(const char *filter)
- {
- max = 0;
- partincluded = NULL;
- unsigned pn=0;
- const char *s=filter;
- if (!s)
- return;
- while (*s) {
- if (isdigit(*s)) {
- pn = pn*10+(*s-'0');
- if (pn>max)
- max = pn;
- }
- else
- pn = 0;
- s++;
- }
- if (max==0)
- return;
- partincluded = new bool[max];
- unsigned i;
- for (i=0;i<max;i++)
- partincluded[i] = false;
- pn=0;
- s=filter;
- unsigned start=0;
- for (;;) {
- if ((*s==0)||(*s==',')||isspace(*s)) {
- if (start) {
- for (i=start-1;i<pn;i++)
- partincluded[i] = true;
- start = 0;
- }
- else
- partincluded[pn-1] = true;
- if (*s==0)
- break;
- pn = 0;
- }
- else if (isdigit(*s)) {
- pn = pn*10+(*s-'0');
- if (pn>max)
- max = pn;
- if (s[1]=='-') {
- s++;
- start = pn;
- pn = 0;
- }
- }
- s++;
- }
- }
- ~CDFPartFilter()
- {
- delete [] partincluded;
- }
- bool includePart(unsigned part)
- {
- if (max==0)
- return true;
- if (part>=max)
- return false;
- return partincluded[part];
- };
- };
- IDFPartFilter *createPartFilter(const char *filter)
- {
- return new CDFPartFilter(filter);
- }
- //=====================================================================================
- // Server Side Support
- class CFileMatch : public CInterface
- {
- StringAttr name;
- Linked<IPropertyTree> tree;
- bool isSuper;
- public:
- CFileMatch(const char *_name, IPropertyTree *_tree, bool _isSuper) : name(_name), tree(_tree), isSuper(_isSuper)
- {
- }
- IPropertyTree &queryFileTree() const { return *tree; }
- const char *queryName() const { return name; }
- bool queryIsSuper() const { return isSuper; }
- };
- typedef CIArrayOf<CFileMatch> CFileMatchArray;
- class CScope : public CInterface
- {
- StringAttr name;
- CIArrayOf<CFileMatch> files; // matches
- CIArrayOf<CScope> subScopes;
- public:
- CScope(const char *_name) : name(_name)
- {
- }
- const char *getName() const { return name; }
- void addMatch(const char *name, IPropertyTree &fileTree, bool isSuper)
- {
- files.append(*new CFileMatch(name, &fileTree, isSuper));
- }
- CScope *addScope(const char *scope)
- {
- CScope *subScope = new CScope(scope);
- subScopes.append(*subScope);
- return subScope;
- }
- void popLastScope()
- {
- subScopes.pop();
- }
- CIArrayOf<CScope> &querySubScopes() { return subScopes; }
- CFileMatchArray &queryFiles() { return files; }
- };
- typedef CIArrayOf<CScope> CScopeArray;
- const char* DFUQFilterFieldNames[] = { "", "@description", "@directory", "@group", "@modified", "@name", "@numclusters", "@numparts",
- "@partmask", "@OrigName", "Attr", "Attr/@job", "Attr/@owner", "Attr/@recordCount", "Attr/@recordSize", "Attr/@size",
- "Attr/@compressedsize", "Attr/@workunit", "Cluster", "Cluster/@defaultBaseDir", "Cluster/@defaultReplDir", "Cluster/@mapFlags",
- "Cluster/@name", "Part", "Part/@name", "Part/@num", "Part/@size", "SuperOwner", "SuperOwner/@name",
- "SubFile", "SubFile/@name", "SubFile/@num", "Attr/@kind" };
- extern da_decl const char* getDFUQFilterFieldName(DFUQFilterField feild)
- {
- return DFUQFilterFieldNames[feild];
- }
- class CDFUSFFilter : public CInterface
- {
- DFUQFilterType filterType;
- StringAttr attrPath;
- bool hasFilter;
- bool hasFilterHigh;
- StringAttr filterValue;
- StringAttr filterValueHigh;
- int filterValueInt;
- int filterValueHighInt;
- __int64 filterValueInt64;
- __int64 filterValueHighInt64;
- bool filterValueBoolean;
- StringAttr sep;
- StringArray filterArray;
- public:
- CDFUSFFilter(DFUQFilterType _filterType, const char *_attrPath, const char *_filterValue, const char *_filterValueHigh)
- : filterType(_filterType), attrPath(_attrPath), filterValue(_filterValue), filterValueHigh(_filterValueHigh) {};
- CDFUSFFilter(DFUQFilterType _filterType, const char *_attrPath, bool _hasFilter, const int _filterValue, bool _hasFilterHigh, const int _filterValueHigh)
- : filterType(_filterType), attrPath(_attrPath), hasFilter(_hasFilter), filterValueInt(_filterValue), hasFilterHigh(_hasFilterHigh), filterValueHighInt(_filterValueHigh) {};
- CDFUSFFilter(DFUQFilterType _filterType, const char *_attrPath, bool _hasFilter, const __int64 _filterValue, bool _hasFilterHigh, const __int64 _filterValueHigh)
- : filterType(_filterType), attrPath(_attrPath), hasFilter(_hasFilter), filterValueInt64(_filterValue), hasFilterHigh(_hasFilterHigh), filterValueHighInt64(_filterValueHigh) {};
- CDFUSFFilter(DFUQFilterType _filterType, const char *_attrPath, bool _filterValue)
- : filterType(_filterType), attrPath(_attrPath), filterValueBoolean(_filterValue) {};
- CDFUSFFilter(DFUQFilterType _filterType, const char *_attrPath, const char *_filterValue, const char *_sep, StringArray& _filterArray)
- : filterType(_filterType), attrPath(_attrPath), filterValue(_filterValue), sep(_sep)
- {
- ForEachItemIn(i,_filterArray)
- {
- const char* filter = _filterArray.item(i);
- if (filter && *filter)
- filterArray.append(filter);
- }
- };
- DFUQFilterType getFilterType() { return filterType;}
- const char * getAttrPath() { return attrPath.get();}
- const char * getFilterValue() { return filterValue.get();}
- const char * getFilterValueHigh() { return filterValueHigh.get();}
- const int getFilterValueInt() { return filterValueInt;}
- const int getFilterValueHighInt() { return filterValueHighInt;}
- const __int64 getFilterValueInt64() { return filterValueInt64;}
- const __int64 getFilterValueHighInt64() { return filterValueHighInt64;}
- const bool getFilterValueBoolean() { return filterValueBoolean;}
- const char * getSep() { return sep.get();}
- void getFilterArray(StringArray &filters)
- {
- ForEachItemIn(c, filterArray)
- filters.append(filterArray.item(c));
- }
- bool checkFilter(IPropertyTree &file)
- {
- bool match = true;
- switch(filterType)
- {
- case DFUQFTwildcardMatch:
- match = doWildMatch(file);
- break;
- case DFUQFTbooleanMatch:
- match = doBooleanMatch(file);
- break;
- case DFUQFThasProp:
- match = checkHasPropFilter(file);
- break;
- case DFUQFTcontainString:
- match = checkContainStringFilter(file);
- break;
- case DFUQFTstringRange:
- match = checkStringRangeFilter(file);
- break;
- case DFUQFTintegerRange:
- match = checkIntegerRangeFilter(file);
- break;
- case DFUQFTinteger64Range:
- match = checkInteger64RangeFilter(file);
- break;
- }
- return match;
- }
- bool doWildMatch(IPropertyTree &file)
- {
- const char* filter = filterValue.get();
- if (!attrPath.get() || !filter || !*filter || streq(filter, "*"))
- return true;
- const char* prop = file.queryProp(attrPath.get());
- if (prop && WildMatch(prop, filter, true))
- return true;
- return false;
- }
- bool doBooleanMatch(IPropertyTree &file)
- {
- if (!attrPath.get())
- return true;
- return filterValueBoolean == file.getPropBool(attrPath.get(), true);
- }
- bool checkHasPropFilter(IPropertyTree &file)
- {
- if (!attrPath.get())
- return true;
- return filterValueBoolean == file.hasProp(attrPath.get());
- }
- bool checkContainStringFilter(IPropertyTree &file)
- {
- if (!attrPath.get())
- return true;
- const char* prop = file.queryProp(attrPath.get());
- if (!prop || !*prop)
- return false;
- bool found = false;
- if (!sep.get())
- {
- if (filterArray.find(prop) != NotFound) //Match with one of values in the filter
- found = true;
- return found;
- }
- StringArray propArray;
- propArray.appendListUniq(prop, sep.get());
- ForEachItemIn(i,propArray)
- {
- const char* value = propArray.item(i);
- if (!value || !*value)
- continue;
- if (filterArray.find(value) != NotFound) //Match with one of values in the filter
- {
- found = true;
- break;
- }
- }
- return found;
- }
- bool checkStringRangeFilter(IPropertyTree &file)
- {
- if (!attrPath.get())
- return true;
- const char* prop = file.queryProp(attrPath.get());
- if (!prop || !*prop)
- return false;
- if (filterValue && (strcmp(filterValue, prop) > 0))
- return false;
- if (filterValueHigh && (strcmp(filterValueHigh, prop) < 0))
- return false;
- return true;
- }
- bool checkIntegerRangeFilter(IPropertyTree &file)
- {
- if (!attrPath.get())
- return true;
- int prop = file.getPropInt(attrPath.get());
- if (hasFilter && (prop < filterValueInt))
- return false;
- if (hasFilterHigh && (prop > filterValueHighInt))
- return false;
- return true;
- }
- bool checkInteger64RangeFilter(IPropertyTree &file)
- {
- if (!attrPath.get())
- return true;
- __int64 prop = file.getPropInt64(attrPath.get());
- if (hasFilter && (prop < filterValueInt64))
- return false;
- if (hasFilterHigh && (prop > filterValueHighInt64))
- return false;
- return true;
- }
- };
- typedef CIArrayOf<CDFUSFFilter> CDFUSFFilterArray;
- class CIterateFileFilterContainer : public CInterface
- {
- StringAttr filterBuf; //Hold original filter string just in case
- StringAttr wildNameFilter;
- unsigned maxFilesFilter;
- DFUQFileTypeFilter fileTypeFilter;
- CIArrayOf<CDFUSFFilter> filters;
- //The 'filters' contains the file scan filters other than wildNameFilter and fileTypeFilter. Those filters are used for
- //filtering the files using File Attributes tree and CDFUSFFilter::checkFilter(). The wildNameFilter and fileTypeFilter need
- //special code to filter the files.
- SerializeFileAttrOptions options;
- bool isValidInteger(const char *s)
- {
- if (!s || !*s)
- return false;
- while (*s)
- {
- if ((*s != '-') && !isdigit(*s))
- return false;
- s++;
- }
- return true;
- }
- void addOption(const char* optionStr)
- {
- if (!optionStr || !*optionStr || !isdigit(*optionStr))
- return;
- DFUQSerializeFileAttrOption option = (DFUQSerializeFileAttrOption) atoi(optionStr);
- switch(option)
- {
- case DFUQSFAOincludeSuperOwner:
- options.includeSuperOwner = true;
- break;
- //Add more when needed
- }
- }
- void addFilter(DFUQFilterType filterType, const char* attr, const char* value, const char* valueHigh)
- {
- if (!attr || !*attr)
- return;
- if ((DFUQFTwildcardMatch == filterType) || (DFUQFTstringRange == filterType))
- {
- filters.append(*new CDFUSFFilter(filterType, attr, value, valueHigh));
- return;
- }
- if ((DFUQFTbooleanMatch == filterType) || (DFUQFThasProp == filterType))
- {
- bool filter = true;
- if (value && (streq(value, "0") || strieq(value, "false")))
- filter = false;
- filters.append(*new CDFUSFFilter(filterType, attr, filter));
- return;
- }
- if ((DFUQFTintegerRange == filterType) || (DFUQFTinteger64Range == filterType))
- {
- bool hasFilter = false;
- bool hasFilterHigh = false;
- if (value && isValidInteger(value))
- hasFilter = true;
- if (valueHigh && isValidInteger(valueHigh))
- hasFilterHigh = true;
- if (!hasFilter && !hasFilterHigh)
- return;
- if (DFUQFTintegerRange == filterType)
- filters.append(*new CDFUSFFilter(filterType, attr, hasFilter, atoi(value), hasFilterHigh, atoi(valueHigh)));
- else
- filters.append(*new CDFUSFFilter(filterType, attr, hasFilter, (__int64) atol(value), hasFilterHigh, (__int64) atol(valueHigh)));
- return;
- }
- }
- void addFilterArray(DFUQFilterType filterType, const char* attr, const char* value, const char* sep)
- {
- if (!attr || !*attr || !value || !*value)
- return;
- StringArray filterArray;
- filterArray.appendListUniq(value, sep);
- filters.append(*new CDFUSFFilter(filterType, attr, value, sep, filterArray));
- }
- void addSpecialFilter(const char* attr, const char* value)
- {
- if (!attr || !*attr || !value || !*value)
- return;
- if (!isdigit(*attr))
- {
- PROGLOG("Unsupported Special Filter: %s", attr);
- return;
- }
- DFUQSpecialFilter filterName = (DFUQSpecialFilter) atoi(attr);
- switch(filterName)
- {
- case DFUQSFFileNameWithPrefix:
- wildNameFilter.set(value);
- break;
- case DFUQSFFileType:
- if (isdigit(*value))
- fileTypeFilter = (DFUQFileTypeFilter) atoi(value);
- else
- PROGLOG("Unsupported Special Filter: %s, value %s", attr, value);
- break;
- case DFUQSFMaxFiles:
- if (isdigit(*value))
- maxFilesFilter = atoi(value);
- else
- PROGLOG("Unsupported Special Filter: %s, value %s", attr, value);
- break;
- default:
- PROGLOG("Unsupported Special Filter: %d", filterName);
- break;
- }
- }
- bool doWildMatch(const char* filter, const char* value)
- {
- if (!filter || !*filter || streq(filter, "*") || (value && WildMatch(value, filter, true)))
- return true;
- return false;
- }
- public:
- CIterateFileFilterContainer()
- {
- maxFilesFilter = ITERATE_FILTEREDFILES_LIMIT;
- fileTypeFilter = DFUQFFTall;
- wildNameFilter.set("*");
- filterBuf.clear();
- };
- void readFilters(const char *filterStr)
- {
- if (!filterStr || !*filterStr)
- return;
- filterBuf.set(filterStr);
- StringArray filterStringArray;
- char sep[] = { DFUQFilterSeparator, '\0' };
- filterStringArray.appendList(filterStr, sep);
- unsigned filterFieldsToRead = filterStringArray.length();
- ForEachItemIn(i,filterStringArray)
- {
- const char* filterTypeStr = filterStringArray.item(i);
- if (!filterTypeStr || !*filterTypeStr)
- continue;
- if (!isdigit(*filterTypeStr))
- continue;
- unsigned filterSize = 4;
- DFUQFilterType filterType = (DFUQFilterType) atoi(filterTypeStr);
- switch(filterType)
- {
- case DFUQFTcontainString:
- if (filterFieldsToRead >= filterSize) //DFUQFilterType | filter name | separator | filter value separated by the separator
- addFilterArray(DFUQFTcontainString, filterStringArray.item(i+1), (const char*)filterStringArray.item(i+2), (const char*)filterStringArray.item(i+3));
- break;
- case DFUQFThasProp:
- case DFUQFTbooleanMatch:
- case DFUQFTwildcardMatch:
- filterSize = 3;
- if (filterFieldsToRead >= filterSize) //DFUQFilterType | filter name | filter value
- addFilter(filterType, filterStringArray.item(i+1), (const char*)filterStringArray.item(i+2), NULL);
- break;
- case DFUQFTstringRange:
- case DFUQFTintegerRange:
- case DFUQFTinteger64Range:
- if (filterFieldsToRead >= filterSize) //DFUQFilterType | filter name | from filter | to filter
- addFilter(filterType, filterStringArray.item(i+1), (const char*)filterStringArray.item(i+2), (const char*)filterStringArray.item(i+3));
- break;
- case DFUQFTincludeFileAttr:
- filterSize = 2;
- if (filterFieldsToRead >= filterSize) //DFUQFilterType | filter
- addOption(filterStringArray.item(i+1));
- break;
- case DFUQFTspecial:
- filterSize = 3;
- if (filterFieldsToRead >= filterSize) //DFUQFilterType | filter name | filter value
- addSpecialFilter(filterStringArray.item(i+1), (const char*)filterStringArray.item(i+2));
- break;
- }
- filterFieldsToRead -= filterSize;
- i += (filterSize - 1);
- }
- }
- bool matchFileScanFilter(const char* name, IPropertyTree &file)
- {
- if (!doWildMatch(wildNameFilter.get(), name))
- return false;
- if (!filters.length())
- return true;
- ForEachItemIn(i,filters)
- {
- CDFUSFFilter &filter = filters.item(i);
- const char* attrPath = filter.getAttrPath();
- try
- {
- if (!filter.checkFilter(file))
- return false;
- }
- catch (IException *e)
- {
- VStringBuffer msg("Failed to check filter %s for %s: ", attrPath, name);
- int code = e->errorCode();
- e->errorMessage(msg);
- e->Release();
- throw MakeStringException(code, "%s", msg.str());
- }
- }
- return true;
- }
- DFUQFileTypeFilter getFileTypeFilter() { return fileTypeFilter; }
- unsigned getMaxFilesFilter() { return maxFilesFilter; }
- void setFileTypeFilter(DFUQFileTypeFilter _fileType)
- {
- fileTypeFilter = _fileType;
- }
- const char* getNameFilter() { return wildNameFilter.get(); }
- void setNameFilter(const char* _wildName)
- {
- if (!_wildName || !*_wildName)
- return;
- wildNameFilter.set(_wildName);
- }
- SerializeFileAttrOptions& getSerializeFileAttrOptions() { return options; }
- };
- class CFileScanner
- {
- bool recursive;
- bool includesuper;
- StringAttr wildname;
- Owned<CScope> topLevelScope;
- CScope *currentScope;
- Owned<CIterateFileFilterContainer> iterateFileFilterContainer;
- bool scopeMatch(const char *name)
- { // name has trailing '::'
- if (!name || !*name)
- return true;
- const char *s1 = NULL;
- if (!iterateFileFilterContainer)
- s1 = wildname.get();
- else
- s1 = iterateFileFilterContainer->getNameFilter();
- if (!s1 || !*s1)
- return true;
- const char *s2 = name;
- while (*s2) {
- if (*s1=='*') {
- if (recursive)
- return true;
- if (*s2==':')
- return false;
- // '*' can only come at end of scope in non-recursive
- while (*s1&&(*s1!=':'))
- s1++;
- while (*s2&&(*s2!=':'))
- s2++;
- }
- else if ((*s1==*s2)||(*s1=='?')) {
- s1++;
- s2++;
- }
- else
- return false;
- }
- return true;
- }
- bool processScopes(IPropertyTree &root,StringBuffer &name)
- {
- bool ret = false;
- CScope *parentScope = currentScope;
- if (parentScope)
- currentScope = parentScope->addScope(name);
- else
- { // once only
- topLevelScope.setown(new CScope(""));
- currentScope = topLevelScope;
- }
- size32_t ns = name.length();
- if (ns)
- name.append("::");
- size32_t ns2 = name.length();
- if (scopeMatch(name.str())) {
- Owned<IPropertyTreeIterator> iter = root.getElements(queryDfsXmlBranchName(DXB_Scope));
- if (iter->first()) {
- do {
- IPropertyTree &scope = iter->query();
- if (scope.hasChildren()) {
- name.append(scope.queryProp("@name"));
- ret |= processScopes(scope, name);
- name.setLength(ns2);
- }
- } while (iter->next());
- }
- if (!iterateFileFilterContainer)
- ret |= processFiles(root,name);
- else
- ret |= processFilesWithFilters(root,name);
- }
- if (!ret && parentScope)
- parentScope->popLastScope(); // discard scopes where no matches
- currentScope = parentScope;
- name.setLength(ns);
- return ret;
- }
- bool processFiles(IPropertyTree &root,StringBuffer &name)
- {
- bool ret = false;
- const char *s1 = wildname.get();
- size32_t ns = name.length();
- Owned<IPropertyTreeIterator> iter = root.getElements(queryDfsXmlBranchName(DXB_File));
- if (iter->first()) {
- IPropertyTree &scope = iter->query();
- do {
- IPropertyTree &file = iter->query();
- name.append(file.queryProp("@name"));
- if (!s1||WildMatch(name.str(),s1,true)) {
- currentScope->addMatch(name,file,false);
- ret = true;
- }
- name.setLength(ns);
- } while (iter->next());
- }
- if (includesuper) {
- iter.setown(root.getElements(queryDfsXmlBranchName(DXB_SuperFile)));
- if (iter->first()) {
- do {
- IPropertyTree &file = iter->query();
- name.append(file.queryProp("@name"));
- if (!s1||WildMatch(name.str(),s1,true)) {
- currentScope->addMatch(name,file,true);
- ret = true;
- }
- name.setLength(ns);
- } while (iter->next());
- }
- }
- return ret;
- }
- bool processFilesWithFilters(IPropertyTree &root, StringBuffer &name)
- {
- bool ret = false;
- size32_t ns = name.length();
- DFUQFileTypeFilter fileTypeFilter = iterateFileFilterContainer->getFileTypeFilter();
- if (fileTypeFilter != DFUQFFTsuperfileonly)
- addMatchedFiles(root.getElements(queryDfsXmlBranchName(DXB_File)), false, name, ns, ret);
- if ((fileTypeFilter == DFUQFFTall) || (fileTypeFilter == DFUQFFTsuperfileonly))
- addMatchedFiles(root.getElements(queryDfsXmlBranchName(DXB_SuperFile)), true, name, ns, ret);
- return ret;
- }
- void addMatchedFiles(IPropertyTreeIterator* files, bool isSuper, StringBuffer &name, size32_t ns, bool& ret)
- {
- Owned<IPropertyTreeIterator> iter = files;
- ForEach(*iter)
- {
- IPropertyTree &file = iter->query();
- name.append(file.queryProp("@name"));
- if (iterateFileFilterContainer->matchFileScanFilter(name.str(), file))
- {
- currentScope->addMatch(name,file,isSuper);
- ret = true;
- }
- name.setLength(ns);
- }
- }
- public:
- void scan(IPropertyTree *sroot, const char *_wildname,bool _recursive,bool _includesuper)
- {
- if (_wildname)
- wildname.set(_wildname);
- else
- wildname.clear();
- recursive = _recursive;
- includesuper = _includesuper;
- StringBuffer name;
- topLevelScope.clear();
- currentScope = NULL;
- processScopes(*sroot->queryPropTree(querySdsFilesRoot()),name);
- }
- void scan(IPropertyTree *sroot, CIterateFileFilterContainer* _iterateFileFilterContainer, bool _recursive)
- {
- iterateFileFilterContainer.setown(_iterateFileFilterContainer);
- recursive = _recursive;
- StringBuffer name;
- topLevelScope.clear();
- currentScope = NULL;
- processScopes(*sroot->queryPropTree(querySdsFilesRoot()),name);
- }
- void _getResults(bool auth, IUserDescriptor *user, CScope &scope, CFileMatchArray &matchingFiles, StringArray &authScopes,
- unsigned &count, bool checkFileCount)
- {
- if (auth)
- {
- SecAccessFlags perm = getScopePermissions(scope.getName(),user,0); // don't audit
- if (!HASREADPERMISSION(perm))
- return;
- authScopes.append(scope.getName());
- }
- CFileMatchArray &files = scope.queryFiles();
- ForEachItemIn(f, files)
- {
- if (checkFileCount && (count == iterateFileFilterContainer->getMaxFilesFilter()))
- throw MakeStringException(DFSERR_PassIterateFilesLimit, "CFileScanner::_getResults() found >%d files.",
- iterateFileFilterContainer->getMaxFilesFilter());
- CFileMatch *match = &files.item(f);
- matchingFiles.append(*LINK(match));
- ++count;
- }
- CScopeArray &subScopes = scope.querySubScopes();
- ForEachItemIn(s, subScopes)
- {
- CScope &subScope = subScopes.item(s);
- _getResults(auth, user, subScope, matchingFiles, authScopes, count, checkFileCount);
- }
- }
- unsigned getResults(bool auth, IUserDescriptor *user, CFileMatchArray &matchingFiles, StringArray &authScopes, unsigned &count, bool checkFileCount)
- {
- _getResults(auth, user, *topLevelScope, matchingFiles, authScopes, count, checkFileCount);
- return count;
- }
- };
- struct CMachineEntry: public CInterface
- {
- CMachineEntry(const char *_mname,SocketEndpoint _ep)
- : mname(_mname),ep(_ep)
- {
- }
- StringAttr mname;
- SocketEndpoint ep;
- };
- typedef CMachineEntry *CMachineEntryPtr;
- typedef MapStringTo<CMachineEntryPtr> CMachineEntryMap;
- StringBuffer &getClusterGroupName(IPropertyTree &cluster, StringBuffer &groupName)
- {
- const char *name = cluster.queryProp("@name");
- const char *nodeGroupName = cluster.queryProp("@nodeGroup");
- if (nodeGroupName && *nodeGroupName)
- name = nodeGroupName;
- groupName.append(name);
- return groupName.trim().toLowerCase();
- }
- StringBuffer &getClusterSpareGroupName(IPropertyTree &cluster, StringBuffer &groupName)
- {
- return getClusterGroupName(cluster, groupName).append("_spares");
- }
- // JCSMORE - dfs group handling may be clearer if in own module
- class CInitGroups
- {
- CMachineEntryMap machinemap;
- CIArrayOf<CMachineEntry> machinelist;
- CConnectLock groupsconnlock;
- StringArray clusternames;
- unsigned defaultTimeout;
- bool machinesLoaded;
- GroupType getGroupType(const char *type)
- {
- if (0 == strcmp("ThorCluster", type))
- return grp_thor;
- else if (0 == strcmp("RoxieCluster", type))
- return grp_roxie;
- else
- throwUnexpected();
- }
- bool clusterGroupCompare(IPropertyTree *newClusterGroup, IPropertyTree *oldClusterGroup)
- {
- if (!newClusterGroup && !oldClusterGroup)
- return true; // i.e. both missing, so match
- else if (!newClusterGroup || !oldClusterGroup)
- return false; // i.e. one of them (not both) missing, so mismatch
- // else // neither missing
- // see if identical
- const char *oldKind = oldClusterGroup->queryProp("@kind");
- const char *oldDir = oldClusterGroup->queryProp("@dir");
- const char *newKind = newClusterGroup->queryProp("@kind");
- const char *newDir = newClusterGroup->queryProp("@dir");
- if (oldKind) {
- if (newKind) {
- if (!streq(newKind, newKind))
- return false;
- }
- else
- return false;
- }
- else if (newKind)
- return false;
- if (oldDir) {
- if (newDir) {
- if (!streq(newDir,oldDir))
- return false;
- }
- else
- return false;
- }
- else if (NULL!=newDir)
- return false;
- unsigned oldGroupCount = oldClusterGroup->getCount("Node");
- unsigned newGroupCount = newClusterGroup->getCount("Node");
- if (oldGroupCount != newGroupCount)
- return false;
- if (0 == newGroupCount)
- return true;
- Owned<IPropertyTreeIterator> newIter = newClusterGroup->getElements("Node");
- Owned<IPropertyTreeIterator> oldIter = oldClusterGroup->getElements("Node");
- if (newIter->first() && oldIter->first()) {
- for (;;) {
- const char *oldIp = oldIter->query().queryProp("@ip");
- const char *newIp = newIter->query().queryProp("@ip");
- if (!streq(oldIp, newIp))
- return false;
- if (!oldIter->next() || !newIter->next())
- break;
- }
- }
- return true;
- }
- void addClusterGroup(const char *name, IPropertyTree *newClusterGroup, bool realCluster)
- {
- VStringBuffer prop("Group[@name=\"%s\"]", name);
- IPropertyTree *root = groupsconnlock.conn->queryRoot();
- IPropertyTree *old = root->queryPropTree(prop.str());
- if (old) {
- // JCSMORE
- // clone
- // iterate through files and point to clone
- // i) if change is minor, worth swapping to new group anyway?
- // ii) if old group has machines that are no longer in new environment, mark file bad?
- root->removeTree(old);
- }
- if (!newClusterGroup)
- return;
- if (realCluster)
- clusternames.append(name);
- IPropertyTree *grp = root->addPropTree("Group", newClusterGroup);
- grp->setProp("@name", name);
- }
- IGroup *getGroupFromCluster(GroupType groupType, IPropertyTree &cluster, bool expand)
- {
- SocketEndpointArray eps;
- const char *processName=NULL;
- switch (groupType)
- {
- case grp_thor:
- processName = "ThorSlaveProcess";
- break;
- case grp_thorspares:
- processName = "ThorSpareProcess";
- break;
- case grp_roxie:
- processName = "RoxieServerProcess";
- break;
- default:
- throwUnexpected();
- }
- SocketEndpoint nullep;
- Owned<IPropertyTreeIterator> nodes = cluster.getElements(processName);
- ForEach(*nodes)
- {
- IPropertyTree &node = nodes->query();
- SocketEndpoint ep;
- const char *computer = node.queryProp("@computer");
- const char *netAddress = node.queryProp("@netAddress");
- if (computer && *computer)
- {
- CMachineEntryPtr *m = machinemap.getValue(computer);
- if (!m)
- {
- OERRLOG("Cannot construct %s, computer name %s not found\n", cluster.queryProp("@name"), computer);
- return NULL;
- }
- ep.set((*m)->ep);
- }
- else if (netAddress && *netAddress)
- {
- ep.set(netAddress, 0);
- }
- else
- {
- OERRLOG("Cannot construct %s, missing computer spec on node\n", cluster.queryProp("@name"));
- return NULL;
- }
- switch (groupType)
- {
- case grp_roxie:
- // Redundant copies are located via the flags.
- // Old environments may contain duplicated sever information for multiple ports
- eps.appendUniq(ep);
- break;
- case grp_thor:
- case grp_thorspares:
- eps.append(ep);
- break;
- default:
- throwUnexpected();
- }
- }
- if (!eps.ordinality())
- return NULL;
- Owned<IGroup> grp;
- if (grp_thor != groupType)
- expand = false;
- if (expand)
- {
- unsigned slavesPerNode = cluster.getPropInt("@slavesPerNode", 1);
- unsigned channelsPerSlave = cluster.getPropInt("@channelsPerSlave", 1);
- SocketEndpointArray msEps;
- for (unsigned s=0; s<(slavesPerNode*channelsPerSlave); s++)
- {
- ForEachItemIn(e, eps)
- msEps.append(eps.item(e));
- }
- grp.setown(createIGroup(msEps));
- }
- else
- grp.setown(createIGroup(eps));
- return grp.getClear();
- }
- bool loadMachineMap()
- {
- if (machinesLoaded)
- return true;
- //GH->JCS This can't be changed to use getEnvironmentFactory() unless that moved inside dalibase;
- Owned<IRemoteConnection> conn = querySDS().connect("/Environment/Hardware", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
- if (!conn) {
- IWARNLOG("Cannot connect to /Environment/Hardware");
- return false;
- }
- IPropertyTree* root = conn->queryRoot();
- Owned<IPropertyTreeIterator> machines= root->getElements("Computer");
- ForEach(*machines) {
- IPropertyTree &machine = machines->query();
- SocketEndpoint ep(machine.queryProp("@netAddress"));
- const char *name = machine.queryProp("@name");
- CMachineEntry *entry = new CMachineEntry(name,ep);
- machinemap.setValue(name, entry);
- machinelist.append(*entry);
- }
- machinesLoaded = true;
- return true;
- }
- IPropertyTree *createClusterGroup(GroupType groupType, IGroup *group, const char *dir, bool realCluster)
- {
- Owned<IPropertyTree> cluster = createPTree("Group");
- if (realCluster)
- cluster->setPropBool("@cluster", true);
- const char *kind=NULL;
- switch (groupType) {
- case grp_thor:
- kind = "Thor";
- break;
- case grp_roxie:
- kind = "Roxie";
- break;
- case grp_hthor:
- kind = "hthor";
- break;
- }
- if (kind)
- cluster->setProp("@kind",kind);
- if (dir)
- cluster->setProp("@dir",dir);
- Owned<INodeIterator> iter = group->getIterator();
- StringBuffer str;
- ForEach(*iter) {
- iter->query().endpoint().getIpText(str.clear());
- IPropertyTree *n = createPTree("Node");
- n->setProp("@ip",str.str());
- cluster->addPropTree("Node", n);
- }
- return cluster.getClear();
- }
- IPropertyTree *createClusterGroupFromEnvCluster(GroupType groupType, IPropertyTree &cluster, const char *dir, bool realCluster)
- {
- Owned<IGroup> group = getGroupFromCluster(groupType, cluster, true);
- if (!group)
- return NULL;
- return createClusterGroup(groupType, group, dir, realCluster);
- }
- bool constructGroup(IPropertyTree &cluster, const char *altName, IPropertyTree *oldEnvCluster, GroupType groupType, bool force, StringBuffer &messages)
- {
- /* a 'realCluster' is a cluster who's name matches it's nodeGroup
- * if the nodeGroup differs it implies it's sharing the nodeGroup with other thor instance(s).
- */
- bool realCluster = true;
- bool oldRealCluster = true;
- StringBuffer gname, oldGname;
- const char *defDir = NULL;
- switch (groupType)
- {
- case grp_thor:
- getClusterGroupName(cluster, gname);
- if (!streq(cluster.queryProp("@name"), gname.str()))
- realCluster = false;
- if (oldEnvCluster)
- {
- getClusterGroupName(*oldEnvCluster, oldGname);
- if (!streq(oldEnvCluster->queryProp("@name"), oldGname.str()))
- oldRealCluster = false;
- }
- break;
- case grp_thorspares:
- getClusterSpareGroupName(cluster, gname);
- oldRealCluster = realCluster = false;
- break;
- case grp_roxie:
- gname.append(cluster.queryProp("@name"));
- break;
- default:
- throwUnexpected();
- }
- if (altName)
- gname.clear().append(altName).toLowerCase();
- VStringBuffer xpath("Group[@name=\"%s\"]", gname.str());
- IPropertyTree *existingClusterGroup = groupsconnlock.conn->queryRoot()->queryPropTree(xpath.str()); // 'live' cluster group
- bool matchOldEnv = false;
- Owned<IPropertyTree> newClusterGroup = createClusterGroupFromEnvCluster(groupType, cluster, defDir, realCluster);
- bool matchExisting = clusterGroupCompare(newClusterGroup, existingClusterGroup);
- if (oldEnvCluster)
- {
- // new matches old, only if neither has changed it's name to mismatch it's nodeGroup name
- if (realCluster == oldRealCluster)
- {
- Owned<IPropertyTree> oldClusterGroup = createClusterGroupFromEnvCluster(groupType, *oldEnvCluster, defDir, oldRealCluster);
- matchOldEnv = clusterGroupCompare(newClusterGroup, oldClusterGroup);
- }
- else
- matchOldEnv = false;
- }
- if (!matchExisting)
- {
- if (force)
- {
- VStringBuffer msg("Forcing new group layout for %s [ matched active = false, matched old environment = %s ]", gname.str(), matchOldEnv?"true":"false");
- UWARNLOG("%s", msg.str());
- messages.append(msg).newline();
- matchOldEnv = false;
- }
- else
- {
- VStringBuffer msg("Active cluster '%s' group layout does not match environment [matched old environment=%s]", gname.str(), matchOldEnv?"true":"false");
- UWARNLOG("%s", msg.str()); \
- messages.append(msg).newline();
- if (existingClusterGroup)
- {
- // NB: not used at moment, but may help spot clusters that have swapped nodes
- existingClusterGroup->setPropBool("@mismatched", true);
- }
- }
- }
- if ((!existingClusterGroup && (grp_thorspares != groupType)) || (!matchExisting && !matchOldEnv))
- {
- VStringBuffer msg("New cluster layout for cluster %s", gname.str());
- UWARNLOG("%s", msg.str());
- messages.append(msg).newline();
- addClusterGroup(gname.str(), newClusterGroup.getClear(), realCluster);
- return true;
- }
- return false;
- }
- void constructHThorGroups(IPropertyTree &cluster)
- {
- const char *groupname = cluster.queryProp("@name");
- if (!groupname || !*groupname)
- return;
- unsigned ins = 0;
- Owned<IPropertyTreeIterator> insts = cluster.getElements("Instance");
- ForEach(*insts) {
- const char *na = insts->query().queryProp("@netAddress");
- if (na&&*na) {
- SocketEndpoint ep(na);
- if (!ep.isNull()) {
- ins++;
- VStringBuffer gname("hthor__%s", groupname);
- if (ins>1)
- gname.append('_').append(ins);
- Owned<IGroup> group = createIGroup(1, &ep);
- Owned<IPropertyTree> clusterGroup = createClusterGroup(grp_hthor, group, NULL, true);
- addClusterGroup(gname.str(), clusterGroup.getClear(), true);
- }
- }
- }
- }
- enum CgCmd { cg_null, cg_reset, cg_add, cg_remove };
- public:
- CInitGroups(unsigned _defaultTimeout)
- : groupsconnlock("constructGroup",SDS_GROUPSTORE_ROOT,true,false,false,_defaultTimeout)
- {
- defaultTimeout = _defaultTimeout;
- machinesLoaded = false;
- }
- bool doClusterGroup(CgCmd cmd, const char *_clusterName, const char *type, bool spares, SocketEndpointArray *eps, StringBuffer &messages)
- {
- Owned<IRemoteConnection> conn = querySDS().connect("/Environment/Software", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
- if (!conn)
- return false;
- if (!_clusterName || !*_clusterName)
- return false;
- StringAttr clusterName = _clusterName;
- clusterName.toLowerCase();
- if (!type || !*type)
- return false;
- bool ret = true;
- IPropertyTree* root = conn->queryRoot();
- Owned<IPropertyTreeIterator> clusters;
- StringBuffer errMsg;
- const char *clusterType = type;
- if (loadMachineMap()) {
- VStringBuffer xpath("%s[@name=\"%s\"]", type, clusterName.get());
- clusters.setown(root->getElements(xpath.str()));
- if (!clusters || !clusters->first()) {
- VStringBuffer errMsg("Could not find type %s, %s cluster", type, clusterName.get());
- UWARNLOG("%s", errMsg.str());
- messages.append(errMsg).newline();
- ret = false;
- }
- else {
- if (!streq("ThorCluster", type))
- return false; // currently only Thor supported here.
- IPropertyTree &cluster = clusters->query();
- switch (cmd) {
- case cg_reset:
- {
- if (spares) {
- if (!constructGroup(cluster,NULL,NULL,grp_thorspares,true,messages))
- ret = false;
- }
- else {
- if (!constructGroup(cluster,NULL,NULL,grp_thor,true,messages))
- ret = false;
- }
- break;
- }
- case cg_add:
- {
- assertex(eps);
- StringBuffer groupName;
- getClusterSpareGroupName(cluster, groupName);
- IPropertyTree *root = groupsconnlock.conn->queryRoot();
- VStringBuffer xpath("Group[@name=\"%s\"]",groupName.str());
- IPropertyTree *existing = root->queryPropTree(xpath.str());
- if (existing) {
- Owned<IPropertyTreeIterator> iter = existing->getElements("Node");
- ForEach(*iter) {
- SocketEndpoint ep(iter->query().queryProp("@ip"));
- if (eps->zap(ep)) {
- StringBuffer epStr;
- VStringBuffer errMsg("addSpares: not adding: %s, already in spares", ep.getUrlStr(epStr).str());
- UWARNLOG("%s", errMsg.str());
- messages.append(errMsg).newline();
- while (eps->zap(ep)); // delete any other duplicates
- }
- }
- }
- else {
- existing = createPTree();
- existing->setProp("@name", groupName.str());
- existing = root->addPropTree("Group", existing);
- }
- // add remaining
- ForEachItemIn(e, *eps) {
- const SocketEndpoint &ep = eps->item(e);
- StringBuffer ipStr;
- ep.getIpText(ipStr);
- IPropertyTree *node = createPTree();
- node->setProp("@ip", ipStr.str());
- existing->addPropTree("Node", node);
- }
- break;
- }
- case cg_remove:
- {
- assertex(eps);
- StringBuffer groupName;
- getClusterSpareGroupName(cluster, groupName);
- IPropertyTree *root = groupsconnlock.conn->queryRoot();
- VStringBuffer xpath("Group[@name=\"%s\"]", groupName.str());
- IPropertyTree *existing = root->queryPropTree(xpath.str());
- if (existing) {
- ForEachItemIn(e, *eps) {
- const SocketEndpoint &ep = eps->item(e);
- StringBuffer ipStr;
- ep.getIpText(ipStr);
- VStringBuffer xpath("Node[@ip=\"%s\"]", ipStr.str());
- if (!existing->removeProp(xpath.str())) {
- VStringBuffer errMsg("removeSpares: %s not found in spares", ipStr.str());
- UWARNLOG("%s", errMsg.str());
- messages.append(errMsg).newline();
- while (eps->zap(ep)); // delete any other duplicates
- }
- else
- while (existing->removeProp(xpath.str())); // remove any others, shouldn't be any
- }
- }
- break;
- }
- }
- if (clusters->next()) {
- VStringBuffer errMsg("resetThorGroup: more than one cluster named: %s", clusterName.get());
- UWARNLOG("%s", errMsg.str());
- messages.append(errMsg).newline();
- ret = false;
- }
- }
- }
- return ret;
- }
- bool resetClusterGroup(const char *clusterName, const char *type, bool spares, StringBuffer &messages)
- {
- return doClusterGroup(cg_reset, clusterName, type, spares, NULL, messages);
- }
- bool addSpares(const char *clusterName, const char *type, SocketEndpointArray &eps, StringBuffer &messages)
- {
- return doClusterGroup(cg_add, clusterName, type, true, &eps, messages);
- }
- bool removeSpares(const char *clusterName, const char *type, SocketEndpointArray &eps, StringBuffer &messages)
- {
- return doClusterGroup(cg_remove, clusterName, type, true, &eps, messages);
- }
- void constructGroups(bool force, StringBuffer &messages, IPropertyTree *oldEnvironment)
- {
- Owned<IRemoteConnection> conn = querySDS().connect("/Environment/Software", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
- if (!conn)
- return;
- clusternames.kill();
- IPropertyTree* root = conn->queryRoot();
- Owned<IPropertyTreeIterator> clusters;
- if (loadMachineMap()) {
- clusters.setown(root->getElements("ThorCluster"));
- ForEach(*clusters) {
- IPropertyTree &cluster = clusters->query();
- IPropertyTree *oldCluster = NULL;
- if (oldEnvironment) {
- VStringBuffer xpath("Software/ThorCluster[@name=\"%s\"]", cluster.queryProp("@name"));
- oldCluster = oldEnvironment->queryPropTree(xpath.str());
- }
- constructGroup(cluster,NULL,oldCluster,grp_thor,force,messages);
- constructGroup(cluster,NULL,oldCluster,grp_thorspares,force,messages);
- }
- clusters.setown(root->getElements("RoxieCluster"));
- ForEach(*clusters) {
- IPropertyTree &cluster = clusters->query();
- IPropertyTree *oldCluster = NULL;
- if (oldEnvironment) {
- VStringBuffer xpath("Software/RoxieCluster[@name=\"%s\"]", cluster.queryProp("@name"));
- oldCluster = oldEnvironment->queryPropTree(xpath.str());
- }
- constructGroup(cluster,NULL,oldCluster,grp_roxie,force,messages);
- }
- clusters.setown(root->getElements("EclAgentProcess"));
- ForEach(*clusters) {
- IPropertyTree &cluster = clusters->query();
- constructHThorGroups(cluster);
- }
- // correct cluster flags
- // JCSMORE - why was this necessary, may well be legacy..
- Owned<IPropertyTreeIterator> grps = groupsconnlock.conn->queryRoot()->getElements("Group");
- ForEach(*grps) {
- IPropertyTree &grp = grps->query();
- const char *name = grp.queryProp("@name");
- bool iscluster = NotFound != clusternames.find(name);
- if (iscluster!=grp.getPropBool("@cluster"))
- {
- if (iscluster)
- grp.setPropBool("@cluster", true);
- else
- grp.removeProp("@cluster");
- }
- }
- }
- }
- IGroup *getGroupFromCluster(const char *type, IPropertyTree &cluster, bool expand)
- {
- loadMachineMap();
- GroupType gt = getGroupType(type);
- return getGroupFromCluster(gt, cluster, expand);
- }
- IPropertyTree *queryRawGroup(const char *name)
- {
- VStringBuffer xpath("Group[@name=\"%s\"]", name);
- return groupsconnlock.conn->queryRoot()->queryPropTree(xpath.str());
- }
- };
- void initClusterGroups(bool force, StringBuffer &response, IPropertyTree *oldEnvironment, unsigned timems)
- {
- CInitGroups init(timems);
- init.constructGroups(force, response, oldEnvironment);
- }
- bool resetClusterGroup(const char *clusterName, const char *type, bool spares, StringBuffer &response, unsigned timems)
- {
- CInitGroups init(timems);
- return init.resetClusterGroup(clusterName, type, spares, response);
- }
- bool addClusterSpares(const char *clusterName, const char *type, SocketEndpointArray &eps, StringBuffer &response, unsigned timems)
- {
- CInitGroups init(timems);
- return init.addSpares(clusterName, type, eps, response);
- }
- bool removeClusterSpares(const char *clusterName, const char *type, SocketEndpointArray &eps, StringBuffer &response, unsigned timems)
- {
- CInitGroups init(timems);
- return init.removeSpares(clusterName, type, eps, response);
- }
- static IGroup *getClusterNodeGroup(const char *clusterName, const char *type, bool processGroup, unsigned timems)
- {
- VStringBuffer clusterPath("/Environment/Software/%s[@name=\"%s\"]", type, clusterName);
- Owned<IRemoteConnection> conn = querySDS().connect(clusterPath.str(), myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
- if (!conn)
- return NULL;
- IPropertyTree &cluster = *conn->queryRoot();
- StringBuffer nodeGroupName;
- getClusterGroupName(cluster, nodeGroupName);
- if (0 == nodeGroupName.length())
- throwUnexpected();
- /* NB: Due to the way node groups and swapNode work, we need to return the IP's from the node group corresponding to the cluster
- * which may no longer match the cluster IP's due to node swapping.
- * As the node group is an expanded form of the cluster group (with a IP per partition/slave), with the cluster group repeated
- * N times, where N is slavesPerNode*channelsPerSlave, return the first M (cluster group width) IP's of the node group.
- * Ideally the node group representation would change to match the cluster group definition, but that require a lot of changes
- * to DFS and elsewhere.
- */
- Owned<IGroup> nodeGroup = queryNamedGroupStore().lookup(nodeGroupName);
- CInitGroups init(timems);
- Owned<IGroup> expandedClusterGroup = init.getGroupFromCluster(type, cluster, true);
- if (!expandedClusterGroup)
- throwStringExceptionV(0, "Failed to get group for '%s' cluster '%s'", type, clusterName);
- if (!expandedClusterGroup->equals(nodeGroup))
- {
- IPropertyTree *rawGroup = init.queryRawGroup(nodeGroupName);
- if (!rawGroup)
- throwUnexpectedX("missing node group");
- unsigned nodesSwapped = rawGroup->getPropInt("@nodesSwapped");
- if (nodesSwapped)
- {
- unsigned rawGroupSize = rawGroup->getCount("Node");
- if (rawGroupSize != expandedClusterGroup->ordinality())
- throwStringExceptionV(0, "DFS cluster topology for '%s', does not match existing DFS group size for group '%s' [Environment cluster group size = %u, Dali group size = %u]",
- clusterName, nodeGroupName.str(), expandedClusterGroup->ordinality(), rawGroupSize);
- VStringBuffer msg("DFS cluster topology for '%s' using group '%s', does not match environment due to previously swapped nodes", clusterName, nodeGroupName.str());
- WARNLOG("%s", msg.str());
- }
- else
- throwStringExceptionV(0, "DFS cluster topology for '%s', does not match existing DFS group layout for group '%s'", clusterName, nodeGroupName.str());
- }
- Owned<IGroup> clusterGroup = init.getGroupFromCluster(type, cluster, false);
- ICopyArrayOf<INode> nodes;
- unsigned l=processGroup?cluster.getPropInt("@slavesPerNode", 1):1; // if process group requested, repeat clusterGroup slavesPerNode times.
- for (unsigned t=0; t<l; t++)
- {
- for (unsigned n=0; n<clusterGroup->ordinality(); n++)
- nodes.append(nodeGroup->queryNode(n));
- }
- return createIGroup(nodes.ordinality(), nodes.getArray());
- }
- IGroup *getClusterNodeGroup(const char *clusterName, const char *type, unsigned timems)
- {
- return getClusterNodeGroup(clusterName, type, false, timems);
- }
- IGroup *getClusterProcessNodeGroup(const char *clusterName, const char *type, unsigned timems)
- {
- return getClusterNodeGroup(clusterName, type, true, timems);
- }
- class CDaliDFSServer: public Thread, public CTransactionLogTracker, implements IDaliServer, implements IExceptionHandler
- { // Coven size
- bool stopped;
- unsigned defaultTimeout;
- unsigned numThreads;
- public:
- IMPLEMENT_IINTERFACE;
- CDaliDFSServer(IPropertyTree *config)
- : Thread("CDaliDFSServer"), CTransactionLogTracker(MDFS_MAX)
- {
- stopped = true;
- defaultTimeout = INFINITE; // server uses default
- numThreads = config->getPropInt("DFS/@numThreads", DEFAULT_NUM_DFS_THREADS);
- PROGLOG("DFS Server: numThreads=%d", numThreads);
- }
- ~CDaliDFSServer()
- {
- }
- void start()
- {
- Thread::start();
- }
- void ready()
- {
- }
- void suspend()
- {
- }
- void stop()
- {
- if (!stopped) {
- stopped = true;
- queryCoven().cancel(RANK_ALL,MPTAG_DFS_REQUEST);
- }
- join();
- }
- int run()
- {
- ICoven &coven=queryCoven();
- CMessageHandler<CDaliDFSServer> handler("CDaliDFSServer", this, &CDaliDFSServer::processMessage, this, numThreads, TIMEOUT_ON_CLOSEDOWN, INFINITE);
- CMessageBuffer mb;
- stopped = false;
- while (!stopped)
- {
- try
- {
- mb.clear();
- if (coven.recv(mb,RANK_ALL,MPTAG_DFS_REQUEST,NULL))
- {
- handler.handleMessage(mb);
- mb.clear(); // ^ has copied mb
- }
- else
- stopped = true;
- }
- catch (IException *e)
- {
- EXCLOG(e, "CDaliDFSServer");
- e->Release();
- }
- }
- return 0;
- }
- void iterateFiles(CMessageBuffer &mb,StringBuffer &trc)
- {
- TransactionLog transactionLog(*this, MDFS_ITERATE_FILES, mb.getSender());
- StringAttr wildname;
- bool recursive;
- bool includesuper = false;
- StringAttr attr;
- mb.read(wildname).read(recursive).read(attr);
- trc.appendf("iterateFiles(%s,%s,%s)",wildname.str(),recursive?"recursive":"",attr.str());
- if (queryTransactionLogging())
- transactionLog.log("%s", trc.str());
- Owned<IUserDescriptor> udesc;
- if (mb.getPos()<mb.length()) {
- mb.read(includesuper);
- if (mb.getPos()<mb.length()) {
- udesc.setown(createUserDescriptor());
- udesc->deserialize(mb);
- }
- }
- mb.clear();
- unsigned count=0;
- mb.append(count);
- CFileScanner scanner;
- CSDSServerLockBlock sdsLock; // lock sds while scanning
- unsigned start = msTick();
- scanner.scan(sdsLock, wildname.get(),recursive,includesuper);
- unsigned tookMs = msTick()-start;
- if (tookMs>100)
- PROGLOG("TIMING(filescan): %s: took %dms",trc.str(), tookMs);
- sdsLock.unlock(); // unlock to perform authentification
- bool auth = scopePermissionsAvail && querySessionManager().checkScopeScansLDAP();
- StringArray authScopes;
- CIArrayOf<CFileMatch> matchingFiles;
- start = msTick();
- scanner.getResults(auth, udesc, matchingFiles, authScopes, count, false);
- tookMs = msTick()-start;
- if (tookMs>100)
- PROGLOG("TIMING(LDAP): %s: took %dms, %d lookups, file matches = %d", trc.str(), tookMs, authScopes.ordinality(), count);
- sdsLock.lock(); // re-lock sds while serializing
- start = msTick();
- SerializeFileAttrOptions options; //The options is needed for the serializeFileAttributes()
- ForEachItemIn(m, matchingFiles)
- {
- CFileMatch &fileMatch = matchingFiles.item(m);
- CDFAttributeIterator::serializeFileAttributes(mb, fileMatch.queryFileTree(), fileMatch.queryName(), fileMatch.queryIsSuper(), options);
- }
- tookMs = msTick()-start;
- if (tookMs>100)
- PROGLOG("TIMING(filescan-serialization): %s: took %dms, %d files",trc.str(), tookMs, count);
- mb.writeDirect(0,sizeof(count),&count);
- }
- void iterateFilteredFiles(TransactionLog &transactionLog, CMessageBuffer &mb,StringBuffer &trc, bool returnAllFilesFlag)
- {
- Owned<IUserDescriptor> udesc;
- StringAttr filters;
- bool recursive;
- mb.read(filters).read(recursive);
- trc.appendf("iterateFilteredFiles(%s,%s)",filters.str(),recursive?"recursive":"");
- if (queryTransactionLogging())
- transactionLog.log("%s", trc.str());
- if (mb.getPos()<mb.length())
- {
- udesc.setown(createUserDescriptor());
- udesc->deserialize(mb);
- }
- mb.clear();
- unsigned count=0;
- mb.append(count);
- Owned<CIterateFileFilterContainer> iterateFileFilterContainer = new CIterateFileFilterContainer();
- iterateFileFilterContainer->readFilters(filters);
- CFileScanner scanner;
- CSDSServerLockBlock sdsLock; // lock sds while scanning
- unsigned start = msTick();
- scanner.scan(sdsLock, iterateFileFilterContainer.getLink(), recursive);
- unsigned tookMs = msTick()-start;
- if (tookMs>100)
- PROGLOG("TIMING(filescan): %s: took %dms",trc.str(), tookMs);
- sdsLock.unlock(); // unlock to perform authentification
- bool auth = scopePermissionsAvail && querySessionManager().checkScopeScansLDAP();
- StringArray authScopes;
- CIArrayOf<CFileMatch> matchingFiles;
- start = msTick();
- bool returnAllMatchingFiles = true;
- try
- {
- scanner.getResults(auth, udesc, matchingFiles, authScopes, count, true);
- }
- catch(IException *e)
- {
- if (DFSERR_PassIterateFilesLimit != e->errorCode())
- throw;
- e->Release();
- returnAllMatchingFiles = false;
- }
- if (returnAllFilesFlag)
- mb.append(returnAllMatchingFiles);
- tookMs = msTick()-start;
- if (tookMs>100)
- PROGLOG("TIMING(LDAP): %s: took %dms, %d lookups, file matches = %d", trc.str(), tookMs, authScopes.ordinality(), count);
- sdsLock.lock(); // re-lock sds while serializing
- start = msTick();
- ForEachItemIn(m, matchingFiles)
- {
- CFileMatch &fileMatch = matchingFiles.item(m);
- unsigned pos = mb.length();
- try
- {
- CDFAttributeIterator::serializeFileAttributes(mb, fileMatch.queryFileTree(), fileMatch.queryName(), fileMatch.queryIsSuper(), iterateFileFilterContainer->getSerializeFileAttrOptions());
- }
- catch (IException *e)
- {
- StringBuffer errMsg("Failed to serialize properties for file: ");
- LOG(MCuserWarning, e, errMsg.append(fileMatch.queryName()));
- e->Release();
- mb.setLength(pos);
- --count;
- }
- }
- tookMs = msTick()-start;
- if (tookMs>100)
- PROGLOG("TIMING(filescan-serialization): %s: took %dms, %d files",trc.str(), tookMs, count);
- mb.writeDirect(0,sizeof(count),&count);
- }
- void iterateFilteredFiles(CMessageBuffer &mb,StringBuffer &trc)
- {
- TransactionLog transactionLog(*this, MDFS_ITERATE_FILTEREDFILES, mb.getSender());
- iterateFilteredFiles(transactionLog, mb, trc, false);
- }
- void iterateFilteredFiles2(CMessageBuffer &mb,StringBuffer &trc)
- {
- TransactionLog transactionLog(*this, MDFS_ITERATE_FILTEREDFILES2, mb.getSender());
- iterateFilteredFiles(transactionLog, mb, trc, true);
- }
- void iterateRelationships(CMessageBuffer &mb,StringBuffer &trc)
- {
- TransactionLog transactionLog(*this, MDFS_ITERATE_RELATIONSHIPS, mb.getSender());
- StringAttr primary;
- StringAttr secondary;
- StringAttr primflds;
- StringAttr secflds;
- StringAttr kind;
- StringAttr cardinality;
- byte payloadb;
- mb.read(primary).read(secondary).read(primflds).read(secflds).read(kind).read(cardinality).read(payloadb);
- mb.clear();
- bool payload = (payloadb==1);
- trc.appendf("iterateRelationships(%s,%s,%s,%s,%s,%s,%d)",primary.str(),secondary.str(),primflds.str(),secflds.str(),kind.str(),cardinality.str(),(int)payloadb);
- if (queryTransactionLogging())
- transactionLog.log("%s", trc.str());
- unsigned start = msTick();
- unsigned count=0;
- CSDSServerLockBlock sdsLock; // lock sds while scanning
- StringBuffer xpath;
- CDistributedFileDirectory::getFileRelationshipXPath(xpath,primary,secondary,primflds,secflds,kind,cardinality,((payloadb==0)||(payloadb==1))?&payload:NULL);
- IPropertyTree *root = sdsLock->queryPropTree(querySdsRelationshipsRoot());
- Owned<IPropertyTreeIterator> iter = root?root->getElements(xpath.str()):NULL;
- mb.append(count);
- // save as sequence of branches
- if (iter) {
- ForEach(*iter.get()) {
- iter->query().serialize(mb);
- count++;
- }
- }
- if (msTick()-start>100) {
- PROGLOG("TIMING(relationshipscan): %s: took %dms, %d relations",trc.str(),msTick()-start,count);
- }
- mb.writeDirect(0,sizeof(count),&count);
- }
- void setFileAccessed(CMessageBuffer &mb,StringBuffer &trc)
- {
- TransactionLog transactionLog(*this, MDFS_SET_FILE_ACCESSED, mb.getSender());
- StringAttr lname;
- mb.read(lname);
- CDateTime dt;
- dt.deserialize(mb);
- trc.appendf("setFileAccessed(%s)",lname.str());
- Owned<IUserDescriptor> udesc;
- if (mb.getPos()<mb.length()) {
- udesc.setown(createUserDescriptor());
- udesc->deserialize(mb);
- }
- if (queryTransactionLogging())
- transactionLog.log("%s", trc.str());
- mb.clear();
- StringBuffer tail;
- CDfsLogicalFileName dlfn;
- dlfn.set(lname);
- if (!checkLogicalName(dlfn,udesc,true,false,true,"setFileAccessed on"))
- return;
- CScopeConnectLock sconnlock("setFileAccessed", dlfn, false, false, false, defaultTimeout);
- IPropertyTree* sroot = sconnlock.conn()?sconnlock.conn()->queryRoot():NULL;
- dlfn.getTail(tail);
- Owned<IPropertyTree> tree = getNamedPropTree(sroot,queryDfsXmlBranchName(DXB_File),"@name",tail.str(),false);
- if (tree) {
- StringBuffer str;
- tree->setProp("@accessed",dt.getString(str).str());
- }
- }
- void setFileProtect(CMessageBuffer &mb,StringBuffer &trc)
- {
- TransactionLog transactionLog(*this, MDFS_SET_FILE_PROTECT, mb.getSender());
- StringAttr lname;
- StringAttr owner;
- bool set;
- mb.read(lname).read(owner).read(set);
- trc.appendf("setFileProtect(%s,%s,%s)",lname.str(),owner.str(),set?"true":"false");
- if (queryTransactionLogging())
- transactionLog.log("%s", trc.str());
- Owned<IUserDescriptor> udesc;
- if (mb.getPos()<mb.length()) {
- udesc.setown(createUserDescriptor());
- udesc->deserialize(mb);
- }
- mb.clear();
- StringBuffer tail;
- CDfsLogicalFileName dlfn;
- dlfn.set(lname);
- if (!checkLogicalName(dlfn,udesc,true,false,true,"setFileProtect"))
- return;
- CScopeConnectLock sconnlock("setFileProtect", dlfn, false, false, false, defaultTimeout);
- IPropertyTree* sroot = sconnlock.conn()?sconnlock.conn()->queryRoot():NULL;
- dlfn.getTail(tail);
- Owned<IPropertyTree> tree = getNamedPropTree(sroot,queryDfsXmlBranchName(DXB_File),"@name",tail.str(),false);
- if (!tree)
- tree.setown(getNamedPropTree(sroot,queryDfsXmlBranchName(DXB_SuperFile),"@name",tail.str(),false));
- if (tree) {
- IPropertyTree *pt = tree->queryPropTree("Attr");
- if (pt)
- setFileProtectTree(*pt,*owner?owner:owner,set);
- }
- }
- void getFileTree(CMessageBuffer &mb,StringBuffer &trc)
- {
- TransactionLog transactionLog(*this, MDFS_GET_FILE_TREE, mb.getSender());
- StringAttr lname;
- mb.read(lname);
- unsigned ver;
- if (mb.length()<mb.getPos()+sizeof(unsigned))
- ver = 0;
- else {
- mb.read(ver);
- // this is a bit of a mess - for backward compatibility where user descriptor specified
- if (ver>MDFS_GET_FILE_TREE_V2) {
- mb.reset(mb.getPos()-sizeof(unsigned));
- ver = 0;
- }
- }
- trc.appendf("getFileTree(%s,%d)",lname.str(),ver);
- if (queryTransactionLogging())
- transactionLog.log("%s", trc.str());
- Owned<IUserDescriptor> udesc;
- if (mb.getPos()<mb.length()) {
- udesc.setown(createUserDescriptor());
- udesc->deserialize(mb);
- }
- mb.clear();
- CDfsLogicalFileName dlfn;
- dlfn.set(lname);
- CDfsLogicalFileName *logicalname=&dlfn;
- Owned<IDfsLogicalFileNameIterator> redmatch;
- for (;;) {
- StringBuffer tail;
- checkLogicalName(*logicalname,udesc,true,false,true,"getFileTree on");
- CScopeConnectLock sconnlock("getFileTree", *logicalname, false, false, false, defaultTimeout);
- IPropertyTree* sroot = sconnlock.conn()?sconnlock.conn()->queryRoot():NULL;
- logicalname->getTail(tail);
- Owned<IPropertyTree> tree = getNamedPropTree(sroot,queryDfsXmlBranchName(DXB_File),"@name",tail.str(),false);
- if (tree) {
- if (ver>=MDFS_GET_FILE_TREE_V2) {
- Owned<IFileDescriptor> fdesc = deserializeFileDescriptorTree(tree,&queryNamedGroupStore(),IFDSF_EXCLUDE_CLUSTERNAMES);
- if (fdesc) {
- ver = MDFS_GET_FILE_TREE_V2;
- mb.append((int)-2).append(ver);
- fdesc->serialize(mb);
- StringBuffer dts;
- if (tree->getProp("@modified",dts)) {
- CDateTime dt;
- dt.setString(dts.str());
- dt.serialize(mb);
- }
- }
- else
- ver = 0;
- }
- if (ver==0) {
- tree.setown(createPTreeFromIPT(tree));
- StringBuffer cname;
- logicalname->getCluster(cname);
- expandFileTree(tree,true,cname.str()); // resolve @node values that may not be set
- tree->serialize(mb);
- }
- break;
- }
- else {
- tree.setown(getNamedPropTree(sroot,queryDfsXmlBranchName(DXB_SuperFile),"@name",tail.str(),false));
- if (tree) {
- tree->serialize(mb);
- break;
- }
- }
- if (redmatch.get()) {
- if (!redmatch->next())
- break;
- }
- else {
- redmatch.setown(queryDistributedFileDirectory().queryRedirection().getMatch(logicalname->get()));
- if (!redmatch.get())
- break;
- if (!redmatch->first())
- break;
- }
- logicalname = &redmatch->query();
- }
- }
- void getGroupTree(CMessageBuffer &mb,StringBuffer &trc)
- {
- TransactionLog transactionLog(*this, MDFS_GET_GROUP_TREE, mb.getSender());
- StringAttr gname;
- mb.read(gname);
- mb.clear();
- trc.appendf("getGroupTree(%s)",gname.str());
- if (queryTransactionLogging())
- transactionLog.log("%s", trc.str());
- byte ok;
- CConnectLock connlock("getGroupTree",SDS_GROUPSTORE_ROOT,false,false,false,defaultTimeout);
- Owned<IPropertyTree> pt = getNamedPropTree(connlock.conn->queryRoot(),"Group","@name",gname.get(),true);
- if (pt) {
- ok = 1;
- mb.append(ok);
- pt->serialize(mb);
- }
- else {
- ok = 0;
- mb.append(ok);
- }
- }
- void processMessage(CMessageBuffer &mb)
- {
- CheckTime block0("CDaliDFSServer::processMessage ");
- ICoven &coven=queryCoven();
- StringBuffer trc;
- int fn;
- mb.read(fn);
- try
- {
- switch (fn)
- {
- case MDFS_ITERATE_FILES:
- {
- iterateFiles(mb, trc);
- break;
- }
- case MDFS_ITERATE_FILTEREDFILES: // legacy, newer clients will send MDFS_ITERATE_FILTEREDFILES2
- {
- iterateFilteredFiles(mb, trc);
- break;
- }
- case MDFS_ITERATE_FILTEREDFILES2:
- {
- iterateFilteredFiles2(mb, trc);
- break;
- }
- case MDFS_ITERATE_RELATIONSHIPS:
- {
- iterateRelationships(mb, trc);
- break;
- }
- case MDFS_GET_FILE_TREE:
- {
- getFileTree(mb, trc);
- break;
- }
- case MDFS_GET_GROUP_TREE:
- {
- getGroupTree(mb, trc);
- break;
- }
- case MDFS_SET_FILE_ACCESSED:
- {
- setFileAccessed(mb, trc);
- break;
- }
- case MDFS_SET_FILE_PROTECT:
- {
- setFileProtect(mb, trc);
- break;
- }
- default:
- {
- mb.clear();
- break;
- }
- }
- }
- catch (IException *e)
- {
- int err=-1; // exception marker
- mb.clear().append(err);
- serializeException(e, mb);
- e->Release();
- }
- coven.reply(mb);
- if (block0.slow())
- {
- SocketEndpoint ep = mb.getSender();
- ep.getUrlStr(block0.appendMsg(trc).append(" from "));
- }
- }
- void nodeDown(rank_t rank)
- {
- assertex(!"TBD");
- }
- // CTransactionLogTracker
- virtual StringBuffer &getCmdText(unsigned cmd, StringBuffer &ret) const
- {
- switch (cmd)
- {
- case MDFS_ITERATE_FILES:
- return ret.append("MDFS_ITERATE_FILES");
- case MDFS_ITERATE_FILTEREDFILES:
- return ret.append("MDFS_ITERATE_FILTEREDFILES");
- case MDFS_ITERATE_FILTEREDFILES2:
- return ret.append("MDFS_ITERATE_FILTEREDFILES2");
- case MDFS_ITERATE_RELATIONSHIPS:
- return ret.append("MDFS_ITERATE_RELATIONSHIPS");
- case MDFS_GET_FILE_TREE:
- return ret.append("MDFS_GET_FILE_TREE");
- case MDFS_GET_GROUP_TREE:
- return ret.append("MDFS_GET_GROUP_TREE");
- case MDFS_SET_FILE_ACCESSED:
- return ret.append("MDFS_SET_FILE_ACCESSED");
- case MDFS_SET_FILE_PROTECT:
- return ret.append("MDFS_SET_FILE_PROTECT");
- default:
- return ret.append("UNKNOWN");
- }
- }
- // IExceptionHandler impl.
- virtual bool fireException(IException *e)
- {
- EXCLOG(e, "CDaliDFSServer exception");
- return true;
- }
- } *daliDFSServer = NULL;
- IDFAttributesIterator *CDistributedFileDirectory::getDFAttributesIterator(const char *wildname, IUserDescriptor *user, bool recursive, bool includesuper,INode *foreigndali,unsigned foreigndalitimeout)
- {
- if (!wildname||!*wildname||(strcmp(wildname,"*")==0)) {
- recursive = true;
- }
- CMessageBuffer mb;
- mb.append((int)MDFS_ITERATE_FILES).append(wildname).append(recursive).append("").append(includesuper); // "" is legacy
- if (user)
- {
- user->serializeWithoutPassword(mb);
- }
- #ifdef NULL_DALIUSER_STACKTRACE
- else
- {
- DBGLOG("UNEXPECTED USER (NULL) in dadfs.cpp getDFAttributesIterator() line %d",__LINE__);
- PrintStackReport();
- }
- #endif
- if (foreigndali)
- foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
- else
- queryCoven().sendRecv(mb,RANK_RANDOM,MPTAG_DFS_REQUEST);
- checkDfsReplyException(mb);
- return new CDFAttributeIterator(mb);
- }
- IDFScopeIterator *CDistributedFileDirectory::getScopeIterator(IUserDescriptor *user, const char *basescope, bool recursive,bool includeempty)
- {
- return new CDFScopeIterator(this,basescope,recursive,includeempty,defaultTimeout);
- }
- static bool isValidLFN(const char *lfn)
- { // bit OTT
- if (!lfn||!*lfn||(strcmp(lfn,".")==0))
- return false;
- StringBuffer tmp(".::");
- tmp.append(lfn);
- CDfsLogicalFileName dlfn;
- return dlfn.setValidate(tmp.str());
- }
- bool CDistributedFileDirectory::loadScopeContents(const char *scopelfn,
- StringArray *scopes,
- StringArray *supers,
- StringArray *files,
- bool includeemptyscopes
- )
- {
- StringBuffer baseq;
- if (scopelfn&&*scopelfn) {
- if (memcmp(scopelfn,".::",3)==0) // scopes not in .
- scopelfn += 3;
- StringBuffer tmp(scopelfn);
- if (tmp.trim().length()) {
- tmp.append("::.");
- CDfsLogicalFileName dlfn;
- if (!dlfn.setValidate(tmp.str()))
- return false;
- dlfn.makeScopeQuery(baseq,false);
- }
- }
- CConnectLock connlock("CDistributedFileDirectory::loadScopeContents",querySdsFilesRoot(),false,false,false,defaultTimeout);
- if (!connlock.conn)
- return false;
- IPropertyTree *root = connlock.conn->queryRoot();
- if (!root)
- return false;
- if (baseq.length()) {
- root = root->queryPropTree(baseq.str());
- if (!root)
- return false;
- }
- Owned<IPropertyTreeIterator> iter;
- if (scopes) {
- iter.setown(root->getElements(queryDfsXmlBranchName(DXB_Scope)));
- ForEach(*iter) {
- IPropertyTree &ct = iter->query();
- if (includeemptyscopes||!recursiveCheckEmptyScope(ct)) {
- StringBuffer name;
- if (ct.getProp("@name",name)&&name.trim().length()&&isValidLFN(name.str()))
- scopes->append(name.str());
- }
- }
- }
- if (!supers&&!files)
- return true;
- if (baseq.length()==0) { // bit odd but top level files are in '.'
- CDfsLogicalFileName dlfn;
- dlfn.set(".",".");
- dlfn.makeScopeQuery(baseq,false);
- root = root->queryPropTree(baseq.str());
- if (!root)
- return true;
- }
- if (supers) {
- iter.setown(root->getElements(queryDfsXmlBranchName(DXB_SuperFile)));
- ForEach(*iter) {
- IPropertyTree &ct = iter->query();
- StringBuffer name;
- if (ct.getProp("@name",name)&&name.trim().length()&&isValidLFN(name.str()))
- supers->append(name.str());
- }
- }
- if (files) {
- iter.setown(root->getElements(queryDfsXmlBranchName(DXB_File)));
- ForEach(*iter) {
- StringBuffer name;
- IPropertyTree &ct = iter->query();
- if (ct.getProp("@name",name)&&name.trim().length()&&isValidLFN(name.str()))
- files->append(name.str());
- }
- }
- return true;
- }
- void CDistributedFileDirectory::setFileAccessed(CDfsLogicalFileName &dlfn,IUserDescriptor *user, const CDateTime &dt, const INode *foreigndali,unsigned foreigndalitimeout)
- {
- // this accepts either a foreign dali node or a foreign lfn
- Owned<INode> fnode;
- SocketEndpoint ep;
- const char *lname;
- if (dlfn.isForeign()) {
- if (!dlfn.getEp(ep))
- throw MakeStringException(-1,"cannot resolve dali ip in foreign file name (%s)",dlfn.get());
- fnode.setown(createINode(ep));
- foreigndali = fnode;
- lname = dlfn.get(true);
- }
- else if (dlfn.isExternal())
- return;
- else
- lname = dlfn.get();
- if (isLocalDali(foreigndali))
- foreigndali = NULL;
- CMessageBuffer mb;
- mb.append((int)MDFS_SET_FILE_ACCESSED).append(lname);
- dt.serialize(mb);
- if (user)
- {
- user->serializeWithoutPassword(mb);
- }
- #ifdef NULL_DALIUSER_STACKTRACE
- else
- {
- DBGLOG("UNEXPECTED USER (NULL) in dadfs.cpp setFileAccessed() line %d",__LINE__);
- PrintStackReport();
- }
- #endif
- if (foreigndali)
- foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
- else
- queryCoven().sendRecv(mb,RANK_RANDOM,MPTAG_DFS_REQUEST);
- checkDfsReplyException(mb);
- }
- void CDistributedFileDirectory::setFileProtect(CDfsLogicalFileName &dlfn,IUserDescriptor *user, const char *owner, bool set, const INode *foreigndali,unsigned foreigndalitimeout)
- {
- // this accepts either a foreign dali node or a foreign lfn
- Owned<INode> fnode;
- SocketEndpoint ep;
- const char *lname;
- if (dlfn.isForeign()) {
- if (!dlfn.getEp(ep))
- throw MakeStringException(-1,"cannot resolve dali ip in foreign file name (%s)",dlfn.get());
- fnode.setown(createINode(ep));
- foreigndali = fnode;
- lname = dlfn.get(true);
- }
- else if (dlfn.isExternal())
- return;
- else
- lname = dlfn.get();
- if (isLocalDali(foreigndali))
- foreigndali = NULL;
- CMessageBuffer mb;
- if (!owner)
- owner = "";
- mb.append((int)MDFS_SET_FILE_PROTECT).append(lname).append(owner).append(set);
- if (user)
- {
- user->serializeWithoutPassword(mb);
- }
- #ifdef NULL_DALIUSER_STACKTRACE
- else
- {
- DBGLOG("UNEXPECTED USER (NULL) in dadfs.cpp setFileProtect() line %d",__LINE__);
- PrintStackReport();
- }
- #endif
- if (foreigndali)
- foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
- else
- queryCoven().sendRecv(mb,RANK_RANDOM,MPTAG_DFS_REQUEST);
- checkDfsReplyException(mb);
- }
- IPropertyTree *CDistributedFileDirectory::getFileTree(const char *lname, IUserDescriptor *user, const INode *foreigndali,unsigned foreigndalitimeout, bool expandnodes, bool appendForeign)
- {
- // this accepts either a foreign dali node or a foreign lfn
- Owned<INode> fnode;
- CDfsLogicalFileName dlfn;
- SocketEndpoint ep;
- dlfn.set(lname);
- if (dlfn.isForeign()) {
- if (!dlfn.getEp(ep))
- throw MakeStringException(-1,"cannot resolve dali ip in foreign file name (%s)",lname);
- fnode.setown(createINode(ep));
- foreigndali = fnode;
- lname = dlfn.get(true);
- }
- if (isLocalDali(foreigndali))
- foreigndali = NULL;
- CMessageBuffer mb;
- mb.append((int)MDFS_GET_FILE_TREE).append(lname);
- mb.append(MDFS_GET_FILE_TREE_V2);
- if (user)
- {
- user->serializeWithoutPassword(mb);
- }
- #ifdef NULL_DALIUSER_STACKTRACE
- else
- {
- DBGLOG("UNEXPECTED USER (NULL) in dadfs.cpp getFileTree() line %d",__LINE__);
- PrintStackReport();
- }
- #endif
- if (foreigndali)
- foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
- else
- queryCoven().sendRecv(mb,RANK_RANDOM,MPTAG_DFS_REQUEST);
- checkDfsReplyException(mb);
- if (mb.length()==0)
- return NULL;
- unsigned ver = 0;
- if ((mb.length()>=sizeof(int))&&(*(int *)mb.bufferBase()) == -2) { // version indicator
- int i;
- mb.read(i);
- mb.read(ver);
- }
- Owned<IPropertyTree> ret;
- if (ver==0)
- ret.setown(createPTree(mb));
- else {
- Owned<IFileDescriptor> fdesc;
- CDateTime modified;
- if (ver==MDFS_GET_FILE_TREE_V2) { // no longer in use but support for back compatibility
- fdesc.setown(deserializeFileDescriptor(mb));
- if (mb.remaining()>0)
- modified.deserialize(mb);
- }
- else
- throw MakeStringException(-1,"Unknown GetFileTree serialization version %d",ver);
- ret.setown(createPTree(queryDfsXmlBranchName(DXB_File)));
- fdesc->serializeTree(*ret,expandnodes?0:CPDMSF_packParts);
- if (!modified.isNull()) {
- StringBuffer dts;
- ret->setProp("@modified",modified.getString(dts).str());
- }
- }
- if (expandnodes) {
- StringBuffer cname;
- dlfn.getCluster(cname);
- expandFileTree(ret,true,cname.str());
- CDfsLogicalFileName dlfn2;
- dlfn2.set(dlfn);
- if (foreigndali)
- dlfn2.setForeign(foreigndali->endpoint(),false);
- ret->setProp("OrigName",dlfn.get());
- }
- if (foreigndali && appendForeign)
- resolveForeignFiles(ret,foreigndali);
- return ret.getClear();
- }
- IFileDescriptor *CDistributedFileDirectory::getFileDescriptor(const char *lname,IUserDescriptor *user,const INode *foreigndali,unsigned foreigndalitimeout)
- {
- Owned<IPropertyTree> tree = getFileTree(lname,user,foreigndali,foreigndalitimeout,false);
- if (!tree)
- return NULL;
- if (strcmp(tree->queryName(),queryDfsXmlBranchName(DXB_SuperFile))==0) {
- CDfsLogicalFileName dlfn;
- dlfn.set(lname);
- Owned<CDistributedSuperFile> sfile = new CDistributedSuperFile(this,tree, dlfn, user);
- return sfile->getFileDescriptor(NULL);
- }
- if (strcmp(tree->queryName(),queryDfsXmlBranchName(DXB_File))!=0)
- return NULL; // what is it?
- IFileDescriptor * fdesc = deserializeFileDescriptorTree(tree,&queryNamedGroupStore(),0);
- if (fdesc)
- fdesc->setTraceName(lname);
- return fdesc;
- }
- IDistributedFile *CDistributedFileDirectory::getFile(const char *lname,IUserDescriptor *user,const INode *foreigndali,unsigned foreigndalitimeout)
- {
- Owned<IPropertyTree> tree = getFileTree(lname,user,foreigndali,foreigndalitimeout,false);
- if (!tree)
- return NULL;
- if (strcmp(tree->queryName(),queryDfsXmlBranchName(DXB_SuperFile))==0) {
- CDfsLogicalFileName dlfn;
- dlfn.set(lname);
- return new CDistributedSuperFile(this,tree, dlfn, user);
- }
- if (strcmp(tree->queryName(),queryDfsXmlBranchName(DXB_File))!=0)
- return NULL; // what is it?
- Owned<IFileDescriptor> fdesc = deserializeFileDescriptorTree(tree,&queryNamedGroupStore(),IFDSF_FOREIGN_GROUP);
- if (!fdesc)
- return NULL;
- fdesc->setTraceName(lname);
- CDistributedFile *ret = new CDistributedFile(this, fdesc, user, false);
- ret->setLogicalName(lname);
- const char *date = tree->queryProp("@modified");
- if (ret) {
- CDateTime dt;
- if (date&&*date)
- dt.setString(date);
- ret->setModificationTime(dt);
- }
- return ret;
- }
- static void addForeignName(IPropertyTree &t,const INode *foreigndali,const char *attr)
- {
- StringBuffer sb;
- const char *name = t.queryProp(attr);
- if (!name||!*name)
- return;
- CDfsLogicalFileName logicalname;
- logicalname.set(name);
- if (logicalname.isExternal()||logicalname.isQuery())
- return; // how did that get in here?
- if (logicalname.isForeign()) {
- SocketEndpoint ep;
- Owned<INode> fd = createINode(ep);
- if (logicalname.getEp(ep)&&isLocalDali(fd)) { // see if pointing back at self
- logicalname.clearForeign();
- t.setProp(attr,logicalname.get());
- }
- }
- else if (foreigndali) {
- logicalname.setForeign(foreigndali->endpoint(),false);
- t.setProp(attr,logicalname.get());
- }
- }
- void CDistributedFileDirectory::resolveForeignFiles(IPropertyTree *tree,const INode *foreigndali)
- {
- if (!tree||!foreigndali)
- return;
- // now add to all sub files
- Owned<IPropertyTreeIterator> pe = tree->getElements("SubFile");
- ForEach(*pe)
- addForeignName(pe->query(),foreigndali,"@name");
- pe.setown(tree->getElements("SuperOwner"));
- ForEach(*pe)
- addForeignName(pe->query(),foreigndali,"@name");
- // do origname?
- }
- SecAccessFlags CDistributedFileDirectory::getFilePermissions(const char *lname,IUserDescriptor *user,unsigned auditflags)
- {
- CDfsLogicalFileName dlfn;
- dlfn.set(lname);
- StringBuffer scopes;
- dlfn.getScopes(scopes);
- return getScopePermissions(scopes.str(),user,auditflags);
- }
- SecAccessFlags CDistributedFileDirectory::getNodePermissions(const IpAddress &ip,IUserDescriptor *user,unsigned auditflags)
- {
- if (ip.isNull())
- return SecAccess_None;
- CDfsLogicalFileName dlfn;
- SocketEndpoint ep(0,ip);
- dlfn.setExternal(ep,"/x");
- StringBuffer scopes;
- dlfn.getScopes(scopes,true);
- return getScopePermissions(scopes.str(),user,auditflags);
- }
- SecAccessFlags CDistributedFileDirectory::getFDescPermissions(IFileDescriptor *fdesc,IUserDescriptor *user,unsigned auditflags)
- {
- // this checks have access to the nodes in the file descriptor
- SecAccessFlags retPerms = SecAccess_Full;
- unsigned np = fdesc->numParts();
- for (unsigned i=0;i<np;i++) {
- INode *node = fdesc->queryNode(i);
- if (node) {
- bool multi = false;
- RemoteMultiFilename mfn;
- unsigned n = 1;
- if (fdesc->isMulti()) {
- fdesc->getMultiFilename(i,0,mfn);
- multi = true;
- n = mfn.ordinality();
- }
- for (unsigned j = 0;j<n;j++) {
- RemoteFilename rfn;
- if (multi) {
- rfn.set(mfn.item(j));
- }
- else
- fdesc->getFilename(i,0,rfn);
- StringBuffer localpath;
- rfn.getLocalPath(localpath);
- // translate wild cards
- for (unsigned k=0;k<localpath.length();k++)
- if ((localpath.charAt(k)=='?')||(localpath.charAt(k)=='*'))
- localpath.setCharAt(k,'_');
- CDfsLogicalFileName dlfn;
- dlfn.setExternal(rfn.queryEndpoint(),localpath.str());
- StringBuffer scopes;
- dlfn.getScopes(scopes);
- SecAccessFlags perm = getScopePermissions(scopes.str(),user,auditflags);
- if (perm < retPerms) {
- retPerms = perm;
- if (retPerms == SecAccess_None)
- return SecAccess_None;
- }
- }
- }
- }
- return retPerms;
- }
- void CDistributedFileDirectory::setDefaultUser(IUserDescriptor *user)
- {
- if (user)
- defaultudesc.set(user);
- else
- defaultudesc.setown(createUserDescriptor());
- }
- IUserDescriptor* CDistributedFileDirectory::queryDefaultUser()
- {
- return defaultudesc.get();
- }
- void CDistributedFileDirectory::setDefaultPreferredClusters(const char *clusters)
- {
- defprefclusters.set(clusters);
- }
- bool removePhysicalFiles(IGroup *grp,const char *_filemask,unsigned short port,ClusterPartDiskMapSpec &mspec,IMultiException *mexcept)
- {
- // TBD this won't remove repeated parts
- PROGLOG("removePhysicalFiles(%s)",_filemask);
- if (!isAbsolutePath(_filemask))
- throw MakeStringException(-1,"removePhysicalFiles: Filename %s must be complete path",_filemask);
- size32_t l = strlen(_filemask);
- while (l&&isdigit(_filemask[l-1]))
- l--;
- unsigned width=0;
- if (l&&(_filemask[l-1]=='_'))
- width = atoi(_filemask+l);
- if (!width)
- width = grp->ordinality();
- CriticalSection errcrit;
- class casyncfor: public CAsyncFor
- {
- unsigned short port;
- CriticalSection &errcrit;
- IMultiException *mexcept;
- unsigned width;
- StringAttr filemask;
- IGroup *grp;
- ClusterPartDiskMapSpec &mspec;
- public:
- bool ok;
- casyncfor(IGroup *_grp,const char *_filemask,unsigned _width,unsigned short _port,ClusterPartDiskMapSpec &_mspec,IMultiException *_mexcept,CriticalSection &_errcrit)
- : mspec(_mspec),filemask(_filemask),errcrit(_errcrit)
- {
- grp = _grp;
- port = _port;
- ok = true;
- mexcept = _mexcept;
- width = _width;
- }
- void Do(unsigned i)
- {
- for (unsigned copy = 0; copy < 2; copy++) // ** TBD
- {
- RemoteFilename rfn;
- constructPartFilename(grp,i+1,width,NULL,filemask,"",copy>0,mspec,rfn);
- if (port)
- rfn.setPort(port); // if daliservix
- Owned<IFile> partfile = createIFile(rfn);
- StringBuffer eps;
- try
- {
- unsigned start = msTick();
- #if 1
- if (partfile->remove()) {
- PROGLOG("Removed '%s'",partfile->queryFilename());
- unsigned t = msTick()-start;
- if (t>5*1000)
- DBGLOG("Removing %s from %s took %ds", partfile->queryFilename(), rfn.queryEndpoint().getUrlStr(eps).str(), t/1000);
- }
- else
- IWARNLOG("Failed to remove file part %s from %s", partfile->queryFilename(),rfn.queryEndpoint().getUrlStr(eps).str());
- #else
- if (partfile->exists())
- PROGLOG("Would remove '%s'",partfile->queryFilename());
- #endif
- }
- catch (IException *e)
- {
- CriticalBlock block(errcrit);
- if (mexcept)
- mexcept->append(*e);
- else {
- StringBuffer s("Failed to remove file part ");
- s.append(partfile->queryFilename()).append(" from ");
- rfn.queryEndpoint().getUrlStr(s);
- EXCLOG(e, s.str());
- e->Release();
- }
- ok = false;
- }
- }
- }
- } afor(grp,_filemask,width,port,mspec,mexcept,errcrit);
- afor.For(width,10,false,true);
- return afor.ok;
- }
- IDaliServer *createDaliDFSServer(IPropertyTree *config)
- {
- assertex(!daliDFSServer); // initialization problem
- daliDFSServer = new CDaliDFSServer(config);
- return daliDFSServer;
- }
- IDistributedFileTransaction *createDistributedFileTransaction(IUserDescriptor *user, ICodeContext *ctx)
- {
- return new CDistributedFileTransaction(user, NULL, ctx);
- }
- static void encodeCompareResult(DistributedFileCompareResult &ret,bool differs,CDateTime &newestdt1,CDateTime &newestdt2)
- {
- if (ret!=DFS_COMPARE_RESULT_FAILURE) {
- int cmp = 0;
- if (!newestdt1.isNull()) {
- if (!newestdt2.isNull()) {
- int cmp = newestdt1.compare(newestdt2,false);
- if (cmp>=0)
- ret = DFS_COMPARE_RESULT_SAME_NEWER;
- else
- ret = DFS_COMPARE_RESULT_SAME_OLDER;
- }
- else
- ret = DFS_COMPARE_RESULT_SAME_NEWER;
- }
- else if (!newestdt2.isNull())
- ret = DFS_COMPARE_RESULT_SAME_OLDER;
- if (differs) {
- if (ret==DFS_COMPARE_RESULT_SAME_OLDER) // ok they could be same but seems rather unlikely!
- ret = DFS_COMPARE_RESULT_DIFFER_OLDER;
- else
- ret = DFS_COMPARE_RESULT_DIFFER_NEWER;
- }
- }
- }
- DistributedFileCompareResult CDistributedFileDirectory::fileCompare(const char *lfn1,const char *lfn2,DistributedFileCompareMode mode,StringBuffer &errstr,IUserDescriptor *user)
- {
- DistributedFileCompareResult ret = DFS_COMPARE_RESULT_SAME;
- StringBuffer msg;
- try
- {
- Owned<IDistributedFile> file1 = lookup(lfn1, user, false, false, false, NULL, defaultTimeout);
- Owned<IDistributedFile> file2 = lookup(lfn2, user, false, false, false, NULL, defaultTimeout);
- if (!file1)
- {
- errstr.appendf("File %s not found",lfn1);
- ret = DFS_COMPARE_RESULT_FAILURE;
- }
- else if (!file2)
- {
- errstr.appendf("File %s not found",lfn2);
- ret = DFS_COMPARE_RESULT_FAILURE;
- }
- else
- {
- unsigned np = file1->numParts();
- if (np!=file2->numParts())
- {
- errstr.appendf("Files %s and %s have differing number of parts",lfn1,lfn2);
- ret = DFS_COMPARE_RESULT_FAILURE;
- }
- else
- {
- CDateTime newestdt1;
- CDateTime newestdt2;
- bool differs = false;
- class casyncfor: public CAsyncFor
- {
- CriticalSection crit;
- DistributedFileCompareResult &ret;
- IDistributedFile *file1;
- IDistributedFile *file2;
- const char *lfn1;
- const char *lfn2;
- StringBuffer &errstr;
- DistributedFileCompareMode mode;
- bool physdatesize;
- CDateTime &newestdt1;
- CDateTime &newestdt2;
- bool &differs;
- public:
- casyncfor(const char *_lfn1,const char *_lfn2,IDistributedFile *_file1,IDistributedFile *_file2,DistributedFileCompareMode _mode,DistributedFileCompareResult &_ret,StringBuffer &_errstr,
- CDateTime &_newestdt1,CDateTime &_newestdt2,bool &_differs)
- : ret(_ret), errstr(_errstr),newestdt1(_newestdt1),newestdt2(_newestdt2),differs(_differs)
- {
- lfn1 = _lfn1;
- lfn2 = _lfn2;
- file1 = _file1;
- file2 = _file2;
- mode = _mode;
- physdatesize = (mode==DFS_COMPARE_FILES_PHYSICAL)||(mode==DFS_COMPARE_FILES_PHYSICAL_CRCS);
- }
- void Do(unsigned p)
- {
- CriticalBlock block (crit);
- StringBuffer msg;
- Owned<IDistributedFilePart> part1 = file1->getPart(p);
- Owned<IDistributedFilePart> part2 = file2->getPart(p);
- CDateTime dt1;
- RemoteFilename rfn;
- bool ok;
- {
- CriticalUnblock unblock(crit);
- ok = part1->getModifiedTime(true,physdatesize,dt1);
- }
- if (!ok) {
- if (errstr.length()==0) {
- errstr.append("Could not find ");
- part1->getFilename(rfn);
- rfn.getPath(errstr);
- }
- ret = DFS_COMPARE_RESULT_FAILURE;
- }
- CDateTime dt2;
- {
- CriticalUnblock unblock(crit);
- ok = part2->getModifiedTime(true,physdatesize,dt2);
- }
- if (!ok) {
- if (errstr.length()==0) {
- errstr.append("Could not find ");
- part2->getFilename(rfn);
- rfn.getPath(errstr);
- }
- ret = DFS_COMPARE_RESULT_FAILURE;
- }
- if (ret!=DFS_COMPARE_RESULT_FAILURE) {
- int cmp = dt1.compare(dt2,false);
- if (cmp>0) {
- if (newestdt1.isNull()||(dt1.compare(newestdt1,false)>0))
- newestdt1.set(dt1);
- }
- else if (cmp<0) {
- if (newestdt2.isNull()||(dt2.compare(newestdt2,false)>0))
- newestdt2.set(dt2);
- }
- }
- if ((ret!=DFS_COMPARE_RESULT_FAILURE)&&!differs) {
- offset_t sz1;
- offset_t sz2;
- {
- CriticalUnblock unblock(crit);
- sz1 = part1->getFileSize(true,physdatesize);
- sz2 = part2->getFileSize(true,physdatesize);
- }
- if (sz1!=sz2)
- differs = true;
- }
- if ((ret!=DFS_COMPARE_RESULT_FAILURE)&&!differs) {
- unsigned crc1;
- unsigned crc2;
- if (mode==DFS_COMPARE_FILES_PHYSICAL_CRCS) {
- {
- CriticalUnblock unblock(crit);
- crc1 = part1->getPhysicalCrc();
- crc2 = part2->getPhysicalCrc();
- }
- }
- else {
- if (!part1->getCrc(crc1))
- return;
- if (!part2->getCrc(crc2))
- return;
- }
- if (crc1!=crc2)
- differs = true;
- }
- }
- } afor(lfn1,lfn2,file1,file2,mode,ret,errstr,newestdt1,newestdt2,differs);
- afor.For(np,20,false,false);
- encodeCompareResult(ret,differs,newestdt1,newestdt2);
- }
- }
- }
- catch (IException *e) {
- if (errstr.length()==0)
- e->errorMessage(errstr);
- else
- EXCLOG(e,"CDistributedFileDirectory::fileCompare");
- e->Release();
- ret = DFS_COMPARE_RESULT_FAILURE;
- }
- return ret;
- }
- bool CDistributedFileDirectory::filePhysicalVerify(const char *lfn, IUserDescriptor *user, bool includecrc, StringBuffer &errstr)
- {
- bool differs = false;
- Owned<IDistributedFile> file = lookup(lfn, user, false, false, false, NULL, defaultTimeout);
- if (!file)
- {
- errstr.appendf("Could not find file: %s",lfn);
- return false;
- }
- try
- {
- unsigned np = file->numParts();
- class casyncfor: public CAsyncFor
- {
- CriticalSection crit;
- IDistributedFile *file;
- const char *lfn;
- StringBuffer &errstr;
- bool includecrc;
- bool &differs;
- unsigned defaultTimeout;
- public:
- casyncfor(const char *_lfn,IDistributedFile *_file,StringBuffer &_errstr, bool _includecrc,
- bool &_differs, unsigned _defaultTimeout)
- : errstr(_errstr), differs(_differs)
- {
- lfn = _lfn;
- file = _file;
- includecrc = _includecrc;
- defaultTimeout = _defaultTimeout;
- }
- void Do(unsigned p)
- {
- CriticalBlock block (crit);
- StringBuffer msg;
- Owned<IDistributedFilePart> part = file->getPart(p);
- CDateTime dt1; // logical
- CDateTime dt2; // physical
- RemoteFilename rfn;
- bool ok;
- bool nological = !part->getModifiedTime(false,false,dt1);
- {
- CriticalUnblock unblock(crit);
- ok = part->getModifiedTime(true,true,dt2);
- }
- if (!ok) {
- if (errstr.length()==0) {
- errstr.append("Could not find part file: ");
- part->getFilename(rfn);
- rfn.getPath(errstr);
- }
- differs = true;
- }
- if (!differs&&!includecrc) {
- if (nological) {
- StringBuffer str;
- // TODO: Create DistributedFilePropertyLock for parts
- part->lockProperties(defaultTimeout);
- part->queryAttributes().setProp("@modified",dt2.getString(str).str());
- part->unlockProperties();
- }
- else {
- if (dt1.compare(dt2,false)!=0) {
- if (errstr.length()==0) {
- errstr.append("Modified time differs for: ");
- part->getFilename(rfn);
- rfn.getPath(errstr);
- }
- differs = true;
- }
- }
- }
- if (!differs) {
- offset_t sz1;
- offset_t sz2;
- {
- CriticalUnblock unblock(crit);
- sz1 = part->getFileSize(false,false);
- sz2 = part->getFileSize(true,true);
- }
- if (sz1!=sz2) {
- if (sz1==(offset_t)-1) {
- // TODO: Create DistributedFilePropertyLock for parts
- part->lockProperties(defaultTimeout);
- part->queryAttributes().setPropInt64("@size",sz2);
- part->unlockProperties();
- }
- else if (sz2!=(offset_t)-1) {
- if (errstr.length()==0) {
- errstr.append("File size differs for: ");
- part->getFilename(rfn);
- rfn.getPath(errstr);
- }
- differs = true;
- }
- }
- }
- if (!differs&&includecrc) {
- unsigned crc1;
- unsigned crc2;
- {
- CriticalUnblock unblock(crit);
- crc2 = part->getPhysicalCrc();
- }
- if (!part->getCrc(crc1)) {
- // TODO: Create DistributedFilePropertyLock for parts
- part->lockProperties(defaultTimeout);
- part->queryAttributes().setPropInt64("@fileCrc",(unsigned)crc2);
- part->unlockProperties();
- }
- else if (crc1!=crc2) {
- if (errstr.length()==0) {
- errstr.append("File CRC differs for: ");
- part->getFilename(rfn);
- rfn.getPath(errstr);
- }
- differs = true;
- }
- }
- }
- } afor(lfn,file,errstr,includecrc,differs,defaultTimeout);
- afor.For(np,10,false,false);
- }
- catch (IException *e) {
- if (errstr.length()==0)
- e->errorMessage(errstr);
- else
- EXCLOG(e,"CDistributedFileDirectory::fileCompare");
- e->Release();
- differs = true;
- }
- return !differs;
- }
- typedef MapStringTo<bool> SubfileSet;
- class CFilterAttrIterator: implements IDFAttributesIterator, public CInterface
- {
- Owned<IDFAttributesIterator> iter;
- Linked<IUserDescriptor> user;
- SubfileSet sfset;
- bool includesub;
- public:
- IMPLEMENT_IINTERFACE;
- CFilterAttrIterator(IDFAttributesIterator *_iter,IUserDescriptor* _user,bool _includesub,unsigned timeoutms)
- : iter(_iter), user(_user)
- {
- includesub = _includesub;
- CDfsLogicalFileName lfn;
- StringBuffer query;
- Owned<IDFScopeIterator> siter = queryDistributedFileDirectory().getScopeIterator(user,NULL,true,false);
- ForEach(*siter) {
- lfn.set(siter->query(),"X");
- lfn.makeScopeQuery(query.clear());
- Owned<IRemoteConnection> conn = querySDS().connect(query.str(),myProcessSession(),0, timeoutms);
- if (conn) {
- IPropertyTree *t = conn->queryRoot();
- Owned<IPropertyTreeIterator> iter = t->getElements("SuperFile/SubFile");
- ForEach(*iter) {
- const char *name = iter->query().queryProp("@name");
- if (!sfset.getValue(name))
- sfset.setValue(name, true);
- }
- }
- }
- }
- inline bool match()
- {
- const char *name = iter->query().queryProp("@name");
- return ((sfset.getValue(name)!=NULL)==includesub);
- }
- bool first()
- {
- if (!iter->first())
- return false;
- while (!match())
- if (!iter->next())
- return false;
- return true;
- }
- bool next()
- {
- do {
- if (!iter->next())
- return false;
- } while (!match());
- return true;
- }
- bool isValid() { return iter->isValid(); }
- IPropertyTree & query() { return iter->query(); }
- };
- IDFAttributesIterator *createSubFileFilter(IDFAttributesIterator *_iter,IUserDescriptor* _user, bool includesub, unsigned timeoutms)
- {
- return new CFilterAttrIterator(_iter,_user,includesub,timeoutms);
- }
- bool decodeChildGroupName(const char *gname,StringBuffer &parentname, StringBuffer &range)
- {
- if (!gname||!*gname)
- return false;
- size32_t l = strlen(gname);
- if (gname[l-1]!=']')
- return false;
- const char *ss = strchr(gname,'[');
- if (!ss||(ss==gname))
- return false;
- range.append(l-(ss-gname)-2,ss+1);
- range.trim();
- if (!range.length())
- return false;
- parentname.append(ss-gname,gname);
- return true;
- }
- /* given a list of group offsets (positions), create a compact representation of the range
- * compatible with the group range syntax, e.g. mygroup[1-5,8-10] or mygroup[1,5,10]
- */
- StringBuffer &encodeChildGroupRange(UnsignedArray &positions, StringBuffer &rangeText)
- {
- unsigned items = positions.ordinality();
- if (0 == items)
- return rangeText;
- unsigned start = positions.item(0);
- unsigned last = start;
- rangeText.append('[');
- unsigned p=1;
- while (true)
- {
- unsigned pos = p==items ? NotFound : positions.item(p++);
- if ((pos != last+1))
- {
- if (last-start>0)
- rangeText.append(start).append('-').append(last);
- else
- rangeText.append(last);
- if (NotFound == pos)
- break;
- rangeText.append(',');
- start = pos;
- }
- last = pos;
- }
- return rangeText.append(']');
- }
- class CLightWeightSuperFileConn: implements ISimpleSuperFileEnquiry, public CInterface
- {
- CFileLock lock;
- bool readonly;
- IArrayOf<IRemoteConnection> children;
- unsigned defaultTimeout;
- Owned<IUserDescriptor> udesc;
- static StringBuffer &getSubPath(StringBuffer &path,unsigned idx)
- {
- return path.append("SubFile[@num=\"").append(idx+1).append("\"]");
- }
- void migrateProp(const char *name, unsigned num,IPropertyTree *from,IPropertyTree *to,IPropertyTree *newt, bool allowunchanged)
- {
- StringBuffer aname("Attr/");
- aname.append(name);
- StringBuffer s;
- StringBuffer o;
- if (from->getProp(aname.str(),s))
- if ((num==1)||(allowunchanged&&to->getProp(aname.str(),o)&&(strcmp(s.str(),o.str())==0)))
- newt->setProp(name,s.str());
- }
- void migrateAttr(IPropertyTree *from,IPropertyTree *to)
- {
- // this tries hard to set what it knows but avoids sibling traversal
- if (!to)
- return;
- const char *desc = to->queryProp("Attr/@description");
- IPropertyTree* newt = getEmptyAttr();
- if (desc)
- newt->setProp("@description",desc);
- if (from) {
- unsigned num=to->getPropInt("@numsubfiles");
- migrateProp("@size",num,from,to,newt,false);
- migrateProp("@checkSum",num,from,to,newt,true);
- migrateProp("@formatCrc",num,from,to,newt,true);
- migrateProp("@recordSize",num,from,to,newt,true);
- MemoryBuffer mb;
- MemoryBuffer mbo;
- const char *aname = "Attr/_record_layout";
- if (from->getPropBin(aname,mb))
- if ((num==1)||(to->getPropBin(aname,mbo)&&
- (mb.length()==mbo.length())&&
- (memcmp(mb.bufferBase(),mbo.bufferBase(),mb.length())==0)))
- newt->setPropBin("_record_layout", mb.length(), mb.bufferBase());
- }
- to->setPropTree("Attr",newt);
- }
- void migrateSuperOwnersAttr(IPropertyTree *from)
- {
- if (!from)
- return;
- Owned<IPropertyTreeIterator> iter = from->getElements("SuperOwner");
- StringBuffer pname;
- StringBuffer query;
- ForEach(*iter) {
- if (iter->query().getProp("@name",pname.clear())) {
- CDfsLogicalFileName lfn;
- lfn.set(pname.str());
- lfn.makeFullnameQuery(query.clear(),DXB_SuperFile,true);
- Owned<IRemoteConnection> conn;
- try {
- conn.setown(querySDS().connect(query.str(),myProcessSession(),RTM_LOCK_WRITE,1000*60*5));
- }
- catch (ISDSException *e) {
- if (SDSExcpt_LockTimeout != e->errorCode())
- throw;
- e->Release();
- IWARNLOG("migrateSuperOwnersAttr: Could not lock parent %s",query.str());
- conn.setown(querySDS().connect(query.str(),myProcessSession(),0,defaultTimeout));
- }
- if (conn) {
- migrateAttr(from,conn->queryRoot());
- migrateSuperOwnersAttr(conn->queryRoot());
- }
- else
- IWARNLOG("migrateSuperOwnersAttr could not connect to parent superfile %s",lfn.get());
- }
- }
- }
- public:
- IMPLEMENT_IINTERFACE;
- CLightWeightSuperFileConn(unsigned _defaultTimeout, IUserDescriptor *_udesc)
- {
- defaultTimeout = _defaultTimeout;
- readonly = false;
- udesc.set(_udesc);
- }
- bool connect(CDistributedFileDirectory *parent,const char *title, const char *name, bool _readonly, bool *autocreate, unsigned timeout)
- {
- if (autocreate)
- *autocreate = false;
- readonly = _readonly;
- disconnect(false);
- CDfsLogicalFileName lfn;
- if (!lfn.setValidate(name))
- throw MakeStringException(-1,"%s: Invalid superfile name '%s'",title,name);
- if (lfn.isMulti()||lfn.isExternal()||lfn.isForeign())
- return false;
- unsigned mode = RTM_SUB | (readonly ? RTM_LOCK_READ : RTM_LOCK_WRITE);
- if (!lock.init(lfn, DXB_SuperFile, mode, timeout, title))
- {
- if (!autocreate) // NB not !*autocreate here !
- return false;
- IPropertyTree *root = createPTree();
- root->setPropInt("@interleaved",2);
- root->setPropInt("@numsubfiles",0);
- root->setPropTree("Attr",getEmptyAttr());
- parent->addEntry(lfn,root,true,false);
- mode = RTM_SUB | RTM_LOCK_WRITE;
- if (!lock.init(lfn, DXB_SuperFile, mode, timeout, title))
- throw MakeStringException(-1,"%s: Cannot create superfile '%s'",title,name);
- if (autocreate)
- *autocreate = true;
- }
- StringBuffer reason;
- if (!readonly&&checkProtectAttr(name,lock.queryRoot(),reason))
- throw MakeStringException(-1,"CDistributedSuperFile::%s %s",title,reason.str());
- return true;
- }
- void disconnect(bool commit)
- {
- if (lock.queryConnection()&&!readonly) {
- if (commit) {
- migrateSuperOwnersAttr(lock.queryRoot());
- CDateTime dt;
- dt.setNow();
- StringBuffer s;
- lock.queryRoot()->setProp("@modified",dt.getString(s).str());
- }
- else {
- ForEachItemIn(i,children)
- children.item(i).rollback();
- lock.queryConnection()->rollback();
- }
- }
- lock.clear();
- children.kill();
- }
- unsigned numSubFiles() const
- {
- return (unsigned)lock.queryRoot()->getPropInt("@numsubfiles");
- }
- bool getSubFileName(unsigned num,StringBuffer &name) const
- {
- if ((unsigned)lock.queryRoot()->getPropInt("@numsubfiles")<=num)
- return false;
- StringBuffer xpath;
- getSubPath(xpath,num);
- IPropertyTree *sub = lock.queryRoot()->queryPropTree(xpath.str());
- if (!sub)
- return false;
- name.append(sub->queryProp("@name"));
- return true;
- }
- unsigned findSubName(const char *subname) const
- {
- unsigned n = findSubFileOrd(subname);
- if (n!=NotFound)
- return n;
- StringBuffer lfn;
- normalizeLFN(subname,lfn);
- Owned<IPropertyTreeIterator> iter = lock.queryRoot()->getElements("SubFile");
- ForEach(*iter) {
- if (stricmp(iter->query().queryProp("@name"),lfn.str())==0) {
- unsigned ret=iter->query().getPropInt("@num");
- if (ret&&((unsigned)lock.queryRoot()->getPropInt("@numsubfiles")>=ret))
- return ret-1;
- }
- }
- return NotFound;
- }
- unsigned getContents(StringArray &contents) const
- {
- // slightly inefficient
- unsigned n = lock.queryRoot()->getPropInt("@numsubfiles");
- StringBuffer xpath;
- for (unsigned sni=0;sni<n;sni++) {
- getSubPath(xpath.clear(),sni);
- IPropertyTree *sub = lock.queryRoot()->queryPropTree(xpath.str());
- if (!sub)
- break;
- contents.append(sub->queryProp("@name"));
- }
- return contents.ordinality();
- }
- };
- // Contention never expected for this function!
- #define PROMOTE_CONN_TIMEOUT (60*1000) // how long to wait for a single superfile
- #define PROMOTE_DELAY (30*1000)
- // Check files don't share subfiles (MORE - make this part of swap files action?)
- static int hasCommonSubChildren(IDistributedSuperFile *orig, IDistributedSuperFile *dest)
- {
- unsigned origSubs = orig->numSubFiles();
- unsigned destSubs = dest->numSubFiles();
- if (origSubs == 0)
- return NotFound;
- for (unsigned j=0; j<origSubs; j++) {
- for (unsigned k=0; k<destSubs; k++) {
- if (strcmp(orig->querySubFile(j).queryLogicalName(), dest->querySubFile(k).queryLogicalName())==0)
- return j;
- }
- }
- return NotFound;
- }
- // MORE - use string arrays, rather than char* arrays or comma-separated strings
- void CDistributedFileDirectory::promoteSuperFiles(unsigned numsf,const char **sfnames,const char *addsubnames,bool delsub,bool createonlyonesuperfile,IUserDescriptor *user,unsigned timeout,StringArray &outunlinked)
- {
- if (!numsf)
- return;
- // Create a local transaction that will be destroyed
- Owned<IDistributedFileTransactionExt> transaction = new CDistributedFileTransaction(user);
- transaction->start();
- // Lookup all files (keep them in transaction's cache)
- bool created = false;
- unsigned files = numsf;
- for (unsigned i=0; i<numsf; i++) {
- Owned<IDistributedSuperFile> super = transaction->lookupSuperFile(sfnames[i]);
- if (!super.get()) {
- if (created && createonlyonesuperfile) {
- files = i;
- break;
- }
- Owned<IDistributedSuperFile> sfile = createSuperFile(sfnames[i],user,true,false,transaction);
- created = true;
- }
- }
- // If last file had sub-files, clean and fill outlinked
- Owned<IDistributedSuperFile> last = transaction->lookupSuperFile(sfnames[files-1]);
- assertex(last.get());
- unsigned lastSubs = last->numSubFiles();
- if (files == numsf && lastSubs > 0) {
- for (unsigned i=0; i<lastSubs; i++) {
- outunlinked.append(last->querySubFile(i).queryLogicalName());
- }
- last->removeSubFile(NULL,false,false,transaction);
- }
- last.clear();
- // Move up, starting from last
- for (unsigned i=files-1; i; i--) {
- Owned<IDistributedSuperFile> orig = transaction->lookupSuperFile(sfnames[i-1]);
- Owned<IDistributedSuperFile> dest = transaction->lookupSuperFile(sfnames[i]);
- assertex(orig.get());
- assertex(dest.get());
- int common = hasCommonSubChildren(orig, dest);
- if (common != NotFound) {
- throw MakeStringException(-1,"promoteSuperFiles: superfiles %s and %s share same subfile %s",
- orig->queryLogicalName(), dest->queryLogicalName(), orig->querySubFile(common).queryLogicalName());
- }
- orig->swapSuperFile(dest, transaction);
- }
- // Move new subs to first super, if any
- Owned<IDistributedSuperFile> first = transaction->lookupSuperFile(sfnames[0]);
- assertex(first.get());
- StringArray toadd;
- toadd.appendListUniq(addsubnames, ",");
- ForEachItemIn(i,toadd) {
- CDfsLogicalFileName lfn;
- if (!lfn.setValidate(toadd.item(i)))
- throw MakeStringException(-1,"promoteSuperFiles: invalid logical name to add: %s",toadd.item(i));
- first->addSubFile(toadd.item(i),false,NULL,false,transaction);
- }
- first.clear();
- transaction->commit();
- // MORE - once deletion of logic files are also in transaction we can move this up (and allow promote within transactions)
- if (delsub) {
- ForEachItemIn(j,outunlinked)
- removeEntry(outunlinked.item(j),user,transaction,timeout);
- }
- }
- ISimpleSuperFileEnquiry * CDistributedFileDirectory::getSimpleSuperFileEnquiry(const char *logicalname,const char *title,IUserDescriptor *udesc,unsigned timeout)
- {
- Owned<CLightWeightSuperFileConn> ret = new CLightWeightSuperFileConn(defaultTimeout,udesc);
- if (ret->connect(this,title,logicalname,true,NULL,timeout))
- return ret.getClear();
- return NULL;
- }
- bool CDistributedFileDirectory::getFileSuperOwners(const char *logicalname, StringArray &owners)
- {
- CFileLock lock;
- CDfsLogicalFileName lfn;
- if (!lfn.setValidate(logicalname))
- throw MakeStringException(-1,"CDistributedFileDirectory::getFileSuperOwners: Invalid file name '%s'",logicalname);
- if (lfn.isMulti()||lfn.isExternal()||lfn.isForeign())
- return false;
- CTimeMon tm(defaultTimeout);
- if (!lock.init(lfn, RTM_LOCK_READ, defaultTimeout, "CDistributedFileDirectory::getFileSuperOwners"))
- return false;
- CFileSuperOwnerLock superOwnerLock;
- unsigned remaining;
- tm.timedout(&remaining);
- verifyex(superOwnerLock.initWithFileLock(lfn, remaining, "CDistributedFileDirectory::getFileSuperOwners(SuperOwnerLock)", lock, RTM_LOCK_READ));
- Owned<IPropertyTreeIterator> iter = lock.queryRoot()->getElements("SuperOwner");
- StringBuffer pname;
- ForEach(*iter) {
- iter->query().getProp("@name",pname.clear());
- if (pname.length())
- owners.append(pname.str());
- }
- return true;
- }
- class CFileRelationship: implements IFileRelationship, public CInterface
- {
- Linked<IPropertyTree> pt;
- const char *queryProp(const char *name)
- {
- if (pt.get()) {
- const char *ret = pt->queryProp(name);
- if (ret)
- return ret;
- }
- return "";
- }
- public:
- IMPLEMENT_IINTERFACE;
- CFileRelationship(IPropertyTree *_pt)
- : pt(_pt)
- {
- }
- virtual const char *queryKind() { return queryProp("@kind"); }
- virtual const char *queryPrimaryFilename() { return queryProp("@primary"); }
- virtual const char *querySecondaryFilename() { return queryProp("@secondary"); }
- virtual const char *queryPrimaryFields() { return queryProp("@primflds"); }
- virtual const char *querySecondaryFields() { return queryProp("@secflds"); }
- virtual const char *queryCardinality() { return queryProp("@cardinality"); }
- virtual bool isPayload() { return pt->getPropBool("@payload"); }
- virtual const char *queryDescription() { return queryProp("Description"); }
- virtual IPropertyTree *queryTree() { return pt.get(); }
- };
- class CFileRelationshipIterator: implements IFileRelationshipIterator, public CInterface
- {
- unsigned num;
- unsigned idx;
- CMessageBuffer mb;
- Owned<CFileRelationship> r;
- Owned<IPropertyTree> pt;
- Linked<INode> foreigndali;
- unsigned defaultTimeout;
- bool setPT()
- {
- if (idx<num) {
- pt.setown(createPTree(mb));
- addForeignName(*pt,foreigndali,"@primary");
- addForeignName(*pt,foreigndali,"@secondary");
- }
- return pt.get()!=NULL;
- }
- public:
- IMPLEMENT_IINTERFACE;
- CFileRelationshipIterator(unsigned timems)
- {
- num = 0;
- idx = 0;
- mb.append(num);
- defaultTimeout = timems;
- }
- void init(
- INode *_foreigndali,
- unsigned foreigndalitimeout,
- const char *primary,
- const char *secondary,
- const char *primflds,
- const char *secflds,
- const char *kind,
- const char *cardinality,
- const bool *payload )
- {
- foreigndali.set(_foreigndali);
- if (isLocalDali(foreigndali)) {
- CConnectLock connlock("lookupFileRelationships",querySdsRelationshipsRoot(),false,false,false,defaultTimeout);
- StringBuffer xpath;
- CDistributedFileDirectory::getFileRelationshipXPath(xpath,primary,secondary,primflds,secflds,kind,cardinality,payload);
- Owned<IPropertyTreeIterator> iter = connlock.conn?connlock.conn->getElements(xpath.str()):NULL;
- mb.clear();
- unsigned count = 0;
- mb.append(count);
- // save as sequence of branches
- if (iter) {
- ForEach(*iter.get()) {
- iter->query().serialize(mb);
- count++;
- }
- mb.writeDirect(0,sizeof(count),&count);
- }
- }
- else {
- byte payloadb = 255;
- if (payload)
- payloadb = *payload?1:0;
- mb.clear().append((int)MDFS_ITERATE_RELATIONSHIPS).append(primary).append(secondary).append(primflds).append(secflds).append(kind).append(cardinality).append(payloadb);
- foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
- checkDfsReplyException(mb);
- if (mb.length()<sizeof(unsigned))
- mb.clear().append((unsigned)0);
- }
- }
- void initall(const char *filename)
- {
- StringBuffer xpath;
- Owned<IPropertyTreeIterator> iter;
- mb.clear();
- unsigned count = 0;
- mb.append(count);
- {
- CConnectLock connlock("lookupFileRelationships",querySdsRelationshipsRoot(),false,false,false,defaultTimeout);
- CDistributedFileDirectory::getFileRelationshipXPath(xpath,filename,NULL,NULL,NULL,NULL,NULL,NULL);
- // save as sequence of branches
- iter.setown(connlock.conn?connlock.conn->getElements(xpath.str()):NULL);
- if (iter) {
- ForEach(*iter.get()) {
- iter->query().serialize(mb);
- count++;
- }
- }
- }
- { // Kludge - seems to be a bug in getElements without second conn lock
- CConnectLock connlock("lookupFileRelationships",querySdsRelationshipsRoot(),false,false,false,defaultTimeout);
- xpath.clear();
- CDistributedFileDirectory::getFileRelationshipXPath(xpath,NULL,filename,NULL,NULL,NULL,NULL,NULL);
- iter.clear();
- iter.setown(connlock.conn?connlock.conn->getElements(xpath.str()):NULL);
- if (iter) {
- ForEach(*iter.get()) {
- IPropertyTree &it = iter->query();
- const char *fn1 = it.queryProp("@primary");
- if (!fn1||(strcmp(fn1,filename)!=0)) { // see if already done
- it.serialize(mb);
- count++;
- }
- }
- }
- }
- mb.writeDirect(0,sizeof(count),&count);
- }
- bool first()
- {
- r.clear();
- pt.clear();
- idx = 0;
- mb.reset().read(num);
- return setPT();
- }
- bool next()
- {
- r.clear();
- pt.clear();
- idx++;
- return setPT();
- }
- bool isValid()
- {
- return pt.get()!=NULL;
- }
- IFileRelationship & query()
- {
- if (!r)
- r.setown(new CFileRelationship(pt));
- return *r;
- }
- };
- static bool isWild(const char *path,bool emptydefault=false)
- {
- if (!path||!*path)
- return emptydefault;
- return ((strchr(path,'?')||strchr(path,'*')));
- }
- static void addRelationCondition(StringBuffer &xpath,const char *fld,const char *mask)
- {
- if (!mask||!*mask||((*mask=='*')&&(!mask[1])))
- return;
- xpath.append('[').append(fld).append('=');
- if (isWild(mask))
- xpath.append('~');
- xpath.append('"').append(mask).append("\"]");
- }
- static void addRelationBoolCondition(StringBuffer &xpath,const char *fld,const bool *mask)
- {
- if (!mask)
- return;
- xpath.append('[').append(fld).append("=\"");
- if (*mask)
- xpath.append("1\"]");
- else
- xpath.append("0\"]");
- }
- static const char *normLFN(const char *name,CDfsLogicalFileName &logicalname,const char *title)
- {
- if (isWild(name,true))
- return name;
- if (!logicalname.setValidate(name))
- throw MakeStringException(-1,"%s: invalid logical file name '%s'",title,name);
- if (logicalname.isForeign()) {
- SocketEndpoint ep;
- Owned<INode> fd = createINode(ep);
- if (logicalname.getEp(ep)&&isLocalDali(fd)) // see if pointing back at self
- logicalname.clearForeign();
- }
- return logicalname.get();
- }
- StringBuffer &CDistributedFileDirectory::getFileRelationshipXPath(
- StringBuffer &xpath,
- const char *primary,
- const char *secondary,
- const char *primflds,
- const char *secflds,
- const char *kind,
- const char *cardinality,
- const bool *payload
- )
- {
- xpath.append("Relationship");
- CDfsLogicalFileName lfn;
- addRelationCondition(xpath,"@kind",kind);
- addRelationCondition(xpath,"@primary",normLFN(primary,lfn,"findFileRelationship(primary)"));
- addRelationCondition(xpath,"@secondary",normLFN(secondary,lfn,"findFileRelationship(secondary)"));
- addRelationCondition(xpath,"@primflds",primflds);
- addRelationCondition(xpath,"@secflds",secflds);
- addRelationCondition(xpath,"@cardinality",cardinality);
- addRelationBoolCondition(xpath,"@payload",payload);
- return xpath;
- }
- void CDistributedFileDirectory::doRemoveFileRelationship(
- IRemoteConnection *conn,
- const char *primary,
- const char *secondary,
- const char *primflds,
- const char *secflds,
- const char *kind
- )
- {
- if (!conn)
- return;
- StringBuffer xpath;
- CDistributedFileDirectory::getFileRelationshipXPath(xpath,primary,secondary,primflds,secflds,kind,NULL,NULL);
- Owned<IPropertyTreeIterator> iter = conn->getElements(xpath.str());
- IArrayOf<IPropertyTree> toremove;
- ForEach(*iter) {
- IPropertyTree &t = iter->query();
- toremove.append(*LINK(&t));
- }
- ForEachItemIn(i, toremove) {
- conn->queryRoot()->removeTree(&toremove.item(i));
- }
- }
- void CDistributedFileDirectory::addFileRelationship(
- const char *primary,
- const char *secondary,
- const char *primflds,
- const char *secflds,
- const char *kind,
- const char *cardinality,
- bool payload,
- IUserDescriptor *user,
- const char *description=NULL
- )
- {
- if (!kind||!*kind)
- kind = S_LINK_RELATIONSHIP_KIND;
- Owned<IPropertyTree> pt = createPTree("Relationship");
- if (isWild(primary,true)||isWild(secondary,true)||isWild(primflds,false)||isWild(secflds,false)||isWild(cardinality,false))
- throw MakeStringException(-1,"Wildcard not allowed in addFileRelation");
- CDfsLogicalFileName pfn;
- if (!pfn.setValidate(primary))
- throw MakeStringException(-1,"addFileRelationship invalid primary name '%s'",primary);
- if (pfn.isExternal()||pfn.isForeign()||pfn.isQuery())
- throw MakeStringException(-1,"addFileRelationship primary %s not allowed",pfn.get());
- primary = pfn.get();
- if (!exists(primary,user))
- throw MakeStringException(-1,"addFileRelationship primary %s does not exist",primary);
- CDfsLogicalFileName sfn;
- if (!sfn.setValidate(secondary))
- throw MakeStringException(-1,"addFileRelationship invalid secondary name '%s'",secondary);
- if (sfn.isExternal()||sfn.isForeign()||sfn.isQuery())
- throw MakeStringException(-1,"addFileRelationship secondary %s not allowed",sfn.get());
- secondary = sfn.get();
- if (!exists(secondary,user))
- throw MakeStringException(-1,"addFileRelationship secondary %s does not exist",secondary);
- if (cardinality&&*cardinality&&!strchr(cardinality,':'))
- throw MakeStringException(-1,"addFileRelationship cardinality %s invalid",cardinality);
- pt->setProp("@kind",kind);
- pt->setProp("@primary",primary);
- pt->setProp("@secondary",secondary);
- pt->setProp("@cardinality",cardinality);
- pt->setProp("@primflds",primflds);
- pt->setProp("@secflds",secflds);
- pt->setPropBool("@payload",payload);
- if (description&&*description)
- pt->setProp("Description",description);
- StringBuffer xpath(querySdsFilesRoot());
- for (unsigned i=0;i<2;i++) {
- CConnectLock connlock("addFileRelation",querySdsRelationshipsRoot(),true,false,false,defaultTimeout);
- if (!connlock.conn) {
- CConnectLock connlock2("addFileRelation.2",querySdsFilesRoot(),true,false,false,defaultTimeout);
- if (!connlock2.conn)
- return;
- Owned<IPropertyTree> ptr = createPTree("Relationships");
- connlock2.conn->queryRoot()->addPropTree("Relationships",ptr.getClear());
- continue;
- }
- StringBuffer query;
- doRemoveFileRelationship(connlock.conn,primary,secondary,primflds,secflds,kind);
- connlock.conn->queryRoot()->addPropTree("Relationship",pt.getClear());
- break;
- }
- }
- void CDistributedFileDirectory::removeFileRelationships(
- const char *primary,
- const char *secondary,
- const char *primflds,
- const char *secflds,
- const char *kind
- )
- {
- if ((!primary||!*primary||(strcmp(primary,"*")==0))&&
- (!secondary||!*secondary||(strcmp(secondary,"*")==0)))
- throw MakeStringException(-1,"removeFileRelationships primary and secondary cannot both be wild");
- CConnectLock connlock("removeFileRelation",querySdsRelationshipsRoot(),true,false,false,defaultTimeout);
- doRemoveFileRelationship(connlock.conn,primary,secondary,primflds,secflds,kind);
- }
- IFileRelationshipIterator *CDistributedFileDirectory::lookupFileRelationships(
- const char *primary,
- const char *secondary,
- const char *primflds,
- const char *secflds,
- const char *kind,
- const char *cardinality,
- const bool *payload,
- const char *foreigndali,
- unsigned foreigndalitimeout
- )
- {
- Owned<INode> foreign;
- if (foreigndali&&*foreigndali) {
- SocketEndpoint ep(foreigndali);
- if (ep.isNull())
- throw MakeStringException(-1,"lookupFileRelationships::Cannot resolve foreign dali %s",foreigndali);
- foreign.setown(createINode(ep));
- }
- Owned<CFileRelationshipIterator> ret = new CFileRelationshipIterator(defaultTimeout);
- ret->init(foreign,foreigndalitimeout,primary,secondary,primflds,secflds,kind,cardinality,payload);
- return ret.getClear();
- }
- void CDistributedFileDirectory::removeAllFileRelationships(const char *filename)
- {
- if (!filename||!*filename||(strcmp(filename,"*")==0))
- throw MakeStringException(-1,"removeAllFileRelationships filename cannot be wild");
- {
- CConnectLock connlock("removeFileRelation",querySdsRelationshipsRoot(),true,false,false,defaultTimeout);
- doRemoveFileRelationship(connlock.conn,filename,NULL,NULL,NULL,NULL);
- }
- { // kludge bug in getElements if connection used twice
- CConnectLock connlock("removeFileRelation",querySdsRelationshipsRoot(),true,false,false,defaultTimeout);
- doRemoveFileRelationship(connlock.conn,NULL,filename,NULL,NULL,NULL);
- }
- }
- IFileRelationshipIterator *CDistributedFileDirectory::lookupAllFileRelationships(
- const char *filename)
- {
- if (isWild(filename,true))
- throw MakeStringException(-1,"Wildcard filename not allowed in lookupAllFileRelationships");
- CDfsLogicalFileName lfn;
- normLFN(filename,lfn,"lookupAllFileRelationships");
- Owned<CFileRelationshipIterator> ret = new CFileRelationshipIterator(defaultTimeout);
- ret->initall(lfn.get());
- return ret.getClear();
- }
- void CDistributedFileDirectory::renameFileRelationships(const char *oldname,const char *newname,IFileRelationshipIterator *reliter,IUserDescriptor*user)
- {
- CDfsLogicalFileName oldlfn;
- normLFN(oldname,oldlfn,"renameFileRelationships(old name)");
- CDfsLogicalFileName newlfn;
- normLFN(newname,newlfn,"renameFileRelationships(new name)");
- ForEach(*reliter) {
- try {
- IFileRelationship &r = reliter->query();
- bool adj = false;
- const char *pf = r.queryPrimaryFilename();
- if (!pf)
- continue;
- if (strcmp(pf,oldlfn.get())==0) {
- adj = true;
- pf = newlfn.get();
- }
- const char *sf = r.querySecondaryFilename();
- if (!sf)
- continue;
- if (strcmp(sf,oldlfn.get())==0) {
- adj = true;
- sf = newlfn.get();
- }
- if (adj)
- addFileRelationship(pf,sf,r.queryPrimaryFields(),r.querySecondaryFields(),r.queryKind(),r.queryCardinality(),r.isPayload(),user,r.queryDescription());
- }
- catch (IException *e)
- {
- EXCLOG(e,"renameFileRelationships");
- e->Release();
- }
- }
- }
- // JCSMORE what was this for, not called by anything afaics
- bool CDistributedFileDirectory::publishMetaFileXML(const CDfsLogicalFileName &logicalname,IUserDescriptor *user)
- {
- if (logicalname.isExternal()||logicalname.isForeign()||logicalname.isQuery())
- return false;
- Owned<IPropertyTree> file = getFileTree(logicalname.get(),user,NULL,FOREIGN_DALI_TIMEOUT,true);
- if (!file.get())
- return false;
- if (strcmp(file->queryName(),queryDfsXmlBranchName(DXB_SuperFile))==0)
- return false;
- unsigned max = file->getPropInt("@numparts");
- SocketEndpointArray ips;
- StringBuffer xpath;
- StringBuffer ipstr;
- for (unsigned i=0;i<max;i++) { // probably could be done better
- xpath.clear().append("Part[@num=\"").append(i+1).append("\"]");
- Owned<IPropertyTree> child = file->getPropTree(xpath.str());
- SocketEndpoint ep;
- if (child.get()&&child->getProp("@node",ipstr.clear()))
- ep.ipset(ipstr.str());
- ips.append(ep);
- }
- Owned<IException> exc;
- CriticalSection errcrit;
- class casyncfor: public CAsyncFor
- {
- IPropertyTree* file;
- CriticalSection &errcrit;
- Owned<IException> &exc;
- SocketEndpointArray &ips;
- public:
- casyncfor(IPropertyTree* _file,SocketEndpointArray &_ips,Owned<IException> &_exc,CriticalSection &_errcrit)
- : ips(_ips), exc(_exc), errcrit(_errcrit)
- {
- file = _file;
- }
- void Do(unsigned i)
- {
- UnsignedArray parts;
- const SocketEndpoint &ep = ips.item(i);
- if (ep.isNull())
- return;
- ForEachItemIn(j,ips) {
- if (j==i)
- parts.append(i);
- else if (ep.ipequals(ips.item(j))) {
- if (j<i)
- return; // already done
- parts.append(j);
- }
- }
- try {
- StringBuffer path;
- StringBuffer mask;
- if (file->getProp("@directory",path)&&file->getProp("@partmask",mask)) {
- addPathSepChar(path).append(mask);
- StringBuffer outpath;
- StringBuffer tail("META__");
- splitFilename(path.str(), &outpath, &outpath, &tail, NULL);
- outpath.append(tail).append(".xml");
- Owned<IPropertyTree> pt = createPTreeFromIPT(file);
- filterParts(pt,parts);
- StringBuffer str;
- toXML(pt, str);
- RemoteFilename rfn;
- rfn.setPath(ep,outpath.str());
- Owned<IFile> out = createIFile(rfn);
- Owned<IFileIO> outio = out->open(IFOcreate);
- if (outio)
- outio->write(0,str.length(),str.str());
- }
- }
- catch(IException *e)
- {
- CriticalBlock block(errcrit);
- EXCLOG(e,"publishMetaFileXML");
- if (!exc.get())
- exc.setown(e);
- else
- e->Release();
- }
- }
- } afor(file,ips,exc,errcrit);
- afor.For(max,20);
- if (exc)
- throw exc.getClear();
- return true;
- }
- IFileDescriptor *CDistributedFileDirectory::createDescriptorFromMetaFile(const CDfsLogicalFileName &logicalname,IUserDescriptor *user)
- {
- return NULL; // TBD
- }
- // Overwrite protection
- bool CDistributedFileDirectory::isProtectedFile(const CDfsLogicalFileName &logicalName, unsigned timeout)
- {
- CFileAttrLock attrLock;
- if (!attrLock.init(logicalName, RTM_LOCK_READ, NULL, timeout?timeout:INFINITE, "CDistributedFileDirectory::isProtectedFile"))
- return false; // timeout will raise exception
- Owned<IPropertyTreeIterator> wpiter = attrLock.queryRoot()->getElements("Protect");
- bool prot = false;
- ForEach(*wpiter) {
- IPropertyTree &t = wpiter->query();
- if (t.getPropInt("@count")) {
- prot = true;
- break;
- }
- }
- // timeout retry TBD
- return prot;
- }
- unsigned CDistributedFileDirectory::queryProtectedCount(const CDfsLogicalFileName &logicalName, const char *owner)
- {
- CFileAttrLock attrLock;
- if (!attrLock.init(logicalName, RTM_LOCK_READ, NULL, defaultTimeout, "CDistributedFileDirectory::queryProtectedCount"))
- return 0; // timeout will raise exception
- Owned<IPropertyTreeIterator> wpiter = attrLock.queryRoot()->getElements("Protect");
- unsigned count = 0;
- ForEach(*wpiter) {
- IPropertyTree &t = wpiter->query();
- const char *name = t.queryProp("@name");
- if (!owner||!*owner||(name&&(strcmp(owner,name)==0)))
- count += t.getPropInt("@count");
- }
- return count;
- }
- bool CDistributedFileDirectory::getProtectedInfo(const CDfsLogicalFileName &logicalName, StringArray &names, UnsignedArray &counts)
- {
- CFileAttrLock attrLock;
- if (!attrLock.init(logicalName, RTM_LOCK_READ, NULL, defaultTimeout, "CDistributedFileDirectory::getProtectedInfo"))
- return false; // timeout will raise exception
- Owned<IPropertyTreeIterator> wpiter = attrLock.queryRoot()->getElements("Protect");
- bool prot = false;
- ForEach(*wpiter) {
- IPropertyTree &t = wpiter->query();
- const char *name = t.queryProp("@name");
- names.append(name?name:"<Unknown>");
- unsigned c = t.getPropInt("@count");
- if (c)
- prot = true;
- counts.append(c);
- }
- return prot;
- }
- IDFProtectedIterator *CDistributedFileDirectory::lookupProtectedFiles(const char *owner,bool notsuper,bool superonly)
- {
- return new CDFProtectedIterator(owner,notsuper,superonly,defaultTimeout);
- }
- const char* DFUQResultFieldNames[] = { "@name", "@description", "@group", "@kind", "@modified", "@job", "@owner",
- "@DFUSFrecordCount", "@recordCount", "@recordSize", "@DFUSFsize", "@size", "@workunit", "@DFUSFcluster", "@numsubfiles",
- "@accessed", "@numparts", "@compressedSize", "@directory", "@partmask", "@superowners", "@persistent", "@protect", "@compressed" };
- extern da_decl const char* getDFUQResultFieldName(DFUQResultField feild)
- {
- return DFUQResultFieldNames[feild];
- }
- IPropertyTreeIterator *deserializeFileAttrIterator(MemoryBuffer& mb, unsigned numFiles, DFUQResultField* localFilters, const char* localFilterBuf)
- {
- class CFileAttrIterator: implements IPropertyTreeIterator, public CInterface
- {
- size32_t fileDataStart;
- Owned<IPropertyTree> cur;
- StringArray fileNodeGroups;
- void setFileNodeGroup(IPropertyTree *attr, const char* group, StringArray& nodeGroupFilter)
- {
- if (!group || !*group)
- return;
- //The group may contain multiple clusters and some of them may match with the clusterFilter.
- if (nodeGroupFilter.length() == 1)
- attr->setProp(getDFUQResultFieldName(DFUQRFnodegroup), nodeGroupFilter.item(0));//Filter has been handled on server side.
- else
- {
- StringArray groups;
- groups.appendListUniq(group, ",");
- ForEachItemIn(i,groups)
- {
- //Add a group if no group filter or the group matches with group filter
- const char* node = groups.item(i);
- if (node && *node && ((!nodeGroupFilter.length()) || (nodeGroupFilter.find(node) != NotFound)))
- fileNodeGroups.append(node);
- }
- if (fileNodeGroups.length())
- {
- //if this file exists on multiple groups, set one of the groups as the "@DFUSFnodegroup" prop for
- //this attr, leaving the rest inside the fileNodeGroups array. Those groups will be used by the
- //duplicateFileAttrOnOtherNodeGroup() to duplicate this file attr on other groups.
- attr->setProp(getDFUQResultFieldName(DFUQRFnodegroup), fileNodeGroups.item(fileNodeGroups.length() -1));
- fileNodeGroups.pop();
- }
- }
- }
- void setRecordCount(IPropertyTree* file)
- {
- __int64 recordCount = 0;
- if (file->hasProp(getDFUQResultFieldName(DFUQRForigrecordcount)))
- recordCount = file->getPropInt64(getDFUQResultFieldName(DFUQRForigrecordcount));
- else
- {
- __int64 recordSize=file->getPropInt64(getDFUQResultFieldName(DFUQRFrecordsize),0);
- if(recordSize)
- {
- __int64 size=file->getPropInt64(getDFUQResultFieldName(DFUQRForigsize),-1);
- recordCount = size/recordSize;
- }
- }
- file->setPropInt64(getDFUQResultFieldName(DFUQRFrecordcount),recordCount);
- return;
- }
- void setIsCompressed(IPropertyTree* file)
- {
- if (isCompressed(*file) || isFileKey(*file))
- file->setPropBool(getDFUQResultFieldName(DFUQRFiscompressed), true);
- }
- IPropertyTree *deserializeFileAttr(MemoryBuffer &mb, StringArray& nodeGroupFilter)
- {
- IPropertyTree *attr = getEmptyAttr();
- StringAttr val;
- unsigned n;
- mb.read(val);
- attr->setProp(getDFUQResultFieldName(DFUQRFname),val.get());
- mb.read(val);
- if (strieq(val,"!SF"))
- {
- mb.read(n);
- attr->setPropInt(getDFUQResultFieldName(DFUQRFnumsubfiles),n);
- mb.read(val); // not used currently
- }
- else
- {
- attr->setProp(getDFUQResultFieldName(DFUQRFdirectory),val.get());
- mb.read(n);
- attr->setPropInt(getDFUQResultFieldName(DFUQRFnumparts),n);
- mb.read(val);
- attr->setProp(getDFUQResultFieldName(DFUQRFpartmask),val.get());
- }
- mb.read(val);
- attr->setProp(getDFUQResultFieldName(DFUQRFtimemodified),val.get());
- unsigned count;
- mb.read(count);
- StringAttr at;
- while (count--)
- {
- mb.read(at);
- mb.read(val);
- attr->setProp(at.get(),val.get());
- if (strieq(at.get(), getDFUQResultFieldName(DFUQRFnodegroups)))
- setFileNodeGroup(attr, val.get(), nodeGroupFilter);
- }
- attr->setPropInt64(getDFUQResultFieldName(DFUQRFsize), attr->getPropInt64(getDFUQResultFieldName(DFUQRForigsize), -1));//Sort the files with empty size to front
- setRecordCount(attr);
- setIsCompressed(attr);
- return attr;
- }
- IPropertyTree *duplicateFileAttrOnOtherNodeGroup(IPropertyTree *previousAttr)
- {
- IPropertyTree *attr = getEmptyAttr();
- Owned<IAttributeIterator> ai = previousAttr->getAttributes();
- ForEach(*ai)
- attr->setProp(ai->queryName(),ai->queryValue());
- attr->setProp(getDFUQResultFieldName(DFUQRFnodegroup), fileNodeGroups.item(fileNodeGroups.length()-1));
- fileNodeGroups.pop();
- return attr;
- }
- public:
- IMPLEMENT_IINTERFACE;
- MemoryBuffer mb;
- unsigned numfiles;
- StringArray nodeGroupFilter;
- CFileAttrIterator(MemoryBuffer &_mb, unsigned _numfiles) : numfiles(_numfiles)
- {
- /* not particuarly nice, but buffer contains extra meta info ahead of serialized file info
- * record position to rewind to, if iterator reused.
- */
- fileDataStart = _mb.getPos();
- mb.swapWith(_mb);
- }
- bool first()
- {
- mb.reset(fileDataStart);
- return next();
- }
- bool next()
- {
- if (fileNodeGroups.length())
- {
- IPropertyTree *attr = duplicateFileAttrOnOtherNodeGroup(cur);
- cur.clear();
- cur.setown(attr);
- return true;
- }
- cur.clear();
- if (mb.getPos()>=mb.length())
- return false;
- cur.setown(deserializeFileAttr(mb, nodeGroupFilter));
- return true;
- }
- bool isValid()
- {
- return cur.get()!=NULL;
- }
- IPropertyTree & query()
- {
- return *cur;
- }
- void setLocalFilters(DFUQResultField* localFilters, const char* localFilterBuf)
- {
- if (!localFilters || !localFilterBuf || !*localFilterBuf)
- return;
- const char *fv = localFilterBuf;
- for (unsigned i=0;localFilters[i]!=DFUQRFterm;i++)
- {
- int fmt = localFilters[i];
- int subfmt = (fmt&0xff);
- if ((subfmt==DFUQRFnodegroup) && fv && *fv)
- nodeGroupFilter.appendListUniq(fv, ",");
- //Add more if needed
- fv = fv + strlen(fv)+1;
- }
- }
- } *fai = new CFileAttrIterator(mb, numFiles);
- fai->setLocalFilters(localFilters, localFilterBuf);
- return fai;
- }
- IPropertyTreeIterator *CDistributedFileDirectory::getDFAttributesTreeIterator(const char* filters, DFUQResultField* localFilters,
- const char* localFilterBuf, IUserDescriptor* user, bool recursive, bool& allMatchingFilesReceived, INode* foreigndali, unsigned foreigndalitimeout)
- {
- CMessageBuffer mb;
- CDaliVersion serverVersionNeeded("3.13");
- bool legacy = (queryDaliServerVersion().compare(serverVersionNeeded) < 0);
- if (legacy)
- mb.append((int)MDFS_ITERATE_FILTEREDFILES);
- else
- mb.append((int)MDFS_ITERATE_FILTEREDFILES2);
- mb.append(filters).append(recursive);
- if (user)
- {
- user->serializeWithoutPassword(mb);
- }
- if (foreigndali)
- foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
- else
- queryCoven().sendRecv(mb,RANK_RANDOM,MPTAG_DFS_REQUEST);
- checkDfsReplyException(mb);
- unsigned numfiles;
- mb.read(numfiles);
- if (legacy)
- allMatchingFilesReceived = true; // don't know any better
- else
- mb.read(allMatchingFilesReceived);
- return deserializeFileAttrIterator(mb, numfiles, localFilters, localFilterBuf);
- }
- IDFAttributesIterator* CDistributedFileDirectory::getLogicalFiles(
- IUserDescriptor* udesc,
- DFUQResultField *sortOrder, // list of fields to sort by (terminated by DFUSFterm)
- const void *filters, // (appended) string values for filters used by dali server
- DFUQResultField *localFilters, //used for filtering query result received from dali server.
- const void *localFilterBuf,
- unsigned startOffset,
- unsigned maxNum,
- __int64 *cacheHint,
- unsigned *total,
- bool *allMatchingFiles,
- bool recursive,
- bool sorted)
- {
- class CDFUPager : implements IElementsPager, public CSimpleInterface
- {
- IUserDescriptor* udesc;
- //StringAttr clusterFilter;
- StringAttr filters;
- DFUQResultField *localFilters;
- StringAttr localFilterBuf;
- StringAttr sortOrder;
- bool recursive, sorted;
- bool allMatchingFilesReceived;
- public:
- IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
- CDFUPager(IUserDescriptor* _udesc, const char*_filters, DFUQResultField*_localFilters, const char*_localFilterBuf,
- const char*_sortOrder, bool _recursive, bool _sorted) : udesc(_udesc), filters(_filters), localFilters(_localFilters), localFilterBuf(_localFilterBuf),
- sortOrder(_sortOrder), recursive(_recursive), sorted(_sorted)
- {
- allMatchingFilesReceived = true;
- }
- virtual IRemoteConnection* getElements(IArrayOf<IPropertyTree> &elements)
- {
- Owned<IPropertyTreeIterator> fi = queryDistributedFileDirectory().getDFAttributesTreeIterator(filters.get(),
- localFilters, localFilterBuf.get(), udesc, recursive, allMatchingFilesReceived);
- StringArray unknownAttributes;
- sortElements(fi, sorted ? sortOrder.get() : NULL, NULL, NULL, unknownAttributes, elements);
- return NULL;
- }
- virtual bool allMatchingElementsReceived() { return allMatchingFilesReceived; }
- };
- StringBuffer so;
- if (sorted && sortOrder)
- {
- for (unsigned i=0;sortOrder[i]!=DFUQRFterm;i++)
- {
- if (so.length())
- so.append(',');
- int fmt = sortOrder[i];
- if (fmt&DFUQRFreverse)
- so.append('-');
- if (fmt&DFUQRFnocase)
- so.append('?');
- if (fmt&DFUQRFnumeric)
- so.append('#');
- so.append(getDFUQResultFieldName((DFUQResultField) (fmt&0xff)));
- }
- }
- IArrayOf<IPropertyTree> results;
- Owned<IElementsPager> elementsPager = new CDFUPager(udesc, (const char*) filters, localFilters, (const char*) localFilterBuf,
- so.length()?so.str():NULL, recursive, sorted);
- Owned<IRemoteConnection> conn = getElementsPaged(elementsPager,startOffset,maxNum,NULL,"",cacheHint,results,total,allMatchingFiles,false);
- return new CDFAttributeIterator(results);
- }
- IDFAttributesIterator* CDistributedFileDirectory::getLogicalFilesSorted(
- IUserDescriptor* udesc,
- DFUQResultField *sortOrder, // list of fields to sort by (terminated by DFUSFterm)
- const void *filters, // (appended) string values for filters used by dali server
- DFUQResultField *localFilters, //used for filtering query result received from dali server.
- const void *localFilterBuf,
- unsigned startOffset,
- unsigned maxNum,
- __int64 *cacheHint,
- unsigned *total,
- bool *allMatchingFiles)
- {
- return getLogicalFiles(udesc, sortOrder, filters, localFilters, localFilterBuf, startOffset, maxNum,
- cacheHint, total, allMatchingFiles, true, true);
- }
- bool CDistributedFileDirectory::removePhysicalPartFiles(const char *logicalName, IFileDescriptor *fileDesc, IMultiException *mexcept, unsigned numParallelDeletes)
- {
- class casyncfor: public CAsyncFor
- {
- IFileDescriptor *fileDesc;
- CriticalSection errcrit;
- IMultiException *mexcept;
- public:
- bool ok;
- bool islazy;
- casyncfor(IFileDescriptor *_fileDesc, IMultiException *_mexcept)
- {
- fileDesc = _fileDesc;
- ok = true;
- islazy = false;
- mexcept = _mexcept;
- }
- void Do(unsigned i)
- {
- IPartDescriptor *part = fileDesc->queryPart(i);
- unsigned nc = part->numCopies();
- for (unsigned copy = 0; copy < nc; copy++)
- {
- RemoteFilename rfn;
- part->getFilename(copy, rfn);
- Owned<IFile> partfile = createIFile(rfn);
- StringBuffer eps;
- try
- {
- unsigned start = msTick();
- if (!partfile->remove()&&(copy==0)&&!islazy) // only warn about missing primary files
- LOG(MCwarning, unknownJob, "Failed to remove file part %s from %s", partfile->queryFilename(),rfn.queryEndpoint().getUrlStr(eps).str());
- else
- {
- unsigned t = msTick()-start;
- if (t>5*1000)
- LOG(MCwarning, unknownJob, "Removing %s from %s took %ds", partfile->queryFilename(), rfn.queryEndpoint().getUrlStr(eps).str(), t/1000);
- }
- }
- catch (IException *e)
- {
- CriticalBlock block(errcrit);
- if (mexcept)
- mexcept->append(*e);
- else
- {
- StringBuffer s("Failed to remove file part ");
- s.append(partfile->queryFilename()).append(" from ");
- rfn.queryEndpoint().getUrlStr(s);
- EXCLOG(e, s.str());
- e->Release();
- }
- ok = false;
- }
- }
- }
- } afor(fileDesc, mexcept);
- afor.islazy = fileDesc->queryProperties().getPropBool("@lazy");
- if (0 == numParallelDeletes)
- numParallelDeletes = fileDesc->numParts();
- if (numParallelDeletes > MAX_PHYSICAL_DELETE_THREADS)
- {
- WARNLOG("Limiting parallel physical delete threads to %d", MAX_PHYSICAL_DELETE_THREADS);
- numParallelDeletes = MAX_PHYSICAL_DELETE_THREADS;
- }
- afor.For(fileDesc->numParts(),numParallelDeletes,false,true);
- return afor.ok;
- }
- #ifdef _USE_CPPUNIT
- /*
- * This method removes files only logically. removeEntry() used to do that, but the only
- * external use for logical-files only is this test-suite, so I'd rather hack the test
- * suite than expose the behaviour to more viewers.
- */
- extern da_decl void removeLogical(const char *fname, IUserDescriptor *user) {
- if (queryDistributedFileDirectory().exists(fname, user)) {
- Owned<IDistributedFile> file = queryDistributedFileDirectory().lookup(fname, user, true);
- CDistributedFile *f = QUERYINTERFACE(file.get(),CDistributedFile);
- assert(f);
- f->detachLogical();
- }
- }
- #endif // _USE_CPPUNIT
|