dautils.cpp 94 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340
  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 "jstring.hpp"
  17. #include "jfile.hpp"
  18. #include "jmisc.hpp"
  19. #include "jsort.hpp"
  20. #include "jprop.hpp"
  21. #include "jregexp.hpp"
  22. #include "jset.hpp"
  23. #include "rmtfile.hpp"
  24. #include "mpbase.hpp"
  25. #include "dautils.hpp"
  26. #include "dadfs.hpp"
  27. #include "dasds.hpp"
  28. #include "daclient.hpp"
  29. #include <vector>
  30. #ifdef _DEBUG
  31. //#define TEST_DEADLOCK_RELEASE
  32. #endif
  33. #define EXTERNAL_SCOPE "file"
  34. #define FOREIGN_SCOPE "foreign"
  35. #define SDS_DFS_ROOT "Files" // followed by scope/name
  36. #define SDS_RELATIONSHIPS_ROOT "Files/Relationships"
  37. #define SDS_CONNECT_TIMEOUT (1000*60*60*2) // better than infinite
  38. #define MIN_REDIRECTION_LOAD_INTERVAL 1000
  39. extern da_decl const char *queryDfsXmlBranchName(DfsXmlBranchKind kind)
  40. {
  41. switch (kind) {
  42. case DXB_File: return "File";
  43. case DXB_SuperFile: return "SuperFile";
  44. case DXB_Collection: return "Collection";
  45. case DXB_Scope: return "Scope";
  46. case DXB_Internal: return "HpccInternal";
  47. }
  48. assertex(!"unknown DFS XML branch name");
  49. return "UNKNOWN";
  50. }
  51. static const char *toLower(const char *s,StringBuffer &str)
  52. {
  53. return str.clear().append(s).toLowerCase().str();
  54. }
  55. static const char *toLowerTrim(const char *s,StringBuffer &str)
  56. {
  57. return str.clear().append(s).trim().toLowerCase().str();
  58. }
  59. inline void skipSp(const char *&s)
  60. {
  61. while (isspace(*s))
  62. s++;
  63. }
  64. inline bool validFNameChar(char c)
  65. {
  66. static const char *invalids = "*\"/:<>?\\|";
  67. return (c>=32 && c<127 && !strchr(invalids, c));
  68. }
  69. /**
  70. * Multi-distributed logical file names. Used by SuperFiles to
  71. * account for the names expanded from wildcards or lists, ex.
  72. * DATASET('scope::{a, b, c}', ...)
  73. *
  74. * This helper class is used for temporary inline super-files
  75. * only.
  76. */
  77. class CMultiDLFN
  78. {
  79. std::vector<CDfsLogicalFileName> dlfns;
  80. bool expanded;
  81. public:
  82. CMultiDLFN(const char *_prefix,const StringArray &lfns)
  83. {
  84. unsigned c = lfns.ordinality();
  85. StringBuffer lfn(_prefix);
  86. size32_t len = lfn.length();
  87. expanded = true;
  88. for (unsigned i=0;i<c;i++) { //Populate CDfsLogicalFileName array with each logical filespec
  89. const char * s = lfns.item(i);
  90. skipSp(s);
  91. if (*s=='~')
  92. s++;//scope provided, create CDfsLogicalFileName as-is
  93. else {
  94. lfn.setLength(len);//scope not specified, append it before creating CDfsLogicalFileName
  95. lfn.append(s);
  96. s = lfn.str();
  97. }
  98. CDfsLogicalFileName lfn;
  99. lfn.setAllowWild(true);
  100. lfn.set(s);
  101. dlfns.push_back(lfn);
  102. if (expanded && (strchr(s,'*') || strchr(s,'?')))
  103. expanded = false;
  104. }
  105. }
  106. CMultiDLFN(CMultiDLFN &other)
  107. {
  108. expanded = other.expanded;
  109. ForEachItemIn(i,other)
  110. dlfns.push_back(other.item(i));
  111. }
  112. void expand(IUserDescriptor *_udesc)
  113. {
  114. if (expanded)
  115. return;
  116. StringArray lfnExpanded;
  117. StringBuffer tmp;
  118. for (unsigned idx=0; idx < dlfns.size(); idx++)
  119. {
  120. const char *suffix = dlfns.at(idx).get();
  121. if (strchr(suffix,'*') || strchr(suffix,'?'))
  122. {
  123. tmp.clear();
  124. if (*suffix=='~')
  125. tmp.append((strcmp(suffix+1,"*")==0) ? "?*" : suffix+1);
  126. else
  127. tmp.append(suffix);
  128. tmp.clip().toLowerCase();
  129. Owned<IDFAttributesIterator> iter=queryDistributedFileDirectory().getDFAttributesIterator(tmp.str(),_udesc,false,true,NULL);
  130. ForEach(*iter)
  131. {
  132. IPropertyTree &attr = iter->query();
  133. const char *name = attr.queryProp("@name");
  134. if (!name||!*name)
  135. continue;
  136. tmp.clear().append('~').append(name); // need leading ~ otherwise will get two prefixes
  137. lfnExpanded.append(tmp.str());
  138. }
  139. }
  140. else
  141. lfnExpanded.append(suffix);
  142. }
  143. dlfns.clear();
  144. ForEachItemIn(i3,lfnExpanded)
  145. {
  146. CDfsLogicalFileName item;
  147. item.set(lfnExpanded.item(i3));
  148. dlfns.push_back(item);
  149. }
  150. expanded = true;
  151. }
  152. static CMultiDLFN *create(const char *_mlfn)
  153. {
  154. StringBuffer mlfn(_mlfn);
  155. mlfn.trim();
  156. if (mlfn.length()<=2)
  157. return NULL;
  158. const char *s = mlfn.str();
  159. if (s[mlfn.length()-1]!='}')
  160. return NULL;
  161. mlfn.remove(mlfn.length()-1,1);
  162. s = mlfn.str(); // prob not needed, but...
  163. const char *start = strchr(s,'{');
  164. if (!start)
  165. return NULL;
  166. StringArray lfns;
  167. lfns.appendList(start+1, ",");
  168. bool anywilds = false;
  169. ForEachItemIn(i1,lfns) {
  170. const char *suffix = lfns.item(i1);
  171. if (strchr(suffix,'*')||strchr(suffix,'?')) {
  172. anywilds = true;
  173. break;
  174. }
  175. }
  176. mlfn.setLength(start-s); // Just keep the prefix (anything before leading {)
  177. CMultiDLFN *ret = new CMultiDLFN(mlfn.str(), lfns);
  178. if (ret->ordinality() || anywilds)
  179. return ret;
  180. delete ret;
  181. return NULL;
  182. }
  183. const CDfsLogicalFileName &item(unsigned idx)
  184. {
  185. assertex(idx < dlfns.size());
  186. return dlfns.at(idx);
  187. }
  188. inline unsigned ordinality() const { return dlfns.size(); }
  189. inline bool isExpanded() const { return expanded; }
  190. };
  191. CDfsLogicalFileName::CDfsLogicalFileName()
  192. {
  193. allowospath = false;
  194. allowWild = false;
  195. multi = NULL;
  196. clear();
  197. }
  198. CDfsLogicalFileName::~CDfsLogicalFileName()
  199. {
  200. delete multi;
  201. }
  202. void CDfsLogicalFileName::clear()
  203. {
  204. lfn.clear();
  205. external = false;
  206. localpos = 0;
  207. tailpos = 0;
  208. delete multi;
  209. multi = NULL;
  210. }
  211. bool CDfsLogicalFileName::isSet() const
  212. {
  213. const char *s = lfn.get();
  214. return s&&*s;
  215. }
  216. CDfsLogicalFileName & CDfsLogicalFileName::operator = (CDfsLogicalFileName const &from)
  217. {
  218. set(from);
  219. return *this;
  220. }
  221. void CDfsLogicalFileName::set(const CDfsLogicalFileName &other)
  222. {
  223. lfn.set(other.lfn);
  224. tailpos = other.tailpos;
  225. localpos = other.localpos;
  226. cluster.set(other.cluster);
  227. external = other.external;
  228. delete multi;
  229. if (other.multi)
  230. multi = new CMultiDLFN(*other.multi);
  231. else
  232. multi = NULL;
  233. }
  234. bool CDfsLogicalFileName::isForeign() const
  235. {
  236. if (localpos!=0)
  237. return true;
  238. if (multi)
  239. {
  240. if (!multi->isExpanded())
  241. throw MakeStringException(-1, "Must call CDfsLogicalFileName::expand() before calling CDfsLogicalFileName::isForeign(), wildcards are specified");
  242. ForEachItemIn(i1,*multi)
  243. if (multi->item(i1).isForeign()) // if any are say all are
  244. return true;
  245. }
  246. return false;
  247. }
  248. bool CDfsLogicalFileName::isExpanded() const
  249. {
  250. if (multi)
  251. return multi->isExpanded();
  252. return true;
  253. }
  254. void CDfsLogicalFileName::expand(IUserDescriptor *user)
  255. {
  256. if (multi && !multi->isExpanded())
  257. {
  258. try
  259. {
  260. multi->expand(user);//expand wildcard specifications
  261. StringBuffer full("{");
  262. ForEachItemIn(i1,*multi)
  263. {
  264. if (i1)
  265. full.append(',');
  266. const CDfsLogicalFileName &item = multi->item(i1);
  267. StringAttr norm;
  268. normalizeName(item.get(), norm, false);
  269. full.append(norm);
  270. if (item.isExternal())
  271. external = external || item.isExternal();
  272. }
  273. full.append('}');
  274. lfn.set(full);
  275. }
  276. catch (IException *e)
  277. {
  278. StringBuffer err;
  279. e->errorMessage(err);
  280. ERRLOG("CDfsLogicalFileName::expand %s",err.str());
  281. throw;
  282. }
  283. }
  284. }
  285. inline void normalizeScope(const char *name, const char *scope, unsigned len, StringBuffer &res, bool strict)
  286. {
  287. while (len && isspace(scope[len-1]))
  288. {
  289. if (strict)
  290. throw MakeStringException(-1, "Scope contains trailing spaces in file name '%s'", name);
  291. len--;
  292. }
  293. while (len && isspace(scope[0]))
  294. {
  295. if (strict)
  296. throw MakeStringException(-1, "Scope contains leading spaces in file name '%s'", name);
  297. len--;
  298. scope++;
  299. }
  300. if (!len)
  301. throw MakeStringException(-1, "Scope is blank in file name '%s'", name);
  302. while (len--)
  303. {
  304. res.append(*scope++);
  305. }
  306. }
  307. void normalizeNodeName(const char *node, unsigned len, SocketEndpoint &ep, bool strict)
  308. {
  309. if (!strict)
  310. {
  311. while (isspace(*node))
  312. {
  313. node++;
  314. len--;
  315. }
  316. }
  317. StringBuffer nodename;
  318. nodename.append(len, node);
  319. if (!strict)
  320. nodename.clip();
  321. ep.set(nodename.str());
  322. }
  323. void CDfsLogicalFileName::normalizeName(const char *name, StringAttr &res, bool strict)
  324. {
  325. // NB: If !strict(default) allows spaces to exist either side of scopes (no idea why would want to permit that, but preserving for bwrd compat.)
  326. StringBuffer str;
  327. StringBuffer nametmp;
  328. const char *ct = nullptr;
  329. bool wilddetected = false;
  330. if ('~' == *name) // allowed 1 leading ~
  331. {
  332. name++;
  333. if (!strict)
  334. skipSp(name);
  335. }
  336. const char *s = name;
  337. char c = *s;
  338. while (c)
  339. {
  340. switch (c)
  341. {
  342. case '@': ct = s; break;
  343. case ':': ct = nullptr; break;
  344. case '?':
  345. case '*': wilddetected = true; break;
  346. case '~':
  347. throw MakeStringException(-1, "Unexpected character '%c' in logical name '%s' detected", *s, name);
  348. case '>':
  349. {
  350. c = '\0'; // will cause break out of loop
  351. continue; // can't validate query syntax
  352. }
  353. default:
  354. {
  355. if (!validFNameChar(c))
  356. throw MakeStringException(-1, "Unexpected character '%c' in logical name '%s' detected", *s, name);
  357. }
  358. }
  359. c = *++s;
  360. }
  361. if (!allowWild && wilddetected)
  362. throw MakeStringException(-1, "Wildcards not allowed in filename (%s)", name);
  363. if (ct&&(ct-name>=1)) // trailing @
  364. {
  365. if ((ct[1]=='@')||(ct[1]=='^')) // escape
  366. {
  367. nametmp.append(ct-name,name).append(ct+1);
  368. name = nametmp.str();
  369. }
  370. else
  371. {
  372. nametmp.append(ct+1);
  373. nametmp.trim().toLowerCase();
  374. if (nametmp.length())
  375. cluster.set(nametmp);
  376. nametmp.clear().append(ct-name,name); // treat trailing @ as no cluster
  377. name = nametmp.str();
  378. }
  379. }
  380. if (!*name)
  381. name = ".::_blank_";
  382. s=strstr(name,"::");
  383. if (s)
  384. {
  385. if (s==name) // JCS: meaning name leads with "::", would have thought should be treated as invalid
  386. str.append('.');
  387. else
  388. {
  389. normalizeScope(name, name, s-name, str, strict);
  390. bool isForeign = 0 == stricmp(str.str(),FOREIGN_SCOPE);
  391. if (isForeign) // normalize node
  392. {
  393. const char *s1 = s+2;
  394. const char *ns1 = strstr(s1,"::");
  395. if (ns1)
  396. {
  397. SocketEndpoint ep;
  398. normalizeNodeName(s1, ns1-s1, ep, strict);
  399. if (!ep.isNull())
  400. {
  401. ep.getUrlStr(str.append("::"));
  402. s = ns1;
  403. localpos = str.length()+2;
  404. }
  405. }
  406. }
  407. }
  408. loop
  409. {
  410. s+=2;
  411. const char *ns = strstr(s,"::");
  412. if (!ns)
  413. break;
  414. str.append("::");
  415. normalizeScope(name, s, ns-s, str, strict);
  416. s = ns;
  417. }
  418. }
  419. else
  420. {
  421. s = name;
  422. str.append(".");
  423. }
  424. str.append("::");
  425. tailpos = str.length();
  426. if (strstr(s,"::")!=nullptr)
  427. ERRLOG("Tail contains '::'!");
  428. normalizeScope(name, s, strlen(name)-(s-name), str, strict);
  429. str.toLowerCase();
  430. res.set(str);
  431. }
  432. bool CDfsLogicalFileName::normalizeExternal(const char * name, StringAttr &res, bool strict)
  433. {
  434. // TODO Should check the name is a valid OS filename
  435. if ('~' == *name) // allowed 1 leading ~
  436. {
  437. name++;
  438. if (!strict)
  439. skipSp(name);
  440. }
  441. bool retVal = memicmp(name,EXTERNAL_SCOPE "::",sizeof(EXTERNAL_SCOPE "::")-1)==0;
  442. if (retVal)
  443. {
  444. lfn.clear();
  445. StringBuffer str;
  446. const char *s=strstr(name,"::");
  447. normalizeScope(name, name, s-name, str, strict);
  448. const char *s1 = s+2;
  449. const char *ns1 = strstr(s1,"::");
  450. if (!ns1)
  451. retVal = false;
  452. else
  453. {
  454. SocketEndpoint ep;
  455. normalizeNodeName(s1, ns1-s1, ep, strict);
  456. if (ep.isNull())
  457. retVal = false;
  458. else
  459. {
  460. ep.getUrlStr(str.append("::"));
  461. s = ns1;
  462. if (s[2] == '>')
  463. {
  464. str.append("::");
  465. tailpos = str.length();
  466. str.append(s+2);
  467. }
  468. else
  469. {
  470. str.append(s);
  471. str.toLowerCase();
  472. }
  473. res.set(str);
  474. }
  475. }
  476. }
  477. return retVal;
  478. }
  479. void CDfsLogicalFileName::set(const char *name, bool removeForeign)
  480. {
  481. clear();
  482. if (!name)
  483. return;
  484. skipSp(name);
  485. if (allowospath&&(isAbsolutePath(name)||(stdIoHandle(name)>=0)||(strstr(name,"::")==nullptr)))
  486. {
  487. RemoteFilename rfn;
  488. rfn.setRemotePath(name);
  489. setExternal(rfn);
  490. return;
  491. }
  492. try
  493. {
  494. multi = CMultiDLFN::create(name);
  495. }
  496. catch (IException *e)
  497. {
  498. StringBuffer err;
  499. e->errorMessage(err);
  500. WARNLOG("CDfsLogicalFileName::set %s",err.str());
  501. e->Release();
  502. }
  503. if (multi)
  504. {
  505. StringBuffer full;
  506. full.append('{');
  507. ForEachItemIn(i1,*multi)
  508. {
  509. if (i1)
  510. full.append(',');
  511. const CDfsLogicalFileName &item = multi->item(i1);
  512. full.append(item.get());
  513. if (item.isExternal())
  514. external = external || item.isExternal();
  515. }
  516. full.append('}');
  517. lfn.set(full);
  518. return;
  519. }
  520. if (normalizeExternal(name, lfn, false))
  521. external = true;
  522. else
  523. {
  524. normalizeName(name, lfn, false);
  525. if (removeForeign)
  526. {
  527. StringAttr _lfn = get(true);
  528. lfn.clear();
  529. lfn.set(_lfn);
  530. }
  531. }
  532. }
  533. bool CDfsLogicalFileName::setValidate(const char *lfn, bool removeForeign)
  534. {
  535. try
  536. {
  537. set(lfn, removeForeign);
  538. return true;
  539. }
  540. catch (IException *e)
  541. {
  542. e->Release();
  543. return false;
  544. }
  545. }
  546. void CDfsLogicalFileName::set(const char *scopes,const char *tail)
  547. {
  548. // probably not good with multi
  549. if (!scopes||!tail||(*tail=='~')) {
  550. set(tail);
  551. return;
  552. }
  553. skipSp(scopes);
  554. skipSp(tail);
  555. if (!*scopes||!*tail) {
  556. set(tail);
  557. return;
  558. }
  559. StringBuffer s(scopes);
  560. if ((s.length()<2)||(s.charAt(s.length()-1)!=':')||(s.charAt(s.length()-2)!=':'))
  561. s.append("::");
  562. s.append(tail);
  563. set(s.str());
  564. if (multi)
  565. WARNLOG("set with scopes called on multi-lfn %s",get());
  566. }
  567. void CDfsLogicalFileName::setForeign(const SocketEndpoint &daliep,bool checklocal)
  568. {
  569. if (daliep.isNull()) {
  570. clearForeign();
  571. return;
  572. }
  573. if (isExternal()||(checklocal&&isForeign()))
  574. return;
  575. StringBuffer str(FOREIGN_SCOPE "::");
  576. daliep.getUrlStr(str);
  577. str.append("::");
  578. str.append(get(true));
  579. set(str);
  580. }
  581. static bool begins(const char *&ln,const char *pat)
  582. {
  583. size32_t sz = strlen(pat);
  584. if (memicmp(ln,pat,sz)==0) {
  585. ln += sz;
  586. return true;
  587. }
  588. return false;
  589. }
  590. bool CDfsLogicalFileName::setFromXPath(const char *xpath)
  591. {
  592. StringBuffer lName;
  593. const char *s = xpath;
  594. if (!begins(s, "/Files"))
  595. return false;
  596. while (*s && (begins(s, "/Scope[@name=\"") || begins(s, "/File[@name=\"") || begins(s, "/SuperFile[@name=\"")))
  597. {
  598. if (lName.length())
  599. lName.append("::");
  600. while (*s && (*s != '"'))
  601. lName.append(*(s++));
  602. if (*s == '"')
  603. s++;
  604. if (*s == ']')
  605. s++;
  606. }
  607. if (0 == lName.length())
  608. return false;
  609. return setValidate(lName);
  610. }
  611. void CDfsLogicalFileName::clearForeign()
  612. {
  613. StringBuffer str;
  614. if (isForeign()) {
  615. str.append(get(true));
  616. set(str);
  617. }
  618. }
  619. unsigned CDfsLogicalFileName::multiOrdinality() const
  620. {
  621. if (multi)
  622. return multi->ordinality();
  623. return 0;
  624. }
  625. bool CDfsLogicalFileName::isQuery() const
  626. {
  627. const char *s = lfn.get();
  628. if (!s||!*s)
  629. return false;
  630. skipSp(s);
  631. if (*s=='~') { // allowed 1 leading ~
  632. s++;
  633. skipSp(s);
  634. }
  635. const char *es = skipScope(s,EXTERNAL_SCOPE);
  636. if (es) {
  637. while (*es&&((*es!=':')||(es[1]!=':'))) // remove IP
  638. es++;
  639. if (*es&&(es[2]=='>'))
  640. return true;
  641. }
  642. return false;
  643. }
  644. const CDfsLogicalFileName &CDfsLogicalFileName::multiItem(unsigned idx) const
  645. {
  646. assertex(multi);
  647. return multi->item(idx);
  648. }
  649. IPropertyTree *CDfsLogicalFileName::createSuperTree() const
  650. {
  651. if (!multi)
  652. return NULL;
  653. IPropertyTree *ret = createPTree("SuperFile");
  654. unsigned numsub = 0;
  655. ForEachItemIn(i1,*multi) {
  656. const CDfsLogicalFileName &sub = multi->item(i1);
  657. IPropertyTree *st = createPTree();
  658. st->setProp("@name",sub.get());
  659. st->setPropInt("@num",++numsub);
  660. ret->addPropTree("SubFile",st);
  661. }
  662. ret->setProp("OrigName",get());
  663. ret->addPropInt("@numsubfiles",numsub);
  664. ret->setPropInt("@interleaved",2);
  665. ret->setProp("@name","__TEMP__");
  666. CDateTime dt;
  667. dt.setNow();
  668. StringBuffer str;
  669. ret->setProp("@modified",dt.getString(str).str());
  670. return ret;
  671. }
  672. void CDfsLogicalFileName::setExternal(const char *location,const char *path)
  673. {
  674. if (!path||!*path)
  675. return;
  676. if (isPathSepChar(path[0])&&(path[0]==path[1])) {
  677. RemoteFilename rfn;
  678. rfn.setRemotePath(path);
  679. setExternal(rfn); // overrides ip
  680. return;
  681. }
  682. StringBuffer str(EXTERNAL_SCOPE "::");
  683. str.append(location);
  684. if ((path[1]==':')&&(path[2]=='\\')) {
  685. str.append("::").append(path[0]).append('$');
  686. path+=2;
  687. }
  688. else if (!isPathSepChar(*path))
  689. str.append("::");
  690. StringBuffer urlencoded;
  691. if (*path == '$') {
  692. size32_t l = strlen(path);
  693. if (l>1) {
  694. str.append("$::");
  695. JBASE32_Encode(path+1, str);
  696. path = path+l;
  697. }
  698. }
  699. else {
  700. StringBuffer tmp;
  701. StringBuffer tmp2;
  702. decodeXML(path, tmp ); // allow user to use escape encoding
  703. if (isSpecialPath(path))
  704. str.append(path);
  705. else {
  706. encodeXML(tmp.str(), urlencoded, ENCODE_NEWLINES, tmp.length());
  707. path = urlencoded.str();
  708. }
  709. }
  710. if (!isSpecialPath(path)) {
  711. while (*path) {
  712. if (isPathSepChar(*path))
  713. str.append("::");
  714. else {
  715. if ((*path=='^')||isupper(*path))
  716. str.append('^');
  717. str.append((char)tolower(*path));
  718. }
  719. path++;
  720. }
  721. }
  722. set(str.str());
  723. }
  724. void CDfsLogicalFileName::setExternal(const SocketEndpoint &dafsip,const char *path)
  725. {
  726. StringBuffer str;
  727. dafsip.getUrlStr(str);
  728. setExternal(str.str(),path);
  729. }
  730. void CDfsLogicalFileName::setExternal(const RemoteFilename &rfn)
  731. {
  732. StringBuffer localpath;
  733. rfn.getLocalPath(localpath);
  734. setExternal(rfn.queryEndpoint(),localpath.str());
  735. }
  736. void CDfsLogicalFileName::setQuery(const char *location,const char *query)
  737. {
  738. clear();
  739. if (!location||!query)
  740. return;
  741. skipSp(location);
  742. if (!*location)
  743. return;
  744. skipSp(query);
  745. StringBuffer str(EXTERNAL_SCOPE "::");
  746. str.append(location).clip().append("::>");
  747. tailpos = str.length()-1; // don't include '>'
  748. str.append(query).clip();
  749. lfn.set(str.str());
  750. external = true;
  751. }
  752. void CDfsLogicalFileName::setQuery(const SocketEndpoint &rfsep,const char *query)
  753. {
  754. StringBuffer str;
  755. rfsep.getUrlStr(str);
  756. setQuery(str.str(),query);
  757. }
  758. void CDfsLogicalFileName::setCluster(const char *cname)
  759. {
  760. if (multi)
  761. WARNLOG("setCluster called on multi-lfn %s",get());
  762. skipSp(cname);
  763. StringBuffer name(cname);
  764. cluster.set(name.clip().toLowerCase().str());
  765. }
  766. const char *CDfsLogicalFileName::get(bool removeforeign) const
  767. {
  768. const char *ret = lfn.get();
  769. if (!ret)
  770. return "";
  771. if (removeforeign) {
  772. if (multi)
  773. WARNLOG("CDfsLogicalFileName::get with removeforeign set called on multi-lfn %s",get());
  774. ret += localpos;
  775. }
  776. return ret;
  777. }
  778. StringBuffer &CDfsLogicalFileName::get(StringBuffer &str, bool removeforeign, bool withCluster) const
  779. {
  780. str.append(get(removeforeign));
  781. if (withCluster && cluster.length())
  782. str.append("@").append(cluster);
  783. return str;
  784. }
  785. const char *CDfsLogicalFileName::queryTail() const
  786. {
  787. if (multi)
  788. WARNLOG("CDfsLogicalFileName::queryTail called on multi-lfn %s",get());
  789. const char *tail = get()+tailpos;
  790. if (strstr(tail,"::")!=NULL) {
  791. ERRLOG("Tail contains '::'!");
  792. }
  793. return get()+tailpos;
  794. }
  795. StringBuffer &CDfsLogicalFileName::getTail(StringBuffer &buf) const
  796. {
  797. return buf.append(queryTail());
  798. }
  799. StringBuffer &CDfsLogicalFileName::getScopes(StringBuffer &buf,bool removeforeign) const
  800. {
  801. if (multi)
  802. WARNLOG("CDfsLogicalFileName::getScopes called on multi-lfn %s",get());
  803. // gets leading scopes without trailing ::
  804. const char *s =lfn.get();
  805. if (!s||(tailpos<=2))
  806. return buf;
  807. size32_t sz = tailpos-2;
  808. if (removeforeign) {
  809. s += localpos;
  810. if (sz<=localpos)
  811. return buf;
  812. sz -= localpos;
  813. }
  814. return buf.append(sz,s);
  815. }
  816. unsigned CDfsLogicalFileName::numScopes(bool removeforeign) const
  817. {
  818. const char *s =lfn.get();
  819. if (!s)
  820. return 0;
  821. if (multi)
  822. WARNLOG("CDfsLogicalFileName::getScopes called on multi-lfn %s",get());
  823. if (removeforeign)
  824. s += localpos;
  825. // num scopes = number of "::"s
  826. unsigned ret = 0;
  827. loop {
  828. s = strstr(s,"::");
  829. if (!s)
  830. return ret;
  831. ret++;
  832. s += 2;
  833. }
  834. }
  835. StringBuffer &CDfsLogicalFileName::getScope(StringBuffer &buf,unsigned idx,bool removeforeign) const
  836. {
  837. const char *s =lfn.get();
  838. if (!s)
  839. return buf;
  840. if (multi)
  841. WARNLOG("CDfsLogicalFileName::getScopes called on multi-lfn %s",get());
  842. if (removeforeign)
  843. s += localpos;
  844. // num scopes = number of "::"s
  845. loop {
  846. const char *p = s;
  847. s = strstr(s,"::");
  848. if (idx--==0) {
  849. if (s)
  850. return buf.append(s-p,p);
  851. return buf.append(p);
  852. }
  853. if (!s)
  854. return buf;
  855. s += 2;
  856. }
  857. }
  858. StringBuffer &CDfsLogicalFileName::makeScopeQuery(StringBuffer &query, bool absolute) const
  859. {
  860. if (multi)
  861. WARNLOG("CDfsLogicalFileName::makeFullnameQuery called on multi-lfn %s",get());
  862. if (absolute)
  863. query.append(SDS_DFS_ROOT "/");
  864. // returns full xpath for containing scope
  865. const char *s=get(true); // skip foreign
  866. bool first=true;
  867. loop {
  868. const char *e=strstr(s,"::");
  869. if (!e)
  870. break;
  871. if (first)
  872. first = false;
  873. else
  874. query.append('/');
  875. query.append("Scope[@name=\"").append(e-s,s).append("\"]");
  876. s = e+2;
  877. }
  878. return query;
  879. }
  880. StringBuffer &CDfsLogicalFileName::makeFullnameQuery(StringBuffer &query, DfsXmlBranchKind bkind, bool absolute) const
  881. {
  882. makeScopeQuery(query,absolute);
  883. query.append('/').append(queryDfsXmlBranchName(bkind));
  884. return query.append("[@name=\"").append(queryTail()).append("\"]");
  885. }
  886. StringBuffer &CDfsLogicalFileName::makeXPathLName(StringBuffer &lfnNodeName) const
  887. {
  888. const char *s=get(true); // skip foreign
  889. // Ensure only chars that are accepted by jptree in an xpath element are used
  890. loop
  891. {
  892. const char *e=strstr(s,"::");
  893. if ((e && 0 != strncmp(".", s, e-s)) || (!e && !streq(".", s))) // skip '.' scopes
  894. {
  895. lfnNodeName.append('_');
  896. while (s != e)
  897. {
  898. char c = *s;
  899. switch (c)
  900. {
  901. case '\0':
  902. return lfnNodeName; // done
  903. case '^':
  904. ++s;
  905. if ('\0' == *s)
  906. return lfnNodeName; // probably an error really to end in '^'
  907. c = toupper(*s);
  908. // fall through
  909. default:
  910. if ('_' == c)
  911. lfnNodeName.append("__"); // to avoid clash with use of '_' here was escape char.
  912. else if (isValidXPathChr(c))
  913. lfnNodeName.append(c);
  914. else
  915. lfnNodeName.append('_').append((unsigned) (unsigned char) c);
  916. break;
  917. }
  918. ++s;
  919. }
  920. }
  921. if (!e)
  922. break;
  923. s = e+2;
  924. }
  925. return lfnNodeName;
  926. }
  927. bool CDfsLogicalFileName::getEp(SocketEndpoint &ep) const
  928. {
  929. SocketEndpoint nullep;
  930. ep = nullep;
  931. const char *ns;
  932. if (isExternal())
  933. ns = skipScope(lfn,EXTERNAL_SCOPE);
  934. else if (isForeign())
  935. ns = skipScope(lfn,FOREIGN_SCOPE);
  936. else
  937. ns = NULL;
  938. if (ns) {
  939. if (multi)
  940. WARNLOG("CDfsLogicalFileName::getEp called on multi-lfn %s",get());
  941. const char *e = strstr(ns,"::");
  942. if (e) {
  943. StringBuffer node(e-ns,ns);
  944. ep.set(node.str());
  945. return !ep.isNull();
  946. }
  947. }
  948. return false;
  949. }
  950. StringBuffer &CDfsLogicalFileName::getGroupName(StringBuffer &grp) const
  951. {
  952. if (isExternal()) {
  953. const char *s = skipScope(lfn,EXTERNAL_SCOPE);
  954. if (s) {
  955. const char *e = strstr(s,"::");
  956. if (e)
  957. grp.append(e-s,s);
  958. }
  959. }
  960. return grp;
  961. }
  962. bool CDfsLogicalFileName::getExternalPath(StringBuffer &dir, StringBuffer &tail, bool iswin, IException **e) const
  963. {
  964. if (e)
  965. *e = NULL;
  966. if (!isExternal()) {
  967. if (e)
  968. *e = MakeStringException(-1,"File not external (%s)",get());
  969. return false;
  970. }
  971. if (multi)
  972. WARNLOG("CDfsLogicalFileName::makeFullnameQuery called on multi-lfn %s",get());
  973. const char *s = skipScope(lfn,EXTERNAL_SCOPE);
  974. if (s)
  975. s = strstr(s,"::");
  976. if (!s) {
  977. if (e)
  978. *e = MakeStringException(-1,"Invalid format for external file (%s)",get());
  979. return false;
  980. }
  981. if (s[2]=='>') {
  982. dir.append('/');
  983. tail.append(s+2);
  984. return true;
  985. }
  986. if (iswin&&(s[3]=='$'))
  987. s += 2; // no leading '\'
  988. const char *s1=s;
  989. const char *t1=NULL;
  990. loop {
  991. s1 = strstr(s1,"::");
  992. if (!s1)
  993. break;
  994. t1 = s1;
  995. s1 = s1+2;
  996. }
  997. if (!t1||!*t1) {
  998. if (e)
  999. *e = MakeStringException(-1,"No directory specified in external file name (%s)",get());
  1000. return false;
  1001. }
  1002. bool start=true;
  1003. size32_t odl = dir.length();
  1004. while (s!=t1) {
  1005. char c=*(s++);
  1006. if (isPathSepChar(c)) {
  1007. if (e)
  1008. *e = MakeStringException(-1,"Path cannot contain separators, use '::' to separate directories: (%s)",get());
  1009. return false;
  1010. }
  1011. if ((c==':')&&(s!=t1)&&(*s==':')) {
  1012. dir.append(iswin?'\\':'/');
  1013. s++;
  1014. }
  1015. else if (c==':') {
  1016. if (e)
  1017. *e = MakeStringException(-1,"Path cannot contain single ':', use 'c$' to indicate 'c:' (%s)",get());
  1018. return false;
  1019. }
  1020. else if (iswin&&start&&(s!=t1)&&(*s=='$')) {
  1021. dir.append(c).append(':');
  1022. s++;
  1023. }
  1024. else {
  1025. if ((c=='^')&&(s!=t1)) {
  1026. c = toupper(*s);
  1027. s++;
  1028. }
  1029. dir.append(c);
  1030. }
  1031. start = false;
  1032. }
  1033. t1+=2; // skip ::
  1034. if ((dir.length()!=odl)&&(!isPathSepChar(dir.charAt(dir.length()-1))))
  1035. dir.append(iswin?'\\':'/');
  1036. while (*t1) {
  1037. char c = *(t1++);
  1038. if ((c=='^')&&*t1) {
  1039. c = toupper(*t1);
  1040. t1++;
  1041. }
  1042. tail.append(c);
  1043. }
  1044. return true;
  1045. }
  1046. bool CDfsLogicalFileName::getExternalFilename(RemoteFilename &rfn) const
  1047. {
  1048. StringBuffer fn;
  1049. if (!getExternalPath(fn,fn))
  1050. return false;
  1051. SocketEndpoint ep;
  1052. getEp(ep);
  1053. rfn.setPath(ep,fn.str());
  1054. return !rfn.isNull();
  1055. }
  1056. bool CDfsLogicalFileName::setFromMask(const char *fname,const char *rootdir)
  1057. {
  1058. if (!fname||!*fname)
  1059. return false;
  1060. // first remove base dir from fname if present
  1061. DFD_OS os = SepCharBaseOs(getPathSepChar(fname));
  1062. const char *dir = (rootdir&&*rootdir)?rootdir:queryBaseDirectory(grp_unknown, 0, os);
  1063. // ignore drive if present
  1064. if (os==DFD_OSwindows) {
  1065. if (dir[1]==':')
  1066. dir += 2;
  1067. if (fname[1]==':')
  1068. fname += 2;
  1069. }
  1070. else {
  1071. if (dir[2]=='$')
  1072. dir += 3;
  1073. if (fname[2]=='$')
  1074. fname += 3;
  1075. }
  1076. if (isPathSepChar(fname[0])) {
  1077. while (*dir&&(*dir==*fname)) {
  1078. dir++;
  1079. fname++;
  1080. }
  1081. if (*dir||!isPathSepChar(*fname))
  1082. return false; // didn't match base
  1083. fname++;
  1084. }
  1085. StringBuffer logicalName;
  1086. loop {
  1087. if (*fname==0) // we didn't find tail
  1088. return false;
  1089. if (isPathSepChar(*fname))
  1090. logicalName.append("::");
  1091. else if (*fname=='.') {
  1092. if (findPathSepChar(fname+1)==NULL) { // check for . mid name
  1093. if (strchr(fname+1,'.')==NULL) { // check for multiple extension
  1094. fname++;
  1095. if (*fname=='_') {
  1096. loop {
  1097. fname++;
  1098. if (!*fname)
  1099. return false;
  1100. if (memicmp(fname,"_of_",4)==0) {
  1101. if (!fname[4])
  1102. return false;
  1103. return setValidate(logicalName.str());
  1104. }
  1105. }
  1106. }
  1107. }
  1108. }
  1109. logicalName.append(*fname);
  1110. }
  1111. else
  1112. logicalName.append((char)tolower(*fname));
  1113. fname++;
  1114. }
  1115. return false;
  1116. }
  1117. const char * skipScope(const char *lname,const char *scope) // returns NULL if scope doesn't match
  1118. {
  1119. // scope assumed normalized
  1120. if (!scope||!*scope)
  1121. return lname;
  1122. if (!lname)
  1123. return NULL;
  1124. skipSp(lname);
  1125. if (!*lname)
  1126. return NULL;
  1127. while (*scope) {
  1128. if (toupper(*scope)!=toupper(*lname))
  1129. return NULL;
  1130. scope++;
  1131. lname++;
  1132. }
  1133. skipSp(lname);
  1134. if (*(lname++)!=':')
  1135. return NULL;
  1136. if (*(lname++)!=':')
  1137. return NULL;
  1138. skipSp(lname);
  1139. return lname;
  1140. }
  1141. const char * querySdsFilesRoot()
  1142. {
  1143. return SDS_DFS_ROOT;
  1144. }
  1145. const char * querySdsRelationshipsRoot()
  1146. {
  1147. return SDS_RELATIONSHIPS_ROOT ;
  1148. }
  1149. #define PAF_HAS_SIZE (0x01)
  1150. #define PAF_HAS_DATE (0x02)
  1151. #define PAF_HAS_CRC (0x04)
  1152. #define PAF_HAS_VAL (0x08)
  1153. #define PAF_HAS_SUB (0x10)
  1154. #define PAF_HAS_FILECRC (0x20)
  1155. MemoryBuffer &serializePartAttr(MemoryBuffer &mb,IPropertyTree *tree)
  1156. {
  1157. byte flags = 0;
  1158. offset_t size = (offset_t)tree->getPropInt64("@size",-1);
  1159. if (size!=(offset_t)-1)
  1160. flags |= PAF_HAS_SIZE;
  1161. StringBuffer dts;
  1162. CDateTime dt;
  1163. if (tree->getProp("@modified",dts)&&dts.length()) {
  1164. dt.setString(dts.str());
  1165. if (!dt.isNull())
  1166. flags |= PAF_HAS_DATE;
  1167. }
  1168. if (tree->hasProp("@fileCrc"))
  1169. flags |= PAF_HAS_FILECRC;
  1170. else if (tree->hasProp("@crc"))
  1171. flags |= PAF_HAS_CRC;
  1172. StringBuffer str;
  1173. if (tree->getProp(NULL,str))
  1174. flags |= PAF_HAS_VAL;
  1175. if (tree->hasChildren())
  1176. flags |= PAF_HAS_SUB;
  1177. mb.append(flags);
  1178. if (flags&PAF_HAS_SIZE)
  1179. mb.append(size);
  1180. if (flags&PAF_HAS_DATE)
  1181. dt.serialize(mb);
  1182. if (flags&PAF_HAS_FILECRC) {
  1183. int crc = tree->getPropInt("@fileCrc");
  1184. mb.append(crc);
  1185. }
  1186. else if (flags&PAF_HAS_CRC) {
  1187. int crc = tree->getPropInt("@crc");
  1188. mb.append(crc);
  1189. }
  1190. if (flags&PAF_HAS_VAL)
  1191. mb.append(str.str());
  1192. if (flags&PAF_HAS_SUB) {
  1193. Owned<IPropertyTreeIterator> ci = tree->getElements("*");
  1194. ForEach(*ci) {
  1195. IPropertyTree &pt = ci->query();
  1196. mb.append(pt.queryName());
  1197. pt.serialize(mb);
  1198. }
  1199. mb.append(""); // chiild terminator. i.e. blank name.
  1200. }
  1201. Owned<IAttributeIterator> ai = tree->getAttributes();
  1202. ForEach(*ai) {
  1203. const char *name = ai->queryName();
  1204. if (!name)
  1205. continue;
  1206. if (*name=='@')
  1207. name++;
  1208. if (strcmp(name,"size")==0)
  1209. continue;
  1210. if (strcmp(name,"modified")==0)
  1211. continue;
  1212. if (strcmp(name,"crc")==0)
  1213. continue;
  1214. if (strcmp(name,"fileCrc")==0)
  1215. continue;
  1216. if (strcmp(name,"num")==0)
  1217. continue;
  1218. mb.append(name);
  1219. mb.append(ai->queryValue());
  1220. }
  1221. return mb.append(""); // attribute terminator. i.e. blank attr name.
  1222. }
  1223. IPropertyTree *deserializePartAttr(MemoryBuffer &mb)
  1224. {
  1225. IPropertyTree *pt = createPTree("Part");
  1226. byte flags;
  1227. mb.read(flags);
  1228. if (flags&PAF_HAS_SIZE) {
  1229. offset_t size;
  1230. mb.read(size);
  1231. pt->setPropInt64("@size",size);
  1232. }
  1233. if (flags&PAF_HAS_DATE) {
  1234. CDateTime dt;
  1235. dt.deserialize(mb);
  1236. StringBuffer dts;
  1237. pt->setProp("@modified",dt.getString(dts).str());
  1238. }
  1239. if (flags&PAF_HAS_FILECRC) {
  1240. int crc;
  1241. mb.read(crc);
  1242. pt->setPropInt("@fileCrc",crc);
  1243. }
  1244. else if (flags&PAF_HAS_CRC) {
  1245. int crc;
  1246. mb.read(crc);
  1247. pt->setPropInt("@crc",crc);
  1248. }
  1249. if (flags&PAF_HAS_VAL) {
  1250. StringAttr val;
  1251. mb.read(val);
  1252. pt->setProp(NULL, val);
  1253. }
  1254. if (flags&PAF_HAS_SUB) {
  1255. loop {
  1256. StringAttr name;
  1257. mb.read(name);
  1258. if (name.length()==0)
  1259. break;
  1260. pt->addPropTree(name.get(),createPTree(mb));
  1261. }
  1262. }
  1263. StringAttr _aname;
  1264. StringAttr avalue;
  1265. StringBuffer aname("@");
  1266. loop {
  1267. mb.read(_aname);
  1268. if (!_aname.length())
  1269. break;
  1270. mb.read(avalue);
  1271. if (_aname[0]=='@')
  1272. pt->setProp(_aname, avalue);
  1273. else {
  1274. aname.setLength(1);
  1275. aname.append(_aname);
  1276. pt->setProp(aname.str(), avalue);
  1277. }
  1278. }
  1279. return pt;
  1280. }
  1281. IPropertyTreeIterator *deserializePartAttrIterator(MemoryBuffer &mb) // takes ownership of mb
  1282. {
  1283. class cPartAttrIterator: implements IPropertyTreeIterator, public CInterface
  1284. {
  1285. Owned<IPropertyTree> cur;
  1286. unsigned pn;
  1287. public:
  1288. IMPLEMENT_IINTERFACE;
  1289. MemoryBuffer mb;
  1290. bool first()
  1291. {
  1292. mb.reset();
  1293. pn = 0;
  1294. return next();
  1295. }
  1296. bool next()
  1297. {
  1298. cur.clear();
  1299. pn++;
  1300. if (mb.getPos()>=mb.length())
  1301. return false;
  1302. cur.setown(deserializePartAttr(mb));
  1303. cur->setPropInt("@num",pn);
  1304. return true;
  1305. }
  1306. bool isValid()
  1307. {
  1308. return cur.get()!=NULL;
  1309. }
  1310. IPropertyTree & query()
  1311. {
  1312. return *cur;
  1313. }
  1314. } *pai = new cPartAttrIterator;
  1315. mb.swapWith(pai->mb);
  1316. return pai;
  1317. }
  1318. unsigned getFileGroups(const char *grplist,StringArray &groups)
  1319. {
  1320. if (!grplist)
  1321. return 0;
  1322. const char *s = grplist;
  1323. StringBuffer gs;
  1324. unsigned sq = 0;
  1325. unsigned pa = 0;
  1326. loop {
  1327. char c = *(s++);
  1328. if (!c||((c==',')&&!sq&&!pa)) {
  1329. gs.clip();
  1330. if (gs.length()) {
  1331. if (strcmp(gs.str(),"SuperFiles")!=0) // special case kludge
  1332. gs.toLowerCase();
  1333. bool add = true;
  1334. ForEachItemIn(j,groups) {
  1335. if (strcmp(groups.item(j),gs.str())==0) {
  1336. add = false;
  1337. break;
  1338. }
  1339. }
  1340. if (add)
  1341. groups.append(gs.str());
  1342. gs.clear();
  1343. }
  1344. if (!c)
  1345. break;
  1346. }
  1347. else {
  1348. if (c=='[')
  1349. sq++;
  1350. else if (sq&&(c==']'))
  1351. sq--;
  1352. else if (c=='(') // future expansion
  1353. pa++;
  1354. else if (pa&&(c==')'))
  1355. pa--;
  1356. gs.append(c);
  1357. }
  1358. }
  1359. return groups.ordinality();
  1360. }
  1361. unsigned getFileGroups(IPropertyTree *pt,StringArray &groups, bool checkclusters)
  1362. {
  1363. unsigned ret = 0;
  1364. if (pt) {
  1365. ret = getFileGroups(pt->queryProp("@group"),groups);
  1366. if (ret==0) {
  1367. if (pt->getPropInt("@numparts")==1) {
  1368. const char *g = pt->queryProp("Part[@num=\"1\"][1]/@node");
  1369. if (g&&*g) {
  1370. groups.append(g);
  1371. return 1;
  1372. }
  1373. }
  1374. }
  1375. if (checkclusters) {
  1376. const char * on = pt->queryProp("OrigName");
  1377. if (!on)
  1378. on = "<UNKNOWN>";
  1379. unsigned nc = pt->getPropInt("@numclusters");
  1380. if (nc&&(nc!=groups.ordinality())) {
  1381. ERRLOG("%s groups/numclusters mismatch",on);
  1382. }
  1383. MemoryAttr ma;
  1384. unsigned ng = groups.ordinality();
  1385. bool *found = (bool *)ma.allocate(sizeof(bool)*ng);
  1386. memset(found,0,sizeof(bool)*ng);
  1387. Owned<IPropertyTreeIterator> iter = pt->getElements("Cluster");
  1388. bool anyfound = false;
  1389. ForEach(*iter) {
  1390. StringBuffer clabel;
  1391. const char *cname = iter->query().queryProp("@roxiePrefix");
  1392. if (!cname||!*cname)
  1393. cname = iter->query().queryProp("@name");
  1394. if (!cname&&*cname) {
  1395. continue;
  1396. }
  1397. anyfound = true;
  1398. bool ok = false;
  1399. ForEachItemIn(i,groups) {
  1400. if (strcmp(cname,groups.item(i))==0) {
  1401. if (found[i]) {
  1402. ERRLOG("'%s' has duplicate cluster",on);
  1403. }
  1404. else
  1405. found[i] = true;
  1406. ok = true;
  1407. break;
  1408. }
  1409. }
  1410. if (!ok) {
  1411. const char * gs = pt->queryProp("@group");
  1412. ERRLOG("'%s' has missing cluster(%s) in groups(%s)",on,cname,gs?gs:"NULL");
  1413. }
  1414. }
  1415. if (anyfound) {
  1416. for (unsigned i=0;i<ng;i++)
  1417. if (!found[i])
  1418. WARNLOG("'%s' has missing group(%s) in clusters",on,groups.item(i));
  1419. }
  1420. }
  1421. }
  1422. return ret;
  1423. }
  1424. bool shrinkFileTree(IPropertyTree *file)
  1425. {
  1426. if (!file)
  1427. return false;
  1428. if (file->hasProp("Parts"))
  1429. return true;
  1430. unsigned n = file->getPropInt("@numparts",0);
  1431. if (n<2)
  1432. return true;
  1433. const char *group=file->queryProp("@group");
  1434. if (!group||!*group||!file->hasProp("Part[2]")) // don't shrink single part files
  1435. return true;
  1436. MemoryBuffer mb;
  1437. IPropertyTree **parts = (IPropertyTree **)calloc(n,sizeof(IPropertyTree *));
  1438. unsigned i;
  1439. loop {
  1440. IPropertyTree *part = file->getBranch("Part[1]");
  1441. if (!part)
  1442. break;
  1443. file->removeTree(part);
  1444. i = part->getPropInt("@num",0)-1;
  1445. if ((i<n)&&(parts[i]==NULL))
  1446. parts[i] = part;
  1447. else
  1448. part->Release();
  1449. }
  1450. for (i=0;i<n;i++) {
  1451. if (!parts[i])
  1452. parts[i] = createPTree("Part");
  1453. serializePartAttr(mb,parts[i]);
  1454. }
  1455. file->setPropBin("Parts",mb.length(),mb.toByteArray());
  1456. for (i=0;i<n;i++)
  1457. parts[i]->Release();
  1458. free(parts);
  1459. return true;
  1460. }
  1461. void expandFileTree(IPropertyTree *file,bool expandnodes,const char *cluster)
  1462. {
  1463. if (!file)
  1464. return;
  1465. MemoryBuffer mb;
  1466. if (file->getPropBin("Parts",mb)) {
  1467. file->removeProp("Parts");
  1468. Owned<IPropertyTreeIterator> pi = deserializePartAttrIterator(mb);
  1469. ForEach(*pi)
  1470. file->addPropTree("Part",&pi->get());
  1471. }
  1472. if (!expandnodes)
  1473. return;
  1474. StringArray groups;
  1475. unsigned ng = getFileGroups(file,groups);
  1476. unsigned cn = 0;
  1477. if (cluster&&*cluster) {
  1478. unsigned i;
  1479. for (i=0;i<ng;i++)
  1480. if (strcmp(cluster,groups.item(i))==0) {
  1481. cn = i;
  1482. break;
  1483. }
  1484. if (i==ng)
  1485. ERRLOG("expandFileTree: Cluster %s not found in file",cluster);
  1486. }
  1487. if (cn<ng) {
  1488. const char *gname = groups.item(cn);
  1489. Owned<IClusterInfo> clusterinfo;
  1490. Owned<IPropertyTreeIterator> iter = file->getElements("Cluster");
  1491. ForEach(*iter) {
  1492. clusterinfo.setown(deserializeClusterInfo(&iter->query(),&queryNamedGroupStore(),0));
  1493. StringBuffer clabel;
  1494. const char *cname = clusterinfo->getClusterLabel(clabel).str();
  1495. if (cname&&(strcmp(cname,gname)==0))
  1496. break;
  1497. }
  1498. file->setProp("@group",gname);
  1499. Owned<IGroup> grp;
  1500. if (!clusterinfo) // try to resolve using cluster info (e.g. when remote)
  1501. grp.setown(queryNamedGroupStore().lookup(gname)); // resolve locally (legacy)
  1502. if (grp||clusterinfo) {
  1503. BoolArray done;
  1504. Owned<IPropertyTreeIterator> iter = file->getElements("Part");
  1505. unsigned max = file->getPropInt("@numparts");
  1506. StringBuffer ips;
  1507. ForEach(*iter) {
  1508. unsigned num = iter->query().getPropInt("@num");
  1509. if (num--) {
  1510. while (num>=done.ordinality())
  1511. done.append(false);
  1512. if (!done.item(num)) {
  1513. done.replace(true,num);
  1514. INode *node = clusterinfo?clusterinfo->queryNode(num,max,0):&grp->queryNode(num%grp->ordinality());
  1515. if (node) {
  1516. node->endpoint().getIpText(ips.clear());
  1517. iter->query().setProp("@node",ips.str());
  1518. }
  1519. }
  1520. }
  1521. }
  1522. }
  1523. if (clusterinfo&&!file->hasProp("@replicated")) // legacy
  1524. file->setPropBool("@replicated",clusterinfo->queryPartDiskMapping().isReplicated());
  1525. }
  1526. }
  1527. void filterParts(IPropertyTree *file,UnsignedArray &partslist)
  1528. {
  1529. if (!file)
  1530. return;
  1531. unsigned max = file->getPropInt("@numparts");
  1532. StringBuffer xpath;
  1533. for (unsigned i=0;i<max;i++) {
  1534. bool del = true;
  1535. ForEachItemIn(j,partslist) {
  1536. if (i==partslist.item(j)) {
  1537. del = false;
  1538. break;
  1539. }
  1540. }
  1541. if (del) {
  1542. xpath.clear().append("Part[@num=\"").append(i+1).append("\"]");
  1543. Owned<IPropertyTree> child = file->getPropTree(xpath.str());
  1544. file->removeTree(child);
  1545. }
  1546. }
  1547. }
  1548. //===================================================================
  1549. #define SDS_LOCK_TIMEOUT (1000*60*5)
  1550. #define SORT_REVERSE 1
  1551. #define SORT_NOCASE 2
  1552. #define SORT_NUMERIC 4
  1553. inline void filteredAdd(IArrayOf<IPropertyTree> &results,const char *namefilterlo,const char *namefilterhi,StringArray& unknownAttributes, IPropertyTree *item)
  1554. {
  1555. if (!item)
  1556. return;
  1557. if (namefilterlo||namefilterhi) {
  1558. const char *n = item->queryName();
  1559. if (!n)
  1560. return;
  1561. if (namefilterlo&&(strcmp(namefilterlo,n)>0))
  1562. return;
  1563. if (namefilterhi&&(strcmp(namefilterhi,n)<0))
  1564. return;
  1565. }
  1566. ForEachItemIn(i, unknownAttributes) {
  1567. const char *attribute = unknownAttributes.item(i);
  1568. if (!attribute || !*attribute)
  1569. continue;
  1570. const char *attrValue = item->queryProp(attribute);
  1571. if (attrValue && *attrValue)
  1572. return;
  1573. }
  1574. item->Link();
  1575. results.append(*item);
  1576. }
  1577. class cSort
  1578. {
  1579. static CriticalSection sortsect;
  1580. static cSort *sortthis;
  1581. mutable CLargeMemoryAllocator buf;
  1582. CIArrayOf<CIStringArray> sortKeys;
  1583. IArrayOf<IPropertyTree> sortvalues;
  1584. IntArray modifiers;
  1585. mutable char **vals;
  1586. unsigned nv;
  1587. unsigned nk;
  1588. public:
  1589. cSort()
  1590. : buf(0x100000*100,0x10000,true)
  1591. {
  1592. }
  1593. void dosort(IPropertyTreeIterator &iter,const char *sortorder, const char *namefilterlo,const char *namefilterhi, StringArray& unknownAttributes, IArrayOf<IPropertyTree> &results)
  1594. {
  1595. StringBuffer sk;
  1596. const char *s = sortorder;
  1597. int mod = 0;
  1598. loop {
  1599. if (!*s||(*s==',')) {
  1600. if (sk.length()) {
  1601. // could add '-' and '?' prefixes here (reverse/caseinsensitive)
  1602. Owned<CIStringArray> keyList = new CIStringArray;
  1603. keyList->appendListUniq(sk.str(), "|");
  1604. sortKeys.append(*keyList.getClear());
  1605. modifiers.append(mod);
  1606. sk.clear();
  1607. }
  1608. mod = 0;
  1609. if (!*s)
  1610. break;
  1611. }
  1612. else if ((*s=='-')&&(sk.length()==0))
  1613. mod |= SORT_REVERSE;
  1614. else if ((*s=='?')&&(sk.length()==0))
  1615. mod |= SORT_NOCASE;
  1616. else if ((*s=='#')&&(sk.length()==0))
  1617. mod |= SORT_NUMERIC;
  1618. else
  1619. sk.append(*s);
  1620. s++;
  1621. }
  1622. ForEach(iter)
  1623. filteredAdd(sortvalues,namefilterlo,namefilterhi,unknownAttributes,&iter.query());
  1624. nv = sortvalues.ordinality();
  1625. nk = sortKeys.ordinality();
  1626. vals = (char **)calloc(sizeof(char *),nv*nk);
  1627. unsigned *idx=(unsigned *)malloc(sizeof(unsigned)*nv);
  1628. unsigned i;
  1629. for (i=0;i<nv;i++)
  1630. idx[i] = i;
  1631. {
  1632. CriticalBlock block(sortsect);
  1633. sortthis = this;
  1634. qsort(idx,nv,sizeof(unsigned),compare);
  1635. }
  1636. for (i=0;i<nv;i++) {
  1637. IPropertyTree &item = sortvalues.item((unsigned)idx[i]);
  1638. item.Link();
  1639. results.append(item);
  1640. }
  1641. free(vals);
  1642. free(idx);
  1643. }
  1644. void getkeyval(unsigned idx,unsigned k,char *&val) const
  1645. {
  1646. StringArray &keys = sortKeys.item(k);
  1647. const char *key = keys.item(0); // must be >=1
  1648. const char *v;
  1649. if ((key[0]=='@')&&(key[1]==0))
  1650. {
  1651. v = sortvalues.item(idx).queryName();
  1652. dbgassertex(1 == keys.ordinality()); // meaningless for multivalue key if key is special key "@"
  1653. }
  1654. else
  1655. {
  1656. unsigned k2=1;
  1657. loop
  1658. {
  1659. v = sortvalues.item(idx).queryProp(key);
  1660. if (v || k2 == keys.ordinality())
  1661. break;
  1662. key = keys.item(k2++);
  1663. }
  1664. }
  1665. if (!v)
  1666. v = "";
  1667. size32_t l = strlen(v)+1;
  1668. val = (char *)buf.alloc(l);
  1669. memcpy(val,v,l);
  1670. }
  1671. int docompare(unsigned i1,unsigned i2) const
  1672. {
  1673. if (i1!=i2) {
  1674. for (unsigned i=0;i<nk;i++) {
  1675. int mod = modifiers.item(i);
  1676. char *&v1 = vals[i1*nk+i];
  1677. if (!v1)
  1678. getkeyval(i1,i,v1);
  1679. char *&v2 = vals[i2*nk+i];
  1680. if (!v2)
  1681. getkeyval(i2,i,v2);
  1682. if (!v1 || !v2)
  1683. return 0;
  1684. int ret;
  1685. if (mod&SORT_NUMERIC)
  1686. {
  1687. __int64 ret0 = _atoi64(v1)-_atoi64(v2);
  1688. if (ret0 > 0)
  1689. ret = 1;
  1690. else if (ret0 < 0)
  1691. ret = -1;
  1692. else
  1693. ret = 0;
  1694. }
  1695. else if (mod&SORT_NOCASE)
  1696. ret = stricmp(v1,v2);
  1697. else
  1698. ret = strcmp(v1,v2);
  1699. if (ret) {
  1700. if (mod&SORT_REVERSE)
  1701. ret = -ret;
  1702. return ret;
  1703. }
  1704. }
  1705. }
  1706. return 0;
  1707. }
  1708. static int compare(const void *a,const void *b)
  1709. {
  1710. return sortthis->docompare(*(unsigned*)a,*(unsigned*)b);
  1711. }
  1712. };
  1713. CriticalSection cSort::sortsect;
  1714. cSort *cSort::sortthis;
  1715. IRemoteConnection *getSortedElements( const char *basexpath,
  1716. const char *xpath,
  1717. const char *sortorder,
  1718. const char *namefilterlo,
  1719. const char *namefilterhi,
  1720. StringArray& unknownAttributes,
  1721. IArrayOf<IPropertyTree> &results)
  1722. {
  1723. Owned<IRemoteConnection> conn = querySDS().connect(basexpath, myProcessSession(), 0, SDS_LOCK_TIMEOUT);
  1724. if (!conn)
  1725. return NULL;
  1726. Owned<IPropertyTreeIterator> iter = conn->getElements(xpath);
  1727. if (!iter)
  1728. return NULL;
  1729. sortElements(iter, sortorder, namefilterlo,namefilterhi,unknownAttributes, results);
  1730. return conn.getClear();
  1731. }
  1732. //==================================================================================
  1733. #define PAGE_CACHE_TIMEOUT (1000*60*10)
  1734. class CTimedCacheItem: public CInterface
  1735. {
  1736. protected: friend class CTimedCache;
  1737. unsigned due;
  1738. StringAttr owner;
  1739. public:
  1740. DALI_UID hint;
  1741. CTimedCacheItem(const char *_owner)
  1742. : owner(_owner)
  1743. {
  1744. hint = queryCoven().getUniqueId();
  1745. due = msTick()+PAGE_CACHE_TIMEOUT;
  1746. }
  1747. };
  1748. class CTimedCache
  1749. {
  1750. class cThread: public Thread
  1751. {
  1752. public:
  1753. CTimedCache *parent;
  1754. cThread()
  1755. : Thread("CTimedCache::thread")
  1756. {
  1757. }
  1758. int run()
  1759. {
  1760. parent->run();
  1761. return 0;
  1762. }
  1763. } thread;
  1764. unsigned check()
  1765. {
  1766. unsigned res = (unsigned)-1;
  1767. unsigned now = msTick();
  1768. ForEachItemInRev(i,items) {
  1769. CTimedCacheItem &item = items.item(i);
  1770. unsigned t = item.due-now;
  1771. if ((int)t<=0)
  1772. items.remove(i);
  1773. else if (t<res)
  1774. res = t;
  1775. }
  1776. return res;
  1777. }
  1778. public:
  1779. void run()
  1780. {
  1781. CriticalBlock block(sect);
  1782. while(!stopping) {
  1783. unsigned delay=check();
  1784. CriticalUnblock unblock(sect);
  1785. sem.wait(delay);
  1786. }
  1787. }
  1788. CIArrayOf<CTimedCacheItem> items;
  1789. CriticalSection sect;
  1790. Semaphore sem;
  1791. bool stopping;
  1792. CTimedCache()
  1793. {
  1794. stopping = false;
  1795. thread.parent = this;
  1796. }
  1797. ~CTimedCache()
  1798. {
  1799. stop();
  1800. }
  1801. DALI_UID add(CTimedCacheItem *item)
  1802. {
  1803. if (!item)
  1804. return 0;
  1805. CriticalBlock block(sect);
  1806. item->due = msTick()+PAGE_CACHE_TIMEOUT;
  1807. items.append(*item);
  1808. DALI_UID ret = item->hint;
  1809. sem.signal();
  1810. return ret;
  1811. }
  1812. void remove(CTimedCacheItem *item)
  1813. {
  1814. CriticalBlock block(sect);
  1815. items.zap(*item);
  1816. }
  1817. CTimedCacheItem *get(const char *owner,DALI_UID hint)
  1818. {
  1819. CriticalBlock block(sect);
  1820. ForEachItemInRev(i,items) {
  1821. CTimedCacheItem &item = items.item(i);
  1822. if ((item.hint==hint)&&(strcmp(item.owner,owner)==0)) {
  1823. item.Link();
  1824. items.remove(i);
  1825. return &item;
  1826. }
  1827. }
  1828. return NULL;
  1829. }
  1830. void start()
  1831. {
  1832. thread.start();
  1833. }
  1834. void stop()
  1835. {
  1836. {
  1837. CriticalBlock block(sect);
  1838. stopping = true;
  1839. sem.signal();
  1840. }
  1841. thread.join();
  1842. }
  1843. };
  1844. static CriticalSection pagedElementsCacheSect;
  1845. static CTimedCache *pagedElementsCache=NULL;
  1846. class CPECacheElem: public CTimedCacheItem
  1847. {
  1848. public:
  1849. CPECacheElem(const char *owner, ISortedElementsTreeFilter *_postFilter)
  1850. : CTimedCacheItem(owner), postFilter(_postFilter), postFiltered(0)
  1851. {
  1852. passesFilter.setown(createThreadSafeBitSet());
  1853. }
  1854. ~CPECacheElem()
  1855. {
  1856. }
  1857. Owned<IRemoteConnection> conn;
  1858. IArrayOf<IPropertyTree> totalres;
  1859. Linked<ISortedElementsTreeFilter> postFilter;
  1860. unsigned postFiltered;
  1861. Owned<IBitSet> passesFilter;
  1862. };
  1863. void sortElements(IPropertyTreeIterator* elementsIter,
  1864. const char *sortOrder,
  1865. const char *nameFilterLo,
  1866. const char *nameFilterHi,
  1867. StringArray& unknownAttributes,
  1868. IArrayOf<IPropertyTree> &sortedElements)
  1869. {
  1870. if (nameFilterLo&&!*nameFilterLo)
  1871. nameFilterLo = NULL;
  1872. if (nameFilterHi&&!*nameFilterHi)
  1873. nameFilterHi = NULL;
  1874. if (sortOrder && *sortOrder)
  1875. {
  1876. cSort sort;
  1877. sort.dosort(*elementsIter,sortOrder,nameFilterLo,nameFilterHi,unknownAttributes, sortedElements);
  1878. }
  1879. else
  1880. ForEach(*elementsIter)
  1881. filteredAdd(sortedElements,nameFilterLo,nameFilterHi,unknownAttributes, &elementsIter->query());
  1882. };
  1883. IRemoteConnection *getElementsPaged( IElementsPager *elementsPager,
  1884. unsigned startoffset,
  1885. unsigned pagesize,
  1886. ISortedElementsTreeFilter *postfilter, // filters before adding to page
  1887. const char *owner,
  1888. __int64 *hint,
  1889. IArrayOf<IPropertyTree> &results,
  1890. unsigned *total,
  1891. bool *allMatchingElementsReceived,
  1892. bool checkConn)
  1893. {
  1894. if ((pagesize==0) || !elementsPager)
  1895. return NULL;
  1896. {
  1897. CriticalBlock block(pagedElementsCacheSect);
  1898. if (!pagedElementsCache) {
  1899. pagedElementsCache = new CTimedCache;
  1900. pagedElementsCache->start();
  1901. }
  1902. }
  1903. Owned<CPECacheElem> elem;
  1904. if (hint&&*hint)
  1905. {
  1906. elem.setown(QUERYINTERFACE(pagedElementsCache->get(owner,*hint),CPECacheElem)); // NB: removes from cache in process, added back at end
  1907. if (elem)
  1908. postfilter = elem->postFilter; // reuse cached postfilter
  1909. }
  1910. if (!elem)
  1911. {
  1912. elem.setown(new CPECacheElem(owner, postfilter));
  1913. elem->conn.setown(elementsPager->getElements(elem->totalres));
  1914. }
  1915. if (checkConn && !elem->conn)
  1916. return NULL;
  1917. unsigned n;
  1918. if (total)
  1919. *total = elem->totalres.ordinality();
  1920. if (postfilter) {
  1921. unsigned numFiltered = 0;
  1922. n = 0;
  1923. ForEachItemIn(i,elem->totalres) {
  1924. IPropertyTree &item = elem->totalres.item(i);
  1925. bool passesFilter = false;
  1926. if (elem->postFiltered>i) // postFiltered is high water mark of items checked
  1927. passesFilter = elem->passesFilter->test(i);
  1928. else
  1929. {
  1930. passesFilter = postfilter->isOK(item);
  1931. elem->passesFilter->set(i, passesFilter);
  1932. elem->postFiltered = i+1;
  1933. }
  1934. if (passesFilter)
  1935. {
  1936. if (n>=startoffset) {
  1937. item.Link();
  1938. results.append(item);
  1939. if (results.ordinality()>=pagesize)
  1940. {
  1941. // if total needed, need to iterate through all items
  1942. if (NULL == total)
  1943. break;
  1944. startoffset = (unsigned)-1; // no more results needed
  1945. }
  1946. }
  1947. n++;
  1948. }
  1949. else
  1950. ++numFiltered;
  1951. }
  1952. if (total)
  1953. *total -= numFiltered;
  1954. }
  1955. else {
  1956. n = (elem->totalres.ordinality()>startoffset)?(elem->totalres.ordinality()-startoffset):0;
  1957. if (n>pagesize)
  1958. n = pagesize;
  1959. for (unsigned i=startoffset;i<startoffset+n;i++) {
  1960. IPropertyTree &item = elem->totalres.item(i);
  1961. item.Link();
  1962. results.append(item);
  1963. }
  1964. }
  1965. if (allMatchingElementsReceived)
  1966. *allMatchingElementsReceived = elementsPager->allMatchingElementsReceived();
  1967. IRemoteConnection *ret = NULL;
  1968. if (elem->conn)
  1969. ret = elem->conn.getLink();
  1970. if (hint) {
  1971. *hint = elem->hint;
  1972. pagedElementsCache->add(elem.getClear());
  1973. }
  1974. return ret;
  1975. }
  1976. void clearPagedElementsCache()
  1977. {
  1978. CriticalBlock block(pagedElementsCacheSect);
  1979. try {
  1980. delete pagedElementsCache;
  1981. }
  1982. catch (IMP_Exception *e)
  1983. {
  1984. if (e->errorCode()!=MPERR_link_closed)
  1985. throw;
  1986. e->Release();
  1987. }
  1988. catch (IDaliClient_Exception *e) {
  1989. if (e->errorCode()!=DCERR_server_closed)
  1990. throw;
  1991. e->Release();
  1992. }
  1993. catch (IException *e)
  1994. {
  1995. EXCLOG(e, "clearPagedElementsCache");
  1996. e->Release();
  1997. }
  1998. pagedElementsCache = NULL;
  1999. }
  2000. void CSDSFileScanner::processScopes(IRemoteConnection *conn,IPropertyTree &root,StringBuffer &name)
  2001. {
  2002. if (!checkScopeOk(name))
  2003. return;
  2004. size32_t ns = name.length();
  2005. if (ns)
  2006. name.append("::");
  2007. size32_t ns2 = name.length();
  2008. Owned<IPropertyTreeIterator> iter = root.getElements("Scope");
  2009. ForEach(*iter) {
  2010. IPropertyTree &scope = iter->query();
  2011. const char *sn = scope.queryProp("@name");
  2012. if (!sn||!*sn)
  2013. continue;
  2014. name.append(sn);
  2015. processScopes(conn,scope,name);
  2016. name.setLength(ns2);
  2017. conn->rollbackChildren(&scope,true);
  2018. }
  2019. processFiles(conn,root,name);
  2020. name.setLength(ns);
  2021. }
  2022. void CSDSFileScanner::processFiles(IRemoteConnection *conn,IPropertyTree &root,StringBuffer &name)
  2023. {
  2024. size32_t ns = name.length();
  2025. if (includefiles) {
  2026. Owned<IPropertyTreeIterator> iter = root.getElements(queryDfsXmlBranchName(DXB_File));
  2027. ForEach(*iter) {
  2028. IPropertyTree &file = iter->query();
  2029. const char *fn = file.queryProp("@name");
  2030. if (!fn||!*fn)
  2031. continue;
  2032. name.append(fn);
  2033. if (checkFileOk(file,name.str())) {
  2034. processFile(file,name);
  2035. }
  2036. else
  2037. ; // DBGLOG("ignoreFile %s",name.str());
  2038. name.setLength(ns);
  2039. conn->rollbackChildren(&file,true);
  2040. }
  2041. }
  2042. if (includesuper) {
  2043. Owned<IPropertyTreeIterator> iter = root.getElements(queryDfsXmlBranchName(DXB_SuperFile));
  2044. ForEach(*iter) {
  2045. IPropertyTree &file = iter->query();
  2046. const char *fn = file.queryProp("@name");
  2047. if (!fn||!*fn)
  2048. continue;
  2049. name.append(fn);
  2050. if (checkSuperFileOk(file,name.str())) {
  2051. file.getBranch(NULL);
  2052. processSuperFile(file,name);
  2053. }
  2054. name.setLength(ns);
  2055. conn->rollbackChildren(&file,true);
  2056. }
  2057. }
  2058. }
  2059. void CSDSFileScanner::scan(IRemoteConnection *conn,
  2060. bool _includefiles,
  2061. bool _includesuper)
  2062. {
  2063. includefiles = _includefiles;
  2064. includesuper = _includesuper;
  2065. StringBuffer name;
  2066. Owned<IPropertyTree> root=conn->getRoot();
  2067. processScopes(conn,*root,name);
  2068. }
  2069. bool CSDSFileScanner::singlefile(IRemoteConnection *conn,CDfsLogicalFileName &lfn)
  2070. {
  2071. if (!conn)
  2072. return false;
  2073. Owned<IPropertyTree> root=conn->getRoot();
  2074. if (!lfn.isSet())
  2075. return false;
  2076. StringBuffer query;
  2077. lfn.makeFullnameQuery(query,DXB_File,false);
  2078. IPropertyTree *file = root->queryPropTree(query.str());
  2079. StringBuffer name;
  2080. lfn.get(name);
  2081. if (file) {
  2082. if (checkFileOk(*file,name.str())) {
  2083. processFile(*file,name);
  2084. }
  2085. }
  2086. else { // try super
  2087. lfn.makeFullnameQuery(query.clear(),DXB_SuperFile,false);
  2088. file = root->queryPropTree(query.str());
  2089. if (!file)
  2090. return false;
  2091. if (checkSuperFileOk(*file,name.str())) {
  2092. processSuperFile(*file,name);
  2093. }
  2094. }
  2095. return true;
  2096. }
  2097. extern da_decl bool isAnonCluster(const char *grp)
  2098. {
  2099. if (!grp)
  2100. return false;
  2101. return((memicmp(grp,"__cluster",9)==0)||(memicmp(grp,"__anon",6)==0));
  2102. }
  2103. IClusterFileScanIterator *getClusterFileScanIterator(
  2104. IRemoteConnection *conn, // conn is connection to Files
  2105. IGroup *group, // only scans file with nodes in specified group
  2106. bool exactmatch, // only files that match group exactly
  2107. bool anymatch, // any nodes match
  2108. bool loadbranch,
  2109. IUserDescriptor *user)
  2110. {
  2111. class cFileScanIterator: implements IClusterFileScanIterator, public CInterface
  2112. {
  2113. Owned<IPropertyTree> cur;
  2114. unsigned fn;
  2115. Linked<IRemoteConnection> conn;
  2116. Linked<IGroup> lgrp;
  2117. bool loadbranch;
  2118. bool exactmatch;
  2119. bool anymatch;
  2120. public:
  2121. StringArray filenames;
  2122. IMPLEMENT_IINTERFACE;
  2123. cFileScanIterator(IRemoteConnection *_conn,bool _loadbranch, IGroup *_lgrp, bool _exactmatch, bool _anymatch)
  2124. : conn(_conn), lgrp(_lgrp)
  2125. {
  2126. fn = 0;
  2127. loadbranch = _loadbranch;
  2128. exactmatch = _exactmatch;
  2129. anymatch = _anymatch;
  2130. }
  2131. bool first()
  2132. {
  2133. fn = 0;
  2134. if (!conn)
  2135. return false;
  2136. return next();
  2137. }
  2138. bool next()
  2139. {
  2140. cur.clear();
  2141. loop {
  2142. if (fn>=filenames.ordinality())
  2143. return false;
  2144. const char *fns = filenames.item(fn++);
  2145. bool needcheck = false;
  2146. if (fns[0]=='\n') { // need to check
  2147. needcheck = true;
  2148. fns++;
  2149. }
  2150. CDfsLogicalFileName lfn;
  2151. lfn.set(fns);
  2152. StringBuffer query;
  2153. lfn.makeFullnameQuery(query,DXB_File,false);
  2154. if (loadbranch)
  2155. cur.setown(conn->queryRoot()->getBranch(query.str()));
  2156. else
  2157. cur.setown(conn->queryRoot()->getPropTree(query.str()));
  2158. if (needcheck) {
  2159. Owned<IFileDescriptor> fdesc = deserializeFileDescriptorTree(cur);
  2160. bool ok = false;
  2161. Owned<IGroup> group = fdesc->getGroup();
  2162. GroupRelation cmp = group->compare(lgrp);
  2163. if (group.get()) {
  2164. if ((cmp==GRidentical)||
  2165. (!exactmatch&&((cmp==GRbasesubset)||(cmp==GRwrappedsuperset)))||
  2166. (anymatch&&(cmp!=GRdisjoint)))
  2167. {
  2168. ok = true;
  2169. break;
  2170. }
  2171. }
  2172. if (!ok)
  2173. cur.clear();
  2174. }
  2175. } while (!cur.get());
  2176. return true;
  2177. }
  2178. bool isValid()
  2179. {
  2180. return cur.get()!=NULL;
  2181. }
  2182. IPropertyTree & query()
  2183. {
  2184. return *cur;
  2185. }
  2186. const char *queryName()
  2187. {
  2188. if (fn&&(fn<=filenames.ordinality()))
  2189. return filenames.item(fn-1); // fn always kept +1
  2190. return NULL;
  2191. }
  2192. } *ret = new cFileScanIterator(conn,loadbranch,group,exactmatch,anymatch);
  2193. StringArray candidategroups;
  2194. if (group) {
  2195. Owned<INamedGroupIterator> iter = queryNamedGroupStore().getIterator();
  2196. StringBuffer name;
  2197. ForEach(*iter) {
  2198. iter->get(name.clear());
  2199. Owned<IGroup> lgrp = queryNamedGroupStore().lookup(name.str());
  2200. GroupRelation cmp = group->compare(lgrp);
  2201. if ((cmp==GRidentical)||
  2202. (!exactmatch&&((cmp==GRbasesubset)||(cmp==GRwrappedsuperset)))||
  2203. (anymatch&&(cmp!=GRdisjoint)))
  2204. {
  2205. candidategroups.append(name.str());
  2206. }
  2207. }
  2208. }
  2209. Owned<IDFAttributesIterator> iter=queryDistributedFileDirectory().getDFAttributesIterator("*",user);
  2210. StringBuffer chkname;
  2211. StringBuffer gname;
  2212. ForEach(*iter) {
  2213. IPropertyTree &attr = iter->query();
  2214. const char *name = attr.queryProp("@name");
  2215. if (!name||!*name)
  2216. continue;
  2217. if (!group) {
  2218. ret->filenames.append(name);
  2219. continue;
  2220. }
  2221. if (exactmatch && (attr.getPropInt("@numparts")!=group->ordinality()))
  2222. continue;
  2223. StringArray groups;
  2224. if (getFileGroups(&attr,groups)==0) {
  2225. StringBuffer chkname;
  2226. chkname.clear().append('\n').append(name); // indicates needs checking
  2227. ret->filenames.append(chkname.str());
  2228. continue;
  2229. }
  2230. bool matched = false;
  2231. ForEachItemIn(i,groups) {
  2232. ForEachItemIn(j,candidategroups) {
  2233. if (stricmp(groups.item(i),candidategroups.item(j))==0) {
  2234. matched = true;
  2235. break;
  2236. }
  2237. }
  2238. if (matched)
  2239. break;
  2240. }
  2241. }
  2242. return ret;
  2243. }
  2244. typedef MapStringTo<bool> IsSuperFileMap;
  2245. void getLogicalFileSuperSubList(MemoryBuffer &mb, IUserDescriptor *user)
  2246. {
  2247. // for fileservices
  2248. IsSuperFileMap supermap;
  2249. IDFAttributesIterator *iter = queryDistributedFileDirectory().getDFAttributesIterator("*",user,true,true);
  2250. if (iter) {
  2251. ForEach(*iter) {
  2252. IPropertyTree &attr=iter->query();
  2253. if (attr.hasProp("@numsubfiles")) {
  2254. const char *name=attr.queryProp("@name");
  2255. if (name&&*name) {
  2256. if (!supermap.getValue(name))
  2257. supermap.setValue(name, true);
  2258. }
  2259. }
  2260. }
  2261. }
  2262. HashIterator siter(supermap);
  2263. ForEach(siter) {
  2264. const char *supername = (const char *)siter.query().getKey();
  2265. CDfsLogicalFileName lname;
  2266. lname.set(supername);
  2267. StringBuffer query;
  2268. lname.makeFullnameQuery(query, DXB_SuperFile, true);
  2269. Owned<IRemoteConnection> conn = querySDS().connect(query.str(),myProcessSession(),0, INFINITE);
  2270. if (conn) { // Superfile may have disappeared by this stage, ignore.
  2271. Owned<IPropertyTree> root = conn->getRoot();
  2272. unsigned n=root->getPropInt("@numsubfiles");
  2273. StringBuffer path;
  2274. StringBuffer subname;
  2275. unsigned subnum = 0;
  2276. for (unsigned si=0;si<n;si++) {
  2277. IPropertyTree *sub = root->queryPropTree(path.clear().appendf("SubFile[@num=\"%d\"]",si+1).str());
  2278. if (sub) {
  2279. const char *subname = sub->queryProp("@name");
  2280. if (subname&&*subname) {
  2281. if (!supermap.getValue(subname)) {
  2282. size32_t sz = strlen(supername);
  2283. mb.append(sz).append(sz,supername);
  2284. sz = strlen(subname);
  2285. mb.append(sz).append(sz,subname);
  2286. }
  2287. }
  2288. }
  2289. }
  2290. }
  2291. }
  2292. }
  2293. class cDaliMutexSub: implements ISDSSubscription, public CInterface
  2294. {
  2295. Semaphore &sem;
  2296. public:
  2297. IMPLEMENT_IINTERFACE;
  2298. cDaliMutexSub(Semaphore &_sem)
  2299. : sem(_sem)
  2300. {
  2301. }
  2302. virtual void notify(SubscriptionId id, const char *xpath, SDSNotifyFlags flags, unsigned valueLen, const void *valueData)
  2303. {
  2304. // PrintLog("Notification(%" I64F "x) of %s - flags = %d",(__int64) id, xpath, flags);
  2305. sem.signal();
  2306. }
  2307. };
  2308. class CDaliMutex: implements IDaliMutex, public CInterface
  2309. {
  2310. StringAttr name;
  2311. CriticalSection crit;
  2312. Semaphore sem;
  2313. unsigned count;
  2314. Owned<IRemoteConnection> oconn;
  2315. SessionId mysession;
  2316. bool stopping;
  2317. public:
  2318. IMPLEMENT_IINTERFACE;
  2319. CDaliMutex(const char *_name)
  2320. : name(_name)
  2321. {
  2322. count = 0;
  2323. mysession = myProcessSession();
  2324. stopping = false;
  2325. }
  2326. bool enter(unsigned timeout=(unsigned)-1,IDaliMutexNotifyWaiting *notify=NULL)
  2327. {
  2328. CriticalBlock block(crit); // crit is held if blocked
  2329. if (count++)
  2330. return true;
  2331. Owned<IRemoteConnection> conn;
  2332. Owned<cDaliMutexSub> sub = new cDaliMutexSub(sem);
  2333. CTimeMon tm(timeout);
  2334. bool first = true;
  2335. bool needstop = false;
  2336. while (!stopping) {
  2337. // first lock semaphore to write
  2338. StringBuffer path;
  2339. path.appendf("/Locks/Mutex[@name=\"%s\"]",name.get());
  2340. conn.setown(querySDS().connect(path.str(),mysession,RTM_LOCK_WRITE,SDS_CONNECT_TIMEOUT));
  2341. if (conn) {
  2342. SessionId oid = (SessionId)conn->queryRoot()->getPropInt64("Owner",0);
  2343. if (!oid||querySessionManager().sessionStopped(oid,0)) {
  2344. StringBuffer opath(path);
  2345. opath.append("/Owner");
  2346. oconn.setown(querySDS().connect(opath.str(),mysession,RTM_CREATE|RTM_LOCK_WRITE|RTM_DELETE_ON_DISCONNECT,SDS_CONNECT_TIMEOUT));
  2347. if (!oconn)
  2348. throw MakeStringException(-1,"CDaliMutex::enter Cannot create %s branch",opath.str());
  2349. oconn->queryRoot()->setPropInt64(NULL,mysession);
  2350. oconn->commit();
  2351. if (needstop)
  2352. notify->stopWait(true);
  2353. return true;
  2354. }
  2355. }
  2356. else {
  2357. Owned<IRemoteConnection> pconn = querySDS().connect("Locks",mysession,RTM_LOCK_WRITE|RTM_CREATE_QUERY,SDS_CONNECT_TIMEOUT);
  2358. if (!pconn)
  2359. throw MakeStringException(-1,"CDaliMutex::enter Cannot create /Locks branch");
  2360. path.clear().appendf("Mutex[@name=\"%s\"]",name.get());
  2361. if (!pconn->queryRoot()->hasProp(name))
  2362. pconn->queryRoot()->addPropTree("Mutex",createPTree("Mutex"))->setProp("@name",name);
  2363. continue; // try again
  2364. }
  2365. unsigned remaining;
  2366. if (tm.timedout(&remaining))
  2367. break;
  2368. // subscribed while still locked
  2369. SubscriptionId subid = querySDS().subscribe(path.str(), *sub);
  2370. conn.clear();
  2371. unsigned ttw = first?1000*60:1000*60*5; // poll every 5m - may up later (once subscription spots auto-delete)
  2372. if (!sem.wait((remaining>ttw)?ttw:remaining)) {
  2373. if (!tm.timedout()) {
  2374. PROGLOG("Waiting for dali mutex %s",name.get());
  2375. if (first) {
  2376. if (notify) {
  2377. notify->startWait();
  2378. needstop = true;
  2379. }
  2380. first = false;
  2381. }
  2382. else if (needstop)
  2383. notify->cycleWait();
  2384. }
  2385. }
  2386. querySDS().unsubscribe(subid);
  2387. }
  2388. if (needstop)
  2389. notify->stopWait(false);
  2390. count = 0;
  2391. return false;
  2392. }
  2393. void leave()
  2394. {
  2395. CriticalBlock block(crit);
  2396. if (!count)
  2397. throw MakeStringException(-1,"CDaliMutex leave without corresponding enter");
  2398. if (--count)
  2399. return;
  2400. oconn.clear();
  2401. }
  2402. void kill()
  2403. {
  2404. stopping = true; // note must not be in crit sect
  2405. sem.signal();
  2406. CriticalBlock block(crit);
  2407. count = 0;
  2408. oconn.clear();
  2409. }
  2410. };
  2411. IDaliMutex *createDaliMutex(const char *name)
  2412. {
  2413. return new CDaliMutex(name);
  2414. }
  2415. // ===============================================================================
  2416. // File redirection
  2417. /*
  2418. /Files
  2419. /Redirection @version
  2420. /Maps
  2421. */
  2422. class CDFSredirection: implements IDFSredirection, public CInterface
  2423. {
  2424. MemoryAttr buf;
  2425. unsigned version;
  2426. unsigned lastloaded;
  2427. public:
  2428. IMPLEMENT_IINTERFACE;
  2429. struct sMapRec
  2430. {
  2431. const char *pat;
  2432. const char *repl;
  2433. bool iswild;
  2434. bool match(const char *name,StringBuffer &out)
  2435. {
  2436. if (iswild)
  2437. return WildMatchReplace(name,pat,repl,true,out);
  2438. if (stricmp(name,pat)!=0)
  2439. return false;
  2440. out.append(repl);
  2441. return true;
  2442. }
  2443. } *maps;
  2444. unsigned nmaps;
  2445. CriticalSection sect;
  2446. unsigned linked;
  2447. CDFSredirection()
  2448. {
  2449. linked = 0;
  2450. maps = NULL;
  2451. nmaps = 0;
  2452. version = (unsigned)-1;
  2453. lastloaded = 0;
  2454. }
  2455. ~CDFSredirection()
  2456. {
  2457. if (linked)
  2458. ERRLOG("CDFSredirection: cDFSredirect leaked(%d)",linked);
  2459. clear();
  2460. }
  2461. void clear()
  2462. {
  2463. buf.clear();
  2464. delete [] maps;
  2465. maps = NULL;
  2466. nmaps = 0;
  2467. lastloaded = 0;
  2468. }
  2469. class cDFSredirect: implements IDfsLogicalFileNameIterator, public CInterface
  2470. {
  2471. unsigned idx;
  2472. unsigned got;
  2473. StringAttr infn;
  2474. CDfsLogicalFileName lfn;
  2475. CDFSredirection &parent;
  2476. public:
  2477. IMPLEMENT_IINTERFACE;
  2478. cDFSredirect(CDFSredirection &_parent,const char *_infn)
  2479. : parent(_parent), infn(_infn)
  2480. {
  2481. // in crit sect
  2482. idx = 0;
  2483. got = (unsigned)-1;
  2484. }
  2485. ~cDFSredirect()
  2486. {
  2487. CriticalBlock block(parent.sect);
  2488. parent.linked--;
  2489. }
  2490. bool first()
  2491. {
  2492. idx = (unsigned)-1;
  2493. return next();
  2494. }
  2495. bool next()
  2496. {
  2497. StringBuffer out;
  2498. loop {
  2499. idx++;
  2500. if (idx>=parent.nmaps)
  2501. break;
  2502. if (parent.maps[idx].match(infn.get(),out.clear())) {
  2503. if (out.length()==0) { // this is 'blocker'
  2504. idx=parent.nmaps;
  2505. break;
  2506. }
  2507. if (lfn.setValidate(out.str())) {
  2508. got = idx;
  2509. return true;
  2510. }
  2511. }
  2512. }
  2513. got = (unsigned)-1;
  2514. return false;
  2515. }
  2516. bool isValid()
  2517. {
  2518. return idx==got;
  2519. }
  2520. CDfsLogicalFileName & query()
  2521. {
  2522. return lfn;
  2523. }
  2524. };
  2525. void load()
  2526. {
  2527. // called in critical section
  2528. if (linked)
  2529. return; // locked in
  2530. if (lastloaded&&(lastloaded-msTick()<MIN_REDIRECTION_LOAD_INTERVAL))
  2531. return; // loaded recently (can be cleared)
  2532. Owned<IRemoteConnection> conn = querySDS().connect("Files/Redirection", myProcessSession(), RTM_LOCK_READ, SDS_LOCK_TIMEOUT);
  2533. clear();
  2534. lastloaded = msTick();
  2535. if (!lastloaded)
  2536. lastloaded++;
  2537. if (conn) {
  2538. IPropertyTree &root = *conn->queryRoot();
  2539. unsigned v = (unsigned)root.getPropInt("@version",-1);
  2540. if ((v!=(unsigned)-1)&&(v==version))
  2541. return;
  2542. version = v;
  2543. clear();
  2544. MemoryBuffer mb;
  2545. if (root.getPropBin("Maps",mb))
  2546. mb.read(nmaps);
  2547. else
  2548. nmaps = 0;
  2549. if (nmaps) {
  2550. maps = new sMapRec[nmaps];
  2551. size32_t len = mb.length()-mb.getPos();
  2552. mb.read(len,buf.allocate(len));
  2553. const char *s = (const char *)buf.bufferBase();
  2554. for (unsigned i=0;*s&&(i<nmaps);i++) {
  2555. maps[i].pat = s;
  2556. const char *r = s+strlen(s)+1;
  2557. maps[i].repl = r;
  2558. maps[i].iswild = (strchr(s,'*')||strchr(s,'?')||strchr(r,'$'));
  2559. s = r+strlen(r)+1;
  2560. }
  2561. // future stuff added here
  2562. }
  2563. }
  2564. else
  2565. clear();
  2566. }
  2567. void update(const char *targpat, const char *targrepl, unsigned idx)
  2568. {
  2569. // *doesn't* reload (but invalidates last load time)
  2570. Owned<IRemoteConnection> conn = querySDS().connect("Files/Redirection", myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
  2571. if (!conn) {
  2572. ERRLOG("Cannot update Files/Redirection");
  2573. return;
  2574. }
  2575. IPropertyTree &root = *conn->queryRoot();
  2576. MemoryBuffer mb;
  2577. MemoryBuffer mbout;
  2578. unsigned nm;
  2579. const char * s;
  2580. if (root.getPropBin("Maps",mb)) {
  2581. mb.read(nm);
  2582. s = (const char *)mb.readDirect(mb.length()-mb.getPos());
  2583. }
  2584. else {
  2585. nm = 0;
  2586. s = NULL;
  2587. }
  2588. unsigned i;
  2589. unsigned no = 0;
  2590. mbout.append(no);
  2591. for (i=0;(i<nm)&&*s;i++) {
  2592. if (i==idx) {
  2593. no++;
  2594. mbout.append(targpat);
  2595. mbout.append(targrepl);
  2596. }
  2597. size32_t ls = strlen(s)+1;
  2598. const char *r = s+ls;
  2599. size32_t lr = strlen(r)+1;
  2600. // see if matches (to delete old)
  2601. if (stricmp(targpat,s)!=0) {
  2602. no++;
  2603. mbout.append(ls,s);
  2604. mbout.append(lr,r);
  2605. }
  2606. s = r+lr;
  2607. }
  2608. if (i<idx) {
  2609. no++;
  2610. mbout.append(targpat);
  2611. mbout.append(targrepl);
  2612. }
  2613. mbout.writeDirect(0,sizeof(no),&no);
  2614. root.setPropBin("Maps",mbout.length(),mbout.toByteArray());
  2615. root.setPropInt("@version",root.getPropInt("@version",-1)+1);
  2616. CriticalBlock block(sect);
  2617. lastloaded = 0;
  2618. }
  2619. IDfsLogicalFileNameIterator *getMatch(const char *infn)
  2620. {
  2621. CriticalBlock block(sect);
  2622. load();
  2623. linked++;
  2624. return new cDFSredirect(*this,infn);
  2625. }
  2626. bool getEntry(unsigned idx,StringBuffer &pat,StringBuffer &repl)
  2627. {
  2628. CriticalBlock block(sect);
  2629. load();
  2630. if (idx>=nmaps)
  2631. return false;
  2632. pat.append(maps[idx].pat);
  2633. repl.append(maps[idx].repl);
  2634. return true;
  2635. }
  2636. unsigned numEntries()
  2637. {
  2638. CriticalBlock block(sect);
  2639. load();
  2640. return nmaps;
  2641. }
  2642. };
  2643. IDFSredirection *createDFSredirection() // only called by dadfs.cpp
  2644. {
  2645. return new CDFSredirection();
  2646. }
  2647. void safeChangeModeWrite(IRemoteConnection *conn,const char *name,bool &reload, unsigned timeoutms)
  2648. {
  2649. unsigned start = msTick();
  2650. unsigned count = 0;
  2651. // if the lock was lost (changed to NONE), means someone else changed it
  2652. // so the caller might want to refresh its cache (of sub-files, for ex.)
  2653. reload = false;
  2654. #ifdef TEST_DEADLOCK_RELEASE
  2655. // release the lock so that other threads can try and lock it
  2656. conn->changeMode(RTM_NONE);
  2657. reload = true; // always reload
  2658. #endif
  2659. unsigned steptime = 1000*60*5;
  2660. if ((timeoutms!=INFINITE)&&(steptime>timeoutms/2))
  2661. steptime = timeoutms/2;
  2662. loop {
  2663. try {
  2664. if ((timeoutms!=INFINITE)&&(steptime>timeoutms))
  2665. steptime = timeoutms;
  2666. conn->changeMode(RTM_LOCK_WRITE,steptime,true);
  2667. // lock was lost at least once, refresh connection
  2668. if (reload) {
  2669. conn->reload();
  2670. PROGLOG("safeChangeModeWrite - re-obtained lock for %s",name);
  2671. }
  2672. break;
  2673. }
  2674. catch (ISDSException *e)
  2675. {
  2676. if (SDSExcpt_LockTimeout == e->errorCode())
  2677. {
  2678. unsigned tt = msTick()-start;
  2679. if (count++>0) {// don't warn first time
  2680. WARNLOG("safeChangeModeWrite on %s waiting for %ds",name,tt/1000);
  2681. if (count==2)
  2682. PrintStackReport();
  2683. }
  2684. if (timeoutms!=INFINITE) {
  2685. timeoutms -= steptime;
  2686. if (timeoutms==0)
  2687. throw;
  2688. }
  2689. e->Release();
  2690. }
  2691. else
  2692. throw;
  2693. }
  2694. // temporarily release the lock, we don't need to warn twice, do we?
  2695. if (!reload) {
  2696. WARNLOG("safeChangeModeWrite - temporarily releasing lock on %s to avoid deadlock",name);
  2697. conn->changeMode(RTM_NONE);
  2698. reload = true;
  2699. }
  2700. unsigned pause = 1000*(30+getRandom()%60);
  2701. if (timeoutms!=INFINITE)
  2702. if (pause>timeoutms/2)
  2703. pause = timeoutms/2;
  2704. Sleep(pause);
  2705. if (timeoutms!=INFINITE)
  2706. timeoutms -= pause;
  2707. }
  2708. }
  2709. class CLocalOrDistributedFile: implements ILocalOrDistributedFile, public CInterface
  2710. {
  2711. bool fileExists;
  2712. Owned<IDistributedFile> dfile;
  2713. CDfsLogicalFileName lfn; // set if localpath but prob not useful
  2714. StringAttr localpath;
  2715. public:
  2716. IMPLEMENT_IINTERFACE;
  2717. CLocalOrDistributedFile()
  2718. {
  2719. fileExists = false;
  2720. }
  2721. const char *queryLogicalName()
  2722. {
  2723. return lfn.get();
  2724. }
  2725. IDistributedFile * queryDistributedFile()
  2726. {
  2727. return dfile.get();
  2728. }
  2729. bool init(const char *fname,IUserDescriptor *user,bool onlylocal,bool onlydfs, bool write)
  2730. {
  2731. fileExists = false;
  2732. if (!onlydfs)
  2733. lfn.allowOsPath(true);
  2734. if (!lfn.setValidate(fname))
  2735. return false;
  2736. if (!onlydfs)
  2737. {
  2738. bool gotlocal = true;
  2739. if (isAbsolutePath(fname)||(stdIoHandle(fname)>=0))
  2740. localpath.set(fname);
  2741. else if (!strstr(fname,"::"))
  2742. {
  2743. // treat it as a relative file
  2744. StringBuffer fn;
  2745. localpath.set(makeAbsolutePath(fname,fn).str());
  2746. }
  2747. else if (!lfn.isExternal())
  2748. gotlocal = false;
  2749. if (gotlocal)
  2750. {
  2751. if (!write && !onlylocal) // MORE - this means the dali access checks not happening... maybe that's ok?
  2752. dfile.setown(queryDistributedFileDirectory().lookup(lfn,user,write)); // MORE - if dFile is not null then arguably exists should be true
  2753. Owned<IFile> file = getPartFile(0,0);
  2754. if (file.get())
  2755. {
  2756. fileExists = file->exists();
  2757. return fileExists || write;
  2758. }
  2759. }
  2760. }
  2761. if (!onlylocal)
  2762. {
  2763. if (lfn.isExternal())
  2764. {
  2765. Owned<IFileDescriptor> fDesc = createExternalFileDescriptor(lfn.get());
  2766. dfile.setown(queryDistributedFileDirectory().createExternal(fDesc, lfn.get()));
  2767. Owned<IFile> file = getPartFile(0,0);
  2768. if (file.get())
  2769. fileExists = file->exists();
  2770. if (write && lfn.isExternal()&&(dfile->numParts()==1)) // if it is writing to an external file then don't return distributed
  2771. dfile.clear();
  2772. return true;
  2773. }
  2774. else
  2775. {
  2776. dfile.setown(queryDistributedFileDirectory().lookup(lfn,user,write));
  2777. if (dfile.get())
  2778. return true;
  2779. }
  2780. // MORE - should we create the IDistributedFile here ready for publishing (and/or to make sure it's locked while we write)?
  2781. StringBuffer physicalPath;
  2782. makePhysicalPartName(lfn.get(), 1, 1, physicalPath, false); // more - may need to override path for roxie
  2783. localpath.set(physicalPath);
  2784. fileExists = (dfile != NULL);
  2785. return write;
  2786. }
  2787. return false;
  2788. }
  2789. IFileDescriptor *getFileDescriptor()
  2790. {
  2791. if (dfile.get())
  2792. return dfile->getFileDescriptor();
  2793. Owned<IFileDescriptor> fileDesc = createFileDescriptor();
  2794. StringBuffer dir;
  2795. if (localpath.isEmpty()) { // e.g. external file
  2796. StringBuffer tail;
  2797. IException *e=NULL;
  2798. bool iswin=
  2799. #ifdef _WIN32
  2800. true;
  2801. #else
  2802. false;
  2803. #endif
  2804. if (!lfn.getExternalPath(dir,tail,iswin,&e)) {
  2805. if (e)
  2806. throw e;
  2807. return NULL;
  2808. }
  2809. }
  2810. else
  2811. splitDirTail(localpath,dir);
  2812. fileDesc->setDefaultDir(dir.str());
  2813. RemoteFilename rfn;
  2814. getPartFilename(rfn,0,0);
  2815. fileDesc->setPart(0,rfn);
  2816. fileDesc->queryPartDiskMapping(0).defaultCopies = DFD_DefaultCopies;
  2817. return fileDesc.getClear();
  2818. }
  2819. bool getModificationTime(CDateTime &dt)
  2820. {
  2821. if (dfile.get())
  2822. return dfile->getModificationTime(dt);
  2823. Owned<IFile> file = getPartFile(0,0);
  2824. if (file.get()) {
  2825. CDateTime dt;
  2826. return file->getTime(NULL,&dt,NULL);
  2827. }
  2828. return false;
  2829. }
  2830. virtual unsigned numParts()
  2831. {
  2832. if (dfile.get())
  2833. return dfile->numParts();
  2834. return 1;
  2835. }
  2836. unsigned numPartCopies(unsigned partnum)
  2837. {
  2838. if (dfile.get())
  2839. return dfile->queryPart(partnum).numCopies();
  2840. return 1;
  2841. }
  2842. IFile *getPartFile(unsigned partnum,unsigned copy)
  2843. {
  2844. RemoteFilename rfn;
  2845. if ((partnum==0)&&(copy==0))
  2846. return createIFile(getPartFilename(rfn,partnum,copy));
  2847. return NULL;
  2848. }
  2849. RemoteFilename &getPartFilename(RemoteFilename &rfn, unsigned partnum,unsigned copy)
  2850. {
  2851. if (dfile.get())
  2852. dfile->queryPart(partnum).getFilename(rfn,copy);
  2853. else if (localpath.isEmpty())
  2854. lfn.getExternalFilename(rfn);
  2855. else
  2856. rfn.setRemotePath(localpath);
  2857. return rfn;
  2858. }
  2859. StringBuffer &getPartFilename(StringBuffer &path, unsigned partnum,unsigned copy)
  2860. {
  2861. RemoteFilename rfn;
  2862. if (dfile.get())
  2863. dfile->queryPart(partnum).getFilename(rfn,copy);
  2864. else if (localpath.isEmpty())
  2865. lfn.getExternalFilename(rfn);
  2866. else
  2867. path.append(localpath);
  2868. if (rfn.isLocal())
  2869. rfn.getLocalPath(path);
  2870. else
  2871. rfn.getRemotePath(path);
  2872. return path;
  2873. }
  2874. bool getPartCrc(unsigned partnum, unsigned &crc)
  2875. {
  2876. if (dfile.get())
  2877. return dfile->queryPart(partnum).getCrc(crc);
  2878. Owned<IFile> file = getPartFile(0,0);
  2879. if (file.get()) {
  2880. crc = file->getCRC();
  2881. return true;
  2882. }
  2883. return false;
  2884. }
  2885. offset_t getPartFileSize(unsigned partnum)
  2886. {
  2887. if (dfile.get())
  2888. return dfile->queryPart(partnum).getFileSize(true,false);
  2889. Owned<IFile> file = getPartFile(0,0);
  2890. if (file.get())
  2891. return file->size();
  2892. return (offset_t)-1;
  2893. }
  2894. offset_t getFileSize()
  2895. {
  2896. if (dfile.get())
  2897. dfile->getFileSize(true,false);
  2898. offset_t ret = 0;
  2899. unsigned np = numParts();
  2900. for (unsigned i = 0;i<np;i++)
  2901. ret += getPartFileSize(i);
  2902. return ret;
  2903. }
  2904. virtual bool exists() const
  2905. {
  2906. return fileExists;
  2907. }
  2908. virtual bool isExternal() const
  2909. {
  2910. return lfn.isExternal();
  2911. }
  2912. };
  2913. ILocalOrDistributedFile* createLocalOrDistributedFile(const char *fname,IUserDescriptor *user,bool onlylocal,bool onlydfs, bool iswrite)
  2914. {
  2915. Owned<CLocalOrDistributedFile> ret = new CLocalOrDistributedFile();
  2916. if (ret->init(fname,user,onlylocal,onlydfs,iswrite))
  2917. return ret.getClear();
  2918. return NULL;
  2919. }
  2920. static bool transactionLoggingOn=false;
  2921. const bool &queryTransactionLogging() { return transactionLoggingOn; }
  2922. bool traceAllTransactions(bool on)
  2923. {
  2924. bool ret = transactionLoggingOn;
  2925. transactionLoggingOn = on;
  2926. return ret;
  2927. }
  2928. class CLockInfo : public CSimpleInterfaceOf<ILockInfo>
  2929. {
  2930. StringAttr xpath;
  2931. SafePointerArrayOf<CLockMetaData> ldInfo;
  2932. public:
  2933. CLockInfo(MemoryBuffer &mb)
  2934. {
  2935. mb.read(xpath);
  2936. unsigned count;
  2937. mb.read(count);
  2938. if (count)
  2939. {
  2940. ldInfo.ensure(count);
  2941. for (unsigned c=0; c<count; c++)
  2942. ldInfo.append(new CLockMetaData(mb));
  2943. }
  2944. }
  2945. CLockInfo(const char *_xpath, const ConnectionInfoMap &map) : xpath(_xpath)
  2946. {
  2947. HashIterator iter(map);
  2948. ForEach(iter)
  2949. {
  2950. IMapping &imap = iter.query();
  2951. LockData *lD = map.mapToValue(&imap);
  2952. ConnectionId connId = * ((ConnectionId *) imap.getKey());
  2953. ldInfo.append(new CLockMetaData(*lD, connId));
  2954. }
  2955. }
  2956. // ILockInfo impl.
  2957. virtual const char *queryXPath() const { return xpath; }
  2958. virtual unsigned queryConnections() const { return ldInfo.ordinality(); }
  2959. virtual CLockMetaData &queryLockData(unsigned lock) const
  2960. {
  2961. return *ldInfo.item(lock);
  2962. }
  2963. virtual void prune(const char *ipPattern)
  2964. {
  2965. StringBuffer ipStr;
  2966. ForEachItemInRev(c, ldInfo)
  2967. {
  2968. CLockMetaData &lD = *ldInfo.item(c);
  2969. SocketEndpoint ep(lD.queryEp());
  2970. ep.getIpText(ipStr.clear());
  2971. if (!WildMatch(ipStr, ipPattern))
  2972. ldInfo.zap(&lD);
  2973. }
  2974. }
  2975. virtual void serialize(MemoryBuffer &mb) const
  2976. {
  2977. mb.append(xpath);
  2978. mb.append(ldInfo.ordinality());
  2979. ForEachItemIn(c, ldInfo)
  2980. ldInfo.item(c)->serialize(mb);
  2981. }
  2982. virtual StringBuffer &toString(StringBuffer &out, unsigned format, bool header, const char *altText) const
  2983. {
  2984. if (ldInfo.ordinality())
  2985. {
  2986. unsigned msNow = msTick();
  2987. CDateTime time;
  2988. time.setNow();
  2989. time_t timeSimple = time.getSimple();
  2990. if (header)
  2991. {
  2992. switch (format)
  2993. {
  2994. case 0: // internal
  2995. {
  2996. out.append("Locks on path: ").append(altText ? altText : xpath.get()).newline();
  2997. out.append("Endpoint |SessionId |ConnectionId |mode |time(duration)]").newline();
  2998. break;
  2999. }
  3000. case 1: // daliadmin
  3001. {
  3002. out.appendf("Server IP |Session |Connection |Mode |Time |Duration |Lock").newline();
  3003. out.appendf("====================|================|==============|========|====================|============|======").newline();
  3004. break;
  3005. }
  3006. default:
  3007. throwUnexpected();
  3008. }
  3009. }
  3010. CDateTime timeLocked;
  3011. StringBuffer timeStr;
  3012. unsigned c = 0;
  3013. loop
  3014. {
  3015. CLockMetaData &lD = *ldInfo.item(c);
  3016. unsigned lockedFor = msNow-lD.timeLockObtained;
  3017. time_t tt = timeSimple - (lockedFor/1000);
  3018. timeLocked.set(tt);
  3019. timeLocked.getString(timeStr.clear());
  3020. switch (format)
  3021. {
  3022. case 0: // internal
  3023. out.appendf("%-20s|%-16" I64F "x|%-16" I64F "x|%-8x|%s(%d ms)", lD.queryEp(), lD.sessId, lD.connectionId, lD.mode, timeStr.str(), lockedFor);
  3024. break;
  3025. case 1: // daliadmin
  3026. out.appendf("%-20s|%-16" I64F "x|%-16" I64F "x|%-8x|%-20s|%-12d|%s", lD.queryEp(), lD.sessId, lD.connectionId, lD.mode, timeStr.str(), lockedFor, altText ? altText : xpath.get());
  3027. break;
  3028. default:
  3029. throwUnexpected();
  3030. }
  3031. ++c;
  3032. if (c>=ldInfo.ordinality())
  3033. break;
  3034. out.newline();
  3035. }
  3036. }
  3037. return out;
  3038. }
  3039. };
  3040. ILockInfo *createLockInfo(const char *xpath, const ConnectionInfoMap &map)
  3041. {
  3042. return new CLockInfo(xpath, map);
  3043. }
  3044. ILockInfo *deserializeLockInfo(MemoryBuffer &mb)
  3045. {
  3046. return new CLockInfo(mb);
  3047. }
  3048. class CLockInfoCollection : public CSimpleInterfaceOf<ILockInfoCollection>
  3049. {
  3050. CLockInfoArray locks;
  3051. public:
  3052. CLockInfoCollection() { }
  3053. CLockInfoCollection(MemoryBuffer &mb)
  3054. {
  3055. unsigned lockCount;
  3056. mb.read(lockCount);
  3057. for (unsigned l=0; l<lockCount; l++)
  3058. {
  3059. Owned<ILockInfo> lockInfo = deserializeLockInfo(mb);
  3060. locks.append(* lockInfo.getClear());
  3061. }
  3062. }
  3063. // ILockInfoCollection impl.
  3064. virtual unsigned queryLocks() const { return locks.ordinality(); }
  3065. virtual ILockInfo &queryLock(unsigned lock) const { return locks.item(lock); }
  3066. virtual void serialize(MemoryBuffer &mb) const
  3067. {
  3068. mb.append(locks.ordinality());
  3069. ForEachItemIn(l, locks)
  3070. locks.item(l).serialize(mb);
  3071. }
  3072. virtual StringBuffer &toString(StringBuffer &out) const
  3073. {
  3074. if (0 == locks.ordinality())
  3075. out.append("No current locks").newline();
  3076. else
  3077. {
  3078. ForEachItemIn(l, locks)
  3079. locks.item(l).toString(out, 0, true).newline();
  3080. }
  3081. return out;
  3082. }
  3083. virtual void add(ILockInfo &lock) { locks.append(lock); }
  3084. };
  3085. ILockInfoCollection *createLockInfoCollection()
  3086. {
  3087. return new CLockInfoCollection();
  3088. }
  3089. ILockInfoCollection *deserializeLockInfoCollection(MemoryBuffer &mb)
  3090. {
  3091. return new CLockInfoCollection(mb);
  3092. }