1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983 |
- /*##############################################################################
- 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"
- #include <string>
- #include <vector>
- #include <unordered_map>
- #include <algorithm>
- #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(const 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, const IPropertyTree *init=nullptr)
- {
- 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);
- case DFSERR_RestrictedFileAccessDenied:
- return str.append(": Access to restricted file denied: ").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, bool privilegedUser, unsigned timeout) override;
- IDistributedFile *lookup(CDfsLogicalFileName &logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, bool privilegedUser, unsigned timeout) override;
- /* 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,bool isPrivilegedUser);
- 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) ;
- 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();
- }
- perms = querySessionManager().getPermissionsLDAP(queryDfsXmlBranchName(DXB_Scope),scopename,user,auditflags);
- 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());
- CTimeMon timer(timeoutms);
- for (;;)
- {
- // Transaction files have already been unlocked at this point, delete all remaining files
- Owned<IDistributedFile> file = queryDistributedFileDirectory().lookup(lfn, user, true, false, true, nullptr, defaultPrivilegedUser, 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());
- Owned<IException> timeoutException;
- // 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:
- timeoutException.setown(e);
- break;
- default:
- throw;
- }
- }
- file.clear();
- unsigned sleepTime = SDS_TRANSACTION_RETRY/2+(getRandom()%SDS_TRANSACTION_RETRY);
- if (INFINITE != timeoutms)
- {
- unsigned remaining;
- if (timer.timedout(&remaining))
- {
- StringBuffer timeoutText;
- throwStringExceptionV(-1, "Failed to remove %s: %s", logicalname, timeoutException->errorMessage(timeoutText).str());
- }
- if (sleepTime>remaining)
- sleepTime = remaining;
- }
- PROGLOG("CDelayedDelete: pausing due to locked file = %s", logicalname);
- Sleep(sleepTime);
- }
- }
- };
- 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, defaultPrivilegedUser, 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) {
- if (plist.length())
- plist.append(',');
- plist.append(name);
- }
- }
- 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;
- 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"));
- }
- 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)
- {
- 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;
- }
- 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 isPrivilegedUser = false;
- bool set()
- {
- while (isValid()) {
- cur.setown(parent->lookup(queryName(),udesc, false, false, false, nullptr, isPrivilegedUser));
- if (cur)
- return true;
- index++;
- }
- return false;
- }
- public:
- CDistributedFileIterator(IDistributedFileDirectory *_dir,const char *wildname,bool includesuper,IUserDescriptor *user,bool _isPrivilegedUser, IDistributedFileTransaction *_transaction=NULL)
- : isPrivilegedUser(_isPrivilegedUser), 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)
- {
- if (protect)
- {
- StringBuffer str;
- t->setProp("@modified", dt.getString(str).str());
- }
- else
- p.removeTree(t);
- }
- else if (protect)
- {
- t.setown(addNamedPropTree(&p, "Protect", "@name", owner));
- 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();
- 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 bool isRestrictedAccess() override
- {
- return queryAttributes().getPropBool("restricted");
- }
- virtual void setRestrictedAccess(bool restricted) override
- {
- DistributedFilePropertyLock lock(this);
- queryAttributes().setPropBool("restricted", restricted);
- }
- 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 && sub->numParts()>1) // ignore if checking 1 part file, which can be flagged as local or non-local
- {
- 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);
- }
- bool calculateSkew(unsigned &maxSkew, unsigned &minSkew, unsigned &maxSkewPart, unsigned &minSkewPart)
- {
- unsigned np = numParts();
- if (0 == np)
- return false;
- offset_t maxPartSz = 0, minPartSz = (offset_t)-1, totalPartSz = 0;
- maxSkewPart = 0;
- minSkewPart = 0;
- for (unsigned p=0; p<np; p++)
- {
- IDistributedFilePart &part = queryPart(p);
- offset_t size = part.getFileSize(true, false);
- if (size > maxPartSz)
- {
- maxPartSz = size;
- maxSkewPart = p;
- }
- if (size < minPartSz)
- {
- minPartSz = size;
- minSkewPart = p;
- }
- totalPartSz += size;
- }
- offset_t avgPartSz = totalPartSz / np;
- if (0 == avgPartSz)
- minSkew = maxSkew = 0;
- else
- {
- maxSkew = (unsigned)(10000.0 * (((double)maxPartSz-avgPartSz)/avgPartSz));
- minSkew = (unsigned)(10000.0 * ((avgPartSz-(double)minPartSz)/avgPartSz));
- }
- return true;
- }
- void calculateSkew() // called when a logical file is attached
- {
- IPropertyTree &attrs = queryAttributes();
- unsigned maxSkew, minSkew, maxSkewPart, minSkewPart;
- if (!calculateSkew(maxSkew, minSkew, maxSkewPart, minSkewPart))
- {
- attrs.removeProp("@maxSkew");
- attrs.removeProp("@minSkew");
- attrs.removeProp("@maxSkewPart");
- attrs.removeProp("@minSkewPart");
- return;
- }
- attrs.setPropInt("@maxSkew", maxSkew);
- attrs.setPropInt("@maxSkewPart", maxSkewPart);
- attrs.setPropInt("@minSkew", minSkew);
- attrs.setPropInt("@minSkewPart", minSkewPart);
- }
- protected: friend class CDistributedFilePart;
- CDistributedFilePartArray &queryParts()
- {
- return parts;
- }
- public:
- IMPLEMENT_IINTERFACE_O;
- // NB: this form is used for pre-existing file, by dolookup
- 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!
- }
- // NB: this form is used for a new/unattached file
- 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
- calculateSkew();
- 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));
- }
- }
- virtual bool getSkewInfo(unsigned &maxSkew, unsigned &minSkew, unsigned &maxSkewPart, unsigned &minSkewPart, bool calculateIfMissing) override
- {
- const IPropertyTree *attrs = root->queryPropTree("Attr");
- if (attrs && attrs->hasProp("@maxSkew"))
- {
- maxSkew = attrs->getPropInt("@maxSkew");
- minSkew = attrs->getPropInt("@minSkew");
- maxSkewPart = attrs->getPropInt("@maxSkewPart");
- minSkewPart = attrs->getPropInt("@minSkewPart");
- return true;
- }
- else if (calculateIfMissing)
- return calculateSkew(maxSkew, minSkew, maxSkewPart, minSkewPart);
- else
- return false;
- }
- };
- 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;
- bool containsRestrictedSubfile = false;
- /**
- * 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, INFINITE);
- } 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, INFINITE);
- }
- }
- }
- // 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, INFINITE);
- }
- }
- }
- // 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->addWuExceptionEx(s.str(), 0, SeverityWarning, MSGAUD_user, "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);
- }
- containsRestrictedSubfile = false;
- // 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, defaultPrivilegedUser, 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 if (logicalName.isMulti())
- {
- /*
- * implicit superfiles, can't validate subfile presence at this point,
- * but will be caught if empty and not OPT later.
- */
- continue;
- }
- else
- ThrowStringException(-1, "CDistributedSuperFile: SuperFile %s: corrupt subfile file '%s' cannot be found", logicalName.get(), subname.str());
- }
- containsRestrictedSubfile = containsRestrictedSubfile || subfile->isRestrictedAccess();
- subfiles.append(*subfile.getClear());
- if (link)
- linkSubFile(f);
- }
- // This is can happen due to missing referenced foreign files, or missing files referenced via an implicit inline superfile definition
- 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());
- while (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());
- while (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)
- {
- IDistributedFile &subFile = subfiles.item(i);
- __int64 rc = subFile.queryAttributes().getPropInt64("@recordCount", -1);
- if (rc == -1)
- {
- IDistributedSuperFile *super = subFile.querySuperFile();
- // if regular file or non-empty super, must have a @recordCount to aggregate a total
- if ((nullptr == super) || (super->numSubFiles()>0))
- return -1;
- }
- else
- 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;
- }
- IPropertyTree &attrs = queryAttributes();
- attrs.removeProp("@size");
- attrs.removeProp("@compressedSize");
- attrs.removeProp("@checkSum");
- attrs.removeProp("@recordCount"); // recordCount not currently supported by superfiles
- attrs.removeProp("@formatCrc"); // formatCrc set if all consistant
- attrs.removeProp("@recordSize"); // record size set if all consistant
- attrs.removeProp("_record_layout"); // legacy info - set if all consistent
- attrs.removeProp("_rtlType"); // new info - set if all consistent
- attrs.removeProp("@maxSkew");
- attrs.removeProp("@minSkew");
- attrs.removeProp("@maxSkewPart");
- attrs.removeProp("@minSkewPart");
- __int64 fs = getFileSize(false,false);
- if (fs!=-1)
- attrs.setPropInt64("@size",fs);
- if (isCompressed(NULL))
- {
- fs = getDiskSize(false,false);
- if (fs!=-1)
- attrs.setPropInt64("@compressedSize",fs);
- }
- unsigned checkSum;
- if (getFileCheckSum(checkSum))
- attrs.setPropInt64("@checkSum", checkSum);
- __int64 rc = getRecordCount();
- if (rc!=-1)
- attrs.setPropInt64("@recordCount",rc);
- unsigned fcrc;
- if (getFormatCrc(fcrc))
- attrs.setPropInt("@formatCrc", fcrc);
- size32_t rsz;
- if (getRecordSize(rsz))
- attrs.setPropInt("@recordSize", rsz);
- MemoryBuffer mb;
- if (getRecordLayout(mb, "_record_layout"))
- attrs.setPropBin("_record_layout", mb.length(), mb.bufferBase());
- if (getRecordLayout(mb, "_rtlType"))
- attrs.setPropBin("_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)
- attrs.setProp("@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);
- }
- }
- virtual bool isRestrictedAccess() override
- {
- // This ensures restriction applies even if superfile is unrestricted but subfiles are.
- // However, this also means that the superfile will show as "Restricted" in ECL Watch and whenever the user tries to unset the flag
- // it will appear to reset to Restricted.
- return containsRestrictedSubfile;
- }
- 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);
- }
- }
- }
- virtual bool getSkewInfo(unsigned &maxSkew, unsigned &minSkew, unsigned &maxSkewPart, unsigned &minSkewPart, bool calculateIfMissing) override
- {
- return false;
- }
- };
- // --------------------------------------------------------
- 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;
- 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;
- };
- static unsigned loadGroup(const IPropertyTree *groupTree, SocketEndpointArray &epa, GroupType *type, StringAttr *groupDir)
- {
- if (type)
- *type = translateGroupType(groupTree->queryProp("@kind"));
- if (groupDir)
- {
- groupDir->set(groupTree->queryProp("@dir"));
- if (groupDir->isEmpty())
- groupDir->set(queryBaseDirectory(*type));
- }
- Owned<IPropertyTreeIterator> pe = groupTree->getElements("Node");
- ForEach(*pe)
- {
- const char *host = pe->query().queryProp("@ip");
- SocketEndpoint ep(host);
- if (ep.isNull())
- {
- IWARNLOG("loadGroup: failed to resolve host '%s'", host);
- return 0;
- }
- epa.append(ep);
- }
- return epa.ordinality();
- }
- 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> groupTree = getNamedPropTree(conn->queryRoot(), "Group", "@name", gname.str(), true);
- if (!groupTree || !loadGroup(groupTree, epa, &type, &groupdir))
- return nullptr;
- }
- }
- 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();
- }
- IPropertyTree *doAddHosts(CConnectLock &connlock, const char *name, const std::vector<std::string> &hosts, bool cluster, const char *dir)
- {
- IPropertyTree *val = createPTree("Group");
- val->setProp("@name",name);
- if (cluster)
- val->setPropBool("@cluster", true);
- if (dir)
- val->setProp("@dir",dir);
- for (auto &hostOrIpRange: hosts)
- {
- SocketEndpointArray epa;
- epa.fromText(hostOrIpRange.c_str(), 0);
- if (epa.ordinality()>1)
- {
- ForEachItemIn(e, epa)
- {
- StringBuffer ipStr;
- epa.item(e).getIpText(ipStr);
- IPropertyTree *n = val->addPropTree("Node");
- n->setProp("@ip", ipStr);
- }
- }
- else
- {
- IPropertyTree *n = val->addPropTree("Node");
- n->setProp("@ip", hostOrIpRange.c_str());
- }
- }
- val = connlock.conn->queryRoot()->addPropTree("Group",val);
- return LINK(val);
- }
- void doadd(CConnectLock &connlock,const char *name,IGroup *group,bool cluster,const char *dir)
- {
- if (!group)
- return;
- Owned<INodeIterator> gi = group->getIterator();
- if (gi->first())
- {
- StringBuffer ipStr;
- std::vector<std::string> ips;
- while (true)
- {
- gi->query().endpoint().getIpText(ipStr.clear());
- ips.push_back(ipStr.str());
- if (!gi->next())
- break;
- }
- ::Release(doAddHosts(connlock, name, ips, cluster, dir));
- }
- }
- void addGroup(const char *logicalgroupname, const std::vector<std::string> &hosts, bool cluster, const char *dir, GroupType groupType, bool overwrite)
- {
- dbgassertex(hosts.size());
- 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);
- if (!overwrite && connlock.conn->queryRoot()->hasProp(prop.str()))
- return;
- connlock.conn->queryRoot()->removeProp(prop.str());
- if (0 == hosts.size())
- return;
- Owned<IPropertyTree> groupTree = doAddHosts(connlock, name.str(), hosts, cluster, dir);
- SocketEndpointArray eps;
- if (!loadGroup(groupTree, eps, nullptr, nullptr))
- {
- IWARNLOG("CNamedGroupStore.add: failed to add group '%s', due to unresolved hosts", name.str());
- return;
- }
- Owned<IGroup> group = createIGroup(eps);
- {
- CriticalBlock block(cachesect);
- cache.kill();
- cache.append(*new CNamedGroupCacheEntry(group, name, dir, groupType));
- }
- }
- virtual void addUnique(IGroup *group,StringBuffer &lname, const char *dir) override
- {
- 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);
- }
- IGroup *lookup(const char *logicalgroupname, StringBuffer &dir, GroupType &groupType)
- {
- return dolookup(logicalgroupname, NULL, &dir, groupType);
- }
- virtual IGroup *lookup(const char *logicalgroupname) override
- {
- GroupType dummy;
- return dolookup(logicalgroupname, NULL, NULL, dummy);
- }
- virtual INamedGroupIterator *getIterator() override
- {
- CConnectLock connlock("CNamedGroup::getIterator", SDS_GROUPSTORE_ROOT, false, true, false, defaultTimeout);
- return new CNamedGroupIterator(connlock.conn); // links connection
- }
- virtual INamedGroupIterator *getIterator(IGroup *match,bool exact) override
- {
- CConnectLock connlock("CNamedGroup::getIterator", SDS_GROUPSTORE_ROOT, false, false, false, defaultTimeout);
- return new CNamedGroupIterator(connlock.conn,match,exact); // links connection
- }
- virtual void add(const char *logicalgroupname, const std::vector<std::string> &hosts, bool cluster, const char *dir, GroupType groupType) override
- {
- addGroup(logicalgroupname, hosts, cluster, dir, groupType, true);
- }
- virtual void ensure(const char *logicalgroupname, const std::vector<std::string> &hosts, bool cluster, const char *dir, GroupType groupType) override
- {
- addGroup(logicalgroupname, hosts, cluster, dir, groupType, false);
- }
- virtual void ensureNasGroup(size32_t size) override
- {
- std::vector<std::string> hosts;
- for (unsigned n=0; n<size; n++)
- hosts.push_back("localhost");
- VStringBuffer nasGroupName("__nas__%u", size);
- ensure(nasGroupName, hosts, false, nullptr, grp_unknown);
- }
- virtual StringBuffer &getNasGroupName(StringBuffer &groupName, size32_t size) const override
- {
- return groupName.append("__nas__").append(size);
- }
- virtual unsigned removeNode(const char *logicalgroupname, const char *nodeToRemove) override
- {
- 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);
- Owned<IPropertyTree> groupTree = getNamedPropTree(connlock.conn->queryRoot(), "Group", "@name", name, true);
- if (!groupTree)
- return 0;
- SocketEndpointArray epa;
- if (!loadGroup(groupTree, epa, nullptr, nullptr))
- return 0;
- unsigned numNodes = epa.ordinality();
- Owned<IGroup> group = createIGroup(epa);
- SocketEndpoint removeEp(nodeToRemove);
- unsigned removeCount = 0;
- while (removeCount != numNodes)
- {
- rank_t r = group->rank(removeEp);
- if (RANK_NULL == r)
- break;
- group.setown(group->remove(r));
- VStringBuffer xpath("Node[%u]", r+1); // 1 based
- verifyex(groupTree->removeProp(xpath)); // remove corresponding position in IPT (in Dali)
- ++removeCount;
- }
- if (0 == removeCount)
- return 0;
- const char *groupDir = groupTree->queryProp("@dir");
- GroupType groupType = translateGroupType(groupTree->queryProp("@kind"));
- CriticalBlock block(cachesect);
- cache.kill();
- cache.append(*new CNamedGroupCacheEntry(group, name, groupDir, groupType));
- return removeCount;
- }
- virtual void remove(const char *logicalgroupname) override
- {
- 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());
- {
- CriticalBlock block(cachesect);
- cache.kill();
- }
- }
- virtual bool find(IGroup *grp, StringBuffer &gname, bool add) override
- {
- // 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;
- }
- virtual void swapNode(const char *fromHost, const char *toHost) override
- {
- SocketEndpoint from(fromHost);
- SocketEndpoint to(toHost);
- if (from.ipequals(to))
- return;
- CConnectLock connlock("CNamedGroup::swapNode", SDS_GROUPSTORE_ROOT, true, false, false, defaultTimeout);
- 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);
- SocketEndpointArray eps;
- if (!loadGroup(&group, eps, nullptr, nullptr))
- {
- IWARNLOG("swapNode: failed to load group: '%s'", name.str());
- return;
- }
- unsigned epsPos = 0;
- while (true)
- {
- if (epsPos == eps.ordinality())
- break;
- if (from == eps.item(epsPos))
- {
- VStringBuffer xpath("Node[%u]/@ip", epsPos+1);
- group.setProp(xpath, toHost);
- PROGLOG("swapNode swapping %s for %s in group %s", fromHost, toHost, name.str());
- unsigned nodesSwapped = group.getPropInt("@nodesSwapped");
- group.setPropInt("@nodesSwapped", nodesSwapped+1);
- }
- ++epsPos;
- }
- }
- CriticalBlock block(cachesect);
- cache.kill();
- }
- virtual unsigned setDefaultTimeout(unsigned timems) override
- {
- unsigned ret = defaultTimeout;
- defaultTimeout = timems;
- return ret;
- }
- virtual unsigned setRemoteTimeout(unsigned timems) override
- {
- unsigned ret = defaultRemoteTimeout;
- defaultRemoteTimeout = timems;
- return ret;
- }
- virtual void resetCache() override
- {
- 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, bool privilegedUser, unsigned timeout)
- {
- CDfsLogicalFileName logicalname;
- logicalname.set(_logicalname);
- return lookup(logicalname, user, writeattr, hold, lockSuperOwner, transaction, privilegedUser, 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, bool privilegedUser, unsigned timeout)
- {
- Owned <IDistributedFile>distributedFile = dolookup(logicalname, user, writeattr, hold, lockSuperOwner, transaction, timeout);
- // Restricted access is currently designed to stop users viewing sensitive information. It is not designed to stop users deleting or overwriting existing restricted files
- if (writeattr==false && distributedFile && distributedFile->isRestrictedAccess() && !privilegedUser)
- throw new CDFS_Exception(DFSERR_RestrictedFileAccessDenied,logicalname.get());
- return distributedFile.getClear();
- }
- 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) {
- // Restricted access is currently designed to stop users viewing sensitive information. Assuming privileged user rights to allow
- // exists() operation to succeed regardless of user rights
- Owned<IDistributedFile> file = lookup(_logicalname, user, false, false, false, NULL, defaultPrivilegedUser, 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)
- {
- // Restricted access is currently designed to stop users viewing sensitive information. Assuming privileged user rights to allow
- // existsPhysical() operation to succeed regardless of user rights
- Owned<IDistributedFile> file = lookup(_logicalname, user, false, false, false, NULL, defaultPrivilegedUser, 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;
- super->setModified();
- 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,bool isPrivilegedUser)
- {
- return new CDistributedFileIterator(this,wildname,includesuper,user,isPrivilegedUser);
- }
- 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", "Attr/@accessed" };
- 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.isEmpty() && (strcmp(filterValue, prop) > 0))
- return false;
- if (!filterValueHigh.isEmpty() && (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;
- }
- };
- StringBuffer &getClusterGroupName(const 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(const IPropertyTree &cluster, StringBuffer &groupName)
- {
- return getClusterGroupName(cluster, groupName).append("_spares");
- }
- // JCSMORE - dfs group handling may be clearer if in own module
- class CInitGroups
- {
- std::unordered_map<std::string, std::string> machineMap;
- 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 (;;)
- {
- // NB: for legacy reason these are called @ip in Dali, but they should typically be hostnames
- SocketEndpoint oldEp, newEp;
- oldEp.set(oldIter->query().queryProp("@ip"));
- newEp.set(newIter->query().queryProp("@ip"));
- if (oldEp != newEp)
- 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, const IPropertyTree &cluster, bool expand)
- {
- Owned<IPropertyTree> groupTree = createClusterGroupFromEnvCluster(groupType, cluster, nullptr, false, expand);
- if (!groupTree)
- return nullptr;
- Owned<IPropertyTreeIterator> nodeIter = groupTree->getElements("Node");
- if (!nodeIter->first())
- return nullptr;
- SocketEndpointArray eps;
- do
- {
- SocketEndpoint ep(nodeIter->query().queryProp("@ip"));
- eps.append(ep);
- }
- while (nodeIter->next());
- return createIGroup(eps);
- }
- bool loadMachineMap(const IPropertyTree *env)
- {
- if (machinesLoaded)
- return true;
- Owned<IPropertyTreeIterator> machines = env->getElements("Hardware/Computer");
- if (!machines->first())
- {
- IWARNLOG("No Hardware/Computer's found");
- return false;
- }
- do
- {
- const IPropertyTree &machine = machines->query();
- const char *host = machine.queryProp("@netAddress");
- const char *name = machine.queryProp("@name");
- machineMap.insert({ name, host });
- }
- while (machines->next());
- machinesLoaded = true;
- return true;
- }
- 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", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
- if (!conn)
- {
- IWARNLOG("Cannot connect to /Environment");
- return false;
- }
- return loadMachineMap(conn->queryRoot());
- }
- IPropertyTree *createClusterGroup(GroupType groupType, const std::vector<std::string> &hosts, const char *dir, const IPropertyTree * envCluster, bool realCluster, bool _expand)
- {
- bool expand = _expand;
- if (grp_thor != groupType)
- expand = false;
- Owned<IPropertyTree> cluster = createPTree("Group");
- if (realCluster)
- cluster->setPropBool("@cluster", true);
- const char *kind=nullptr;
- 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);
- auto addHostsToIPTFunc = [cluster, &hosts]()
- {
- for (auto &host: hosts)
- {
- IPropertyTree *node = cluster->addPropTree("Node");
- node->setProp("@ip", host.c_str());
- }
- };
- if (expand)
- {
- assertex(envCluster);
- unsigned slavesPerNode = envCluster->getPropInt("@slavesPerNode", 1);
- unsigned channelsPerSlave = envCluster->getPropInt("@channelsPerSlave", 1);
- for (unsigned s=0; s<(slavesPerNode*channelsPerSlave); s++)
- addHostsToIPTFunc();
- }
- else
- addHostsToIPTFunc();
- return cluster.getClear();
- }
- IPropertyTree *createClusterGroupFromEnvCluster(GroupType groupType, const IPropertyTree &cluster, const char *dir, bool realCluster, bool expand)
- {
- const char *processName=nullptr;
- switch (groupType)
- {
- case grp_thor:
- processName = "ThorSlaveProcess";
- break;
- case grp_thorspares:
- processName = "ThorSpareProcess";
- break;
- case grp_roxie:
- processName = "RoxieServerProcess";
- break;
- default:
- throwUnexpected();
- }
- std::vector<std::string> hosts;
- Owned<IPropertyTreeIterator> nodes = cluster.getElements(processName);
- ForEach(*nodes)
- {
- IPropertyTree &node = nodes->query();
- const char *computer = node.queryProp("@computer");
- const char *host = nullptr;
- if (!isEmptyString(computer))
- {
- auto it = machineMap.find(computer);
- if (it == machineMap.end())
- {
- OERRLOG("Cannot construct %s, computer name %s not found\n", cluster.queryProp("@name"), computer);
- return nullptr;
- }
- host = it->second.c_str();
- }
- else
- {
- host = node.queryProp("@netAddress");
- if (isEmptyString(host))
- {
- OERRLOG("Cannot construct %s, missing computer spec on node\n", cluster.queryProp("@name"));
- return nullptr;
- }
- }
- switch (groupType)
- {
- case grp_roxie:
- // Redundant copies are located via the flags.
- // Old environments may contain duplicated sever information for multiple ports
- if (hosts.end() == std::find(hosts.begin(), hosts.end(), host)) // only add if not already there
- hosts.push_back(host);
- break;
- case grp_thor:
- case grp_thorspares:
- hosts.push_back(host);
- break;
- default:
- throwUnexpected();
- }
- }
- if (!hosts.size())
- return nullptr;
- return createClusterGroup(groupType, hosts, dir, &cluster, realCluster, expand);
- }
- bool constructGroup(const 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();
- IPropertyTree *existingClusterGroup = queryExistingGroup(gname);
- bool matchOldEnv = false;
- Owned<IPropertyTree> newClusterGroup = createClusterGroupFromEnvCluster(groupType, cluster, defDir, realCluster, true);
- bool matchExisting = !force && 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, true);
- 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 (!isEmptyString(na))
- {
- SocketEndpoint ep(na);
- if (!ep.isNull())
- {
- ins++;
- VStringBuffer gname("hthor__%s", groupname);
- if (ins>1)
- gname.append('_').append(ins);
- Owned<IPropertyTree> clusterGroup = createClusterGroup(grp_hthor, { na }, nullptr, &cluster, true, false);
- addClusterGroup(gname.str(), clusterGroup.getClear(), true);
- }
- }
- }
- }
- struct BoundHost
- {
- BoundHost(const std::string &_host) : host(_host), ep(_host.c_str()) { }
- std::string host;
- SocketEndpoint ep;
- bool operator == (const SocketEndpoint &other) const
- {
- return ep == other;
- }
- };
- unsigned bind(const std::vector<std::string> &hosts, std::vector<BoundHost> &boundHosts) const
- {
- for (const auto &host: hosts)
- {
- SocketEndpoint boundHost(host.c_str());
- if (boundHosts.end() == std::find(boundHosts.begin(), boundHosts.end(), boundHost))
- boundHosts.push_back(BoundHost(host));
- }
- return boundHosts.size();
- }
- IPropertyTree *queryExistingSpareGroup(const IPropertyTree *cluster, StringBuffer &groupName)
- {
- getClusterSpareGroupName(*cluster, groupName);
- IPropertyTree *root = groupsconnlock.conn->queryRoot();
- VStringBuffer xpath("Group[@name=\"%s\"]", groupName.str());
- return root->queryPropTree(xpath.str());
- }
- public:
- CInitGroups(unsigned _defaultTimeout)
- : groupsconnlock("constructGroup",SDS_GROUPSTORE_ROOT,true,false,false,_defaultTimeout)
- {
- defaultTimeout = _defaultTimeout;
- machinesLoaded = false;
- }
- IPropertyTree *queryCluster(const IPropertyTree *env, const char *_clusterName, const char *type, const char *msg, StringBuffer &messages)
- {
- if (isEmptyString(_clusterName) || isEmptyString(type))
- return nullptr;
- if (!streq("ThorCluster", type)) // currently only Thor supported here.
- return nullptr;
- StringAttr clusterName = _clusterName;
- clusterName.toLowerCase();
- if (loadMachineMap())
- {
- VStringBuffer xpath("Software/%s[@name=\"%s\"]", type, clusterName.get());
- Owned<IPropertyTreeIterator> clusterIter = env->getElements(xpath);
- if (!clusterIter->first())
- {
- VStringBuffer errMsg("%s: Could not find type %s, %s cluster", msg, type, clusterName.get());
- UWARNLOG("%s", errMsg.str());
- messages.append(errMsg).newline();
- }
- else
- {
- IPropertyTree *cluster = &clusterIter->query();
- if (!clusterIter->next())
- return cluster;
- VStringBuffer errMsg("%s: more than one cluster named: %s", msg, clusterName.get());
- UWARNLOG("%s", errMsg.str());
- messages.append(errMsg).newline();
- }
- }
- return nullptr;
- }
- bool resetClusterGroup(const char *clusterName, const char *type, bool spares, StringBuffer &messages)
- {
- Owned<IRemoteConnection> conn = querySDS().connect("/Environment", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
- if (!conn)
- return false;
- const IPropertyTree *cluster = queryCluster(conn->queryRoot(), clusterName, type, "resetClusterGroup", messages);
- if (!cluster)
- return false;
- if (spares)
- {
- if (constructGroup(*cluster,NULL,NULL,grp_thorspares,true,messages))
- return true;
- }
- else
- {
- if (constructGroup(*cluster,NULL,NULL,grp_thor,true,messages))
- return true;
- }
- return false;
- }
- bool addSpares(const char *clusterName, const char *type, const std::vector<std::string> &hosts, StringBuffer &messages)
- {
- Owned<IRemoteConnection> conn = querySDS().connect("/Environment", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
- if (!conn)
- return false;
- const IPropertyTree *cluster = queryCluster(conn->queryRoot(), clusterName, type, "addSpares", messages);
- if (!cluster)
- return false;
- std::vector<BoundHost> boundHostsToAdd;
- bind(hosts, boundHostsToAdd);
- StringBuffer groupName;
- IPropertyTree *existing = queryExistingSpareGroup(cluster, groupName);
- if (existing)
- {
- Owned<IPropertyTreeIterator> iter = existing->getElements("Node");
- ForEach(*iter)
- {
- const char *host = iter->query().queryProp("@ip");
- SocketEndpoint ep(host); // NB: for legacy reason it's called @ip, but should typically be a hostname
- // delete any entries that are already in Group
- auto it = std::remove(boundHostsToAdd.begin(), boundHostsToAdd.end(), ep);
- if (it != boundHostsToAdd.end())
- {
- boundHostsToAdd.erase(it, boundHostsToAdd.end());
- VStringBuffer errMsg("addSpares: not adding: %s, already in spares", host);
- UWARNLOG("%s", errMsg.str());
- messages.append(errMsg).newline();
- }
- }
- }
- else
- {
- existing = groupsconnlock.conn->queryRoot()->addPropTree("Group");
- existing->setProp("@name", groupName.str());
- }
- // add remaining
- for (const auto &boundHost: boundHostsToAdd)
- {
- IPropertyTree *node = existing->addPropTree("Node");
- node->setProp("@ip", boundHost.host.c_str());
- }
- return true;
- }
- bool removeSpares(const char *clusterName, const char *type, const std::vector<std::string> &hosts, StringBuffer &messages)
- {
- Owned<IRemoteConnection> conn = querySDS().connect("/Environment", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
- if (!conn)
- return false;
- const IPropertyTree *cluster = queryCluster(conn->queryRoot(), clusterName, type, "removeSpares", messages);
- if (!cluster)
- return false;
- std::vector<BoundHost> boundHostsToRemove;
- bind(hosts, boundHostsToRemove);
- StringBuffer groupName;
- IPropertyTree *existing = queryExistingSpareGroup(cluster, groupName);
- if (existing)
- {
- SocketEndpointArray existingGroupBoundEps;
- StringAttr groupDir;
- GroupType type;
- if (!loadGroup(existing, existingGroupBoundEps, nullptr, nullptr))
- {
- IWARNLOG("removeSpares: failed to load group: '%s'", groupName.str());
- return false;
- }
- for (const auto &boundHostToRemove: boundHostsToRemove)
- {
- bool matched = true;
- ForEachItemIn(e, existingGroupBoundEps)
- {
- if (existingGroupBoundEps.item(e) == boundHostToRemove.ep)
- {
- VStringBuffer xpath("Node[%u]", e+1);
- verifyex(existing->removeProp(xpath));
- matched = true;
- }
- // there shouldn't be any others, but in keeping with legacy code, continue matching
- }
- if (!matched)
- {
- VStringBuffer errMsg("removeSpares: %s not found in spares", boundHostToRemove.host.c_str());
- UWARNLOG("%s", errMsg.str());
- messages.append(errMsg).newline();
- }
- }
- }
- return true;
- }
- 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");
- }
- }
- }
- }
- IPropertyTree * createStorageGroup(const char * name, size32_t size, const char * path)
- {
- std::vector<std::string> hosts(size, "localhost");
- return createClusterGroup(grp_unknown, hosts, path, nullptr, false, false);
- }
- void ensureStorageGroup(bool force, const char * name, unsigned numDevices, const char * path, StringBuffer & messages)
- {
- IPropertyTree *existingClusterGroup = queryExistingGroup(name);
- Owned<IPropertyTree> newClusterGroup = createStorageGroup(name, numDevices, path);
- bool matchExisting = clusterGroupCompare(newClusterGroup, existingClusterGroup);
- if (!existingClusterGroup || !matchExisting)
- {
- if (!existingClusterGroup)
- {
- VStringBuffer msg("New cluster layout for cluster %s", name);
- UWARNLOG("%s", msg.str());
- messages.append(msg).newline();
- addClusterGroup(name, newClusterGroup.getClear(), false);
- }
- else if (force)
- {
- VStringBuffer msg("Forcing new group layout for storageplane %s", name);
- UWARNLOG("%s", msg.str());
- messages.append(msg).newline();
- addClusterGroup(name, newClusterGroup.getClear(), false);
- }
- else
- {
- VStringBuffer msg("Active cluster '%s' group layout does not match stroageplane definition", name);
- UWARNLOG("%s", msg.str()); \
- messages.append(msg).newline();
- }
- }
- }
- void constructStorageGroups(bool force, StringBuffer &messages)
- {
- IPropertyTree & global = queryGlobalConfig();
- IPropertyTree * storage = global.queryPropTree("storage");
- if (storage)
- {
- Owned<IPropertyTreeIterator> planes = storage->getElements("planes");
- ForEach(*planes)
- {
- IPropertyTree & plane = planes->query();
- const char * name = plane.queryProp("@name");
- if (isEmptyString(name))
- continue;
- //Lower case the group name - see CnamedGroupStore::dolookup which lower cases before resolving.
- StringBuffer gname;
- gname.append(name).toLowerCase();
- //Two main type of storage plane - with a host group (bare metal) and without.
- IPropertyTree *existingGroup = queryExistingGroup(gname);
- const char * hosts = plane.queryProp("@hosts");
- const char * prefix = plane.queryProp("@prefix");
- if (hosts)
- {
- IPropertyTree *existingClusterGroup = queryExistingGroup(gname);
- if (!existingClusterGroup)
- UNIMPLEMENTED_X("Bare metal storage planes not yet supported");
- }
- else
- {
- unsigned numDevices = plane.getPropInt("@numDevices", 1);
- ensureStorageGroup(force, gname, numDevices, prefix, messages);
- }
- }
- }
- }
- IGroup *getGroupFromCluster(const char *type, const IPropertyTree &cluster, bool expand)
- {
- loadMachineMap();
- GroupType gt = getGroupType(type);
- return getGroupFromCluster(gt, cluster, expand);
- }
- IPropertyTree *queryExistingGroup(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);
- }
- void initClusterAndStoragePlaneGroups(bool force, IPropertyTree *oldEnvironment, unsigned timems)
- {
- CInitGroups init(timems);
- StringBuffer response;
- init.constructGroups(force, response, oldEnvironment);
- if (response.length())
- PROGLOG("DFS group initialization : %s", response.str()); // should this be a syslog?
- response.clear();
- init.constructStorageGroups(false, response);
- if (response.length())
- PROGLOG("StoragePlane group initialization : %s", response.str()); // should this be a syslog?
- }
- 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, const std::vector<std::string> &hosts, StringBuffer &response, unsigned timems)
- {
- CInitGroups init(timems);
- return init.addSpares(clusterName, type, hosts, response);
- }
- bool removeClusterSpares(const char *clusterName, const char *type, const std::vector<std::string> &hosts, StringBuffer &response, unsigned timems)
- {
- CInitGroups init(timems);
- return init.removeSpares(clusterName, type, hosts, 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.queryExistingGroup(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, defaultPrivilegedUser, defaultTimeout);
- Owned<IDistributedFile> file2 = lookup(lfn2, user, false, false, false, NULL, defaultPrivilegedUser, 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, defaultPrivilegedUser, 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
- return attrLock.queryRoot()->hasProp("Protect");
- }
- 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, false, false, nullptr, defaultPrivilegedUser);
- CDistributedFile *f = QUERYINTERFACE(file.get(),CDistributedFile);
- assert(f);
- f->detachLogical();
- }
- }
- #endif // _USE_CPPUNIT
|