dadfs.cpp 454 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #define da_decl DECL_EXPORT
  14. #include "platform.h"
  15. #include "jlib.hpp"
  16. #include "jfile.hpp"
  17. #include "jlzw.hpp"
  18. #include "jmisc.hpp"
  19. #include "jtime.hpp"
  20. #include "jregexp.hpp"
  21. #include "jexcept.hpp"
  22. #include "jsort.hpp"
  23. #include "jptree.hpp"
  24. #include "jbuff.hpp"
  25. #include "dafdesc.hpp"
  26. #include "dasds.hpp"
  27. #include "dasess.hpp"
  28. #include "daclient.hpp"
  29. #include "daserver.hpp"
  30. #include "dautils.hpp"
  31. #include "danqs.hpp"
  32. #include "mputil.hpp"
  33. #include "dadfs.hpp"
  34. #include "eclhelper.hpp"
  35. #include "seclib.hpp"
  36. #include <string>
  37. #include <vector>
  38. #include <unordered_map>
  39. #include <algorithm>
  40. #ifdef _DEBUG
  41. //#define EXTRA_LOGGING
  42. //#define TRACE_LOCKS
  43. #endif
  44. #define SDS_CONNECT_TIMEOUT (1000*60*60*2) // better than infinite
  45. #define SDS_SUB_LOCK_TIMEOUT (10000)
  46. #define SDS_TRANSACTION_RETRY (60000)
  47. #define SDS_UPDATEFS_TIMEOUT (10000)
  48. #define DEFAULT_NUM_DFS_THREADS 30
  49. #define TIMEOUT_ON_CLOSEDOWN 120000 // On closedown, give up on trying to join a thread in CDaliDFSServer after two minutes
  50. #define MAX_PHYSICAL_DELETE_THREADS 1000
  51. #if _INTERNAL_EDITION == 1
  52. #ifndef _MSC_VER
  53. #warning Disabling Sub-file compatibility checking
  54. #endif
  55. #else
  56. #define SUBFILE_COMPATIBILITY_CHECKING
  57. #endif
  58. //#define PACK_ECL
  59. #define SDS_GROUPSTORE_ROOT "Groups" // followed by name
  60. class CDistributedFile;
  61. enum MDFSRequestKind
  62. {
  63. MDFS_ITERATE_FILES,
  64. MDFS_UNUSED1,
  65. MDFS_GET_FILE_TREE,
  66. MDFS_GET_GROUP_TREE,
  67. MDFS_SET_FILE_ACCESSED,
  68. MDFS_ITERATE_RELATIONSHIPS,
  69. MDFS_SET_FILE_PROTECT,
  70. MDFS_ITERATE_FILTEREDFILES,
  71. MDFS_ITERATE_FILTEREDFILES2,
  72. MDFS_MAX
  73. };
  74. // Mutex for physical operations (remove/rename)
  75. static CriticalSection physicalChange;
  76. #define MDFS_GET_FILE_TREE_V2 ((unsigned)1)
  77. static int strcompare(const void * left, const void * right)
  78. {
  79. const char * l = (const char *)left;
  80. const char * r = (const char *)right;
  81. return stricmp(l,r);
  82. }
  83. inline unsigned ipGroupDistance(const IpAddress &ip,const IGroup *grp)
  84. {
  85. if (!grp)
  86. return (unsigned)-1;
  87. return grp->distance(ip);
  88. }
  89. inline unsigned groupDistance(IGroup *grp1,IGroup *grp2)
  90. {
  91. if (grp1==grp2)
  92. return 0;
  93. if (!grp1||!grp2)
  94. return (unsigned)-1;
  95. return grp1->distance(grp2);
  96. }
  97. static StringBuffer &normalizeFormat(StringBuffer &in)
  98. {
  99. in.toLowerCase();
  100. for (unsigned i = 0;i<in.length();)
  101. {
  102. switch (in.charAt(i)) {
  103. case '-':
  104. case '_':
  105. case ' ':
  106. in.remove(i,1);
  107. break;
  108. default:
  109. i++;
  110. break;
  111. }
  112. }
  113. return in;
  114. }
  115. static StringBuffer &getAttrQueryStr(StringBuffer &str,const char *sub,const char *key,const char *name)
  116. {
  117. assertex(key[0]=='@');
  118. str.appendf("%s[%s=\"%s\"]",sub,key,name);
  119. return str;
  120. }
  121. static IPropertyTree *getNamedPropTree(const IPropertyTree *parent, const char *sub, const char *key, const char *name, bool preload)
  122. { // no create
  123. if (!parent)
  124. return NULL;
  125. StringBuffer query;
  126. getAttrQueryStr(query,sub,key,name);
  127. if (preload)
  128. return parent->getBranch(query.str());
  129. return parent->getPropTree(query.str());
  130. }
  131. static IPropertyTree *addNamedPropTree(IPropertyTree *parent, const char *sub, const char *key, const char *name, const IPropertyTree *init=nullptr)
  132. {
  133. IPropertyTree* ret = init?createPTreeFromIPT(init):createPTree(sub);
  134. assertex(key[0]=='@');
  135. ret->setProp(key,name);
  136. ret = parent->addPropTree(sub,ret);
  137. return LINK(ret);
  138. }
  139. const char *normalizeLFN(const char *s,StringBuffer &tmp)
  140. {
  141. CDfsLogicalFileName dlfn;
  142. dlfn.set(s);
  143. return dlfn.get(tmp).str();
  144. }
  145. static IPropertyTree *getEmptyAttr()
  146. {
  147. return createPTree("Attr");
  148. }
  149. RemoteFilename &constructPartFilename(IGroup *grp,unsigned partno,unsigned partmax,const char *name,const char *partmask,const char *partdir,unsigned copy,ClusterPartDiskMapSpec &mspec,RemoteFilename &rfn)
  150. {
  151. partno--;
  152. StringBuffer tmp;
  153. if (!name||!*name) {
  154. if (!partmask||!*partmask) {
  155. partmask = "!ERROR!._$P$_of_$N$"; // could use logical tail name if I had it
  156. IERRLOG("No partmask for constructPartFilename");
  157. }
  158. name = expandMask(tmp,partmask,partno,partmax).str();
  159. }
  160. StringBuffer fullname;
  161. if (findPathSepChar(name)==NULL)
  162. addPathSepChar(fullname.append(partdir));
  163. fullname.append(name);
  164. unsigned n;
  165. unsigned d;
  166. mspec.calcPartLocation(partno,partmax,copy,grp?grp->ordinality():partmax,n,d);
  167. setReplicateFilename(fullname,d);
  168. SocketEndpoint ep;
  169. if (grp)
  170. ep=grp->queryNode(n).endpoint();
  171. rfn.setPath(ep,fullname.toLowerCase().str());
  172. return rfn;
  173. }
  174. inline void LOGPTREE(const char *title,IPropertyTree *pt)
  175. {
  176. StringBuffer buf;
  177. if (pt) {
  178. toXML(pt,buf);
  179. PROGLOG("%s:\n%s\n",title,buf.str());
  180. }
  181. else
  182. PROGLOG("%s : NULL",title);
  183. }
  184. inline void LOGFDESC(const char *title,IFileDescriptor *fdesc)
  185. {
  186. if (fdesc) {
  187. Owned<IPropertyTree> pt = fdesc->getFileTree();
  188. LOGPTREE(title,pt);
  189. }
  190. else
  191. PROGLOG("%s : NULL",title);
  192. }
  193. class DECL_EXCEPTION CDFS_Exception: implements IDFS_Exception, public CInterface
  194. {
  195. int errcode;
  196. StringAttr errstr;
  197. public:
  198. CDFS_Exception(int _errcode, const char *_errstr)
  199. : errstr(_errstr)
  200. {
  201. errcode = _errcode;
  202. }
  203. int errorCode() const { return errcode; }
  204. StringBuffer & errorMessage(StringBuffer &str) const
  205. {
  206. if (errcode==DFSERR_ok)
  207. return str;
  208. str.append("DFS Exception: ").append(errcode);
  209. switch(errcode) {
  210. case DFSERR_LogicalNameAlreadyExists:
  211. return str.append(": logical name ").append(errstr).append(" already exists");
  212. case DFSERR_CannotFindPartFileSize:
  213. return str.append(": Cannot find physical file size for ").append(errstr);
  214. case DFSERR_CannotFindPartFileCrc:
  215. return str.append(": Cannot find physical file crc for ").append(errstr);
  216. case DFSERR_LookupAccessDenied:
  217. {
  218. StringBuffer ip;
  219. queryMyNode()->endpoint().getIpText(ip);
  220. return str.appendf(" Lookup access denied for scope %s at Dali %s", errstr.str(), ip.str());
  221. }
  222. case DFSERR_CreateAccessDenied:
  223. return str.append(" Create access denied for scope ").append(errstr);
  224. case DFSERR_PhysicalPartAlreadyExists:
  225. return str.append(": physical part ").append(errstr).append(" already exists");
  226. case DFSERR_PhysicalPartDoesntExist:
  227. return str.append(": physical part ").append(errstr).append(" doesnt exist");
  228. case DFSERR_ForeignDaliTimeout:
  229. return str.append(": Timeout connecting to Dali Server on ").append(errstr);
  230. case DFSERR_ClusterNotFound:
  231. return str.append(": Cluster not found: ").append(errstr);
  232. case DFSERR_ClusterAlreadyExists:
  233. return str.append(": Cluster already exists: ").append(errstr);
  234. case DFSERR_LookupConnectionTimout:
  235. return str.append(": Lookup connection timeout: ").append(errstr);
  236. case DFSERR_FailedToDeleteFile:
  237. return str.append(": Failed to delete file: ").append(errstr);
  238. case DFSERR_RestrictedFileAccessDenied:
  239. return str.append(": Access to restricted file denied: ").append(errstr);
  240. }
  241. return str.append("Unknown DFS Exception");
  242. }
  243. MessageAudience errorAudience() const { return MSGAUD_user; }
  244. IMPLEMENT_IINTERFACE;
  245. };
  246. class CConnectLock
  247. {
  248. public:
  249. Owned<IRemoteConnection> conn;
  250. CConnectLock(const char *caller, const char *name, bool write, bool preload, bool hold, unsigned timeout)
  251. {
  252. unsigned start = msTick();
  253. bool first = true;
  254. for (;;)
  255. {
  256. try
  257. {
  258. unsigned mode = write ? RTM_LOCK_WRITE : RTM_LOCK_READ;
  259. if (preload) mode |= RTM_SUB;
  260. if (hold) mode |= RTM_LOCK_HOLD;
  261. conn.setown(querySDS().connect(name, queryCoven().inCoven() ? 0 : myProcessSession(), mode, (timeout==INFINITE)?1000*60*5:timeout));
  262. #ifdef TRACE_LOCKS
  263. PROGLOG("%s: LOCKGOT(%x) %s %s",caller,(unsigned)(memsize_t)conn.get(),name,write?"WRITE":"");
  264. LogRemoteConn(conn);
  265. PrintStackReport();
  266. #endif
  267. break;
  268. }
  269. catch (ISDSException *e)
  270. {
  271. if (SDSExcpt_LockTimeout == e->errorCode())
  272. {
  273. #ifdef TRACE_LOCKS
  274. PROGLOG("%s: LOCKFAIL %s %s",caller,name,write?"WRITE":"");
  275. LogRemoteConn(conn);
  276. #endif
  277. unsigned tt = msTick()-start;
  278. if (timeout!=INFINITE)
  279. throw;
  280. IWARNLOG("CConnectLock on %s waiting for %ds",name,tt/1000);
  281. if (first)
  282. {
  283. PrintStackReport();
  284. first = false;
  285. }
  286. if (tt>SDS_CONNECT_TIMEOUT)
  287. throw;
  288. e->Release();
  289. }
  290. else
  291. throw;
  292. }
  293. catch (IException *e)
  294. {
  295. StringBuffer tmp("CConnectLock ");
  296. tmp.append(caller).append(' ').append(name);
  297. EXCLOG(e, tmp.str());
  298. throw;
  299. }
  300. }
  301. }
  302. IRemoteConnection *detach()
  303. {
  304. #ifdef TRACE_LOCKS
  305. if (conn.get()) {
  306. PROGLOG("LOCKDETACH(%x)",(unsigned)(memsize_t)conn.get());
  307. LogRemoteConn(conn);
  308. }
  309. #endif
  310. return conn.getClear();
  311. }
  312. #ifdef TRACE_LOCKS
  313. ~CConnectLock()
  314. {
  315. if (conn.get()) {
  316. PROGLOG("LOCKDELETE(%x)",(unsigned)(memsize_t)conn.get());
  317. LogRemoteConn(conn);
  318. }
  319. }
  320. #endif
  321. };
  322. void ensureFileScope(const CDfsLogicalFileName &dlfn,unsigned timeout)
  323. {
  324. CConnectLock connlock("ensureFileScope",querySdsFilesRoot(),true,false,false,timeout);
  325. StringBuffer query;
  326. IPropertyTree *r = connlock.conn->getRoot();
  327. StringBuffer scopes;
  328. const char *s=dlfn.getScopes(scopes,true).str();
  329. for (;;) {
  330. IPropertyTree *nr;
  331. const char *e = strstr(s,"::");
  332. query.clear();
  333. if (e)
  334. query.append(e-s,s);
  335. else
  336. query.append(s);
  337. nr = getNamedPropTree(r,queryDfsXmlBranchName(DXB_Scope),"@name",query.trim().toLowerCase().str(),false);
  338. if (!nr)
  339. nr = addNamedPropTree(r,queryDfsXmlBranchName(DXB_Scope),"@name",query.str());
  340. r->Release();
  341. if (!e) {
  342. ::Release(nr);
  343. break;
  344. }
  345. r = nr;
  346. s = e+2;
  347. }
  348. }
  349. void removeFileEmptyScope(const CDfsLogicalFileName &dlfn,unsigned timeout)
  350. {
  351. CConnectLock connlock("removeFileEmptyScope",querySdsFilesRoot(),true,false,false,timeout); //*1
  352. IPropertyTree *root = connlock.conn.get()?connlock.conn->queryRoot():NULL;
  353. if (!root)
  354. return;
  355. StringBuffer query;
  356. dlfn.makeScopeQuery(query.clear(),false);
  357. StringBuffer head;
  358. for (;;) {
  359. if (query.length()) {
  360. const char *tail = splitXPath(query.str(),head.clear());
  361. if (!tail||!*tail)
  362. break;
  363. IPropertyTree *pt;
  364. if (head.length()) {
  365. query.set(head);
  366. pt = root->queryPropTree(query.str());
  367. }
  368. else
  369. pt = root;
  370. IPropertyTree *t = pt?pt->queryPropTree(tail):NULL;
  371. if (t) {
  372. if (t->hasChildren())
  373. break;
  374. pt->removeTree(t);
  375. if (root==pt)
  376. break;
  377. }
  378. else
  379. break;
  380. }
  381. else
  382. break;
  383. }
  384. }
  385. class CFileLockBase
  386. {
  387. IRemoteConnection *conn;
  388. protected:
  389. Owned<IRemoteConnection> lock;
  390. bool init(const char *lockPath, unsigned mode, IRemoteConnection *_conn, unsigned timeout, const char *msg)
  391. {
  392. conn = NULL;
  393. lock.clear();
  394. CTimeMon tm(timeout);
  395. for (;;)
  396. {
  397. try
  398. {
  399. lock.setown(querySDS().connect(lockPath, myProcessSession(), mode, timeout>60000 ? 60000 : timeout));
  400. if (lock.get())
  401. {
  402. conn = _conn;
  403. return true;
  404. }
  405. return false;
  406. }
  407. catch (ISDSException *e)
  408. {
  409. if (SDSExcpt_LockTimeout != e->errorCode() || tm.timedout())
  410. throw;
  411. IWARNLOG("CFileAttrLockBase(%s) blocked for %ds", msg, tm.elapsed()/1000);
  412. e->Release();
  413. }
  414. }
  415. }
  416. public:
  417. CFileLockBase()
  418. {
  419. conn = NULL;
  420. }
  421. ~CFileLockBase()
  422. {
  423. // if conn provided, 'lock' was just a surrogate for the owner connection, commit now to conn if write lock
  424. if (conn && lock)
  425. conn->commit();
  426. }
  427. IRemoteConnection *detach()
  428. {
  429. return lock.getClear();
  430. }
  431. void clear()
  432. {
  433. lock.clear();
  434. conn = NULL;
  435. }
  436. void commit() { if (conn) conn->commit(); }
  437. IPropertyTree *queryRoot() const
  438. {
  439. return lock.get() ? lock->queryRoot() : NULL;
  440. }
  441. };
  442. class CFileLock : protected CFileLockBase
  443. {
  444. protected:
  445. DfsXmlBranchKind kind;
  446. public:
  447. CFileLock()
  448. {
  449. kind = DXB_Internal;
  450. }
  451. bool init(const CDfsLogicalFileName &logicalName, DfsXmlBranchKind bkind, unsigned mode, unsigned timeout, const char *msg)
  452. {
  453. StringBuffer lockPath;
  454. logicalName.makeFullnameQuery(lockPath, bkind, true);
  455. if (CFileLockBase::init(lockPath, mode, NULL, timeout, msg))
  456. {
  457. kind = bkind;
  458. return true;
  459. }
  460. kind = DXB_Internal;
  461. return false;
  462. }
  463. bool init(const CDfsLogicalFileName &logicalName, unsigned mode, unsigned timeout, const char *msg)
  464. {
  465. StringBuffer lockPath;
  466. logicalName.makeFullnameQuery(lockPath, DXB_File, true);
  467. if (CFileLockBase::init(lockPath, mode, NULL, timeout, msg))
  468. {
  469. kind = DXB_File;
  470. return true;
  471. }
  472. // try super
  473. logicalName.makeFullnameQuery(lockPath.clear(), DXB_SuperFile, true);
  474. if (CFileLockBase::init(lockPath, mode, NULL, timeout, msg))
  475. {
  476. kind = DXB_SuperFile;
  477. return true;
  478. }
  479. kind = DXB_Internal;
  480. return false;
  481. }
  482. IRemoteConnection *detach() { return CFileLockBase::detach(); }
  483. IPropertyTree *queryRoot() const { return CFileLockBase::queryRoot(); }
  484. IRemoteConnection *queryConnection() const
  485. {
  486. return lock;
  487. }
  488. void clear()
  489. {
  490. CFileLockBase::clear();
  491. kind = DXB_Internal;
  492. }
  493. DfsXmlBranchKind getKind() const { return kind; }
  494. };
  495. class CFileSubLock : protected CFileLockBase
  496. {
  497. public:
  498. bool init(const CDfsLogicalFileName &logicalName, DfsXmlBranchKind bkind, unsigned mode, const char *subLock, IRemoteConnection *conn, unsigned timeout, const char *msg)
  499. {
  500. StringBuffer lockPath;
  501. logicalName.makeFullnameQuery(lockPath, bkind, true);
  502. lockPath.appendf("/%s", subLock);
  503. return CFileLockBase::init(lockPath, mode, conn, timeout, msg);
  504. }
  505. bool init(const CDfsLogicalFileName &logicalName, unsigned mode, const char *subLock, IRemoteConnection *conn, unsigned timeout, const char *msg)
  506. {
  507. StringBuffer lockPath;
  508. logicalName.makeFullnameQuery(lockPath, DXB_File, true);
  509. lockPath.appendf("/%s", subLock);
  510. if (CFileLockBase::init(lockPath, mode, conn, timeout, msg))
  511. return true;
  512. // try super
  513. logicalName.makeFullnameQuery(lockPath.clear(), DXB_SuperFile, true);
  514. return CFileLockBase::init(lockPath, mode, conn, timeout, msg);
  515. }
  516. };
  517. class CFileAttrLock : protected CFileSubLock
  518. {
  519. public:
  520. bool init(const CDfsLogicalFileName &logicalName, DfsXmlBranchKind bkind, unsigned mode, IRemoteConnection *conn, unsigned timeout, const char *msg)
  521. {
  522. return CFileSubLock::init(logicalName, bkind, mode, "Attr", conn, timeout, msg);
  523. }
  524. bool init(const CDfsLogicalFileName &logicalName, unsigned mode, IRemoteConnection *conn, unsigned timeout, const char *msg)
  525. {
  526. return CFileSubLock::init(logicalName, mode, "Attr", conn, timeout, msg);
  527. }
  528. IPropertyTree *queryRoot() const { return CFileSubLock::queryRoot(); }
  529. void commit() { CFileSubLock::commit(); }
  530. };
  531. class CFileLockCompound : protected CFileLockBase
  532. {
  533. public:
  534. bool init(const CDfsLogicalFileName &logicalName, unsigned mode, IRemoteConnection *conn, const char *subLock, unsigned timeout, const char *msg)
  535. {
  536. StringBuffer lockPath;
  537. if (subLock)
  538. lockPath.appendf("/_Locks/%s/", subLock);
  539. logicalName.makeXPathLName(lockPath);
  540. return CFileLockBase::init(lockPath, mode, conn, timeout, msg);
  541. }
  542. };
  543. class CFileSuperOwnerLock : protected CFileLockCompound
  544. {
  545. public:
  546. bool init(const CDfsLogicalFileName &logicalName, IRemoteConnection *conn, unsigned timeout, const char *msg)
  547. {
  548. return CFileLockCompound::init(logicalName, RTM_CREATE_QUERY | RTM_LOCK_WRITE | RTM_DELETE_ON_DISCONNECT, conn, "SuperOwnerLock", timeout, msg);
  549. }
  550. IRemoteConnection *detach()
  551. {
  552. return CFileLockCompound::detach();
  553. }
  554. bool initWithFileLock(const CDfsLogicalFileName &logicalName, unsigned timeout, const char *msg, CFileLock &fcl, unsigned fclmode)
  555. {
  556. // SuperOwnerLock while holding fcl
  557. IRemoteConnection *fclConn = fcl.queryConnection();
  558. if (!fclConn)
  559. return false; // throw ?
  560. CTimeMon tm(timeout);
  561. unsigned remaining = timeout;
  562. for (;;)
  563. {
  564. try
  565. {
  566. if (init(logicalName, NULL, 0, msg))
  567. return true;
  568. else
  569. return false; // throw ?
  570. }
  571. catch (ISDSException *e)
  572. {
  573. if (SDSExcpt_LockTimeout != e->errorCode() || tm.timedout(&remaining))
  574. throw;
  575. e->Release();
  576. }
  577. // release lock
  578. {
  579. fclConn->changeMode(RTM_NONE, remaining);
  580. }
  581. tm.timedout(&remaining);
  582. unsigned stime = 1000 * (2+getRandom()%15); // 2-15 sec
  583. if (stime > remaining)
  584. stime = remaining;
  585. // let another get excl lock
  586. Sleep(stime);
  587. tm.timedout(&remaining);
  588. // get lock again (waiting for other to release excl)
  589. {
  590. fclConn->changeMode(fclmode, remaining);
  591. fclConn->reload();
  592. }
  593. }
  594. }
  595. };
  596. class CScopeConnectLock
  597. {
  598. CConnectLock *lock;
  599. public:
  600. CScopeConnectLock()
  601. {
  602. lock = NULL;
  603. }
  604. CScopeConnectLock(const char *caller, const CDfsLogicalFileName &lname, bool write, bool preload, bool hold, unsigned timeout)
  605. {
  606. lock = NULL;
  607. init(caller, lname, write, preload, hold, timeout);
  608. }
  609. ~CScopeConnectLock()
  610. {
  611. delete lock;
  612. }
  613. bool init(const char *caller, const CDfsLogicalFileName &lname, bool write, bool preload, bool hold, unsigned timeout)
  614. {
  615. delete lock;
  616. StringBuffer query;
  617. lname.makeScopeQuery(query,true);
  618. lock = new CConnectLock(caller, query.str(), write, preload,hold, timeout);
  619. if (lock->conn.get()==NULL)
  620. {
  621. delete lock;
  622. lock = NULL;
  623. ensureFileScope(lname);
  624. lock = new CConnectLock(caller, query.str(), write, preload, hold, timeout);
  625. }
  626. return lock->conn.get()!=NULL;
  627. }
  628. IRemoteConnection *detach()
  629. {
  630. return lock?lock->detach():NULL;
  631. }
  632. IRemoteConnection *conn()
  633. {
  634. return lock?lock->conn:NULL;
  635. }
  636. IPropertyTree *queryRoot()
  637. {
  638. return (lock&&lock->conn.get())?lock->conn->queryRoot():NULL;
  639. }
  640. void remove()
  641. {
  642. if (lock&&lock->conn.get())
  643. lock->conn->close(true);
  644. }
  645. IPropertyTree *queryFileRoot(const CDfsLogicalFileName &dlfn,DfsXmlBranchKind &bkind)
  646. {
  647. bool external;
  648. bool foreign;
  649. external = dlfn.isExternal();
  650. foreign = dlfn.isForeign();
  651. if (external||foreign)
  652. return NULL;
  653. IPropertyTree *sroot = queryRoot();
  654. if (!sroot)
  655. return NULL;
  656. StringBuffer tail;
  657. dlfn.getTail(tail);
  658. StringBuffer query;
  659. getAttrQueryStr(query,queryDfsXmlBranchName(DXB_File),"@name",tail.str());
  660. IPropertyTree *froot = sroot->queryPropTree(query.str());
  661. bkind = DXB_File;
  662. if (!froot) {
  663. // check for super file
  664. getAttrQueryStr(query.clear(),queryDfsXmlBranchName(DXB_SuperFile),"@name",tail.str());
  665. froot = sroot->queryPropTree(query.str());
  666. if (froot)
  667. bkind = DXB_SuperFile;
  668. }
  669. return froot;
  670. }
  671. };
  672. class CClustersLockedSection
  673. {
  674. Owned<IRemoteConnection> conn;
  675. public:
  676. CClustersLockedSection(CDfsLogicalFileName &dlfn, bool exclusive)
  677. {
  678. StringBuffer xpath;
  679. dlfn.makeFullnameQuery(xpath,DXB_File,true).append("/ClusterLock");
  680. /* Avoid RTM_CREATE_QUERY connect() if possible by making 1st call without. This is to avoid write contention caused by RTM_CREATE*
  681. * NB: RTM_CREATE_QUERY should probably only gain exclusive access in Dali if node is missing.
  682. */
  683. conn.setown(querySDS().connect(xpath.str(), myProcessSession(), exclusive ? RTM_LOCK_WRITE : RTM_LOCK_READ, SDS_CONNECT_TIMEOUT));
  684. if (!conn.get()) // NB: ClusterLock is now created at File create time, so this can only be true for pre-existing File's
  685. {
  686. conn.setown(querySDS().connect(xpath.str(), myProcessSession(), RTM_CREATE_QUERY | RTM_LOCK_WRITE, SDS_CONNECT_TIMEOUT));
  687. assertex(conn.get());
  688. if (!exclusive)
  689. conn->changeMode(RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
  690. }
  691. }
  692. };
  693. static void checkDfsReplyException(MemoryBuffer &mb)
  694. {
  695. if (mb.length()<=sizeof(int))
  696. return;
  697. if ((*(int *)mb.bufferBase()) == -1) { // exception indicator
  698. int i;
  699. mb.read(i);
  700. throw deserializeException(mb);
  701. }
  702. }
  703. static void foreignDaliSendRecv(const INode *foreigndali,CMessageBuffer &mb, unsigned foreigndalitimeout)
  704. {
  705. SocketEndpoint ep = foreigndali->endpoint();
  706. if (ep.port==0)
  707. ep.port = DALI_SERVER_PORT;
  708. Owned<IGroup> grp = createIGroup(1,&ep);
  709. Owned<ICommunicator> comm = createCommunicator(grp,true);
  710. if (!comm->verifyConnection(0,foreigndalitimeout)) {
  711. StringBuffer tmp;
  712. IDFS_Exception *e = new CDFS_Exception(DFSERR_ForeignDaliTimeout, foreigndali->endpoint().getUrlStr(tmp).str());
  713. throw e;
  714. }
  715. comm->sendRecv(mb,0,MPTAG_DFS_REQUEST);
  716. }
  717. static bool isLocalDali(const INode *foreigndali)
  718. {
  719. if (!foreigndali)
  720. return true;
  721. Owned<INode> node;
  722. SocketEndpoint ep = foreigndali->endpoint();
  723. if (ep.port==0) {
  724. ep.port = DALI_SERVER_PORT;
  725. node.setown(createINode(ep));
  726. foreigndali = node.get();
  727. }
  728. return queryCoven().inCoven((INode *)foreigndali);
  729. }
  730. class FileClusterInfoArray: public IArrayOf<IClusterInfo>
  731. {
  732. ClusterPartDiskMapSpec defaultmapping;
  733. bool singleclusteroverride;
  734. public:
  735. FileClusterInfoArray()
  736. {
  737. singleclusteroverride = false;
  738. }
  739. void clear()
  740. {
  741. IArrayOf<IClusterInfo>::kill();
  742. }
  743. unsigned getNames(StringArray &clusternames)
  744. {
  745. StringBuffer name;
  746. ForEachItem(i) {
  747. clusternames.append(item(i).getClusterLabel(name.clear()).str());
  748. if (singleclusteroverride)
  749. break;
  750. }
  751. return clusternames.ordinality();
  752. }
  753. unsigned find(const char *_clusterName)
  754. {
  755. StringAttr clusterName = _clusterName;
  756. clusterName.toLowerCase();
  757. StringBuffer name;
  758. ForEachItem(i) {
  759. if (strcmp(item(i).getClusterLabel(name.clear()).str(),clusterName)==0)
  760. return i;
  761. if (singleclusteroverride)
  762. break;
  763. }
  764. return NotFound;
  765. }
  766. IGroup *queryGroup(unsigned clusternum)
  767. {
  768. if (clusternum>=ordinality())
  769. return NULL;
  770. if (singleclusteroverride&&clusternum)
  771. return NULL;
  772. return item(clusternum).queryGroup();
  773. }
  774. IGroup *getGroup(unsigned clusternum)
  775. {
  776. IGroup *ret = queryGroup(clusternum);
  777. return LINK(ret);
  778. }
  779. unsigned copyNum(unsigned part,unsigned copy,unsigned maxparts, unsigned *replicate)
  780. {
  781. ForEachItem(i) {
  782. IGroup *g = queryGroup(i);
  783. unsigned cw = g?g->ordinality():1;
  784. unsigned mc = item(i).queryPartDiskMapping().numCopies(part,cw,maxparts);
  785. if (copy<mc) {
  786. if (replicate)
  787. *replicate = copy;
  788. return i;
  789. }
  790. copy -= mc;
  791. if (singleclusteroverride)
  792. break;
  793. }
  794. return NotFound;
  795. }
  796. ClusterPartDiskMapSpec &queryPartDiskMapping(unsigned clusternum)
  797. {
  798. if (clusternum>=ordinality()||(singleclusteroverride&&clusternum))
  799. return defaultmapping;
  800. return item(clusternum).queryPartDiskMapping();
  801. }
  802. void updatePartDiskMapping(unsigned clusternum,const ClusterPartDiskMapSpec &spec)
  803. {
  804. if (clusternum<ordinality())
  805. item(clusternum).queryPartDiskMapping() = spec;
  806. }
  807. StringBuffer &getName(unsigned clusternum,StringBuffer &name)
  808. {
  809. if (clusternum<ordinality())
  810. item(clusternum).getClusterLabel(name);
  811. return name;
  812. }
  813. void setPreferred(const char *clusters,CDfsLogicalFileName &lfname)
  814. {
  815. unsigned nc = ordinality();
  816. if (nc<=1)
  817. return;
  818. StringBuffer cname;
  819. StringArray clustlist;
  820. if (lfname.getCluster(cname).length())
  821. clustlist.append(cname.str());
  822. unsigned i;
  823. if (clusters) {
  824. for (;;) {
  825. const char *s = clusters;
  826. while (*s&&(*s!=','))
  827. s++;
  828. if (s!=clusters) {
  829. cname.clear().append(s-clusters,clusters);
  830. for (i=0;i<clustlist.ordinality();i++)
  831. if (strcmp(clustlist.item(i),cname.str())==0)
  832. break;
  833. if (i==clustlist.ordinality())
  834. clustlist.append(cname.str());
  835. }
  836. if (!*s)
  837. break;
  838. clusters = s+1;
  839. }
  840. }
  841. if (clustlist.ordinality()==0) {
  842. // sort by closest to this node
  843. const IpAddress &myip = queryMyNode()->endpoint();
  844. unsigned *d=new unsigned[nc];
  845. for (i=0;i<nc;i++)
  846. d[i] = ipGroupDistance(myip,item(i).queryGroup());
  847. // bubble sort it - only a few
  848. for (i=0;i+1<nc;i++)
  849. for (unsigned j=0;j+i+1<nc;j++)
  850. if (d[j+1]<d[j]) {
  851. unsigned bd = d[j+1];
  852. d[j+1] = d[j];
  853. d[j] = bd;
  854. swap(j,j+1);
  855. }
  856. delete [] d;
  857. return;
  858. }
  859. Owned<IGroup> firstgrp;
  860. unsigned done = 0;
  861. StringBuffer name;
  862. StringBuffer name2;
  863. ForEachItemIn(ci,clustlist) {
  864. const char *cls = clustlist.item(ci);
  865. Owned<IGroup> grp = queryNamedGroupStore().lookup(cls);
  866. if (!grp) {
  867. IERRLOG("IDistributedFile::setPreferred - cannot find cluster %s",cls);
  868. return;
  869. }
  870. if (!firstgrp.get())
  871. firstgrp.set(grp);
  872. for (i=done;i<nc;i++) {
  873. IClusterInfo &info=item(i);
  874. if (stricmp(info.getClusterLabel(name2.clear()).str(),name.str())==0)
  875. break;
  876. IGroup *grp2 = info.queryGroup();
  877. if (grp2&&(grp->compare(grp2)!=GRdisjoint))
  878. break;
  879. }
  880. if (i<nc) {
  881. if (i) {
  882. Linked<IClusterInfo> tmp = &item(i);
  883. remove(i);
  884. add(*tmp.getClear(),done);
  885. }
  886. done++;
  887. if (done+1>=nc)
  888. break;
  889. }
  890. }
  891. if (done+1<nc) { // sort remaining by nearest to first group
  892. unsigned *d=new unsigned[nc]; // only use done to nc
  893. for (i=done;i<nc;i++)
  894. d[i] = groupDistance(firstgrp,item(i).queryGroup());
  895. // bubble sort it - only a few
  896. for (i=done;i+1<nc;i++)
  897. for (unsigned j=done;j+i+1<nc;j++)
  898. if (d[j+1]<d[j]) {
  899. unsigned bd = d[j+1];
  900. d[j+1] = d[j];
  901. d[j] = bd;
  902. swap(j,j+1);
  903. }
  904. delete [] d;
  905. }
  906. }
  907. void setSingleClusterOnly(bool set=true)
  908. {
  909. singleclusteroverride = set;
  910. }
  911. unsigned numCopies(unsigned part,unsigned maxparts)
  912. {
  913. unsigned ret = 0;
  914. ForEachItem(i) {
  915. IGroup *g = queryGroup(i);
  916. unsigned cw = g?g->ordinality():1;
  917. ret += item(i).queryPartDiskMapping().numCopies(part,cw,maxparts);
  918. if (singleclusteroverride)
  919. break;
  920. }
  921. return ret;
  922. }
  923. };
  924. // Internal extension of transaction interface, used to manipulate and track transaction
  925. interface IDistributedFileTransactionExt : extends IDistributedFileTransaction
  926. {
  927. virtual IUserDescriptor *queryUser()=0;
  928. virtual void descend()=0; // descend into a recursive call (can't autoCommit if depth is not zero)
  929. virtual void ascend()=0; // ascend back from the deep, one step at a time
  930. virtual void autoCommit()=0; // if transaction not active, commit straight away
  931. virtual void addAction(CDFAction *action)=0;
  932. virtual void addFile(IDistributedFile *file)=0;
  933. virtual void ensureFile(IDistributedFile *file)=0;
  934. virtual void clearFile(IDistributedFile *file)=0;
  935. virtual void clearFiles()=0;
  936. virtual void noteAddSubFile(IDistributedSuperFile *super, const char *superName, IDistributedFile *sub) = 0;
  937. virtual void noteRemoveSubFile(IDistributedSuperFile *super, IDistributedFile *sub) = 0;
  938. virtual void noteSuperSwap(IDistributedSuperFile *super1, IDistributedSuperFile *super2) = 0;
  939. virtual void clearSubFiles(IDistributedSuperFile *super) = 0;
  940. virtual void noteRename(IDistributedFile *file, const char *newName) = 0;
  941. virtual void validateAddSubFile(IDistributedSuperFile *super, IDistributedFile *sub, const char *subName) = 0;
  942. virtual bool isSubFile(IDistributedSuperFile *super, const char *subFile, bool sub) = 0;
  943. virtual bool addDelayedDelete(CDfsLogicalFileName &lfn,unsigned timeoutms=INFINITE)=0; // used internally to delay deletes until commit
  944. virtual bool prepareActions()=0;
  945. virtual void retryActions()=0;
  946. virtual void runActions()=0;
  947. virtual void commitAndClearup()=0;
  948. virtual ICodeContext *queryCodeContext()=0;
  949. };
  950. class CDistributedFileDirectory: implements IDistributedFileDirectory, public CInterface
  951. {
  952. Owned<IUserDescriptor> defaultudesc;
  953. Owned<IDFSredirection> redirection;
  954. void resolveForeignFiles(IPropertyTree *tree,const INode *foreigndali);
  955. protected: friend class CDistributedFile;
  956. StringAttr defprefclusters;
  957. unsigned defaultTimeout;
  958. public:
  959. IMPLEMENT_IINTERFACE;
  960. CDistributedFileDirectory()
  961. {
  962. defaultTimeout = INFINITE;
  963. defaultudesc.setown(createUserDescriptor());
  964. redirection.setown(createDFSredirection());
  965. }
  966. unsigned queryDefaultTimeout() const { return defaultTimeout; }
  967. IDistributedFile *dolookup(CDfsLogicalFileName &logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, unsigned timeout);
  968. IDistributedFile *lookup(const char *_logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, bool privilegedUser, unsigned timeout) override;
  969. IDistributedFile *lookup(CDfsLogicalFileName &logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, bool privilegedUser, unsigned timeout) override;
  970. /* createNew always creates an unnamed unattached distributed file
  971. * The caller must associated it with a name and credentials when it is attached (attach())
  972. */
  973. IDistributedFile *createNew(IFileDescriptor * fdesc);
  974. IDistributedFile *createExternal(IFileDescriptor *desc, const char *name);
  975. IDistributedSuperFile *createSuperFile(const char *logicalname,IUserDescriptor *user,bool interleaved,bool ifdoesnotexist,IDistributedFileTransaction *transaction=NULL);
  976. IDistributedSuperFile *createNewSuperFile(IPropertyTree *tree, const char *optionalName=nullptr);
  977. void removeSuperFile(const char *_logicalname, bool delSubs, IUserDescriptor *user, IDistributedFileTransaction *transaction);
  978. IDistributedFileIterator *getIterator(const char *wildname, bool includesuper,IUserDescriptor *user,bool isPrivilegedUser);
  979. IDFAttributesIterator *getDFAttributesIterator(const char *wildname, IUserDescriptor *user, bool recursive, bool includesuper,INode *foreigndali,unsigned foreigndalitimeout);
  980. IPropertyTreeIterator *getDFAttributesTreeIterator(const char *filters, DFUQResultField* localFilters, const char *localFilterBuf,
  981. IUserDescriptor *user, bool recursive, bool& allMatchingFilesReceived, INode *foreigndali,unsigned foreigndalitimeout);
  982. IDFAttributesIterator *getForeignDFAttributesIterator(const char *wildname, IUserDescriptor *user, bool recursive=true, bool includesuper=false, const char *foreigndali="", unsigned foreigndalitimeout=FOREIGN_DALI_TIMEOUT)
  983. {
  984. Owned<INode> foreign;
  985. if (foreigndali&&*foreigndali) {
  986. SocketEndpoint ep(foreigndali);
  987. foreign.setown(createINode(ep));
  988. }
  989. return getDFAttributesIterator(wildname, user, recursive, includesuper,foreign,foreigndalitimeout);
  990. }
  991. IDFScopeIterator *getScopeIterator(IUserDescriptor *user, const char *subscope,bool recursive,bool includeempty);
  992. bool loadScopeContents(const char *scopelfn,StringArray *scopes, StringArray *supers,StringArray *files, bool includeemptyscopes);
  993. IPropertyTree *getFileTree(const char *lname,IUserDescriptor *user,const INode *foreigndali,unsigned foreigndalitimeout,bool expandnodes=false, bool appendForeign=true);
  994. void setFileAccessed(CDfsLogicalFileName &dlfn, IUserDescriptor *user,const CDateTime &dt,const INode *foreigndali=NULL,unsigned foreigndalitimeout=FOREIGN_DALI_TIMEOUT);
  995. IFileDescriptor *getFileDescriptor(const char *lname,IUserDescriptor *user,const INode *foreigndali=NULL,unsigned foreigndalitimeout=FOREIGN_DALI_TIMEOUT);
  996. IDistributedFile *getFile(const char *lname,IUserDescriptor *user,const INode *foreigndali=NULL,unsigned foreigndalitimeout=FOREIGN_DALI_TIMEOUT);
  997. bool exists(const char *_logicalname,IUserDescriptor *user,bool notsuper=false,bool superonly=false);
  998. bool existsPhysical(const char *_logicalname,IUserDescriptor *user);
  999. void addEntry(CDfsLogicalFileName &lfn,IPropertyTree *root,bool superfile, bool ignoreexists);
  1000. bool removeEntry(const char *name, IUserDescriptor *user, IDistributedFileTransaction *transaction=NULL, unsigned timeoutms=INFINITE, bool throwException=false);
  1001. void renamePhysical(const char *oldname,const char *newname,IUserDescriptor *user,IDistributedFileTransaction *transaction);
  1002. void removeEmptyScope(const char *name);
  1003. IDistributedSuperFile *lookupSuperFile(const char *logicalname,IUserDescriptor *user,IDistributedFileTransaction *transaction,unsigned timeout=INFINITE);
  1004. SecAccessFlags getFilePermissions(const char *lname,IUserDescriptor *user,unsigned auditflags);
  1005. SecAccessFlags getNodePermissions(const IpAddress &ip,IUserDescriptor *user,unsigned auditflags);
  1006. SecAccessFlags getFDescPermissions(IFileDescriptor *,IUserDescriptor *user,unsigned auditflags=0);
  1007. void setDefaultUser(IUserDescriptor *user);
  1008. IUserDescriptor* queryDefaultUser();
  1009. DistributedFileCompareResult fileCompare(const char *lfn1,const char *lfn2,DistributedFileCompareMode mode,StringBuffer &errstr,IUserDescriptor *user);
  1010. bool filePhysicalVerify(const char *lfn1,IUserDescriptor *user,bool includecrc,StringBuffer &errstr);
  1011. void setDefaultPreferredClusters(const char *clusters);
  1012. void fixDates(IDistributedFile *fil);
  1013. GetFileClusterNamesType getFileClusterNames(const char *logicalname,StringArray &out); // returns 0 for normal file, 1 for
  1014. bool isSuperFile( const char *logicalname, IUserDescriptor *user=NULL, INode *foreigndali=NULL, unsigned timeout=0);
  1015. void promoteSuperFiles(unsigned numsf,const char **sfnames,const char *addsubnames,bool delsub,bool createonlyonesuperfile,IUserDescriptor *user, unsigned timeout, StringArray &outunlinked);
  1016. ISimpleSuperFileEnquiry * getSimpleSuperFileEnquiry(const char *logicalname,const char *title,IUserDescriptor *udesc,unsigned timeout);
  1017. bool getFileSuperOwners(const char *logicalname, StringArray &owners);
  1018. IDFSredirection & queryRedirection() { return *redirection; }
  1019. static StringBuffer &getFileRelationshipXPath(StringBuffer &xpath, const char *primary, const char *secondary,const char *primflds,const char *secflds,
  1020. const char *kind, const char *cardinality, const bool *payload
  1021. );
  1022. void doRemoveFileRelationship( IRemoteConnection *conn, const char *primary,const char *secondary,const char *primflds,const char *secflds, const char *kind);
  1023. void removeFileRelationships(const char *primary,const char *secondary, const char *primflds, const char *secflds, const char *kind);
  1024. 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);
  1025. IFileRelationshipIterator *lookupFileRelationships(const char *primary,const char *secondary,const char *primflds,const char *secflds,
  1026. const char *kind,const char *cardinality,const bool *payload,
  1027. const char *foreigndali,unsigned foreigndalitimeout);
  1028. void removeAllFileRelationships(const char *filename);
  1029. IFileRelationshipIterator *lookupAllFileRelationships(const char *filenames);
  1030. void renameFileRelationships(const char *oldname,const char *newname,IFileRelationshipIterator *reliter, IUserDescriptor *user);
  1031. bool publishMetaFileXML(const CDfsLogicalFileName &logicalname,IUserDescriptor *user);
  1032. IFileDescriptor *createDescriptorFromMetaFile(const CDfsLogicalFileName &logicalname,IUserDescriptor *user);
  1033. bool isProtectedFile(const CDfsLogicalFileName &logicalname, unsigned timeout) ;
  1034. IDFProtectedIterator *lookupProtectedFiles(const char *owner=NULL,bool notsuper=false,bool superonly=false);
  1035. IDFAttributesIterator* getLogicalFilesSorted(IUserDescriptor* udesc, DFUQResultField *sortOrder, const void *filterBuf, DFUQResultField *specialFilters,
  1036. const void *specialFilterBuf, unsigned startOffset, unsigned maxNum, __int64 *cacheHint, unsigned *total, bool *allMatchingFilesReceived);
  1037. IDFAttributesIterator* getLogicalFiles(IUserDescriptor* udesc, DFUQResultField *sortOrder, const void *filterBuf, DFUQResultField *specialFilters,
  1038. const void *specialFilterBuf, unsigned startOffset, unsigned maxNum, __int64 *cacheHint, unsigned *total, bool *allMatchingFilesReceived, bool recursive, bool sorted);
  1039. void setFileProtect(CDfsLogicalFileName &dlfn,IUserDescriptor *user, const char *owner, bool set, const INode *foreigndali=NULL,unsigned foreigndalitimeout=FOREIGN_DALI_TIMEOUT);
  1040. unsigned setDefaultTimeout(unsigned timems)
  1041. {
  1042. unsigned ret = defaultTimeout;
  1043. defaultTimeout = timems;
  1044. return ret;
  1045. }
  1046. virtual bool removePhysicalPartFiles(const char *logicalName, IFileDescriptor *fileDesc, IMultiException *mexcept, unsigned numParallelDeletes=0) override;
  1047. };
  1048. // === Transactions
  1049. class CDFAction: public CInterface
  1050. {
  1051. unsigned locked;
  1052. protected:
  1053. IDistributedFileTransactionExt *transaction;
  1054. IArrayOf<IDistributedFile> lockedFiles;
  1055. DFTransactionState state;
  1056. void addFileLock(IDistributedFile *file)
  1057. {
  1058. // derived's prepare must call this before locking
  1059. lockedFiles.append(*LINK(file));
  1060. }
  1061. bool lock()
  1062. {
  1063. // Files most have been acquired already by derived's class prepare
  1064. ForEachItemIn(i,lockedFiles)
  1065. {
  1066. try
  1067. {
  1068. lockedFiles.item(i).lockProperties(0);
  1069. }
  1070. catch (ISDSException *e)
  1071. {
  1072. if (SDSExcpt_LockTimeout != e->errorCode())
  1073. throw;
  1074. e->Release();
  1075. PROGLOG("CDFAction lock timed out on %s",lockedFiles.item(i).queryLogicalName());
  1076. return false;
  1077. }
  1078. locked++;
  1079. }
  1080. return true;
  1081. }
  1082. void unlock()
  1083. {
  1084. for(unsigned i=0; i<locked; i++)
  1085. lockedFiles.item(i).unlockProperties(state);
  1086. locked = 0;
  1087. lockedFiles.kill();
  1088. }
  1089. public:
  1090. CDFAction() : locked(0), state(TAS_NONE)
  1091. {
  1092. transaction = NULL;
  1093. }
  1094. // Clear all locked files (when re-using transaction on auto-commit mode)
  1095. virtual ~CDFAction()
  1096. {
  1097. if (transaction)
  1098. unlock();
  1099. }
  1100. void setTransaction(IDistributedFileTransactionExt *_transaction)
  1101. {
  1102. assertex(_transaction);
  1103. assertex(!transaction);
  1104. transaction = _transaction;
  1105. }
  1106. virtual bool prepare()=0; // should call lock
  1107. virtual void run()=0; // must override this
  1108. // If some lock fails, call this
  1109. virtual void retry()
  1110. {
  1111. state = TAS_RETRY;
  1112. unlock();
  1113. }
  1114. // MORE: In the rare event of a commit failure, not all actions can be rolled back.
  1115. // Since all actions today occur during "run", and since commit phases does very little,
  1116. // this chance is minimal and will probably be caused by corrupted file descriptors.
  1117. // The problem is that the state of the sub removals and the order in which they occur might not
  1118. // be trivial on such a low level error, and there's no way to atomically do operations in SDS
  1119. // at present time. We need more thought about this.
  1120. virtual void commit()
  1121. {
  1122. state = TAS_SUCCESS;
  1123. unlock();
  1124. }
  1125. virtual void rollback()
  1126. {
  1127. state = TAS_FAILURE;
  1128. unlock();
  1129. }
  1130. };
  1131. static void setUserDescriptor(Linked<IUserDescriptor> &udesc,IUserDescriptor *user)
  1132. {
  1133. if (!user)
  1134. {
  1135. #ifdef NULL_DALIUSER_STACKTRACE
  1136. StringBuffer sb;
  1137. if (user)
  1138. user->getUserName(sb);
  1139. if (sb.length()==0)
  1140. {
  1141. IERRLOG("UNEXPECTED USER (NULL) in dadfs.cpp setUserDescriptor() %d",__LINE__);
  1142. //following debug code to be removed
  1143. PrintStackReport();
  1144. }
  1145. #endif
  1146. user = queryDistributedFileDirectory().queryDefaultUser();
  1147. }
  1148. udesc.set(user);
  1149. }
  1150. static bool scopePermissionsAvail = true;
  1151. static SecAccessFlags getScopePermissions(const char *scopename,IUserDescriptor *user,unsigned auditflags)
  1152. { // scope must be normalized already
  1153. SecAccessFlags perms = SecAccess_Full;
  1154. if (scopePermissionsAvail && scopename && *scopename) {
  1155. if (!user)
  1156. {
  1157. #ifdef NULL_DALIUSER_STACKTRACE
  1158. IERRLOG("UNEXPECTED USER (NULL) in dadfs.cpp getScopePermissions() line %d",__LINE__);
  1159. //following debug code to be removed
  1160. PrintStackReport();
  1161. #endif
  1162. user = queryDistributedFileDirectory().queryDefaultUser();
  1163. }
  1164. perms = querySessionManager().getPermissionsLDAP(queryDfsXmlBranchName(DXB_Scope),scopename,user,auditflags);
  1165. if (perms<0) {
  1166. if (perms == SecAccess_Unavailable) {
  1167. scopePermissionsAvail=false;
  1168. perms = SecAccess_Full;
  1169. }
  1170. else
  1171. perms = SecAccess_None;
  1172. }
  1173. }
  1174. return perms;
  1175. }
  1176. static void checkLogicalScope(const char *scopename,IUserDescriptor *user,bool readreq,bool createreq)
  1177. {
  1178. // scope must be normalized already
  1179. if (!readreq&&!createreq)
  1180. return;
  1181. unsigned auditflags = 0;
  1182. if (readreq)
  1183. auditflags |= (DALI_LDAP_AUDIT_REPORT|DALI_LDAP_READ_WANTED);
  1184. if (createreq)
  1185. auditflags |= (DALI_LDAP_AUDIT_REPORT|DALI_LDAP_WRITE_WANTED);
  1186. #ifdef NULL_DALIUSER_STACKTRACE
  1187. if (!user)
  1188. {
  1189. IERRLOG("UNEXPECTED USER (NULL) in dadfs.cpp checkLogicalScope() line %d",__LINE__);
  1190. PrintStackReport();
  1191. }
  1192. #endif
  1193. SecAccessFlags perm = getScopePermissions(scopename,user,auditflags);
  1194. IDFS_Exception *e = NULL;
  1195. if (readreq&&!HASREADPERMISSION(perm))
  1196. e = new CDFS_Exception(DFSERR_LookupAccessDenied,scopename);
  1197. else if (createreq&&!HASWRITEPERMISSION(perm))
  1198. e = new CDFS_Exception(DFSERR_CreateAccessDenied,scopename);
  1199. if (e)
  1200. throw e;
  1201. }
  1202. bool checkLogicalName(CDfsLogicalFileName &dlfn,IUserDescriptor *user,bool readreq,bool createreq,bool allowquery,const char *specialnotallowedmsg)
  1203. {
  1204. bool ret = true;
  1205. if (dlfn.isMulti()) { //is temporary superFile?
  1206. if (specialnotallowedmsg)
  1207. throw MakeStringException(-1,"cannot %s a multi file name (%s)",specialnotallowedmsg,dlfn.get());
  1208. if (!dlfn.isExpanded())
  1209. dlfn.expand(user);//expand wildcards
  1210. unsigned i = dlfn.multiOrdinality();
  1211. while (--i)//continue looping even when ret is false, in order to check for illegal elements (foreigns/externals), and to check each scope permission
  1212. ret = checkLogicalName((CDfsLogicalFileName &)dlfn.multiItem(i),user,readreq,createreq,allowquery,specialnotallowedmsg)&&ret;
  1213. }
  1214. else {
  1215. if (specialnotallowedmsg) {
  1216. if (dlfn.isExternal()) {
  1217. if (dlfn.isQuery()&&allowquery)
  1218. ret = false;
  1219. else
  1220. throw MakeStringException(-1,"cannot %s an external file name (%s)",specialnotallowedmsg,dlfn.get());
  1221. }
  1222. if (dlfn.isForeign()) {
  1223. throw MakeStringException(-1,"cannot %s a foreign file name (%s)",specialnotallowedmsg,dlfn.get());
  1224. }
  1225. }
  1226. StringBuffer scopes;
  1227. dlfn.getScopes(scopes);
  1228. checkLogicalScope(scopes.str(),user,readreq,createreq);
  1229. }
  1230. return ret;
  1231. }
  1232. bool checkLogicalName(const char *lfn,IUserDescriptor *user,bool readreq,bool createreq,bool allowquery,const char *specialnotallowedmsg)
  1233. {
  1234. CDfsLogicalFileName dlfn;
  1235. dlfn.set(lfn);
  1236. return checkLogicalName(dlfn, user, readreq, createreq, allowquery, specialnotallowedmsg);
  1237. }
  1238. /*
  1239. * This class removes all files marked for deletion during transactions.
  1240. *
  1241. * TODO: the doDelete method re-acquires the lock to remove the files, and
  1242. * that creates a window (between end of commit and deletion) where other
  1243. * processes can acquire locks and blow things up. To fix this, you'd have
  1244. * to be selective on what files you unlock during commit, so that you
  1245. * can still keep an unified cache AND release the deletions later on.
  1246. */
  1247. class CDelayedDelete: public CInterface
  1248. {
  1249. CDfsLogicalFileName lfn;
  1250. Linked<IUserDescriptor> user;
  1251. unsigned timeoutms;
  1252. public:
  1253. CDelayedDelete(CDfsLogicalFileName &_lfn,IUserDescriptor *_user,unsigned _timeoutms)
  1254. : user(_user), timeoutms(_timeoutms)
  1255. {
  1256. lfn.set(_lfn);
  1257. }
  1258. void doDelete() // Throw on error!
  1259. {
  1260. const char *logicalname = lfn.get();
  1261. if (!lfn.isExternal() && !checkLogicalName(lfn,user,true,true,true,"remove"))
  1262. ThrowStringException(-1, "Logical Name fails for removal on %s", lfn.get());
  1263. CTimeMon timer(timeoutms);
  1264. for (;;)
  1265. {
  1266. // Transaction files have already been unlocked at this point, delete all remaining files
  1267. Owned<IDistributedFile> file = queryDistributedFileDirectory().lookup(lfn, user, true, false, true, nullptr, defaultPrivilegedUser, SDS_SUB_LOCK_TIMEOUT);
  1268. if (!file.get())
  1269. return;
  1270. StringBuffer reason;
  1271. if (!file->canRemove(reason, false))
  1272. ThrowStringException(-1, "Can't remove %s: %s", lfn.get(), reason.str());
  1273. Owned<IException> timeoutException;
  1274. // This will do the right thing for either super-files and logical-files.
  1275. try
  1276. {
  1277. file->detach(0, NULL); // 0 == timeout immediately if cannot get exclusive lock
  1278. return;
  1279. }
  1280. catch (ISDSException *e)
  1281. {
  1282. switch (e->errorCode())
  1283. {
  1284. case SDSExcpt_LockTimeout:
  1285. case SDSExcpt_LockHeld:
  1286. timeoutException.setown(e);
  1287. break;
  1288. default:
  1289. throw;
  1290. }
  1291. }
  1292. file.clear();
  1293. unsigned sleepTime = SDS_TRANSACTION_RETRY/2+(getRandom()%SDS_TRANSACTION_RETRY);
  1294. if (INFINITE != timeoutms)
  1295. {
  1296. unsigned remaining;
  1297. if (timer.timedout(&remaining))
  1298. {
  1299. StringBuffer timeoutText;
  1300. throwStringExceptionV(-1, "Failed to remove %s: %s", logicalname, timeoutException->errorMessage(timeoutText).str());
  1301. }
  1302. if (sleepTime>remaining)
  1303. sleepTime = remaining;
  1304. }
  1305. PROGLOG("CDelayedDelete: pausing due to locked file = %s", logicalname);
  1306. Sleep(sleepTime);
  1307. }
  1308. }
  1309. };
  1310. class CDistributedFileTransaction: implements IDistributedFileTransactionExt, public CInterface
  1311. {
  1312. class CTransactionFile : public CSimpleInterface
  1313. {
  1314. class HTMapping : public CInterface
  1315. {
  1316. IDistributedFile *file;
  1317. StringAttr name;
  1318. public:
  1319. HTMapping(const char *_name, IDistributedFile *_file) : name(_name), file(_file) { }
  1320. IDistributedFile &query() { return *file; }
  1321. const char *queryFindString() const { return name; }
  1322. const void *queryFindParam() const { return &file; }
  1323. };
  1324. class CSubFileIter : protected SuperHashIteratorOf<HTMapping>, implements IDistributedFileIterator
  1325. {
  1326. typedef SuperHashIteratorOf<HTMapping> PARENT;
  1327. public:
  1328. IMPLEMENT_IINTERFACE_USING(PARENT);
  1329. CSubFileIter(OwningStringSuperHashTableOf<HTMapping> &table) : PARENT(table)
  1330. {
  1331. }
  1332. // IDistributedFileIterator impl.
  1333. virtual IDistributedFile &query()
  1334. {
  1335. HTMapping &map = PARENT::query();
  1336. return map.query();
  1337. }
  1338. virtual bool first() { return PARENT::first(); }
  1339. virtual bool isValid() { return PARENT::isValid(); }
  1340. virtual bool next() { return PARENT::next(); }
  1341. virtual StringBuffer &getName(StringBuffer &name)
  1342. {
  1343. HTMapping &map = PARENT::query();
  1344. return name.append(map.queryFindString());
  1345. }
  1346. };
  1347. CDistributedFileTransaction &owner;
  1348. OwningStringSuperHashTableOf<HTMapping> subFilesByName;
  1349. StringAttr name;
  1350. Linked<IDistributedFile> file;
  1351. public:
  1352. CTransactionFile(CDistributedFileTransaction &_owner, const char *_name, IDistributedFile *_file) : owner(_owner), name(_name), file(_file)
  1353. {
  1354. }
  1355. const char *queryName() const { return name; }
  1356. IDistributedFile *queryFile() { return file; }
  1357. IDistributedFileIterator *getSubFiles()
  1358. {
  1359. IDistributedSuperFile *super = file->querySuperFile();
  1360. if (!super)
  1361. return NULL;
  1362. return new CSubFileIter(subFilesByName);
  1363. }
  1364. void clearSubs()
  1365. {
  1366. subFilesByName.kill();
  1367. }
  1368. unsigned numSubFiles() const { return subFilesByName.count(); }
  1369. void noteAddSubFile(IDistributedFile *sub)
  1370. {
  1371. if (NULL == subFilesByName.find(sub->queryLogicalName()))
  1372. {
  1373. Owned<HTMapping> map = new HTMapping(sub->queryLogicalName(), sub);
  1374. subFilesByName.replace(*map.getLink());
  1375. }
  1376. }
  1377. void noteRemoveSubFile(IDistributedFile *sub)
  1378. {
  1379. HTMapping *map = subFilesByName.find(sub->queryLogicalName());
  1380. if (map)
  1381. verifyex(subFilesByName.removeExact(map));
  1382. }
  1383. bool find(const char *subFile, bool sub)
  1384. {
  1385. StringBuffer tmp;
  1386. subFile = normalizeLFN(subFile, tmp);
  1387. HTMapping *match = subFilesByName.find(subFile);
  1388. if (match)
  1389. return true;
  1390. else if (sub)
  1391. {
  1392. SuperHashIteratorOf<HTMapping> iter(subFilesByName);
  1393. ForEach(iter)
  1394. {
  1395. HTMapping &map = iter.query();
  1396. IDistributedFile &file = map.query();
  1397. IDistributedSuperFile *super = file.querySuperFile();
  1398. if (super)
  1399. {
  1400. if (owner.isSubFile(super, subFile, sub))
  1401. return true;
  1402. }
  1403. }
  1404. }
  1405. return false;
  1406. }
  1407. const void *queryFindParam() const { return &file; }
  1408. const char *queryFindString() const { return name; }
  1409. };
  1410. CIArrayOf<CDFAction> actions;
  1411. OwningSimpleHashTableOf<CTransactionFile, IDistributedFile *> trackedFiles;
  1412. OwningStringSuperHashTableOf<CTransactionFile> trackedFilesByName;
  1413. bool isactive;
  1414. Linked<IUserDescriptor> udesc;
  1415. CIArrayOf<CDelayedDelete> delayeddelete;
  1416. // auto-commit only works at depth zero (for recursive calls)
  1417. // MORE: Maybe this needs a context object (descend on c-tor, ascend on d-tor)
  1418. // But we need all actions within transactions first to find out if there is
  1419. // any exception to the rule used by addSubFile / removeSubFile
  1420. unsigned depth;
  1421. unsigned prepared;
  1422. ICodeContext *codeCtx;
  1423. /* 'owner' is set if, transaction object is implicitly created, because none provided
  1424. * The owner cannot be release or unlocked. The transaction can still retry if other files are locked,
  1425. * so need to ensure 'owner' remains in tracked file cache.
  1426. */
  1427. IDistributedSuperFile *owner;
  1428. CTransactionFile *queryCreate(const char *name, IDistributedFile *file, bool recreate=false)
  1429. {
  1430. Owned<CTransactionFile> trackedFile;
  1431. if (!recreate)
  1432. trackedFile.set(trackedFiles.find(file));
  1433. if (!trackedFile)
  1434. {
  1435. StringBuffer tmp;
  1436. name = normalizeLFN(name, tmp);
  1437. trackedFile.setown(new CTransactionFile(*this, tmp.str(), file));
  1438. trackedFiles.replace(*trackedFile.getLink());
  1439. trackedFilesByName.replace(*trackedFile.getLink());
  1440. }
  1441. return trackedFile;
  1442. }
  1443. CTransactionFile *lookupTrackedFile(IDistributedFile *file)
  1444. {
  1445. return trackedFiles.find(file);
  1446. }
  1447. void commitActions()
  1448. {
  1449. while (actions.ordinality()) // if we get here everything should work!
  1450. {
  1451. Owned<CDFAction> action = &actions.popGet();
  1452. action->commit();
  1453. }
  1454. }
  1455. IDistributedFile *findFile(const char *name)
  1456. {
  1457. StringBuffer tmp;
  1458. name = normalizeLFN(name, tmp);
  1459. CTransactionFile *trackedFile = trackedFilesByName.find(tmp.str());
  1460. if (!trackedFile)
  1461. return NULL;
  1462. return trackedFile->queryFile();
  1463. }
  1464. void deleteFiles() // no rollback at this point
  1465. {
  1466. Owned<IMultiException> me = MakeMultiException("Transaction");
  1467. ForEachItemIn(i,delayeddelete) {
  1468. try {
  1469. delayeddelete.item(i).doDelete();
  1470. } catch (IException *e) {
  1471. me->append(*e);
  1472. }
  1473. }
  1474. delayeddelete.kill();
  1475. if (me->ordinality())
  1476. throw me.getClear();
  1477. }
  1478. public:
  1479. IMPLEMENT_IINTERFACE;
  1480. CDistributedFileTransaction(IUserDescriptor *user, IDistributedSuperFile *_owner=NULL, ICodeContext *_codeCtx=NULL)
  1481. : isactive(false), depth(0), prepared(0), owner(_owner), codeCtx(_codeCtx)
  1482. {
  1483. setUserDescriptor(udesc,user);
  1484. }
  1485. ~CDistributedFileTransaction()
  1486. {
  1487. // New files should be removed automatically if not committed
  1488. // MORE - refactor cCreateSuperFileAction to avoid this
  1489. if (isactive)
  1490. rollback();
  1491. assert(depth == 0);
  1492. }
  1493. // IDistributedFileTransaction impl.
  1494. virtual void start()
  1495. {
  1496. if (isactive)
  1497. throw MakeStringException(-1,"Transaction already started");
  1498. clearFiles();
  1499. actions.kill();
  1500. isactive = true;
  1501. prepared = 0;
  1502. assertex(actions.ordinality()==0);
  1503. }
  1504. virtual void commit()
  1505. {
  1506. if (!isactive)
  1507. return;
  1508. IException *rete=NULL;
  1509. // =============== PREPARE AND RETRY UNTIL READY
  1510. try
  1511. {
  1512. for (;;)
  1513. {
  1514. if (prepareActions())
  1515. break;
  1516. else
  1517. retryActions();
  1518. PROGLOG("CDistributedFileTransaction: Transaction pausing");
  1519. Sleep(SDS_TRANSACTION_RETRY/2+(getRandom()%SDS_TRANSACTION_RETRY));
  1520. }
  1521. runActions();
  1522. commitAndClearup();
  1523. return;
  1524. }
  1525. catch (IException *e)
  1526. {
  1527. rete = e;
  1528. }
  1529. rollback();
  1530. throw rete;
  1531. }
  1532. virtual void rollback()
  1533. {
  1534. // =============== ROLLBACK AND CLEANUP
  1535. while (actions.ordinality())
  1536. {
  1537. try
  1538. {
  1539. // we don't want to unlock what hasn't been locked
  1540. // if an exception was thrown while locking, but we
  1541. // do want to pop them all
  1542. Owned<CDFAction> action = &actions.popGet();
  1543. if (actions.ordinality()<prepared)
  1544. action->rollback();
  1545. }
  1546. catch (IException *e)
  1547. {
  1548. e->Release();
  1549. }
  1550. }
  1551. actions.kill(); // should be empty
  1552. clearFiles(); // release locks
  1553. if (!isactive)
  1554. return;
  1555. isactive = false;
  1556. // this we only want to do if active
  1557. delayeddelete.kill();
  1558. }
  1559. virtual bool active()
  1560. {
  1561. return isactive;
  1562. }
  1563. virtual IDistributedFile *lookupFile(const char *name,unsigned timeout)
  1564. {
  1565. IDistributedFile *ret = findFile(name);
  1566. if (ret)
  1567. return LINK(ret);
  1568. else
  1569. {
  1570. ret = queryDistributedFileDirectory().lookup(name, udesc, false, false, false, this, defaultPrivilegedUser, timeout);
  1571. if (ret)
  1572. queryCreate(name, ret, true);
  1573. return ret;
  1574. }
  1575. }
  1576. virtual IDistributedSuperFile *lookupSuperFile(const char *name, unsigned timeout)
  1577. {
  1578. IDistributedFile *f = findFile(name);
  1579. if (f)
  1580. return LINK(f->querySuperFile());
  1581. else
  1582. {
  1583. IDistributedSuperFile *ret = queryDistributedFileDirectory().lookupSuperFile(name,udesc,this,timeout);
  1584. if (ret)
  1585. addFile(ret);
  1586. return ret;
  1587. }
  1588. }
  1589. // IDistributedFileTransactionExt impl.
  1590. virtual IUserDescriptor *queryUser()
  1591. {
  1592. return udesc;
  1593. }
  1594. virtual void descend() // Call this when you're recurring
  1595. {
  1596. depth++;
  1597. }
  1598. virtual void ascend() // Call this at the end of the block that started recursion
  1599. {
  1600. assertex(depth);
  1601. depth--;
  1602. }
  1603. virtual void autoCommit()
  1604. {
  1605. // Recursive calls to transaction will not commit until
  1606. // all calls have finished (gone back to zero depth)
  1607. if (!depth && !isactive) {
  1608. try {
  1609. isactive = true;
  1610. commit();
  1611. }
  1612. catch (IException *) {
  1613. rollback();
  1614. throw;
  1615. }
  1616. }
  1617. }
  1618. virtual void addAction(CDFAction *action)
  1619. {
  1620. actions.append(*action); // takes ownership
  1621. action->setTransaction(this);
  1622. }
  1623. virtual void addFile(IDistributedFile *file)
  1624. {
  1625. CTransactionFile *trackedFile = queryCreate(file->queryLogicalName(), file, false);
  1626. // Also add subfiles to cache
  1627. IDistributedSuperFile *sfile = file->querySuperFile();
  1628. if (sfile)
  1629. {
  1630. Owned<IDistributedFileIterator> iter = sfile->getSubFileIterator();
  1631. ForEach(*iter)
  1632. {
  1633. IDistributedFile *f = &iter->query();
  1634. trackedFile->noteAddSubFile(f);
  1635. addFile(f);
  1636. }
  1637. }
  1638. }
  1639. virtual void ensureFile(IDistributedFile *file)
  1640. {
  1641. if (!trackedFiles.find(file))
  1642. addFile(file);
  1643. }
  1644. virtual void clearFile(IDistributedFile *file)
  1645. {
  1646. CTransactionFile *trackedFile = lookupTrackedFile(file);
  1647. IDistributedSuperFile *sfile = file->querySuperFile();
  1648. if (trackedFile)
  1649. {
  1650. Owned<IDistributedFileIterator> iter = trackedFile->getSubFiles();
  1651. if (iter)
  1652. {
  1653. ForEach(*iter)
  1654. clearFile(&iter->query());
  1655. }
  1656. trackedFiles.removeExact(trackedFile);
  1657. trackedFilesByName.removeExact(trackedFile);
  1658. }
  1659. }
  1660. virtual void clearFiles()
  1661. {
  1662. trackedFiles.kill();
  1663. trackedFilesByName.kill();
  1664. if (owner)
  1665. addFile(owner); // ensure remains tracked
  1666. }
  1667. virtual void noteAddSubFile(IDistributedSuperFile *super, const char *superName, IDistributedFile *sub)
  1668. {
  1669. CTransactionFile *trackedSuper = queryCreate(superName, super);
  1670. trackedSuper->noteAddSubFile(sub);
  1671. }
  1672. virtual void noteRemoveSubFile(IDistributedSuperFile *super, IDistributedFile *sub)
  1673. {
  1674. CTransactionFile *trackedSuper = lookupTrackedFile(super);
  1675. if (trackedSuper)
  1676. trackedSuper->noteRemoveSubFile(sub);
  1677. }
  1678. virtual void noteSuperSwap(IDistributedSuperFile *super1, IDistributedSuperFile *super2)
  1679. {
  1680. CTransactionFile *trackedSuper1 = lookupTrackedFile(super1);
  1681. CTransactionFile *trackedSuper2 = lookupTrackedFile(super2);
  1682. assertex(trackedSuper1 && trackedSuper2);
  1683. ICopyArrayOf<IDistributedFile> super1Subs, super2Subs;
  1684. Owned<IDistributedFileIterator> iter = trackedSuper1->getSubFiles();
  1685. ForEach(*iter)
  1686. super1Subs.append(iter->query());
  1687. trackedSuper1->clearSubs();
  1688. iter.setown(trackedSuper2->getSubFiles());
  1689. ForEach(*iter)
  1690. super2Subs.append(iter->query());
  1691. trackedSuper2->clearSubs();
  1692. ForEachItemIn(s, super2Subs)
  1693. trackedSuper1->noteAddSubFile(&super2Subs.item(s));
  1694. ForEachItemIn(s2, super1Subs)
  1695. trackedSuper2->noteAddSubFile(&super1Subs.item(s2));
  1696. }
  1697. virtual void clearSubFiles(IDistributedSuperFile *super)
  1698. {
  1699. CTransactionFile *trackedSuper = lookupTrackedFile(super);
  1700. if (trackedSuper)
  1701. trackedSuper->clearSubs();
  1702. }
  1703. virtual void noteRename(IDistributedFile *file, const char *newName)
  1704. {
  1705. CTransactionFile *trackedFile = lookupTrackedFile(file);
  1706. if (trackedFile)
  1707. {
  1708. trackedFiles.removeExact(trackedFile);
  1709. trackedFilesByName.removeExact(trackedFile);
  1710. trackedFile = queryCreate(newName, file);
  1711. }
  1712. }
  1713. virtual void validateAddSubFile(IDistributedSuperFile *super, IDistributedFile *sub, const char *subName);
  1714. virtual bool isSubFile(IDistributedSuperFile *super, const char *subFile, bool sub)
  1715. {
  1716. CTransactionFile *trackedSuper = lookupTrackedFile(super);
  1717. if (!trackedSuper)
  1718. return false;
  1719. return trackedSuper->find(subFile, sub);
  1720. }
  1721. virtual bool addDelayedDelete(CDfsLogicalFileName &lfn,unsigned timeoutms)
  1722. {
  1723. delayeddelete.append(*new CDelayedDelete(lfn,udesc,timeoutms));
  1724. return true;
  1725. }
  1726. virtual bool prepareActions()
  1727. {
  1728. prepared = 0;
  1729. unsigned toPrepare = actions.ordinality();
  1730. ForEachItemIn(i0,actions)
  1731. {
  1732. if (actions.item(i0).prepare())
  1733. ++prepared;
  1734. else
  1735. break;
  1736. }
  1737. return prepared == toPrepare;
  1738. }
  1739. virtual void retryActions()
  1740. {
  1741. clearFiles(); // clear all previously tracked pending file changes, e.g. renames, super file additions/removals
  1742. while (prepared) // unlock for retry
  1743. actions.item(--prepared).retry();
  1744. }
  1745. virtual void runActions()
  1746. {
  1747. ForEachItemIn(i,actions)
  1748. actions.item(i).run();
  1749. }
  1750. virtual void commitAndClearup()
  1751. {
  1752. // =============== COMMIT and CLEANUP
  1753. commitActions();
  1754. clearFiles();
  1755. isactive = false;
  1756. actions.kill();
  1757. deleteFiles();
  1758. }
  1759. virtual ICodeContext *queryCodeContext()
  1760. {
  1761. return codeCtx;
  1762. }
  1763. };
  1764. static bool recursiveCheckEmptyScope(IPropertyTree &ct)
  1765. {
  1766. Owned<IPropertyTreeIterator> iter = ct.getElements("*");
  1767. ForEach(*iter) {
  1768. IPropertyTree &item = iter->query();
  1769. const char *n = item.queryName();
  1770. if (!n||(strcmp(n,queryDfsXmlBranchName(DXB_Scope))!=0))
  1771. return false;
  1772. if (!recursiveCheckEmptyScope(item))
  1773. return false;
  1774. }
  1775. return true;
  1776. }
  1777. class CDFScopeIterator: implements IDFScopeIterator, public CInterface
  1778. {
  1779. PointerArray scopes;
  1780. unsigned index;
  1781. IDistributedFileDirectory *dir;
  1782. bool includeempty;
  1783. void add(IPropertyTree &t, bool recursive, StringBuffer &name)
  1784. {
  1785. name.trim();
  1786. size32_t nl = name.length();
  1787. size32_t l=nl;
  1788. if (nl) {
  1789. name.append("::");
  1790. l+=2;
  1791. }
  1792. Owned<IPropertyTreeIterator> iter = t.getElements(queryDfsXmlBranchName(DXB_Scope));
  1793. ForEach(*iter) {
  1794. IPropertyTree &ct = iter->query();
  1795. if (includeempty||!recursiveCheckEmptyScope(ct)) {
  1796. name.append(ct.queryProp("@name"));
  1797. scopes.append(strdup(name.str()));
  1798. if (recursive)
  1799. add(ct,recursive,name);
  1800. name.setLength(l);
  1801. }
  1802. }
  1803. name.setLength(nl);
  1804. }
  1805. public:
  1806. IMPLEMENT_IINTERFACE;
  1807. CDFScopeIterator(IDistributedFileDirectory *_dir,const char *base,bool recursive, bool _includeempty,unsigned timeout) // attrib not yet implemented
  1808. {
  1809. includeempty = _includeempty;
  1810. dir = _dir;
  1811. StringBuffer lockPath;
  1812. if (!isEmptyString(base))
  1813. {
  1814. CDfsLogicalFileName dlfn;
  1815. dlfn.set(base, "dummyfilename"); // makeScopeQuery expects a lfn to a file, 'dummyfilename' will not be used
  1816. dlfn.makeScopeQuery(lockPath, true);
  1817. }
  1818. else
  1819. lockPath.append(querySdsFilesRoot());
  1820. {
  1821. CConnectLock connlock("CDFScopeIterator", lockPath, false, false, false, timeout);
  1822. if (connlock.conn)
  1823. {
  1824. StringBuffer name;
  1825. add(*connlock.conn->queryRoot(),recursive,name);
  1826. }
  1827. }
  1828. if (scopes.ordinality()>1)
  1829. qsortvec(scopes.getArray(),scopes.ordinality(),strcompare);
  1830. index = 0;
  1831. }
  1832. ~CDFScopeIterator()
  1833. {
  1834. ForEachItemIn(i,scopes) {
  1835. free(scopes.item(i));
  1836. }
  1837. }
  1838. bool first()
  1839. {
  1840. index = 0;
  1841. return isValid();
  1842. }
  1843. bool next()
  1844. {
  1845. index++;
  1846. return isValid();
  1847. }
  1848. bool isValid()
  1849. {
  1850. return (index<scopes.ordinality());
  1851. }
  1852. const char *query()
  1853. {
  1854. return (const char *)scopes.item(index);
  1855. }
  1856. };
  1857. struct SerializeFileAttrOptions
  1858. {
  1859. bool includeSuperOwner;
  1860. //Add more as needed
  1861. SerializeFileAttrOptions()
  1862. {
  1863. includeSuperOwner = false;
  1864. }
  1865. };
  1866. class CDFAttributeIterator: implements IDFAttributesIterator, public CInterface
  1867. {
  1868. unsigned index;
  1869. IArrayOf<IPropertyTree> attrs;
  1870. public:
  1871. IMPLEMENT_IINTERFACE;
  1872. static MemoryBuffer &serializeFileAttributes(MemoryBuffer &mb, IPropertyTree &root, const char *name, bool issuper, SerializeFileAttrOptions& option)
  1873. {
  1874. StringBuffer buf;
  1875. mb.append(name);
  1876. if (issuper) {
  1877. mb.append("!SF");
  1878. mb.append(root.getPropInt("@numsubfiles",0));
  1879. mb.append("");
  1880. }
  1881. else {
  1882. mb.append(root.queryProp("@directory"));
  1883. mb.append(root.getPropInt("@numparts",0));
  1884. mb.append(root.queryProp("@partmask"));
  1885. }
  1886. mb.append(root.queryProp("@modified"));
  1887. Owned<IPropertyTree> attrs = root.getPropTree("Attr");;
  1888. Owned<IAttributeIterator> attriter;
  1889. if (attrs)
  1890. attriter.setown(attrs->getAttributes());
  1891. unsigned count=0;
  1892. size32_t countpos = mb.length();
  1893. mb.append(count);
  1894. if (attriter.get()&&attriter->first()) {
  1895. do {
  1896. mb.append(attriter->queryName());
  1897. mb.append(attriter->queryValue());
  1898. count++;
  1899. } while (attriter->next());
  1900. }
  1901. const char *ps = root.queryProp("@group");
  1902. if (ps&&*ps) {
  1903. count++;
  1904. mb.append("@group");
  1905. mb.append(ps);
  1906. }
  1907. // add protected
  1908. if (attrs) {
  1909. Owned<IPropertyTreeIterator> piter = attrs->getElements("Protect");
  1910. StringBuffer plist;
  1911. ForEach(*piter) {
  1912. const char *name = piter->get().queryProp("@name");
  1913. if (name&&*name) {
  1914. if (plist.length())
  1915. plist.append(',');
  1916. plist.append(name);
  1917. }
  1918. }
  1919. if (plist.length()) {
  1920. count++;
  1921. mb.append("@protect");
  1922. mb.append(plist.str());
  1923. }
  1924. }
  1925. if (option.includeSuperOwner) {
  1926. //add superowners
  1927. StringBuffer soList;
  1928. Owned<IPropertyTreeIterator> superOwners = root.getElements("SuperOwner");
  1929. ForEach(*superOwners) {
  1930. IPropertyTree &superOwner = superOwners->query();
  1931. const char *name = superOwner.queryProp("@name");
  1932. if (name&&*name) {
  1933. if (soList.length())
  1934. soList.append(",");
  1935. soList.append(name);
  1936. }
  1937. }
  1938. if (soList.length()) {
  1939. count++;
  1940. mb.append("@superowners");
  1941. mb.append(soList.str());
  1942. }
  1943. }
  1944. mb.writeDirect(countpos,sizeof(count),&count);
  1945. return mb;
  1946. }
  1947. CDFAttributeIterator(MemoryBuffer &mb) // attrib not yet implemented
  1948. {
  1949. unsigned numfiles;
  1950. mb.read(numfiles);
  1951. while (numfiles--) {
  1952. IPropertyTree *attr = getEmptyAttr();
  1953. StringAttr val;
  1954. unsigned n;
  1955. mb.read(val);
  1956. attr->setProp("@name",val.get());
  1957. mb.read(val);
  1958. if (stricmp(val,"!SF")==0) {
  1959. mb.read(n);
  1960. attr->setPropInt("@numsubfiles",n);
  1961. mb.read(val); // not used currently
  1962. }
  1963. else {
  1964. attr->setProp("@directory",val.get());
  1965. mb.read(n);
  1966. attr->setPropInt("@numparts",n);
  1967. mb.read(val);
  1968. attr->setProp("@partmask",val.get());
  1969. }
  1970. mb.read(val);
  1971. attr->setProp("@modified",val.get());
  1972. unsigned count;
  1973. mb.read(count);
  1974. StringAttr at;
  1975. while (count--) {
  1976. mb.read(at);
  1977. mb.read(val);
  1978. attr->setProp(at.get(),val.get());
  1979. }
  1980. attrs.append(*attr);
  1981. }
  1982. index = 0;
  1983. }
  1984. CDFAttributeIterator(IArrayOf<IPropertyTree>& trees)
  1985. {
  1986. ForEachItemIn(t, trees)
  1987. attrs.append(*LINK(&trees.item(t)));
  1988. index = 0;
  1989. }
  1990. ~CDFAttributeIterator()
  1991. {
  1992. attrs.kill();
  1993. }
  1994. bool first()
  1995. {
  1996. index = 0;
  1997. return (attrs.ordinality()!=0);
  1998. }
  1999. bool next()
  2000. {
  2001. index++;
  2002. return (index<attrs.ordinality());
  2003. }
  2004. bool isValid()
  2005. {
  2006. return (index<attrs.ordinality());
  2007. }
  2008. IPropertyTree & query()
  2009. {
  2010. return attrs.item(index);
  2011. }
  2012. };
  2013. class CDFProtectedIterator: implements IDFProtectedIterator, public CInterface
  2014. {
  2015. StringAttr owner;
  2016. StringAttr fn;
  2017. bool issuper;
  2018. Owned<IRemoteConnection> conn;
  2019. Owned<IPropertyTreeIterator> fiter;
  2020. Owned<IPropertyTreeIterator> piter;
  2021. unsigned defaultTimeout;
  2022. bool notsuper;
  2023. bool superonly;
  2024. void fill()
  2025. {
  2026. IPropertyTree &t = fiter->query();
  2027. fn.set(t.queryProp("OrigName"));
  2028. IPropertyTree &pt = piter->query();
  2029. owner.set(pt.queryProp("@name"));
  2030. }
  2031. void clear()
  2032. {
  2033. piter.clear();
  2034. fiter.clear();
  2035. conn.clear();
  2036. issuper = false;
  2037. }
  2038. public:
  2039. IMPLEMENT_IINTERFACE;
  2040. CDFProtectedIterator(const char *_owner,bool _notsuper,bool _superonly,unsigned _defaultTimeout)
  2041. : owner(_owner)
  2042. {
  2043. issuper = false;
  2044. notsuper=_notsuper;
  2045. superonly=_superonly;
  2046. defaultTimeout = _defaultTimeout;
  2047. }
  2048. ~CDFProtectedIterator()
  2049. {
  2050. clear();
  2051. }
  2052. bool first()
  2053. {
  2054. clear();
  2055. conn.setown(querySDS().connect("Files",myProcessSession(),0, defaultTimeout));
  2056. if (!conn)
  2057. return false;
  2058. IPropertyTree *t = conn->queryRoot();
  2059. if (!superonly) {
  2060. fiter.setown(t->getElements("//File[Attr/Protect]", iptiter_remote));
  2061. if (fiter->first()) {
  2062. piter.setown(fiter->query().getElements("Attr/Protect"));
  2063. if (piter->first()) {
  2064. fill();
  2065. return true;
  2066. }
  2067. }
  2068. }
  2069. if (!notsuper) {
  2070. issuper = true;
  2071. fiter.clear();
  2072. fiter.setown(t->getElements("//SuperFile[Attr/Protect]", iptiter_remote));
  2073. if (fiter->first()) {
  2074. piter.setown(fiter->query().getElements("Attr/Protect"));
  2075. if (piter->first()) {
  2076. fill();
  2077. return true;
  2078. }
  2079. }
  2080. }
  2081. clear();
  2082. return false;
  2083. }
  2084. bool next()
  2085. {
  2086. if (!fiter.get())
  2087. return false;
  2088. if (piter->next()) {
  2089. fill();
  2090. return true;
  2091. }
  2092. for (;;) {
  2093. if (fiter->next()) {
  2094. piter.setown(fiter->query().getElements("Attr/Protect"));
  2095. if (piter->first()) {
  2096. fill();
  2097. return true;
  2098. }
  2099. }
  2100. else if (!notsuper&&!issuper) {
  2101. issuper = true;
  2102. fiter.clear();
  2103. fiter.setown(conn->queryRoot()->getElements("//SuperFile[Attr/Protect]", iptiter_remote));
  2104. if (fiter->first()) {
  2105. piter.setown(fiter->query().getElements("Attr/Protect"));
  2106. if (piter->first()) {
  2107. fill();
  2108. return true;
  2109. }
  2110. }
  2111. else
  2112. break;
  2113. }
  2114. else
  2115. break;
  2116. }
  2117. clear();
  2118. return false;
  2119. }
  2120. bool isValid()
  2121. {
  2122. return fiter.get()!=NULL;
  2123. }
  2124. const char *queryFilename()
  2125. {
  2126. return fn;
  2127. }
  2128. const char *queryOwner()
  2129. {
  2130. return owner;
  2131. }
  2132. bool isSuper()
  2133. {
  2134. return issuper;
  2135. }
  2136. };
  2137. // --------------------------------------------------------
  2138. class CDistributedFilePart: public CInterface, implements IDistributedFilePart
  2139. {
  2140. unsigned partIndex;
  2141. CDistributedFile &parent;
  2142. Owned<IPropertyTree> attr;
  2143. CriticalSection sect;
  2144. StringAttr overridename; // may or not be relative to directory
  2145. bool dirty; // whether needs updating in tree
  2146. offset_t getSize(bool checkCompressed);
  2147. public:
  2148. virtual void Link(void) const;
  2149. virtual bool Release(void) const;
  2150. void set(IPropertyTree *pt,FileClusterInfoArray &clusters,unsigned maxcluster);
  2151. RemoteFilename &getFilename(RemoteFilename &ret,unsigned copy);
  2152. void renameFile(IFile *file);
  2153. IPropertyTree &queryAttributes();
  2154. bool lockProperties(unsigned timems);
  2155. void unlockProperties(DFTransactionState state);
  2156. bool isHost(unsigned copy);
  2157. offset_t getFileSize(bool allowphysical,bool forcephysical);
  2158. offset_t getDiskSize(bool allowphysical,bool forcephysical);
  2159. bool getModifiedTime(bool allowphysical,bool forcephysical,CDateTime &dt);
  2160. bool getCrc(unsigned &crc);
  2161. unsigned getPhysicalCrc();
  2162. IPartDescriptor *getPartDescriptor();
  2163. unsigned numCopies();
  2164. INode *queryNode(unsigned copy);
  2165. unsigned queryDrive(unsigned copy);
  2166. StringBuffer &getPartName(StringBuffer &name);
  2167. StringBuffer &getPartDirectory(StringBuffer &name,unsigned copy);
  2168. const char *queryOverrideName() { return overridename; }
  2169. void clearOverrideName()
  2170. {
  2171. if (overridename.get()&&overridename.length()) {
  2172. dirty = true;
  2173. overridename.clear();
  2174. }
  2175. }
  2176. unsigned bestCopyNum(const IpAddress &ip,unsigned rel=0);
  2177. unsigned copyClusterNum(unsigned copy,unsigned *replicate=NULL);
  2178. void childLink(void) { CInterface::Link(); }
  2179. bool childRelease(void) { return CInterface::Release(); }
  2180. CDistributedFilePart(CDistributedFile &_parent,unsigned _part,IPartDescriptor *pd);
  2181. unsigned getPartIndex()
  2182. {
  2183. return partIndex;
  2184. }
  2185. INode *getNode(unsigned copy)
  2186. {
  2187. INode *ret = queryNode(copy);
  2188. if (ret)
  2189. return LINK(ret);
  2190. return NULL;
  2191. }
  2192. void setAttr(IPropertyTree &pt)
  2193. {
  2194. attr.setown(createPTreeFromIPT(&pt)); // take a copy
  2195. dirty = false;
  2196. }
  2197. IPropertyTree *queryAttr()
  2198. {
  2199. return attr;
  2200. }
  2201. inline CDistributedFile &queryParent()
  2202. {
  2203. return parent;
  2204. }
  2205. inline bool isDirty()
  2206. {
  2207. return dirty;
  2208. }
  2209. inline bool clearDirty()
  2210. {
  2211. bool ret=dirty;
  2212. dirty = false;
  2213. return ret;
  2214. }
  2215. };
  2216. // --------------------------------------------------------
  2217. class CDistributedFilePartArray: public CIArrayOf<CDistributedFilePart>
  2218. {
  2219. public:
  2220. virtual ~CDistributedFilePartArray() // this shouldn't be needed - points to problem in CIArrayOf?
  2221. {
  2222. kill();
  2223. }
  2224. void kill(bool nodel = false)
  2225. {
  2226. if (nodel)
  2227. CIArrayOf<CDistributedFilePart>::kill(true);
  2228. else {
  2229. while (ordinality()) {
  2230. CDistributedFilePart &part = popGet();
  2231. part.Release();
  2232. }
  2233. }
  2234. }
  2235. };
  2236. // --------------------------------------------------------
  2237. /**
  2238. * Base Iterator class for all iterator types. Implements basic iteration
  2239. * logic and forces all iterators to behave similarly. This will simplify
  2240. * future compatibility with STL containers/algorithms.
  2241. *
  2242. * INTERFACE needs to be extended from IIteratorOf<>
  2243. * ARRAYTY need to be extended from IArrayOf<>
  2244. */
  2245. template <class INTERFACE, class ARRAYTY>
  2246. class CDistributedFileIteratorBase: implements INTERFACE, public CInterface
  2247. {
  2248. protected:
  2249. unsigned index;
  2250. ARRAYTY list;
  2251. virtual bool set() { return isValid(); }
  2252. public:
  2253. IMPLEMENT_IINTERFACE;
  2254. CDistributedFileIteratorBase()
  2255. : index(0)
  2256. {
  2257. }
  2258. virtual ~CDistributedFileIteratorBase()
  2259. {
  2260. list.kill();
  2261. }
  2262. bool first()
  2263. {
  2264. if (list.ordinality() == 0)
  2265. return false;
  2266. index = 0;
  2267. return set();
  2268. }
  2269. bool next()
  2270. {
  2271. index++;
  2272. set();
  2273. return isValid();
  2274. }
  2275. bool isValid()
  2276. {
  2277. return (index < list.ordinality());
  2278. }
  2279. };
  2280. /**
  2281. * FilePart Iterator, used by files to manipulate its parts.
  2282. */
  2283. class CDistributedFilePartIterator: public CDistributedFileIteratorBase<IDistributedFilePartIterator, CDistributedFilePartArray>
  2284. {
  2285. public:
  2286. CDistributedFilePartIterator(CDistributedFilePartArray &parts, IDFPartFilter *filter)
  2287. {
  2288. ForEachItemIn(i,parts) {
  2289. if (!filter||filter->includePart(i))
  2290. list.append(*LINK(&parts.item(i)));
  2291. }
  2292. }
  2293. CDistributedFilePartIterator()
  2294. {
  2295. }
  2296. IDistributedFilePart & query()
  2297. {
  2298. return list.item(index);
  2299. }
  2300. CDistributedFilePartArray &queryParts()
  2301. {
  2302. return list;
  2303. }
  2304. };
  2305. /**
  2306. * File Iterator, used by directory to list file search results.
  2307. */
  2308. class CDistributedFileIterator: public CDistributedFileIteratorBase<IDistributedFileIterator, PointerArray>
  2309. {
  2310. Owned<IDistributedFile> cur;
  2311. IDistributedFileDirectory *parent;
  2312. Linked<IUserDescriptor> udesc;
  2313. Linked<IDistributedFileTransaction> transaction;
  2314. bool isPrivilegedUser = false;
  2315. bool set()
  2316. {
  2317. while (isValid()) {
  2318. cur.setown(parent->lookup(queryName(),udesc, false, false, false, nullptr, isPrivilegedUser));
  2319. if (cur)
  2320. return true;
  2321. index++;
  2322. }
  2323. return false;
  2324. }
  2325. public:
  2326. CDistributedFileIterator(IDistributedFileDirectory *_dir,const char *wildname,bool includesuper,IUserDescriptor *user,bool _isPrivilegedUser, IDistributedFileTransaction *_transaction=NULL)
  2327. : isPrivilegedUser(_isPrivilegedUser), transaction(_transaction)
  2328. {
  2329. setUserDescriptor(udesc,user);
  2330. if (!wildname||!*wildname)
  2331. wildname = "*";
  2332. parent = _dir;
  2333. bool recursive = (stricmp(wildname,"*")==0);
  2334. Owned<IDFAttributesIterator> attriter = parent->getDFAttributesIterator(wildname,user,recursive,includesuper,NULL);
  2335. ForEach(*attriter) {
  2336. IPropertyTree &pt = attriter->query();
  2337. list.append(strdup(pt.queryProp("@name")));
  2338. }
  2339. index = 0;
  2340. if (list.ordinality()>1)
  2341. qsortvec(list.getArray(),list.ordinality(),strcompare);
  2342. }
  2343. const char *queryName()
  2344. {
  2345. return (const char *)list.item(index);
  2346. }
  2347. StringBuffer & getName(StringBuffer &name)
  2348. {
  2349. return name.append(queryName());
  2350. }
  2351. IDistributedFile & query()
  2352. {
  2353. return *cur;
  2354. }
  2355. };
  2356. /**
  2357. * SuperFile Iterator, used by CDistributedFile to list all its super-owners by name.
  2358. */
  2359. class CDistributedSuperFileIterator: public CDistributedFileIteratorBase<IDistributedSuperFileIterator, StringAttrArray>
  2360. {
  2361. CDistributedFileDirectory *parent;
  2362. Linked<IUserDescriptor> udesc;
  2363. Linked<IDistributedFileTransaction> transaction;
  2364. Owned<IDistributedSuperFile> cur;
  2365. Linked<IDistributedFile> owner;
  2366. public:
  2367. CDistributedSuperFileIterator(IDistributedFile *_owner, CDistributedFileDirectory *_parent,IPropertyTree *root,IUserDescriptor *user, IDistributedFileTransaction *_transaction)
  2368. : owner(_owner), transaction(_transaction)
  2369. {
  2370. setUserDescriptor(udesc,user);
  2371. parent = _parent;
  2372. if (root)
  2373. {
  2374. Owned<IPropertyTreeIterator> iter = root->getElements("SuperOwner");
  2375. StringBuffer pname;
  2376. ForEach(*iter)
  2377. {
  2378. iter->query().getProp("@name",pname.clear());
  2379. if (pname.length())
  2380. list.append(* new StringAttrItem(pname.str()));
  2381. }
  2382. }
  2383. }
  2384. IDistributedSuperFile & query()
  2385. {
  2386. // NOTE: This used to include a do/while (!cur.get()&&next()) loop
  2387. // this should never be needed but match previous semantics
  2388. // throwing an exception now, to catch the error early on
  2389. if (transaction.get())
  2390. cur.setown(transaction->lookupSuperFile(queryName()));
  2391. else
  2392. cur.setown(parent->lookupSuperFile(queryName(),udesc,NULL));
  2393. if (!cur.get())
  2394. throw MakeStringException(-1,"superFileIter: invalid super-file on query at %s", queryName());
  2395. return *cur;
  2396. }
  2397. virtual const char *queryName()
  2398. {
  2399. if (isValid())
  2400. return list.item(index).text.get();
  2401. return NULL;
  2402. }
  2403. };
  2404. //-----------------------------------------------------------------------------
  2405. inline void dfCheckRoot(const char *trc,Owned<IPropertyTree> &root,IRemoteConnection *conn)
  2406. {
  2407. if (root.get()!=conn->queryRoot()) {
  2408. DBGLOG("%s - root changed",trc);
  2409. #ifdef _DEBUG
  2410. PrintStackReport();
  2411. #endif
  2412. root.setown(conn->getRoot());
  2413. }
  2414. }
  2415. static bool setFileProtectTree(IPropertyTree &p,const char *owner, bool protect)
  2416. {
  2417. bool ret = false;
  2418. CDateTime dt;
  2419. dt.setNow();
  2420. if (owner&&*owner)
  2421. {
  2422. Owned<IPropertyTree> t = getNamedPropTree(&p, "Protect", "@name", owner, false);
  2423. if (t)
  2424. {
  2425. if (protect)
  2426. {
  2427. StringBuffer str;
  2428. t->setProp("@modified", dt.getString(str).str());
  2429. }
  2430. else
  2431. p.removeTree(t);
  2432. }
  2433. else if (protect)
  2434. {
  2435. t.setown(addNamedPropTree(&p, "Protect", "@name", owner));
  2436. StringBuffer str;
  2437. t->setProp("@modified",dt.getString(str).str());
  2438. }
  2439. ret = true;
  2440. }
  2441. else if (!protect)
  2442. {
  2443. unsigned n=0;
  2444. IPropertyTree *pt;
  2445. while ((pt=p.queryPropTree("Protect[1]"))!=NULL)
  2446. {
  2447. p.removeTree(pt);
  2448. n++;
  2449. }
  2450. if (n)
  2451. ret = true;
  2452. }
  2453. return ret;
  2454. }
  2455. static bool checkProtectAttr(const char *logicalname,IPropertyTree *froot,StringBuffer &reason)
  2456. {
  2457. Owned<IPropertyTreeIterator> wpiter = froot->getElements("Attr/Protect");
  2458. bool prot = false;
  2459. ForEach(*wpiter)
  2460. {
  2461. IPropertyTree &t = wpiter->query();
  2462. const char *wpname = t.queryProp("@name");
  2463. if (!wpname||!*wpname)
  2464. wpname = "<Unknown>";
  2465. if (prot)
  2466. reason.appendf(", %s", wpname);
  2467. else
  2468. {
  2469. reason.appendf("file %s protected by %s", logicalname, wpname);
  2470. prot = true;
  2471. }
  2472. }
  2473. return prot;
  2474. }
  2475. /**
  2476. * A template class which implements the common methods of an IDistributedFile interface.
  2477. * The actual interface (extended from IDistributedFile) is provided as a template argument.
  2478. */
  2479. template <class INTERFACE>
  2480. class CDistributedFileBase : implements INTERFACE, public CInterface
  2481. {
  2482. protected:
  2483. Owned<IPropertyTree> root;
  2484. Owned<IRemoteConnection> conn; // kept connected during lifetime for attributes
  2485. CDfsLogicalFileName logicalName;
  2486. CriticalSection sect;
  2487. CDistributedFileDirectory *parent;
  2488. unsigned proplockcount;
  2489. unsigned transactionnest;
  2490. Linked<IUserDescriptor> udesc;
  2491. unsigned defaultTimeout;
  2492. bool dirty;
  2493. bool external = false;
  2494. Owned<IRemoteConnection> superOwnerLock;
  2495. public:
  2496. IPropertyTree *queryRoot() { return root; }
  2497. CDistributedFileBase<INTERFACE>()
  2498. {
  2499. parent = NULL;
  2500. proplockcount = 0;
  2501. transactionnest = 0;
  2502. defaultTimeout = INFINITE;
  2503. dirty = false;
  2504. }
  2505. ~CDistributedFileBase<INTERFACE>()
  2506. {
  2507. root.clear();
  2508. }
  2509. void setSuperOwnerLock(IRemoteConnection *_superOwnerLock)
  2510. {
  2511. superOwnerLock.setown(_superOwnerLock);
  2512. }
  2513. unsigned setPropLockCount(unsigned _propLockCount)
  2514. {
  2515. unsigned prevPropLockCount = proplockcount;
  2516. proplockcount = _propLockCount;
  2517. return prevPropLockCount;
  2518. }
  2519. bool isCompressed(bool *blocked)
  2520. {
  2521. return ::isCompressed(queryAttributes(),blocked);
  2522. }
  2523. StringBuffer &getLogicalName(StringBuffer &lname)
  2524. {
  2525. lname.append(logicalName.get());
  2526. return lname;
  2527. }
  2528. void setLogicalName(const char *lname)
  2529. {
  2530. logicalName.set(lname);
  2531. }
  2532. const char *queryLogicalName()
  2533. {
  2534. return logicalName.get();
  2535. }
  2536. IPropertyTree &queryAttributes()
  2537. {
  2538. IPropertyTree *t = root->queryPropTree("Attr");
  2539. if (!t)
  2540. t = root->setPropTree("Attr",createPTree("Attr")); // takes ownership
  2541. return *t;
  2542. }
  2543. IPropertyTree *queryHistory() const
  2544. {
  2545. IPropertyTree *attr = root->queryPropTree("Attr");
  2546. if (attr)
  2547. return attr->queryPropTree("History");
  2548. return nullptr;
  2549. }
  2550. void resetHistory()
  2551. {
  2552. DistributedFilePropertyLock lock(this);
  2553. queryAttributes().removeTree(queryHistory());
  2554. }
  2555. protected:
  2556. class CFileChangeWriteLock
  2557. {
  2558. IRemoteConnection *conn;
  2559. unsigned timeoutMs, prevMode;
  2560. public:
  2561. CFileChangeWriteLock(IRemoteConnection *_conn, unsigned _timeoutMs)
  2562. : conn(_conn), timeoutMs(_timeoutMs)
  2563. {
  2564. if (conn)
  2565. {
  2566. prevMode = conn->queryMode();
  2567. unsigned newMode = (prevMode & ~RTM_LOCKBASIC_MASK) | RTM_LOCK_WRITE;
  2568. conn->changeMode(RTM_LOCK_WRITE, timeoutMs);
  2569. }
  2570. else
  2571. prevMode = RTM_NONE;
  2572. }
  2573. ~CFileChangeWriteLock()
  2574. {
  2575. if (conn)
  2576. conn->changeMode(prevMode, timeoutMs);
  2577. }
  2578. void clear() { conn = NULL; }
  2579. };
  2580. IPropertyTree *closeConnection(bool removeFile)
  2581. {
  2582. Owned<IPropertyTree> detachedRoot = createPTreeFromIPT(root);
  2583. root.clear();
  2584. if (conn)
  2585. {
  2586. conn->close(removeFile);
  2587. conn.clear();
  2588. }
  2589. return detachedRoot.getClear();
  2590. }
  2591. IPropertyTree *resetFileAttr(IPropertyTree *prop=NULL)
  2592. {
  2593. if (prop)
  2594. return root->setPropTree("Attr", prop);
  2595. root->removeProp("Attr");
  2596. return NULL;
  2597. }
  2598. void updateFS(const CDfsLogicalFileName &lfn, unsigned timeoutMs)
  2599. {
  2600. // Update the file system
  2601. removeFileEmptyScope(lfn, timeoutMs);
  2602. // MORE: We shouldn't have to update all relationships if we had done a better job making sure
  2603. // that all correct relationships were properly cleaned up
  2604. queryDistributedFileDirectory().removeAllFileRelationships(lfn.get());
  2605. }
  2606. public:
  2607. bool isAnon()
  2608. {
  2609. return !logicalName.isSet();
  2610. }
  2611. /*
  2612. * Change connection to write-mode, allowing multiple writers only on the same instance.
  2613. * Returns true if the lock was lost at least once before succeeding, hinting that some
  2614. * resources might need reload (like sub-files list, etc).
  2615. *
  2616. * WARN: This is not thread-safe
  2617. *
  2618. * @deprecated : use DistributedFilePropertyLock instead, when possible
  2619. */
  2620. bool lockProperties(unsigned timeoutms)
  2621. {
  2622. bool reload = false;
  2623. if (timeoutms==INFINITE)
  2624. timeoutms = defaultTimeout;
  2625. if (proplockcount++==0)
  2626. {
  2627. if (conn)
  2628. {
  2629. conn->rollback(); // changes chouldn't be done outside lock properties
  2630. #ifdef TRACE_LOCKS
  2631. PROGLOG("lockProperties: pre safeChangeModeWrite(%x)",(unsigned)(memsize_t)conn.get());
  2632. #endif
  2633. try
  2634. {
  2635. if (0 == timeoutms)
  2636. conn->changeMode(RTM_LOCK_WRITE, 0, true); // 0 timeout, test and fail immediately if contention
  2637. else
  2638. safeChangeModeWrite(conn,queryLogicalName(),reload,timeoutms);
  2639. }
  2640. catch(IException *)
  2641. {
  2642. proplockcount--;
  2643. dfCheckRoot("lockProperties",root,conn);
  2644. if (reload)
  2645. dirty = true; // safeChangeModeWrite unlocked, and reload will be need if retried
  2646. throw;
  2647. }
  2648. if (dirty) // a previous attempt unlocked and did not reload
  2649. {
  2650. dirty = false;
  2651. if (!reload) // if reload=true, safeChangeModeWrite has just reloaded, so no need to again here
  2652. {
  2653. conn->reload();
  2654. reload = true;
  2655. }
  2656. }
  2657. #ifdef TRACE_LOCKS
  2658. PROGLOG("lockProperties: done safeChangeModeWrite(%x)",(unsigned)(memsize_t)conn.get());
  2659. LogRemoteConn(conn);
  2660. #endif
  2661. dfCheckRoot("lockProperties",root,conn);
  2662. }
  2663. }
  2664. return reload;
  2665. }
  2666. /*
  2667. * Change connection back to read mode on the last unlock. There should never be
  2668. * an uneven number of locks/unlocks, since that will leave the connection with
  2669. * the DFS locked until the instance's destruction.
  2670. *
  2671. * WARN: This is not thread-safe
  2672. *
  2673. * @deprecated : use DistributedFilePropertyLock instead, when possible
  2674. */
  2675. void unlockProperties(DFTransactionState state=TAS_NONE)
  2676. {
  2677. savePartsAttr();
  2678. if (--proplockcount==0) {
  2679. if (conn) {
  2680. // Transactional logic, if any
  2681. switch(state) {
  2682. case TAS_SUCCESS:
  2683. conn->commit();
  2684. break;
  2685. case TAS_FAILURE:
  2686. conn->rollback();
  2687. break;
  2688. case TAS_RETRY:
  2689. conn->changeMode(RTM_NONE,defaultTimeout,true);
  2690. return;
  2691. // TAS_NONE, do nothing
  2692. }
  2693. #ifdef TRACE_LOCKS
  2694. PROGLOG("unlockProperties: pre changeMode(%x)",(unsigned)(memsize_t)conn.get());
  2695. #endif
  2696. conn->changeMode(RTM_LOCK_READ,defaultTimeout,true);
  2697. #ifdef TRACE_LOCKS
  2698. PROGLOG("unlockProperties: post changeMode(%x)",(unsigned)(memsize_t)conn.get());
  2699. LogRemoteConn(conn);
  2700. #endif
  2701. dfCheckRoot("unlockProperties",root,conn);
  2702. }
  2703. }
  2704. }
  2705. bool getModificationTime(CDateTime &dt)
  2706. {
  2707. StringBuffer str;
  2708. if (!root->getProp("@modified",str))
  2709. return false;
  2710. dt.setString(str.str());
  2711. return true;
  2712. }
  2713. void setModificationTime(const CDateTime &dt)
  2714. {
  2715. DistributedFilePropertyLock lock(this);
  2716. if (dt.isNull())
  2717. root->removeProp("@modified");
  2718. else {
  2719. StringBuffer str;
  2720. root->setProp("@modified",dt.getString(str).str());
  2721. }
  2722. root->removeProp("@verified");
  2723. }
  2724. void setModified()
  2725. {
  2726. CDateTime dt;
  2727. dt.setNow();
  2728. setModificationTime(dt);
  2729. }
  2730. virtual StringBuffer &getECL(StringBuffer &buf)
  2731. {
  2732. MemoryBuffer mb;
  2733. if (queryAttributes().getPropBin("ECLbin",mb))
  2734. buf.deserialize(mb);
  2735. else
  2736. queryAttributes().getProp("ECL",buf);
  2737. return buf;
  2738. }
  2739. virtual void setECL(const char *ecl)
  2740. {
  2741. DistributedFilePropertyLock lock(this);
  2742. IPropertyTree &p = queryAttributes();
  2743. #ifdef PACK_ECL
  2744. p.removeProp("ECL");
  2745. if (!ecl||!*ecl)
  2746. p.removeProp("ECLbin");
  2747. else {
  2748. MemoryBuffer mb; // could be better
  2749. StringBuffer buf(ecl);
  2750. buf.serialize(mb);
  2751. root->setPropBin("ECLbin",mb.length(),mb.toByteArray());
  2752. }
  2753. #else
  2754. p.setProp("ECL",ecl);
  2755. #endif
  2756. }
  2757. void setProtect(const char *owner, bool protect, unsigned timems)
  2758. {
  2759. if (logicalName.isForeign()) {
  2760. parent->setFileProtect(logicalName,udesc,owner,protect);
  2761. }
  2762. else {
  2763. bool ret=false;
  2764. if (conn) {
  2765. DistributedFilePropertyLock lock(this);
  2766. IPropertyTree &p = queryAttributes();
  2767. CDateTime dt;
  2768. dt.setNow();
  2769. if (setFileProtectTree(p,owner,protect))
  2770. conn->commit();
  2771. dfCheckRoot("setProtect.1",root,conn);
  2772. }
  2773. else
  2774. IERRLOG("setProtect - cannot protect %s (no connection in file)",owner?owner:"");
  2775. }
  2776. }
  2777. virtual bool isRestrictedAccess() override
  2778. {
  2779. return queryAttributes().getPropBool("restricted");
  2780. }
  2781. virtual void setRestrictedAccess(bool restricted) override
  2782. {
  2783. DistributedFilePropertyLock lock(this);
  2784. queryAttributes().setPropBool("restricted", restricted);
  2785. }
  2786. virtual IDistributedSuperFileIterator *getOwningSuperFiles(IDistributedFileTransaction *_transaction)
  2787. {
  2788. CriticalBlock block(sect);
  2789. return new CDistributedSuperFileIterator(this,parent,root,udesc,_transaction);
  2790. }
  2791. virtual void checkFormatAttr(IDistributedFile *sub, const char* exprefix="")
  2792. {
  2793. // check file has same (or similar) format
  2794. IPropertyTree &superProp = queryAttributes();
  2795. IPropertyTree &subProp = sub->queryAttributes();
  2796. if (!exprefix)
  2797. exprefix = "CheckFormatAttr";
  2798. bool superBlocked = false;
  2799. bool superComp = ::isCompressed(superProp,&superBlocked);
  2800. bool subBlocked = false;
  2801. bool subComp = ::isCompressed(subProp,&subBlocked);
  2802. // FIXME: this may fail if an empty superfile added to a compressed superfile
  2803. if (superComp != subComp)
  2804. throw MakeStringException(-1,"%s: %s's compression setting (%s) is different than %s's (%s)",
  2805. exprefix, sub->queryLogicalName(), (subComp?"compressed":"uncompressed"),
  2806. queryLogicalName(), (superComp?"compressed":"uncompressed"));
  2807. if (superBlocked != subBlocked)
  2808. throw MakeStringException(-1,"%s: %s's blocked setting (%s) is different than %s's (%s)",
  2809. exprefix, sub->queryLogicalName(), (subBlocked?"blocked":"unblocked"),
  2810. queryLogicalName(), (superBlocked?"blocked":"unblocked"));
  2811. #ifdef SUBFILE_COMPATIBILITY_CHECKING
  2812. // MORE - this first check looks completely useless to me
  2813. bool subSoft = subProp.hasProp("_record_layout");
  2814. bool superSoft = superProp.hasProp("_record_layout");
  2815. if (superSoft != subSoft)
  2816. throw MakeStringException(-1,"%s: %s's record layout (%s) is different than %s's (%s)",
  2817. exprefix, sub->queryLogicalName(), (subSoft?"dynamic":"fixed"),
  2818. queryLogicalName(), (superSoft?"dynamic":"fixed"));
  2819. // If they don't, they must have the same size
  2820. if (!superSoft) {
  2821. unsigned superSize = superProp.getPropInt("@recordSize",0);
  2822. unsigned subSize = subProp.getPropInt("@recordSize",0);
  2823. // Variable length files (CSV, etc) have zero record size
  2824. if (superSize && subSize && (superSize != subSize))
  2825. throw MakeStringException(-1,"%s: %s's record size (%d) is different than %s's (%d)",
  2826. exprefix, sub->queryLogicalName(), subSize, queryLogicalName(), superSize);
  2827. }
  2828. StringBuffer superFmt;
  2829. bool superHasFmt = superProp.getProp("@format",superFmt);
  2830. StringBuffer subFmt;
  2831. bool subHasFmt = subProp.getProp("@format",subFmt);
  2832. if (subHasFmt && superHasFmt)
  2833. if (strcmp(normalizeFormat(superFmt).str(),normalizeFormat(subFmt).str()) != 0)
  2834. throw MakeStringException(-1,"%s: %s's format (%s) is different than %s's (%s)",
  2835. exprefix, sub->queryLogicalName(), superFmt.str(),
  2836. queryLogicalName(), subFmt.str());
  2837. #endif
  2838. bool superLocal = superProp.getPropBool("@local",false);
  2839. bool subLocal = subProp.getPropBool("@local",false);
  2840. if (subLocal != superLocal && sub->numParts()>1) // ignore if checking 1 part file, which can be flagged as local or non-local
  2841. {
  2842. throw MakeStringException(-1,"%s: %s's local setting (%s) is different than %s's (%s)",
  2843. exprefix, sub->queryLogicalName(), (subLocal?"local":"global"),
  2844. queryLogicalName(), (superLocal?"local":"global"));
  2845. }
  2846. int superRepO = superProp.getPropInt("@replicateOffset",1);
  2847. int subRepO = subProp.getPropInt("@replicateOffset",1);
  2848. if (subRepO != superRepO)
  2849. throw MakeStringException(-1,"%s: %s's replication offset (%d) is different than %s's (%d)",
  2850. exprefix, sub->queryLogicalName(), subRepO,
  2851. queryLogicalName(), superRepO);
  2852. }
  2853. void getSuperOwners(StringArray &owners)
  2854. {
  2855. if (root)
  2856. {
  2857. StringBuffer owner;
  2858. Owned<IPropertyTreeIterator> iter = root->getElements("SuperOwner");
  2859. ForEach (*iter)
  2860. {
  2861. iter->query().getProp("@name", owner.clear());
  2862. if (owner.length())
  2863. {
  2864. if (NotFound == owners.find(owner))
  2865. owners.append(owner);
  2866. }
  2867. }
  2868. }
  2869. }
  2870. void linkSuperOwner(const char *superfile,bool link)
  2871. {
  2872. if (!superfile||!*superfile)
  2873. return;
  2874. if (conn)
  2875. {
  2876. CFileSuperOwnerLock attrLock;
  2877. if (0 == proplockcount)
  2878. verifyex(attrLock.init(logicalName, conn, defaultTimeout, "CDistributedFile::linkSuperOwner"));
  2879. Owned<IPropertyTree> t = getNamedPropTree(root,"SuperOwner","@name",superfile,false);
  2880. if (t && !link)
  2881. root->removeTree(t);
  2882. else if (!t && link)
  2883. t.setown(addNamedPropTree(root,"SuperOwner","@name",superfile));
  2884. }
  2885. else
  2886. IERRLOG("linkSuperOwner - cannot link to %s (no connection in file)",superfile);
  2887. }
  2888. void setAccessed()
  2889. {
  2890. CDateTime dt;
  2891. dt.setNow();
  2892. setAccessedTime(dt);
  2893. }
  2894. virtual StringBuffer &getColumnMapping(StringBuffer &mapping)
  2895. {
  2896. queryAttributes().getProp("@columnMapping",mapping);
  2897. return mapping;
  2898. }
  2899. virtual void setColumnMapping(const char *mapping)
  2900. {
  2901. DistributedFilePropertyLock lock(this);
  2902. if (!mapping||!*mapping)
  2903. queryAttributes().removeProp("@columnMapping");
  2904. else
  2905. queryAttributes().setProp("@columnMapping",mapping);
  2906. }
  2907. unsigned setDefaultTimeout(unsigned timems)
  2908. {
  2909. unsigned ret = defaultTimeout;
  2910. defaultTimeout = timems;
  2911. return ret;
  2912. }
  2913. // MORE - simplify this, after removing CLightWeightSuperFileConn
  2914. bool canModify(StringBuffer &reason)
  2915. {
  2916. return !checkProtectAttr(logicalName.get(),root,reason);
  2917. }
  2918. bool checkOwned(StringBuffer &error)
  2919. {
  2920. Owned<IPropertyTreeIterator> iter = root->getElements("SuperOwner");
  2921. if (iter->first())
  2922. {
  2923. error.append("Cannot remove file ").append(logicalName.get()).append(" as owned by SuperFile(s): ");
  2924. for (;;)
  2925. {
  2926. error.append(iter->query().queryProp("@name"));
  2927. if (!iter->next())
  2928. break;
  2929. error.append(", ");
  2930. }
  2931. return true;
  2932. }
  2933. return false;
  2934. }
  2935. bool canRemove(StringBuffer &reason,bool ignoresub=false)
  2936. {
  2937. CriticalBlock block(sect);
  2938. if (!canModify(reason))
  2939. return false;
  2940. const char *logicalname = logicalName.get();
  2941. if (!logicalname||!*logicalname) {
  2942. reason.appendf("empty filename");
  2943. return false;
  2944. }
  2945. if (logicalName.isQuery())
  2946. {
  2947. reason.appendf("%s is query",logicalname);
  2948. return false;
  2949. }
  2950. if (logicalName.isForeign())
  2951. {
  2952. reason.appendf("%s is foreign",logicalname);
  2953. return false;
  2954. }
  2955. if (logicalName.isMulti())
  2956. {
  2957. reason.appendf("%s is multi",logicalname);
  2958. return false;
  2959. }
  2960. if (!ignoresub)
  2961. {
  2962. if (checkOwned(reason))
  2963. return false;
  2964. }
  2965. return true;
  2966. }
  2967. virtual const char *queryDefaultDir() = 0;
  2968. virtual unsigned numParts() = 0;
  2969. virtual IDistributedFilePart &queryPart(unsigned idx) = 0;
  2970. virtual IDistributedFilePart* getPart(unsigned idx) = 0;
  2971. virtual void savePartsAttr(bool force=false) = 0;
  2972. virtual IDistributedFilePartIterator *getIterator(IDFPartFilter *filter=NULL) = 0;
  2973. virtual IDistributedSuperFile *querySuperFile() = 0;
  2974. virtual ClusterPartDiskMapSpec &queryPartDiskMapping(unsigned clusternum)=0;
  2975. virtual void updatePartDiskMapping(const char *clustername,const ClusterPartDiskMapSpec &spec)=0;
  2976. virtual void enqueueReplicate()=0;
  2977. virtual bool getAccessedTime(CDateTime &dt) = 0; // get date and time last accessed (returns false if not set)
  2978. virtual void setAccessedTime(const CDateTime &dt) = 0; // set date and time last accessed
  2979. virtual bool isExternal() const { return external; }
  2980. };
  2981. class CDistributedFile: public CDistributedFileBase<IDistributedFile>
  2982. {
  2983. protected:
  2984. CDistributedFilePartArray parts; // use queryParts to access
  2985. CriticalSection sect;
  2986. StringAttr directory;
  2987. StringAttr partmask;
  2988. FileClusterInfoArray clusters;
  2989. void savePartsAttr(bool force) override
  2990. {
  2991. CriticalBlock block (sect);
  2992. IPropertyTree *pt;
  2993. if (parts.ordinality()==1) { // single part saved as part
  2994. if (parts.item(0).clearDirty()||force) {
  2995. CDistributedFilePart &part = parts.item(0);
  2996. while ((pt=root->queryPropTree("Part[1]"))!=NULL)
  2997. root->removeTree(pt);
  2998. pt = createPTreeFromIPT(part.queryAttr());
  2999. pt->setPropInt("@num",1);
  3000. const char *grp = root->queryProp("@group");
  3001. if (!grp||!*grp) {
  3002. StringBuffer eps;
  3003. pt->addProp("@node",part.queryNode(0)->endpoint().getUrlStr(eps).str()); // legacy
  3004. }
  3005. const char *override = part.queryOverrideName();
  3006. if (override&&*override)
  3007. pt->setProp("@name",override);
  3008. else {
  3009. pt->removeProp("@name");
  3010. const char *mask=queryPartMask();
  3011. if (mask&&*mask) {
  3012. StringBuffer tmp;
  3013. expandMask(tmp,mask,0,1);
  3014. pt->setProp("@name",tmp.str());
  3015. }
  3016. }
  3017. root->setPropTree("Part",pt);
  3018. }
  3019. }
  3020. else {
  3021. unsigned n = parts.ordinality();
  3022. unsigned i1;
  3023. for (i1=0;i1<n;i1++) {
  3024. if (parts.item(i1).clearDirty()||force) {
  3025. MemoryBuffer mb;
  3026. CriticalBlock block (sect);
  3027. ForEachItemIn(i2,parts)
  3028. serializePartAttr(mb,parts.item(i2).queryAttr());
  3029. root->setPropBin("Parts",mb.length(),mb.toByteArray());
  3030. while ((pt=root->queryPropTree("Part[1]"))!=NULL)
  3031. root->removeTree(pt);
  3032. break;
  3033. }
  3034. }
  3035. while (i1<n)
  3036. parts.item(i1++).clearDirty();
  3037. }
  3038. }
  3039. void detach(unsigned timeoutMs, bool removePhysicals, ICodeContext *ctx)
  3040. {
  3041. // Removes either a cluster in case of multi cluster file or the whole File entry from DFS
  3042. assert(proplockcount == 0 && "CDistributedFile detach: Some properties are still locked");
  3043. assertex(!isAnon()); // not attached!
  3044. if (removePhysicals)
  3045. {
  3046. // Avoid removing physically when there is no physical representation
  3047. if (logicalName.isMulti())
  3048. removePhysicals = false;
  3049. }
  3050. StringBuffer clusterName;
  3051. Owned<IFileDescriptor> fileDescCopy;
  3052. #ifdef EXTRA_LOGGING
  3053. PROGLOG("CDistributedFile::detach(%s)",logicalName.get());
  3054. LOGPTREE("CDistributedFile::detach root.1",root);
  3055. #endif
  3056. {
  3057. CriticalBlock block(sect); // JCSMORE - not convinced this is still necessary
  3058. CFileChangeWriteLock writeLock(conn, timeoutMs);
  3059. logicalName.getCluster(clusterName);
  3060. // copy file descriptor before altered, used by physical file removal routines
  3061. if (removePhysicals)
  3062. {
  3063. MemoryBuffer mb;
  3064. Owned<IFileDescriptor> fdesc = getFileDescriptor(clusterName);
  3065. fdesc->serialize(mb);
  3066. fileDescCopy.setown(deserializeFileDescriptor(mb));
  3067. }
  3068. bool removeFile=true;
  3069. if (clusterName.length())
  3070. {
  3071. // Remove just cluster specified, unless it's the last, in which case detach below will remove File entry.
  3072. if (clusters.ordinality()>1)
  3073. {
  3074. if (removeCluster(clusterName.str()))
  3075. removeFile=false;
  3076. else
  3077. ThrowStringException(-1, "Cluster %s not present in file %s", clusterName.str(), logicalName.get());
  3078. }
  3079. }
  3080. if (removeFile)
  3081. {
  3082. // check can remove, e.g. cannot if this is a subfile of a super
  3083. StringBuffer reason;
  3084. if (!canRemove(reason))
  3085. throw MakeStringException(-1,"detach: %s", reason.str());
  3086. }
  3087. // detach this IDistributeFile
  3088. /* JCSMORE - In 'removeFile=true' case, this should really delete before release exclusive lock.
  3089. */
  3090. writeLock.clear();
  3091. root.setown(closeConnection(removeFile));
  3092. // NB: The file is now unlocked
  3093. if (removeFile && !logicalName.isExternal())
  3094. updateFS(logicalName, parent->queryDefaultTimeout());
  3095. logicalName.clear();
  3096. }
  3097. // NB: beyond unlock
  3098. if (removePhysicals)
  3099. {
  3100. CriticalBlock block(physicalChange);
  3101. Owned<IMultiException> exceptions = MakeMultiException("CDistributedFile::detach");
  3102. removePhysicalPartFiles(fileDescCopy, exceptions);
  3103. if (exceptions->ordinality())
  3104. throw exceptions.getClear();
  3105. }
  3106. }
  3107. bool removePhysicalPartFiles(IFileDescriptor *fileDesc, IMultiException *mexcept, unsigned numParallelDeletes=0)
  3108. {
  3109. if (logicalName.isExternal())
  3110. {
  3111. if (logicalName.isQuery())
  3112. return false;
  3113. }
  3114. if (logicalName.isForeign())
  3115. throw MakeStringException(-1,"cannot remove a foreign file (%s)",logicalName.get());
  3116. return parent->removePhysicalPartFiles(logicalName.get(), fileDesc, mexcept, numParallelDeletes);
  3117. }
  3118. bool calculateSkew(unsigned &maxSkew, unsigned &minSkew, unsigned &maxSkewPart, unsigned &minSkewPart)
  3119. {
  3120. unsigned np = numParts();
  3121. if (0 == np)
  3122. return false;
  3123. offset_t maxPartSz = 0, minPartSz = (offset_t)-1, totalPartSz = 0;
  3124. maxSkewPart = 0;
  3125. minSkewPart = 0;
  3126. for (unsigned p=0; p<np; p++)
  3127. {
  3128. IDistributedFilePart &part = queryPart(p);
  3129. offset_t size = part.getFileSize(true, false);
  3130. if (size > maxPartSz)
  3131. {
  3132. maxPartSz = size;
  3133. maxSkewPart = p;
  3134. }
  3135. if (size < minPartSz)
  3136. {
  3137. minPartSz = size;
  3138. minSkewPart = p;
  3139. }
  3140. totalPartSz += size;
  3141. }
  3142. offset_t avgPartSz = totalPartSz / np;
  3143. if (0 == avgPartSz)
  3144. minSkew = maxSkew = 0;
  3145. else
  3146. {
  3147. maxSkew = (unsigned)(10000.0 * (((double)maxPartSz-avgPartSz)/avgPartSz));
  3148. minSkew = (unsigned)(10000.0 * ((avgPartSz-(double)minPartSz)/avgPartSz));
  3149. }
  3150. return true;
  3151. }
  3152. void calculateSkew() // called when a logical file is attached
  3153. {
  3154. IPropertyTree &attrs = queryAttributes();
  3155. unsigned maxSkew, minSkew, maxSkewPart, minSkewPart;
  3156. if (!calculateSkew(maxSkew, minSkew, maxSkewPart, minSkewPart))
  3157. {
  3158. attrs.removeProp("@maxSkew");
  3159. attrs.removeProp("@minSkew");
  3160. attrs.removeProp("@maxSkewPart");
  3161. attrs.removeProp("@minSkewPart");
  3162. return;
  3163. }
  3164. attrs.setPropInt("@maxSkew", maxSkew);
  3165. attrs.setPropInt("@maxSkewPart", maxSkewPart);
  3166. attrs.setPropInt("@minSkew", minSkew);
  3167. attrs.setPropInt("@minSkewPart", minSkewPart);
  3168. }
  3169. protected: friend class CDistributedFilePart;
  3170. CDistributedFilePartArray &queryParts()
  3171. {
  3172. return parts;
  3173. }
  3174. public:
  3175. IMPLEMENT_IINTERFACE_O;
  3176. // NB: this form is used for pre-existing file, by dolookup
  3177. CDistributedFile(CDistributedFileDirectory *_parent, IRemoteConnection *_conn,const CDfsLogicalFileName &lname,IUserDescriptor *user) // takes ownership of conn
  3178. {
  3179. setUserDescriptor(udesc,user);
  3180. logicalName.set(lname);
  3181. parent = _parent;
  3182. conn.setown(_conn);
  3183. CClustersLockedSection sect(logicalName, false);
  3184. root.setown(conn->getRoot());
  3185. root->queryBranch("."); // load branch
  3186. #ifdef EXTRA_LOGGING
  3187. LOGPTREE("CDistributedFile.a root",root);
  3188. #endif
  3189. Owned<IFileDescriptor> fdesc = deserializeFileDescriptorTree(root,&queryNamedGroupStore(),0);
  3190. #ifdef EXTRA_LOGGING
  3191. LOGFDESC("CDistributedFile.a fdesc",fdesc);
  3192. #endif
  3193. setFileAttrs(fdesc,false);
  3194. setClusters(fdesc);
  3195. setPreferredClusters(_parent->defprefclusters);
  3196. setParts(fdesc,false);
  3197. //shrinkFileTree(root); // enable when safe!
  3198. }
  3199. // NB: this form is used for a new/unattached file
  3200. CDistributedFile(CDistributedFileDirectory *_parent, IFileDescriptor *fdesc, IUserDescriptor *user, bool _external)
  3201. {
  3202. #ifdef EXTRA_LOGGING
  3203. LOGFDESC("CDistributedFile.b fdesc",fdesc);
  3204. #endif
  3205. parent = _parent;
  3206. root.setown(createPTree(queryDfsXmlBranchName(DXB_File)));
  3207. root->setPropTree("ClusterLock", createPTree());
  3208. // fdesc->serializeTree(*root,IFDSF_EXCLUDE_NODES);
  3209. setFileAttrs(fdesc,true);
  3210. setClusters(fdesc);
  3211. setPreferredClusters(_parent->defprefclusters);
  3212. saveClusters();
  3213. setParts(fdesc,true);
  3214. udesc.set(user);
  3215. external = _external;
  3216. #ifdef EXTRA_LOGGING
  3217. LOGPTREE("CDistributedFile.b root.1",root);
  3218. #endif
  3219. offset_t totalsize=0;
  3220. unsigned checkSum = ~0;
  3221. bool useableCheckSum = true;
  3222. MemoryBuffer pmb;
  3223. unsigned n = fdesc->numParts();
  3224. for (unsigned i=0;i<n;i++) {
  3225. IPropertyTree *partattr = &fdesc->queryPart(i)->queryProperties();
  3226. if (!partattr)
  3227. {
  3228. totalsize = (unsigned)-1;
  3229. useableCheckSum = false;
  3230. }
  3231. else
  3232. {
  3233. offset_t psz;
  3234. if (totalsize!=(offset_t)-1) {
  3235. psz = (offset_t)partattr->getPropInt64("@size", -1);
  3236. if (psz==(offset_t)-1)
  3237. totalsize = psz;
  3238. else
  3239. totalsize += psz;
  3240. }
  3241. if (useableCheckSum) {
  3242. unsigned crc;
  3243. if (fdesc->queryPart(i)->getCrc(crc))
  3244. checkSum ^= crc;
  3245. else
  3246. useableCheckSum = false;
  3247. }
  3248. }
  3249. }
  3250. shrinkFileTree(root);
  3251. if (totalsize!=(offset_t)-1)
  3252. queryAttributes().setPropInt64("@size", totalsize);
  3253. if (useableCheckSum)
  3254. queryAttributes().setPropInt64("@checkSum", checkSum);
  3255. setModified();
  3256. #ifdef EXTRA_LOGGING
  3257. LOGPTREE("CDistributedFile.b root.2",root);
  3258. #endif
  3259. }
  3260. void killParts()
  3261. {
  3262. ForEachItemIn(i,parts)
  3263. parts.item(i).childRelease();
  3264. parts.kill(true);
  3265. }
  3266. ~CDistributedFile()
  3267. {
  3268. assert(proplockcount == 0 && "CDistributedFile destructor: Some properties are still locked");
  3269. if (conn)
  3270. conn->rollback(); // changes should always be done in locked properties
  3271. killParts();
  3272. clusters.kill();
  3273. }
  3274. IFileDescriptor *getFileDescriptor(const char *_clusterName) override
  3275. {
  3276. CriticalBlock block (sect);
  3277. Owned<IFileDescriptor> fdesc = deserializeFileDescriptorTree(root,&queryNamedGroupStore(),0);
  3278. fdesc->setTraceName(logicalName.get());
  3279. StringArray cnames;
  3280. if (_clusterName&&*_clusterName)
  3281. {
  3282. StringAttr clusterName = _clusterName;
  3283. clusterName.toLowerCase();
  3284. cnames.append(clusterName);
  3285. }
  3286. else
  3287. getClusterNames(cnames);
  3288. fdesc->setClusterOrder(cnames,_clusterName&&*_clusterName);
  3289. return fdesc.getClear();
  3290. }
  3291. void setFileAttrs(IFileDescriptor *fdesc,bool save)
  3292. {
  3293. directory.set(fdesc->queryDefaultDir());
  3294. partmask.set(fdesc->queryPartMask());
  3295. const char *lfn = logicalName.get();
  3296. if (lfn&&*lfn) {
  3297. if (partmask.isEmpty()) {
  3298. StringBuffer mask;
  3299. getPartMask(mask,lfn,0);
  3300. partmask.set(mask);
  3301. }
  3302. }
  3303. if (!save)
  3304. return;
  3305. if (directory.isEmpty())
  3306. root->removeProp("@directory");
  3307. else
  3308. root->setProp("@directory",directory);
  3309. if (partmask.isEmpty())
  3310. root->removeProp("@partmask");
  3311. else
  3312. root->setProp("@partmask",partmask);
  3313. IPropertyTree *t = &fdesc->queryProperties();
  3314. if (isEmptyPTree(t))
  3315. resetFileAttr();
  3316. else
  3317. resetFileAttr(createPTreeFromIPT(t));
  3318. }
  3319. void setClusters(IFileDescriptor *fdesc)
  3320. {
  3321. clusters.clear();
  3322. unsigned nc = fdesc->numClusters();
  3323. if (nc) {
  3324. for (unsigned i=0;i<nc;i++) {
  3325. StringBuffer cname;
  3326. StringBuffer clabel;
  3327. IClusterInfo &cluster = *createClusterInfo(
  3328. fdesc->getClusterGroupName(i,cname,NULL).str(),
  3329. fdesc->queryClusterGroup(i),
  3330. fdesc->queryPartDiskMapping(i),
  3331. &queryNamedGroupStore()
  3332. );
  3333. #ifdef EXTRA_LOGGING
  3334. PROGLOG("setClusters(%d,%s)",i,cname.str());
  3335. #endif
  3336. if (!cluster.queryGroup(&queryNamedGroupStore())) {
  3337. IERRLOG("IDistributedFileDescriptor cannot set cluster for %s",logicalName.get());
  3338. }
  3339. clusters.append(cluster);
  3340. }
  3341. }
  3342. else
  3343. IERRLOG("No cluster specified for %s",logicalName.get());
  3344. }
  3345. virtual unsigned numClusters() override
  3346. {
  3347. return clusters.ordinality();
  3348. }
  3349. virtual unsigned findCluster(const char *clustername) override
  3350. {
  3351. return clusters.find(clustername);
  3352. }
  3353. virtual unsigned getClusterNames(StringArray &clusternames) override
  3354. {
  3355. return clusters.getNames(clusternames);
  3356. }
  3357. void reloadClusters()
  3358. {
  3359. // called from CClustersLockedSection
  3360. if (!CDistributedFileBase<IDistributedFile>::conn)
  3361. return;
  3362. assertex(CDistributedFileBase<IDistributedFile>::proplockcount==0); // cannot reload clusters if properties locked
  3363. CDistributedFileBase<IDistributedFile>::conn->reload(); // should only be cluster changes but a bit dangerous
  3364. IPropertyTree *t = CDistributedFileBase<IDistributedFile>::conn->queryRoot(); // NB not CDistributedFileBase<IDistributedFile>::queryRoot();
  3365. if (!t)
  3366. return;
  3367. clusters.clear();
  3368. getClusterInfo(*t,&queryNamedGroupStore(),0,clusters);
  3369. }
  3370. void saveClusters()
  3371. {
  3372. // called from CClustersLockedSection
  3373. IPropertyTree *t;
  3374. if (CDistributedFileBase<IDistributedFile>::conn)
  3375. t = CDistributedFileBase<IDistributedFile>::conn->queryRoot();
  3376. else
  3377. t = CDistributedFileBase<IDistributedFile>::queryRoot(); //cache
  3378. if (!t)
  3379. return;
  3380. IPropertyTree *pt;
  3381. IPropertyTree *tc = CDistributedFileBase<IDistributedFile>::queryRoot(); //cache
  3382. IPropertyTree *t0 = t;
  3383. StringBuffer grplist;
  3384. // the following is complicated by fact there is a cache of the file branch
  3385. for (;;) {
  3386. while ((pt=t->queryPropTree("Cluster[1]"))!=NULL)
  3387. t->removeTree(pt);
  3388. ForEachItemIn(i,clusters) {
  3389. IPropertyTree *pt = createPTree("Cluster");
  3390. clusters.item(i).serializeTree(pt,IFDSF_EXCLUDE_GROUPS);
  3391. if (!isEmptyPTree(pt)) {
  3392. t->addPropTree("Cluster",pt);
  3393. if (t==t0) {
  3394. StringBuffer clabel;
  3395. clusters.item(i).getClusterLabel(clabel);
  3396. if (clabel.length()) {
  3397. if (grplist.length())
  3398. grplist.append(',');
  3399. grplist.append(clabel);
  3400. }
  3401. }
  3402. }
  3403. else
  3404. DBGLOG("CFileClusterOwner::saveClusters - empty cluster");
  3405. }
  3406. if (grplist.length())
  3407. t->setProp("@group",grplist.str());
  3408. else
  3409. t->removeProp("@group");
  3410. t->setPropInt("@numclusters",clusters.ordinality());
  3411. t->setProp("@directory", directory);
  3412. if (t==tc)
  3413. break;
  3414. t = tc; // now fix cache
  3415. }
  3416. if (CDistributedFileBase<IDistributedFile>::conn)
  3417. CDistributedFileBase<IDistributedFile>::conn->commit(); // should only be cluster changes but a bit dangerous
  3418. }
  3419. virtual void addCluster(const char *clustername,const ClusterPartDiskMapSpec &mspec) override
  3420. {
  3421. if (!clustername&&!*clustername)
  3422. return;
  3423. CClustersLockedSection cls(CDistributedFileBase<IDistributedFile>::logicalName, true);
  3424. reloadClusters();
  3425. if (findCluster(clustername)!=NotFound) {
  3426. IDFS_Exception *e = new CDFS_Exception(DFSERR_ClusterAlreadyExists,clustername);
  3427. throw e;
  3428. }
  3429. Owned<IClusterInfo> cluster = createClusterInfo(clustername,NULL,mspec,&queryNamedGroupStore());
  3430. if (cluster->queryGroup(&queryNamedGroupStore())) {
  3431. clusters.append(*cluster.getClear());
  3432. }
  3433. else {
  3434. IDFS_Exception *e = new CDFS_Exception(DFSERR_ClusterNotFound,clustername);
  3435. throw e;
  3436. }
  3437. saveClusters();
  3438. }
  3439. virtual bool removeCluster(const char *clustername) override
  3440. {
  3441. CClustersLockedSection cls(CDistributedFileBase<IDistributedFile>::logicalName, true);
  3442. reloadClusters();
  3443. unsigned i = findCluster(clustername);
  3444. if (i!=NotFound) {
  3445. if (clusters.ordinality()==1)
  3446. throw MakeStringException(-1,"CFileClusterOwner::removeCluster cannot remove sole cluster %s",clustername);
  3447. // If the cluster is the 'default' one we need to update the directory too
  3448. StringBuffer oldBaseDir;
  3449. char pathSepChar = getPathSepChar(directory.get());
  3450. DFD_OS os = SepCharBaseOs(pathSepChar);
  3451. clusters.item(i).getBaseDir(oldBaseDir, os);
  3452. unsigned oldLen = oldBaseDir.length();
  3453. clusters.remove(i);
  3454. if (oldLen && strncmp(directory, oldBaseDir, oldLen)==0 && (directory[oldLen]==pathSepChar || directory[oldLen]=='\0'))
  3455. {
  3456. StringBuffer newBaseDir;
  3457. clusters.item(0).getBaseDir(newBaseDir, os);
  3458. newBaseDir.append(directory.get() + oldBaseDir.length());
  3459. directory.set(newBaseDir);
  3460. }
  3461. saveClusters();
  3462. return true;
  3463. }
  3464. return false;
  3465. }
  3466. virtual void setPreferredClusters(const char *clusterlist) override
  3467. {
  3468. clusters.setPreferred(clusterlist,CDistributedFileBase<IDistributedFile>::logicalName);
  3469. }
  3470. INode *queryNode(unsigned idx,unsigned copy)
  3471. {
  3472. unsigned rep;
  3473. unsigned cluster = copyClusterNum(idx,copy,&rep);
  3474. if (cluster==NotFound)
  3475. return queryNullNode();
  3476. unsigned nn;
  3477. unsigned dn;
  3478. IGroup *grp = clusters.queryGroup(cluster);
  3479. if (!grp)
  3480. return queryNullNode();
  3481. if (!clusters.item(cluster).queryPartDiskMapping().calcPartLocation (idx, numParts(),rep, grp->ordinality(), nn, dn))
  3482. return queryNullNode();
  3483. return &grp->queryNode(nn);
  3484. }
  3485. unsigned queryDrive(unsigned idx,unsigned copy,const char *dir)
  3486. {
  3487. // this is odd routine
  3488. unsigned dn = dir?getPathDrive(dir):0;
  3489. if (dn)
  3490. return dn;
  3491. unsigned rep;
  3492. unsigned cluster = copyClusterNum(idx,copy,&rep);
  3493. if (cluster==NotFound)
  3494. return 0;
  3495. unsigned nn;
  3496. IGroup *grp = clusters.queryGroup(cluster);
  3497. if (!grp)
  3498. return 0;
  3499. if (!clusters.item(cluster).queryPartDiskMapping().calcPartLocation (idx, numParts(),rep, grp->ordinality(), nn, dn))
  3500. return 0;
  3501. return dn;
  3502. }
  3503. virtual StringBuffer &getClusterName(unsigned clusternum,StringBuffer &name) override
  3504. {
  3505. return clusters.getName(clusternum,name);
  3506. }
  3507. unsigned copyClusterNum(unsigned part, unsigned copy,unsigned *replicate)
  3508. {
  3509. return clusters.copyNum(part,copy, numParts(),replicate);
  3510. }
  3511. virtual ClusterPartDiskMapSpec &queryPartDiskMapping(unsigned clusternum) override
  3512. {
  3513. assertex(clusternum<clusters.ordinality());
  3514. return clusters.queryPartDiskMapping(clusternum);
  3515. }
  3516. virtual void updatePartDiskMapping(const char *clustername,const ClusterPartDiskMapSpec &spec) override
  3517. {
  3518. CClustersLockedSection cls(CDistributedFileBase<IDistributedFile>::logicalName, true);
  3519. reloadClusters();
  3520. unsigned i = findCluster(clustername);
  3521. if (i!=NotFound) {
  3522. clusters.updatePartDiskMapping(i,spec);
  3523. saveClusters();
  3524. }
  3525. }
  3526. virtual IGroup *queryClusterGroup(unsigned clusternum) override
  3527. {
  3528. return clusters.queryGroup(clusternum);
  3529. }
  3530. virtual StringBuffer &getClusterGroupName(unsigned clusternum, StringBuffer &name) override
  3531. {
  3532. return clusters.item(clusternum).getGroupName(name, &queryNamedGroupStore());
  3533. }
  3534. virtual unsigned numCopies(unsigned partno) override
  3535. {
  3536. return clusters.numCopies(partno,numParts());
  3537. }
  3538. virtual void setSingleClusterOnly() override
  3539. {
  3540. clusters.setSingleClusterOnly();
  3541. }
  3542. unsigned numClusterCopies(unsigned cnum,unsigned partnum)
  3543. {
  3544. IClusterInfo &cluster = clusters.item(cnum);
  3545. IGroup *grp = cluster.queryGroup();
  3546. return cluster.queryPartDiskMapping().numCopies(partnum,grp?grp->ordinality():1,numParts());
  3547. }
  3548. void adjustClusterDir(unsigned partno,unsigned copy, StringBuffer &path)
  3549. {
  3550. // this corrects the directory for a copy
  3551. // assumes default dir matches one of clusters
  3552. unsigned rep=0;
  3553. unsigned cluster = NotFound;
  3554. const char *ds = path.str();
  3555. unsigned nc = clusters.ordinality();
  3556. if (nc>1) {
  3557. StringAttr matched;
  3558. StringAttr toadd;
  3559. unsigned i=0;
  3560. bool c = 0;
  3561. int cp = (int)copy;
  3562. while (i<nc) {
  3563. StringBuffer dcmp;
  3564. clusters.item(i).getBaseDir(dcmp,SepCharBaseOs(getPathSepChar(ds))); // no trailing sep
  3565. const char *t = dcmp.str();
  3566. const char *d = ds;
  3567. while (*d&&(*t==*d)) {
  3568. d++;
  3569. t++;
  3570. }
  3571. if (!*t&&(!*d||isPathSepChar(*d))&&(dcmp.length()>matched.length()))
  3572. matched.set(dcmp);
  3573. unsigned mc = numClusterCopies(i,partno);
  3574. if ((cp>=0)&&(cp<(int)mc)) {
  3575. toadd.set(dcmp);
  3576. rep = (unsigned)cp;
  3577. cluster = i;
  3578. }
  3579. cp -= mc;
  3580. i++;
  3581. }
  3582. if (!matched.isEmpty()&&!toadd.isEmpty()&&(strcmp(matched,toadd)!=0)) {
  3583. StringBuffer tmp(toadd);
  3584. tmp.append(ds+matched.length());
  3585. path.swapWith(tmp);
  3586. }
  3587. }
  3588. else {
  3589. rep = copy;
  3590. cluster = 0;
  3591. }
  3592. // now set replicate
  3593. if (cluster!=NotFound) {
  3594. unsigned n;
  3595. unsigned d;
  3596. ClusterPartDiskMapSpec& mspec = clusters.item(cluster).queryPartDiskMapping();
  3597. mspec.calcPartLocation(partno,numParts(),rep,clusters.queryGroup(cluster)?clusters.queryGroup(cluster)->ordinality():numParts(),n,d);
  3598. if ((d==1) && (mspec.flags&CPDMSF_overloadedConfig) && mspec.defaultReplicateDir.length())
  3599. path.set(mspec.defaultReplicateDir.get());
  3600. else
  3601. setReplicateFilename(path,d);
  3602. }
  3603. }
  3604. void setParts(IFileDescriptor *fdesc,bool save)
  3605. {
  3606. unsigned np = fdesc->numParts();
  3607. for (unsigned i = 0;i<np;i++) {
  3608. CDistributedFilePart &part = *new CDistributedFilePart(*this,i,fdesc->queryPart(i));
  3609. parts.append(part);
  3610. }
  3611. if (save) {
  3612. root->setPropInt("@numparts",parts.ordinality());
  3613. savePartsAttr(true);
  3614. }
  3615. }
  3616. virtual unsigned numParts() override
  3617. {
  3618. return parts.ordinality();
  3619. }
  3620. virtual IDistributedFilePart &queryPart(unsigned idx) override
  3621. {
  3622. if (idx<parts.ordinality())
  3623. return queryParts().item(idx);
  3624. return *(IDistributedFilePart *)NULL;
  3625. }
  3626. virtual IDistributedFilePart* getPart(unsigned idx) override
  3627. {
  3628. if (idx>=parts.ordinality())
  3629. return NULL;
  3630. IDistributedFilePart *ret = &queryParts().item(idx);
  3631. return LINK(ret);
  3632. }
  3633. virtual IDistributedFilePartIterator *getIterator(IDFPartFilter *filter=NULL) override
  3634. {
  3635. return new CDistributedFilePartIterator(queryParts(),filter);
  3636. }
  3637. virtual void rename(const char *_logicalname,IUserDescriptor *user) override
  3638. {
  3639. StringBuffer prevname;
  3640. Owned<IFileRelationshipIterator> reliter;
  3641. // set prevname
  3642. if (!isAnon()) {
  3643. getLogicalName(prevname);
  3644. try {
  3645. IFileRelationshipIterator *iter = parent->lookupAllFileRelationships(prevname.str());
  3646. reliter.setown(iter);
  3647. }
  3648. catch (IException *e) {
  3649. EXCLOG(e,"CDistributedFile::rename");
  3650. e->Release();
  3651. }
  3652. detachLogical();
  3653. }
  3654. attach(_logicalname,user);
  3655. if (prevname.length()) {
  3656. DistributedFilePropertyLock lock(this);
  3657. IPropertyTree &pt = queryAttributes();
  3658. StringBuffer list;
  3659. if (pt.getProp("@renamedFrom",list)&&list.length())
  3660. list.append(',');
  3661. pt.setProp("@renamedFrom",list.append(prevname).str());
  3662. }
  3663. if (reliter.get()) {
  3664. // add back any relationships with new name
  3665. parent->renameFileRelationships(prevname.str(),_logicalname,reliter,user);
  3666. }
  3667. }
  3668. virtual const char *queryDefaultDir() override
  3669. {
  3670. CriticalBlock block (sect);
  3671. return directory.get();
  3672. }
  3673. virtual const char *queryPartMask() override
  3674. {
  3675. CriticalBlock block (sect);
  3676. if (partmask.isEmpty()) {
  3677. assertex(root);
  3678. partmask.set(root->queryProp("@partmask"));
  3679. }
  3680. return partmask.get();
  3681. }
  3682. bool isAnon()
  3683. {
  3684. return (!logicalName.isSet());
  3685. }
  3686. virtual void attach(const char *_logicalname,IUserDescriptor *user) override
  3687. {
  3688. CriticalBlock block (sect);
  3689. assertex(isAnon()); // already attached!
  3690. logicalName.set(_logicalname);
  3691. if (!checkLogicalName(logicalName,user,true,true,true,"attach"))
  3692. return; // query
  3693. #ifdef EXTRA_LOGGING
  3694. PROGLOG("CDistributedFile::attach(%s)",_logicalname);
  3695. LOGPTREE("CDistributedFile::attach root.1",root);
  3696. #endif
  3697. calculateSkew();
  3698. parent->addEntry(logicalName,root.getClear(),false,false);
  3699. killParts();
  3700. clusters.kill();
  3701. CFileLock fcl;
  3702. verifyex(fcl.init(logicalName, DXB_File, RTM_LOCK_READ, defaultTimeout, "CDistributedFile::attach"));
  3703. conn.setown(fcl.detach());
  3704. root.setown(conn->getRoot());
  3705. root->queryBranch("."); // load branch
  3706. Owned<IFileDescriptor> fdesc = deserializeFileDescriptorTree(root,&queryNamedGroupStore(),0);
  3707. setFileAttrs(fdesc,false);
  3708. setClusters(fdesc);
  3709. setParts(fdesc,false);
  3710. setUserDescriptor(udesc, user);
  3711. #ifdef EXTRA_LOGGING
  3712. LOGFDESC("CDistributedFile::attach fdesc",fdesc);
  3713. LOGPTREE("CDistributedFile::attach root.2",root);
  3714. #endif
  3715. }
  3716. /*
  3717. * Internal method (not in IDistributedFile interface) that is used
  3718. * when renaming files (so don't delete the physical representation).
  3719. *
  3720. * This is also used during CPPUINT tests, so we need to make them public
  3721. * only when tests are enabled (ie. non-production mode).
  3722. *
  3723. * See removeLogical()
  3724. */
  3725. public:
  3726. void detachLogical(unsigned timeoutms=INFINITE)
  3727. {
  3728. detach(timeoutms, false, NULL);
  3729. }
  3730. public:
  3731. virtual void detach(unsigned timeoutMs=INFINITE, ICodeContext *ctx=NULL) override
  3732. {
  3733. detach(timeoutMs, true, ctx);
  3734. }
  3735. virtual bool existsPhysicalPartFiles(unsigned short port) override
  3736. {
  3737. unsigned width = numParts();
  3738. CriticalSection errcrit;
  3739. class casyncfor: public CAsyncFor
  3740. {
  3741. IDistributedFile *file;
  3742. unsigned short port;
  3743. CriticalSection &errcrit;
  3744. unsigned width;
  3745. public:
  3746. bool ok;
  3747. casyncfor(IDistributedFile *_file,unsigned _width,unsigned short _port,CriticalSection &_errcrit)
  3748. : errcrit(_errcrit)
  3749. {
  3750. file = _file;
  3751. port = _port;
  3752. ok = true;
  3753. width = _width;
  3754. ok = true;
  3755. }
  3756. void Do(unsigned i)
  3757. {
  3758. {
  3759. CriticalBlock block(errcrit);
  3760. if (!ok)
  3761. return;
  3762. }
  3763. Owned<IDistributedFilePart> part = file->getPart(i);
  3764. unsigned nc = part->numCopies();
  3765. for (unsigned copy = 0; copy < nc; copy++)
  3766. {
  3767. RemoteFilename rfn;
  3768. part->getFilename(rfn,copy);
  3769. if (port)
  3770. rfn.setPort(port); // if daliservix
  3771. Owned<IFile> partfile = createIFile(rfn);
  3772. try
  3773. {
  3774. if (partfile->exists())
  3775. return;
  3776. }
  3777. catch (IException *e)
  3778. {
  3779. CriticalBlock block(errcrit);
  3780. StringBuffer s("Failed to find file part ");
  3781. s.append(partfile->queryFilename()).append(" on ");
  3782. rfn.queryEndpoint().getUrlStr(s);
  3783. EXCLOG(e, s.str());
  3784. e->Release();
  3785. }
  3786. }
  3787. CriticalBlock block(errcrit);
  3788. ok = false;
  3789. }
  3790. } afor(this,width,port,errcrit);
  3791. afor.For(width,10,false,true);
  3792. return afor.ok;
  3793. }
  3794. // This method takes an existing physical directory path for a logical file
  3795. // and a constructed path to the same logical file created in this context
  3796. // and deduces the original base path e.g. /var/lib/HPCCSystems/hpcc-data/thor
  3797. // This is necessary, because there is no not enough context to directly fetch the
  3798. // original base path to construct new paths for the rename
  3799. bool getBase(const char *oldPath, const char *thisPath, StringBuffer &baseDir)
  3800. {
  3801. const char *oldEnd = oldPath+strlen(oldPath)-1;
  3802. const char *thisEnd = thisPath+strlen(thisPath)-1;
  3803. if (isPathSepChar(*oldEnd))
  3804. oldEnd--;
  3805. if (isPathSepChar(*thisEnd))
  3806. thisEnd--;
  3807. const char *oldP = oldEnd, *thisP = thisEnd;
  3808. for (;;) {
  3809. if (oldP==oldPath || thisP==thisPath)
  3810. break;
  3811. if (*oldP != *thisP) {
  3812. // unless last was separator, continue until find one
  3813. if (isPathSepChar(*(oldP+1)))
  3814. oldP++;
  3815. else {
  3816. while (oldP != oldPath && (!isPathSepChar(*oldP)))
  3817. oldP--;
  3818. }
  3819. baseDir.append(oldP-oldPath, oldPath);
  3820. return true;
  3821. }
  3822. --oldP;
  3823. --thisP;
  3824. }
  3825. return false;
  3826. }
  3827. virtual bool renamePhysicalPartFiles(const char *newname,
  3828. const char *cluster,
  3829. IMultiException *mexcept,
  3830. const char *newbasedir) override
  3831. {
  3832. // cluster TBD
  3833. unsigned width = numParts();
  3834. StringBuffer newdir;
  3835. StringBuffer newmask;
  3836. const char *diroverride = NULL;
  3837. char psc = getPathSepChar(directory.get());
  3838. DFD_OS os = SepCharBaseOs(psc);
  3839. StringBuffer basedir;
  3840. const char *myBase;
  3841. if (newbasedir)
  3842. {
  3843. diroverride = newbasedir;
  3844. myBase = newbasedir;
  3845. }
  3846. else
  3847. myBase = queryBaseDirectory(grp_unknown, 0, os);
  3848. StringBuffer baseDir, newPath;
  3849. makePhysicalPartName(logicalName.get(), 0, 0, newPath, false, os, diroverride);
  3850. if (!getBase(directory, newPath, baseDir))
  3851. baseDir.append(myBase); // getBase returns false, if directory==newPath, so have common base
  3852. getPartMask(newmask,newname,width);
  3853. if (newmask.length()==0)
  3854. return false;
  3855. makePhysicalPartName(newname, 0, 0, newPath.clear(), false, os, diroverride);
  3856. if (newPath.length()==0)
  3857. return false;
  3858. if (isPathSepChar(newPath.charAt(newPath.length()-1)))
  3859. newPath.setLength(newPath.length()-1);
  3860. newPath.remove(0, strlen(myBase));
  3861. newdir.append(baseDir).append(newPath);
  3862. StringBuffer fullname;
  3863. CIArrayOf<CIStringArray> newNames;
  3864. unsigned i;
  3865. for (i=0;i<width;i++) {
  3866. newNames.append(*new CIStringArray);
  3867. CDistributedFilePart &part = parts.item(i);
  3868. for (unsigned copy=0; copy<part.numCopies(); copy++) {
  3869. makePhysicalPartName(newname, i+1, width, newPath.clear(), false, os, myBase);
  3870. newPath.remove(0, strlen(myBase));
  3871. StringBuffer copyDir(baseDir);
  3872. adjustClusterDir(i, copy, copyDir);
  3873. fullname.clear().append(copyDir).append(newPath);
  3874. newNames.item(i).append(fullname);
  3875. }
  3876. }
  3877. // NB: the code below, specifically deals with 1 primary + 1 replicate
  3878. // it will need refactoring if it's to deal with multiple clusters/copies
  3879. // first check file doestn't exist for any new part
  3880. CriticalSection crit;
  3881. class casyncforbase: public CAsyncFor
  3882. {
  3883. protected:
  3884. CriticalSection &crit;
  3885. CIArrayOf<CIStringArray> &newNames;
  3886. IDistributedFile *file;
  3887. unsigned width;
  3888. IMultiException *mexcept;
  3889. bool *ignoreprim;
  3890. bool *ignorerep;
  3891. public:
  3892. bool ok;
  3893. bool * doneprim;
  3894. bool * donerep;
  3895. IException *except;
  3896. casyncforbase(IDistributedFile *_file,CIArrayOf<CIStringArray> &_newNames,unsigned _width,IMultiException *_mexcept,CriticalSection &_crit,bool *_ignoreprim,bool *_ignorerep)
  3897. : newNames(_newNames),crit(_crit)
  3898. {
  3899. width = _width;
  3900. file = _file;
  3901. ok = true;
  3902. mexcept = _mexcept;
  3903. doneprim = (bool *)calloc(sizeof(bool),width);
  3904. donerep = (bool *)calloc(sizeof(bool),width);
  3905. except = NULL;
  3906. ignoreprim = _ignoreprim;
  3907. ignorerep = _ignorerep;
  3908. }
  3909. ~casyncforbase()
  3910. {
  3911. free(doneprim);
  3912. free(donerep);
  3913. }
  3914. virtual bool doPart(IDistributedFilePart *,bool,RemoteFilename &,RemoteFilename &, bool &)
  3915. #ifdef _WIN32
  3916. {
  3917. assertex(!"doPart"); // stupid microsoft error
  3918. return false;
  3919. }
  3920. #else
  3921. = 0;
  3922. #endif
  3923. void Do(unsigned idx)
  3924. {
  3925. {
  3926. CriticalBlock block(crit);
  3927. if (!ok)
  3928. return;
  3929. }
  3930. Owned<IDistributedFilePart> part = file->getPart(idx);
  3931. unsigned copies = part->numCopies();
  3932. for (int copy = copies-1; copy>=0; copy--)
  3933. {
  3934. if ((copy==0)&&ignoreprim&&ignoreprim[idx])
  3935. continue;
  3936. if ((copy!=0)&&ignorerep&&ignorerep[idx])
  3937. continue;
  3938. bool pok=false;
  3939. IException *ex = NULL;
  3940. RemoteFilename oldrfn;
  3941. part->getFilename(oldrfn,(unsigned)copy);
  3942. const char *newfn = newNames.item(idx).item(copy);
  3943. if (!newfn||!*newfn)
  3944. continue;
  3945. RemoteFilename newrfn;
  3946. newrfn.setPath(part->queryNode(copy)->endpoint(),newfn);
  3947. try {
  3948. pok = doPart(part,copy!=0,oldrfn,newrfn,(copy==0)?doneprim[idx]:donerep[idx]);
  3949. }
  3950. catch (IException *e) {
  3951. EXCLOG(e, NULL);
  3952. ex = e;
  3953. }
  3954. CriticalBlock block(crit);
  3955. if (!pok||ex) {
  3956. ok = false;
  3957. if (ex) {
  3958. StringBuffer s("renamePhysicalPartFiles ");
  3959. s.append(file->queryLogicalName()).append(" part ").append(newfn);
  3960. EXCLOG(ex, s.str());
  3961. if (mexcept)
  3962. mexcept->append(*ex);
  3963. else {
  3964. if (except)
  3965. ex->Release();
  3966. else
  3967. except = ex;
  3968. }
  3969. }
  3970. }
  3971. }
  3972. }
  3973. };
  3974. class casyncfor1: public casyncforbase
  3975. {
  3976. public:
  3977. casyncfor1(IDistributedFile *_file,CIArrayOf<CIStringArray> &_newNames,unsigned _width,IMultiException *_mexcept,CriticalSection &_crit,bool *_ignoreprim,bool *_ignorerep)
  3978. : casyncforbase(_file,_newNames,_width,_mexcept,_crit,_ignoreprim,_ignorerep)
  3979. {
  3980. }
  3981. bool doPart(IDistributedFilePart *part,bool isrep,RemoteFilename &oldrfn,RemoteFilename &newrfn, bool &done)
  3982. {
  3983. done = false;
  3984. Owned<IFile> src = createIFile(oldrfn);
  3985. if (src->exists())
  3986. done = true;
  3987. else {
  3988. StringBuffer s;
  3989. oldrfn.getRemotePath(s);
  3990. DBGLOG("renamePhysicalPartFiles: %s doesn't exist",s.str());
  3991. return true;
  3992. }
  3993. Owned<IFile> dest = createIFile(newrfn);
  3994. StringBuffer newname;
  3995. newrfn.getRemotePath(newname);
  3996. if (dest->exists()) {
  3997. IDFS_Exception *e = new CDFS_Exception(DFSERR_PhysicalPartAlreadyExists,newname.str());
  3998. throw e;
  3999. }
  4000. // check destination directory exists
  4001. StringBuffer newdir;
  4002. splitDirTail(newname.str(),newdir);
  4003. Owned<IFile> destdir = createIFile(newdir.str());
  4004. destdir->createDirectory();
  4005. return true;
  4006. }
  4007. } afor1 (this,newNames,width,mexcept,crit,NULL,NULL);
  4008. afor1.For(width,10,false,true);
  4009. if (afor1.except)
  4010. throw afor1.except; // no recovery needed
  4011. if (!afor1.ok)
  4012. return false; // no recovery needed
  4013. MemoryAttr ignorebuf;
  4014. bool *ignoreprim = (bool *)ignorebuf.allocate(width*sizeof(bool)*2);
  4015. bool *ignorerep = ignoreprim+width;
  4016. for (i=0;i<width;i++) {
  4017. if (afor1.donerep[i]) {
  4018. ignorerep[i] = false;
  4019. ignoreprim[i] = !afor1.doneprim[i];
  4020. }
  4021. else if (afor1.doneprim[i]) {
  4022. ignorerep[i] = true;
  4023. ignoreprim[i] = false;
  4024. }
  4025. else {
  4026. StringBuffer s(queryLogicalName());
  4027. s.append(" Part ").append(i+1);
  4028. IDFS_Exception *e = new CDFS_Exception(DFSERR_PhysicalPartDoesntExist,s.str());
  4029. throw e;
  4030. }
  4031. }
  4032. // now do the rename
  4033. class casyncfor2: public casyncforbase
  4034. {
  4035. public:
  4036. casyncfor2(IDistributedFile *_file,CIArrayOf<CIStringArray> &_newNames,unsigned _width,IMultiException *_mexcept,CriticalSection &_crit,bool *_ignoreprim,bool *_ignorerep)
  4037. : casyncforbase(_file,_newNames,_width,_mexcept,_crit,_ignoreprim,_ignorerep)
  4038. {
  4039. }
  4040. bool doPart(IDistributedFilePart *part,bool isrep,RemoteFilename &oldrfn,RemoteFilename &newrfn, bool &done)
  4041. {
  4042. done = false;
  4043. StringBuffer oldfn;
  4044. oldrfn.getRemotePath(oldfn);
  4045. StringBuffer newfn;
  4046. newrfn.getRemotePath(newfn);
  4047. Owned<IFile> f = createIFile(oldrfn);
  4048. if (!isrep||f->exists()) { // ignore non-existant replicates
  4049. f->move(newfn.str());
  4050. PROGLOG("Succeeded rename %s to %s",oldfn.str(),newfn.str());
  4051. }
  4052. done = true;
  4053. return true;;
  4054. }
  4055. } afor2 (this,newNames,width,mexcept,crit,ignoreprim,ignorerep);
  4056. afor2.For(width,10,false,true);
  4057. if (afor2.ok) {
  4058. // now rename directory and partmask
  4059. DistributedFilePropertyLock lock(this);
  4060. root->setProp("@directory",newdir.str());
  4061. root->setProp("@partmask",newmask.str());
  4062. partmask.set(newmask.str());
  4063. directory.set(newdir.str());
  4064. StringBuffer mask;
  4065. for (unsigned i=0;i<width;i++) {
  4066. mask.appendf("Part[%d]/@name",i+1);
  4067. parts.item(i).clearOverrideName();
  4068. }
  4069. savePartsAttr(false);
  4070. }
  4071. else {
  4072. // attempt recovery
  4073. // do this synchronously to maximize chance of success (I don't expect many to have been done)
  4074. for (i=0;i<width;i++) {
  4075. Owned<IDistributedFilePart> part = getPart(i);
  4076. unsigned copies = part->numCopies();
  4077. for (int copy = copies-1; copy>=0; copy--) {
  4078. bool done = (copy==0)?afor2.doneprim[i]:afor2.donerep[i];
  4079. if (done) {
  4080. RemoteFilename oldrfn;
  4081. part->getFilename(oldrfn,(unsigned)copy);
  4082. const char *newfn = newNames.item(i).item(copy);
  4083. if (!newfn||!*newfn)
  4084. continue;
  4085. RemoteFilename newrfn;
  4086. newrfn.setPath(part->queryNode(copy)->endpoint(),newfn);
  4087. for (unsigned t=1;t<3;t++) { // 3 goes
  4088. try {
  4089. StringBuffer oldfn;
  4090. oldrfn.getRemotePath(oldfn);
  4091. StringBuffer newfn;
  4092. newrfn.getRemotePath(newfn);
  4093. Owned<IFile> f = createIFile(newrfn);
  4094. f->move(oldfn.str());
  4095. PROGLOG("Succeeded rename %s back to %s",newfn.str(),oldfn.str());
  4096. break;
  4097. }
  4098. catch (IException *e) {
  4099. if (!afor2.except)
  4100. afor2.except = e;
  4101. else
  4102. e->Release();
  4103. }
  4104. }
  4105. }
  4106. }
  4107. }
  4108. }
  4109. if (afor2.except)
  4110. throw afor2.except;
  4111. return afor2.ok;
  4112. }
  4113. IPropertyTree *queryRoot() { return root; }
  4114. virtual __int64 getFileSize(bool allowphysical,bool forcephysical) override
  4115. {
  4116. __int64 ret = (__int64)(forcephysical?-1:queryAttributes().getPropInt64("@size",-1));
  4117. if (ret==-1)
  4118. {
  4119. ret = 0;
  4120. unsigned n = numParts();
  4121. for (unsigned i=0;i<n;i++)
  4122. {
  4123. Owned<IDistributedFilePart> part = getPart(i);
  4124. __int64 ps = part->getFileSize(allowphysical,forcephysical);
  4125. if (ps == -1)
  4126. {
  4127. ret = ps;
  4128. break;
  4129. }
  4130. ret += ps;
  4131. }
  4132. }
  4133. return ret;
  4134. }
  4135. virtual __int64 getDiskSize(bool allowphysical,bool forcephysical) override
  4136. {
  4137. if (!isCompressed(NULL))
  4138. return getFileSize(allowphysical, forcephysical);
  4139. __int64 ret = (__int64)(forcephysical?-1:queryAttributes().getPropInt64("@compressedSize",-1));
  4140. if (ret==-1)
  4141. {
  4142. ret = 0;
  4143. unsigned n = numParts();
  4144. for (unsigned i=0;i<n;i++)
  4145. {
  4146. Owned<IDistributedFilePart> part = getPart(i);
  4147. __int64 ps = part->getDiskSize(allowphysical,forcephysical);
  4148. if (ps == -1)
  4149. {
  4150. ret = ps;
  4151. break;
  4152. }
  4153. ret += ps;
  4154. }
  4155. }
  4156. return ret;
  4157. }
  4158. virtual bool getFileCheckSum(unsigned &checkSum) override
  4159. {
  4160. if (queryAttributes().hasProp("@checkSum"))
  4161. checkSum = (unsigned)queryAttributes().getPropInt64("@checkSum");
  4162. else
  4163. {
  4164. checkSum = ~0;
  4165. unsigned n = numParts();
  4166. for (unsigned i=0;i<n;i++) {
  4167. Owned<IDistributedFilePart> part = getPart(i);
  4168. unsigned crc;
  4169. if (!part->getCrc(crc))
  4170. return false;
  4171. checkSum ^= crc;
  4172. }
  4173. }
  4174. return true;
  4175. }
  4176. virtual bool getFormatCrc(unsigned &crc) override
  4177. {
  4178. if (queryAttributes().hasProp("@formatCrc")) {
  4179. // NB pre record_layout CRCs are not valid
  4180. crc = (unsigned)queryAttributes().getPropInt("@formatCrc");
  4181. return true;
  4182. }
  4183. return false;
  4184. }
  4185. virtual bool getRecordLayout(MemoryBuffer &layout, const char *attrname) override
  4186. {
  4187. return queryAttributes().getPropBin(attrname, layout);
  4188. }
  4189. virtual bool getRecordSize(size32_t &rsz) override
  4190. {
  4191. if (queryAttributes().hasProp("@recordSize")) {
  4192. rsz = (size32_t)queryAttributes().getPropInt("@recordSize");
  4193. return true;
  4194. }
  4195. return false;
  4196. }
  4197. virtual unsigned getPositionPart(offset_t pos, offset_t &base) override
  4198. {
  4199. unsigned n = numParts();
  4200. base = 0;
  4201. for (unsigned i=0;i<n;i++) {
  4202. Owned<IDistributedFilePart> part = getPart(i);
  4203. offset_t ps = part->getFileSize(true,false);
  4204. if (ps==(offset_t)-1)
  4205. break;
  4206. if (ps>pos)
  4207. return i;
  4208. pos -= ps;
  4209. base += ps;
  4210. }
  4211. return NotFound;
  4212. }
  4213. IDistributedSuperFile *querySuperFile() override
  4214. {
  4215. return NULL; // i.e. this isn't super file
  4216. }
  4217. virtual bool checkClusterCompatible(IFileDescriptor &fdesc, StringBuffer &err) override
  4218. {
  4219. unsigned n = numParts();
  4220. if (fdesc.numParts()!=n) {
  4221. err.appendf("Different cluster width (%d/%d)",n,fdesc.numParts());
  4222. return false;
  4223. }
  4224. if (fdesc.numClusters()!=1) {
  4225. err.append("Cannot merge more than one cluster");
  4226. return false;
  4227. }
  4228. StringBuffer cname;
  4229. fdesc.getClusterLabel(0,cname);
  4230. if (cname.length()&&(findCluster(cname.str())!=NotFound)) {
  4231. err.append("File already contains cluster");
  4232. err.append(cname.str());
  4233. return false;
  4234. }
  4235. StringBuffer pname;
  4236. StringBuffer fdtail;
  4237. for (unsigned pn=0;pn<n;pn++) {
  4238. IDistributedFilePart &part = queryPart(pn);
  4239. part.getPartName(pname.clear());
  4240. fdesc.queryPart(pn)->getTail(fdtail.clear());
  4241. if (strcmp(pname.str(),fdtail.str())!=0) {
  4242. err.appendf("Part name mismatch (%s,%s)",pname.str(),fdtail.str());
  4243. return false;
  4244. }
  4245. RemoteFilename fdrfn;
  4246. fdesc.getFilename(pn,0,fdrfn);
  4247. unsigned nc = numCopies(pn);
  4248. for (unsigned c = 0;c<nc;c++) {
  4249. RemoteFilename rfn;
  4250. part.getFilename(rfn,c);
  4251. if (rfn.equals(fdrfn)) {
  4252. err.appendf("Parts overlap %s and %s",pname.str(),fdtail.str());
  4253. return false;
  4254. }
  4255. }
  4256. }
  4257. return true;
  4258. }
  4259. virtual void enqueueReplicate() override
  4260. {
  4261. MemoryBuffer mb;
  4262. mb.append((byte)DRQ_REPLICATE).append(queryLogicalName());
  4263. udesc->serialize(mb);
  4264. CDateTime filedt;
  4265. getModificationTime(filedt);
  4266. filedt.serialize(mb);
  4267. Owned<INamedQueueConnection> qconn = createNamedQueueConnection(0);
  4268. Owned<IQueueChannel> qchannel = qconn->open(DFS_REPLICATE_QUEUE);
  4269. qchannel->put(mb);
  4270. }
  4271. virtual bool getAccessedTime(CDateTime &dt) override
  4272. {
  4273. StringBuffer str;
  4274. if (!root->getProp("@accessed",str))
  4275. return false;
  4276. dt.setString(str.str());
  4277. return true;
  4278. }
  4279. virtual void setAccessedTime(const CDateTime &dt) override
  4280. {
  4281. if (logicalName.isForeign())
  4282. parent->setFileAccessed(logicalName,udesc,dt);
  4283. else
  4284. {
  4285. CFileAttrLock attrLock;
  4286. if (conn)
  4287. {
  4288. if (!attrLock.init(logicalName, DXB_File, RTM_LOCK_WRITE, conn, defaultTimeout, "CDistributedFile::setAccessedTime"))
  4289. {
  4290. // In unlikely event File/Attr doesn't exist, must ensure created, commited and root connection is reloaded.
  4291. verifyex(attrLock.init(logicalName, DXB_File, RTM_LOCK_WRITE|RTM_CREATE_QUERY, conn, defaultTimeout, "CDistributedFile::setAccessedTime"));
  4292. attrLock.commit();
  4293. conn->commit();
  4294. conn->reload();
  4295. root.setown(conn->getRoot());
  4296. }
  4297. }
  4298. if (dt.isNull())
  4299. queryAttributes().removeProp("@accessed");
  4300. else
  4301. {
  4302. StringBuffer str;
  4303. queryAttributes().setProp("@accessed",dt.getString(str).str());
  4304. }
  4305. }
  4306. }
  4307. virtual void setAccessed() override
  4308. {
  4309. CDateTime dt;
  4310. dt.setNow();
  4311. setAccessedTime(dt);
  4312. }
  4313. virtual void validate() override
  4314. {
  4315. if (!existsPhysicalPartFiles(0))
  4316. {
  4317. const char * logicalName = queryLogicalName();
  4318. throw MakeStringException(-1, "Some physical parts do not exists, for logical file : %s",(isEmptyString(logicalName) ? "[unattached]" : logicalName));
  4319. }
  4320. }
  4321. virtual bool getSkewInfo(unsigned &maxSkew, unsigned &minSkew, unsigned &maxSkewPart, unsigned &minSkewPart, bool calculateIfMissing) override
  4322. {
  4323. const IPropertyTree *attrs = root->queryPropTree("Attr");
  4324. if (attrs && attrs->hasProp("@maxSkew"))
  4325. {
  4326. maxSkew = attrs->getPropInt("@maxSkew");
  4327. minSkew = attrs->getPropInt("@minSkew");
  4328. maxSkewPart = attrs->getPropInt("@maxSkewPart");
  4329. minSkewPart = attrs->getPropInt("@minSkewPart");
  4330. return true;
  4331. }
  4332. else if (calculateIfMissing)
  4333. return calculateSkew(maxSkew, minSkew, maxSkewPart, minSkewPart);
  4334. else
  4335. return false;
  4336. }
  4337. };
  4338. static unsigned findSubFileOrd(const char *name)
  4339. {
  4340. if (*name=='#') {
  4341. const char *n = name+1;
  4342. if (*n) {
  4343. do { n++; } while (*n&&isdigit(*n));
  4344. if (!*n)
  4345. return atoi(name+1)-1;
  4346. }
  4347. }
  4348. return NotFound;
  4349. }
  4350. class CDistributedSuperFile: public CDistributedFileBase<IDistributedSuperFile>
  4351. {
  4352. void checkNotForeign()
  4353. {
  4354. if (!conn)
  4355. throw MakeStringException(-1,"Operation not allowed on foreign file");
  4356. }
  4357. CDistributedFilePartArray partscache;
  4358. FileClusterInfoArray clusterscache;
  4359. bool containsRestrictedSubfile = false;
  4360. /**
  4361. * Adds a sub-file to a super-file within a transaction.
  4362. */
  4363. class cAddSubFileAction: public CDFAction
  4364. {
  4365. StringAttr parentlname;
  4366. Owned<IDistributedSuperFile> parent;
  4367. Owned<IDistributedFile> sub;
  4368. StringAttr subfile;
  4369. bool before;
  4370. StringAttr other;
  4371. public:
  4372. cAddSubFileAction(const char *_parentlname,const char *_subfile,bool _before,const char *_other)
  4373. : parentlname(_parentlname), subfile(_subfile), before(_before), other(_other)
  4374. {
  4375. }
  4376. virtual bool prepare()
  4377. {
  4378. parent.setown(transaction->lookupSuperFile(parentlname));
  4379. if (!parent)
  4380. throw MakeStringException(-1,"addSubFile: SuperFile %s cannot be found",parentlname.get());
  4381. if (!subfile.isEmpty())
  4382. {
  4383. try
  4384. {
  4385. sub.setown(transaction->lookupFile(subfile,SDS_SUB_LOCK_TIMEOUT));
  4386. if (!sub)
  4387. throw MakeStringException(-1,"cAddSubFileAction: sub file %s not found", subfile.str());
  4388. // Must validate before locking for update below, to check sub is not already in parent (and therefore locked already)
  4389. transaction->validateAddSubFile(parent, sub, subfile);
  4390. }
  4391. catch (ISDSException *e)
  4392. {
  4393. if (e->errorCode()!=SDSExcpt_LockTimeout)
  4394. throw;
  4395. e->Release();
  4396. return false;
  4397. }
  4398. if (!sub.get())
  4399. throw MakeStringException(-1,"addSubFile: File %s cannot be found to add",subfile.get());
  4400. }
  4401. // Try to lock all files
  4402. addFileLock(parent);
  4403. if (lock())
  4404. {
  4405. transaction->noteAddSubFile(parent, parentlname, sub);
  4406. return true;
  4407. }
  4408. unlock();
  4409. parent.clear();
  4410. sub.clear();
  4411. return false;
  4412. }
  4413. virtual void run()
  4414. {
  4415. if (!sub)
  4416. throw MakeStringException(-1,"addSubFile(2): File %s cannot be found to add",subfile.get());
  4417. CDistributedSuperFile *sf = QUERYINTERFACE(parent.get(),CDistributedSuperFile);
  4418. if (sf)
  4419. sf->doAddSubFile(LINK(sub),before,other,transaction);
  4420. }
  4421. virtual void commit()
  4422. {
  4423. CDistributedSuperFile *sf = QUERYINTERFACE(parent.get(),CDistributedSuperFile);
  4424. if (sf)
  4425. sf->updateParentFileAttrs(transaction);
  4426. CDFAction::commit();
  4427. }
  4428. virtual void retry()
  4429. {
  4430. parent.clear();
  4431. sub.clear();
  4432. CDFAction::retry();
  4433. }
  4434. };
  4435. /**
  4436. * Removes a sub-file of a super-file within a transaction.
  4437. */
  4438. class cRemoveSubFileAction: public CDFAction
  4439. {
  4440. StringAttr parentlname;
  4441. Owned<IDistributedSuperFile> parent;
  4442. Owned<IDistributedFile> sub;
  4443. StringAttr subfile;
  4444. bool remsub;
  4445. public:
  4446. cRemoveSubFileAction(const char *_parentlname,const char *_subfile,bool _remsub=false)
  4447. : parentlname(_parentlname), subfile(_subfile), remsub(_remsub)
  4448. {
  4449. }
  4450. virtual bool prepare()
  4451. {
  4452. parent.setown(transaction->lookupSuperFile(parentlname));
  4453. if (!parent)
  4454. throw MakeStringException(-1,"removeSubFile: SuperFile %s cannot be found",parentlname.get());
  4455. if (!subfile.isEmpty())
  4456. {
  4457. try
  4458. {
  4459. sub.setown(transaction->lookupFile(subfile,SDS_SUB_LOCK_TIMEOUT));
  4460. }
  4461. catch (ISDSException *e)
  4462. {
  4463. if (e->errorCode()!=SDSExcpt_LockTimeout)
  4464. throw;
  4465. e->Release();
  4466. return false;
  4467. }
  4468. if (!transaction->isSubFile(parent, subfile, true))
  4469. {
  4470. IWARNLOG("removeSubFile: File %s is not a subfile of %s", subfile.get(), parent->queryLogicalName());
  4471. parent.clear();
  4472. sub.clear();
  4473. return true; // NB: sub was not a member of super, issue warning and continue without locking
  4474. }
  4475. }
  4476. // Try to lock all files
  4477. addFileLock(parent);
  4478. if (sub && remsub) // NB: I only need to lock (for exclusivity, if going to delete
  4479. addFileLock(sub);
  4480. if (lock())
  4481. {
  4482. if (sub)
  4483. transaction->noteRemoveSubFile(parent, sub);
  4484. else
  4485. transaction->clearSubFiles(parent);
  4486. return true;
  4487. }
  4488. unlock();
  4489. parent.clear();
  4490. sub.clear();
  4491. return false;
  4492. }
  4493. virtual void run()
  4494. {
  4495. CDistributedSuperFile *sf = QUERYINTERFACE(parent.get(),CDistributedSuperFile);
  4496. if (sf) {
  4497. // Delay the deletion of the subs until commit
  4498. if (remsub) {
  4499. if (subfile) {
  4500. CDfsLogicalFileName lname;
  4501. lname.set(subfile.get());
  4502. transaction->addDelayedDelete(lname, INFINITE);
  4503. } else { // Remove all subfiles
  4504. Owned<IDistributedFileIterator> iter = parent->getSubFileIterator(false);
  4505. ForEach (*iter) {
  4506. CDfsLogicalFileName lname;
  4507. IDistributedFile *f = &iter->query();
  4508. lname.set(f->queryLogicalName());
  4509. transaction->addDelayedDelete(lname, INFINITE);
  4510. }
  4511. }
  4512. }
  4513. // Now we clean the subs
  4514. if (subfile.get())
  4515. sf->doRemoveSubFile(subfile.get(), transaction);
  4516. else
  4517. sf->doRemoveSubFiles(transaction);
  4518. }
  4519. }
  4520. virtual void retry()
  4521. {
  4522. parent.clear();
  4523. sub.clear();
  4524. CDFAction::retry();
  4525. }
  4526. };
  4527. /**
  4528. * Removes all subfiles exclusively owned by named superfile within a transaction.
  4529. */
  4530. class cRemoveOwnedSubFilesAction: public CDFAction
  4531. {
  4532. StringAttr parentlname;
  4533. Owned<IDistributedSuperFile> parent;
  4534. bool remsub;
  4535. public:
  4536. cRemoveOwnedSubFilesAction(IDistributedFileTransaction *_transaction, const char *_parentlname,bool _remsub=false)
  4537. : parentlname(_parentlname), remsub(_remsub)
  4538. {
  4539. }
  4540. virtual bool prepare()
  4541. {
  4542. parent.setown(transaction->lookupSuperFile(parentlname));
  4543. if (!parent)
  4544. throw MakeStringException(-1,"removeOwnedSubFiles: SuperFile %s cannot be found", parentlname.get());
  4545. // Try to lock all files
  4546. addFileLock(parent);
  4547. if (lock())
  4548. return true;
  4549. unlock();
  4550. parent.clear();
  4551. return false;
  4552. }
  4553. virtual void run()
  4554. {
  4555. CDistributedSuperFile *sf = QUERYINTERFACE(parent.get(),CDistributedSuperFile);
  4556. if (sf)
  4557. {
  4558. StringArray toRemove;
  4559. Owned<IDistributedFileIterator> iter = parent->getSubFileIterator(false);
  4560. ForEach (*iter)
  4561. {
  4562. IDistributedFile *file = &iter->query();
  4563. IDistributedSuperFile *super = file->querySuperFile();
  4564. StringArray owners;
  4565. if (super)
  4566. {
  4567. CDistributedSuperFile *_super = QUERYINTERFACE(super, CDistributedSuperFile);
  4568. if (_super)
  4569. _super->getSuperOwners(owners);
  4570. }
  4571. else
  4572. {
  4573. CDistributedFile *_file = QUERYINTERFACE(file, CDistributedFile);
  4574. if (_file)
  4575. _file->getSuperOwners(owners);
  4576. }
  4577. if (NotFound == owners.find(parentlname))
  4578. ThrowStringException(-1, "removeOwnedSubFiles: SuperFile %s, subfile %s - subfile not owned by superfile", parentlname.get(), file->queryLogicalName());
  4579. if (1 == owners.ordinality()) // just me
  4580. {
  4581. const char *logicalName = file->queryLogicalName();
  4582. toRemove.append(logicalName);
  4583. // Delay the deletion of the subs until commit
  4584. if (remsub)
  4585. {
  4586. CDfsLogicalFileName lname;
  4587. lname.set(logicalName);
  4588. transaction->addDelayedDelete(lname, INFINITE);
  4589. }
  4590. }
  4591. }
  4592. // Now we clean the subs
  4593. if (sf->numSubFiles(false) == toRemove.ordinality())
  4594. sf->doRemoveSubFiles(transaction); // remove all
  4595. else
  4596. {
  4597. ForEachItemIn(r, toRemove)
  4598. sf->doRemoveSubFile(toRemove.item(r), transaction);
  4599. }
  4600. }
  4601. }
  4602. virtual void retry()
  4603. {
  4604. parent.clear();
  4605. CDFAction::retry();
  4606. }
  4607. };
  4608. /**
  4609. * Swaps sub-files between two super-files within a transaction.
  4610. */
  4611. class cSwapFileAction: public CDFAction
  4612. {
  4613. Linked<IDistributedSuperFile> super1, super2;
  4614. StringAttr super1Name, super2Name;
  4615. public:
  4616. cSwapFileAction(const char *_super1Name, const char *_super2Name)
  4617. : super1Name(_super1Name), super2Name(_super2Name)
  4618. {
  4619. }
  4620. virtual bool prepare()
  4621. {
  4622. super1.setown(transaction->lookupSuperFile(super1Name));
  4623. if (!super1)
  4624. throw MakeStringException(-1,"swapSuperFile: SuperFile %s cannot be found", super1Name.get());
  4625. super2.setown(transaction->lookupSuperFile(super2Name));
  4626. if (!super2)
  4627. {
  4628. super1.clear();
  4629. throw MakeStringException(-1,"swapSuperFile: SuperFile %s cannot be found", super2Name.get());
  4630. }
  4631. // Try to lock all files
  4632. addFileLock(super1);
  4633. for (unsigned i=0; i<super1->numSubFiles(); i++)
  4634. addFileLock(&super1->querySubFile(i));
  4635. addFileLock(super2);
  4636. for (unsigned i=0; i<super2->numSubFiles(); i++)
  4637. addFileLock(&super2->querySubFile(i));
  4638. if (lock())
  4639. {
  4640. transaction->noteSuperSwap(super1, super2);
  4641. return true;
  4642. }
  4643. unlock();
  4644. super1.clear();
  4645. super2.clear();
  4646. return false;
  4647. }
  4648. virtual void run()
  4649. {
  4650. CDistributedSuperFile *sf = QUERYINTERFACE(super1.get(),CDistributedSuperFile);
  4651. if (sf)
  4652. sf->doSwapSuperFile(super2,transaction);
  4653. }
  4654. virtual void retry()
  4655. {
  4656. super1.clear();
  4657. super2.clear();
  4658. CDFAction::retry();
  4659. }
  4660. };
  4661. /**
  4662. * SubFile Iterator, used only to list sub-files of a super-file.
  4663. */
  4664. class cSubFileIterator: public CDistributedFileIteratorBase< IDistributedFileIterator, IArrayOf<IDistributedFile> >
  4665. {
  4666. public:
  4667. cSubFileIterator(IArrayOf<IDistributedFile> &_subfiles, bool supersub)
  4668. {
  4669. ForEachItemIn(i,_subfiles) {
  4670. IDistributedSuperFile* super = supersub?_subfiles.item(i).querySuperFile():NULL;
  4671. if (super) {
  4672. Owned<IDistributedFileIterator> iter = super->getSubFileIterator(true);
  4673. ForEach(*iter)
  4674. list.append(iter->get());
  4675. }
  4676. else
  4677. list.append(*LINK(&_subfiles.item(i)));
  4678. }
  4679. }
  4680. StringBuffer & getName(StringBuffer &name)
  4681. {
  4682. return list.item(index).getLogicalName(name);
  4683. }
  4684. IDistributedFile & query()
  4685. {
  4686. return list.item(index);
  4687. }
  4688. };
  4689. void checkModify(const char *title)
  4690. {
  4691. StringBuffer reason;
  4692. if (!canModify(reason)) {
  4693. #ifdef EXTRA_LOGGING
  4694. PROGLOG("CDistributedSuperFile::%s(canModify) %s",title,reason.str());
  4695. #endif
  4696. if (reason.length())
  4697. throw MakeStringException(-1,"CDistributedSuperFile::%s %s",title,reason.str());
  4698. }
  4699. }
  4700. protected:
  4701. int interleaved; // 0 not interleaved, 1 interleaved old, 2 interleaved new
  4702. IArrayOf<IDistributedFile> subfiles;
  4703. void clearSuperOwners(unsigned timeoutMs, ICodeContext *ctx)
  4704. {
  4705. /* JCSMORE - Why on earth is this doing this way?
  4706. * We are in a super file, we already have [read] locks to sub files (in 'subfiles' array)
  4707. * This should iterate through those and call unlinkSubFile I think.
  4708. */
  4709. Owned<IPropertyTreeIterator> iter = root->getElements("SubFile");
  4710. StringBuffer oquery;
  4711. oquery.append("SuperOwner[@name=\"").append(logicalName.get()).append("\"]");
  4712. ForEach(*iter)
  4713. {
  4714. const char *name = iter->query().queryProp("@name");
  4715. if (name&&*name)
  4716. {
  4717. CDfsLogicalFileName subfn;
  4718. subfn.set(name);
  4719. CFileLock fconnlockSub;
  4720. // JCSMORE - this is really not right, but consistent with previous version
  4721. // MORE: Use CDistributedSuperFile::linkSuperOwner(false) - ie. unlink
  4722. if (fconnlockSub.init(subfn, RTM_LOCK_READ, timeoutMs, "CDistributedFile::doRemoveEntry"))
  4723. {
  4724. IPropertyTree *subfroot = fconnlockSub.queryRoot();
  4725. if (subfroot)
  4726. {
  4727. if (!subfroot->removeProp(oquery.str()))
  4728. {
  4729. VStringBuffer s("SubFile %s is not owned by SuperFile %s", name, logicalName.get());
  4730. if (ctx)
  4731. ctx->addWuExceptionEx(s.str(), 0, SeverityWarning, MSGAUD_user, "DFS[clearSuperOwner]");
  4732. else
  4733. {
  4734. Owned<IException> e = makeStringException(-1, s.str());
  4735. EXCLOG(e, "DFS[clearSuperOwner]");
  4736. }
  4737. }
  4738. }
  4739. }
  4740. }
  4741. }
  4742. }
  4743. static StringBuffer &getSubPath(StringBuffer &path,unsigned idx)
  4744. {
  4745. return path.append("SubFile[@num=\"").append(idx+1).append("\"]");
  4746. }
  4747. void loadSubFiles(IDistributedFileTransaction *transaction, unsigned timeout, bool link=false)
  4748. {
  4749. partscache.kill();
  4750. StringBuffer path;
  4751. StringBuffer subname;
  4752. subfiles.kill();
  4753. unsigned n = root->getPropInt("@numsubfiles");
  4754. if (n == 0)
  4755. return;
  4756. try
  4757. {
  4758. // Find all reported indexes and bail on bad range (before we lock any file)
  4759. Owned<IPropertyTreeIterator> subit = root->getElements("SubFile");
  4760. // Adding a sub 'before' another get the list out of order (but still valid)
  4761. OwnedMalloc<IPropertyTree *> orderedSubFiles(n, true);
  4762. ForEach (*subit)
  4763. {
  4764. IPropertyTree &sub = subit->query();
  4765. unsigned sn = sub.getPropInt("@num",0);
  4766. if (sn == 0)
  4767. ThrowStringException(-1, "CDistributedSuperFile: SuperFile %s: bad subfile part number %d of %d", logicalName.get(), sn, n);
  4768. if (sn > n)
  4769. ThrowStringException(-1, "CDistributedSuperFile: SuperFile %s: out-of-range subfile part number %d of %d", logicalName.get(), sn, n);
  4770. if (orderedSubFiles[sn-1])
  4771. ThrowStringException(-1, "CDistributedSuperFile: SuperFile %s: duplicated subfile part number %d of %d", logicalName.get(), sn, n);
  4772. orderedSubFiles[sn-1] = &sub;
  4773. }
  4774. for (unsigned i=0; i<n; i++)
  4775. {
  4776. if (!orderedSubFiles[i])
  4777. ThrowStringException(-1, "CDistributedSuperFile: SuperFile %s: missing subfile part number %d of %d", logicalName.get(), i+1, n);
  4778. }
  4779. containsRestrictedSubfile = false;
  4780. // Now try to resolve them all (file/superfile)
  4781. for (unsigned f=0; f<n; f++)
  4782. {
  4783. IPropertyTree &sub = *(orderedSubFiles[f]);
  4784. sub.getProp("@name",subname.clear());
  4785. Owned<IDistributedFile> subfile;
  4786. subfile.setown(transaction?transaction->lookupFile(subname.str(),timeout):parent->lookup(subname.str(), udesc, false, false, false, transaction, defaultPrivilegedUser, timeout));
  4787. if (!subfile.get())
  4788. subfile.setown(transaction?transaction->lookupSuperFile(subname.str(),timeout):parent->lookupSuperFile(subname.str(),udesc,transaction,timeout));
  4789. // Some files are ok not to exist
  4790. if (!subfile.get())
  4791. {
  4792. CDfsLogicalFileName cdfsl;
  4793. cdfsl.set(subname);
  4794. if (cdfsl.isForeign())
  4795. {
  4796. IWARNLOG("CDistributedSuperFile: SuperFile %s's sub-file file '%s' is foreign, but missing", logicalName.get(), subname.str());
  4797. // Create a dummy empty superfile as a placeholder for the missing foreign file
  4798. Owned<IPropertyTree> dummySuperRoot = createPTree();
  4799. dummySuperRoot->setPropInt("@interleaved", 0);
  4800. subfile.setown(queryDistributedFileDirectory().createNewSuperFile(dummySuperRoot, subname));
  4801. if (transaction)
  4802. {
  4803. IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
  4804. _transaction->ensureFile(subfile);
  4805. }
  4806. }
  4807. else if (logicalName.isMulti())
  4808. {
  4809. /*
  4810. * implicit superfiles, can't validate subfile presence at this point,
  4811. * but will be caught if empty and not OPT later.
  4812. */
  4813. continue;
  4814. }
  4815. else
  4816. ThrowStringException(-1, "CDistributedSuperFile: SuperFile %s: corrupt subfile file '%s' cannot be found", logicalName.get(), subname.str());
  4817. }
  4818. containsRestrictedSubfile = containsRestrictedSubfile || subfile->isRestrictedAccess();
  4819. subfiles.append(*subfile.getClear());
  4820. if (link)
  4821. linkSubFile(f);
  4822. }
  4823. // This is can happen due to missing referenced foreign files, or missing files referenced via an implicit inline superfile definition
  4824. if (subfiles.ordinality() != n)
  4825. {
  4826. IWARNLOG("CDistributedSuperFile: SuperFile %s's number of sub-files updated to %d", logicalName.get(), subfiles.ordinality());
  4827. root->setPropInt("@numsubfiles", subfiles.ordinality());
  4828. }
  4829. }
  4830. catch (IException *)
  4831. {
  4832. partscache.kill();
  4833. subfiles.kill(); // one out, all out
  4834. throw;
  4835. }
  4836. }
  4837. void addItem(unsigned pos,IDistributedFile *_file)
  4838. {
  4839. Owned<IDistributedFile> file = _file;
  4840. partscache.kill();
  4841. // first renumber all above
  4842. StringBuffer path;
  4843. IPropertyTree *sub;
  4844. for (unsigned i=subfiles.ordinality();i>pos;i--)
  4845. {
  4846. sub = root->queryPropTree(getSubPath(path.clear(),i-1).str());
  4847. if (!sub)
  4848. throw MakeStringException(-1,"C(2): Corrupt subfile file part %d cannot be found",i);
  4849. sub->setPropInt("@num",i+1);
  4850. }
  4851. sub = createPTree();
  4852. sub->setPropInt("@num",pos+1);
  4853. sub->setProp("@name",file->queryLogicalName());
  4854. if (pos==0)
  4855. {
  4856. Owned<IPropertyTree> superAttrs = createPTreeFromIPT(&file->queryAttributes());
  4857. while (superAttrs->removeProp("Protect")); // do not automatically inherit protected status
  4858. resetFileAttr(superAttrs.getClear());
  4859. }
  4860. root->addPropTree("SubFile",sub);
  4861. subfiles.add(*file.getClear(),pos);
  4862. root->setPropInt("@numsubfiles",subfiles.ordinality());
  4863. }
  4864. void removeItem(unsigned pos)
  4865. {
  4866. partscache.kill();
  4867. StringBuffer path;
  4868. IPropertyTree* sub = root->queryPropTree(getSubPath(path,pos).str());
  4869. if (!sub)
  4870. throw MakeStringException(-1,"CDistributedSuperFile(3): Corrupt subfile file part %d cannot be found",pos+1);
  4871. root->removeTree(sub);
  4872. // now renumber all above
  4873. for (unsigned i=pos+1; i<subfiles.ordinality(); i++)
  4874. {
  4875. sub = root->queryPropTree(getSubPath(path.clear(),i).str());
  4876. if (!sub)
  4877. throw MakeStringException(-1,"CDistributedSuperFile(2): Corrupt subfile file part %d cannot be found",i+1);
  4878. sub->setPropInt("@num",i);
  4879. }
  4880. subfiles.remove(pos);
  4881. if (pos==0)
  4882. {
  4883. if (subfiles.ordinality())
  4884. {
  4885. Owned<IPropertyTree> superAttrs = createPTreeFromIPT(&subfiles.item(0).queryAttributes());
  4886. while (superAttrs->removeProp("Protect")); // do not automatically inherit protected status
  4887. resetFileAttr(superAttrs.getClear());
  4888. }
  4889. else
  4890. resetFileAttr(getEmptyAttr());
  4891. }
  4892. root->setPropInt("@numsubfiles",subfiles.ordinality());
  4893. }
  4894. void loadParts(CDistributedFilePartArray &partsret, IDFPartFilter *filter)
  4895. {
  4896. unsigned p = 0;
  4897. if (interleaved) { // a bit convoluted but should work
  4898. IArrayOf<IDistributedFile> allsubfiles;
  4899. ForEachItemIn(i,subfiles) {
  4900. // if old format keep original interleaving
  4901. IDistributedSuperFile* super = (interleaved==1)?NULL:subfiles.item(i).querySuperFile();
  4902. if (super) {
  4903. Owned<IDistributedFileIterator> iter = super->getSubFileIterator(true);
  4904. ForEach(*iter)
  4905. allsubfiles.append(iter->get());
  4906. }
  4907. else
  4908. allsubfiles.append(*LINK(&subfiles.item(i)));
  4909. }
  4910. unsigned *pn = new unsigned[allsubfiles.ordinality()];
  4911. ForEachItemIn(j,allsubfiles)
  4912. pn[j] = allsubfiles.item(j).numParts();
  4913. unsigned f=0;
  4914. bool found=false;
  4915. for (;;) {
  4916. if (f==allsubfiles.ordinality()) {
  4917. if (!found)
  4918. break; // no more
  4919. found = false;
  4920. f = 0;
  4921. }
  4922. if (pn[f]) {
  4923. found = true;
  4924. if (!filter||filter->includePart(p)) {
  4925. IDistributedFile &subfile = allsubfiles.item(f);
  4926. IDistributedFilePart *part = subfile.getPart(subfile.numParts()-pn[f]);
  4927. partsret.append(*QUERYINTERFACE(part,CDistributedFilePart)); // bit kludgy
  4928. }
  4929. p++;
  4930. pn[f]--;
  4931. }
  4932. f++;
  4933. }
  4934. delete [] pn;
  4935. }
  4936. else { // sequential
  4937. ForEachItemIn(i,subfiles) { // not wonderfully quick
  4938. IDistributedFile &subfile = subfiles.item(i);
  4939. unsigned n = subfile.numParts();
  4940. unsigned j = 0;
  4941. while (n--) {
  4942. if (!filter||filter->includePart(p)) {
  4943. IDistributedFilePart *part = subfile.getPart(j++);
  4944. partsret.append(*QUERYINTERFACE(part,CDistributedFilePart)); // bit kludgy
  4945. }
  4946. p++;
  4947. }
  4948. }
  4949. }
  4950. }
  4951. void linkSubFile(unsigned pos, bool link=true)
  4952. {
  4953. IDistributedFile *subfile = &subfiles.item(pos);
  4954. IDistributedSuperFile *ssub = subfile->querySuperFile();
  4955. if (ssub) {
  4956. CDistributedSuperFile *cdsuper = QUERYINTERFACE(ssub,CDistributedSuperFile);
  4957. cdsuper->linkSuperOwner(queryLogicalName(),link);
  4958. }
  4959. else {
  4960. CDistributedFile *cdfile = QUERYINTERFACE(subfile,CDistributedFile);
  4961. cdfile->linkSuperOwner(queryLogicalName(),link);
  4962. }
  4963. }
  4964. void unlinkSubFile(unsigned pos)
  4965. {
  4966. linkSubFile(pos, false);
  4967. }
  4968. void checkSubFormatAttr(IDistributedFile *sub, const char* exprefix="")
  4969. {
  4970. // empty super files now pass
  4971. ForEachItemIn(i,subfiles) {
  4972. IDistributedSuperFile* super = subfiles.item(i).querySuperFile();
  4973. if (super) {
  4974. CDistributedSuperFile *cdsuper = QUERYINTERFACE(super,CDistributedSuperFile);
  4975. if (cdsuper)
  4976. cdsuper->checkSubFormatAttr(sub,exprefix);
  4977. return;
  4978. }
  4979. CDistributedFile *cdfile = QUERYINTERFACE(&subfiles.item(0),CDistributedFile);
  4980. if (cdfile)
  4981. cdfile->checkFormatAttr(sub,exprefix); // any file will do
  4982. }
  4983. }
  4984. void addPropIfCommon(IPropertyTree &target, const char *prop, const char *value)
  4985. {
  4986. bool ok = true;
  4987. // add attributes that are common
  4988. for (unsigned i=1; i<subfiles.ordinality(); i++)
  4989. {
  4990. IDistributedFile &file = subfiles.item(i);
  4991. IDistributedSuperFile *sFile = file.querySuperFile();
  4992. if (!sFile || sFile->numSubFiles(true)) // skip empty super files
  4993. {
  4994. const char *otherValue = file.queryAttributes().queryProp(prop);
  4995. if (!otherValue || !streq(otherValue, value))
  4996. {
  4997. ok = false;
  4998. break;
  4999. }
  5000. }
  5001. }
  5002. if (ok)
  5003. target.setProp(prop, value);
  5004. }
  5005. public:
  5006. virtual void checkFormatAttr(IDistributedFile *sub, const char* exprefix="") override
  5007. {
  5008. IDistributedSuperFile *superSub = sub->querySuperFile();
  5009. if (superSub && (0 == superSub->numSubFiles(true)))
  5010. return;
  5011. // only check sub files not siblings, which is excessive (format checking is really only debug aid)
  5012. checkSubFormatAttr(sub,exprefix);
  5013. }
  5014. unsigned findSubFile(const char *name)
  5015. {
  5016. StringBuffer lfn;
  5017. normalizeLFN(name,lfn);
  5018. ForEachItemIn(i,subfiles)
  5019. if (stricmp(subfiles.item(i).queryLogicalName(),lfn.str())==0)
  5020. return i;
  5021. return NotFound;
  5022. }
  5023. IMPLEMENT_IINTERFACE_O;
  5024. void commonInit(CDistributedFileDirectory *_parent, IPropertyTree *_root)
  5025. {
  5026. parent = _parent;
  5027. root.set(_root);
  5028. const char *val = root->queryProp("@interleaved");
  5029. if (val&&isdigit(*val))
  5030. interleaved = atoi(val);
  5031. else
  5032. interleaved = strToBool(val)?1:0;
  5033. }
  5034. void init(CDistributedFileDirectory *_parent, IPropertyTree *_root, const CDfsLogicalFileName &_name, IUserDescriptor* user, IDistributedFileTransaction *transaction, unsigned timeout=INFINITE)
  5035. {
  5036. assertex(_name.isSet());
  5037. setUserDescriptor(udesc,user);
  5038. logicalName.set(_name);
  5039. commonInit(_parent, _root);
  5040. loadSubFiles(transaction,timeout);
  5041. }
  5042. CDistributedSuperFile(CDistributedFileDirectory *_parent, IPropertyTree *_root,const CDfsLogicalFileName &_name,IUserDescriptor* user)
  5043. {
  5044. init(_parent,_root,_name,user,NULL);
  5045. }
  5046. CDistributedSuperFile(CDistributedFileDirectory *_parent, IRemoteConnection *_conn,const CDfsLogicalFileName &_name,IUserDescriptor* user, IDistributedFileTransaction *transaction,unsigned timeout)
  5047. {
  5048. conn.setown(_conn);
  5049. init(_parent,conn->queryRoot(),_name,user,transaction,timeout);
  5050. }
  5051. CDistributedSuperFile(CDistributedFileDirectory *_parent, CDfsLogicalFileName &_name, IUserDescriptor* user, IDistributedFileTransaction *transaction)
  5052. {
  5053. // temp super file
  5054. assertex(_name.isMulti());
  5055. if (!_name.isExpanded())
  5056. _name.expand(user);//expand wildcards
  5057. Owned<IPropertyTree> tree = _name.createSuperTree();
  5058. init(_parent,tree,_name,user,transaction);
  5059. updateFileAttrs();
  5060. }
  5061. CDistributedSuperFile(CDistributedFileDirectory *_parent, IPropertyTree *_root, const char *optionalName)
  5062. {
  5063. commonInit(_parent, _root);
  5064. if (optionalName)
  5065. logicalName.set(optionalName);
  5066. }
  5067. ~CDistributedSuperFile()
  5068. {
  5069. partscache.kill();
  5070. subfiles.kill();
  5071. }
  5072. virtual StringBuffer &getClusterName(unsigned clusternum,StringBuffer &name) override
  5073. {
  5074. // returns the cluster name if all the same
  5075. CriticalBlock block (sect);
  5076. if (subfiles.ordinality()==1)
  5077. return subfiles.item(0).getClusterName(clusternum,name);
  5078. size32_t rl = name.length();
  5079. StringBuffer test;
  5080. ForEachItemIn(i,subfiles) {
  5081. if (i) {
  5082. subfiles.item(i).getClusterName(clusternum,test.clear());
  5083. if (strcmp(name.str(),test.str())!=0) {
  5084. name.setLength(rl);
  5085. break;
  5086. }
  5087. }
  5088. else
  5089. subfiles.item(i).getClusterName(clusternum,name);
  5090. }
  5091. return name;
  5092. }
  5093. virtual IFileDescriptor *getFileDescriptor(const char *clustername) override
  5094. {
  5095. CriticalBlock block (sect);
  5096. if (subfiles.ordinality()==1)
  5097. return subfiles.item(0).getFileDescriptor(clustername);
  5098. // superfiles assume consistant replication & compression
  5099. UnsignedArray subcounts;
  5100. bool mixedwidth = false;
  5101. unsigned width = 0;
  5102. bool first = true;
  5103. Owned<IPropertyTree> at = getEmptyAttr();
  5104. Owned<IDistributedFileIterator> fiter = getSubFileIterator(true);
  5105. ForEach(*fiter)
  5106. {
  5107. IDistributedFile &file = fiter->query();
  5108. if (first)
  5109. {
  5110. first = false;
  5111. Owned<IAttributeIterator> ait = file.queryAttributes().getAttributes();
  5112. ForEach(*ait)
  5113. {
  5114. const char *name = ait->queryName();
  5115. if ((stricmp(name,"@size")!=0)&&(stricmp(name,"@recordCount")!=0))
  5116. {
  5117. const char *v = ait->queryValue();
  5118. if (!v)
  5119. continue;
  5120. addPropIfCommon(*at, name, v);
  5121. }
  5122. }
  5123. MemoryBuffer mb;
  5124. if (getRecordLayout(mb, "_record_layout"))
  5125. at->setPropBin("_record_layout", mb.length(), mb.bufferBase());
  5126. if (getRecordLayout(mb, "_rtlType"))
  5127. at->setPropBin("_rtlType", mb.length(), mb.bufferBase());
  5128. const char *ecl = file.queryAttributes().queryProp("ECL");
  5129. if (!isEmptyString(ecl))
  5130. addPropIfCommon(*at, "ECL", ecl);
  5131. }
  5132. unsigned np = file.numParts();
  5133. if (0 == width)
  5134. width = np;
  5135. else if (np!=width)
  5136. mixedwidth = true;
  5137. subcounts.append(np);
  5138. }
  5139. // need common attributes
  5140. Owned<ISuperFileDescriptor> fdesc=createSuperFileDescriptor(at.getClear());
  5141. if (interleaved&&(interleaved!=2))
  5142. IWARNLOG("getFileDescriptor: Unsupported interleave value (1)");
  5143. fdesc->setSubMapping(subcounts,interleaved!=0);
  5144. fdesc->setTraceName(logicalName.get());
  5145. Owned<IDistributedFilePartIterator> iter = getIterator(NULL);
  5146. unsigned n = 0;
  5147. SocketEndpointArray reps;
  5148. ForEach(*iter) {
  5149. IDistributedFilePart &part = iter->query();
  5150. CDistributedFilePart *cpart = (clustername&&*clustername)?QUERYINTERFACE(&part,CDistributedFilePart):NULL;
  5151. unsigned copy = 0;
  5152. if (cpart) {
  5153. IDistributedFile &f = cpart->queryParent();
  5154. unsigned cn = f.findCluster(clustername);
  5155. if (cn!=NotFound) {
  5156. for (unsigned i = 0;i<cpart->numCopies();i++)
  5157. if (cpart->copyClusterNum(i,NULL)==cn) {
  5158. copy = i;
  5159. break;
  5160. }
  5161. }
  5162. }
  5163. if (mixedwidth) {
  5164. SocketEndpoint rep;
  5165. if (copy+1<part.numCopies())
  5166. rep = part.queryNode(copy+1)->endpoint();
  5167. reps.append(rep);
  5168. }
  5169. RemoteFilename rfn;
  5170. fdesc->setPart(n,part.getFilename(rfn,copy),&part.queryAttributes());
  5171. n++;
  5172. }
  5173. ClusterPartDiskMapSpec mspec;
  5174. if (subfiles.ordinality()) {
  5175. mspec = subfiles.item(0).queryPartDiskMapping(0);
  5176. }
  5177. mspec.interleave = numSubFiles(true);
  5178. fdesc->endCluster(mspec);
  5179. if (mixedwidth) { // bleah - have to add replicate node numbers
  5180. Owned<IGroup> group = fdesc->getGroup();
  5181. unsigned gw = group->ordinality();
  5182. for (unsigned pn=0;pn<reps.ordinality();pn++) {
  5183. const SocketEndpoint &ep=reps.item(pn);
  5184. if (!ep.isNull()) {
  5185. unsigned gn = pn;
  5186. if (gn<gw) {
  5187. do {
  5188. gn++;
  5189. if (gn==gw)
  5190. gn = 0;
  5191. if (ep.equals(group->queryNode((rank_t)gn).endpoint())) {
  5192. IPartDescriptor *part = fdesc->queryPart(pn);
  5193. if (part)
  5194. part->queryProperties().setPropInt("@rn",(unsigned)gn);
  5195. break;
  5196. }
  5197. } while (gn!=pn);
  5198. }
  5199. }
  5200. }
  5201. }
  5202. return fdesc.getClear();
  5203. }
  5204. virtual unsigned numParts() override
  5205. {
  5206. CriticalBlock block(sect);
  5207. unsigned ret=0;
  5208. ForEachItemIn(i,subfiles)
  5209. ret += subfiles.item(i).numParts();
  5210. return ret;
  5211. }
  5212. virtual IDistributedFilePart &queryPart(unsigned idx) override
  5213. {
  5214. CriticalBlock block(sect);
  5215. if (subfiles.ordinality()==1)
  5216. return subfiles.item(0).queryPart(idx);
  5217. if (partscache.ordinality()==0)
  5218. loadParts(partscache,NULL);
  5219. if (idx>=partscache.ordinality())
  5220. return *(IDistributedFilePart *)NULL;
  5221. return partscache.item(idx);
  5222. }
  5223. virtual IDistributedFilePart* getPart(unsigned idx) override
  5224. {
  5225. IDistributedFilePart* ret = &queryPart(idx);
  5226. return LINK(ret);
  5227. }
  5228. virtual IDistributedFilePartIterator *getIterator(IDFPartFilter *filter=NULL) override
  5229. {
  5230. CriticalBlock block(sect);
  5231. if (subfiles.ordinality()==1)
  5232. return subfiles.item(0).getIterator(filter);
  5233. CDistributedFilePartIterator *ret = new CDistributedFilePartIterator();
  5234. loadParts(ret->queryParts(),filter);
  5235. return ret;
  5236. }
  5237. virtual void rename(const char *_logicalname,IUserDescriptor *user) override
  5238. {
  5239. StringBuffer prevname;
  5240. Owned<IFileRelationshipIterator> reliter;
  5241. // set prevname
  5242. if (!isAnon()) {
  5243. getLogicalName(prevname);
  5244. try {
  5245. IFileRelationshipIterator *iter = parent->lookupAllFileRelationships(prevname.str());
  5246. reliter.setown(iter);
  5247. }
  5248. catch (IException *e) {
  5249. EXCLOG(e,"CDistributedFileDirectory::rename");
  5250. e->Release();
  5251. }
  5252. detach();
  5253. }
  5254. attach(_logicalname,user);
  5255. if (reliter.get()) {
  5256. // add back any relationships with new name
  5257. parent->renameFileRelationships(prevname.str(),_logicalname,reliter,user);
  5258. }
  5259. }
  5260. virtual const char *queryDefaultDir() override
  5261. {
  5262. // returns the directory if all the same
  5263. const char *ret = NULL;
  5264. CriticalBlock block (sect);
  5265. ForEachItemIn(i,subfiles) {
  5266. if (subfiles.item(i).numParts())
  5267. {
  5268. const char *s = subfiles.item(i).queryDefaultDir();
  5269. if (!s)
  5270. return NULL;
  5271. if (!ret)
  5272. ret = s;
  5273. else if (strcmp(ret,s)!=0)
  5274. return NULL;
  5275. }
  5276. }
  5277. return ret;
  5278. }
  5279. virtual const char *queryPartMask() override
  5280. {
  5281. // returns the part mask if all the same
  5282. const char *ret = NULL;
  5283. CriticalBlock block (sect);
  5284. ForEachItemIn(i,subfiles) {
  5285. const char *s = subfiles.item(i).queryPartMask();
  5286. if (!s)
  5287. return NULL;
  5288. if (!ret)
  5289. ret = s;
  5290. else if (stricmp(ret,s)!=0)
  5291. return NULL;
  5292. }
  5293. return ret;
  5294. }
  5295. virtual void attach(const char *_logicalname,IUserDescriptor *user) override
  5296. {
  5297. assertex(!conn.get()); // already attached
  5298. CriticalBlock block (sect);
  5299. StringBuffer tail;
  5300. StringBuffer lfn;
  5301. logicalName.set(_logicalname);
  5302. checkLogicalName(logicalName,user,true,true,false,"attach");
  5303. parent->addEntry(logicalName,root.getClear(),true,false);
  5304. conn.clear();
  5305. CFileLock fcl;
  5306. verifyex(fcl.init(logicalName, DXB_SuperFile, RTM_LOCK_READ, defaultTimeout, "CDistributedSuperFile::attach"));
  5307. conn.setown(fcl.detach());
  5308. assertex(conn.get()); // must have been attached
  5309. root.setown(conn->getRoot());
  5310. loadSubFiles(NULL, 0, true);
  5311. }
  5312. virtual void detach(unsigned timeoutMs=INFINITE, ICodeContext *ctx=NULL) override
  5313. {
  5314. assertex(conn.get()); // must be attached
  5315. CriticalBlock block(sect);
  5316. checkModify("CDistributedSuperFile::detach");
  5317. StringBuffer reason;
  5318. if (checkOwned(reason))
  5319. throw MakeStringException(-1, "detach: %s", reason.str());
  5320. subfiles.kill();
  5321. // Remove from SDS
  5322. /* JCSMORE - this looks very kludgy...
  5323. * We have readlock, this code is doing
  5324. * 1) change to write lock (not using lockProperties or DistributedFilePropertyLock to do so) [using CFileChangeWriteLock]
  5325. * CFileChangeWriteLock doesn't preserve lock mode quite right.. (see 'newMode')
  5326. * 2) manually deleting SuperOwner from subfiles (in clearSuperOwners)
  5327. * 3) Using the connection to delete the SuperFile from Dali (clones to 'root' in process)
  5328. * 4) ~CFileChangeWriteLock() [writeLock.clear()], restores read lock from write to read
  5329. * 5) updateFS (housekeeping of empty scopes, relationships) - ok
  5330. */
  5331. CFileChangeWriteLock writeLock(conn, timeoutMs);
  5332. clearSuperOwners(timeoutMs, ctx);
  5333. writeLock.clear();
  5334. root.setown(closeConnection(true));
  5335. updateFS(logicalName, parent->queryDefaultTimeout());
  5336. logicalName.clear();
  5337. }
  5338. virtual bool existsPhysicalPartFiles(unsigned short port) override
  5339. {
  5340. CriticalBlock block (sect);
  5341. ForEachItemIn(i,subfiles) {
  5342. IDistributedFile &f=subfiles.item(i);
  5343. if (!f.existsPhysicalPartFiles(port))
  5344. return false;
  5345. }
  5346. return true;
  5347. }
  5348. virtual bool renamePhysicalPartFiles(const char *newlfn,const char *cluster,IMultiException *mexcept,const char *newbasedir) override
  5349. {
  5350. throw MakeStringException(-1,"renamePhysicalPartFiles not supported for SuperFiles");
  5351. return false;
  5352. }
  5353. void serialize(MemoryBuffer &mb)
  5354. {
  5355. UNIMPLEMENTED; // not yet needed
  5356. }
  5357. virtual unsigned numCopies(unsigned partno) override
  5358. {
  5359. unsigned ret = (unsigned)-1;
  5360. CriticalBlock block (sect);
  5361. ForEachItemIn(i,subfiles) {
  5362. IDistributedFile &f=subfiles.item(i);
  5363. unsigned fnc = f.numCopies(partno);
  5364. if (fnc<ret)
  5365. ret = fnc;
  5366. }
  5367. return (ret==(unsigned)-1)?1:ret;
  5368. }
  5369. virtual __int64 getFileSize(bool allowphysical,bool forcephysical) override
  5370. {
  5371. __int64 ret = (__int64)(forcephysical?-1:queryAttributes().getPropInt64("@size",-1));
  5372. if (ret==-1)
  5373. {
  5374. ret = 0;
  5375. ForEachItemIn(i,subfiles)
  5376. {
  5377. __int64 ps = subfiles.item(i).getFileSize(allowphysical,forcephysical);
  5378. if (ps == -1)
  5379. return -1; // i.e. if cannot determine size of any part, total is unknown
  5380. ret += ps;
  5381. }
  5382. }
  5383. return ret;
  5384. }
  5385. virtual __int64 getDiskSize(bool allowphysical,bool forcephysical) override
  5386. {
  5387. if (!isCompressed(NULL))
  5388. return getFileSize(allowphysical, forcephysical);
  5389. __int64 ret = (__int64)(forcephysical?-1:queryAttributes().getPropInt64("@compressedSize",-1));
  5390. if (ret==-1)
  5391. {
  5392. ret = 0;
  5393. ForEachItemIn(i,subfiles)
  5394. {
  5395. __int64 ps = subfiles.item(i).getDiskSize(allowphysical,forcephysical);
  5396. if (ps == -1)
  5397. return -1; // i.e. if cannot determine size of any part, total is unknown
  5398. ret += ps;
  5399. }
  5400. }
  5401. return ret;
  5402. }
  5403. __int64 getRecordCount()
  5404. {
  5405. __int64 ret = queryAttributes().getPropInt64("@recordCount",-1);
  5406. if (ret==-1)
  5407. {
  5408. ret = 0;
  5409. ForEachItemIn(i,subfiles)
  5410. {
  5411. IDistributedFile &subFile = subfiles.item(i);
  5412. __int64 rc = subFile.queryAttributes().getPropInt64("@recordCount", -1);
  5413. if (rc == -1)
  5414. {
  5415. IDistributedSuperFile *super = subFile.querySuperFile();
  5416. // if regular file or non-empty super, must have a @recordCount to aggregate a total
  5417. if ((nullptr == super) || (super->numSubFiles()>0))
  5418. return -1;
  5419. }
  5420. else
  5421. ret += rc;
  5422. }
  5423. }
  5424. return ret;
  5425. }
  5426. virtual bool getFileCheckSum(unsigned &checkSum) override
  5427. {
  5428. if (queryAttributes().hasProp("@checkSum"))
  5429. checkSum = (unsigned)queryAttributes().getPropInt64("@checkSum");
  5430. else
  5431. {
  5432. checkSum = ~0;
  5433. ForEachItemIn(i,subfiles) {
  5434. unsigned cs;
  5435. if (!subfiles.item(i).getFileCheckSum(cs))
  5436. return false;
  5437. checkSum ^= cs;
  5438. }
  5439. }
  5440. return true;
  5441. }
  5442. virtual IDistributedSuperFile *querySuperFile() override
  5443. {
  5444. return this;
  5445. }
  5446. virtual IDistributedFile &querySubFile(unsigned idx,bool sub) override
  5447. {
  5448. CriticalBlock block (sect);
  5449. if (sub) {
  5450. ForEachItemIn(i,subfiles) {
  5451. IDistributedFile &f=subfiles.item(i);
  5452. IDistributedSuperFile *super = f.querySuperFile();
  5453. if (super) {
  5454. unsigned ns = super->numSubFiles(true);
  5455. if (ns>idx)
  5456. return super->querySubFile(idx,true);
  5457. idx -= ns;
  5458. }
  5459. else if (idx--==0)
  5460. return f;
  5461. }
  5462. // fall through to error
  5463. }
  5464. return subfiles.item(idx);
  5465. }
  5466. virtual IDistributedFile *querySubFileNamed(const char *name, bool sub) override
  5467. {
  5468. CriticalBlock block (sect);
  5469. unsigned idx=findSubFileOrd(name);
  5470. if ((idx!=NotFound)&&(idx<subfiles.ordinality()))
  5471. return &subfiles.item(idx);
  5472. idx=findSubFile(name);
  5473. if (idx!=NotFound)
  5474. return &subfiles.item(idx);
  5475. if (sub) {
  5476. ForEachItemIn(i,subfiles) {
  5477. IDistributedFile &f=subfiles.item(i);
  5478. IDistributedSuperFile *super = f.querySuperFile();
  5479. if (super) {
  5480. IDistributedFile *ret = super->querySubFileNamed(name);
  5481. if (ret)
  5482. return ret;
  5483. }
  5484. }
  5485. }
  5486. return NULL;
  5487. }
  5488. virtual IDistributedFile *getSubFile(unsigned idx,bool sub) override
  5489. {
  5490. CriticalBlock block (sect);
  5491. return LINK(&querySubFile(idx,sub));
  5492. }
  5493. virtual unsigned numSubFiles(bool sub) override
  5494. {
  5495. CriticalBlock block (sect);
  5496. unsigned ret = 0;
  5497. if (sub) {
  5498. ForEachItemIn(i,subfiles) {
  5499. IDistributedFile &f=subfiles.item(i);
  5500. IDistributedSuperFile *super = f.querySuperFile();
  5501. if (super)
  5502. ret += super->numSubFiles(sub);
  5503. else
  5504. ret++;
  5505. }
  5506. }
  5507. else
  5508. ret = subfiles.ordinality();
  5509. return ret;
  5510. }
  5511. virtual bool getFormatCrc(unsigned &crc) override
  5512. {
  5513. if (queryAttributes().hasProp("@formatCrc")) {
  5514. crc = (unsigned)queryAttributes().getPropInt("@formatCrc");
  5515. return true;
  5516. }
  5517. bool found = false;
  5518. ForEachItemIn(i,subfiles) {
  5519. unsigned c;
  5520. if (subfiles.item(i).getFormatCrc(c)) {
  5521. if (found&&(c!=crc))
  5522. return false;
  5523. found = true;
  5524. crc = c;
  5525. }
  5526. }
  5527. return found;
  5528. }
  5529. virtual bool getRecordLayout(MemoryBuffer &layout, const char *attrname) override
  5530. {
  5531. layout.clear();
  5532. if (queryAttributes().getPropBin(attrname, layout))
  5533. return true;
  5534. bool found = false;
  5535. ForEachItemIn(i,subfiles) {
  5536. MemoryBuffer b;
  5537. if (subfiles.item(i).getRecordLayout(found?b:layout, attrname)) {
  5538. if (found) {
  5539. if ((b.length()!=layout.length())||(memcmp(b.bufferBase(),layout.bufferBase(),b.length())!=0))
  5540. return false;
  5541. }
  5542. else
  5543. found = true;
  5544. }
  5545. }
  5546. return found;
  5547. }
  5548. virtual bool getRecordSize(size32_t &rsz) override
  5549. {
  5550. if (queryAttributes().hasProp("@recordSize")) {
  5551. rsz = (size32_t)queryAttributes().getPropInt("@recordSize");
  5552. return true;
  5553. }
  5554. bool found = false;
  5555. ForEachItemIn(i,subfiles) {
  5556. size32_t sz;
  5557. if (subfiles.item(i).getRecordSize(sz)) {
  5558. if (found&&(sz!=rsz))
  5559. return false;
  5560. found = true;
  5561. rsz = sz;
  5562. }
  5563. }
  5564. return found;
  5565. }
  5566. virtual bool isInterleaved() override
  5567. {
  5568. return interleaved!=0;
  5569. }
  5570. virtual IDistributedFile *querySubPart(unsigned partidx,unsigned &subfileidx) override
  5571. {
  5572. CriticalBlock block (sect);
  5573. subfileidx = 0;
  5574. Owned<IDistributedFilePart> part = getPart(partidx);
  5575. if (!part)
  5576. return NULL;
  5577. CDistributedFilePart *cpart = QUERYINTERFACE(part.get(),CDistributedFilePart);
  5578. if (!cpart)
  5579. return NULL;
  5580. IDistributedFile &ret = cpart->queryParent();
  5581. unsigned n = ret.numParts();
  5582. for (unsigned i=0;i<n;i++) {
  5583. Owned<IDistributedFilePart> spart = ret.getPart(i);
  5584. if (spart.get()==part.get()) {
  5585. subfileidx = i;
  5586. return &ret;
  5587. }
  5588. }
  5589. return NULL;
  5590. }
  5591. virtual unsigned getPositionPart(offset_t pos, offset_t &base) override
  5592. { // not very quick!
  5593. CriticalBlock block (sect);
  5594. unsigned n = numParts();
  5595. base = 0;
  5596. for (unsigned i=0;i<n;i++) {
  5597. Owned<IDistributedFilePart> part = getPart(i);
  5598. offset_t ps = part->getFileSize(true,false);
  5599. if (ps==(offset_t)-1)
  5600. break;
  5601. if (ps>pos)
  5602. return i;
  5603. pos -= ps;
  5604. base += ps;
  5605. }
  5606. return NotFound;
  5607. }
  5608. virtual IDistributedFileIterator *getSubFileIterator(bool supersub=false) override
  5609. {
  5610. CriticalBlock block (sect);
  5611. return new cSubFileIterator(subfiles,supersub);
  5612. }
  5613. void updateFileAttrs()
  5614. {
  5615. if (subfiles.ordinality()==0) {
  5616. StringBuffer desc;
  5617. root->getProp("Attr/@description",desc);
  5618. root->removeProp("Attr"); // remove all other attributes if superfile empty
  5619. IPropertyTree *t=resetFileAttr(getEmptyAttr());
  5620. if (desc.length())
  5621. t->setProp("@description",desc.str());
  5622. return;
  5623. }
  5624. IPropertyTree &attrs = queryAttributes();
  5625. attrs.removeProp("@size");
  5626. attrs.removeProp("@compressedSize");
  5627. attrs.removeProp("@checkSum");
  5628. attrs.removeProp("@recordCount"); // recordCount not currently supported by superfiles
  5629. attrs.removeProp("@formatCrc"); // formatCrc set if all consistant
  5630. attrs.removeProp("@recordSize"); // record size set if all consistant
  5631. attrs.removeProp("_record_layout"); // legacy info - set if all consistent
  5632. attrs.removeProp("_rtlType"); // new info - set if all consistent
  5633. attrs.removeProp("@maxSkew");
  5634. attrs.removeProp("@minSkew");
  5635. attrs.removeProp("@maxSkewPart");
  5636. attrs.removeProp("@minSkewPart");
  5637. __int64 fs = getFileSize(false,false);
  5638. if (fs!=-1)
  5639. attrs.setPropInt64("@size",fs);
  5640. if (isCompressed(NULL))
  5641. {
  5642. fs = getDiskSize(false,false);
  5643. if (fs!=-1)
  5644. attrs.setPropInt64("@compressedSize",fs);
  5645. }
  5646. unsigned checkSum;
  5647. if (getFileCheckSum(checkSum))
  5648. attrs.setPropInt64("@checkSum", checkSum);
  5649. __int64 rc = getRecordCount();
  5650. if (rc!=-1)
  5651. attrs.setPropInt64("@recordCount",rc);
  5652. unsigned fcrc;
  5653. if (getFormatCrc(fcrc))
  5654. attrs.setPropInt("@formatCrc", fcrc);
  5655. size32_t rsz;
  5656. if (getRecordSize(rsz))
  5657. attrs.setPropInt("@recordSize", rsz);
  5658. MemoryBuffer mb;
  5659. if (getRecordLayout(mb, "_record_layout"))
  5660. attrs.setPropBin("_record_layout", mb.length(), mb.bufferBase());
  5661. if (getRecordLayout(mb, "_rtlType"))
  5662. attrs.setPropBin("_rtlType", mb.length(), mb.bufferBase());
  5663. const char *kind = nullptr;
  5664. Owned<IDistributedFileIterator> subIter = getSubFileIterator(true);
  5665. ForEach(*subIter)
  5666. {
  5667. IDistributedFile &file = subIter->query();
  5668. const char *curKind = file.queryAttributes().queryProp("@kind");
  5669. if (!kind)
  5670. kind = curKind;
  5671. else if (!strsame(kind, curKind))
  5672. {
  5673. kind = nullptr;
  5674. break;
  5675. }
  5676. }
  5677. if (kind)
  5678. attrs.setProp("@kind", kind);
  5679. }
  5680. void updateParentFileAttrs(IDistributedFileTransaction *transaction)
  5681. {
  5682. Owned<IPropertyTreeIterator> iter = root->getElements("SuperOwner");
  5683. StringBuffer pname;
  5684. ForEach(*iter) {
  5685. iter->query().getProp("@name",pname.clear());
  5686. Owned<IDistributedSuperFile> psfile = transaction?transaction->lookupSuperFile(pname.str()):
  5687. queryDistributedFileDirectory().lookupSuperFile(pname.str(),udesc,NULL);
  5688. CDistributedSuperFile *file = QUERYINTERFACE(psfile.get(),CDistributedSuperFile);
  5689. if (file) {
  5690. {
  5691. DistributedFilePropertyLock lock(file);
  5692. file->setModified();
  5693. file->updateFileAttrs();
  5694. }
  5695. file->updateParentFileAttrs(transaction);
  5696. }
  5697. }
  5698. }
  5699. void validateAddSubFile(IDistributedFile *sub)
  5700. {
  5701. if (strcmp(sub->queryLogicalName(),queryLogicalName())==0)
  5702. throw MakeStringException(-1,"addSubFile: Cannot add file %s to itself", queryLogicalName());
  5703. if (subfiles.ordinality())
  5704. checkFormatAttr(sub,"addSubFile");
  5705. if (NotFound!=findSubFile(sub->queryLogicalName()))
  5706. throw MakeStringException(-1,"addSubFile: File %s is already a subfile of %s", sub->queryLogicalName(),queryLogicalName());
  5707. }
  5708. virtual void validate() override
  5709. {
  5710. unsigned numSubfiles = root->getPropInt("@numsubfiles",0);
  5711. if (numSubfiles)
  5712. {
  5713. Owned<IPropertyTreeIterator> treeIter = root->getElements("SubFile");
  5714. unsigned subFileCount = 0;
  5715. ForEach(*treeIter)
  5716. {
  5717. IPropertyTree & st = treeIter->query();
  5718. StringBuffer subfilename;
  5719. st.getProp("@name", subfilename);
  5720. if (!parent->exists(subfilename.str(), NULL))
  5721. throw MakeStringException(-1, "Logical subfile '%s' doesn't exists!", subfilename.str());
  5722. if (!parent->isSuperFile(subfilename.str()))
  5723. if (!parent->existsPhysical(subfilename.str(), NULL))
  5724. {
  5725. const char * logicalName = queryLogicalName();
  5726. throw MakeStringException(-1, "Some physical parts do not exists, for logical file : %s",(isEmptyString(logicalName) ? "[unattached]" : logicalName));
  5727. }
  5728. subFileCount++;
  5729. }
  5730. if (numSubfiles != subFileCount)
  5731. throw MakeStringException(-1, "The value of @numsubfiles (%d) is not equal to the number of SubFile items (%d)!",numSubfiles, subFileCount);
  5732. }
  5733. }
  5734. virtual bool isRestrictedAccess() override
  5735. {
  5736. // This ensures restriction applies even if superfile is unrestricted but subfiles are.
  5737. // However, this also means that the superfile will show as "Restricted" in ECL Watch and whenever the user tries to unset the flag
  5738. // it will appear to reset to Restricted.
  5739. return containsRestrictedSubfile;
  5740. }
  5741. private:
  5742. void doAddSubFile(IDistributedFile *_sub,bool before,const char *other,IDistributedFileTransactionExt *transaction) // takes ownership of sub
  5743. {
  5744. Owned<IDistributedFile> sub = _sub;
  5745. validateAddSubFile(sub); // shouldn't really be necessary, was validated in transaction before here
  5746. unsigned pos;
  5747. if (other&&*other) {
  5748. pos = findSubFileOrd(other);
  5749. if (pos==NotFound)
  5750. pos = findSubFile(other);
  5751. if (pos==NotFound)
  5752. pos = before?0:subfiles.ordinality();
  5753. else if (!before&&(pos<subfiles.ordinality()))
  5754. pos++;
  5755. }
  5756. else
  5757. pos = before?0:subfiles.ordinality();
  5758. if (pos > subfiles.ordinality())
  5759. throw MakeStringException(-1,"addSubFile: Insert position %d out of range for file %s in superfile %s", pos+1, sub->queryLogicalName(), queryLogicalName());
  5760. addItem(pos,sub.getClear()); // remove if failure TBD?
  5761. setModified();
  5762. updateFileAttrs();
  5763. linkSubFile(pos);
  5764. }
  5765. bool doRemoveSubFiles(IDistributedFileTransactionExt *transaction)
  5766. {
  5767. // have to be quite careful here
  5768. unsigned pos = subfiles.ordinality();
  5769. if (pos)
  5770. {
  5771. DistributedFilePropertyLock lock(this);
  5772. if (lock.needsReload())
  5773. loadSubFiles(transaction,1000*60*10);
  5774. pos = subfiles.ordinality();
  5775. if (pos)
  5776. {
  5777. do
  5778. {
  5779. pos--;
  5780. unlinkSubFile(pos);
  5781. removeItem(pos);
  5782. } while (pos);
  5783. setModified();
  5784. updateFileAttrs();
  5785. lock.unlock();
  5786. updateParentFileAttrs(transaction);
  5787. }
  5788. }
  5789. return true;
  5790. }
  5791. bool doRemoveSubFile(const char *subfile,
  5792. IDistributedFileTransactionExt *transaction)
  5793. {
  5794. // have to be quite careful here
  5795. unsigned pos=findSubFileOrd(subfile);
  5796. if ((pos==NotFound)||(pos>=subfiles.ordinality()))
  5797. pos = findSubFile(subfile);
  5798. if (pos==NotFound)
  5799. return false;
  5800. {
  5801. DistributedFilePropertyLock lock(this);
  5802. // don't reload subfiles here
  5803. pos=findSubFileOrd(subfile);
  5804. if ((pos==NotFound)||(pos>=subfiles.ordinality()))
  5805. pos = findSubFile(subfile);
  5806. if (pos==NotFound)
  5807. return false;
  5808. unlinkSubFile(pos);
  5809. removeItem(pos);
  5810. setModified();
  5811. updateFileAttrs();
  5812. }
  5813. updateParentFileAttrs(transaction);
  5814. return true;
  5815. }
  5816. bool doSwapSuperFile(IDistributedSuperFile *_file,
  5817. IDistributedFileTransactionExt *transaction)
  5818. {
  5819. assertex(transaction);
  5820. CDistributedSuperFile *file = QUERYINTERFACE(_file,CDistributedSuperFile);
  5821. if (!file)
  5822. return false;
  5823. // Cache names (so we can delete without problems)
  5824. StringArray subnames1;
  5825. StringArray subnames2;
  5826. for (unsigned i=0; i<this->numSubFiles(false); i++)
  5827. subnames1.append(querySubFile(i, false).queryLogicalName());
  5828. for (unsigned i=0; i<file->numSubFiles(false); i++)
  5829. subnames2.append(file->querySubFile(i, false).queryLogicalName());
  5830. // Delete all files
  5831. ForEachItemIn(d1,subnames1) {
  5832. Owned<IDistributedFile> sub = transaction->lookupFile(subnames1.item(d1));
  5833. if (!doRemoveSubFile(sub->queryLogicalName(), transaction))
  5834. return false;
  5835. }
  5836. ForEachItemIn(d2,subnames2) {
  5837. Owned<IDistributedFile> sub = transaction->lookupFile(subnames2.item(d2));
  5838. if (!file->doRemoveSubFile(sub->queryLogicalName(), transaction))
  5839. return false;
  5840. }
  5841. // Add files swapped
  5842. ForEachItemIn(a1,subnames1) {
  5843. Owned<IDistributedFile> sub = transaction->lookupFile(subnames1.item(a1));
  5844. file->doAddSubFile(LINK(sub), false, NULL, transaction);
  5845. }
  5846. ForEachItemIn(a2,subnames2) {
  5847. Owned<IDistributedFile> sub = transaction->lookupFile(subnames2.item(a2));
  5848. doAddSubFile(LINK(sub), false, NULL, transaction);
  5849. }
  5850. return true;
  5851. }
  5852. public:
  5853. virtual void addSubFile(const char * subfile,
  5854. bool before=false, // if true before other
  5855. const char *other=NULL, // in NULL add at end (before=false) or start(before=true)
  5856. bool addcontents=false,
  5857. IDistributedFileTransaction *transaction=NULL
  5858. ) override
  5859. {
  5860. CriticalBlock block (sect);
  5861. if (!subfile||!*subfile)
  5862. return;
  5863. checkModify("addSubFile");
  5864. partscache.kill();
  5865. // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
  5866. Linked<IDistributedFileTransactionExt> localtrans;
  5867. if (transaction)
  5868. {
  5869. IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
  5870. localtrans.set(_transaction);
  5871. }
  5872. else
  5873. localtrans.setown(new CDistributedFileTransaction(udesc, this));
  5874. localtrans->ensureFile(this);
  5875. if (addcontents)
  5876. {
  5877. localtrans->descend();
  5878. StringArray subs;
  5879. Owned<IDistributedSuperFile> sfile = localtrans->lookupSuperFile(subfile);
  5880. if (sfile)
  5881. {
  5882. Owned<IDistributedFileIterator> iter = sfile->getSubFileIterator();
  5883. ForEach(*iter)
  5884. subs.append(iter->query().queryLogicalName());
  5885. }
  5886. sfile.clear();
  5887. ForEachItemIn(i,subs)
  5888. addSubFile(subs.item(i),before,other,false,localtrans);
  5889. localtrans->ascend();
  5890. }
  5891. else
  5892. {
  5893. cAddSubFileAction *action = new cAddSubFileAction(queryLogicalName(),subfile,before,other);
  5894. localtrans->addAction(action); // takes ownership
  5895. }
  5896. localtrans->autoCommit();
  5897. }
  5898. virtual bool removeSubFile(const char *subfile, // if NULL removes all
  5899. bool remsub, // if true removes subfiles from DFS
  5900. bool remcontents, // if true, recurse super-files
  5901. IDistributedFileTransaction *transaction) override
  5902. {
  5903. CriticalBlock block (sect);
  5904. if (subfile&&!*subfile)
  5905. return false;
  5906. checkModify("removeSubFile");
  5907. partscache.kill();
  5908. // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
  5909. Linked<IDistributedFileTransactionExt> localtrans;
  5910. if (transaction)
  5911. {
  5912. IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
  5913. localtrans.set(_transaction);
  5914. }
  5915. else
  5916. localtrans.setown(new CDistributedFileTransaction(udesc, this));
  5917. // Make sure this file is in cache (reuse below)
  5918. localtrans->ensureFile(this);
  5919. // If recurring, traverse super-file subs (if super)
  5920. if (remcontents)
  5921. {
  5922. localtrans->descend();
  5923. CDfsLogicalFileName logicalname;
  5924. logicalname.set(subfile);
  5925. IDistributedFile *sub = querySubFileNamed(logicalname.get(),false);
  5926. if (!sub)
  5927. return false;
  5928. IDistributedSuperFile *sfile = sub->querySuperFile();
  5929. if (sfile)
  5930. {
  5931. Owned<IDistributedFileIterator> iter = sfile->getSubFileIterator(true);
  5932. bool ret = true;
  5933. StringArray toremove;
  5934. ForEach(*iter)
  5935. toremove.append(iter->query().queryLogicalName());
  5936. iter.clear();
  5937. ForEachItemIn(i,toremove)
  5938. {
  5939. if (!sfile->removeSubFile(toremove.item(i),remsub,false,localtrans))
  5940. ret = false;
  5941. }
  5942. if (!ret||!remsub)
  5943. return ret;
  5944. }
  5945. localtrans->ascend();
  5946. }
  5947. cRemoveSubFileAction *action = new cRemoveSubFileAction(queryLogicalName(),subfile,remsub);
  5948. localtrans->addAction(action); // takes ownership
  5949. localtrans->autoCommit();
  5950. // MORE - auto-commit will throw an exception, change this to void
  5951. return true;
  5952. }
  5953. virtual bool removeOwnedSubFiles(bool remsub, // if true removes subfiles from DFS
  5954. IDistributedFileTransaction *transaction) override
  5955. {
  5956. CriticalBlock block (sect);
  5957. checkModify("removeOwnedSubFiles");
  5958. partscache.kill();
  5959. // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
  5960. Linked<IDistributedFileTransactionExt> localtrans;
  5961. if (transaction)
  5962. {
  5963. IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
  5964. localtrans.set(_transaction);
  5965. }
  5966. else
  5967. localtrans.setown(new CDistributedFileTransaction(udesc, this));
  5968. // Make sure this file is in cache (reuse below)
  5969. localtrans->addFile(this);
  5970. cRemoveOwnedSubFilesAction *action = new cRemoveOwnedSubFilesAction(localtrans, queryLogicalName(), remsub);
  5971. localtrans->addAction(action); // takes ownership
  5972. localtrans->autoCommit();
  5973. // MORE - auto-commit will throw an exception, change this to void
  5974. return true;
  5975. }
  5976. virtual bool swapSuperFile( IDistributedSuperFile *_file,
  5977. IDistributedFileTransaction *transaction) override
  5978. {
  5979. CriticalBlock block (sect);
  5980. if (!_file)
  5981. return false;
  5982. checkModify("swapSuperFile");
  5983. partscache.kill();
  5984. // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
  5985. Linked<IDistributedFileTransactionExt> localtrans;
  5986. if (transaction)
  5987. {
  5988. IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
  5989. localtrans.set(_transaction);
  5990. }
  5991. else
  5992. localtrans.setown(new CDistributedFileTransaction(udesc, this));
  5993. // Make sure this file is in cache
  5994. localtrans->ensureFile(this);
  5995. cSwapFileAction *action = new cSwapFileAction(queryLogicalName(),_file->queryLogicalName());
  5996. localtrans->addAction(action); // takes ownership
  5997. localtrans->autoCommit();
  5998. return true;
  5999. }
  6000. virtual void savePartsAttr(bool force) override
  6001. {
  6002. }
  6003. void fillClustersCache()
  6004. {
  6005. if (clusterscache.ordinality()==0) {
  6006. StringBuffer name;
  6007. ForEachItemIn(i,subfiles) {
  6008. StringArray clusters;
  6009. IDistributedFile &f=subfiles.item(i);
  6010. unsigned nc = f.numClusters();
  6011. for(unsigned j=0;j<nc;j++) {
  6012. f.getClusterName(j,name.clear());
  6013. if (clusterscache.find(name.str())==NotFound) {
  6014. IClusterInfo &cluster = *createClusterInfo(name.str(),f.queryClusterGroup(j),f.queryPartDiskMapping(j),&queryNamedGroupStore());
  6015. clusterscache.append(cluster);
  6016. }
  6017. }
  6018. }
  6019. }
  6020. }
  6021. virtual unsigned getClusterNames(StringArray &clusters) override
  6022. {
  6023. CriticalBlock block (sect);
  6024. fillClustersCache();
  6025. return clusterscache.getNames(clusters);
  6026. }
  6027. virtual unsigned numClusters() override
  6028. {
  6029. CriticalBlock block (sect);
  6030. fillClustersCache();
  6031. return clusterscache.ordinality();
  6032. }
  6033. virtual unsigned findCluster(const char *clustername) override
  6034. {
  6035. CriticalBlock block (sect);
  6036. fillClustersCache();
  6037. return clusterscache.find(clustername);
  6038. }
  6039. virtual ClusterPartDiskMapSpec &queryPartDiskMapping(unsigned clusternum) override
  6040. {
  6041. CriticalBlock block (sect);
  6042. fillClustersCache();
  6043. return clusterscache.queryPartDiskMapping(clusternum);
  6044. }
  6045. virtual void updatePartDiskMapping(const char *clustername,const ClusterPartDiskMapSpec &spec) override
  6046. {
  6047. if (!clustername||!*clustername)
  6048. return;
  6049. CriticalBlock block (sect);
  6050. fillClustersCache();
  6051. ForEachItemIn(i,subfiles) {
  6052. IDistributedFile &f=subfiles.item(i);
  6053. f.updatePartDiskMapping(clustername,spec);
  6054. }
  6055. }
  6056. virtual IGroup *queryClusterGroup(unsigned clusternum) override
  6057. {
  6058. CriticalBlock block (sect);
  6059. fillClustersCache();
  6060. return clusterscache.queryGroup(clusternum);
  6061. }
  6062. virtual StringBuffer &getClusterGroupName(unsigned clusternum, StringBuffer &name) override
  6063. {
  6064. CriticalBlock block (sect);
  6065. fillClustersCache();
  6066. return clusterscache.item(clusternum).getGroupName(name, &queryNamedGroupStore());
  6067. }
  6068. virtual void addCluster(const char *clustername,const ClusterPartDiskMapSpec &mspec) override
  6069. {
  6070. if (!clustername||!*clustername)
  6071. return;
  6072. CriticalBlock block (sect);
  6073. clusterscache.clear();
  6074. subfiles.item(0).addCluster(clustername,mspec);
  6075. }
  6076. virtual bool removeCluster(const char *clustername) override
  6077. {
  6078. bool clusterRemoved=false;
  6079. CriticalBlock block (sect);
  6080. clusterscache.clear();
  6081. ForEachItemIn(i,subfiles) {
  6082. IDistributedFile &f=subfiles.item(i);
  6083. clusterRemoved |= f.removeCluster(clustername);
  6084. }
  6085. return clusterRemoved;
  6086. }
  6087. virtual void setPreferredClusters(const char *clusters) override
  6088. {
  6089. CriticalBlock block (sect);
  6090. clusterscache.clear();
  6091. ForEachItemIn(i,subfiles) {
  6092. IDistributedFile &f=subfiles.item(i);
  6093. f.setPreferredClusters(clusters);
  6094. }
  6095. }
  6096. virtual bool checkClusterCompatible(IFileDescriptor &fdesc, StringBuffer &err) override
  6097. {
  6098. CriticalBlock block (sect);
  6099. if (subfiles.ordinality()!=1) {
  6100. err.append("only singleton superfiles allowed");
  6101. return false;
  6102. }
  6103. ForEachItemIn(i,subfiles) {
  6104. IDistributedFile &f=subfiles.item(i);
  6105. if (!f.checkClusterCompatible(fdesc,err))
  6106. return false;
  6107. }
  6108. return true;
  6109. }
  6110. virtual void setSingleClusterOnly() override
  6111. {
  6112. CriticalBlock block (sect);
  6113. ForEachItemIn(i,subfiles) {
  6114. IDistributedFile &f=subfiles.item(i);
  6115. f.setSingleClusterOnly();
  6116. }
  6117. }
  6118. virtual void enqueueReplicate() override
  6119. {
  6120. CriticalBlock block (sect);
  6121. ForEachItemIn(i,subfiles) {
  6122. IDistributedFile &f=subfiles.item(i);
  6123. f.enqueueReplicate();
  6124. }
  6125. }
  6126. virtual bool getAccessedTime(CDateTime &dt) override
  6127. {
  6128. bool set=false;
  6129. CriticalBlock block (sect);
  6130. ForEachItemIn(i,subfiles) {
  6131. IDistributedFile &f=subfiles.item(i);
  6132. if (!set)
  6133. set = f.getAccessedTime(dt);
  6134. else {
  6135. CDateTime cmp;
  6136. if (f.getAccessedTime(cmp)) {
  6137. if (cmp.compare(dt)>0)
  6138. dt.set(cmp);
  6139. }
  6140. }
  6141. }
  6142. return set;
  6143. }
  6144. virtual void setAccessedTime(const CDateTime &dt) override
  6145. {
  6146. {
  6147. CriticalBlock block (sect);
  6148. ForEachItemIn(i,subfiles) {
  6149. IDistributedFile &f=subfiles.item(i);
  6150. f.setAccessedTime(dt);
  6151. }
  6152. }
  6153. }
  6154. virtual bool getSkewInfo(unsigned &maxSkew, unsigned &minSkew, unsigned &maxSkewPart, unsigned &minSkewPart, bool calculateIfMissing) override
  6155. {
  6156. return false;
  6157. }
  6158. };
  6159. // --------------------------------------------------------
  6160. void CDistributedFileTransaction::validateAddSubFile(IDistributedSuperFile *super, IDistributedFile *sub, const char *subName)
  6161. {
  6162. CTransactionFile *trackedSuper = lookupTrackedFile(super);
  6163. if (!trackedSuper)
  6164. return;
  6165. const char *superName = trackedSuper->queryName();
  6166. if (strcmp(subName, superName)==0)
  6167. throw MakeStringException(-1,"addSubFile: Cannot add file %s to itself", superName);
  6168. if (trackedSuper->numSubFiles())
  6169. {
  6170. CDistributedSuperFile *sf = dynamic_cast<CDistributedSuperFile *>(super);
  6171. sf->checkFormatAttr(sub, "addSubFile");
  6172. if (trackedSuper->find(subName, false))
  6173. throw MakeStringException(-1,"addSubFile: File %s is already a subfile of %s", subName, superName);
  6174. }
  6175. }
  6176. // --------------------------------------------------------
  6177. CDistributedFilePart::CDistributedFilePart(CDistributedFile &_parent,unsigned _part,IPartDescriptor *pd)
  6178. : parent(_parent)
  6179. {
  6180. partIndex = _part;
  6181. dirty = false;
  6182. if (pd) {
  6183. if (pd->isMulti())
  6184. IERRLOG("Multi filenames not supported in Dali DFS Part %d of %s",_part+1,_parent.queryLogicalName());
  6185. overridename.set(pd->queryOverrideName());
  6186. setAttr(*pd->getProperties());
  6187. }
  6188. else
  6189. IERRLOG("CDistributedFilePart::CDistributedFilePart no IPartDescriptor for part");
  6190. }
  6191. void CDistributedFilePart::Link(void) const
  6192. {
  6193. parent.Link();
  6194. CInterface::Link();
  6195. }
  6196. bool CDistributedFilePart::Release(void) const
  6197. {
  6198. parent.Release();
  6199. return CInterface::Release();
  6200. }
  6201. offset_t CDistributedFilePart::getSize(bool checkCompressed)
  6202. {
  6203. offset_t ret = (offset_t)-1;
  6204. StringBuffer firstname;
  6205. bool compressed = ::isCompressed(parent.queryAttributes());
  6206. unsigned nc=parent.numCopies(partIndex);
  6207. for (unsigned copy=0;copy<nc;copy++)
  6208. {
  6209. RemoteFilename rfn;
  6210. try
  6211. {
  6212. Owned<IFile> partfile = createIFile(getFilename(rfn,copy));
  6213. if (checkCompressed && compressed)
  6214. {
  6215. Owned<ICompressedFileIO> compressedIO = createCompressedFileReader(partfile);
  6216. if (compressedIO)
  6217. ret = compressedIO->size();
  6218. }
  6219. else
  6220. ret = partfile->size();
  6221. if (ret!=(offset_t)-1)
  6222. return ret;
  6223. }
  6224. catch (IException *e)
  6225. {
  6226. StringBuffer s("CDistributedFilePart::getSize ");
  6227. rfn.getRemotePath(s);
  6228. EXCLOG(e, s.str());
  6229. e->Release();
  6230. }
  6231. if (copy==0)
  6232. rfn.getRemotePath(firstname);
  6233. }
  6234. throw new CDFS_Exception(DFSERR_CannotFindPartFileSize,firstname.str());;
  6235. }
  6236. StringBuffer & CDistributedFilePart::getPartName(StringBuffer &partname)
  6237. {
  6238. if (!overridename.isEmpty()) {
  6239. if (isSpecialPath(overridename)) {
  6240. // bit of a kludge
  6241. if (isPathSepChar(*overridename)&&partname.length()&&isPathSepChar(partname.charAt(partname.length()-1)))
  6242. partname.setLength(partname.length()-1);
  6243. return partname.append(overridename);
  6244. }
  6245. return partname.append(pathTail(overridename));
  6246. }
  6247. const char *mask=parent.queryPartMask();
  6248. if (!mask||!*mask) {
  6249. const char *err ="CDistributedFilePart::getPartName cannot determine part name (no mask)";
  6250. IERRLOG("%s", err);
  6251. throw MakeStringExceptionDirect(-1, err);
  6252. }
  6253. expandMask(partname,mask,partIndex,parent.numParts());
  6254. return partname;
  6255. }
  6256. unsigned CDistributedFilePart::bestCopyNum(const IpAddress &ip,unsigned rel)
  6257. {
  6258. unsigned n = numCopies();
  6259. unsigned *dist = new unsigned[n];
  6260. unsigned *idx = new unsigned[n];
  6261. for (unsigned c=0;c<n;c++) {
  6262. dist[c] = ip.ipdistance(queryNode(c)->endpoint());
  6263. idx[c] = c;
  6264. }
  6265. if (rel>=n)
  6266. rel = n-1;
  6267. // do bubble sort as not that many!
  6268. for (unsigned i=0; i<n-1; i++)
  6269. for (unsigned j=0; j<n-1-i; j++)
  6270. if (dist[idx[j+1]] < dist[idx[j]]) {
  6271. unsigned t = idx[j];
  6272. idx[j] = idx[j+1];
  6273. idx[j+1] = t;
  6274. }
  6275. unsigned ret = idx[rel];
  6276. delete [] idx;
  6277. delete [] dist;
  6278. return ret;
  6279. }
  6280. unsigned CDistributedFilePart::copyClusterNum(unsigned copy,unsigned *replicate)
  6281. {
  6282. return parent.copyClusterNum(partIndex,copy,replicate);
  6283. }
  6284. StringBuffer &CDistributedFilePart::getPartDirectory(StringBuffer &ret,unsigned copy)
  6285. {
  6286. const char *defdir = parent.queryDefaultDir();
  6287. StringBuffer dir;
  6288. const char *pn;
  6289. if (overridename.isEmpty())
  6290. pn = parent.queryPartMask();
  6291. else {
  6292. pn = overridename.get();
  6293. if (isSpecialPath(pn)) // its a query
  6294. return ret; // ret.append('/'); // not sure if really need '/' here
  6295. }
  6296. if (pn&&*pn) {
  6297. StringBuffer odir;
  6298. splitDirTail(pn,odir);
  6299. if (odir.length()) {
  6300. if (isAbsolutePath(pn))
  6301. dir.append(odir);
  6302. else if (defdir&&*defdir)
  6303. addPathSepChar(dir.append(defdir)).append(odir);
  6304. }
  6305. else
  6306. dir.append(defdir);
  6307. }
  6308. if (dir.length()==0)
  6309. IERRLOG("IDistributedFilePart::getPartDirectory unable to determine part directory");
  6310. else {
  6311. parent.adjustClusterDir(partIndex,copy,dir);
  6312. ret.append(dir);
  6313. }
  6314. return ret;
  6315. }
  6316. unsigned CDistributedFilePart::numCopies()
  6317. {
  6318. return parent.numCopies(partIndex);
  6319. }
  6320. INode *CDistributedFilePart::queryNode(unsigned copy)
  6321. {
  6322. return parent.queryNode(partIndex,copy);
  6323. }
  6324. unsigned CDistributedFilePart::queryDrive(unsigned copy)
  6325. {
  6326. return parent.queryDrive(partIndex,copy,parent.directory);
  6327. }
  6328. bool CDistributedFilePart::isHost(unsigned copy)
  6329. {
  6330. return (queryNode(copy)->isHost());
  6331. }
  6332. IPropertyTree &CDistributedFilePart::queryAttributes()
  6333. {
  6334. CriticalBlock block (sect); // avoid nested blocks
  6335. if (attr)
  6336. return *attr;
  6337. DBGLOG("CDistributedFilePart::queryAttributes missing part attributes");
  6338. attr.setown(getEmptyAttr());
  6339. return *attr;
  6340. }
  6341. RemoteFilename &CDistributedFilePart::getFilename(RemoteFilename &ret,unsigned copy)
  6342. {
  6343. // this is probably not as efficient as could be
  6344. StringBuffer fullpath;
  6345. getPartDirectory(fullpath,copy);
  6346. addPathSepChar(fullpath);
  6347. getPartName(fullpath);
  6348. SocketEndpoint ep;
  6349. INode *node=queryNode(copy);
  6350. if (node)
  6351. ep = node->endpoint();
  6352. ret.setPath(ep,fullpath.str());
  6353. return ret;
  6354. }
  6355. bool CDistributedFilePart::getCrc(unsigned &crc)
  6356. {
  6357. return getCrcFromPartProps(parent.queryAttributes(),queryAttributes(), crc);
  6358. }
  6359. unsigned CDistributedFilePart::getPhysicalCrc()
  6360. {
  6361. StringBuffer firstname;
  6362. unsigned nc=parent.numCopies(partIndex);
  6363. for (unsigned copy=0;copy<nc;copy++) {
  6364. RemoteFilename rfn;
  6365. try {
  6366. Owned<IFile> partfile = createIFile(getFilename(rfn,copy));
  6367. if (partfile&&partfile->exists())
  6368. return partfile->getCRC();
  6369. }
  6370. catch (IException *e)
  6371. {
  6372. StringBuffer s("CDistributedFilePart::getPhysicalCrc ");
  6373. rfn.getRemotePath(s);
  6374. EXCLOG(e, s.str());
  6375. e->Release();
  6376. }
  6377. if (copy==0)
  6378. rfn.getRemotePath(firstname);
  6379. }
  6380. IDFS_Exception *e = new CDFS_Exception(DFSERR_CannotFindPartFileCrc,firstname.str());
  6381. throw e;
  6382. }
  6383. // TODO: Create DistributedFilePropertyLock for parts
  6384. bool CDistributedFilePart::lockProperties(unsigned timeoutms)
  6385. {
  6386. dirty = true;
  6387. return parent.lockProperties(timeoutms);
  6388. }
  6389. // TODO: Create DistributedFilePropertyLock for parts
  6390. void CDistributedFilePart::unlockProperties(DFTransactionState state=TAS_NONE)
  6391. {
  6392. parent.unlockProperties(state);
  6393. }
  6394. offset_t CDistributedFilePart::getFileSize(bool allowphysical,bool forcephysical)
  6395. {
  6396. offset_t ret = (offset_t)((forcephysical&&allowphysical)?-1:queryAttributes().getPropInt64("@size", -1));
  6397. if (allowphysical&&(ret==(offset_t)-1))
  6398. ret = getSize(true);
  6399. return ret;
  6400. }
  6401. offset_t CDistributedFilePart::getDiskSize(bool allowphysical,bool forcephysical)
  6402. {
  6403. if (!::isCompressed(parent.queryAttributes()))
  6404. return getFileSize(allowphysical, forcephysical);
  6405. if (forcephysical && allowphysical)
  6406. return getSize(false); // i.e. only if force, because all compressed should have @compressedSize attribute
  6407. // NB: compressSize is disk size
  6408. return queryAttributes().getPropInt64("@compressedSize", -1);
  6409. }
  6410. bool CDistributedFilePart::getModifiedTime(bool allowphysical,bool forcephysical, CDateTime &dt)
  6411. {
  6412. StringBuffer s;
  6413. if (!forcephysical&&queryAttributes().getProp("@modified", s)) {
  6414. dt.setString(s.str());
  6415. if (!dt.isNull())
  6416. return true;
  6417. }
  6418. if (allowphysical) {
  6419. unsigned nc=parent.numCopies(partIndex);
  6420. for (unsigned copy=0;copy<nc;copy++) {
  6421. RemoteFilename rfn;
  6422. try {
  6423. Owned<IFile> partfile = createIFile(getFilename(rfn,copy));
  6424. if (partfile->getTime(NULL,&dt,NULL))
  6425. return true;
  6426. }
  6427. catch (IException *e)
  6428. {
  6429. StringBuffer s("CDistributedFilePart::getFileTime ");
  6430. rfn.getRemotePath(s);
  6431. EXCLOG(e, s.str());
  6432. e->Release();
  6433. }
  6434. }
  6435. }
  6436. return false;
  6437. }
  6438. unsigned getSuperFileSubs(IDistributedSuperFile *super, IArrayOf<IDistributedFile> &subFiles, bool superSub)
  6439. {
  6440. unsigned numSubs = super->numSubFiles(superSub);
  6441. for (unsigned s=0; s<numSubs; s++)
  6442. {
  6443. IDistributedFile &subFile = super->querySubFile(s, superSub);
  6444. subFiles.append(*LINK(&subFile));
  6445. }
  6446. return numSubs;
  6447. }
  6448. // --------------------------------------------------------
  6449. class CNamedGroupIterator: implements INamedGroupIterator, public CInterface
  6450. {
  6451. Owned<IPropertyTreeIterator> pe;
  6452. Linked<IRemoteConnection> conn;
  6453. Linked<IGroup> matchgroup;
  6454. bool exactmatch;
  6455. bool match();
  6456. public:
  6457. IMPLEMENT_IINTERFACE;
  6458. CNamedGroupIterator(IRemoteConnection *_conn,IGroup *_matchgroup=NULL,bool _exactmatch=false)
  6459. : conn(_conn), matchgroup(_matchgroup)
  6460. {
  6461. exactmatch = _exactmatch;
  6462. pe.setown(conn->queryRoot()->getElements("Group"));
  6463. }
  6464. bool first()
  6465. {
  6466. if (!pe->first())
  6467. return false;
  6468. if (match())
  6469. return true;
  6470. return next();
  6471. }
  6472. bool next()
  6473. {
  6474. while (pe->next())
  6475. if (match())
  6476. return true;
  6477. return false;
  6478. }
  6479. bool isValid()
  6480. {
  6481. return pe->isValid();
  6482. }
  6483. StringBuffer &get(StringBuffer &name)
  6484. {
  6485. pe->query().getProp("@name",name);
  6486. return name;
  6487. }
  6488. StringBuffer &getdir(StringBuffer &dir)
  6489. {
  6490. pe->query().getProp("@dir",dir);
  6491. return dir;
  6492. }
  6493. bool isCluster()
  6494. {
  6495. return pe->query().getPropBool("@cluster");
  6496. }
  6497. };
  6498. // --------------------------------------------------------
  6499. #define GROUP_CACHE_INTERVAL (1000*60)
  6500. #define GROUP_EXCEPTION_CACHE_INTERVAL (1000*60*10)
  6501. static GroupType translateGroupType(const char *groupType)
  6502. {
  6503. if (!groupType)
  6504. return grp_unknown;
  6505. if (strieq(groupType, "Thor"))
  6506. return grp_thor;
  6507. else if (strieq(groupType, "Roxie"))
  6508. return grp_roxie;
  6509. else if (strieq(groupType, "hthor"))
  6510. return grp_hthor;
  6511. else
  6512. return grp_unknown;
  6513. }
  6514. class CNamedGroupCacheEntry: public CInterface
  6515. {
  6516. public:
  6517. Linked<IGroup> group;
  6518. StringAttr name;
  6519. StringAttr groupDir;
  6520. GroupType groupType;
  6521. Linked<IException> exception;
  6522. CNamedGroupCacheEntry(IGroup *_group, const char *_name, const char *_dir, GroupType _groupType)
  6523. : group(_group), name(_name), groupDir(_dir), groupType(_groupType)
  6524. {
  6525. cachedtime = msTick();
  6526. }
  6527. CNamedGroupCacheEntry(IException *_exception, const char *_name)
  6528. : exception(_exception), name(_name), groupType(grp_unknown)
  6529. {
  6530. cachedtime = msTick();
  6531. }
  6532. bool expired(unsigned timeNow)
  6533. {
  6534. if (exception)
  6535. return timeNow-cachedtime > GROUP_EXCEPTION_CACHE_INTERVAL;
  6536. else
  6537. return timeNow-cachedtime > GROUP_CACHE_INTERVAL;
  6538. }
  6539. protected:
  6540. unsigned cachedtime;
  6541. };
  6542. static unsigned loadGroup(const IPropertyTree *groupTree, SocketEndpointArray &epa, GroupType *type, StringAttr *groupDir)
  6543. {
  6544. if (type)
  6545. *type = translateGroupType(groupTree->queryProp("@kind"));
  6546. if (groupDir)
  6547. {
  6548. groupDir->set(groupTree->queryProp("@dir"));
  6549. if (groupDir->isEmpty())
  6550. groupDir->set(queryBaseDirectory(*type));
  6551. }
  6552. Owned<IPropertyTreeIterator> pe = groupTree->getElements("Node");
  6553. ForEach(*pe)
  6554. {
  6555. const char *host = pe->query().queryProp("@ip");
  6556. SocketEndpoint ep(host);
  6557. if (ep.isNull())
  6558. {
  6559. IWARNLOG("loadGroup: failed to resolve host '%s'", host);
  6560. return 0;
  6561. }
  6562. epa.append(ep);
  6563. }
  6564. return epa.ordinality();
  6565. }
  6566. class CNamedGroupStore: implements INamedGroupStore, public CInterface
  6567. {
  6568. CriticalSection cachesect;
  6569. CIArrayOf<CNamedGroupCacheEntry> cache;
  6570. unsigned defaultTimeout;
  6571. unsigned defaultRemoteTimeout;
  6572. public:
  6573. IMPLEMENT_IINTERFACE;
  6574. CNamedGroupStore()
  6575. {
  6576. defaultTimeout = INFINITE;
  6577. defaultRemoteTimeout = FOREIGN_DALI_TIMEOUT;
  6578. }
  6579. IGroup *dolookup(const char *logicalgroupname,IRemoteConnection *conn, StringBuffer *dirret, GroupType &groupType)
  6580. {
  6581. SocketEndpointArray epa;
  6582. StringBuffer gname(logicalgroupname);
  6583. gname.trim();
  6584. groupType = grp_unknown;
  6585. if (!gname.length())
  6586. return nullptr;
  6587. gname.toLowerCase();
  6588. logicalgroupname = gname.str();
  6589. bool isiprange = (*logicalgroupname!=0);
  6590. for (const char *s1=logicalgroupname;*s1;s1++)
  6591. if (isalpha(*s1)) {
  6592. isiprange = false;
  6593. break;
  6594. }
  6595. if (isiprange) {
  6596. // allow IP or IP list instead of group name
  6597. // I don't think this is a security problem as groups not checked
  6598. // NB ports not allowed here
  6599. char *buf = strdup(logicalgroupname);
  6600. char *s = buf;
  6601. while (*s) {
  6602. char *next = strchr(s,',');
  6603. if (next)
  6604. *next = 0;
  6605. SocketEndpoint ep;
  6606. unsigned n = ep.ipsetrange(s);
  6607. for (unsigned i=0;i<n;i++) {
  6608. if (ep.isNull()) { // failed
  6609. epa.kill();
  6610. break;
  6611. }
  6612. epa.append(ep);
  6613. ep.ipincrement(1);
  6614. }
  6615. if (!next)
  6616. break;
  6617. s = next+1;
  6618. }
  6619. free(buf);
  6620. if (epa.ordinality())
  6621. return createIGroup(epa);
  6622. }
  6623. StringBuffer range;
  6624. StringBuffer parent;
  6625. if (decodeChildGroupName(gname.str(),parent,range)) {
  6626. gname.clear().append(parent);
  6627. logicalgroupname = gname.str();
  6628. }
  6629. StringAttr groupdir;
  6630. GroupType type = grp_unknown;
  6631. bool cached = false;
  6632. unsigned timeNow = msTick();
  6633. {
  6634. CriticalBlock block(cachesect);
  6635. ForEachItemInRev(idx, cache)
  6636. {
  6637. CNamedGroupCacheEntry &entry = cache.item(idx);
  6638. if (entry.expired(timeNow))
  6639. {
  6640. cache.remove(idx);
  6641. }
  6642. else if (strcmp(gname.str(),entry.name.get())==0)
  6643. {
  6644. cached = true;
  6645. if (entry.exception)
  6646. throw LINK(entry.exception);
  6647. if (!entry.group) //cache entry of a deleted groupname
  6648. return nullptr;
  6649. if (range.length()==0)
  6650. {
  6651. if (dirret)
  6652. dirret->append(entry.groupDir);
  6653. groupType = entry.groupType;
  6654. return entry.group.getLink();
  6655. }
  6656. // there is a range so copy to epa
  6657. entry.group->getSocketEndpoints(epa);
  6658. groupdir.set(entry.groupDir);
  6659. type = entry.groupType;
  6660. break;
  6661. }
  6662. }
  6663. }
  6664. try
  6665. {
  6666. if ((gname.length()>9)&&(memcmp(logicalgroupname,"foreign::",9)==0))
  6667. {
  6668. StringBuffer eps;
  6669. const char *s = logicalgroupname+9;
  6670. while (*s&&((*s!=':')||(s[1]!=':')))
  6671. eps.append(*(s++));
  6672. if (*s)
  6673. {
  6674. s+=2;
  6675. if (*s)
  6676. {
  6677. Owned<INode> dali = createINode(eps.str());
  6678. if (!dali || !getRemoteGroup(dali, s, defaultRemoteTimeout, groupdir, type, epa))
  6679. {
  6680. if (!cached)
  6681. {
  6682. CriticalBlock block(cachesect);
  6683. cache.append(*new CNamedGroupCacheEntry(NULL, gname, NULL, grp_unknown));
  6684. }
  6685. return nullptr;
  6686. }
  6687. }
  6688. }
  6689. }
  6690. else if (epa.ordinality()==0) {
  6691. struct sLock
  6692. {
  6693. sLock() { lock = NULL; };
  6694. ~sLock() { delete lock; };
  6695. CConnectLock *lock;
  6696. } slock;
  6697. if (!conn)
  6698. {
  6699. slock.lock = new CConnectLock("CNamedGroup::lookup", SDS_GROUPSTORE_ROOT, false, false, false, defaultTimeout);
  6700. conn = slock.lock->conn;
  6701. if (!conn)
  6702. {
  6703. if (!cached)
  6704. {
  6705. CriticalBlock block(cachesect);
  6706. cache.append(*new CNamedGroupCacheEntry(NULL, gname, NULL, grp_unknown));
  6707. }
  6708. return nullptr;
  6709. }
  6710. }
  6711. Owned<IPropertyTree> groupTree = getNamedPropTree(conn->queryRoot(), "Group", "@name", gname.str(), true);
  6712. if (!groupTree || !loadGroup(groupTree, epa, &type, &groupdir))
  6713. return nullptr;
  6714. }
  6715. }
  6716. catch (IException *E)
  6717. {
  6718. // cache the exception
  6719. CriticalBlock block(cachesect);
  6720. cache.append(*new CNamedGroupCacheEntry(E, gname));
  6721. throw;
  6722. }
  6723. Owned<IGroup> ret = createIGroup(epa);
  6724. if (!cached)
  6725. {
  6726. CriticalBlock block(cachesect);
  6727. cache.append(*new CNamedGroupCacheEntry(ret, gname, groupdir, type));
  6728. }
  6729. if (range.length())
  6730. {
  6731. SocketEndpointArray epar;
  6732. const char *s = range.str();
  6733. while (*s)
  6734. {
  6735. unsigned start = 0;
  6736. while (isdigit(*s))
  6737. {
  6738. start = start*10+*s-'0';
  6739. s++;
  6740. }
  6741. if (!start)
  6742. break;
  6743. unsigned end;
  6744. if (*s=='-')
  6745. {
  6746. s++;
  6747. end = 0;
  6748. while (isdigit(*s))
  6749. {
  6750. end = end*10+*s-'0';
  6751. s++;
  6752. }
  6753. if (!end)
  6754. end = epa.ordinality();
  6755. }
  6756. else
  6757. end = start;
  6758. if ((start>epa.ordinality())||(end>epa.ordinality()))
  6759. {
  6760. s = range.str();
  6761. break;
  6762. }
  6763. if (*s==',')
  6764. s++;
  6765. unsigned i=start-1;
  6766. do { // allow 400-1 etc
  6767. i++;
  6768. if (i>epa.ordinality())
  6769. i = 1;
  6770. epar.append(epa.item(i-1));
  6771. } while (i!=end);
  6772. }
  6773. if (*s)
  6774. throw MakeStringException(-1,"Invalid group range %s",range.str());
  6775. ret.setown(createIGroup(epar));
  6776. }
  6777. if (dirret)
  6778. dirret->append(groupdir);
  6779. groupType = type;
  6780. return ret.getClear();
  6781. }
  6782. IPropertyTree *doAddHosts(CConnectLock &connlock, const char *name, const std::vector<std::string> &hosts, bool cluster, const char *dir)
  6783. {
  6784. IPropertyTree *val = createPTree("Group");
  6785. val->setProp("@name",name);
  6786. if (cluster)
  6787. val->setPropBool("@cluster", true);
  6788. if (dir)
  6789. val->setProp("@dir",dir);
  6790. for (auto &hostOrIpRange: hosts)
  6791. {
  6792. SocketEndpointArray epa;
  6793. epa.fromText(hostOrIpRange.c_str(), 0);
  6794. if (epa.ordinality()>1)
  6795. {
  6796. ForEachItemIn(e, epa)
  6797. {
  6798. StringBuffer ipStr;
  6799. epa.item(e).getIpText(ipStr);
  6800. IPropertyTree *n = val->addPropTree("Node");
  6801. n->setProp("@ip", ipStr);
  6802. }
  6803. }
  6804. else
  6805. {
  6806. IPropertyTree *n = val->addPropTree("Node");
  6807. n->setProp("@ip", hostOrIpRange.c_str());
  6808. }
  6809. }
  6810. val = connlock.conn->queryRoot()->addPropTree("Group",val);
  6811. return LINK(val);
  6812. }
  6813. void doadd(CConnectLock &connlock,const char *name,IGroup *group,bool cluster,const char *dir)
  6814. {
  6815. if (!group)
  6816. return;
  6817. Owned<INodeIterator> gi = group->getIterator();
  6818. if (gi->first())
  6819. {
  6820. StringBuffer ipStr;
  6821. std::vector<std::string> ips;
  6822. while (true)
  6823. {
  6824. gi->query().endpoint().getIpText(ipStr.clear());
  6825. ips.push_back(ipStr.str());
  6826. if (!gi->next())
  6827. break;
  6828. }
  6829. ::Release(doAddHosts(connlock, name, ips, cluster, dir));
  6830. }
  6831. }
  6832. void addGroup(const char *logicalgroupname, const std::vector<std::string> &hosts, bool cluster, const char *dir, GroupType groupType, bool overwrite)
  6833. {
  6834. dbgassertex(hosts.size());
  6835. StringBuffer name(logicalgroupname);
  6836. name.toLowerCase();
  6837. name.trim();
  6838. StringBuffer prop;
  6839. prop.appendf("Group[@name=\"%s\"]",name.str());
  6840. CConnectLock connlock("CNamedGroup::add", SDS_GROUPSTORE_ROOT, true, false, false, defaultTimeout);
  6841. if (!overwrite && connlock.conn->queryRoot()->hasProp(prop.str()))
  6842. return;
  6843. connlock.conn->queryRoot()->removeProp(prop.str());
  6844. if (0 == hosts.size())
  6845. return;
  6846. Owned<IPropertyTree> groupTree = doAddHosts(connlock, name.str(), hosts, cluster, dir);
  6847. SocketEndpointArray eps;
  6848. if (!loadGroup(groupTree, eps, nullptr, nullptr))
  6849. {
  6850. IWARNLOG("CNamedGroupStore.add: failed to add group '%s', due to unresolved hosts", name.str());
  6851. return;
  6852. }
  6853. Owned<IGroup> group = createIGroup(eps);
  6854. {
  6855. CriticalBlock block(cachesect);
  6856. cache.kill();
  6857. cache.append(*new CNamedGroupCacheEntry(group, name, dir, groupType));
  6858. }
  6859. }
  6860. virtual void addUnique(IGroup *group,StringBuffer &lname, const char *dir) override
  6861. {
  6862. if (group->ordinality()==1)
  6863. {
  6864. group->getText(lname);
  6865. return;
  6866. }
  6867. CConnectLock connlock("CNamedGroup::addUnique", SDS_GROUPSTORE_ROOT, true, false, false, defaultTimeout);
  6868. StringBuffer name;
  6869. StringBuffer prop;
  6870. unsigned scale = 16;
  6871. for (;;) {
  6872. name.clear();
  6873. if (lname.length()) { // try suggested name
  6874. name.append(lname);
  6875. name.toLowerCase();
  6876. lname.clear();
  6877. }
  6878. else
  6879. name.append("__anon").append(getRandom()%scale);
  6880. prop.clear().appendf("Group[@name=\"%s\"]",name.str());
  6881. if (!connlock.conn->queryRoot()->hasProp(prop.str()))
  6882. break;
  6883. scale*=2;
  6884. }
  6885. doadd(connlock,name.str(),group,false,dir);
  6886. lname.append(name);
  6887. }
  6888. IGroup *lookup(const char *logicalgroupname, StringBuffer &dir, GroupType &groupType)
  6889. {
  6890. return dolookup(logicalgroupname, NULL, &dir, groupType);
  6891. }
  6892. virtual IGroup *lookup(const char *logicalgroupname) override
  6893. {
  6894. GroupType dummy;
  6895. return dolookup(logicalgroupname, NULL, NULL, dummy);
  6896. }
  6897. virtual INamedGroupIterator *getIterator() override
  6898. {
  6899. CConnectLock connlock("CNamedGroup::getIterator", SDS_GROUPSTORE_ROOT, false, true, false, defaultTimeout);
  6900. return new CNamedGroupIterator(connlock.conn); // links connection
  6901. }
  6902. virtual INamedGroupIterator *getIterator(IGroup *match,bool exact) override
  6903. {
  6904. CConnectLock connlock("CNamedGroup::getIterator", SDS_GROUPSTORE_ROOT, false, false, false, defaultTimeout);
  6905. return new CNamedGroupIterator(connlock.conn,match,exact); // links connection
  6906. }
  6907. virtual void add(const char *logicalgroupname, const std::vector<std::string> &hosts, bool cluster, const char *dir, GroupType groupType) override
  6908. {
  6909. addGroup(logicalgroupname, hosts, cluster, dir, groupType, true);
  6910. }
  6911. virtual void ensure(const char *logicalgroupname, const std::vector<std::string> &hosts, bool cluster, const char *dir, GroupType groupType) override
  6912. {
  6913. addGroup(logicalgroupname, hosts, cluster, dir, groupType, false);
  6914. }
  6915. virtual void ensureNasGroup(size32_t size) override
  6916. {
  6917. std::vector<std::string> hosts;
  6918. for (unsigned n=0; n<size; n++)
  6919. hosts.push_back("localhost");
  6920. VStringBuffer nasGroupName("__nas__%u", size);
  6921. ensure(nasGroupName, hosts, false, nullptr, grp_unknown);
  6922. }
  6923. virtual StringBuffer &getNasGroupName(StringBuffer &groupName, size32_t size) const override
  6924. {
  6925. return groupName.append("__nas__").append(size);
  6926. }
  6927. virtual unsigned removeNode(const char *logicalgroupname, const char *nodeToRemove) override
  6928. {
  6929. StringBuffer name(logicalgroupname);
  6930. name.toLowerCase();
  6931. name.trim();
  6932. StringBuffer prop;
  6933. prop.appendf("Group[@name=\"%s\"]",name.str());
  6934. CConnectLock connlock("CNamedGroup::add", SDS_GROUPSTORE_ROOT, true, false, false, defaultTimeout);
  6935. Owned<IPropertyTree> groupTree = getNamedPropTree(connlock.conn->queryRoot(), "Group", "@name", name, true);
  6936. if (!groupTree)
  6937. return 0;
  6938. SocketEndpointArray epa;
  6939. if (!loadGroup(groupTree, epa, nullptr, nullptr))
  6940. return 0;
  6941. unsigned numNodes = epa.ordinality();
  6942. Owned<IGroup> group = createIGroup(epa);
  6943. SocketEndpoint removeEp(nodeToRemove);
  6944. unsigned removeCount = 0;
  6945. while (removeCount != numNodes)
  6946. {
  6947. rank_t r = group->rank(removeEp);
  6948. if (RANK_NULL == r)
  6949. break;
  6950. group.setown(group->remove(r));
  6951. VStringBuffer xpath("Node[%u]", r+1); // 1 based
  6952. verifyex(groupTree->removeProp(xpath)); // remove corresponding position in IPT (in Dali)
  6953. ++removeCount;
  6954. }
  6955. if (0 == removeCount)
  6956. return 0;
  6957. const char *groupDir = groupTree->queryProp("@dir");
  6958. GroupType groupType = translateGroupType(groupTree->queryProp("@kind"));
  6959. CriticalBlock block(cachesect);
  6960. cache.kill();
  6961. cache.append(*new CNamedGroupCacheEntry(group, name, groupDir, groupType));
  6962. return removeCount;
  6963. }
  6964. virtual void remove(const char *logicalgroupname) override
  6965. {
  6966. StringBuffer name(logicalgroupname);
  6967. name.toLowerCase();
  6968. name.trim();
  6969. StringBuffer prop;
  6970. prop.appendf("Group[@name=\"%s\"]",name.str());
  6971. CConnectLock connlock("CNamedGroup::add", SDS_GROUPSTORE_ROOT, true, false, false, defaultTimeout);
  6972. connlock.conn->queryRoot()->removeProp(prop.str());
  6973. {
  6974. CriticalBlock block(cachesect);
  6975. cache.kill();
  6976. }
  6977. }
  6978. virtual bool find(IGroup *grp, StringBuffer &gname, bool add) override
  6979. {
  6980. // gname on entry is suggested name for add if set
  6981. unsigned n = grp->ordinality();
  6982. if (!grp||!n)
  6983. return false;
  6984. Owned<INamedGroupIterator> iter=getIterator(grp,(n==1)); // one node clusters must be exact match
  6985. StringAttr bestname;
  6986. StringBuffer name;
  6987. ForEach(*iter) {
  6988. bool iscluster = iter->isCluster();
  6989. if (iscluster||(bestname.isEmpty())) {
  6990. iter->get(name.clear());
  6991. if (name.length()) {
  6992. bestname.set(name);
  6993. if (iscluster)
  6994. break;
  6995. }
  6996. }
  6997. }
  6998. iter.clear();
  6999. if (bestname.isEmpty()) {
  7000. if (add||(n==1)) // single-nodes always have implicit group of IP
  7001. addUnique(grp,gname,NULL);
  7002. return false;
  7003. }
  7004. gname.clear().append(bestname);
  7005. return true;
  7006. }
  7007. virtual void swapNode(const char *fromHost, const char *toHost) override
  7008. {
  7009. SocketEndpoint from(fromHost);
  7010. SocketEndpoint to(toHost);
  7011. if (from.ipequals(to))
  7012. return;
  7013. CConnectLock connlock("CNamedGroup::swapNode", SDS_GROUPSTORE_ROOT, true, false, false, defaultTimeout);
  7014. Owned<IPropertyTreeIterator> pe = connlock.conn->queryRoot()->getElements("Group");
  7015. ForEach(*pe)
  7016. {
  7017. IPropertyTree &group = pe->query();
  7018. const char *kind = group.queryProp("@kind");
  7019. if (kind && streq("Spare", kind))
  7020. continue;
  7021. StringBuffer name;
  7022. group.getProp("@name", name);
  7023. SocketEndpointArray eps;
  7024. if (!loadGroup(&group, eps, nullptr, nullptr))
  7025. {
  7026. IWARNLOG("swapNode: failed to load group: '%s'", name.str());
  7027. return;
  7028. }
  7029. unsigned epsPos = 0;
  7030. while (true)
  7031. {
  7032. if (epsPos == eps.ordinality())
  7033. break;
  7034. if (from == eps.item(epsPos))
  7035. {
  7036. VStringBuffer xpath("Node[%u]/@ip", epsPos+1);
  7037. group.setProp(xpath, toHost);
  7038. PROGLOG("swapNode swapping %s for %s in group %s", fromHost, toHost, name.str());
  7039. unsigned nodesSwapped = group.getPropInt("@nodesSwapped");
  7040. group.setPropInt("@nodesSwapped", nodesSwapped+1);
  7041. }
  7042. ++epsPos;
  7043. }
  7044. }
  7045. CriticalBlock block(cachesect);
  7046. cache.kill();
  7047. }
  7048. virtual unsigned setDefaultTimeout(unsigned timems) override
  7049. {
  7050. unsigned ret = defaultTimeout;
  7051. defaultTimeout = timems;
  7052. return ret;
  7053. }
  7054. virtual unsigned setRemoteTimeout(unsigned timems) override
  7055. {
  7056. unsigned ret = defaultRemoteTimeout;
  7057. defaultRemoteTimeout = timems;
  7058. return ret;
  7059. }
  7060. virtual void resetCache() override
  7061. {
  7062. CriticalBlock block(cachesect);
  7063. cache.kill();
  7064. }
  7065. private:
  7066. bool getRemoteGroup(const INode *foreigndali, const char *gname, unsigned foreigndalitimeout,
  7067. StringAttr &groupdir, GroupType &type, SocketEndpointArray &epa)
  7068. {
  7069. StringBuffer lcname(gname);
  7070. gname = lcname.trim().toLowerCase().str();
  7071. CMessageBuffer mb;
  7072. mb.append((int)MDFS_GET_GROUP_TREE).append(gname);
  7073. size32_t mbsz = mb.length();
  7074. foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
  7075. checkDfsReplyException(mb);
  7076. if (mb.length()==0)
  7077. return false;
  7078. byte ok;
  7079. mb.read(ok);
  7080. if (ok!=1) {
  7081. // kludge for prev bug
  7082. if ((ok==(byte)MDFS_GET_GROUP_TREE)&&mb.length()>mbsz) {
  7083. mb.skip(mbsz-1);
  7084. mb.read(ok);
  7085. if (ok!=1)
  7086. return false;
  7087. }
  7088. else
  7089. return false;
  7090. }
  7091. Owned<IPropertyTree> pt = createPTree(mb);
  7092. Owned<IPropertyTreeIterator> pe = pt->getElements("Node");
  7093. groupdir.set(pt->queryProp("@dir"));
  7094. type = translateGroupType(pt->queryProp("@kind"));
  7095. ForEach(*pe) {
  7096. SocketEndpoint ep(pe->query().queryProp("@ip"));
  7097. epa.append(ep);
  7098. }
  7099. return epa.ordinality() > 0;
  7100. }
  7101. };
  7102. static CNamedGroupStore *groupStore = NULL;
  7103. static CriticalSection groupsect;
  7104. bool CNamedGroupIterator::match()
  7105. {
  7106. if (conn.get()) {
  7107. if (matchgroup.get()) {
  7108. if (!groupStore)
  7109. return false;
  7110. const char *name = pe->query().queryProp("@name");
  7111. if (!name||!*name)
  7112. return false;
  7113. GroupType dummy;
  7114. Owned<IGroup> lgrp = groupStore->dolookup(name, conn, NULL, dummy);
  7115. if (lgrp) {
  7116. if (exactmatch)
  7117. return lgrp->equals(matchgroup);
  7118. GroupRelation gr = matchgroup->compare(lgrp);
  7119. return (gr==GRidentical)||(gr==GRbasesubset)||(gr==GRwrappedsuperset);
  7120. }
  7121. }
  7122. else
  7123. return true;
  7124. }
  7125. return false;
  7126. }
  7127. INamedGroupStore &queryNamedGroupStore()
  7128. {
  7129. if (!groupStore) {
  7130. CriticalBlock block(groupsect);
  7131. if (!groupStore)
  7132. groupStore = new CNamedGroupStore();
  7133. }
  7134. return *groupStore;
  7135. }
  7136. // --------------------------------------------------------
  7137. IDistributedFile *CDistributedFileDirectory::lookup(const char *_logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, bool privilegedUser, unsigned timeout)
  7138. {
  7139. CDfsLogicalFileName logicalname;
  7140. logicalname.set(_logicalname);
  7141. return lookup(logicalname, user, writeattr, hold, lockSuperOwner, transaction, privilegedUser, timeout);
  7142. }
  7143. IDistributedFile *CDistributedFileDirectory::dolookup(CDfsLogicalFileName &_logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, unsigned timeout)
  7144. {
  7145. CDfsLogicalFileName *logicalname = &_logicalname;
  7146. if (logicalname->isMulti())
  7147. // don't bother checking because the sub file creation will
  7148. return new CDistributedSuperFile(this,*logicalname,user,transaction); // temp superfile
  7149. if (strchr(logicalname->get(), '*')) // '*' only wildcard supported. NB: This is a temporary fix (See: HPCC-12523)
  7150. throw MakeStringException(-1, "Invalid filename lookup: %s", logicalname->get());
  7151. Owned<IDfsLogicalFileNameIterator> redmatch;
  7152. for (;;)
  7153. {
  7154. checkLogicalName(*logicalname,user,true,writeattr,true,NULL);
  7155. if (logicalname->isExternal()) {
  7156. Owned<IFileDescriptor> fDesc = getExternalFileDescriptor(logicalname->get());
  7157. if (!fDesc)
  7158. return NULL;
  7159. return queryDistributedFileDirectory().createExternal(fDesc, logicalname->get());
  7160. }
  7161. if (logicalname->isForeign()) {
  7162. IDistributedFile * ret = getFile(logicalname->get(),user,NULL);
  7163. if (ret)
  7164. return ret;
  7165. }
  7166. else {
  7167. unsigned start = 0;
  7168. for (;;) {
  7169. CFileLock fcl;
  7170. unsigned mode = RTM_LOCK_READ | RTM_SUB;
  7171. if (hold) mode |= RTM_LOCK_HOLD;
  7172. CTimeMon tm(timeout);
  7173. if (!fcl.init(*logicalname, mode, timeout, "CDistributedFileDirectory::lookup"))
  7174. break;
  7175. CFileSuperOwnerLock superOwnerLock;
  7176. if (lockSuperOwner)
  7177. {
  7178. unsigned remaining;
  7179. tm.timedout(&remaining);
  7180. verifyex(superOwnerLock.initWithFileLock(*logicalname, remaining, "CDistributedFileDirectory::dolookup(SuperOwnerLock)", fcl, mode));
  7181. }
  7182. if (fcl.getKind() == DXB_File)
  7183. {
  7184. StringBuffer cname;
  7185. if (logicalname->getCluster(cname).length())
  7186. {
  7187. IPropertyTree *froot=fcl.queryRoot();
  7188. if (froot)
  7189. {
  7190. StringBuffer query;
  7191. query.appendf("Cluster[@name=\"%s\"]",cname.str());
  7192. if (!froot->hasProp(query.str()))
  7193. break;
  7194. }
  7195. }
  7196. CDistributedFile *ret = new CDistributedFile(this,fcl.detach(),*logicalname,user); // found
  7197. ret->setSuperOwnerLock(superOwnerLock.detach());
  7198. return ret;
  7199. }
  7200. // now super file
  7201. if (fcl.getKind() != DXB_SuperFile)
  7202. break;
  7203. if (start==0)
  7204. start = msTick();
  7205. unsigned elapsed;
  7206. try
  7207. {
  7208. CDistributedSuperFile *ret = new CDistributedSuperFile(this,fcl.detach(),*logicalname,user,transaction,SDS_SUB_LOCK_TIMEOUT);
  7209. ret->setSuperOwnerLock(superOwnerLock.detach());
  7210. return ret;
  7211. }
  7212. catch (ISDSException *e)
  7213. {
  7214. elapsed = msTick()-start;
  7215. if ((e->errorCode()!=SDSExcpt_LockTimeout)||(elapsed>((timeout==INFINITE)?SDS_CONNECT_TIMEOUT:timeout)))
  7216. throw;
  7217. EXCLOG(e,"Superfile lookup");
  7218. e->Release();
  7219. }
  7220. PROGLOG("CDistributedSuperFile connect timeout (%dms) pausing",elapsed);
  7221. Sleep(SDS_TRANSACTION_RETRY/2+(getRandom()%SDS_TRANSACTION_RETRY));
  7222. }
  7223. }
  7224. if (redmatch.get()) {
  7225. if (!redmatch->next())
  7226. break;
  7227. }
  7228. else {
  7229. redmatch.setown(queryRedirection().getMatch(logicalname->get()));
  7230. if (!redmatch.get())
  7231. break;
  7232. if (!redmatch->first())
  7233. break;
  7234. }
  7235. logicalname = &redmatch->query();
  7236. }
  7237. return NULL;
  7238. }
  7239. IDistributedFile *CDistributedFileDirectory::lookup(CDfsLogicalFileName &logicalname, IUserDescriptor *user, bool writeattr, bool hold, bool lockSuperOwner, IDistributedFileTransaction *transaction, bool privilegedUser, unsigned timeout)
  7240. {
  7241. Owned <IDistributedFile>distributedFile = dolookup(logicalname, user, writeattr, hold, lockSuperOwner, transaction, timeout);
  7242. // Restricted access is currently designed to stop users viewing sensitive information. It is not designed to stop users deleting or overwriting existing restricted files
  7243. if (writeattr==false && distributedFile && distributedFile->isRestrictedAccess() && !privilegedUser)
  7244. throw new CDFS_Exception(DFSERR_RestrictedFileAccessDenied,logicalname.get());
  7245. return distributedFile.getClear();
  7246. }
  7247. IDistributedSuperFile *CDistributedFileDirectory::lookupSuperFile(const char *_logicalname,IUserDescriptor *user,IDistributedFileTransaction *transaction, unsigned timeout)
  7248. {
  7249. CDfsLogicalFileName logicalname;
  7250. logicalname.set(_logicalname);
  7251. IDistributedFile *file = dolookup(logicalname, user, false, false, false, transaction, timeout);
  7252. if (file) {
  7253. IDistributedSuperFile *sf = file->querySuperFile();
  7254. if (sf)
  7255. return sf;
  7256. file->Release();
  7257. }
  7258. return NULL;
  7259. }
  7260. bool CDistributedFileDirectory::isSuperFile( const char *logicalname,
  7261. IUserDescriptor *user,
  7262. INode *foreigndali,
  7263. unsigned timeout)
  7264. {
  7265. Owned<IPropertyTree> tree = getFileTree(logicalname, user, foreigndali,timeout, false);
  7266. return tree.get()&&(strcmp(tree->queryName(),queryDfsXmlBranchName(DXB_SuperFile))==0);
  7267. }
  7268. bool CDistributedFileDirectory::exists(const char *_logicalname,IUserDescriptor *user,bool notsuper,bool superonly)
  7269. {
  7270. // (currently) no check on scope permissions for exists
  7271. bool external;
  7272. bool foreign;
  7273. CDfsLogicalFileName dlfn;
  7274. dlfn.set(_logicalname);
  7275. const char *logicalname = dlfn.get();
  7276. external = dlfn.isExternal();
  7277. foreign = dlfn.isForeign();
  7278. if (foreign) {
  7279. // Restricted access is currently designed to stop users viewing sensitive information. Assuming privileged user rights to allow
  7280. // exists() operation to succeed regardless of user rights
  7281. Owned<IDistributedFile> file = lookup(_logicalname, user, false, false, false, NULL, defaultPrivilegedUser, defaultTimeout);
  7282. if (file.get()==NULL)
  7283. return false;
  7284. if (file->querySuperFile()) {
  7285. if (notsuper)
  7286. return false;
  7287. }
  7288. else
  7289. if (superonly)
  7290. return false;
  7291. }
  7292. else if (external) {
  7293. if (!existsPhysical(_logicalname,user))
  7294. return false;
  7295. }
  7296. else {
  7297. StringBuffer str;
  7298. if (!superonly) {
  7299. dlfn.makeFullnameQuery(str,DXB_File,true);
  7300. CConnectLock connlockfile("CDistributedFileDirectory::exists",str.str(),false,false,false,defaultTimeout);
  7301. if (connlockfile.conn.get())
  7302. return true;
  7303. }
  7304. if (notsuper)
  7305. return false;
  7306. dlfn.makeFullnameQuery(str.clear(),DXB_SuperFile,true);
  7307. CConnectLock connlocksuper("CDistributedFileDirectory::exists",str.str(),false,false,false,defaultTimeout);
  7308. if (!connlocksuper.conn.get())
  7309. return false;
  7310. }
  7311. return true;
  7312. }
  7313. bool CDistributedFileDirectory::existsPhysical(const char *_logicalname, IUserDescriptor *user)
  7314. {
  7315. // Restricted access is currently designed to stop users viewing sensitive information. Assuming privileged user rights to allow
  7316. // existsPhysical() operation to succeed regardless of user rights
  7317. Owned<IDistributedFile> file = lookup(_logicalname, user, false, false, false, NULL, defaultPrivilegedUser, defaultTimeout);
  7318. if (!file)
  7319. return false;
  7320. return file->existsPhysicalPartFiles(0);
  7321. }
  7322. IDistributedFile *CDistributedFileDirectory::createNew(IFileDescriptor *fdesc)
  7323. {
  7324. return new CDistributedFile(this, fdesc, NULL, false);
  7325. }
  7326. IDistributedFile *CDistributedFileDirectory::createExternal(IFileDescriptor *fdesc, const char *name)
  7327. {
  7328. CDistributedFile *dFile = new CDistributedFile(this, fdesc, NULL, true);
  7329. dFile->setLogicalName(name);
  7330. return dFile;
  7331. }
  7332. ////////////////////////////////////
  7333. class DistributedFilePropertyLockFree
  7334. {
  7335. unsigned lockedCount;
  7336. CDistributedFile *file;
  7337. CDistributedSuperFile *sfile;
  7338. public:
  7339. DistributedFilePropertyLockFree(IDistributedFile *_file)
  7340. {
  7341. file = dynamic_cast<CDistributedFile *>(_file);
  7342. sfile = NULL;
  7343. if (file)
  7344. lockedCount = file->setPropLockCount(0);
  7345. else
  7346. {
  7347. sfile = dynamic_cast<CDistributedSuperFile *>(_file);
  7348. lockedCount = sfile->setPropLockCount(0);
  7349. }
  7350. }
  7351. ~DistributedFilePropertyLockFree()
  7352. {
  7353. if (file)
  7354. verifyex(0 == file->setPropLockCount(lockedCount));
  7355. else if (sfile)
  7356. verifyex(0 == sfile->setPropLockCount(lockedCount));
  7357. }
  7358. };
  7359. /**
  7360. * Creates a super-file within a transaction.
  7361. */
  7362. class CCreateSuperFileAction: public CDFAction
  7363. {
  7364. CDfsLogicalFileName logicalname;
  7365. CDistributedFileDirectory *parent;
  7366. Linked<IDistributedSuperFile> super;
  7367. IUserDescriptor *user;
  7368. bool interleaved, created;
  7369. void clearSuper()
  7370. {
  7371. if (created)
  7372. {
  7373. created = false;
  7374. super->detach();
  7375. }
  7376. super.clear();
  7377. }
  7378. public:
  7379. CCreateSuperFileAction(CDistributedFileDirectory *_parent,
  7380. IUserDescriptor *_user,
  7381. const char *_flname,
  7382. bool _interleaved)
  7383. : parent(_parent), user(_user), created(false), interleaved(_interleaved)
  7384. {
  7385. logicalname.set(_flname);
  7386. }
  7387. IDistributedSuperFile *getSuper()
  7388. {
  7389. if (!super)
  7390. {
  7391. Owned<IDistributedSuperFile> _super = transaction->lookupSuperFile(logicalname.get(), SDS_SUB_LOCK_TIMEOUT);
  7392. if (_super)
  7393. super.setown(_super.getClear());
  7394. else
  7395. {
  7396. /* No super, create one if necessary.
  7397. * This really shouldn't have to work this way, looking up super early, or creating super stub now,
  7398. * because other super file transactions are based on
  7399. * TBD: There should be a way to obtain lock independently of actually attaching.
  7400. */
  7401. Owned<IPropertyTree> root = createPTree();
  7402. root->setPropInt("@interleaved",interleaved?2:0); // this is ill placed
  7403. super.setown(new CDistributedSuperFile(parent, root, logicalname, user));
  7404. created = true;
  7405. super->setModified();
  7406. transaction->addFile(super);
  7407. }
  7408. }
  7409. return super.getLink();
  7410. }
  7411. bool prepare()
  7412. {
  7413. Owned<IDistributedFile> _super = getSuper();
  7414. // Attach the file to DFS, if wasn't there already
  7415. if (created)
  7416. super->attach(logicalname.get(), user);
  7417. addFileLock(super);
  7418. if (lock())
  7419. return true;
  7420. unlock();
  7421. return false;
  7422. }
  7423. void run()
  7424. {
  7425. // Do nothing, file is already created
  7426. }
  7427. void retry()
  7428. {
  7429. // on retry, we need to remove the file so next lock doesn't fail
  7430. clearSuper();
  7431. CDFAction::retry();
  7432. }
  7433. void rollback()
  7434. {
  7435. state = TAS_FAILURE;
  7436. clearSuper();
  7437. CDFAction::rollback();
  7438. }
  7439. };
  7440. /**
  7441. * Removes a super-file within a transaction.
  7442. */
  7443. class CRemoveSuperFileAction: public CDFAction
  7444. {
  7445. CDfsLogicalFileName logicalname;
  7446. Linked<IDistributedSuperFile> super;
  7447. IUserDescriptor *user;
  7448. bool delSub;
  7449. Owned<IDistributedFileTransactionExt> nestedTransaction;
  7450. class CNestedTransaction : public CDistributedFileTransaction
  7451. {
  7452. IDistributedFileTransactionExt *transaction;
  7453. CIArrayOf<CDFAction> actions;
  7454. public:
  7455. CNestedTransaction(IDistributedFileTransactionExt *_transaction, IUserDescriptor *user)
  7456. : CDistributedFileTransaction(user), transaction(_transaction)
  7457. {
  7458. if (transaction->active())
  7459. start();
  7460. }
  7461. // wrap rest of calls into parent transaction calls
  7462. virtual IDistributedFile *lookupFile(const char *lfn,unsigned timeout=INFINITE)
  7463. {
  7464. return transaction->lookupFile(lfn, timeout);
  7465. }
  7466. virtual IDistributedSuperFile *lookupSuperFile(const char *slfn,unsigned timeout=INFINITE)
  7467. {
  7468. return transaction->lookupSuperFile(slfn, timeout);
  7469. }
  7470. virtual IUserDescriptor *queryUser() { return transaction->queryUser(); }
  7471. virtual void descend() { transaction->descend(); }
  7472. virtual void ascend() { transaction->ascend(); }
  7473. virtual void addFile(IDistributedFile *file) { transaction->addFile(file); }
  7474. virtual void ensureFile(IDistributedFile *file) { transaction->ensureFile(file); }
  7475. virtual void clearFile(IDistributedFile *file) { transaction->clearFile(file); }
  7476. virtual void noteAddSubFile(IDistributedSuperFile *super, const char *superName, IDistributedFile *sub)
  7477. {
  7478. transaction->noteAddSubFile(super, superName, sub);
  7479. }
  7480. virtual void noteRemoveSubFile(IDistributedSuperFile *super, IDistributedFile *sub)
  7481. {
  7482. transaction->noteRemoveSubFile(super, sub);
  7483. }
  7484. virtual void noteSuperSwap(IDistributedSuperFile *super1, IDistributedSuperFile *super2)
  7485. {
  7486. transaction->noteSuperSwap(super1, super2);
  7487. }
  7488. virtual void clearSubFiles(IDistributedSuperFile *super)
  7489. {
  7490. transaction->clearSubFiles(super);
  7491. }
  7492. virtual void noteRename(IDistributedFile *file, const char *newName)
  7493. {
  7494. transaction->noteRename(file, newName);
  7495. }
  7496. virtual void validateAddSubFile(IDistributedSuperFile *super, IDistributedFile *sub, const char *subName)
  7497. {
  7498. transaction->validateAddSubFile(super, sub, subName);
  7499. }
  7500. virtual bool isSubFile(IDistributedSuperFile *super, const char *subFile, bool sub)
  7501. {
  7502. return transaction->isSubFile(super, subFile, sub);
  7503. }
  7504. virtual bool addDelayedDelete(CDfsLogicalFileName &lfn,unsigned timeoutms=INFINITE)
  7505. {
  7506. return transaction->addDelayedDelete(lfn, timeoutms);
  7507. }
  7508. };
  7509. public:
  7510. CRemoveSuperFileAction(IUserDescriptor *_user,
  7511. const char *_flname,
  7512. bool _delSub)
  7513. : user(_user), delSub(_delSub)
  7514. {
  7515. logicalname.set(_flname);
  7516. }
  7517. virtual bool prepare()
  7518. {
  7519. // We *have* to make sure the file exists here
  7520. super.setown(transaction->lookupSuperFile(logicalname.get(), SDS_SUB_LOCK_TIMEOUT));
  7521. if (!super)
  7522. ThrowStringException(-1, "Super File %s doesn't exist in the file system", logicalname.get());
  7523. addFileLock(super);
  7524. // Adds actions to transactions before this one and gets executed only on commit
  7525. if (delSub)
  7526. {
  7527. // As 'delSub' means additional actions, handle them in their own transaction context
  7528. // Wrap lookups and record of removed/added subs to parent transaction, for common cache purposes
  7529. nestedTransaction.setown(new CNestedTransaction(transaction, user));
  7530. super->removeSubFile(NULL, true, false, nestedTransaction);
  7531. }
  7532. if (lock())
  7533. {
  7534. if (nestedTransaction)
  7535. {
  7536. if (nestedTransaction->prepareActions())
  7537. return true;
  7538. }
  7539. else
  7540. return true;
  7541. }
  7542. unlock();
  7543. super.clear();
  7544. return false;
  7545. }
  7546. virtual void retry()
  7547. {
  7548. super.clear();
  7549. if (nestedTransaction)
  7550. nestedTransaction->retryActions();
  7551. CDFAction::retry();
  7552. }
  7553. virtual void run()
  7554. {
  7555. if (nestedTransaction)
  7556. nestedTransaction->runActions();
  7557. super->detach(INFINITE, transaction->queryCodeContext());
  7558. }
  7559. virtual void commit()
  7560. {
  7561. if (nestedTransaction)
  7562. nestedTransaction->commitAndClearup();
  7563. CDFAction::commit();
  7564. }
  7565. };
  7566. /**
  7567. * Renames a file within a transaction.
  7568. */
  7569. class CRenameFileAction: public CDFAction
  7570. {
  7571. CDfsLogicalFileName fromName, toName;
  7572. CDistributedFileDirectory *parent;
  7573. Linked<IDistributedFile> file;
  7574. IUserDescriptor *user;
  7575. bool renamed;
  7576. enum RenameAction { ra_regular, ra_splitfrom, ra_mergeinto } ra;
  7577. public:
  7578. CRenameFileAction(CDistributedFileDirectory *_parent,
  7579. IUserDescriptor *_user,
  7580. const char *_flname,
  7581. const char *_newname)
  7582. : user(_user), parent(_parent)
  7583. {
  7584. fromName.set(_flname);
  7585. // Basic consistency checking
  7586. toName.set(_newname);
  7587. if (fromName.isExternal() || toName.isExternal())
  7588. throw MakeStringException(-1,"rename: cannot rename external files"); // JCSMORE perhaps you should be able to?
  7589. if (fromName.isForeign() || toName.isForeign())
  7590. throw MakeStringException(-1,"rename: cannot rename foreign files");
  7591. // Make sure files are not the same
  7592. if (0 == strcmp(fromName.get(), toName.get()))
  7593. ThrowStringException(-1, "rename: cannot rename file %s to itself", toName.get());
  7594. ra = ra_regular;
  7595. renamed = false;
  7596. }
  7597. virtual bool prepare()
  7598. {
  7599. // We *have* to make sure the source file exists and can be renamed
  7600. file.setown(transaction->lookupFile(fromName.get(), SDS_SUB_LOCK_TIMEOUT));
  7601. if (!file)
  7602. ThrowStringException(-1, "rename: file %s doesn't exist in the file system", fromName.get());
  7603. if (file->querySuperFile())
  7604. ThrowStringException(-1,"rename: cannot rename file %s as is SuperFile", fromName.get()); // Why not
  7605. StringBuffer reason;
  7606. if (!file->canRemove(reason))
  7607. ThrowStringException(-1,"rename: %s",reason.str());
  7608. addFileLock(file);
  7609. renamed = false;
  7610. if (lock())
  7611. {
  7612. StringBuffer oldcluster, newcluster;
  7613. fromName.getCluster(oldcluster);
  7614. toName.getCluster(newcluster);
  7615. Owned<IDistributedFile> newFile = transaction->lookupFile(toName.get(), SDS_SUB_LOCK_TIMEOUT);
  7616. if (newFile)
  7617. {
  7618. if (newcluster.length())
  7619. {
  7620. if (oldcluster.length())
  7621. ThrowStringException(-1,"rename: cannot specify both source and destination clusters on rename");
  7622. if (newFile->findCluster(newcluster.str())!=NotFound)
  7623. ThrowStringException(-1,"rename: cluster %s already part of file %s",newcluster.str(),toName.get());
  7624. if (file->numClusters()!=1)
  7625. ThrowStringException(-1,"rename: source file %s has more than one cluster",fromName.get());
  7626. // check compatible here ** TBD
  7627. ra = ra_mergeinto;
  7628. }
  7629. else
  7630. ThrowStringException(-1, "rename: file %s already exist in the file system", toName.get());
  7631. }
  7632. else if (oldcluster.length())
  7633. {
  7634. if (newcluster.length())
  7635. ThrowStringException(-1,"rename: cannot specify both source and destination clusters on rename");
  7636. if (file->numClusters()==1)
  7637. ThrowStringException(-1,"rename: cannot rename sole cluster %s",oldcluster.str());
  7638. if (file->findCluster(oldcluster.str())==NotFound)
  7639. ThrowStringException(-1,"rename: cannot find cluster %s",oldcluster.str());
  7640. ra = ra_splitfrom;
  7641. }
  7642. else
  7643. {
  7644. // TODO: something should check that file being renamed is not a subfile of a super where both created in transaction
  7645. transaction->noteRename(file, toName.get());
  7646. ra = ra_regular;
  7647. }
  7648. return true;
  7649. }
  7650. unlock();
  7651. file.clear();
  7652. return false;
  7653. }
  7654. virtual void run()
  7655. {
  7656. doRename(fromName, toName, ra);
  7657. renamed = true;
  7658. }
  7659. virtual void retry()
  7660. {
  7661. file.clear();
  7662. CDFAction::retry();
  7663. }
  7664. virtual void rollback()
  7665. {
  7666. // Only roll back if already renamed
  7667. if (renamed)
  7668. {
  7669. switch (ra)
  7670. {
  7671. case ra_regular:
  7672. doRename(toName, fromName, ra_regular);
  7673. break;
  7674. case ra_splitfrom:
  7675. doRename(toName, fromName, ra_mergeinto);
  7676. break;
  7677. case ra_mergeinto:
  7678. doRename(toName, fromName, ra_splitfrom);
  7679. break;
  7680. default:
  7681. throwUnexpected();
  7682. }
  7683. renamed = false;
  7684. }
  7685. CDFAction::rollback();
  7686. }
  7687. private:
  7688. void doRename(CDfsLogicalFileName &from, CDfsLogicalFileName &to, RenameAction ra)
  7689. {
  7690. CriticalBlock block(physicalChange);
  7691. StringBuffer oldcluster, newcluster;
  7692. fromName.getCluster(oldcluster);
  7693. toName.getCluster(newcluster);
  7694. Owned<IDistributedFile> oldfile;
  7695. if (ra_splitfrom == ra)
  7696. {
  7697. oldfile.setown(file.getClear());
  7698. Owned<IFileDescriptor> newdesc = oldfile->getFileDescriptor(oldcluster.str());
  7699. file.setown(parent->createNew(newdesc));
  7700. }
  7701. // Physical Rename
  7702. Owned<IMultiException> exceptions = MakeMultiException();
  7703. if (!file->renamePhysicalPartFiles(to.get(),newcluster,exceptions))
  7704. {
  7705. unlock();
  7706. StringBuffer errors;
  7707. exceptions->errorMessage(errors);
  7708. ThrowStringException(-1, "rename: could not rename logical file %s to %s: %s", fromName.get(), to.get(), errors.str());
  7709. }
  7710. // Logical rename and cleanup
  7711. switch (ra)
  7712. {
  7713. case ra_splitfrom:
  7714. {
  7715. unlock();
  7716. oldfile->removeCluster(oldcluster.str());
  7717. file->attach(to.get(), user);
  7718. lock();
  7719. break;
  7720. }
  7721. case ra_mergeinto:
  7722. {
  7723. Owned<IDistributedFile> newFile = transaction->lookupFile(to.get(), SDS_SUB_LOCK_TIMEOUT);
  7724. ClusterPartDiskMapSpec mspec = file->queryPartDiskMapping(0);
  7725. // Unlock the old file
  7726. unlock();
  7727. CDistributedFile *_file = dynamic_cast<CDistributedFile *>(file.get());
  7728. _file->detachLogical(INFINITE); // don't delete physicals, now used by newFile
  7729. transaction->clearFile(file); // no long used in transaction
  7730. newFile->addCluster(newcluster.str(),mspec);
  7731. parent->fixDates(newFile);
  7732. // need to clear and re-lookup as changed outside of transaction
  7733. // TBD: Allow 'addCluster' 'fixDates' etc. to be delayed/work inside transaction
  7734. transaction->clearFile(newFile);
  7735. newFile.clear();
  7736. file.setown(transaction->lookupFile(to.get(), SDS_SUB_LOCK_TIMEOUT));
  7737. addFileLock(file);
  7738. lock();
  7739. break;
  7740. }
  7741. case ra_regular:
  7742. {
  7743. /* It is not enough to unlock this actions locks on the file being renamed,
  7744. * because other actions, before and after may hold locks to the same file.
  7745. * For now, IDistributeFile::rename, needs to work on a lock free instance.
  7746. * TBD: Allow IDistributedFile::rename to work properly within transaction.
  7747. */
  7748. DistributedFilePropertyLockFree unlock(file);
  7749. file->rename(to.get(), user);
  7750. break;
  7751. }
  7752. default:
  7753. throwUnexpected();
  7754. }
  7755. // MORE: If the logical rename fails, we should roll back the physical renaming
  7756. // What if the physical renaming-back fails?!
  7757. // For now, leaving as it was, since physical renaming is more prone to errors than logical
  7758. // And checks were made earlier to make sure it was safe to rename
  7759. }
  7760. };
  7761. // MORE: This should be implemented in DFSAccess later on
  7762. IDistributedSuperFile *CDistributedFileDirectory::createSuperFile(const char *_logicalname,IUserDescriptor *user, bool _interleaved,bool ifdoesnotexist,IDistributedFileTransaction *transaction)
  7763. {
  7764. CDfsLogicalFileName logicalname;
  7765. logicalname.set(_logicalname);
  7766. checkLogicalName(logicalname,user,true,true,false,"have a superfile with");
  7767. // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
  7768. Linked<IDistributedFileTransactionExt> localtrans;
  7769. if (transaction)
  7770. {
  7771. IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
  7772. localtrans.set(_transaction);
  7773. }
  7774. else
  7775. localtrans.setown(new CDistributedFileTransaction(user));
  7776. IDistributedSuperFile *sfile = localtrans->lookupSuperFile(logicalname.get());
  7777. if (sfile)
  7778. {
  7779. if (ifdoesnotexist)
  7780. return sfile;
  7781. else
  7782. throw MakeStringException(-1,"createSuperFile: SuperFile %s already exists",logicalname.get());
  7783. }
  7784. Owned<CCreateSuperFileAction> action = new CCreateSuperFileAction(this,user,_logicalname,_interleaved);
  7785. localtrans->addAction(action.getLink()); // takes ownership
  7786. localtrans->autoCommit();
  7787. return action->getSuper();
  7788. }
  7789. // MORE: This should be implemented in DFSAccess later on
  7790. IDistributedSuperFile *CDistributedFileDirectory::createNewSuperFile(IPropertyTree *tree, const char *optionalName)
  7791. {
  7792. return new CDistributedSuperFile(this, tree, optionalName);
  7793. }
  7794. // MORE: This should be implemented in DFSAccess later on
  7795. void CDistributedFileDirectory::removeSuperFile(const char *_logicalname, bool delSubs, IUserDescriptor *user, IDistributedFileTransaction *transaction)
  7796. {
  7797. CDfsLogicalFileName logicalname;
  7798. logicalname.set(_logicalname);
  7799. checkLogicalName(logicalname,user,true,true,false,"have a superfile with");
  7800. // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
  7801. Linked<IDistributedFileTransactionExt> localtrans;
  7802. if (transaction)
  7803. {
  7804. IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
  7805. localtrans.set(_transaction);
  7806. }
  7807. else
  7808. localtrans.setown(new CDistributedFileTransaction(user));
  7809. CRemoveSuperFileAction *action = new CRemoveSuperFileAction(user, _logicalname, delSubs);
  7810. localtrans->addAction(action); // takes ownership
  7811. localtrans->autoCommit();
  7812. }
  7813. bool CDistributedFileDirectory::removeEntry(const char *name, IUserDescriptor *user, IDistributedFileTransaction *transaction, unsigned timeoutms, bool throwException)
  7814. {
  7815. CDfsLogicalFileName logicalname;
  7816. logicalname.set(name);
  7817. if (!logicalname.isExternal())
  7818. checkLogicalName(logicalname,user,true,true,false,"delete");
  7819. // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
  7820. Linked<IDistributedFileTransactionExt> localtrans;
  7821. if (transaction)
  7822. {
  7823. IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
  7824. localtrans.set(_transaction);
  7825. }
  7826. else
  7827. localtrans.setown(new CDistributedFileTransaction(user));
  7828. // Action will be executed at the end of the transaction (commit)
  7829. localtrans->addDelayedDelete(logicalname, timeoutms);
  7830. try
  7831. {
  7832. localtrans->autoCommit();
  7833. }
  7834. catch (IException *e)
  7835. {
  7836. // TODO: Transform removeEntry into void
  7837. StringBuffer msg(logicalname.get());
  7838. msg.append(" - cause: ");
  7839. e->errorMessage(msg);
  7840. IERRLOG("%s", msg.str());
  7841. if (throwException)
  7842. throw new CDFS_Exception(DFSERR_FailedToDeleteFile, msg.str());
  7843. e->Release();
  7844. return false;
  7845. }
  7846. return true;
  7847. }
  7848. void CDistributedFileDirectory::removeEmptyScope(const char *scope)
  7849. {
  7850. if (scope&&*scope) {
  7851. StringBuffer fn(scope);
  7852. fn.append("::x");
  7853. CDfsLogicalFileName dlfn;
  7854. dlfn.set(fn.str());
  7855. removeFileEmptyScope(dlfn,defaultTimeout);
  7856. }
  7857. }
  7858. void CDistributedFileDirectory::renamePhysical(const char *oldname,const char *newname,IUserDescriptor *user,IDistributedFileTransaction *transaction)
  7859. {
  7860. if (!user)
  7861. {
  7862. #ifdef NULL_DALIUSER_STACKTRACE
  7863. DBGLOG("UNEXPECTED USER (NULL) in dadfs.cpp CDistributedFileDirectory::renamePhysical %d",__LINE__);
  7864. //following debug code to be removed
  7865. PrintStackReport();
  7866. #endif
  7867. user = defaultudesc.get();
  7868. }
  7869. CDfsLogicalFileName oldlogicalname;
  7870. oldlogicalname.set(oldname);
  7871. checkLogicalName(oldlogicalname,user,true,true,false,"rename");
  7872. // Create a local transaction that will be destroyed (MORE: make transaction compulsory)
  7873. Linked<IDistributedFileTransactionExt> localtrans;
  7874. if (transaction)
  7875. {
  7876. IDistributedFileTransactionExt *_transaction = dynamic_cast<IDistributedFileTransactionExt *>(transaction);
  7877. localtrans.set(_transaction);
  7878. }
  7879. else
  7880. localtrans.setown(new CDistributedFileTransaction(user));
  7881. CRenameFileAction *action = new CRenameFileAction(this, user, oldname, newname);
  7882. localtrans->addAction(action); // takes ownership
  7883. localtrans->autoCommit();
  7884. }
  7885. void CDistributedFileDirectory::fixDates(IDistributedFile *file)
  7886. {
  7887. // should do in parallel
  7888. unsigned width = file->numParts();
  7889. CriticalSection crit;
  7890. class casyncfor: public CAsyncFor
  7891. {
  7892. IDistributedFile *file;
  7893. CriticalSection &crit;
  7894. unsigned width;
  7895. public:
  7896. bool ok;
  7897. casyncfor(IDistributedFile *_file,unsigned _width,CriticalSection &_errcrit)
  7898. : crit(_errcrit)
  7899. {
  7900. file = _file;
  7901. ok = true;
  7902. width = _width;
  7903. ok = true;
  7904. }
  7905. void Do(unsigned i)
  7906. {
  7907. CriticalBlock block(crit);
  7908. Owned<IDistributedFilePart> part = file->getPart(i);
  7909. CDateTime dt;
  7910. if (!part->getModifiedTime(false,false,dt))
  7911. return;
  7912. unsigned nc = part->numCopies();
  7913. for (unsigned copy = 0; copy < nc; copy++) {
  7914. RemoteFilename rfn;
  7915. part->getFilename(rfn,copy);
  7916. Owned<IFile> partfile = createIFile(rfn);
  7917. try {
  7918. CriticalUnblock unblock(crit);
  7919. CDateTime dt2;
  7920. if (partfile->getTime(NULL,&dt2,NULL)) {
  7921. if (!dt.equals(dt2)) {
  7922. partfile->setTime(NULL,&dt,NULL);
  7923. }
  7924. }
  7925. }
  7926. catch (IException *e) {
  7927. CriticalBlock block(crit);
  7928. StringBuffer s("Failed to find file part ");
  7929. s.append(partfile->queryFilename()).append(" on ");
  7930. rfn.queryEndpoint().getUrlStr(s);
  7931. EXCLOG(e, s.str());
  7932. e->Release();
  7933. }
  7934. }
  7935. }
  7936. } afor(file,width,crit);
  7937. afor.For(width,10,false,true);
  7938. }
  7939. void CDistributedFileDirectory::addEntry(CDfsLogicalFileName &dlfn,IPropertyTree *root,bool superfile, bool ignoreexists)
  7940. {
  7941. // add bit awkward
  7942. bool external;
  7943. bool foreign;
  7944. external = dlfn.isExternal();
  7945. foreign = dlfn.isForeign();
  7946. if (external) {
  7947. root->Release();
  7948. return; // ignore attempts to add external
  7949. }
  7950. CScopeConnectLock sconnlock("CDistributedFileDirectory::addEntry", dlfn, true, false, false, defaultTimeout);
  7951. if (!sconnlock.conn()) {// warn?
  7952. root->Release();
  7953. return;
  7954. }
  7955. IPropertyTree* sroot = sconnlock.conn()->queryRoot();
  7956. StringBuffer tail;
  7957. dlfn.getTail(tail);
  7958. IPropertyTree *prev = getNamedPropTree(sroot,superfile?queryDfsXmlBranchName(DXB_SuperFile):queryDfsXmlBranchName(DXB_File),"@name",tail.str(),false);
  7959. if (!prev) // check super/file doesn't exist
  7960. prev = getNamedPropTree(sroot,superfile?queryDfsXmlBranchName(DXB_File):queryDfsXmlBranchName(DXB_SuperFile),"@name",tail.str(),false);
  7961. if (prev!=nullptr)
  7962. {
  7963. prev->Release();
  7964. root->Release();
  7965. if (ignoreexists)
  7966. return;
  7967. throw new CDFS_Exception(DFSERR_LogicalNameAlreadyExists,dlfn.get());
  7968. }
  7969. root->setProp("@name",tail.str());
  7970. root->setProp("OrigName",dlfn.get());
  7971. if (superfile)
  7972. sroot->addPropTree(queryDfsXmlBranchName(DXB_SuperFile), root); // now owns root
  7973. else
  7974. {
  7975. IPropertyTree *file = sroot->addPropTree(queryDfsXmlBranchName(DXB_File), root); // now owns root
  7976. file->setPropTree("ClusterLock", createPTree());
  7977. }
  7978. }
  7979. IDistributedFileIterator *CDistributedFileDirectory::getIterator(const char *wildname, bool includesuper, IUserDescriptor *user,bool isPrivilegedUser)
  7980. {
  7981. return new CDistributedFileIterator(this,wildname,includesuper,user,isPrivilegedUser);
  7982. }
  7983. GetFileClusterNamesType CDistributedFileDirectory::getFileClusterNames(const char *_logicalname,StringArray &out)
  7984. {
  7985. CDfsLogicalFileName logicalname;
  7986. logicalname.set(_logicalname);
  7987. if (logicalname.isForeign())
  7988. return GFCN_Foreign;
  7989. if (logicalname.isExternal())
  7990. return GFCN_External;
  7991. CScopeConnectLock sconnlock("CDistributedFileDirectory::getFileClusterList", logicalname, false, false, false, defaultTimeout);
  7992. DfsXmlBranchKind bkind;
  7993. IPropertyTree *froot = sconnlock.queryFileRoot(logicalname,bkind);
  7994. if (froot) {
  7995. if (bkind==DXB_File) {
  7996. getFileGroups(froot,out);
  7997. return GFCN_Normal;
  7998. }
  7999. if (bkind==DXB_SuperFile)
  8000. return GFCN_Super;
  8001. }
  8002. return GFCN_NotFound;
  8003. }
  8004. // --------------------------------------------------------
  8005. static CDistributedFileDirectory *DFdir = NULL;
  8006. static CriticalSection dfdirCrit;
  8007. /**
  8008. * Public method to control DistributedFileDirectory access
  8009. * as a singleton. This is the only way to get directories,
  8010. * files, super-files and logic-files.
  8011. */
  8012. IDistributedFileDirectory &queryDistributedFileDirectory()
  8013. {
  8014. if (!DFdir) {
  8015. CriticalBlock block(dfdirCrit);
  8016. if (!DFdir)
  8017. DFdir = new CDistributedFileDirectory();
  8018. }
  8019. return *DFdir;
  8020. }
  8021. /**
  8022. * Shutdown distributed file system (root directory).
  8023. */
  8024. void closedownDFS() // called by dacoven
  8025. {
  8026. CriticalBlock block(dfdirCrit);
  8027. try {
  8028. delete DFdir;
  8029. }
  8030. catch (IMP_Exception *e) {
  8031. if (e->errorCode()!=MPERR_link_closed)
  8032. throw;
  8033. PrintExceptionLog(e,"closedownDFS");
  8034. e->Release();
  8035. }
  8036. catch (IDaliClient_Exception *e) {
  8037. if (e->errorCode()!=DCERR_server_closed)
  8038. throw;
  8039. e->Release();
  8040. }
  8041. DFdir = NULL;
  8042. CriticalBlock block2(groupsect);
  8043. ::Release(groupStore);
  8044. groupStore = NULL;
  8045. }
  8046. class CDFPartFilter : implements IDFPartFilter, public CInterface
  8047. {
  8048. protected:
  8049. bool *partincluded;
  8050. unsigned max;
  8051. public:
  8052. IMPLEMENT_IINTERFACE;
  8053. CDFPartFilter(const char *filter)
  8054. {
  8055. max = 0;
  8056. partincluded = NULL;
  8057. unsigned pn=0;
  8058. const char *s=filter;
  8059. if (!s)
  8060. return;
  8061. while (*s) {
  8062. if (isdigit(*s)) {
  8063. pn = pn*10+(*s-'0');
  8064. if (pn>max)
  8065. max = pn;
  8066. }
  8067. else
  8068. pn = 0;
  8069. s++;
  8070. }
  8071. if (max==0)
  8072. return;
  8073. partincluded = new bool[max];
  8074. unsigned i;
  8075. for (i=0;i<max;i++)
  8076. partincluded[i] = false;
  8077. pn=0;
  8078. s=filter;
  8079. unsigned start=0;
  8080. for (;;) {
  8081. if ((*s==0)||(*s==',')||isspace(*s)) {
  8082. if (start) {
  8083. for (i=start-1;i<pn;i++)
  8084. partincluded[i] = true;
  8085. start = 0;
  8086. }
  8087. else
  8088. partincluded[pn-1] = true;
  8089. if (*s==0)
  8090. break;
  8091. pn = 0;
  8092. }
  8093. else if (isdigit(*s)) {
  8094. pn = pn*10+(*s-'0');
  8095. if (pn>max)
  8096. max = pn;
  8097. if (s[1]=='-') {
  8098. s++;
  8099. start = pn;
  8100. pn = 0;
  8101. }
  8102. }
  8103. s++;
  8104. }
  8105. }
  8106. ~CDFPartFilter()
  8107. {
  8108. delete [] partincluded;
  8109. }
  8110. bool includePart(unsigned part)
  8111. {
  8112. if (max==0)
  8113. return true;
  8114. if (part>=max)
  8115. return false;
  8116. return partincluded[part];
  8117. };
  8118. };
  8119. IDFPartFilter *createPartFilter(const char *filter)
  8120. {
  8121. return new CDFPartFilter(filter);
  8122. }
  8123. //=====================================================================================
  8124. // Server Side Support
  8125. class CFileMatch : public CInterface
  8126. {
  8127. StringAttr name;
  8128. Linked<IPropertyTree> tree;
  8129. bool isSuper;
  8130. public:
  8131. CFileMatch(const char *_name, IPropertyTree *_tree, bool _isSuper) : name(_name), tree(_tree), isSuper(_isSuper)
  8132. {
  8133. }
  8134. IPropertyTree &queryFileTree() const { return *tree; }
  8135. const char *queryName() const { return name; }
  8136. bool queryIsSuper() const { return isSuper; }
  8137. };
  8138. typedef CIArrayOf<CFileMatch> CFileMatchArray;
  8139. class CScope : public CInterface
  8140. {
  8141. StringAttr name;
  8142. CIArrayOf<CFileMatch> files; // matches
  8143. CIArrayOf<CScope> subScopes;
  8144. public:
  8145. CScope(const char *_name) : name(_name)
  8146. {
  8147. }
  8148. const char *getName() const { return name; }
  8149. void addMatch(const char *name, IPropertyTree &fileTree, bool isSuper)
  8150. {
  8151. files.append(*new CFileMatch(name, &fileTree, isSuper));
  8152. }
  8153. CScope *addScope(const char *scope)
  8154. {
  8155. CScope *subScope = new CScope(scope);
  8156. subScopes.append(*subScope);
  8157. return subScope;
  8158. }
  8159. void popLastScope()
  8160. {
  8161. subScopes.pop();
  8162. }
  8163. CIArrayOf<CScope> &querySubScopes() { return subScopes; }
  8164. CFileMatchArray &queryFiles() { return files; }
  8165. };
  8166. typedef CIArrayOf<CScope> CScopeArray;
  8167. const char* DFUQFilterFieldNames[] = { "", "@description", "@directory", "@group", "@modified", "@name", "@numclusters", "@numparts",
  8168. "@partmask", "@OrigName", "Attr", "Attr/@job", "Attr/@owner", "Attr/@recordCount", "Attr/@recordSize", "Attr/@size",
  8169. "Attr/@compressedsize", "Attr/@workunit", "Cluster", "Cluster/@defaultBaseDir", "Cluster/@defaultReplDir", "Cluster/@mapFlags",
  8170. "Cluster/@name", "Part", "Part/@name", "Part/@num", "Part/@size", "SuperOwner", "SuperOwner/@name",
  8171. "SubFile", "SubFile/@name", "SubFile/@num", "Attr/@kind", "Attr/@accessed" };
  8172. extern da_decl const char* getDFUQFilterFieldName(DFUQFilterField feild)
  8173. {
  8174. return DFUQFilterFieldNames[feild];
  8175. }
  8176. class CDFUSFFilter : public CInterface
  8177. {
  8178. DFUQFilterType filterType;
  8179. StringAttr attrPath;
  8180. bool hasFilter;
  8181. bool hasFilterHigh;
  8182. StringAttr filterValue;
  8183. StringAttr filterValueHigh;
  8184. int filterValueInt;
  8185. int filterValueHighInt;
  8186. __int64 filterValueInt64;
  8187. __int64 filterValueHighInt64;
  8188. bool filterValueBoolean;
  8189. StringAttr sep;
  8190. StringArray filterArray;
  8191. public:
  8192. CDFUSFFilter(DFUQFilterType _filterType, const char *_attrPath, const char *_filterValue, const char *_filterValueHigh)
  8193. : filterType(_filterType), attrPath(_attrPath), filterValue(_filterValue), filterValueHigh(_filterValueHigh) {};
  8194. CDFUSFFilter(DFUQFilterType _filterType, const char *_attrPath, bool _hasFilter, const int _filterValue, bool _hasFilterHigh, const int _filterValueHigh)
  8195. : filterType(_filterType), attrPath(_attrPath), hasFilter(_hasFilter), filterValueInt(_filterValue), hasFilterHigh(_hasFilterHigh), filterValueHighInt(_filterValueHigh) {};
  8196. CDFUSFFilter(DFUQFilterType _filterType, const char *_attrPath, bool _hasFilter, const __int64 _filterValue, bool _hasFilterHigh, const __int64 _filterValueHigh)
  8197. : filterType(_filterType), attrPath(_attrPath), hasFilter(_hasFilter), filterValueInt64(_filterValue), hasFilterHigh(_hasFilterHigh), filterValueHighInt64(_filterValueHigh) {};
  8198. CDFUSFFilter(DFUQFilterType _filterType, const char *_attrPath, bool _filterValue)
  8199. : filterType(_filterType), attrPath(_attrPath), filterValueBoolean(_filterValue) {};
  8200. CDFUSFFilter(DFUQFilterType _filterType, const char *_attrPath, const char *_filterValue, const char *_sep, StringArray& _filterArray)
  8201. : filterType(_filterType), attrPath(_attrPath), filterValue(_filterValue), sep(_sep)
  8202. {
  8203. ForEachItemIn(i,_filterArray)
  8204. {
  8205. const char* filter = _filterArray.item(i);
  8206. if (filter && *filter)
  8207. filterArray.append(filter);
  8208. }
  8209. };
  8210. DFUQFilterType getFilterType() { return filterType;}
  8211. const char * getAttrPath() { return attrPath.get();}
  8212. const char * getFilterValue() { return filterValue.get();}
  8213. const char * getFilterValueHigh() { return filterValueHigh.get();}
  8214. const int getFilterValueInt() { return filterValueInt;}
  8215. const int getFilterValueHighInt() { return filterValueHighInt;}
  8216. const __int64 getFilterValueInt64() { return filterValueInt64;}
  8217. const __int64 getFilterValueHighInt64() { return filterValueHighInt64;}
  8218. const bool getFilterValueBoolean() { return filterValueBoolean;}
  8219. const char * getSep() { return sep.get();}
  8220. void getFilterArray(StringArray &filters)
  8221. {
  8222. ForEachItemIn(c, filterArray)
  8223. filters.append(filterArray.item(c));
  8224. }
  8225. bool checkFilter(IPropertyTree &file)
  8226. {
  8227. bool match = true;
  8228. switch(filterType)
  8229. {
  8230. case DFUQFTwildcardMatch:
  8231. match = doWildMatch(file);
  8232. break;
  8233. case DFUQFTbooleanMatch:
  8234. match = doBooleanMatch(file);
  8235. break;
  8236. case DFUQFThasProp:
  8237. match = checkHasPropFilter(file);
  8238. break;
  8239. case DFUQFTcontainString:
  8240. match = checkContainStringFilter(file);
  8241. break;
  8242. case DFUQFTstringRange:
  8243. match = checkStringRangeFilter(file);
  8244. break;
  8245. case DFUQFTintegerRange:
  8246. match = checkIntegerRangeFilter(file);
  8247. break;
  8248. case DFUQFTinteger64Range:
  8249. match = checkInteger64RangeFilter(file);
  8250. break;
  8251. }
  8252. return match;
  8253. }
  8254. bool doWildMatch(IPropertyTree &file)
  8255. {
  8256. const char* filter = filterValue.get();
  8257. if (!attrPath.get() || !filter || !*filter || streq(filter, "*"))
  8258. return true;
  8259. const char* prop = file.queryProp(attrPath.get());
  8260. if (prop && WildMatch(prop, filter, true))
  8261. return true;
  8262. return false;
  8263. }
  8264. bool doBooleanMatch(IPropertyTree &file)
  8265. {
  8266. if (!attrPath.get())
  8267. return true;
  8268. return filterValueBoolean == file.getPropBool(attrPath.get(), true);
  8269. }
  8270. bool checkHasPropFilter(IPropertyTree &file)
  8271. {
  8272. if (!attrPath.get())
  8273. return true;
  8274. return filterValueBoolean == file.hasProp(attrPath.get());
  8275. }
  8276. bool checkContainStringFilter(IPropertyTree &file)
  8277. {
  8278. if (!attrPath.get())
  8279. return true;
  8280. const char* prop = file.queryProp(attrPath.get());
  8281. if (!prop || !*prop)
  8282. return false;
  8283. bool found = false;
  8284. if (!sep.get())
  8285. {
  8286. if (filterArray.find(prop) != NotFound) //Match with one of values in the filter
  8287. found = true;
  8288. return found;
  8289. }
  8290. StringArray propArray;
  8291. propArray.appendListUniq(prop, sep.get());
  8292. ForEachItemIn(i,propArray)
  8293. {
  8294. const char* value = propArray.item(i);
  8295. if (!value || !*value)
  8296. continue;
  8297. if (filterArray.find(value) != NotFound) //Match with one of values in the filter
  8298. {
  8299. found = true;
  8300. break;
  8301. }
  8302. }
  8303. return found;
  8304. }
  8305. bool checkStringRangeFilter(IPropertyTree &file)
  8306. {
  8307. if (!attrPath.get())
  8308. return true;
  8309. const char* prop = file.queryProp(attrPath.get());
  8310. if (!prop || !*prop)
  8311. return false;
  8312. if (!filterValue.isEmpty() && (strcmp(filterValue, prop) > 0))
  8313. return false;
  8314. if (!filterValueHigh.isEmpty() && (strcmp(filterValueHigh, prop) < 0))
  8315. return false;
  8316. return true;
  8317. }
  8318. bool checkIntegerRangeFilter(IPropertyTree &file)
  8319. {
  8320. if (!attrPath.get())
  8321. return true;
  8322. int prop = file.getPropInt(attrPath.get());
  8323. if (hasFilter && (prop < filterValueInt))
  8324. return false;
  8325. if (hasFilterHigh && (prop > filterValueHighInt))
  8326. return false;
  8327. return true;
  8328. }
  8329. bool checkInteger64RangeFilter(IPropertyTree &file)
  8330. {
  8331. if (!attrPath.get())
  8332. return true;
  8333. __int64 prop = file.getPropInt64(attrPath.get());
  8334. if (hasFilter && (prop < filterValueInt64))
  8335. return false;
  8336. if (hasFilterHigh && (prop > filterValueHighInt64))
  8337. return false;
  8338. return true;
  8339. }
  8340. };
  8341. typedef CIArrayOf<CDFUSFFilter> CDFUSFFilterArray;
  8342. class CIterateFileFilterContainer : public CInterface
  8343. {
  8344. StringAttr filterBuf; //Hold original filter string just in case
  8345. StringAttr wildNameFilter;
  8346. unsigned maxFilesFilter;
  8347. DFUQFileTypeFilter fileTypeFilter;
  8348. CIArrayOf<CDFUSFFilter> filters;
  8349. //The 'filters' contains the file scan filters other than wildNameFilter and fileTypeFilter. Those filters are used for
  8350. //filtering the files using File Attributes tree and CDFUSFFilter::checkFilter(). The wildNameFilter and fileTypeFilter need
  8351. //special code to filter the files.
  8352. SerializeFileAttrOptions options;
  8353. bool isValidInteger(const char *s)
  8354. {
  8355. if (!s || !*s)
  8356. return false;
  8357. while (*s)
  8358. {
  8359. if ((*s != '-') && !isdigit(*s))
  8360. return false;
  8361. s++;
  8362. }
  8363. return true;
  8364. }
  8365. void addOption(const char* optionStr)
  8366. {
  8367. if (!optionStr || !*optionStr || !isdigit(*optionStr))
  8368. return;
  8369. DFUQSerializeFileAttrOption option = (DFUQSerializeFileAttrOption) atoi(optionStr);
  8370. switch(option)
  8371. {
  8372. case DFUQSFAOincludeSuperOwner:
  8373. options.includeSuperOwner = true;
  8374. break;
  8375. //Add more when needed
  8376. }
  8377. }
  8378. void addFilter(DFUQFilterType filterType, const char* attr, const char* value, const char* valueHigh)
  8379. {
  8380. if (!attr || !*attr)
  8381. return;
  8382. if ((DFUQFTwildcardMatch == filterType) || (DFUQFTstringRange == filterType))
  8383. {
  8384. filters.append(*new CDFUSFFilter(filterType, attr, value, valueHigh));
  8385. return;
  8386. }
  8387. if ((DFUQFTbooleanMatch == filterType) || (DFUQFThasProp == filterType))
  8388. {
  8389. bool filter = true;
  8390. if (value && (streq(value, "0") || strieq(value, "false")))
  8391. filter = false;
  8392. filters.append(*new CDFUSFFilter(filterType, attr, filter));
  8393. return;
  8394. }
  8395. if ((DFUQFTintegerRange == filterType) || (DFUQFTinteger64Range == filterType))
  8396. {
  8397. bool hasFilter = false;
  8398. bool hasFilterHigh = false;
  8399. if (value && isValidInteger(value))
  8400. hasFilter = true;
  8401. if (valueHigh && isValidInteger(valueHigh))
  8402. hasFilterHigh = true;
  8403. if (!hasFilter && !hasFilterHigh)
  8404. return;
  8405. if (DFUQFTintegerRange == filterType)
  8406. filters.append(*new CDFUSFFilter(filterType, attr, hasFilter, atoi(value), hasFilterHigh, atoi(valueHigh)));
  8407. else
  8408. filters.append(*new CDFUSFFilter(filterType, attr, hasFilter, (__int64) atol(value), hasFilterHigh, (__int64) atol(valueHigh)));
  8409. return;
  8410. }
  8411. }
  8412. void addFilterArray(DFUQFilterType filterType, const char* attr, const char* value, const char* sep)
  8413. {
  8414. if (!attr || !*attr || !value || !*value)
  8415. return;
  8416. StringArray filterArray;
  8417. filterArray.appendListUniq(value, sep);
  8418. filters.append(*new CDFUSFFilter(filterType, attr, value, sep, filterArray));
  8419. }
  8420. void addSpecialFilter(const char* attr, const char* value)
  8421. {
  8422. if (!attr || !*attr || !value || !*value)
  8423. return;
  8424. if (!isdigit(*attr))
  8425. {
  8426. PROGLOG("Unsupported Special Filter: %s", attr);
  8427. return;
  8428. }
  8429. DFUQSpecialFilter filterName = (DFUQSpecialFilter) atoi(attr);
  8430. switch(filterName)
  8431. {
  8432. case DFUQSFFileNameWithPrefix:
  8433. wildNameFilter.set(value);
  8434. break;
  8435. case DFUQSFFileType:
  8436. if (isdigit(*value))
  8437. fileTypeFilter = (DFUQFileTypeFilter) atoi(value);
  8438. else
  8439. PROGLOG("Unsupported Special Filter: %s, value %s", attr, value);
  8440. break;
  8441. case DFUQSFMaxFiles:
  8442. if (isdigit(*value))
  8443. maxFilesFilter = atoi(value);
  8444. else
  8445. PROGLOG("Unsupported Special Filter: %s, value %s", attr, value);
  8446. break;
  8447. default:
  8448. PROGLOG("Unsupported Special Filter: %d", filterName);
  8449. break;
  8450. }
  8451. }
  8452. bool doWildMatch(const char* filter, const char* value)
  8453. {
  8454. if (!filter || !*filter || streq(filter, "*") || (value && WildMatch(value, filter, true)))
  8455. return true;
  8456. return false;
  8457. }
  8458. public:
  8459. CIterateFileFilterContainer()
  8460. {
  8461. maxFilesFilter = ITERATE_FILTEREDFILES_LIMIT;
  8462. fileTypeFilter = DFUQFFTall;
  8463. wildNameFilter.set("*");
  8464. filterBuf.clear();
  8465. };
  8466. void readFilters(const char *filterStr)
  8467. {
  8468. if (!filterStr || !*filterStr)
  8469. return;
  8470. filterBuf.set(filterStr);
  8471. StringArray filterStringArray;
  8472. char sep[] = { DFUQFilterSeparator, '\0' };
  8473. filterStringArray.appendList(filterStr, sep);
  8474. unsigned filterFieldsToRead = filterStringArray.length();
  8475. ForEachItemIn(i,filterStringArray)
  8476. {
  8477. const char* filterTypeStr = filterStringArray.item(i);
  8478. if (!filterTypeStr || !*filterTypeStr)
  8479. continue;
  8480. if (!isdigit(*filterTypeStr))
  8481. continue;
  8482. unsigned filterSize = 4;
  8483. DFUQFilterType filterType = (DFUQFilterType) atoi(filterTypeStr);
  8484. switch(filterType)
  8485. {
  8486. case DFUQFTcontainString:
  8487. if (filterFieldsToRead >= filterSize) //DFUQFilterType | filter name | separator | filter value separated by the separator
  8488. addFilterArray(DFUQFTcontainString, filterStringArray.item(i+1), (const char*)filterStringArray.item(i+2), (const char*)filterStringArray.item(i+3));
  8489. break;
  8490. case DFUQFThasProp:
  8491. case DFUQFTbooleanMatch:
  8492. case DFUQFTwildcardMatch:
  8493. filterSize = 3;
  8494. if (filterFieldsToRead >= filterSize) //DFUQFilterType | filter name | filter value
  8495. addFilter(filterType, filterStringArray.item(i+1), (const char*)filterStringArray.item(i+2), NULL);
  8496. break;
  8497. case DFUQFTstringRange:
  8498. case DFUQFTintegerRange:
  8499. case DFUQFTinteger64Range:
  8500. if (filterFieldsToRead >= filterSize) //DFUQFilterType | filter name | from filter | to filter
  8501. addFilter(filterType, filterStringArray.item(i+1), (const char*)filterStringArray.item(i+2), (const char*)filterStringArray.item(i+3));
  8502. break;
  8503. case DFUQFTincludeFileAttr:
  8504. filterSize = 2;
  8505. if (filterFieldsToRead >= filterSize) //DFUQFilterType | filter
  8506. addOption(filterStringArray.item(i+1));
  8507. break;
  8508. case DFUQFTspecial:
  8509. filterSize = 3;
  8510. if (filterFieldsToRead >= filterSize) //DFUQFilterType | filter name | filter value
  8511. addSpecialFilter(filterStringArray.item(i+1), (const char*)filterStringArray.item(i+2));
  8512. break;
  8513. }
  8514. filterFieldsToRead -= filterSize;
  8515. i += (filterSize - 1);
  8516. }
  8517. }
  8518. bool matchFileScanFilter(const char* name, IPropertyTree &file)
  8519. {
  8520. if (!doWildMatch(wildNameFilter.get(), name))
  8521. return false;
  8522. if (!filters.length())
  8523. return true;
  8524. ForEachItemIn(i,filters)
  8525. {
  8526. CDFUSFFilter &filter = filters.item(i);
  8527. const char* attrPath = filter.getAttrPath();
  8528. try
  8529. {
  8530. if (!filter.checkFilter(file))
  8531. return false;
  8532. }
  8533. catch (IException *e)
  8534. {
  8535. VStringBuffer msg("Failed to check filter %s for %s: ", attrPath, name);
  8536. int code = e->errorCode();
  8537. e->errorMessage(msg);
  8538. e->Release();
  8539. throw MakeStringException(code, "%s", msg.str());
  8540. }
  8541. }
  8542. return true;
  8543. }
  8544. DFUQFileTypeFilter getFileTypeFilter() { return fileTypeFilter; }
  8545. unsigned getMaxFilesFilter() { return maxFilesFilter; }
  8546. void setFileTypeFilter(DFUQFileTypeFilter _fileType)
  8547. {
  8548. fileTypeFilter = _fileType;
  8549. }
  8550. const char* getNameFilter() { return wildNameFilter.get(); }
  8551. void setNameFilter(const char* _wildName)
  8552. {
  8553. if (!_wildName || !*_wildName)
  8554. return;
  8555. wildNameFilter.set(_wildName);
  8556. }
  8557. SerializeFileAttrOptions& getSerializeFileAttrOptions() { return options; }
  8558. };
  8559. class CFileScanner
  8560. {
  8561. bool recursive;
  8562. bool includesuper;
  8563. StringAttr wildname;
  8564. Owned<CScope> topLevelScope;
  8565. CScope *currentScope;
  8566. Owned<CIterateFileFilterContainer> iterateFileFilterContainer;
  8567. bool scopeMatch(const char *name)
  8568. { // name has trailing '::'
  8569. if (!name || !*name)
  8570. return true;
  8571. const char *s1 = NULL;
  8572. if (!iterateFileFilterContainer)
  8573. s1 = wildname.get();
  8574. else
  8575. s1 = iterateFileFilterContainer->getNameFilter();
  8576. if (!s1 || !*s1)
  8577. return true;
  8578. const char *s2 = name;
  8579. while (*s2) {
  8580. if (*s1=='*') {
  8581. if (recursive)
  8582. return true;
  8583. if (*s2==':')
  8584. return false;
  8585. // '*' can only come at end of scope in non-recursive
  8586. while (*s1&&(*s1!=':'))
  8587. s1++;
  8588. while (*s2&&(*s2!=':'))
  8589. s2++;
  8590. }
  8591. else if ((*s1==*s2)||(*s1=='?')) {
  8592. s1++;
  8593. s2++;
  8594. }
  8595. else
  8596. return false;
  8597. }
  8598. return true;
  8599. }
  8600. bool processScopes(IPropertyTree &root,StringBuffer &name)
  8601. {
  8602. bool ret = false;
  8603. CScope *parentScope = currentScope;
  8604. if (parentScope)
  8605. currentScope = parentScope->addScope(name);
  8606. else
  8607. { // once only
  8608. topLevelScope.setown(new CScope(""));
  8609. currentScope = topLevelScope;
  8610. }
  8611. size32_t ns = name.length();
  8612. if (ns)
  8613. name.append("::");
  8614. size32_t ns2 = name.length();
  8615. if (scopeMatch(name.str())) {
  8616. Owned<IPropertyTreeIterator> iter = root.getElements(queryDfsXmlBranchName(DXB_Scope));
  8617. if (iter->first()) {
  8618. do {
  8619. IPropertyTree &scope = iter->query();
  8620. if (scope.hasChildren()) {
  8621. name.append(scope.queryProp("@name"));
  8622. ret |= processScopes(scope, name);
  8623. name.setLength(ns2);
  8624. }
  8625. } while (iter->next());
  8626. }
  8627. if (!iterateFileFilterContainer)
  8628. ret |= processFiles(root,name);
  8629. else
  8630. ret |= processFilesWithFilters(root,name);
  8631. }
  8632. if (!ret && parentScope)
  8633. parentScope->popLastScope(); // discard scopes where no matches
  8634. currentScope = parentScope;
  8635. name.setLength(ns);
  8636. return ret;
  8637. }
  8638. bool processFiles(IPropertyTree &root,StringBuffer &name)
  8639. {
  8640. bool ret = false;
  8641. const char *s1 = wildname.get();
  8642. size32_t ns = name.length();
  8643. Owned<IPropertyTreeIterator> iter = root.getElements(queryDfsXmlBranchName(DXB_File));
  8644. if (iter->first()) {
  8645. IPropertyTree &scope = iter->query();
  8646. do {
  8647. IPropertyTree &file = iter->query();
  8648. name.append(file.queryProp("@name"));
  8649. if (!s1||WildMatch(name.str(),s1,true)) {
  8650. currentScope->addMatch(name,file,false);
  8651. ret = true;
  8652. }
  8653. name.setLength(ns);
  8654. } while (iter->next());
  8655. }
  8656. if (includesuper) {
  8657. iter.setown(root.getElements(queryDfsXmlBranchName(DXB_SuperFile)));
  8658. if (iter->first()) {
  8659. do {
  8660. IPropertyTree &file = iter->query();
  8661. name.append(file.queryProp("@name"));
  8662. if (!s1||WildMatch(name.str(),s1,true)) {
  8663. currentScope->addMatch(name,file,true);
  8664. ret = true;
  8665. }
  8666. name.setLength(ns);
  8667. } while (iter->next());
  8668. }
  8669. }
  8670. return ret;
  8671. }
  8672. bool processFilesWithFilters(IPropertyTree &root, StringBuffer &name)
  8673. {
  8674. bool ret = false;
  8675. size32_t ns = name.length();
  8676. DFUQFileTypeFilter fileTypeFilter = iterateFileFilterContainer->getFileTypeFilter();
  8677. if (fileTypeFilter != DFUQFFTsuperfileonly)
  8678. addMatchedFiles(root.getElements(queryDfsXmlBranchName(DXB_File)), false, name, ns, ret);
  8679. if ((fileTypeFilter == DFUQFFTall) || (fileTypeFilter == DFUQFFTsuperfileonly))
  8680. addMatchedFiles(root.getElements(queryDfsXmlBranchName(DXB_SuperFile)), true, name, ns, ret);
  8681. return ret;
  8682. }
  8683. void addMatchedFiles(IPropertyTreeIterator* files, bool isSuper, StringBuffer &name, size32_t ns, bool& ret)
  8684. {
  8685. Owned<IPropertyTreeIterator> iter = files;
  8686. ForEach(*iter)
  8687. {
  8688. IPropertyTree &file = iter->query();
  8689. name.append(file.queryProp("@name"));
  8690. if (iterateFileFilterContainer->matchFileScanFilter(name.str(), file))
  8691. {
  8692. currentScope->addMatch(name,file,isSuper);
  8693. ret = true;
  8694. }
  8695. name.setLength(ns);
  8696. }
  8697. }
  8698. public:
  8699. void scan(IPropertyTree *sroot, const char *_wildname,bool _recursive,bool _includesuper)
  8700. {
  8701. if (_wildname)
  8702. wildname.set(_wildname);
  8703. else
  8704. wildname.clear();
  8705. recursive = _recursive;
  8706. includesuper = _includesuper;
  8707. StringBuffer name;
  8708. topLevelScope.clear();
  8709. currentScope = NULL;
  8710. processScopes(*sroot->queryPropTree(querySdsFilesRoot()),name);
  8711. }
  8712. void scan(IPropertyTree *sroot, CIterateFileFilterContainer* _iterateFileFilterContainer, bool _recursive)
  8713. {
  8714. iterateFileFilterContainer.setown(_iterateFileFilterContainer);
  8715. recursive = _recursive;
  8716. StringBuffer name;
  8717. topLevelScope.clear();
  8718. currentScope = NULL;
  8719. processScopes(*sroot->queryPropTree(querySdsFilesRoot()),name);
  8720. }
  8721. void _getResults(bool auth, IUserDescriptor *user, CScope &scope, CFileMatchArray &matchingFiles, StringArray &authScopes,
  8722. unsigned &count, bool checkFileCount)
  8723. {
  8724. if (auth)
  8725. {
  8726. SecAccessFlags perm = getScopePermissions(scope.getName(),user,0); // don't audit
  8727. if (!HASREADPERMISSION(perm))
  8728. return;
  8729. authScopes.append(scope.getName());
  8730. }
  8731. CFileMatchArray &files = scope.queryFiles();
  8732. ForEachItemIn(f, files)
  8733. {
  8734. if (checkFileCount && (count == iterateFileFilterContainer->getMaxFilesFilter()))
  8735. throw MakeStringException(DFSERR_PassIterateFilesLimit, "CFileScanner::_getResults() found >%d files.",
  8736. iterateFileFilterContainer->getMaxFilesFilter());
  8737. CFileMatch *match = &files.item(f);
  8738. matchingFiles.append(*LINK(match));
  8739. ++count;
  8740. }
  8741. CScopeArray &subScopes = scope.querySubScopes();
  8742. ForEachItemIn(s, subScopes)
  8743. {
  8744. CScope &subScope = subScopes.item(s);
  8745. _getResults(auth, user, subScope, matchingFiles, authScopes, count, checkFileCount);
  8746. }
  8747. }
  8748. unsigned getResults(bool auth, IUserDescriptor *user, CFileMatchArray &matchingFiles, StringArray &authScopes, unsigned &count, bool checkFileCount)
  8749. {
  8750. _getResults(auth, user, *topLevelScope, matchingFiles, authScopes, count, checkFileCount);
  8751. return count;
  8752. }
  8753. };
  8754. StringBuffer &getClusterGroupName(const IPropertyTree &cluster, StringBuffer &groupName)
  8755. {
  8756. const char *name = cluster.queryProp("@name");
  8757. const char *nodeGroupName = cluster.queryProp("@nodeGroup");
  8758. if (nodeGroupName && *nodeGroupName)
  8759. name = nodeGroupName;
  8760. groupName.append(name);
  8761. return groupName.trim().toLowerCase();
  8762. }
  8763. StringBuffer &getClusterSpareGroupName(const IPropertyTree &cluster, StringBuffer &groupName)
  8764. {
  8765. return getClusterGroupName(cluster, groupName).append("_spares");
  8766. }
  8767. // JCSMORE - dfs group handling may be clearer if in own module
  8768. class CInitGroups
  8769. {
  8770. std::unordered_map<std::string, std::string> machineMap;
  8771. CConnectLock groupsconnlock;
  8772. StringArray clusternames;
  8773. unsigned defaultTimeout;
  8774. bool machinesLoaded;
  8775. GroupType getGroupType(const char *type)
  8776. {
  8777. if (0 == strcmp("ThorCluster", type))
  8778. return grp_thor;
  8779. else if (0 == strcmp("RoxieCluster", type))
  8780. return grp_roxie;
  8781. else
  8782. throwUnexpected();
  8783. }
  8784. bool clusterGroupCompare(IPropertyTree *newClusterGroup, IPropertyTree *oldClusterGroup)
  8785. {
  8786. if (!newClusterGroup && !oldClusterGroup)
  8787. return true; // i.e. both missing, so match
  8788. else if (!newClusterGroup || !oldClusterGroup)
  8789. return false; // i.e. one of them (not both) missing, so mismatch
  8790. // else // neither missing
  8791. // see if identical
  8792. const char *oldKind = oldClusterGroup->queryProp("@kind");
  8793. const char *oldDir = oldClusterGroup->queryProp("@dir");
  8794. const char *newKind = newClusterGroup->queryProp("@kind");
  8795. const char *newDir = newClusterGroup->queryProp("@dir");
  8796. if (oldKind)
  8797. {
  8798. if (newKind)
  8799. {
  8800. if (!streq(newKind, newKind))
  8801. return false;
  8802. }
  8803. else
  8804. return false;
  8805. }
  8806. else if (newKind)
  8807. return false;
  8808. if (oldDir)
  8809. {
  8810. if (newDir)
  8811. {
  8812. if (!streq(newDir,oldDir))
  8813. return false;
  8814. }
  8815. else
  8816. return false;
  8817. }
  8818. else if (NULL!=newDir)
  8819. return false;
  8820. unsigned oldGroupCount = oldClusterGroup->getCount("Node");
  8821. unsigned newGroupCount = newClusterGroup->getCount("Node");
  8822. if (oldGroupCount != newGroupCount)
  8823. return false;
  8824. if (0 == newGroupCount)
  8825. return true;
  8826. Owned<IPropertyTreeIterator> newIter = newClusterGroup->getElements("Node");
  8827. Owned<IPropertyTreeIterator> oldIter = oldClusterGroup->getElements("Node");
  8828. if (newIter->first() && oldIter->first())
  8829. {
  8830. for (;;)
  8831. {
  8832. // NB: for legacy reason these are called @ip in Dali, but they should typically be hostnames
  8833. SocketEndpoint oldEp, newEp;
  8834. oldEp.set(oldIter->query().queryProp("@ip"));
  8835. newEp.set(newIter->query().queryProp("@ip"));
  8836. if (oldEp != newEp)
  8837. return false;
  8838. if (!oldIter->next() || !newIter->next())
  8839. break;
  8840. }
  8841. }
  8842. return true;
  8843. }
  8844. void addClusterGroup(const char *name, IPropertyTree *newClusterGroup, bool realCluster)
  8845. {
  8846. VStringBuffer prop("Group[@name=\"%s\"]", name);
  8847. IPropertyTree *root = groupsconnlock.conn->queryRoot();
  8848. IPropertyTree *old = root->queryPropTree(prop.str());
  8849. if (old) {
  8850. // JCSMORE
  8851. // clone
  8852. // iterate through files and point to clone
  8853. // i) if change is minor, worth swapping to new group anyway?
  8854. // ii) if old group has machines that are no longer in new environment, mark file bad?
  8855. root->removeTree(old);
  8856. }
  8857. if (!newClusterGroup)
  8858. return;
  8859. if (realCluster)
  8860. clusternames.append(name);
  8861. IPropertyTree *grp = root->addPropTree("Group", newClusterGroup);
  8862. grp->setProp("@name", name);
  8863. }
  8864. IGroup *getGroupFromCluster(GroupType groupType, const IPropertyTree &cluster, bool expand)
  8865. {
  8866. Owned<IPropertyTree> groupTree = createClusterGroupFromEnvCluster(groupType, cluster, nullptr, false, expand);
  8867. if (!groupTree)
  8868. return nullptr;
  8869. Owned<IPropertyTreeIterator> nodeIter = groupTree->getElements("Node");
  8870. if (!nodeIter->first())
  8871. return nullptr;
  8872. SocketEndpointArray eps;
  8873. do
  8874. {
  8875. SocketEndpoint ep(nodeIter->query().queryProp("@ip"));
  8876. eps.append(ep);
  8877. }
  8878. while (nodeIter->next());
  8879. return createIGroup(eps);
  8880. }
  8881. bool loadMachineMap(const IPropertyTree *env)
  8882. {
  8883. if (machinesLoaded)
  8884. return true;
  8885. Owned<IPropertyTreeIterator> machines = env->getElements("Hardware/Computer");
  8886. if (!machines->first())
  8887. {
  8888. IWARNLOG("No Hardware/Computer's found");
  8889. return false;
  8890. }
  8891. do
  8892. {
  8893. const IPropertyTree &machine = machines->query();
  8894. const char *host = machine.queryProp("@netAddress");
  8895. const char *name = machine.queryProp("@name");
  8896. machineMap.insert({ name, host });
  8897. }
  8898. while (machines->next());
  8899. machinesLoaded = true;
  8900. return true;
  8901. }
  8902. bool loadMachineMap()
  8903. {
  8904. if (machinesLoaded)
  8905. return true;
  8906. //GH->JCS This can't be changed to use getEnvironmentFactory() unless that moved inside dalibase;
  8907. Owned<IRemoteConnection> conn = querySDS().connect("/Environment", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
  8908. if (!conn)
  8909. {
  8910. IWARNLOG("Cannot connect to /Environment");
  8911. return false;
  8912. }
  8913. return loadMachineMap(conn->queryRoot());
  8914. }
  8915. IPropertyTree *createClusterGroup(GroupType groupType, const std::vector<std::string> &hosts, const char *dir, const IPropertyTree * envCluster, bool realCluster, bool _expand)
  8916. {
  8917. bool expand = _expand;
  8918. if (grp_thor != groupType)
  8919. expand = false;
  8920. Owned<IPropertyTree> cluster = createPTree("Group");
  8921. if (realCluster)
  8922. cluster->setPropBool("@cluster", true);
  8923. const char *kind=nullptr;
  8924. switch (groupType)
  8925. {
  8926. case grp_thor:
  8927. kind = "Thor";
  8928. break;
  8929. case grp_roxie:
  8930. kind = "Roxie";
  8931. break;
  8932. case grp_hthor:
  8933. kind = "hthor";
  8934. break;
  8935. }
  8936. if (kind)
  8937. cluster->setProp("@kind",kind);
  8938. if (dir)
  8939. cluster->setProp("@dir",dir);
  8940. auto addHostsToIPTFunc = [cluster, &hosts]()
  8941. {
  8942. for (auto &host: hosts)
  8943. {
  8944. IPropertyTree *node = cluster->addPropTree("Node");
  8945. node->setProp("@ip", host.c_str());
  8946. }
  8947. };
  8948. if (expand)
  8949. {
  8950. assertex(envCluster);
  8951. unsigned slavesPerNode = envCluster->getPropInt("@slavesPerNode", 1);
  8952. unsigned channelsPerSlave = envCluster->getPropInt("@channelsPerSlave", 1);
  8953. for (unsigned s=0; s<(slavesPerNode*channelsPerSlave); s++)
  8954. addHostsToIPTFunc();
  8955. }
  8956. else
  8957. addHostsToIPTFunc();
  8958. return cluster.getClear();
  8959. }
  8960. IPropertyTree *createClusterGroupFromEnvCluster(GroupType groupType, const IPropertyTree &cluster, const char *dir, bool realCluster, bool expand)
  8961. {
  8962. const char *processName=nullptr;
  8963. switch (groupType)
  8964. {
  8965. case grp_thor:
  8966. processName = "ThorSlaveProcess";
  8967. break;
  8968. case grp_thorspares:
  8969. processName = "ThorSpareProcess";
  8970. break;
  8971. case grp_roxie:
  8972. processName = "RoxieServerProcess";
  8973. break;
  8974. default:
  8975. throwUnexpected();
  8976. }
  8977. std::vector<std::string> hosts;
  8978. Owned<IPropertyTreeIterator> nodes = cluster.getElements(processName);
  8979. ForEach(*nodes)
  8980. {
  8981. IPropertyTree &node = nodes->query();
  8982. const char *computer = node.queryProp("@computer");
  8983. const char *host = nullptr;
  8984. if (!isEmptyString(computer))
  8985. {
  8986. auto it = machineMap.find(computer);
  8987. if (it == machineMap.end())
  8988. {
  8989. OERRLOG("Cannot construct %s, computer name %s not found\n", cluster.queryProp("@name"), computer);
  8990. return nullptr;
  8991. }
  8992. host = it->second.c_str();
  8993. }
  8994. else
  8995. {
  8996. host = node.queryProp("@netAddress");
  8997. if (isEmptyString(host))
  8998. {
  8999. OERRLOG("Cannot construct %s, missing computer spec on node\n", cluster.queryProp("@name"));
  9000. return nullptr;
  9001. }
  9002. }
  9003. switch (groupType)
  9004. {
  9005. case grp_roxie:
  9006. // Redundant copies are located via the flags.
  9007. // Old environments may contain duplicated sever information for multiple ports
  9008. if (hosts.end() == std::find(hosts.begin(), hosts.end(), host)) // only add if not already there
  9009. hosts.push_back(host);
  9010. break;
  9011. case grp_thor:
  9012. case grp_thorspares:
  9013. hosts.push_back(host);
  9014. break;
  9015. default:
  9016. throwUnexpected();
  9017. }
  9018. }
  9019. if (!hosts.size())
  9020. return nullptr;
  9021. return createClusterGroup(groupType, hosts, dir, &cluster, realCluster, expand);
  9022. }
  9023. bool constructGroup(const IPropertyTree &cluster, const char *altName, IPropertyTree *oldEnvCluster, GroupType groupType, bool force, StringBuffer &messages)
  9024. {
  9025. /* a 'realCluster' is a cluster who's name matches it's nodeGroup
  9026. * if the nodeGroup differs it implies it's sharing the nodeGroup with other thor instance(s).
  9027. */
  9028. bool realCluster = true;
  9029. bool oldRealCluster = true;
  9030. StringBuffer gname, oldGname;
  9031. const char *defDir = NULL;
  9032. switch (groupType)
  9033. {
  9034. case grp_thor:
  9035. getClusterGroupName(cluster, gname);
  9036. if (!streq(cluster.queryProp("@name"), gname.str()))
  9037. realCluster = false;
  9038. if (oldEnvCluster)
  9039. {
  9040. getClusterGroupName(*oldEnvCluster, oldGname);
  9041. if (!streq(oldEnvCluster->queryProp("@name"), oldGname.str()))
  9042. oldRealCluster = false;
  9043. }
  9044. break;
  9045. case grp_thorspares:
  9046. getClusterSpareGroupName(cluster, gname);
  9047. oldRealCluster = realCluster = false;
  9048. break;
  9049. case grp_roxie:
  9050. gname.append(cluster.queryProp("@name"));
  9051. break;
  9052. default:
  9053. throwUnexpected();
  9054. }
  9055. if (altName)
  9056. gname.clear().append(altName).toLowerCase();
  9057. IPropertyTree *existingClusterGroup = queryExistingGroup(gname);
  9058. bool matchOldEnv = false;
  9059. Owned<IPropertyTree> newClusterGroup = createClusterGroupFromEnvCluster(groupType, cluster, defDir, realCluster, true);
  9060. bool matchExisting = !force && clusterGroupCompare(newClusterGroup, existingClusterGroup);
  9061. if (oldEnvCluster)
  9062. {
  9063. // new matches old, only if neither has changed it's name to mismatch it's nodeGroup name
  9064. if (realCluster == oldRealCluster)
  9065. {
  9066. Owned<IPropertyTree> oldClusterGroup = createClusterGroupFromEnvCluster(groupType, *oldEnvCluster, defDir, oldRealCluster, true);
  9067. matchOldEnv = clusterGroupCompare(newClusterGroup, oldClusterGroup);
  9068. }
  9069. else
  9070. matchOldEnv = false;
  9071. }
  9072. if (!matchExisting)
  9073. {
  9074. if (force)
  9075. {
  9076. VStringBuffer msg("Forcing new group layout for %s [ matched active = false, matched old environment = %s ]", gname.str(), matchOldEnv?"true":"false");
  9077. UWARNLOG("%s", msg.str());
  9078. messages.append(msg).newline();
  9079. matchOldEnv = false;
  9080. }
  9081. else
  9082. {
  9083. VStringBuffer msg("Active cluster '%s' group layout does not match environment [matched old environment=%s]", gname.str(), matchOldEnv?"true":"false");
  9084. UWARNLOG("%s", msg.str()); \
  9085. messages.append(msg).newline();
  9086. if (existingClusterGroup)
  9087. {
  9088. // NB: not used at moment, but may help spot clusters that have swapped nodes
  9089. existingClusterGroup->setPropBool("@mismatched", true);
  9090. }
  9091. }
  9092. }
  9093. if ((!existingClusterGroup && (grp_thorspares != groupType)) || (!matchExisting && !matchOldEnv))
  9094. {
  9095. VStringBuffer msg("New cluster layout for cluster %s", gname.str());
  9096. UWARNLOG("%s", msg.str());
  9097. messages.append(msg).newline();
  9098. addClusterGroup(gname.str(), newClusterGroup.getClear(), realCluster);
  9099. return true;
  9100. }
  9101. return false;
  9102. }
  9103. void constructHThorGroups(IPropertyTree &cluster)
  9104. {
  9105. const char *groupname = cluster.queryProp("@name");
  9106. if (!groupname || !*groupname)
  9107. return;
  9108. unsigned ins = 0;
  9109. Owned<IPropertyTreeIterator> insts = cluster.getElements("Instance");
  9110. ForEach(*insts)
  9111. {
  9112. const char *na = insts->query().queryProp("@netAddress");
  9113. if (!isEmptyString(na))
  9114. {
  9115. SocketEndpoint ep(na);
  9116. if (!ep.isNull())
  9117. {
  9118. ins++;
  9119. VStringBuffer gname("hthor__%s", groupname);
  9120. if (ins>1)
  9121. gname.append('_').append(ins);
  9122. Owned<IPropertyTree> clusterGroup = createClusterGroup(grp_hthor, { na }, nullptr, &cluster, true, false);
  9123. addClusterGroup(gname.str(), clusterGroup.getClear(), true);
  9124. }
  9125. }
  9126. }
  9127. }
  9128. struct BoundHost
  9129. {
  9130. BoundHost(const std::string &_host) : host(_host), ep(_host.c_str()) { }
  9131. std::string host;
  9132. SocketEndpoint ep;
  9133. bool operator == (const SocketEndpoint &other) const
  9134. {
  9135. return ep == other;
  9136. }
  9137. };
  9138. unsigned bind(const std::vector<std::string> &hosts, std::vector<BoundHost> &boundHosts) const
  9139. {
  9140. for (const auto &host: hosts)
  9141. {
  9142. SocketEndpoint boundHost(host.c_str());
  9143. if (boundHosts.end() == std::find(boundHosts.begin(), boundHosts.end(), boundHost))
  9144. boundHosts.push_back(BoundHost(host));
  9145. }
  9146. return boundHosts.size();
  9147. }
  9148. IPropertyTree *queryExistingSpareGroup(const IPropertyTree *cluster, StringBuffer &groupName)
  9149. {
  9150. getClusterSpareGroupName(*cluster, groupName);
  9151. IPropertyTree *root = groupsconnlock.conn->queryRoot();
  9152. VStringBuffer xpath("Group[@name=\"%s\"]", groupName.str());
  9153. return root->queryPropTree(xpath.str());
  9154. }
  9155. public:
  9156. CInitGroups(unsigned _defaultTimeout)
  9157. : groupsconnlock("constructGroup",SDS_GROUPSTORE_ROOT,true,false,false,_defaultTimeout)
  9158. {
  9159. defaultTimeout = _defaultTimeout;
  9160. machinesLoaded = false;
  9161. }
  9162. IPropertyTree *queryCluster(const IPropertyTree *env, const char *_clusterName, const char *type, const char *msg, StringBuffer &messages)
  9163. {
  9164. if (isEmptyString(_clusterName) || isEmptyString(type))
  9165. return nullptr;
  9166. if (!streq("ThorCluster", type)) // currently only Thor supported here.
  9167. return nullptr;
  9168. StringAttr clusterName = _clusterName;
  9169. clusterName.toLowerCase();
  9170. if (loadMachineMap())
  9171. {
  9172. VStringBuffer xpath("Software/%s[@name=\"%s\"]", type, clusterName.get());
  9173. Owned<IPropertyTreeIterator> clusterIter = env->getElements(xpath);
  9174. if (!clusterIter->first())
  9175. {
  9176. VStringBuffer errMsg("%s: Could not find type %s, %s cluster", msg, type, clusterName.get());
  9177. UWARNLOG("%s", errMsg.str());
  9178. messages.append(errMsg).newline();
  9179. }
  9180. else
  9181. {
  9182. IPropertyTree *cluster = &clusterIter->query();
  9183. if (!clusterIter->next())
  9184. return cluster;
  9185. VStringBuffer errMsg("%s: more than one cluster named: %s", msg, clusterName.get());
  9186. UWARNLOG("%s", errMsg.str());
  9187. messages.append(errMsg).newline();
  9188. }
  9189. }
  9190. return nullptr;
  9191. }
  9192. bool resetClusterGroup(const char *clusterName, const char *type, bool spares, StringBuffer &messages)
  9193. {
  9194. Owned<IRemoteConnection> conn = querySDS().connect("/Environment", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
  9195. if (!conn)
  9196. return false;
  9197. const IPropertyTree *cluster = queryCluster(conn->queryRoot(), clusterName, type, "resetClusterGroup", messages);
  9198. if (!cluster)
  9199. return false;
  9200. if (spares)
  9201. {
  9202. if (constructGroup(*cluster,NULL,NULL,grp_thorspares,true,messages))
  9203. return true;
  9204. }
  9205. else
  9206. {
  9207. if (constructGroup(*cluster,NULL,NULL,grp_thor,true,messages))
  9208. return true;
  9209. }
  9210. return false;
  9211. }
  9212. bool addSpares(const char *clusterName, const char *type, const std::vector<std::string> &hosts, StringBuffer &messages)
  9213. {
  9214. Owned<IRemoteConnection> conn = querySDS().connect("/Environment", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
  9215. if (!conn)
  9216. return false;
  9217. const IPropertyTree *cluster = queryCluster(conn->queryRoot(), clusterName, type, "addSpares", messages);
  9218. if (!cluster)
  9219. return false;
  9220. std::vector<BoundHost> boundHostsToAdd;
  9221. bind(hosts, boundHostsToAdd);
  9222. StringBuffer groupName;
  9223. IPropertyTree *existing = queryExistingSpareGroup(cluster, groupName);
  9224. if (existing)
  9225. {
  9226. Owned<IPropertyTreeIterator> iter = existing->getElements("Node");
  9227. ForEach(*iter)
  9228. {
  9229. const char *host = iter->query().queryProp("@ip");
  9230. SocketEndpoint ep(host); // NB: for legacy reason it's called @ip, but should typically be a hostname
  9231. // delete any entries that are already in Group
  9232. auto it = std::remove(boundHostsToAdd.begin(), boundHostsToAdd.end(), ep);
  9233. if (it != boundHostsToAdd.end())
  9234. {
  9235. boundHostsToAdd.erase(it, boundHostsToAdd.end());
  9236. VStringBuffer errMsg("addSpares: not adding: %s, already in spares", host);
  9237. UWARNLOG("%s", errMsg.str());
  9238. messages.append(errMsg).newline();
  9239. }
  9240. }
  9241. }
  9242. else
  9243. {
  9244. existing = groupsconnlock.conn->queryRoot()->addPropTree("Group");
  9245. existing->setProp("@name", groupName.str());
  9246. }
  9247. // add remaining
  9248. for (const auto &boundHost: boundHostsToAdd)
  9249. {
  9250. IPropertyTree *node = existing->addPropTree("Node");
  9251. node->setProp("@ip", boundHost.host.c_str());
  9252. }
  9253. return true;
  9254. }
  9255. bool removeSpares(const char *clusterName, const char *type, const std::vector<std::string> &hosts, StringBuffer &messages)
  9256. {
  9257. Owned<IRemoteConnection> conn = querySDS().connect("/Environment", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
  9258. if (!conn)
  9259. return false;
  9260. const IPropertyTree *cluster = queryCluster(conn->queryRoot(), clusterName, type, "removeSpares", messages);
  9261. if (!cluster)
  9262. return false;
  9263. std::vector<BoundHost> boundHostsToRemove;
  9264. bind(hosts, boundHostsToRemove);
  9265. StringBuffer groupName;
  9266. IPropertyTree *existing = queryExistingSpareGroup(cluster, groupName);
  9267. if (existing)
  9268. {
  9269. SocketEndpointArray existingGroupBoundEps;
  9270. StringAttr groupDir;
  9271. GroupType type;
  9272. if (!loadGroup(existing, existingGroupBoundEps, nullptr, nullptr))
  9273. {
  9274. IWARNLOG("removeSpares: failed to load group: '%s'", groupName.str());
  9275. return false;
  9276. }
  9277. for (const auto &boundHostToRemove: boundHostsToRemove)
  9278. {
  9279. bool matched = true;
  9280. ForEachItemIn(e, existingGroupBoundEps)
  9281. {
  9282. if (existingGroupBoundEps.item(e) == boundHostToRemove.ep)
  9283. {
  9284. VStringBuffer xpath("Node[%u]", e+1);
  9285. verifyex(existing->removeProp(xpath));
  9286. matched = true;
  9287. }
  9288. // there shouldn't be any others, but in keeping with legacy code, continue matching
  9289. }
  9290. if (!matched)
  9291. {
  9292. VStringBuffer errMsg("removeSpares: %s not found in spares", boundHostToRemove.host.c_str());
  9293. UWARNLOG("%s", errMsg.str());
  9294. messages.append(errMsg).newline();
  9295. }
  9296. }
  9297. }
  9298. return true;
  9299. }
  9300. void constructGroups(bool force, StringBuffer &messages, IPropertyTree *oldEnvironment)
  9301. {
  9302. Owned<IRemoteConnection> conn = querySDS().connect("/Environment/Software", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
  9303. if (!conn)
  9304. return;
  9305. clusternames.kill();
  9306. IPropertyTree* root = conn->queryRoot();
  9307. Owned<IPropertyTreeIterator> clusters;
  9308. if (loadMachineMap()) {
  9309. clusters.setown(root->getElements("ThorCluster"));
  9310. ForEach(*clusters) {
  9311. IPropertyTree &cluster = clusters->query();
  9312. IPropertyTree *oldCluster = NULL;
  9313. if (oldEnvironment) {
  9314. VStringBuffer xpath("Software/ThorCluster[@name=\"%s\"]", cluster.queryProp("@name"));
  9315. oldCluster = oldEnvironment->queryPropTree(xpath.str());
  9316. }
  9317. constructGroup(cluster,NULL,oldCluster,grp_thor,force,messages);
  9318. constructGroup(cluster,NULL,oldCluster,grp_thorspares,force,messages);
  9319. }
  9320. clusters.setown(root->getElements("RoxieCluster"));
  9321. ForEach(*clusters) {
  9322. IPropertyTree &cluster = clusters->query();
  9323. IPropertyTree *oldCluster = NULL;
  9324. if (oldEnvironment) {
  9325. VStringBuffer xpath("Software/RoxieCluster[@name=\"%s\"]", cluster.queryProp("@name"));
  9326. oldCluster = oldEnvironment->queryPropTree(xpath.str());
  9327. }
  9328. constructGroup(cluster,NULL,oldCluster,grp_roxie,force,messages);
  9329. }
  9330. clusters.setown(root->getElements("EclAgentProcess"));
  9331. ForEach(*clusters) {
  9332. IPropertyTree &cluster = clusters->query();
  9333. constructHThorGroups(cluster);
  9334. }
  9335. // correct cluster flags
  9336. // JCSMORE - why was this necessary, may well be legacy..
  9337. Owned<IPropertyTreeIterator> grps = groupsconnlock.conn->queryRoot()->getElements("Group");
  9338. ForEach(*grps) {
  9339. IPropertyTree &grp = grps->query();
  9340. const char *name = grp.queryProp("@name");
  9341. bool iscluster = NotFound != clusternames.find(name);
  9342. if (iscluster!=grp.getPropBool("@cluster"))
  9343. {
  9344. if (iscluster)
  9345. grp.setPropBool("@cluster", true);
  9346. else
  9347. grp.removeProp("@cluster");
  9348. }
  9349. }
  9350. }
  9351. }
  9352. IPropertyTree * createStorageGroup(const char * name, size32_t size, const char * path)
  9353. {
  9354. std::vector<std::string> hosts(size, "localhost");
  9355. return createClusterGroup(grp_unknown, hosts, path, nullptr, false, false);
  9356. }
  9357. void ensureStorageGroup(bool force, const char * name, unsigned numDevices, const char * path, StringBuffer & messages)
  9358. {
  9359. IPropertyTree *existingClusterGroup = queryExistingGroup(name);
  9360. Owned<IPropertyTree> newClusterGroup = createStorageGroup(name, numDevices, path);
  9361. bool matchExisting = clusterGroupCompare(newClusterGroup, existingClusterGroup);
  9362. if (!existingClusterGroup || !matchExisting)
  9363. {
  9364. if (!existingClusterGroup)
  9365. {
  9366. VStringBuffer msg("New cluster layout for cluster %s", name);
  9367. UWARNLOG("%s", msg.str());
  9368. messages.append(msg).newline();
  9369. addClusterGroup(name, newClusterGroup.getClear(), false);
  9370. }
  9371. else if (force)
  9372. {
  9373. VStringBuffer msg("Forcing new group layout for storageplane %s", name);
  9374. UWARNLOG("%s", msg.str());
  9375. messages.append(msg).newline();
  9376. addClusterGroup(name, newClusterGroup.getClear(), false);
  9377. }
  9378. else
  9379. {
  9380. VStringBuffer msg("Active cluster '%s' group layout does not match stroageplane definition", name);
  9381. UWARNLOG("%s", msg.str()); \
  9382. messages.append(msg).newline();
  9383. }
  9384. }
  9385. }
  9386. void constructStorageGroups(bool force, StringBuffer &messages)
  9387. {
  9388. IPropertyTree & global = queryGlobalConfig();
  9389. IPropertyTree * storage = global.queryPropTree("storage");
  9390. if (storage)
  9391. {
  9392. Owned<IPropertyTreeIterator> planes = storage->getElements("planes");
  9393. ForEach(*planes)
  9394. {
  9395. IPropertyTree & plane = planes->query();
  9396. const char * name = plane.queryProp("@name");
  9397. if (isEmptyString(name))
  9398. continue;
  9399. //Lower case the group name - see CnamedGroupStore::dolookup which lower cases before resolving.
  9400. StringBuffer gname;
  9401. gname.append(name).toLowerCase();
  9402. //Two main type of storage plane - with a host group (bare metal) and without.
  9403. IPropertyTree *existingGroup = queryExistingGroup(gname);
  9404. const char * hosts = plane.queryProp("@hosts");
  9405. const char * prefix = plane.queryProp("@prefix");
  9406. if (hosts)
  9407. {
  9408. IPropertyTree *existingClusterGroup = queryExistingGroup(gname);
  9409. if (!existingClusterGroup)
  9410. UNIMPLEMENTED_X("Bare metal storage planes not yet supported");
  9411. }
  9412. else
  9413. {
  9414. unsigned numDevices = plane.getPropInt("@numDevices", 1);
  9415. ensureStorageGroup(force, gname, numDevices, prefix, messages);
  9416. }
  9417. }
  9418. }
  9419. }
  9420. IGroup *getGroupFromCluster(const char *type, const IPropertyTree &cluster, bool expand)
  9421. {
  9422. loadMachineMap();
  9423. GroupType gt = getGroupType(type);
  9424. return getGroupFromCluster(gt, cluster, expand);
  9425. }
  9426. IPropertyTree *queryExistingGroup(const char *name)
  9427. {
  9428. VStringBuffer xpath("Group[@name=\"%s\"]", name);
  9429. return groupsconnlock.conn->queryRoot()->queryPropTree(xpath.str());
  9430. }
  9431. };
  9432. void initClusterGroups(bool force, StringBuffer &response, IPropertyTree *oldEnvironment, unsigned timems)
  9433. {
  9434. CInitGroups init(timems);
  9435. init.constructGroups(force, response, oldEnvironment);
  9436. }
  9437. void initClusterAndStoragePlaneGroups(bool force, IPropertyTree *oldEnvironment, unsigned timems)
  9438. {
  9439. CInitGroups init(timems);
  9440. StringBuffer response;
  9441. init.constructGroups(force, response, oldEnvironment);
  9442. if (response.length())
  9443. PROGLOG("DFS group initialization : %s", response.str()); // should this be a syslog?
  9444. response.clear();
  9445. init.constructStorageGroups(false, response);
  9446. if (response.length())
  9447. PROGLOG("StoragePlane group initialization : %s", response.str()); // should this be a syslog?
  9448. }
  9449. bool resetClusterGroup(const char *clusterName, const char *type, bool spares, StringBuffer &response, unsigned timems)
  9450. {
  9451. CInitGroups init(timems);
  9452. return init.resetClusterGroup(clusterName, type, spares, response);
  9453. }
  9454. bool addClusterSpares(const char *clusterName, const char *type, const std::vector<std::string> &hosts, StringBuffer &response, unsigned timems)
  9455. {
  9456. CInitGroups init(timems);
  9457. return init.addSpares(clusterName, type, hosts, response);
  9458. }
  9459. bool removeClusterSpares(const char *clusterName, const char *type, const std::vector<std::string> &hosts, StringBuffer &response, unsigned timems)
  9460. {
  9461. CInitGroups init(timems);
  9462. return init.removeSpares(clusterName, type, hosts, response);
  9463. }
  9464. static IGroup *getClusterNodeGroup(const char *clusterName, const char *type, bool processGroup, unsigned timems)
  9465. {
  9466. VStringBuffer clusterPath("/Environment/Software/%s[@name=\"%s\"]", type, clusterName);
  9467. Owned<IRemoteConnection> conn = querySDS().connect(clusterPath.str(), myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
  9468. if (!conn)
  9469. return NULL;
  9470. IPropertyTree &cluster = *conn->queryRoot();
  9471. StringBuffer nodeGroupName;
  9472. getClusterGroupName(cluster, nodeGroupName);
  9473. if (0 == nodeGroupName.length())
  9474. throwUnexpected();
  9475. /* 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
  9476. * which may no longer match the cluster IP's due to node swapping.
  9477. * As the node group is an expanded form of the cluster group (with a IP per partition/slave), with the cluster group repeated
  9478. * N times, where N is slavesPerNode*channelsPerSlave, return the first M (cluster group width) IP's of the node group.
  9479. * Ideally the node group representation would change to match the cluster group definition, but that require a lot of changes
  9480. * to DFS and elsewhere.
  9481. */
  9482. Owned<IGroup> nodeGroup = queryNamedGroupStore().lookup(nodeGroupName);
  9483. CInitGroups init(timems);
  9484. Owned<IGroup> expandedClusterGroup = init.getGroupFromCluster(type, cluster, true);
  9485. if (!expandedClusterGroup)
  9486. throwStringExceptionV(0, "Failed to get group for '%s' cluster '%s'", type, clusterName);
  9487. if (!expandedClusterGroup->equals(nodeGroup))
  9488. {
  9489. IPropertyTree *rawGroup = init.queryExistingGroup(nodeGroupName);
  9490. if (!rawGroup)
  9491. throwUnexpectedX("missing node group");
  9492. unsigned nodesSwapped = rawGroup->getPropInt("@nodesSwapped");
  9493. if (nodesSwapped)
  9494. {
  9495. unsigned rawGroupSize = rawGroup->getCount("Node");
  9496. if (rawGroupSize != expandedClusterGroup->ordinality())
  9497. 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]",
  9498. clusterName, nodeGroupName.str(), expandedClusterGroup->ordinality(), rawGroupSize);
  9499. VStringBuffer msg("DFS cluster topology for '%s' using group '%s', does not match environment due to previously swapped nodes", clusterName, nodeGroupName.str());
  9500. WARNLOG("%s", msg.str());
  9501. }
  9502. else
  9503. throwStringExceptionV(0, "DFS cluster topology for '%s', does not match existing DFS group layout for group '%s'", clusterName, nodeGroupName.str());
  9504. }
  9505. Owned<IGroup> clusterGroup = init.getGroupFromCluster(type, cluster, false);
  9506. ICopyArrayOf<INode> nodes;
  9507. unsigned l=processGroup?cluster.getPropInt("@slavesPerNode", 1):1; // if process group requested, repeat clusterGroup slavesPerNode times.
  9508. for (unsigned t=0; t<l; t++)
  9509. {
  9510. for (unsigned n=0; n<clusterGroup->ordinality(); n++)
  9511. nodes.append(nodeGroup->queryNode(n));
  9512. }
  9513. return createIGroup(nodes.ordinality(), nodes.getArray());
  9514. }
  9515. IGroup *getClusterNodeGroup(const char *clusterName, const char *type, unsigned timems)
  9516. {
  9517. return getClusterNodeGroup(clusterName, type, false, timems);
  9518. }
  9519. IGroup *getClusterProcessNodeGroup(const char *clusterName, const char *type, unsigned timems)
  9520. {
  9521. return getClusterNodeGroup(clusterName, type, true, timems);
  9522. }
  9523. class CDaliDFSServer: public Thread, public CTransactionLogTracker, implements IDaliServer, implements IExceptionHandler
  9524. { // Coven size
  9525. bool stopped;
  9526. unsigned defaultTimeout;
  9527. unsigned numThreads;
  9528. public:
  9529. IMPLEMENT_IINTERFACE;
  9530. CDaliDFSServer(IPropertyTree *config)
  9531. : Thread("CDaliDFSServer"), CTransactionLogTracker(MDFS_MAX)
  9532. {
  9533. stopped = true;
  9534. defaultTimeout = INFINITE; // server uses default
  9535. numThreads = config->getPropInt("DFS/@numThreads", DEFAULT_NUM_DFS_THREADS);
  9536. PROGLOG("DFS Server: numThreads=%d", numThreads);
  9537. }
  9538. ~CDaliDFSServer()
  9539. {
  9540. }
  9541. void start()
  9542. {
  9543. Thread::start();
  9544. }
  9545. void ready()
  9546. {
  9547. }
  9548. void suspend()
  9549. {
  9550. }
  9551. void stop()
  9552. {
  9553. if (!stopped) {
  9554. stopped = true;
  9555. queryCoven().cancel(RANK_ALL,MPTAG_DFS_REQUEST);
  9556. }
  9557. join();
  9558. }
  9559. int run()
  9560. {
  9561. ICoven &coven=queryCoven();
  9562. CMessageHandler<CDaliDFSServer> handler("CDaliDFSServer", this, &CDaliDFSServer::processMessage, this, numThreads, TIMEOUT_ON_CLOSEDOWN, INFINITE);
  9563. CMessageBuffer mb;
  9564. stopped = false;
  9565. while (!stopped)
  9566. {
  9567. try
  9568. {
  9569. mb.clear();
  9570. if (coven.recv(mb,RANK_ALL,MPTAG_DFS_REQUEST,NULL))
  9571. {
  9572. handler.handleMessage(mb);
  9573. mb.clear(); // ^ has copied mb
  9574. }
  9575. else
  9576. stopped = true;
  9577. }
  9578. catch (IException *e)
  9579. {
  9580. EXCLOG(e, "CDaliDFSServer");
  9581. e->Release();
  9582. }
  9583. }
  9584. return 0;
  9585. }
  9586. void iterateFiles(CMessageBuffer &mb,StringBuffer &trc)
  9587. {
  9588. TransactionLog transactionLog(*this, MDFS_ITERATE_FILES, mb.getSender());
  9589. StringAttr wildname;
  9590. bool recursive;
  9591. bool includesuper = false;
  9592. StringAttr attr;
  9593. mb.read(wildname).read(recursive).read(attr);
  9594. trc.appendf("iterateFiles(%s,%s,%s)",wildname.str(),recursive?"recursive":"",attr.str());
  9595. if (queryTransactionLogging())
  9596. transactionLog.log("%s", trc.str());
  9597. Owned<IUserDescriptor> udesc;
  9598. if (mb.getPos()<mb.length()) {
  9599. mb.read(includesuper);
  9600. if (mb.getPos()<mb.length()) {
  9601. udesc.setown(createUserDescriptor());
  9602. udesc->deserialize(mb);
  9603. }
  9604. }
  9605. mb.clear();
  9606. unsigned count=0;
  9607. mb.append(count);
  9608. CFileScanner scanner;
  9609. CSDSServerLockBlock sdsLock; // lock sds while scanning
  9610. unsigned start = msTick();
  9611. scanner.scan(sdsLock, wildname.get(),recursive,includesuper);
  9612. unsigned tookMs = msTick()-start;
  9613. if (tookMs>100)
  9614. PROGLOG("TIMING(filescan): %s: took %dms",trc.str(), tookMs);
  9615. sdsLock.unlock(); // unlock to perform authentification
  9616. bool auth = scopePermissionsAvail && querySessionManager().checkScopeScansLDAP();
  9617. StringArray authScopes;
  9618. CIArrayOf<CFileMatch> matchingFiles;
  9619. start = msTick();
  9620. scanner.getResults(auth, udesc, matchingFiles, authScopes, count, false);
  9621. tookMs = msTick()-start;
  9622. if (tookMs>100)
  9623. PROGLOG("TIMING(LDAP): %s: took %dms, %d lookups, file matches = %d", trc.str(), tookMs, authScopes.ordinality(), count);
  9624. sdsLock.lock(); // re-lock sds while serializing
  9625. start = msTick();
  9626. SerializeFileAttrOptions options; //The options is needed for the serializeFileAttributes()
  9627. ForEachItemIn(m, matchingFiles)
  9628. {
  9629. CFileMatch &fileMatch = matchingFiles.item(m);
  9630. CDFAttributeIterator::serializeFileAttributes(mb, fileMatch.queryFileTree(), fileMatch.queryName(), fileMatch.queryIsSuper(), options);
  9631. }
  9632. tookMs = msTick()-start;
  9633. if (tookMs>100)
  9634. PROGLOG("TIMING(filescan-serialization): %s: took %dms, %d files",trc.str(), tookMs, count);
  9635. mb.writeDirect(0,sizeof(count),&count);
  9636. }
  9637. void iterateFilteredFiles(TransactionLog &transactionLog, CMessageBuffer &mb,StringBuffer &trc, bool returnAllFilesFlag)
  9638. {
  9639. Owned<IUserDescriptor> udesc;
  9640. StringAttr filters;
  9641. bool recursive;
  9642. mb.read(filters).read(recursive);
  9643. trc.appendf("iterateFilteredFiles(%s,%s)",filters.str(),recursive?"recursive":"");
  9644. if (queryTransactionLogging())
  9645. transactionLog.log("%s", trc.str());
  9646. if (mb.getPos()<mb.length())
  9647. {
  9648. udesc.setown(createUserDescriptor());
  9649. udesc->deserialize(mb);
  9650. }
  9651. mb.clear();
  9652. unsigned count=0;
  9653. mb.append(count);
  9654. Owned<CIterateFileFilterContainer> iterateFileFilterContainer = new CIterateFileFilterContainer();
  9655. iterateFileFilterContainer->readFilters(filters);
  9656. CFileScanner scanner;
  9657. CSDSServerLockBlock sdsLock; // lock sds while scanning
  9658. unsigned start = msTick();
  9659. scanner.scan(sdsLock, iterateFileFilterContainer.getLink(), recursive);
  9660. unsigned tookMs = msTick()-start;
  9661. if (tookMs>100)
  9662. PROGLOG("TIMING(filescan): %s: took %dms",trc.str(), tookMs);
  9663. sdsLock.unlock(); // unlock to perform authentification
  9664. bool auth = scopePermissionsAvail && querySessionManager().checkScopeScansLDAP();
  9665. StringArray authScopes;
  9666. CIArrayOf<CFileMatch> matchingFiles;
  9667. start = msTick();
  9668. bool returnAllMatchingFiles = true;
  9669. try
  9670. {
  9671. scanner.getResults(auth, udesc, matchingFiles, authScopes, count, true);
  9672. }
  9673. catch(IException *e)
  9674. {
  9675. if (DFSERR_PassIterateFilesLimit != e->errorCode())
  9676. throw;
  9677. e->Release();
  9678. returnAllMatchingFiles = false;
  9679. }
  9680. if (returnAllFilesFlag)
  9681. mb.append(returnAllMatchingFiles);
  9682. tookMs = msTick()-start;
  9683. if (tookMs>100)
  9684. PROGLOG("TIMING(LDAP): %s: took %dms, %d lookups, file matches = %d", trc.str(), tookMs, authScopes.ordinality(), count);
  9685. sdsLock.lock(); // re-lock sds while serializing
  9686. start = msTick();
  9687. ForEachItemIn(m, matchingFiles)
  9688. {
  9689. CFileMatch &fileMatch = matchingFiles.item(m);
  9690. unsigned pos = mb.length();
  9691. try
  9692. {
  9693. CDFAttributeIterator::serializeFileAttributes(mb, fileMatch.queryFileTree(), fileMatch.queryName(), fileMatch.queryIsSuper(), iterateFileFilterContainer->getSerializeFileAttrOptions());
  9694. }
  9695. catch (IException *e)
  9696. {
  9697. StringBuffer errMsg("Failed to serialize properties for file: ");
  9698. LOG(MCuserWarning, e, errMsg.append(fileMatch.queryName()));
  9699. e->Release();
  9700. mb.setLength(pos);
  9701. --count;
  9702. }
  9703. }
  9704. tookMs = msTick()-start;
  9705. if (tookMs>100)
  9706. PROGLOG("TIMING(filescan-serialization): %s: took %dms, %d files",trc.str(), tookMs, count);
  9707. mb.writeDirect(0,sizeof(count),&count);
  9708. }
  9709. void iterateFilteredFiles(CMessageBuffer &mb,StringBuffer &trc)
  9710. {
  9711. TransactionLog transactionLog(*this, MDFS_ITERATE_FILTEREDFILES, mb.getSender());
  9712. iterateFilteredFiles(transactionLog, mb, trc, false);
  9713. }
  9714. void iterateFilteredFiles2(CMessageBuffer &mb,StringBuffer &trc)
  9715. {
  9716. TransactionLog transactionLog(*this, MDFS_ITERATE_FILTEREDFILES2, mb.getSender());
  9717. iterateFilteredFiles(transactionLog, mb, trc, true);
  9718. }
  9719. void iterateRelationships(CMessageBuffer &mb,StringBuffer &trc)
  9720. {
  9721. TransactionLog transactionLog(*this, MDFS_ITERATE_RELATIONSHIPS, mb.getSender());
  9722. StringAttr primary;
  9723. StringAttr secondary;
  9724. StringAttr primflds;
  9725. StringAttr secflds;
  9726. StringAttr kind;
  9727. StringAttr cardinality;
  9728. byte payloadb;
  9729. mb.read(primary).read(secondary).read(primflds).read(secflds).read(kind).read(cardinality).read(payloadb);
  9730. mb.clear();
  9731. bool payload = (payloadb==1);
  9732. trc.appendf("iterateRelationships(%s,%s,%s,%s,%s,%s,%d)",primary.str(),secondary.str(),primflds.str(),secflds.str(),kind.str(),cardinality.str(),(int)payloadb);
  9733. if (queryTransactionLogging())
  9734. transactionLog.log("%s", trc.str());
  9735. unsigned start = msTick();
  9736. unsigned count=0;
  9737. CSDSServerLockBlock sdsLock; // lock sds while scanning
  9738. StringBuffer xpath;
  9739. CDistributedFileDirectory::getFileRelationshipXPath(xpath,primary,secondary,primflds,secflds,kind,cardinality,((payloadb==0)||(payloadb==1))?&payload:NULL);
  9740. IPropertyTree *root = sdsLock->queryPropTree(querySdsRelationshipsRoot());
  9741. Owned<IPropertyTreeIterator> iter = root?root->getElements(xpath.str()):NULL;
  9742. mb.append(count);
  9743. // save as sequence of branches
  9744. if (iter) {
  9745. ForEach(*iter.get()) {
  9746. iter->query().serialize(mb);
  9747. count++;
  9748. }
  9749. }
  9750. if (msTick()-start>100) {
  9751. PROGLOG("TIMING(relationshipscan): %s: took %dms, %d relations",trc.str(),msTick()-start,count);
  9752. }
  9753. mb.writeDirect(0,sizeof(count),&count);
  9754. }
  9755. void setFileAccessed(CMessageBuffer &mb,StringBuffer &trc)
  9756. {
  9757. TransactionLog transactionLog(*this, MDFS_SET_FILE_ACCESSED, mb.getSender());
  9758. StringAttr lname;
  9759. mb.read(lname);
  9760. CDateTime dt;
  9761. dt.deserialize(mb);
  9762. trc.appendf("setFileAccessed(%s)",lname.str());
  9763. Owned<IUserDescriptor> udesc;
  9764. if (mb.getPos()<mb.length()) {
  9765. udesc.setown(createUserDescriptor());
  9766. udesc->deserialize(mb);
  9767. }
  9768. if (queryTransactionLogging())
  9769. transactionLog.log("%s", trc.str());
  9770. mb.clear();
  9771. StringBuffer tail;
  9772. CDfsLogicalFileName dlfn;
  9773. dlfn.set(lname);
  9774. if (!checkLogicalName(dlfn,udesc,true,false,true,"setFileAccessed on"))
  9775. return;
  9776. CScopeConnectLock sconnlock("setFileAccessed", dlfn, false, false, false, defaultTimeout);
  9777. IPropertyTree* sroot = sconnlock.conn()?sconnlock.conn()->queryRoot():NULL;
  9778. dlfn.getTail(tail);
  9779. Owned<IPropertyTree> tree = getNamedPropTree(sroot,queryDfsXmlBranchName(DXB_File),"@name",tail.str(),false);
  9780. if (tree) {
  9781. StringBuffer str;
  9782. tree->setProp("@accessed",dt.getString(str).str());
  9783. }
  9784. }
  9785. void setFileProtect(CMessageBuffer &mb,StringBuffer &trc)
  9786. {
  9787. TransactionLog transactionLog(*this, MDFS_SET_FILE_PROTECT, mb.getSender());
  9788. StringAttr lname;
  9789. StringAttr owner;
  9790. bool set;
  9791. mb.read(lname).read(owner).read(set);
  9792. trc.appendf("setFileProtect(%s,%s,%s)",lname.str(),owner.str(),set?"true":"false");
  9793. if (queryTransactionLogging())
  9794. transactionLog.log("%s", trc.str());
  9795. Owned<IUserDescriptor> udesc;
  9796. if (mb.getPos()<mb.length()) {
  9797. udesc.setown(createUserDescriptor());
  9798. udesc->deserialize(mb);
  9799. }
  9800. mb.clear();
  9801. StringBuffer tail;
  9802. CDfsLogicalFileName dlfn;
  9803. dlfn.set(lname);
  9804. if (!checkLogicalName(dlfn,udesc,true,false,true,"setFileProtect"))
  9805. return;
  9806. CScopeConnectLock sconnlock("setFileProtect", dlfn, false, false, false, defaultTimeout);
  9807. IPropertyTree* sroot = sconnlock.conn()?sconnlock.conn()->queryRoot():NULL;
  9808. dlfn.getTail(tail);
  9809. Owned<IPropertyTree> tree = getNamedPropTree(sroot,queryDfsXmlBranchName(DXB_File),"@name",tail.str(),false);
  9810. if (!tree)
  9811. tree.setown(getNamedPropTree(sroot,queryDfsXmlBranchName(DXB_SuperFile),"@name",tail.str(),false));
  9812. if (tree) {
  9813. IPropertyTree *pt = tree->queryPropTree("Attr");
  9814. if (pt)
  9815. setFileProtectTree(*pt,*owner?owner:owner,set);
  9816. }
  9817. }
  9818. void getFileTree(CMessageBuffer &mb,StringBuffer &trc)
  9819. {
  9820. TransactionLog transactionLog(*this, MDFS_GET_FILE_TREE, mb.getSender());
  9821. StringAttr lname;
  9822. mb.read(lname);
  9823. unsigned ver;
  9824. if (mb.length()<mb.getPos()+sizeof(unsigned))
  9825. ver = 0;
  9826. else {
  9827. mb.read(ver);
  9828. // this is a bit of a mess - for backward compatibility where user descriptor specified
  9829. if (ver>MDFS_GET_FILE_TREE_V2) {
  9830. mb.reset(mb.getPos()-sizeof(unsigned));
  9831. ver = 0;
  9832. }
  9833. }
  9834. trc.appendf("getFileTree(%s,%d)",lname.str(),ver);
  9835. if (queryTransactionLogging())
  9836. transactionLog.log("%s", trc.str());
  9837. Owned<IUserDescriptor> udesc;
  9838. if (mb.getPos()<mb.length()) {
  9839. udesc.setown(createUserDescriptor());
  9840. udesc->deserialize(mb);
  9841. }
  9842. mb.clear();
  9843. CDfsLogicalFileName dlfn;
  9844. dlfn.set(lname);
  9845. CDfsLogicalFileName *logicalname=&dlfn;
  9846. Owned<IDfsLogicalFileNameIterator> redmatch;
  9847. for (;;) {
  9848. StringBuffer tail;
  9849. checkLogicalName(*logicalname,udesc,true,false,true,"getFileTree on");
  9850. CScopeConnectLock sconnlock("getFileTree", *logicalname, false, false, false, defaultTimeout);
  9851. IPropertyTree* sroot = sconnlock.conn()?sconnlock.conn()->queryRoot():NULL;
  9852. logicalname->getTail(tail);
  9853. Owned<IPropertyTree> tree = getNamedPropTree(sroot,queryDfsXmlBranchName(DXB_File),"@name",tail.str(),false);
  9854. if (tree) {
  9855. if (ver>=MDFS_GET_FILE_TREE_V2) {
  9856. Owned<IFileDescriptor> fdesc = deserializeFileDescriptorTree(tree,&queryNamedGroupStore(),IFDSF_EXCLUDE_CLUSTERNAMES);
  9857. if (fdesc) {
  9858. ver = MDFS_GET_FILE_TREE_V2;
  9859. mb.append((int)-2).append(ver);
  9860. fdesc->serialize(mb);
  9861. StringBuffer dts;
  9862. if (tree->getProp("@modified",dts)) {
  9863. CDateTime dt;
  9864. dt.setString(dts.str());
  9865. dt.serialize(mb);
  9866. }
  9867. }
  9868. else
  9869. ver = 0;
  9870. }
  9871. if (ver==0) {
  9872. tree.setown(createPTreeFromIPT(tree));
  9873. StringBuffer cname;
  9874. logicalname->getCluster(cname);
  9875. expandFileTree(tree,true,cname.str()); // resolve @node values that may not be set
  9876. tree->serialize(mb);
  9877. }
  9878. break;
  9879. }
  9880. else {
  9881. tree.setown(getNamedPropTree(sroot,queryDfsXmlBranchName(DXB_SuperFile),"@name",tail.str(),false));
  9882. if (tree) {
  9883. tree->serialize(mb);
  9884. break;
  9885. }
  9886. }
  9887. if (redmatch.get()) {
  9888. if (!redmatch->next())
  9889. break;
  9890. }
  9891. else {
  9892. redmatch.setown(queryDistributedFileDirectory().queryRedirection().getMatch(logicalname->get()));
  9893. if (!redmatch.get())
  9894. break;
  9895. if (!redmatch->first())
  9896. break;
  9897. }
  9898. logicalname = &redmatch->query();
  9899. }
  9900. }
  9901. void getGroupTree(CMessageBuffer &mb,StringBuffer &trc)
  9902. {
  9903. TransactionLog transactionLog(*this, MDFS_GET_GROUP_TREE, mb.getSender());
  9904. StringAttr gname;
  9905. mb.read(gname);
  9906. mb.clear();
  9907. trc.appendf("getGroupTree(%s)",gname.str());
  9908. if (queryTransactionLogging())
  9909. transactionLog.log("%s", trc.str());
  9910. byte ok;
  9911. CConnectLock connlock("getGroupTree",SDS_GROUPSTORE_ROOT,false,false,false,defaultTimeout);
  9912. Owned<IPropertyTree> pt = getNamedPropTree(connlock.conn->queryRoot(),"Group","@name",gname.get(),true);
  9913. if (pt) {
  9914. ok = 1;
  9915. mb.append(ok);
  9916. pt->serialize(mb);
  9917. }
  9918. else {
  9919. ok = 0;
  9920. mb.append(ok);
  9921. }
  9922. }
  9923. void processMessage(CMessageBuffer &mb)
  9924. {
  9925. CheckTime block0("CDaliDFSServer::processMessage ");
  9926. ICoven &coven=queryCoven();
  9927. StringBuffer trc;
  9928. int fn;
  9929. mb.read(fn);
  9930. try
  9931. {
  9932. switch (fn)
  9933. {
  9934. case MDFS_ITERATE_FILES:
  9935. {
  9936. iterateFiles(mb, trc);
  9937. break;
  9938. }
  9939. case MDFS_ITERATE_FILTEREDFILES: // legacy, newer clients will send MDFS_ITERATE_FILTEREDFILES2
  9940. {
  9941. iterateFilteredFiles(mb, trc);
  9942. break;
  9943. }
  9944. case MDFS_ITERATE_FILTEREDFILES2:
  9945. {
  9946. iterateFilteredFiles2(mb, trc);
  9947. break;
  9948. }
  9949. case MDFS_ITERATE_RELATIONSHIPS:
  9950. {
  9951. iterateRelationships(mb, trc);
  9952. break;
  9953. }
  9954. case MDFS_GET_FILE_TREE:
  9955. {
  9956. getFileTree(mb, trc);
  9957. break;
  9958. }
  9959. case MDFS_GET_GROUP_TREE:
  9960. {
  9961. getGroupTree(mb, trc);
  9962. break;
  9963. }
  9964. case MDFS_SET_FILE_ACCESSED:
  9965. {
  9966. setFileAccessed(mb, trc);
  9967. break;
  9968. }
  9969. case MDFS_SET_FILE_PROTECT:
  9970. {
  9971. setFileProtect(mb, trc);
  9972. break;
  9973. }
  9974. default:
  9975. {
  9976. mb.clear();
  9977. break;
  9978. }
  9979. }
  9980. }
  9981. catch (IException *e)
  9982. {
  9983. int err=-1; // exception marker
  9984. mb.clear().append(err);
  9985. serializeException(e, mb);
  9986. e->Release();
  9987. }
  9988. coven.reply(mb);
  9989. if (block0.slow())
  9990. {
  9991. SocketEndpoint ep = mb.getSender();
  9992. ep.getUrlStr(block0.appendMsg(trc).append(" from "));
  9993. }
  9994. }
  9995. void nodeDown(rank_t rank)
  9996. {
  9997. assertex(!"TBD");
  9998. }
  9999. // CTransactionLogTracker
  10000. virtual StringBuffer &getCmdText(unsigned cmd, StringBuffer &ret) const
  10001. {
  10002. switch (cmd)
  10003. {
  10004. case MDFS_ITERATE_FILES:
  10005. return ret.append("MDFS_ITERATE_FILES");
  10006. case MDFS_ITERATE_FILTEREDFILES:
  10007. return ret.append("MDFS_ITERATE_FILTEREDFILES");
  10008. case MDFS_ITERATE_FILTEREDFILES2:
  10009. return ret.append("MDFS_ITERATE_FILTEREDFILES2");
  10010. case MDFS_ITERATE_RELATIONSHIPS:
  10011. return ret.append("MDFS_ITERATE_RELATIONSHIPS");
  10012. case MDFS_GET_FILE_TREE:
  10013. return ret.append("MDFS_GET_FILE_TREE");
  10014. case MDFS_GET_GROUP_TREE:
  10015. return ret.append("MDFS_GET_GROUP_TREE");
  10016. case MDFS_SET_FILE_ACCESSED:
  10017. return ret.append("MDFS_SET_FILE_ACCESSED");
  10018. case MDFS_SET_FILE_PROTECT:
  10019. return ret.append("MDFS_SET_FILE_PROTECT");
  10020. default:
  10021. return ret.append("UNKNOWN");
  10022. }
  10023. }
  10024. // IExceptionHandler impl.
  10025. virtual bool fireException(IException *e)
  10026. {
  10027. EXCLOG(e, "CDaliDFSServer exception");
  10028. return true;
  10029. }
  10030. } *daliDFSServer = NULL;
  10031. IDFAttributesIterator *CDistributedFileDirectory::getDFAttributesIterator(const char *wildname, IUserDescriptor *user, bool recursive, bool includesuper,INode *foreigndali,unsigned foreigndalitimeout)
  10032. {
  10033. if (!wildname||!*wildname||(strcmp(wildname,"*")==0)) {
  10034. recursive = true;
  10035. }
  10036. CMessageBuffer mb;
  10037. mb.append((int)MDFS_ITERATE_FILES).append(wildname).append(recursive).append("").append(includesuper); // "" is legacy
  10038. if (user)
  10039. {
  10040. user->serializeWithoutPassword(mb);
  10041. }
  10042. #ifdef NULL_DALIUSER_STACKTRACE
  10043. else
  10044. {
  10045. DBGLOG("UNEXPECTED USER (NULL) in dadfs.cpp getDFAttributesIterator() line %d",__LINE__);
  10046. PrintStackReport();
  10047. }
  10048. #endif
  10049. if (foreigndali)
  10050. foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
  10051. else
  10052. queryCoven().sendRecv(mb,RANK_RANDOM,MPTAG_DFS_REQUEST);
  10053. checkDfsReplyException(mb);
  10054. return new CDFAttributeIterator(mb);
  10055. }
  10056. IDFScopeIterator *CDistributedFileDirectory::getScopeIterator(IUserDescriptor *user, const char *basescope, bool recursive,bool includeempty)
  10057. {
  10058. return new CDFScopeIterator(this,basescope,recursive,includeempty,defaultTimeout);
  10059. }
  10060. static bool isValidLFN(const char *lfn)
  10061. { // bit OTT
  10062. if (!lfn||!*lfn||(strcmp(lfn,".")==0))
  10063. return false;
  10064. StringBuffer tmp(".::");
  10065. tmp.append(lfn);
  10066. CDfsLogicalFileName dlfn;
  10067. return dlfn.setValidate(tmp.str());
  10068. }
  10069. bool CDistributedFileDirectory::loadScopeContents(const char *scopelfn,
  10070. StringArray *scopes,
  10071. StringArray *supers,
  10072. StringArray *files,
  10073. bool includeemptyscopes
  10074. )
  10075. {
  10076. StringBuffer baseq;
  10077. if (scopelfn&&*scopelfn) {
  10078. if (memcmp(scopelfn,".::",3)==0) // scopes not in .
  10079. scopelfn += 3;
  10080. StringBuffer tmp(scopelfn);
  10081. if (tmp.trim().length()) {
  10082. tmp.append("::.");
  10083. CDfsLogicalFileName dlfn;
  10084. if (!dlfn.setValidate(tmp.str()))
  10085. return false;
  10086. dlfn.makeScopeQuery(baseq,false);
  10087. }
  10088. }
  10089. CConnectLock connlock("CDistributedFileDirectory::loadScopeContents",querySdsFilesRoot(),false,false,false,defaultTimeout);
  10090. if (!connlock.conn)
  10091. return false;
  10092. IPropertyTree *root = connlock.conn->queryRoot();
  10093. if (!root)
  10094. return false;
  10095. if (baseq.length()) {
  10096. root = root->queryPropTree(baseq.str());
  10097. if (!root)
  10098. return false;
  10099. }
  10100. Owned<IPropertyTreeIterator> iter;
  10101. if (scopes) {
  10102. iter.setown(root->getElements(queryDfsXmlBranchName(DXB_Scope)));
  10103. ForEach(*iter) {
  10104. IPropertyTree &ct = iter->query();
  10105. if (includeemptyscopes||!recursiveCheckEmptyScope(ct)) {
  10106. StringBuffer name;
  10107. if (ct.getProp("@name",name)&&name.trim().length()&&isValidLFN(name.str()))
  10108. scopes->append(name.str());
  10109. }
  10110. }
  10111. }
  10112. if (!supers&&!files)
  10113. return true;
  10114. if (baseq.length()==0) { // bit odd but top level files are in '.'
  10115. CDfsLogicalFileName dlfn;
  10116. dlfn.set(".",".");
  10117. dlfn.makeScopeQuery(baseq,false);
  10118. root = root->queryPropTree(baseq.str());
  10119. if (!root)
  10120. return true;
  10121. }
  10122. if (supers) {
  10123. iter.setown(root->getElements(queryDfsXmlBranchName(DXB_SuperFile)));
  10124. ForEach(*iter) {
  10125. IPropertyTree &ct = iter->query();
  10126. StringBuffer name;
  10127. if (ct.getProp("@name",name)&&name.trim().length()&&isValidLFN(name.str()))
  10128. supers->append(name.str());
  10129. }
  10130. }
  10131. if (files) {
  10132. iter.setown(root->getElements(queryDfsXmlBranchName(DXB_File)));
  10133. ForEach(*iter) {
  10134. StringBuffer name;
  10135. IPropertyTree &ct = iter->query();
  10136. if (ct.getProp("@name",name)&&name.trim().length()&&isValidLFN(name.str()))
  10137. files->append(name.str());
  10138. }
  10139. }
  10140. return true;
  10141. }
  10142. void CDistributedFileDirectory::setFileAccessed(CDfsLogicalFileName &dlfn,IUserDescriptor *user, const CDateTime &dt, const INode *foreigndali,unsigned foreigndalitimeout)
  10143. {
  10144. // this accepts either a foreign dali node or a foreign lfn
  10145. Owned<INode> fnode;
  10146. SocketEndpoint ep;
  10147. const char *lname;
  10148. if (dlfn.isForeign()) {
  10149. if (!dlfn.getEp(ep))
  10150. throw MakeStringException(-1,"cannot resolve dali ip in foreign file name (%s)",dlfn.get());
  10151. fnode.setown(createINode(ep));
  10152. foreigndali = fnode;
  10153. lname = dlfn.get(true);
  10154. }
  10155. else if (dlfn.isExternal())
  10156. return;
  10157. else
  10158. lname = dlfn.get();
  10159. if (isLocalDali(foreigndali))
  10160. foreigndali = NULL;
  10161. CMessageBuffer mb;
  10162. mb.append((int)MDFS_SET_FILE_ACCESSED).append(lname);
  10163. dt.serialize(mb);
  10164. if (user)
  10165. {
  10166. user->serializeWithoutPassword(mb);
  10167. }
  10168. #ifdef NULL_DALIUSER_STACKTRACE
  10169. else
  10170. {
  10171. DBGLOG("UNEXPECTED USER (NULL) in dadfs.cpp setFileAccessed() line %d",__LINE__);
  10172. PrintStackReport();
  10173. }
  10174. #endif
  10175. if (foreigndali)
  10176. foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
  10177. else
  10178. queryCoven().sendRecv(mb,RANK_RANDOM,MPTAG_DFS_REQUEST);
  10179. checkDfsReplyException(mb);
  10180. }
  10181. void CDistributedFileDirectory::setFileProtect(CDfsLogicalFileName &dlfn,IUserDescriptor *user, const char *owner, bool set, const INode *foreigndali,unsigned foreigndalitimeout)
  10182. {
  10183. // this accepts either a foreign dali node or a foreign lfn
  10184. Owned<INode> fnode;
  10185. SocketEndpoint ep;
  10186. const char *lname;
  10187. if (dlfn.isForeign()) {
  10188. if (!dlfn.getEp(ep))
  10189. throw MakeStringException(-1,"cannot resolve dali ip in foreign file name (%s)",dlfn.get());
  10190. fnode.setown(createINode(ep));
  10191. foreigndali = fnode;
  10192. lname = dlfn.get(true);
  10193. }
  10194. else if (dlfn.isExternal())
  10195. return;
  10196. else
  10197. lname = dlfn.get();
  10198. if (isLocalDali(foreigndali))
  10199. foreigndali = NULL;
  10200. CMessageBuffer mb;
  10201. if (!owner)
  10202. owner = "";
  10203. mb.append((int)MDFS_SET_FILE_PROTECT).append(lname).append(owner).append(set);
  10204. if (user)
  10205. {
  10206. user->serializeWithoutPassword(mb);
  10207. }
  10208. #ifdef NULL_DALIUSER_STACKTRACE
  10209. else
  10210. {
  10211. DBGLOG("UNEXPECTED USER (NULL) in dadfs.cpp setFileProtect() line %d",__LINE__);
  10212. PrintStackReport();
  10213. }
  10214. #endif
  10215. if (foreigndali)
  10216. foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
  10217. else
  10218. queryCoven().sendRecv(mb,RANK_RANDOM,MPTAG_DFS_REQUEST);
  10219. checkDfsReplyException(mb);
  10220. }
  10221. IPropertyTree *CDistributedFileDirectory::getFileTree(const char *lname, IUserDescriptor *user, const INode *foreigndali,unsigned foreigndalitimeout, bool expandnodes, bool appendForeign)
  10222. {
  10223. // this accepts either a foreign dali node or a foreign lfn
  10224. Owned<INode> fnode;
  10225. CDfsLogicalFileName dlfn;
  10226. SocketEndpoint ep;
  10227. dlfn.set(lname);
  10228. if (dlfn.isForeign()) {
  10229. if (!dlfn.getEp(ep))
  10230. throw MakeStringException(-1,"cannot resolve dali ip in foreign file name (%s)",lname);
  10231. fnode.setown(createINode(ep));
  10232. foreigndali = fnode;
  10233. lname = dlfn.get(true);
  10234. }
  10235. if (isLocalDali(foreigndali))
  10236. foreigndali = NULL;
  10237. CMessageBuffer mb;
  10238. mb.append((int)MDFS_GET_FILE_TREE).append(lname);
  10239. mb.append(MDFS_GET_FILE_TREE_V2);
  10240. if (user)
  10241. {
  10242. user->serializeWithoutPassword(mb);
  10243. }
  10244. #ifdef NULL_DALIUSER_STACKTRACE
  10245. else
  10246. {
  10247. DBGLOG("UNEXPECTED USER (NULL) in dadfs.cpp getFileTree() line %d",__LINE__);
  10248. PrintStackReport();
  10249. }
  10250. #endif
  10251. if (foreigndali)
  10252. foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
  10253. else
  10254. queryCoven().sendRecv(mb,RANK_RANDOM,MPTAG_DFS_REQUEST);
  10255. checkDfsReplyException(mb);
  10256. if (mb.length()==0)
  10257. return NULL;
  10258. unsigned ver = 0;
  10259. if ((mb.length()>=sizeof(int))&&(*(int *)mb.bufferBase()) == -2) { // version indicator
  10260. int i;
  10261. mb.read(i);
  10262. mb.read(ver);
  10263. }
  10264. Owned<IPropertyTree> ret;
  10265. if (ver==0)
  10266. ret.setown(createPTree(mb));
  10267. else {
  10268. Owned<IFileDescriptor> fdesc;
  10269. CDateTime modified;
  10270. if (ver==MDFS_GET_FILE_TREE_V2) { // no longer in use but support for back compatibility
  10271. fdesc.setown(deserializeFileDescriptor(mb));
  10272. if (mb.remaining()>0)
  10273. modified.deserialize(mb);
  10274. }
  10275. else
  10276. throw MakeStringException(-1,"Unknown GetFileTree serialization version %d",ver);
  10277. ret.setown(createPTree(queryDfsXmlBranchName(DXB_File)));
  10278. fdesc->serializeTree(*ret,expandnodes?0:CPDMSF_packParts);
  10279. if (!modified.isNull()) {
  10280. StringBuffer dts;
  10281. ret->setProp("@modified",modified.getString(dts).str());
  10282. }
  10283. }
  10284. if (expandnodes) {
  10285. StringBuffer cname;
  10286. dlfn.getCluster(cname);
  10287. expandFileTree(ret,true,cname.str());
  10288. CDfsLogicalFileName dlfn2;
  10289. dlfn2.set(dlfn);
  10290. if (foreigndali)
  10291. dlfn2.setForeign(foreigndali->endpoint(),false);
  10292. ret->setProp("OrigName",dlfn.get());
  10293. }
  10294. if (foreigndali && appendForeign)
  10295. resolveForeignFiles(ret,foreigndali);
  10296. return ret.getClear();
  10297. }
  10298. IFileDescriptor *CDistributedFileDirectory::getFileDescriptor(const char *lname,IUserDescriptor *user,const INode *foreigndali,unsigned foreigndalitimeout)
  10299. {
  10300. Owned<IPropertyTree> tree = getFileTree(lname,user,foreigndali,foreigndalitimeout,false);
  10301. if (!tree)
  10302. return NULL;
  10303. if (strcmp(tree->queryName(),queryDfsXmlBranchName(DXB_SuperFile))==0) {
  10304. CDfsLogicalFileName dlfn;
  10305. dlfn.set(lname);
  10306. Owned<CDistributedSuperFile> sfile = new CDistributedSuperFile(this,tree, dlfn, user);
  10307. return sfile->getFileDescriptor(NULL);
  10308. }
  10309. if (strcmp(tree->queryName(),queryDfsXmlBranchName(DXB_File))!=0)
  10310. return NULL; // what is it?
  10311. IFileDescriptor * fdesc = deserializeFileDescriptorTree(tree,&queryNamedGroupStore(),0);
  10312. if (fdesc)
  10313. fdesc->setTraceName(lname);
  10314. return fdesc;
  10315. }
  10316. IDistributedFile *CDistributedFileDirectory::getFile(const char *lname,IUserDescriptor *user,const INode *foreigndali,unsigned foreigndalitimeout)
  10317. {
  10318. Owned<IPropertyTree> tree = getFileTree(lname,user,foreigndali,foreigndalitimeout,false);
  10319. if (!tree)
  10320. return NULL;
  10321. if (strcmp(tree->queryName(),queryDfsXmlBranchName(DXB_SuperFile))==0) {
  10322. CDfsLogicalFileName dlfn;
  10323. dlfn.set(lname);
  10324. return new CDistributedSuperFile(this,tree, dlfn, user);
  10325. }
  10326. if (strcmp(tree->queryName(),queryDfsXmlBranchName(DXB_File))!=0)
  10327. return NULL; // what is it?
  10328. Owned<IFileDescriptor> fdesc = deserializeFileDescriptorTree(tree,&queryNamedGroupStore(),IFDSF_FOREIGN_GROUP);
  10329. if (!fdesc)
  10330. return NULL;
  10331. fdesc->setTraceName(lname);
  10332. CDistributedFile *ret = new CDistributedFile(this, fdesc, user, false);
  10333. ret->setLogicalName(lname);
  10334. const char *date = tree->queryProp("@modified");
  10335. if (ret) {
  10336. CDateTime dt;
  10337. if (date&&*date)
  10338. dt.setString(date);
  10339. ret->setModificationTime(dt);
  10340. }
  10341. return ret;
  10342. }
  10343. static void addForeignName(IPropertyTree &t,const INode *foreigndali,const char *attr)
  10344. {
  10345. StringBuffer sb;
  10346. const char *name = t.queryProp(attr);
  10347. if (!name||!*name)
  10348. return;
  10349. CDfsLogicalFileName logicalname;
  10350. logicalname.set(name);
  10351. if (logicalname.isExternal()||logicalname.isQuery())
  10352. return; // how did that get in here?
  10353. if (logicalname.isForeign()) {
  10354. SocketEndpoint ep;
  10355. Owned<INode> fd = createINode(ep);
  10356. if (logicalname.getEp(ep)&&isLocalDali(fd)) { // see if pointing back at self
  10357. logicalname.clearForeign();
  10358. t.setProp(attr,logicalname.get());
  10359. }
  10360. }
  10361. else if (foreigndali) {
  10362. logicalname.setForeign(foreigndali->endpoint(),false);
  10363. t.setProp(attr,logicalname.get());
  10364. }
  10365. }
  10366. void CDistributedFileDirectory::resolveForeignFiles(IPropertyTree *tree,const INode *foreigndali)
  10367. {
  10368. if (!tree||!foreigndali)
  10369. return;
  10370. // now add to all sub files
  10371. Owned<IPropertyTreeIterator> pe = tree->getElements("SubFile");
  10372. ForEach(*pe)
  10373. addForeignName(pe->query(),foreigndali,"@name");
  10374. pe.setown(tree->getElements("SuperOwner"));
  10375. ForEach(*pe)
  10376. addForeignName(pe->query(),foreigndali,"@name");
  10377. // do origname?
  10378. }
  10379. SecAccessFlags CDistributedFileDirectory::getFilePermissions(const char *lname,IUserDescriptor *user,unsigned auditflags)
  10380. {
  10381. CDfsLogicalFileName dlfn;
  10382. dlfn.set(lname);
  10383. StringBuffer scopes;
  10384. dlfn.getScopes(scopes);
  10385. return getScopePermissions(scopes.str(),user,auditflags);
  10386. }
  10387. SecAccessFlags CDistributedFileDirectory::getNodePermissions(const IpAddress &ip,IUserDescriptor *user,unsigned auditflags)
  10388. {
  10389. if (ip.isNull())
  10390. return SecAccess_None;
  10391. CDfsLogicalFileName dlfn;
  10392. SocketEndpoint ep(0,ip);
  10393. dlfn.setExternal(ep,"/x");
  10394. StringBuffer scopes;
  10395. dlfn.getScopes(scopes,true);
  10396. return getScopePermissions(scopes.str(),user,auditflags);
  10397. }
  10398. SecAccessFlags CDistributedFileDirectory::getFDescPermissions(IFileDescriptor *fdesc,IUserDescriptor *user,unsigned auditflags)
  10399. {
  10400. // this checks have access to the nodes in the file descriptor
  10401. SecAccessFlags retPerms = SecAccess_Full;
  10402. unsigned np = fdesc->numParts();
  10403. for (unsigned i=0;i<np;i++) {
  10404. INode *node = fdesc->queryNode(i);
  10405. if (node) {
  10406. bool multi = false;
  10407. RemoteMultiFilename mfn;
  10408. unsigned n = 1;
  10409. if (fdesc->isMulti()) {
  10410. fdesc->getMultiFilename(i,0,mfn);
  10411. multi = true;
  10412. n = mfn.ordinality();
  10413. }
  10414. for (unsigned j = 0;j<n;j++) {
  10415. RemoteFilename rfn;
  10416. if (multi) {
  10417. rfn.set(mfn.item(j));
  10418. }
  10419. else
  10420. fdesc->getFilename(i,0,rfn);
  10421. StringBuffer localpath;
  10422. rfn.getLocalPath(localpath);
  10423. // translate wild cards
  10424. for (unsigned k=0;k<localpath.length();k++)
  10425. if ((localpath.charAt(k)=='?')||(localpath.charAt(k)=='*'))
  10426. localpath.setCharAt(k,'_');
  10427. CDfsLogicalFileName dlfn;
  10428. dlfn.setExternal(rfn.queryEndpoint(),localpath.str());
  10429. StringBuffer scopes;
  10430. dlfn.getScopes(scopes);
  10431. SecAccessFlags perm = getScopePermissions(scopes.str(),user,auditflags);
  10432. if (perm < retPerms) {
  10433. retPerms = perm;
  10434. if (retPerms == SecAccess_None)
  10435. return SecAccess_None;
  10436. }
  10437. }
  10438. }
  10439. }
  10440. return retPerms;
  10441. }
  10442. void CDistributedFileDirectory::setDefaultUser(IUserDescriptor *user)
  10443. {
  10444. if (user)
  10445. defaultudesc.set(user);
  10446. else
  10447. defaultudesc.setown(createUserDescriptor());
  10448. }
  10449. IUserDescriptor* CDistributedFileDirectory::queryDefaultUser()
  10450. {
  10451. return defaultudesc.get();
  10452. }
  10453. void CDistributedFileDirectory::setDefaultPreferredClusters(const char *clusters)
  10454. {
  10455. defprefclusters.set(clusters);
  10456. }
  10457. bool removePhysicalFiles(IGroup *grp,const char *_filemask,unsigned short port,ClusterPartDiskMapSpec &mspec,IMultiException *mexcept)
  10458. {
  10459. // TBD this won't remove repeated parts
  10460. PROGLOG("removePhysicalFiles(%s)",_filemask);
  10461. if (!isAbsolutePath(_filemask))
  10462. throw MakeStringException(-1,"removePhysicalFiles: Filename %s must be complete path",_filemask);
  10463. size32_t l = strlen(_filemask);
  10464. while (l&&isdigit(_filemask[l-1]))
  10465. l--;
  10466. unsigned width=0;
  10467. if (l&&(_filemask[l-1]=='_'))
  10468. width = atoi(_filemask+l);
  10469. if (!width)
  10470. width = grp->ordinality();
  10471. CriticalSection errcrit;
  10472. class casyncfor: public CAsyncFor
  10473. {
  10474. unsigned short port;
  10475. CriticalSection &errcrit;
  10476. IMultiException *mexcept;
  10477. unsigned width;
  10478. StringAttr filemask;
  10479. IGroup *grp;
  10480. ClusterPartDiskMapSpec &mspec;
  10481. public:
  10482. bool ok;
  10483. casyncfor(IGroup *_grp,const char *_filemask,unsigned _width,unsigned short _port,ClusterPartDiskMapSpec &_mspec,IMultiException *_mexcept,CriticalSection &_errcrit)
  10484. : mspec(_mspec),filemask(_filemask),errcrit(_errcrit)
  10485. {
  10486. grp = _grp;
  10487. port = _port;
  10488. ok = true;
  10489. mexcept = _mexcept;
  10490. width = _width;
  10491. }
  10492. void Do(unsigned i)
  10493. {
  10494. for (unsigned copy = 0; copy < 2; copy++) // ** TBD
  10495. {
  10496. RemoteFilename rfn;
  10497. constructPartFilename(grp,i+1,width,NULL,filemask,"",copy>0,mspec,rfn);
  10498. if (port)
  10499. rfn.setPort(port); // if daliservix
  10500. Owned<IFile> partfile = createIFile(rfn);
  10501. StringBuffer eps;
  10502. try
  10503. {
  10504. unsigned start = msTick();
  10505. #if 1
  10506. if (partfile->remove()) {
  10507. PROGLOG("Removed '%s'",partfile->queryFilename());
  10508. unsigned t = msTick()-start;
  10509. if (t>5*1000)
  10510. DBGLOG("Removing %s from %s took %ds", partfile->queryFilename(), rfn.queryEndpoint().getUrlStr(eps).str(), t/1000);
  10511. }
  10512. else
  10513. IWARNLOG("Failed to remove file part %s from %s", partfile->queryFilename(),rfn.queryEndpoint().getUrlStr(eps).str());
  10514. #else
  10515. if (partfile->exists())
  10516. PROGLOG("Would remove '%s'",partfile->queryFilename());
  10517. #endif
  10518. }
  10519. catch (IException *e)
  10520. {
  10521. CriticalBlock block(errcrit);
  10522. if (mexcept)
  10523. mexcept->append(*e);
  10524. else {
  10525. StringBuffer s("Failed to remove file part ");
  10526. s.append(partfile->queryFilename()).append(" from ");
  10527. rfn.queryEndpoint().getUrlStr(s);
  10528. EXCLOG(e, s.str());
  10529. e->Release();
  10530. }
  10531. ok = false;
  10532. }
  10533. }
  10534. }
  10535. } afor(grp,_filemask,width,port,mspec,mexcept,errcrit);
  10536. afor.For(width,10,false,true);
  10537. return afor.ok;
  10538. }
  10539. IDaliServer *createDaliDFSServer(IPropertyTree *config)
  10540. {
  10541. assertex(!daliDFSServer); // initialization problem
  10542. daliDFSServer = new CDaliDFSServer(config);
  10543. return daliDFSServer;
  10544. }
  10545. IDistributedFileTransaction *createDistributedFileTransaction(IUserDescriptor *user, ICodeContext *ctx)
  10546. {
  10547. return new CDistributedFileTransaction(user, NULL, ctx);
  10548. }
  10549. static void encodeCompareResult(DistributedFileCompareResult &ret,bool differs,CDateTime &newestdt1,CDateTime &newestdt2)
  10550. {
  10551. if (ret!=DFS_COMPARE_RESULT_FAILURE) {
  10552. int cmp = 0;
  10553. if (!newestdt1.isNull()) {
  10554. if (!newestdt2.isNull()) {
  10555. int cmp = newestdt1.compare(newestdt2,false);
  10556. if (cmp>=0)
  10557. ret = DFS_COMPARE_RESULT_SAME_NEWER;
  10558. else
  10559. ret = DFS_COMPARE_RESULT_SAME_OLDER;
  10560. }
  10561. else
  10562. ret = DFS_COMPARE_RESULT_SAME_NEWER;
  10563. }
  10564. else if (!newestdt2.isNull())
  10565. ret = DFS_COMPARE_RESULT_SAME_OLDER;
  10566. if (differs) {
  10567. if (ret==DFS_COMPARE_RESULT_SAME_OLDER) // ok they could be same but seems rather unlikely!
  10568. ret = DFS_COMPARE_RESULT_DIFFER_OLDER;
  10569. else
  10570. ret = DFS_COMPARE_RESULT_DIFFER_NEWER;
  10571. }
  10572. }
  10573. }
  10574. DistributedFileCompareResult CDistributedFileDirectory::fileCompare(const char *lfn1,const char *lfn2,DistributedFileCompareMode mode,StringBuffer &errstr,IUserDescriptor *user)
  10575. {
  10576. DistributedFileCompareResult ret = DFS_COMPARE_RESULT_SAME;
  10577. StringBuffer msg;
  10578. try
  10579. {
  10580. Owned<IDistributedFile> file1 = lookup(lfn1, user, false, false, false, NULL, defaultPrivilegedUser, defaultTimeout);
  10581. Owned<IDistributedFile> file2 = lookup(lfn2, user, false, false, false, NULL, defaultPrivilegedUser, defaultTimeout);
  10582. if (!file1)
  10583. {
  10584. errstr.appendf("File %s not found",lfn1);
  10585. ret = DFS_COMPARE_RESULT_FAILURE;
  10586. }
  10587. else if (!file2)
  10588. {
  10589. errstr.appendf("File %s not found",lfn2);
  10590. ret = DFS_COMPARE_RESULT_FAILURE;
  10591. }
  10592. else
  10593. {
  10594. unsigned np = file1->numParts();
  10595. if (np!=file2->numParts())
  10596. {
  10597. errstr.appendf("Files %s and %s have differing number of parts",lfn1,lfn2);
  10598. ret = DFS_COMPARE_RESULT_FAILURE;
  10599. }
  10600. else
  10601. {
  10602. CDateTime newestdt1;
  10603. CDateTime newestdt2;
  10604. bool differs = false;
  10605. class casyncfor: public CAsyncFor
  10606. {
  10607. CriticalSection crit;
  10608. DistributedFileCompareResult &ret;
  10609. IDistributedFile *file1;
  10610. IDistributedFile *file2;
  10611. const char *lfn1;
  10612. const char *lfn2;
  10613. StringBuffer &errstr;
  10614. DistributedFileCompareMode mode;
  10615. bool physdatesize;
  10616. CDateTime &newestdt1;
  10617. CDateTime &newestdt2;
  10618. bool &differs;
  10619. public:
  10620. casyncfor(const char *_lfn1,const char *_lfn2,IDistributedFile *_file1,IDistributedFile *_file2,DistributedFileCompareMode _mode,DistributedFileCompareResult &_ret,StringBuffer &_errstr,
  10621. CDateTime &_newestdt1,CDateTime &_newestdt2,bool &_differs)
  10622. : ret(_ret), errstr(_errstr),newestdt1(_newestdt1),newestdt2(_newestdt2),differs(_differs)
  10623. {
  10624. lfn1 = _lfn1;
  10625. lfn2 = _lfn2;
  10626. file1 = _file1;
  10627. file2 = _file2;
  10628. mode = _mode;
  10629. physdatesize = (mode==DFS_COMPARE_FILES_PHYSICAL)||(mode==DFS_COMPARE_FILES_PHYSICAL_CRCS);
  10630. }
  10631. void Do(unsigned p)
  10632. {
  10633. CriticalBlock block (crit);
  10634. StringBuffer msg;
  10635. Owned<IDistributedFilePart> part1 = file1->getPart(p);
  10636. Owned<IDistributedFilePart> part2 = file2->getPart(p);
  10637. CDateTime dt1;
  10638. RemoteFilename rfn;
  10639. bool ok;
  10640. {
  10641. CriticalUnblock unblock(crit);
  10642. ok = part1->getModifiedTime(true,physdatesize,dt1);
  10643. }
  10644. if (!ok) {
  10645. if (errstr.length()==0) {
  10646. errstr.append("Could not find ");
  10647. part1->getFilename(rfn);
  10648. rfn.getPath(errstr);
  10649. }
  10650. ret = DFS_COMPARE_RESULT_FAILURE;
  10651. }
  10652. CDateTime dt2;
  10653. {
  10654. CriticalUnblock unblock(crit);
  10655. ok = part2->getModifiedTime(true,physdatesize,dt2);
  10656. }
  10657. if (!ok) {
  10658. if (errstr.length()==0) {
  10659. errstr.append("Could not find ");
  10660. part2->getFilename(rfn);
  10661. rfn.getPath(errstr);
  10662. }
  10663. ret = DFS_COMPARE_RESULT_FAILURE;
  10664. }
  10665. if (ret!=DFS_COMPARE_RESULT_FAILURE) {
  10666. int cmp = dt1.compare(dt2,false);
  10667. if (cmp>0) {
  10668. if (newestdt1.isNull()||(dt1.compare(newestdt1,false)>0))
  10669. newestdt1.set(dt1);
  10670. }
  10671. else if (cmp<0) {
  10672. if (newestdt2.isNull()||(dt2.compare(newestdt2,false)>0))
  10673. newestdt2.set(dt2);
  10674. }
  10675. }
  10676. if ((ret!=DFS_COMPARE_RESULT_FAILURE)&&!differs) {
  10677. offset_t sz1;
  10678. offset_t sz2;
  10679. {
  10680. CriticalUnblock unblock(crit);
  10681. sz1 = part1->getFileSize(true,physdatesize);
  10682. sz2 = part2->getFileSize(true,physdatesize);
  10683. }
  10684. if (sz1!=sz2)
  10685. differs = true;
  10686. }
  10687. if ((ret!=DFS_COMPARE_RESULT_FAILURE)&&!differs) {
  10688. unsigned crc1;
  10689. unsigned crc2;
  10690. if (mode==DFS_COMPARE_FILES_PHYSICAL_CRCS) {
  10691. {
  10692. CriticalUnblock unblock(crit);
  10693. crc1 = part1->getPhysicalCrc();
  10694. crc2 = part2->getPhysicalCrc();
  10695. }
  10696. }
  10697. else {
  10698. if (!part1->getCrc(crc1))
  10699. return;
  10700. if (!part2->getCrc(crc2))
  10701. return;
  10702. }
  10703. if (crc1!=crc2)
  10704. differs = true;
  10705. }
  10706. }
  10707. } afor(lfn1,lfn2,file1,file2,mode,ret,errstr,newestdt1,newestdt2,differs);
  10708. afor.For(np,20,false,false);
  10709. encodeCompareResult(ret,differs,newestdt1,newestdt2);
  10710. }
  10711. }
  10712. }
  10713. catch (IException *e) {
  10714. if (errstr.length()==0)
  10715. e->errorMessage(errstr);
  10716. else
  10717. EXCLOG(e,"CDistributedFileDirectory::fileCompare");
  10718. e->Release();
  10719. ret = DFS_COMPARE_RESULT_FAILURE;
  10720. }
  10721. return ret;
  10722. }
  10723. bool CDistributedFileDirectory::filePhysicalVerify(const char *lfn, IUserDescriptor *user, bool includecrc, StringBuffer &errstr)
  10724. {
  10725. bool differs = false;
  10726. Owned<IDistributedFile> file = lookup(lfn, user, false, false, false, NULL, defaultPrivilegedUser, defaultTimeout);
  10727. if (!file)
  10728. {
  10729. errstr.appendf("Could not find file: %s",lfn);
  10730. return false;
  10731. }
  10732. try
  10733. {
  10734. unsigned np = file->numParts();
  10735. class casyncfor: public CAsyncFor
  10736. {
  10737. CriticalSection crit;
  10738. IDistributedFile *file;
  10739. const char *lfn;
  10740. StringBuffer &errstr;
  10741. bool includecrc;
  10742. bool &differs;
  10743. unsigned defaultTimeout;
  10744. public:
  10745. casyncfor(const char *_lfn,IDistributedFile *_file,StringBuffer &_errstr, bool _includecrc,
  10746. bool &_differs, unsigned _defaultTimeout)
  10747. : errstr(_errstr), differs(_differs)
  10748. {
  10749. lfn = _lfn;
  10750. file = _file;
  10751. includecrc = _includecrc;
  10752. defaultTimeout = _defaultTimeout;
  10753. }
  10754. void Do(unsigned p)
  10755. {
  10756. CriticalBlock block (crit);
  10757. StringBuffer msg;
  10758. Owned<IDistributedFilePart> part = file->getPart(p);
  10759. CDateTime dt1; // logical
  10760. CDateTime dt2; // physical
  10761. RemoteFilename rfn;
  10762. bool ok;
  10763. bool nological = !part->getModifiedTime(false,false,dt1);
  10764. {
  10765. CriticalUnblock unblock(crit);
  10766. ok = part->getModifiedTime(true,true,dt2);
  10767. }
  10768. if (!ok) {
  10769. if (errstr.length()==0) {
  10770. errstr.append("Could not find part file: ");
  10771. part->getFilename(rfn);
  10772. rfn.getPath(errstr);
  10773. }
  10774. differs = true;
  10775. }
  10776. if (!differs&&!includecrc) {
  10777. if (nological) {
  10778. StringBuffer str;
  10779. // TODO: Create DistributedFilePropertyLock for parts
  10780. part->lockProperties(defaultTimeout);
  10781. part->queryAttributes().setProp("@modified",dt2.getString(str).str());
  10782. part->unlockProperties();
  10783. }
  10784. else {
  10785. if (dt1.compare(dt2,false)!=0) {
  10786. if (errstr.length()==0) {
  10787. errstr.append("Modified time differs for: ");
  10788. part->getFilename(rfn);
  10789. rfn.getPath(errstr);
  10790. }
  10791. differs = true;
  10792. }
  10793. }
  10794. }
  10795. if (!differs) {
  10796. offset_t sz1;
  10797. offset_t sz2;
  10798. {
  10799. CriticalUnblock unblock(crit);
  10800. sz1 = part->getFileSize(false,false);
  10801. sz2 = part->getFileSize(true,true);
  10802. }
  10803. if (sz1!=sz2) {
  10804. if (sz1==(offset_t)-1) {
  10805. // TODO: Create DistributedFilePropertyLock for parts
  10806. part->lockProperties(defaultTimeout);
  10807. part->queryAttributes().setPropInt64("@size",sz2);
  10808. part->unlockProperties();
  10809. }
  10810. else if (sz2!=(offset_t)-1) {
  10811. if (errstr.length()==0) {
  10812. errstr.append("File size differs for: ");
  10813. part->getFilename(rfn);
  10814. rfn.getPath(errstr);
  10815. }
  10816. differs = true;
  10817. }
  10818. }
  10819. }
  10820. if (!differs&&includecrc) {
  10821. unsigned crc1;
  10822. unsigned crc2;
  10823. {
  10824. CriticalUnblock unblock(crit);
  10825. crc2 = part->getPhysicalCrc();
  10826. }
  10827. if (!part->getCrc(crc1)) {
  10828. // TODO: Create DistributedFilePropertyLock for parts
  10829. part->lockProperties(defaultTimeout);
  10830. part->queryAttributes().setPropInt64("@fileCrc",(unsigned)crc2);
  10831. part->unlockProperties();
  10832. }
  10833. else if (crc1!=crc2) {
  10834. if (errstr.length()==0) {
  10835. errstr.append("File CRC differs for: ");
  10836. part->getFilename(rfn);
  10837. rfn.getPath(errstr);
  10838. }
  10839. differs = true;
  10840. }
  10841. }
  10842. }
  10843. } afor(lfn,file,errstr,includecrc,differs,defaultTimeout);
  10844. afor.For(np,10,false,false);
  10845. }
  10846. catch (IException *e) {
  10847. if (errstr.length()==0)
  10848. e->errorMessage(errstr);
  10849. else
  10850. EXCLOG(e,"CDistributedFileDirectory::fileCompare");
  10851. e->Release();
  10852. differs = true;
  10853. }
  10854. return !differs;
  10855. }
  10856. typedef MapStringTo<bool> SubfileSet;
  10857. class CFilterAttrIterator: implements IDFAttributesIterator, public CInterface
  10858. {
  10859. Owned<IDFAttributesIterator> iter;
  10860. Linked<IUserDescriptor> user;
  10861. SubfileSet sfset;
  10862. bool includesub;
  10863. public:
  10864. IMPLEMENT_IINTERFACE;
  10865. CFilterAttrIterator(IDFAttributesIterator *_iter,IUserDescriptor* _user,bool _includesub,unsigned timeoutms)
  10866. : iter(_iter), user(_user)
  10867. {
  10868. includesub = _includesub;
  10869. CDfsLogicalFileName lfn;
  10870. StringBuffer query;
  10871. Owned<IDFScopeIterator> siter = queryDistributedFileDirectory().getScopeIterator(user,NULL,true,false);
  10872. ForEach(*siter) {
  10873. lfn.set(siter->query(),"X");
  10874. lfn.makeScopeQuery(query.clear());
  10875. Owned<IRemoteConnection> conn = querySDS().connect(query.str(),myProcessSession(),0, timeoutms);
  10876. if (conn) {
  10877. IPropertyTree *t = conn->queryRoot();
  10878. Owned<IPropertyTreeIterator> iter = t->getElements("SuperFile/SubFile");
  10879. ForEach(*iter) {
  10880. const char *name = iter->query().queryProp("@name");
  10881. if (!sfset.getValue(name))
  10882. sfset.setValue(name, true);
  10883. }
  10884. }
  10885. }
  10886. }
  10887. inline bool match()
  10888. {
  10889. const char *name = iter->query().queryProp("@name");
  10890. return ((sfset.getValue(name)!=NULL)==includesub);
  10891. }
  10892. bool first()
  10893. {
  10894. if (!iter->first())
  10895. return false;
  10896. while (!match())
  10897. if (!iter->next())
  10898. return false;
  10899. return true;
  10900. }
  10901. bool next()
  10902. {
  10903. do {
  10904. if (!iter->next())
  10905. return false;
  10906. } while (!match());
  10907. return true;
  10908. }
  10909. bool isValid() { return iter->isValid(); }
  10910. IPropertyTree & query() { return iter->query(); }
  10911. };
  10912. IDFAttributesIterator *createSubFileFilter(IDFAttributesIterator *_iter,IUserDescriptor* _user, bool includesub, unsigned timeoutms)
  10913. {
  10914. return new CFilterAttrIterator(_iter,_user,includesub,timeoutms);
  10915. }
  10916. bool decodeChildGroupName(const char *gname,StringBuffer &parentname, StringBuffer &range)
  10917. {
  10918. if (!gname||!*gname)
  10919. return false;
  10920. size32_t l = strlen(gname);
  10921. if (gname[l-1]!=']')
  10922. return false;
  10923. const char *ss = strchr(gname,'[');
  10924. if (!ss||(ss==gname))
  10925. return false;
  10926. range.append(l-(ss-gname)-2,ss+1);
  10927. range.trim();
  10928. if (!range.length())
  10929. return false;
  10930. parentname.append(ss-gname,gname);
  10931. return true;
  10932. }
  10933. /* given a list of group offsets (positions), create a compact representation of the range
  10934. * compatible with the group range syntax, e.g. mygroup[1-5,8-10] or mygroup[1,5,10]
  10935. */
  10936. StringBuffer &encodeChildGroupRange(UnsignedArray &positions, StringBuffer &rangeText)
  10937. {
  10938. unsigned items = positions.ordinality();
  10939. if (0 == items)
  10940. return rangeText;
  10941. unsigned start = positions.item(0);
  10942. unsigned last = start;
  10943. rangeText.append('[');
  10944. unsigned p=1;
  10945. while (true)
  10946. {
  10947. unsigned pos = p==items ? NotFound : positions.item(p++);
  10948. if ((pos != last+1))
  10949. {
  10950. if (last-start>0)
  10951. rangeText.append(start).append('-').append(last);
  10952. else
  10953. rangeText.append(last);
  10954. if (NotFound == pos)
  10955. break;
  10956. rangeText.append(',');
  10957. start = pos;
  10958. }
  10959. last = pos;
  10960. }
  10961. return rangeText.append(']');
  10962. }
  10963. class CLightWeightSuperFileConn: implements ISimpleSuperFileEnquiry, public CInterface
  10964. {
  10965. CFileLock lock;
  10966. bool readonly;
  10967. IArrayOf<IRemoteConnection> children;
  10968. unsigned defaultTimeout;
  10969. Owned<IUserDescriptor> udesc;
  10970. static StringBuffer &getSubPath(StringBuffer &path,unsigned idx)
  10971. {
  10972. return path.append("SubFile[@num=\"").append(idx+1).append("\"]");
  10973. }
  10974. void migrateProp(const char *name, unsigned num,IPropertyTree *from,IPropertyTree *to,IPropertyTree *newt, bool allowunchanged)
  10975. {
  10976. StringBuffer aname("Attr/");
  10977. aname.append(name);
  10978. StringBuffer s;
  10979. StringBuffer o;
  10980. if (from->getProp(aname.str(),s))
  10981. if ((num==1)||(allowunchanged&&to->getProp(aname.str(),o)&&(strcmp(s.str(),o.str())==0)))
  10982. newt->setProp(name,s.str());
  10983. }
  10984. void migrateAttr(IPropertyTree *from,IPropertyTree *to)
  10985. {
  10986. // this tries hard to set what it knows but avoids sibling traversal
  10987. if (!to)
  10988. return;
  10989. const char *desc = to->queryProp("Attr/@description");
  10990. IPropertyTree* newt = getEmptyAttr();
  10991. if (desc)
  10992. newt->setProp("@description",desc);
  10993. if (from) {
  10994. unsigned num=to->getPropInt("@numsubfiles");
  10995. migrateProp("@size",num,from,to,newt,false);
  10996. migrateProp("@checkSum",num,from,to,newt,true);
  10997. migrateProp("@formatCrc",num,from,to,newt,true);
  10998. migrateProp("@recordSize",num,from,to,newt,true);
  10999. MemoryBuffer mb;
  11000. MemoryBuffer mbo;
  11001. const char *aname = "Attr/_record_layout";
  11002. if (from->getPropBin(aname,mb))
  11003. if ((num==1)||(to->getPropBin(aname,mbo)&&
  11004. (mb.length()==mbo.length())&&
  11005. (memcmp(mb.bufferBase(),mbo.bufferBase(),mb.length())==0)))
  11006. newt->setPropBin("_record_layout", mb.length(), mb.bufferBase());
  11007. }
  11008. to->setPropTree("Attr",newt);
  11009. }
  11010. void migrateSuperOwnersAttr(IPropertyTree *from)
  11011. {
  11012. if (!from)
  11013. return;
  11014. Owned<IPropertyTreeIterator> iter = from->getElements("SuperOwner");
  11015. StringBuffer pname;
  11016. StringBuffer query;
  11017. ForEach(*iter) {
  11018. if (iter->query().getProp("@name",pname.clear())) {
  11019. CDfsLogicalFileName lfn;
  11020. lfn.set(pname.str());
  11021. lfn.makeFullnameQuery(query.clear(),DXB_SuperFile,true);
  11022. Owned<IRemoteConnection> conn;
  11023. try {
  11024. conn.setown(querySDS().connect(query.str(),myProcessSession(),RTM_LOCK_WRITE,1000*60*5));
  11025. }
  11026. catch (ISDSException *e) {
  11027. if (SDSExcpt_LockTimeout != e->errorCode())
  11028. throw;
  11029. e->Release();
  11030. IWARNLOG("migrateSuperOwnersAttr: Could not lock parent %s",query.str());
  11031. conn.setown(querySDS().connect(query.str(),myProcessSession(),0,defaultTimeout));
  11032. }
  11033. if (conn) {
  11034. migrateAttr(from,conn->queryRoot());
  11035. migrateSuperOwnersAttr(conn->queryRoot());
  11036. }
  11037. else
  11038. IWARNLOG("migrateSuperOwnersAttr could not connect to parent superfile %s",lfn.get());
  11039. }
  11040. }
  11041. }
  11042. public:
  11043. IMPLEMENT_IINTERFACE;
  11044. CLightWeightSuperFileConn(unsigned _defaultTimeout, IUserDescriptor *_udesc)
  11045. {
  11046. defaultTimeout = _defaultTimeout;
  11047. readonly = false;
  11048. udesc.set(_udesc);
  11049. }
  11050. bool connect(CDistributedFileDirectory *parent,const char *title, const char *name, bool _readonly, bool *autocreate, unsigned timeout)
  11051. {
  11052. if (autocreate)
  11053. *autocreate = false;
  11054. readonly = _readonly;
  11055. disconnect(false);
  11056. CDfsLogicalFileName lfn;
  11057. if (!lfn.setValidate(name))
  11058. throw MakeStringException(-1,"%s: Invalid superfile name '%s'",title,name);
  11059. if (lfn.isMulti()||lfn.isExternal()||lfn.isForeign())
  11060. return false;
  11061. unsigned mode = RTM_SUB | (readonly ? RTM_LOCK_READ : RTM_LOCK_WRITE);
  11062. if (!lock.init(lfn, DXB_SuperFile, mode, timeout, title))
  11063. {
  11064. if (!autocreate) // NB not !*autocreate here !
  11065. return false;
  11066. IPropertyTree *root = createPTree();
  11067. root->setPropInt("@interleaved",2);
  11068. root->setPropInt("@numsubfiles",0);
  11069. root->setPropTree("Attr",getEmptyAttr());
  11070. parent->addEntry(lfn,root,true,false);
  11071. mode = RTM_SUB | RTM_LOCK_WRITE;
  11072. if (!lock.init(lfn, DXB_SuperFile, mode, timeout, title))
  11073. throw MakeStringException(-1,"%s: Cannot create superfile '%s'",title,name);
  11074. if (autocreate)
  11075. *autocreate = true;
  11076. }
  11077. StringBuffer reason;
  11078. if (!readonly&&checkProtectAttr(name,lock.queryRoot(),reason))
  11079. throw MakeStringException(-1,"CDistributedSuperFile::%s %s",title,reason.str());
  11080. return true;
  11081. }
  11082. void disconnect(bool commit)
  11083. {
  11084. if (lock.queryConnection()&&!readonly) {
  11085. if (commit) {
  11086. migrateSuperOwnersAttr(lock.queryRoot());
  11087. CDateTime dt;
  11088. dt.setNow();
  11089. StringBuffer s;
  11090. lock.queryRoot()->setProp("@modified",dt.getString(s).str());
  11091. }
  11092. else {
  11093. ForEachItemIn(i,children)
  11094. children.item(i).rollback();
  11095. lock.queryConnection()->rollback();
  11096. }
  11097. }
  11098. lock.clear();
  11099. children.kill();
  11100. }
  11101. unsigned numSubFiles() const
  11102. {
  11103. return (unsigned)lock.queryRoot()->getPropInt("@numsubfiles");
  11104. }
  11105. bool getSubFileName(unsigned num,StringBuffer &name) const
  11106. {
  11107. if ((unsigned)lock.queryRoot()->getPropInt("@numsubfiles")<=num)
  11108. return false;
  11109. StringBuffer xpath;
  11110. getSubPath(xpath,num);
  11111. IPropertyTree *sub = lock.queryRoot()->queryPropTree(xpath.str());
  11112. if (!sub)
  11113. return false;
  11114. name.append(sub->queryProp("@name"));
  11115. return true;
  11116. }
  11117. unsigned findSubName(const char *subname) const
  11118. {
  11119. unsigned n = findSubFileOrd(subname);
  11120. if (n!=NotFound)
  11121. return n;
  11122. StringBuffer lfn;
  11123. normalizeLFN(subname,lfn);
  11124. Owned<IPropertyTreeIterator> iter = lock.queryRoot()->getElements("SubFile");
  11125. ForEach(*iter) {
  11126. if (stricmp(iter->query().queryProp("@name"),lfn.str())==0) {
  11127. unsigned ret=iter->query().getPropInt("@num");
  11128. if (ret&&((unsigned)lock.queryRoot()->getPropInt("@numsubfiles")>=ret))
  11129. return ret-1;
  11130. }
  11131. }
  11132. return NotFound;
  11133. }
  11134. unsigned getContents(StringArray &contents) const
  11135. {
  11136. // slightly inefficient
  11137. unsigned n = lock.queryRoot()->getPropInt("@numsubfiles");
  11138. StringBuffer xpath;
  11139. for (unsigned sni=0;sni<n;sni++) {
  11140. getSubPath(xpath.clear(),sni);
  11141. IPropertyTree *sub = lock.queryRoot()->queryPropTree(xpath.str());
  11142. if (!sub)
  11143. break;
  11144. contents.append(sub->queryProp("@name"));
  11145. }
  11146. return contents.ordinality();
  11147. }
  11148. };
  11149. // Contention never expected for this function!
  11150. #define PROMOTE_CONN_TIMEOUT (60*1000) // how long to wait for a single superfile
  11151. #define PROMOTE_DELAY (30*1000)
  11152. // Check files don't share subfiles (MORE - make this part of swap files action?)
  11153. static int hasCommonSubChildren(IDistributedSuperFile *orig, IDistributedSuperFile *dest)
  11154. {
  11155. unsigned origSubs = orig->numSubFiles();
  11156. unsigned destSubs = dest->numSubFiles();
  11157. if (origSubs == 0)
  11158. return NotFound;
  11159. for (unsigned j=0; j<origSubs; j++) {
  11160. for (unsigned k=0; k<destSubs; k++) {
  11161. if (strcmp(orig->querySubFile(j).queryLogicalName(), dest->querySubFile(k).queryLogicalName())==0)
  11162. return j;
  11163. }
  11164. }
  11165. return NotFound;
  11166. }
  11167. // MORE - use string arrays, rather than char* arrays or comma-separated strings
  11168. void CDistributedFileDirectory::promoteSuperFiles(unsigned numsf,const char **sfnames,const char *addsubnames,bool delsub,bool createonlyonesuperfile,IUserDescriptor *user,unsigned timeout,StringArray &outunlinked)
  11169. {
  11170. if (!numsf)
  11171. return;
  11172. // Create a local transaction that will be destroyed
  11173. Owned<IDistributedFileTransactionExt> transaction = new CDistributedFileTransaction(user);
  11174. transaction->start();
  11175. // Lookup all files (keep them in transaction's cache)
  11176. bool created = false;
  11177. unsigned files = numsf;
  11178. for (unsigned i=0; i<numsf; i++) {
  11179. Owned<IDistributedSuperFile> super = transaction->lookupSuperFile(sfnames[i]);
  11180. if (!super.get()) {
  11181. if (created && createonlyonesuperfile) {
  11182. files = i;
  11183. break;
  11184. }
  11185. Owned<IDistributedSuperFile> sfile = createSuperFile(sfnames[i],user,true,false,transaction);
  11186. created = true;
  11187. }
  11188. }
  11189. // If last file had sub-files, clean and fill outlinked
  11190. Owned<IDistributedSuperFile> last = transaction->lookupSuperFile(sfnames[files-1]);
  11191. assertex(last.get());
  11192. unsigned lastSubs = last->numSubFiles();
  11193. if (files == numsf && lastSubs > 0) {
  11194. for (unsigned i=0; i<lastSubs; i++) {
  11195. outunlinked.append(last->querySubFile(i).queryLogicalName());
  11196. }
  11197. last->removeSubFile(NULL,false,false,transaction);
  11198. }
  11199. last.clear();
  11200. // Move up, starting from last
  11201. for (unsigned i=files-1; i; i--) {
  11202. Owned<IDistributedSuperFile> orig = transaction->lookupSuperFile(sfnames[i-1]);
  11203. Owned<IDistributedSuperFile> dest = transaction->lookupSuperFile(sfnames[i]);
  11204. assertex(orig.get());
  11205. assertex(dest.get());
  11206. int common = hasCommonSubChildren(orig, dest);
  11207. if (common != NotFound) {
  11208. throw MakeStringException(-1,"promoteSuperFiles: superfiles %s and %s share same subfile %s",
  11209. orig->queryLogicalName(), dest->queryLogicalName(), orig->querySubFile(common).queryLogicalName());
  11210. }
  11211. orig->swapSuperFile(dest, transaction);
  11212. }
  11213. // Move new subs to first super, if any
  11214. Owned<IDistributedSuperFile> first = transaction->lookupSuperFile(sfnames[0]);
  11215. assertex(first.get());
  11216. StringArray toadd;
  11217. toadd.appendListUniq(addsubnames, ",");
  11218. ForEachItemIn(i,toadd) {
  11219. CDfsLogicalFileName lfn;
  11220. if (!lfn.setValidate(toadd.item(i)))
  11221. throw MakeStringException(-1,"promoteSuperFiles: invalid logical name to add: %s",toadd.item(i));
  11222. first->addSubFile(toadd.item(i),false,NULL,false,transaction);
  11223. }
  11224. first.clear();
  11225. transaction->commit();
  11226. // MORE - once deletion of logic files are also in transaction we can move this up (and allow promote within transactions)
  11227. if (delsub) {
  11228. ForEachItemIn(j,outunlinked)
  11229. removeEntry(outunlinked.item(j),user,transaction,timeout);
  11230. }
  11231. }
  11232. ISimpleSuperFileEnquiry * CDistributedFileDirectory::getSimpleSuperFileEnquiry(const char *logicalname,const char *title,IUserDescriptor *udesc,unsigned timeout)
  11233. {
  11234. Owned<CLightWeightSuperFileConn> ret = new CLightWeightSuperFileConn(defaultTimeout,udesc);
  11235. if (ret->connect(this,title,logicalname,true,NULL,timeout))
  11236. return ret.getClear();
  11237. return NULL;
  11238. }
  11239. bool CDistributedFileDirectory::getFileSuperOwners(const char *logicalname, StringArray &owners)
  11240. {
  11241. CFileLock lock;
  11242. CDfsLogicalFileName lfn;
  11243. if (!lfn.setValidate(logicalname))
  11244. throw MakeStringException(-1,"CDistributedFileDirectory::getFileSuperOwners: Invalid file name '%s'",logicalname);
  11245. if (lfn.isMulti()||lfn.isExternal()||lfn.isForeign())
  11246. return false;
  11247. CTimeMon tm(defaultTimeout);
  11248. if (!lock.init(lfn, RTM_LOCK_READ, defaultTimeout, "CDistributedFileDirectory::getFileSuperOwners"))
  11249. return false;
  11250. CFileSuperOwnerLock superOwnerLock;
  11251. unsigned remaining;
  11252. tm.timedout(&remaining);
  11253. verifyex(superOwnerLock.initWithFileLock(lfn, remaining, "CDistributedFileDirectory::getFileSuperOwners(SuperOwnerLock)", lock, RTM_LOCK_READ));
  11254. Owned<IPropertyTreeIterator> iter = lock.queryRoot()->getElements("SuperOwner");
  11255. StringBuffer pname;
  11256. ForEach(*iter) {
  11257. iter->query().getProp("@name",pname.clear());
  11258. if (pname.length())
  11259. owners.append(pname.str());
  11260. }
  11261. return true;
  11262. }
  11263. class CFileRelationship: implements IFileRelationship, public CInterface
  11264. {
  11265. Linked<IPropertyTree> pt;
  11266. const char *queryProp(const char *name)
  11267. {
  11268. if (pt.get()) {
  11269. const char *ret = pt->queryProp(name);
  11270. if (ret)
  11271. return ret;
  11272. }
  11273. return "";
  11274. }
  11275. public:
  11276. IMPLEMENT_IINTERFACE;
  11277. CFileRelationship(IPropertyTree *_pt)
  11278. : pt(_pt)
  11279. {
  11280. }
  11281. virtual const char *queryKind() { return queryProp("@kind"); }
  11282. virtual const char *queryPrimaryFilename() { return queryProp("@primary"); }
  11283. virtual const char *querySecondaryFilename() { return queryProp("@secondary"); }
  11284. virtual const char *queryPrimaryFields() { return queryProp("@primflds"); }
  11285. virtual const char *querySecondaryFields() { return queryProp("@secflds"); }
  11286. virtual const char *queryCardinality() { return queryProp("@cardinality"); }
  11287. virtual bool isPayload() { return pt->getPropBool("@payload"); }
  11288. virtual const char *queryDescription() { return queryProp("Description"); }
  11289. virtual IPropertyTree *queryTree() { return pt.get(); }
  11290. };
  11291. class CFileRelationshipIterator: implements IFileRelationshipIterator, public CInterface
  11292. {
  11293. unsigned num;
  11294. unsigned idx;
  11295. CMessageBuffer mb;
  11296. Owned<CFileRelationship> r;
  11297. Owned<IPropertyTree> pt;
  11298. Linked<INode> foreigndali;
  11299. unsigned defaultTimeout;
  11300. bool setPT()
  11301. {
  11302. if (idx<num) {
  11303. pt.setown(createPTree(mb));
  11304. addForeignName(*pt,foreigndali,"@primary");
  11305. addForeignName(*pt,foreigndali,"@secondary");
  11306. }
  11307. return pt.get()!=NULL;
  11308. }
  11309. public:
  11310. IMPLEMENT_IINTERFACE;
  11311. CFileRelationshipIterator(unsigned timems)
  11312. {
  11313. num = 0;
  11314. idx = 0;
  11315. mb.append(num);
  11316. defaultTimeout = timems;
  11317. }
  11318. void init(
  11319. INode *_foreigndali,
  11320. unsigned foreigndalitimeout,
  11321. const char *primary,
  11322. const char *secondary,
  11323. const char *primflds,
  11324. const char *secflds,
  11325. const char *kind,
  11326. const char *cardinality,
  11327. const bool *payload )
  11328. {
  11329. foreigndali.set(_foreigndali);
  11330. if (isLocalDali(foreigndali)) {
  11331. CConnectLock connlock("lookupFileRelationships",querySdsRelationshipsRoot(),false,false,false,defaultTimeout);
  11332. StringBuffer xpath;
  11333. CDistributedFileDirectory::getFileRelationshipXPath(xpath,primary,secondary,primflds,secflds,kind,cardinality,payload);
  11334. Owned<IPropertyTreeIterator> iter = connlock.conn?connlock.conn->getElements(xpath.str()):NULL;
  11335. mb.clear();
  11336. unsigned count = 0;
  11337. mb.append(count);
  11338. // save as sequence of branches
  11339. if (iter) {
  11340. ForEach(*iter.get()) {
  11341. iter->query().serialize(mb);
  11342. count++;
  11343. }
  11344. mb.writeDirect(0,sizeof(count),&count);
  11345. }
  11346. }
  11347. else {
  11348. byte payloadb = 255;
  11349. if (payload)
  11350. payloadb = *payload?1:0;
  11351. mb.clear().append((int)MDFS_ITERATE_RELATIONSHIPS).append(primary).append(secondary).append(primflds).append(secflds).append(kind).append(cardinality).append(payloadb);
  11352. foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
  11353. checkDfsReplyException(mb);
  11354. if (mb.length()<sizeof(unsigned))
  11355. mb.clear().append((unsigned)0);
  11356. }
  11357. }
  11358. void initall(const char *filename)
  11359. {
  11360. StringBuffer xpath;
  11361. Owned<IPropertyTreeIterator> iter;
  11362. mb.clear();
  11363. unsigned count = 0;
  11364. mb.append(count);
  11365. {
  11366. CConnectLock connlock("lookupFileRelationships",querySdsRelationshipsRoot(),false,false,false,defaultTimeout);
  11367. CDistributedFileDirectory::getFileRelationshipXPath(xpath,filename,NULL,NULL,NULL,NULL,NULL,NULL);
  11368. // save as sequence of branches
  11369. iter.setown(connlock.conn?connlock.conn->getElements(xpath.str()):NULL);
  11370. if (iter) {
  11371. ForEach(*iter.get()) {
  11372. iter->query().serialize(mb);
  11373. count++;
  11374. }
  11375. }
  11376. }
  11377. { // Kludge - seems to be a bug in getElements without second conn lock
  11378. CConnectLock connlock("lookupFileRelationships",querySdsRelationshipsRoot(),false,false,false,defaultTimeout);
  11379. xpath.clear();
  11380. CDistributedFileDirectory::getFileRelationshipXPath(xpath,NULL,filename,NULL,NULL,NULL,NULL,NULL);
  11381. iter.clear();
  11382. iter.setown(connlock.conn?connlock.conn->getElements(xpath.str()):NULL);
  11383. if (iter) {
  11384. ForEach(*iter.get()) {
  11385. IPropertyTree &it = iter->query();
  11386. const char *fn1 = it.queryProp("@primary");
  11387. if (!fn1||(strcmp(fn1,filename)!=0)) { // see if already done
  11388. it.serialize(mb);
  11389. count++;
  11390. }
  11391. }
  11392. }
  11393. }
  11394. mb.writeDirect(0,sizeof(count),&count);
  11395. }
  11396. bool first()
  11397. {
  11398. r.clear();
  11399. pt.clear();
  11400. idx = 0;
  11401. mb.reset().read(num);
  11402. return setPT();
  11403. }
  11404. bool next()
  11405. {
  11406. r.clear();
  11407. pt.clear();
  11408. idx++;
  11409. return setPT();
  11410. }
  11411. bool isValid()
  11412. {
  11413. return pt.get()!=NULL;
  11414. }
  11415. IFileRelationship & query()
  11416. {
  11417. if (!r)
  11418. r.setown(new CFileRelationship(pt));
  11419. return *r;
  11420. }
  11421. };
  11422. static bool isWild(const char *path,bool emptydefault=false)
  11423. {
  11424. if (!path||!*path)
  11425. return emptydefault;
  11426. return ((strchr(path,'?')||strchr(path,'*')));
  11427. }
  11428. static void addRelationCondition(StringBuffer &xpath,const char *fld,const char *mask)
  11429. {
  11430. if (!mask||!*mask||((*mask=='*')&&(!mask[1])))
  11431. return;
  11432. xpath.append('[').append(fld).append('=');
  11433. if (isWild(mask))
  11434. xpath.append('~');
  11435. xpath.append('"').append(mask).append("\"]");
  11436. }
  11437. static void addRelationBoolCondition(StringBuffer &xpath,const char *fld,const bool *mask)
  11438. {
  11439. if (!mask)
  11440. return;
  11441. xpath.append('[').append(fld).append("=\"");
  11442. if (*mask)
  11443. xpath.append("1\"]");
  11444. else
  11445. xpath.append("0\"]");
  11446. }
  11447. static const char *normLFN(const char *name,CDfsLogicalFileName &logicalname,const char *title)
  11448. {
  11449. if (isWild(name,true))
  11450. return name;
  11451. if (!logicalname.setValidate(name))
  11452. throw MakeStringException(-1,"%s: invalid logical file name '%s'",title,name);
  11453. if (logicalname.isForeign()) {
  11454. SocketEndpoint ep;
  11455. Owned<INode> fd = createINode(ep);
  11456. if (logicalname.getEp(ep)&&isLocalDali(fd)) // see if pointing back at self
  11457. logicalname.clearForeign();
  11458. }
  11459. return logicalname.get();
  11460. }
  11461. StringBuffer &CDistributedFileDirectory::getFileRelationshipXPath(
  11462. StringBuffer &xpath,
  11463. const char *primary,
  11464. const char *secondary,
  11465. const char *primflds,
  11466. const char *secflds,
  11467. const char *kind,
  11468. const char *cardinality,
  11469. const bool *payload
  11470. )
  11471. {
  11472. xpath.append("Relationship");
  11473. CDfsLogicalFileName lfn;
  11474. addRelationCondition(xpath,"@kind",kind);
  11475. addRelationCondition(xpath,"@primary",normLFN(primary,lfn,"findFileRelationship(primary)"));
  11476. addRelationCondition(xpath,"@secondary",normLFN(secondary,lfn,"findFileRelationship(secondary)"));
  11477. addRelationCondition(xpath,"@primflds",primflds);
  11478. addRelationCondition(xpath,"@secflds",secflds);
  11479. addRelationCondition(xpath,"@cardinality",cardinality);
  11480. addRelationBoolCondition(xpath,"@payload",payload);
  11481. return xpath;
  11482. }
  11483. void CDistributedFileDirectory::doRemoveFileRelationship(
  11484. IRemoteConnection *conn,
  11485. const char *primary,
  11486. const char *secondary,
  11487. const char *primflds,
  11488. const char *secflds,
  11489. const char *kind
  11490. )
  11491. {
  11492. if (!conn)
  11493. return;
  11494. StringBuffer xpath;
  11495. CDistributedFileDirectory::getFileRelationshipXPath(xpath,primary,secondary,primflds,secflds,kind,NULL,NULL);
  11496. Owned<IPropertyTreeIterator> iter = conn->getElements(xpath.str());
  11497. IArrayOf<IPropertyTree> toremove;
  11498. ForEach(*iter) {
  11499. IPropertyTree &t = iter->query();
  11500. toremove.append(*LINK(&t));
  11501. }
  11502. ForEachItemIn(i, toremove) {
  11503. conn->queryRoot()->removeTree(&toremove.item(i));
  11504. }
  11505. }
  11506. void CDistributedFileDirectory::addFileRelationship(
  11507. const char *primary,
  11508. const char *secondary,
  11509. const char *primflds,
  11510. const char *secflds,
  11511. const char *kind,
  11512. const char *cardinality,
  11513. bool payload,
  11514. IUserDescriptor *user,
  11515. const char *description=NULL
  11516. )
  11517. {
  11518. if (!kind||!*kind)
  11519. kind = S_LINK_RELATIONSHIP_KIND;
  11520. Owned<IPropertyTree> pt = createPTree("Relationship");
  11521. if (isWild(primary,true)||isWild(secondary,true)||isWild(primflds,false)||isWild(secflds,false)||isWild(cardinality,false))
  11522. throw MakeStringException(-1,"Wildcard not allowed in addFileRelation");
  11523. CDfsLogicalFileName pfn;
  11524. if (!pfn.setValidate(primary))
  11525. throw MakeStringException(-1,"addFileRelationship invalid primary name '%s'",primary);
  11526. if (pfn.isExternal()||pfn.isForeign()||pfn.isQuery())
  11527. throw MakeStringException(-1,"addFileRelationship primary %s not allowed",pfn.get());
  11528. primary = pfn.get();
  11529. if (!exists(primary,user))
  11530. throw MakeStringException(-1,"addFileRelationship primary %s does not exist",primary);
  11531. CDfsLogicalFileName sfn;
  11532. if (!sfn.setValidate(secondary))
  11533. throw MakeStringException(-1,"addFileRelationship invalid secondary name '%s'",secondary);
  11534. if (sfn.isExternal()||sfn.isForeign()||sfn.isQuery())
  11535. throw MakeStringException(-1,"addFileRelationship secondary %s not allowed",sfn.get());
  11536. secondary = sfn.get();
  11537. if (!exists(secondary,user))
  11538. throw MakeStringException(-1,"addFileRelationship secondary %s does not exist",secondary);
  11539. if (cardinality&&*cardinality&&!strchr(cardinality,':'))
  11540. throw MakeStringException(-1,"addFileRelationship cardinality %s invalid",cardinality);
  11541. pt->setProp("@kind",kind);
  11542. pt->setProp("@primary",primary);
  11543. pt->setProp("@secondary",secondary);
  11544. pt->setProp("@cardinality",cardinality);
  11545. pt->setProp("@primflds",primflds);
  11546. pt->setProp("@secflds",secflds);
  11547. pt->setPropBool("@payload",payload);
  11548. if (description&&*description)
  11549. pt->setProp("Description",description);
  11550. StringBuffer xpath(querySdsFilesRoot());
  11551. for (unsigned i=0;i<2;i++) {
  11552. CConnectLock connlock("addFileRelation",querySdsRelationshipsRoot(),true,false,false,defaultTimeout);
  11553. if (!connlock.conn) {
  11554. CConnectLock connlock2("addFileRelation.2",querySdsFilesRoot(),true,false,false,defaultTimeout);
  11555. if (!connlock2.conn)
  11556. return;
  11557. Owned<IPropertyTree> ptr = createPTree("Relationships");
  11558. connlock2.conn->queryRoot()->addPropTree("Relationships",ptr.getClear());
  11559. continue;
  11560. }
  11561. StringBuffer query;
  11562. doRemoveFileRelationship(connlock.conn,primary,secondary,primflds,secflds,kind);
  11563. connlock.conn->queryRoot()->addPropTree("Relationship",pt.getClear());
  11564. break;
  11565. }
  11566. }
  11567. void CDistributedFileDirectory::removeFileRelationships(
  11568. const char *primary,
  11569. const char *secondary,
  11570. const char *primflds,
  11571. const char *secflds,
  11572. const char *kind
  11573. )
  11574. {
  11575. if ((!primary||!*primary||(strcmp(primary,"*")==0))&&
  11576. (!secondary||!*secondary||(strcmp(secondary,"*")==0)))
  11577. throw MakeStringException(-1,"removeFileRelationships primary and secondary cannot both be wild");
  11578. CConnectLock connlock("removeFileRelation",querySdsRelationshipsRoot(),true,false,false,defaultTimeout);
  11579. doRemoveFileRelationship(connlock.conn,primary,secondary,primflds,secflds,kind);
  11580. }
  11581. IFileRelationshipIterator *CDistributedFileDirectory::lookupFileRelationships(
  11582. const char *primary,
  11583. const char *secondary,
  11584. const char *primflds,
  11585. const char *secflds,
  11586. const char *kind,
  11587. const char *cardinality,
  11588. const bool *payload,
  11589. const char *foreigndali,
  11590. unsigned foreigndalitimeout
  11591. )
  11592. {
  11593. Owned<INode> foreign;
  11594. if (foreigndali&&*foreigndali) {
  11595. SocketEndpoint ep(foreigndali);
  11596. if (ep.isNull())
  11597. throw MakeStringException(-1,"lookupFileRelationships::Cannot resolve foreign dali %s",foreigndali);
  11598. foreign.setown(createINode(ep));
  11599. }
  11600. Owned<CFileRelationshipIterator> ret = new CFileRelationshipIterator(defaultTimeout);
  11601. ret->init(foreign,foreigndalitimeout,primary,secondary,primflds,secflds,kind,cardinality,payload);
  11602. return ret.getClear();
  11603. }
  11604. void CDistributedFileDirectory::removeAllFileRelationships(const char *filename)
  11605. {
  11606. if (!filename||!*filename||(strcmp(filename,"*")==0))
  11607. throw MakeStringException(-1,"removeAllFileRelationships filename cannot be wild");
  11608. {
  11609. CConnectLock connlock("removeFileRelation",querySdsRelationshipsRoot(),true,false,false,defaultTimeout);
  11610. doRemoveFileRelationship(connlock.conn,filename,NULL,NULL,NULL,NULL);
  11611. }
  11612. { // kludge bug in getElements if connection used twice
  11613. CConnectLock connlock("removeFileRelation",querySdsRelationshipsRoot(),true,false,false,defaultTimeout);
  11614. doRemoveFileRelationship(connlock.conn,NULL,filename,NULL,NULL,NULL);
  11615. }
  11616. }
  11617. IFileRelationshipIterator *CDistributedFileDirectory::lookupAllFileRelationships(
  11618. const char *filename)
  11619. {
  11620. if (isWild(filename,true))
  11621. throw MakeStringException(-1,"Wildcard filename not allowed in lookupAllFileRelationships");
  11622. CDfsLogicalFileName lfn;
  11623. normLFN(filename,lfn,"lookupAllFileRelationships");
  11624. Owned<CFileRelationshipIterator> ret = new CFileRelationshipIterator(defaultTimeout);
  11625. ret->initall(lfn.get());
  11626. return ret.getClear();
  11627. }
  11628. void CDistributedFileDirectory::renameFileRelationships(const char *oldname,const char *newname,IFileRelationshipIterator *reliter,IUserDescriptor*user)
  11629. {
  11630. CDfsLogicalFileName oldlfn;
  11631. normLFN(oldname,oldlfn,"renameFileRelationships(old name)");
  11632. CDfsLogicalFileName newlfn;
  11633. normLFN(newname,newlfn,"renameFileRelationships(new name)");
  11634. ForEach(*reliter) {
  11635. try {
  11636. IFileRelationship &r = reliter->query();
  11637. bool adj = false;
  11638. const char *pf = r.queryPrimaryFilename();
  11639. if (!pf)
  11640. continue;
  11641. if (strcmp(pf,oldlfn.get())==0) {
  11642. adj = true;
  11643. pf = newlfn.get();
  11644. }
  11645. const char *sf = r.querySecondaryFilename();
  11646. if (!sf)
  11647. continue;
  11648. if (strcmp(sf,oldlfn.get())==0) {
  11649. adj = true;
  11650. sf = newlfn.get();
  11651. }
  11652. if (adj)
  11653. addFileRelationship(pf,sf,r.queryPrimaryFields(),r.querySecondaryFields(),r.queryKind(),r.queryCardinality(),r.isPayload(),user,r.queryDescription());
  11654. }
  11655. catch (IException *e)
  11656. {
  11657. EXCLOG(e,"renameFileRelationships");
  11658. e->Release();
  11659. }
  11660. }
  11661. }
  11662. // JCSMORE what was this for, not called by anything afaics
  11663. bool CDistributedFileDirectory::publishMetaFileXML(const CDfsLogicalFileName &logicalname,IUserDescriptor *user)
  11664. {
  11665. if (logicalname.isExternal()||logicalname.isForeign()||logicalname.isQuery())
  11666. return false;
  11667. Owned<IPropertyTree> file = getFileTree(logicalname.get(),user,NULL,FOREIGN_DALI_TIMEOUT,true);
  11668. if (!file.get())
  11669. return false;
  11670. if (strcmp(file->queryName(),queryDfsXmlBranchName(DXB_SuperFile))==0)
  11671. return false;
  11672. unsigned max = file->getPropInt("@numparts");
  11673. SocketEndpointArray ips;
  11674. StringBuffer xpath;
  11675. StringBuffer ipstr;
  11676. for (unsigned i=0;i<max;i++) { // probably could be done better
  11677. xpath.clear().append("Part[@num=\"").append(i+1).append("\"]");
  11678. Owned<IPropertyTree> child = file->getPropTree(xpath.str());
  11679. SocketEndpoint ep;
  11680. if (child.get()&&child->getProp("@node",ipstr.clear()))
  11681. ep.ipset(ipstr.str());
  11682. ips.append(ep);
  11683. }
  11684. Owned<IException> exc;
  11685. CriticalSection errcrit;
  11686. class casyncfor: public CAsyncFor
  11687. {
  11688. IPropertyTree* file;
  11689. CriticalSection &errcrit;
  11690. Owned<IException> &exc;
  11691. SocketEndpointArray &ips;
  11692. public:
  11693. casyncfor(IPropertyTree* _file,SocketEndpointArray &_ips,Owned<IException> &_exc,CriticalSection &_errcrit)
  11694. : ips(_ips), exc(_exc), errcrit(_errcrit)
  11695. {
  11696. file = _file;
  11697. }
  11698. void Do(unsigned i)
  11699. {
  11700. UnsignedArray parts;
  11701. const SocketEndpoint &ep = ips.item(i);
  11702. if (ep.isNull())
  11703. return;
  11704. ForEachItemIn(j,ips) {
  11705. if (j==i)
  11706. parts.append(i);
  11707. else if (ep.ipequals(ips.item(j))) {
  11708. if (j<i)
  11709. return; // already done
  11710. parts.append(j);
  11711. }
  11712. }
  11713. try {
  11714. StringBuffer path;
  11715. StringBuffer mask;
  11716. if (file->getProp("@directory",path)&&file->getProp("@partmask",mask)) {
  11717. addPathSepChar(path).append(mask);
  11718. StringBuffer outpath;
  11719. StringBuffer tail("META__");
  11720. splitFilename(path.str(), &outpath, &outpath, &tail, NULL);
  11721. outpath.append(tail).append(".xml");
  11722. Owned<IPropertyTree> pt = createPTreeFromIPT(file);
  11723. filterParts(pt,parts);
  11724. StringBuffer str;
  11725. toXML(pt, str);
  11726. RemoteFilename rfn;
  11727. rfn.setPath(ep,outpath.str());
  11728. Owned<IFile> out = createIFile(rfn);
  11729. Owned<IFileIO> outio = out->open(IFOcreate);
  11730. if (outio)
  11731. outio->write(0,str.length(),str.str());
  11732. }
  11733. }
  11734. catch(IException *e)
  11735. {
  11736. CriticalBlock block(errcrit);
  11737. EXCLOG(e,"publishMetaFileXML");
  11738. if (!exc.get())
  11739. exc.setown(e);
  11740. else
  11741. e->Release();
  11742. }
  11743. }
  11744. } afor(file,ips,exc,errcrit);
  11745. afor.For(max,20);
  11746. if (exc)
  11747. throw exc.getClear();
  11748. return true;
  11749. }
  11750. IFileDescriptor *CDistributedFileDirectory::createDescriptorFromMetaFile(const CDfsLogicalFileName &logicalname,IUserDescriptor *user)
  11751. {
  11752. return NULL; // TBD
  11753. }
  11754. // Overwrite protection
  11755. bool CDistributedFileDirectory::isProtectedFile(const CDfsLogicalFileName &logicalName, unsigned timeout)
  11756. {
  11757. CFileAttrLock attrLock;
  11758. if (!attrLock.init(logicalName, RTM_LOCK_READ, NULL, timeout?timeout:INFINITE, "CDistributedFileDirectory::isProtectedFile"))
  11759. return false; // timeout will raise exception
  11760. return attrLock.queryRoot()->hasProp("Protect");
  11761. }
  11762. IDFProtectedIterator *CDistributedFileDirectory::lookupProtectedFiles(const char *owner,bool notsuper,bool superonly)
  11763. {
  11764. return new CDFProtectedIterator(owner,notsuper,superonly,defaultTimeout);
  11765. }
  11766. const char* DFUQResultFieldNames[] = { "@name", "@description", "@group", "@kind", "@modified", "@job", "@owner",
  11767. "@DFUSFrecordCount", "@recordCount", "@recordSize", "@DFUSFsize", "@size", "@workunit", "@DFUSFcluster", "@numsubfiles",
  11768. "@accessed", "@numparts", "@compressedSize", "@directory", "@partmask", "@superowners", "@persistent", "@protect", "@compressed" };
  11769. extern da_decl const char* getDFUQResultFieldName(DFUQResultField feild)
  11770. {
  11771. return DFUQResultFieldNames[feild];
  11772. }
  11773. IPropertyTreeIterator *deserializeFileAttrIterator(MemoryBuffer& mb, unsigned numFiles, DFUQResultField* localFilters, const char* localFilterBuf)
  11774. {
  11775. class CFileAttrIterator: implements IPropertyTreeIterator, public CInterface
  11776. {
  11777. size32_t fileDataStart;
  11778. Owned<IPropertyTree> cur;
  11779. StringArray fileNodeGroups;
  11780. void setFileNodeGroup(IPropertyTree *attr, const char* group, StringArray& nodeGroupFilter)
  11781. {
  11782. if (!group || !*group)
  11783. return;
  11784. //The group may contain multiple clusters and some of them may match with the clusterFilter.
  11785. if (nodeGroupFilter.length() == 1)
  11786. attr->setProp(getDFUQResultFieldName(DFUQRFnodegroup), nodeGroupFilter.item(0));//Filter has been handled on server side.
  11787. else
  11788. {
  11789. StringArray groups;
  11790. groups.appendListUniq(group, ",");
  11791. ForEachItemIn(i,groups)
  11792. {
  11793. //Add a group if no group filter or the group matches with group filter
  11794. const char* node = groups.item(i);
  11795. if (node && *node && ((!nodeGroupFilter.length()) || (nodeGroupFilter.find(node) != NotFound)))
  11796. fileNodeGroups.append(node);
  11797. }
  11798. if (fileNodeGroups.length())
  11799. {
  11800. //if this file exists on multiple groups, set one of the groups as the "@DFUSFnodegroup" prop for
  11801. //this attr, leaving the rest inside the fileNodeGroups array. Those groups will be used by the
  11802. //duplicateFileAttrOnOtherNodeGroup() to duplicate this file attr on other groups.
  11803. attr->setProp(getDFUQResultFieldName(DFUQRFnodegroup), fileNodeGroups.item(fileNodeGroups.length() -1));
  11804. fileNodeGroups.pop();
  11805. }
  11806. }
  11807. }
  11808. void setRecordCount(IPropertyTree* file)
  11809. {
  11810. __int64 recordCount = 0;
  11811. if (file->hasProp(getDFUQResultFieldName(DFUQRForigrecordcount)))
  11812. recordCount = file->getPropInt64(getDFUQResultFieldName(DFUQRForigrecordcount));
  11813. else
  11814. {
  11815. __int64 recordSize=file->getPropInt64(getDFUQResultFieldName(DFUQRFrecordsize),0);
  11816. if(recordSize)
  11817. {
  11818. __int64 size=file->getPropInt64(getDFUQResultFieldName(DFUQRForigsize),-1);
  11819. recordCount = size/recordSize;
  11820. }
  11821. }
  11822. file->setPropInt64(getDFUQResultFieldName(DFUQRFrecordcount),recordCount);
  11823. return;
  11824. }
  11825. void setIsCompressed(IPropertyTree* file)
  11826. {
  11827. if (isCompressed(*file) || isFileKey(*file))
  11828. file->setPropBool(getDFUQResultFieldName(DFUQRFiscompressed), true);
  11829. }
  11830. IPropertyTree *deserializeFileAttr(MemoryBuffer &mb, StringArray& nodeGroupFilter)
  11831. {
  11832. IPropertyTree *attr = getEmptyAttr();
  11833. StringAttr val;
  11834. unsigned n;
  11835. mb.read(val);
  11836. attr->setProp(getDFUQResultFieldName(DFUQRFname),val.get());
  11837. mb.read(val);
  11838. if (strieq(val,"!SF"))
  11839. {
  11840. mb.read(n);
  11841. attr->setPropInt(getDFUQResultFieldName(DFUQRFnumsubfiles),n);
  11842. mb.read(val); // not used currently
  11843. }
  11844. else
  11845. {
  11846. attr->setProp(getDFUQResultFieldName(DFUQRFdirectory),val.get());
  11847. mb.read(n);
  11848. attr->setPropInt(getDFUQResultFieldName(DFUQRFnumparts),n);
  11849. mb.read(val);
  11850. attr->setProp(getDFUQResultFieldName(DFUQRFpartmask),val.get());
  11851. }
  11852. mb.read(val);
  11853. attr->setProp(getDFUQResultFieldName(DFUQRFtimemodified),val.get());
  11854. unsigned count;
  11855. mb.read(count);
  11856. StringAttr at;
  11857. while (count--)
  11858. {
  11859. mb.read(at);
  11860. mb.read(val);
  11861. attr->setProp(at.get(),val.get());
  11862. if (strieq(at.get(), getDFUQResultFieldName(DFUQRFnodegroups)))
  11863. setFileNodeGroup(attr, val.get(), nodeGroupFilter);
  11864. }
  11865. attr->setPropInt64(getDFUQResultFieldName(DFUQRFsize), attr->getPropInt64(getDFUQResultFieldName(DFUQRForigsize), -1));//Sort the files with empty size to front
  11866. setRecordCount(attr);
  11867. setIsCompressed(attr);
  11868. return attr;
  11869. }
  11870. IPropertyTree *duplicateFileAttrOnOtherNodeGroup(IPropertyTree *previousAttr)
  11871. {
  11872. IPropertyTree *attr = getEmptyAttr();
  11873. Owned<IAttributeIterator> ai = previousAttr->getAttributes();
  11874. ForEach(*ai)
  11875. attr->setProp(ai->queryName(),ai->queryValue());
  11876. attr->setProp(getDFUQResultFieldName(DFUQRFnodegroup), fileNodeGroups.item(fileNodeGroups.length()-1));
  11877. fileNodeGroups.pop();
  11878. return attr;
  11879. }
  11880. public:
  11881. IMPLEMENT_IINTERFACE;
  11882. MemoryBuffer mb;
  11883. unsigned numfiles;
  11884. StringArray nodeGroupFilter;
  11885. CFileAttrIterator(MemoryBuffer &_mb, unsigned _numfiles) : numfiles(_numfiles)
  11886. {
  11887. /* not particuarly nice, but buffer contains extra meta info ahead of serialized file info
  11888. * record position to rewind to, if iterator reused.
  11889. */
  11890. fileDataStart = _mb.getPos();
  11891. mb.swapWith(_mb);
  11892. }
  11893. bool first()
  11894. {
  11895. mb.reset(fileDataStart);
  11896. return next();
  11897. }
  11898. bool next()
  11899. {
  11900. if (fileNodeGroups.length())
  11901. {
  11902. IPropertyTree *attr = duplicateFileAttrOnOtherNodeGroup(cur);
  11903. cur.clear();
  11904. cur.setown(attr);
  11905. return true;
  11906. }
  11907. cur.clear();
  11908. if (mb.getPos()>=mb.length())
  11909. return false;
  11910. cur.setown(deserializeFileAttr(mb, nodeGroupFilter));
  11911. return true;
  11912. }
  11913. bool isValid()
  11914. {
  11915. return cur.get()!=NULL;
  11916. }
  11917. IPropertyTree & query()
  11918. {
  11919. return *cur;
  11920. }
  11921. void setLocalFilters(DFUQResultField* localFilters, const char* localFilterBuf)
  11922. {
  11923. if (!localFilters || !localFilterBuf || !*localFilterBuf)
  11924. return;
  11925. const char *fv = localFilterBuf;
  11926. for (unsigned i=0;localFilters[i]!=DFUQRFterm;i++)
  11927. {
  11928. int fmt = localFilters[i];
  11929. int subfmt = (fmt&0xff);
  11930. if ((subfmt==DFUQRFnodegroup) && fv && *fv)
  11931. nodeGroupFilter.appendListUniq(fv, ",");
  11932. //Add more if needed
  11933. fv = fv + strlen(fv)+1;
  11934. }
  11935. }
  11936. } *fai = new CFileAttrIterator(mb, numFiles);
  11937. fai->setLocalFilters(localFilters, localFilterBuf);
  11938. return fai;
  11939. }
  11940. IPropertyTreeIterator *CDistributedFileDirectory::getDFAttributesTreeIterator(const char* filters, DFUQResultField* localFilters,
  11941. const char* localFilterBuf, IUserDescriptor* user, bool recursive, bool& allMatchingFilesReceived, INode* foreigndali, unsigned foreigndalitimeout)
  11942. {
  11943. CMessageBuffer mb;
  11944. CDaliVersion serverVersionNeeded("3.13");
  11945. bool legacy = (queryDaliServerVersion().compare(serverVersionNeeded) < 0);
  11946. if (legacy)
  11947. mb.append((int)MDFS_ITERATE_FILTEREDFILES);
  11948. else
  11949. mb.append((int)MDFS_ITERATE_FILTEREDFILES2);
  11950. mb.append(filters).append(recursive);
  11951. if (user)
  11952. {
  11953. user->serializeWithoutPassword(mb);
  11954. }
  11955. if (foreigndali)
  11956. foreignDaliSendRecv(foreigndali,mb,foreigndalitimeout);
  11957. else
  11958. queryCoven().sendRecv(mb,RANK_RANDOM,MPTAG_DFS_REQUEST);
  11959. checkDfsReplyException(mb);
  11960. unsigned numfiles;
  11961. mb.read(numfiles);
  11962. if (legacy)
  11963. allMatchingFilesReceived = true; // don't know any better
  11964. else
  11965. mb.read(allMatchingFilesReceived);
  11966. return deserializeFileAttrIterator(mb, numfiles, localFilters, localFilterBuf);
  11967. }
  11968. IDFAttributesIterator* CDistributedFileDirectory::getLogicalFiles(
  11969. IUserDescriptor* udesc,
  11970. DFUQResultField *sortOrder, // list of fields to sort by (terminated by DFUSFterm)
  11971. const void *filters, // (appended) string values for filters used by dali server
  11972. DFUQResultField *localFilters, //used for filtering query result received from dali server.
  11973. const void *localFilterBuf,
  11974. unsigned startOffset,
  11975. unsigned maxNum,
  11976. __int64 *cacheHint,
  11977. unsigned *total,
  11978. bool *allMatchingFiles,
  11979. bool recursive,
  11980. bool sorted)
  11981. {
  11982. class CDFUPager : implements IElementsPager, public CSimpleInterface
  11983. {
  11984. IUserDescriptor* udesc;
  11985. //StringAttr clusterFilter;
  11986. StringAttr filters;
  11987. DFUQResultField *localFilters;
  11988. StringAttr localFilterBuf;
  11989. StringAttr sortOrder;
  11990. bool recursive, sorted;
  11991. bool allMatchingFilesReceived;
  11992. public:
  11993. IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
  11994. CDFUPager(IUserDescriptor* _udesc, const char*_filters, DFUQResultField*_localFilters, const char*_localFilterBuf,
  11995. const char*_sortOrder, bool _recursive, bool _sorted) : udesc(_udesc), filters(_filters), localFilters(_localFilters), localFilterBuf(_localFilterBuf),
  11996. sortOrder(_sortOrder), recursive(_recursive), sorted(_sorted)
  11997. {
  11998. allMatchingFilesReceived = true;
  11999. }
  12000. virtual IRemoteConnection* getElements(IArrayOf<IPropertyTree> &elements)
  12001. {
  12002. Owned<IPropertyTreeIterator> fi = queryDistributedFileDirectory().getDFAttributesTreeIterator(filters.get(),
  12003. localFilters, localFilterBuf.get(), udesc, recursive, allMatchingFilesReceived);
  12004. StringArray unknownAttributes;
  12005. sortElements(fi, sorted ? sortOrder.get() : NULL, NULL, NULL, unknownAttributes, elements);
  12006. return NULL;
  12007. }
  12008. virtual bool allMatchingElementsReceived() { return allMatchingFilesReceived; }
  12009. };
  12010. StringBuffer so;
  12011. if (sorted && sortOrder)
  12012. {
  12013. for (unsigned i=0;sortOrder[i]!=DFUQRFterm;i++)
  12014. {
  12015. if (so.length())
  12016. so.append(',');
  12017. int fmt = sortOrder[i];
  12018. if (fmt&DFUQRFreverse)
  12019. so.append('-');
  12020. if (fmt&DFUQRFnocase)
  12021. so.append('?');
  12022. if (fmt&DFUQRFnumeric)
  12023. so.append('#');
  12024. so.append(getDFUQResultFieldName((DFUQResultField) (fmt&0xff)));
  12025. }
  12026. }
  12027. IArrayOf<IPropertyTree> results;
  12028. Owned<IElementsPager> elementsPager = new CDFUPager(udesc, (const char*) filters, localFilters, (const char*) localFilterBuf,
  12029. so.length()?so.str():NULL, recursive, sorted);
  12030. Owned<IRemoteConnection> conn = getElementsPaged(elementsPager,startOffset,maxNum,NULL,"",cacheHint,results,total,allMatchingFiles,false);
  12031. return new CDFAttributeIterator(results);
  12032. }
  12033. IDFAttributesIterator* CDistributedFileDirectory::getLogicalFilesSorted(
  12034. IUserDescriptor* udesc,
  12035. DFUQResultField *sortOrder, // list of fields to sort by (terminated by DFUSFterm)
  12036. const void *filters, // (appended) string values for filters used by dali server
  12037. DFUQResultField *localFilters, //used for filtering query result received from dali server.
  12038. const void *localFilterBuf,
  12039. unsigned startOffset,
  12040. unsigned maxNum,
  12041. __int64 *cacheHint,
  12042. unsigned *total,
  12043. bool *allMatchingFiles)
  12044. {
  12045. return getLogicalFiles(udesc, sortOrder, filters, localFilters, localFilterBuf, startOffset, maxNum,
  12046. cacheHint, total, allMatchingFiles, true, true);
  12047. }
  12048. bool CDistributedFileDirectory::removePhysicalPartFiles(const char *logicalName, IFileDescriptor *fileDesc, IMultiException *mexcept, unsigned numParallelDeletes)
  12049. {
  12050. class casyncfor: public CAsyncFor
  12051. {
  12052. IFileDescriptor *fileDesc;
  12053. CriticalSection errcrit;
  12054. IMultiException *mexcept;
  12055. public:
  12056. bool ok;
  12057. bool islazy;
  12058. casyncfor(IFileDescriptor *_fileDesc, IMultiException *_mexcept)
  12059. {
  12060. fileDesc = _fileDesc;
  12061. ok = true;
  12062. islazy = false;
  12063. mexcept = _mexcept;
  12064. }
  12065. void Do(unsigned i)
  12066. {
  12067. IPartDescriptor *part = fileDesc->queryPart(i);
  12068. unsigned nc = part->numCopies();
  12069. for (unsigned copy = 0; copy < nc; copy++)
  12070. {
  12071. RemoteFilename rfn;
  12072. part->getFilename(copy, rfn);
  12073. Owned<IFile> partfile = createIFile(rfn);
  12074. StringBuffer eps;
  12075. try
  12076. {
  12077. unsigned start = msTick();
  12078. if (!partfile->remove()&&(copy==0)&&!islazy) // only warn about missing primary files
  12079. LOG(MCwarning, unknownJob, "Failed to remove file part %s from %s", partfile->queryFilename(),rfn.queryEndpoint().getUrlStr(eps).str());
  12080. else
  12081. {
  12082. unsigned t = msTick()-start;
  12083. if (t>5*1000)
  12084. LOG(MCwarning, unknownJob, "Removing %s from %s took %ds", partfile->queryFilename(), rfn.queryEndpoint().getUrlStr(eps).str(), t/1000);
  12085. }
  12086. }
  12087. catch (IException *e)
  12088. {
  12089. CriticalBlock block(errcrit);
  12090. if (mexcept)
  12091. mexcept->append(*e);
  12092. else
  12093. {
  12094. StringBuffer s("Failed to remove file part ");
  12095. s.append(partfile->queryFilename()).append(" from ");
  12096. rfn.queryEndpoint().getUrlStr(s);
  12097. EXCLOG(e, s.str());
  12098. e->Release();
  12099. }
  12100. ok = false;
  12101. }
  12102. }
  12103. }
  12104. } afor(fileDesc, mexcept);
  12105. afor.islazy = fileDesc->queryProperties().getPropBool("@lazy");
  12106. if (0 == numParallelDeletes)
  12107. numParallelDeletes = fileDesc->numParts();
  12108. if (numParallelDeletes > MAX_PHYSICAL_DELETE_THREADS)
  12109. {
  12110. WARNLOG("Limiting parallel physical delete threads to %d", MAX_PHYSICAL_DELETE_THREADS);
  12111. numParallelDeletes = MAX_PHYSICAL_DELETE_THREADS;
  12112. }
  12113. afor.For(fileDesc->numParts(),numParallelDeletes,false,true);
  12114. return afor.ok;
  12115. }
  12116. #ifdef _USE_CPPUNIT
  12117. /*
  12118. * This method removes files only logically. removeEntry() used to do that, but the only
  12119. * external use for logical-files only is this test-suite, so I'd rather hack the test
  12120. * suite than expose the behaviour to more viewers.
  12121. */
  12122. extern da_decl void removeLogical(const char *fname, IUserDescriptor *user) {
  12123. if (queryDistributedFileDirectory().exists(fname, user)) {
  12124. Owned<IDistributedFile> file = queryDistributedFileDirectory().lookup(fname, user, true, false, false, nullptr, defaultPrivilegedUser);
  12125. CDistributedFile *f = QUERYINTERFACE(file.get(),CDistributedFile);
  12126. assert(f);
  12127. f->detachLogical();
  12128. }
  12129. }
  12130. #endif // _USE_CPPUNIT