jfile.cpp 174 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532
  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. #ifdef _WIN32
  14. #define _WIN32_WINNT 0x0500 // for CreateHardLink
  15. #include <errno.h>
  16. //#include <winsock.h> // for TransmitFile
  17. #endif
  18. #include "platform.h"
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21. #include <algorithm>
  22. #if defined (__linux__)
  23. #include <time.h>
  24. #include <dirent.h>
  25. #include <utime.h>
  26. #include <sys/vfs.h>
  27. #include <sys/mman.h>
  28. #include <sys/sendfile.h>
  29. #endif
  30. #include "time.h"
  31. #include "jerror.hpp"
  32. #include "jlib.hpp"
  33. #include "jio.hpp"
  34. #include "jmisc.hpp"
  35. #include "jsort.hpp"
  36. #include "jmutex.hpp"
  37. #include "jfile.hpp"
  38. #include "jfile.ipp"
  39. #include <limits.h>
  40. #include "jexcept.hpp"
  41. #include "jsocket.hpp" // for IpAddress
  42. #include "jregexp.hpp"
  43. #include "portlist.h"
  44. // #define REMOTE_DISCONNECT_ON_DESTRUCTOR // enable to disconnect on IFile destructor
  45. // this should not be enabled in WindowRemoteDirectory used
  46. #ifdef _DEBUG
  47. #define ASSERTEX(e) assertex(e);
  48. #else
  49. #define ASSERTEX(e)
  50. #endif
  51. #ifdef __64BIT__
  52. #define DEFAULT_STREAM_BUFFER_SIZE 0x100000
  53. #else
  54. // Restrict buffer sizes on 32-bit systems
  55. #define DEFAULT_STREAM_BUFFER_SIZE 0x10000
  56. #endif
  57. #ifdef _WIN32
  58. #define NULLFILE INVALID_HANDLE_VALUE
  59. #else
  60. #define NULLFILE -1
  61. #endif
  62. static IFile *createIFileByHook(const RemoteFilename & filename);
  63. static IFile *createContainedIFileByHook(const char *filename);
  64. static char ShareChar='$';
  65. bool isShareChar(char c)
  66. {
  67. return (c==ShareChar)||(c=='$');
  68. }
  69. char getShareChar()
  70. {
  71. return ShareChar;
  72. }
  73. void setShareChar(char c)
  74. {
  75. ShareChar = c;
  76. }
  77. StringBuffer &setPathDrive(StringBuffer &filename,unsigned drvnum)
  78. {
  79. return swapPathDrive(filename,(unsigned)-1,drvnum);
  80. }
  81. unsigned getPathDrive(const char *s)
  82. {
  83. char c = *s;
  84. if (isPathSepChar(c)&&(s[1]==c)) {
  85. s = strchr(s+2,c);
  86. if (!s)
  87. return 0;
  88. }
  89. if (isPathSepChar(c))
  90. s++;
  91. if (*s&&((s[1]==':')||(isShareChar(s[1]))))
  92. return *s-'c';
  93. return 0;
  94. }
  95. StringBuffer &swapPathDrive(StringBuffer &filename,unsigned fromdrvnum,unsigned todrvnum,const char *frommask,const char *tomask)
  96. {
  97. const char *s = filename.str();
  98. char c = *s;
  99. if (isPathSepChar(c)&&(s[1]==c)) {
  100. s = strchr(s+2,c);
  101. if (!s)
  102. return filename;
  103. }
  104. if (isPathSepChar(c))
  105. s++;
  106. if (*s&&((fromdrvnum==(unsigned)-1)||(*s==(fromdrvnum+'c')))&&((s[1]==':')||(isShareChar(s[1]))))
  107. filename.setCharAt((size32_t)(s-filename.str()),todrvnum+'c');
  108. else if (frommask&&*frommask) { // OSS
  109. StringBuffer tmp;
  110. if (replaceConfigurationDirectoryEntry(filename.str(),frommask,tomask, tmp))
  111. tmp.swapWith(filename);
  112. }
  113. return filename;
  114. }
  115. const char *pathTail(const char *path)
  116. {
  117. if (!path)
  118. return NULL;
  119. const char *tail=path;
  120. const char *s = path;
  121. while (*s)
  122. if (isPathSepChar(*(s++)))
  123. tail = s;
  124. return tail;
  125. }
  126. const char * pathExtension(const char * path)
  127. {
  128. const char * tail = pathTail(path);
  129. if (tail)
  130. return strrchr(tail, '.');
  131. return NULL;
  132. }
  133. bool checkFileExists(const char * filename)
  134. {
  135. #ifdef _WIN32
  136. for (unsigned i=0;i<10;i++) {
  137. DWORD ret = (DWORD)GetFileAttributes(filename);
  138. if (ret!=(DWORD)-1)
  139. return true;
  140. DWORD err = GetLastError();
  141. if (err!=ERROR_IO_PENDING)
  142. break;
  143. Sleep(100*i);
  144. }
  145. return false;
  146. #else
  147. struct stat info;
  148. return (stat(filename, &info) == 0);
  149. #endif
  150. }
  151. bool checkDirExists(const char * filename)
  152. {
  153. #ifdef _WIN32
  154. DWORD attr = GetFileAttributes(filename);
  155. return (attr != (DWORD)-1)&&(attr & FILE_ATTRIBUTE_DIRECTORY);
  156. #else
  157. struct stat info;
  158. if (stat(filename, &info) != 0)
  159. return false;
  160. return S_ISDIR(info.st_mode);
  161. #endif
  162. }
  163. static void set_inherit(HANDLE handle, bool inherit)
  164. {
  165. #ifndef _WIN32
  166. long flag = fcntl(handle, F_GETFD);
  167. if(inherit)
  168. flag &= ~FD_CLOEXEC;
  169. else
  170. flag |= FD_CLOEXEC;
  171. fcntl(handle, F_SETFD, flag);
  172. #endif
  173. }
  174. static StringBuffer &getLocalOrRemoteName(StringBuffer &name,const RemoteFilename & filename)
  175. {
  176. if (filename.isLocal()&&!filename.isNull()) {
  177. filename.getLocalPath(name);
  178. #ifdef _WIN32
  179. // kludge to allow local linux paths on windows
  180. const char *n=name.str();
  181. if (n[0]=='/') {
  182. StringBuffer tmp;
  183. if (isShareChar(n[2])) {
  184. tmp.append(n[1]).append(':');
  185. n+=3;
  186. }
  187. while (*n) {
  188. if (*n=='/')
  189. tmp.append('\\');
  190. else
  191. tmp.append(*n);
  192. n++;
  193. }
  194. name.clear().append(tmp);
  195. }
  196. #endif
  197. }
  198. else
  199. filename.getRemotePath(name);
  200. return name;
  201. }
  202. CFile::CFile(const char * _filename)
  203. {
  204. filename.set(_filename);
  205. flags = ((unsigned)IFSHread)|((S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)<<16);
  206. }
  207. void CFile::setCreateFlags(unsigned cflags)
  208. {
  209. flags |= (cflags<<16);
  210. }
  211. void CFile::setShareMode(IFSHmode shmode)
  212. {
  213. flags &= ~(IFSHfull|IFSHread);
  214. flags |= (unsigned)(shmode&(IFSHfull|IFSHread));
  215. }
  216. bool CFile::exists()
  217. {
  218. if (stdIoHandle(filename)>=0)
  219. return true;
  220. return checkFileExists(filename);
  221. }
  222. #ifdef _WIN32
  223. bool WindowsCreateDirectory(const char * path)
  224. {
  225. unsigned retry = 0;
  226. loop {
  227. if (CreateDirectory(path, NULL))
  228. return true;
  229. DWORD err = GetLastError();
  230. if ((err==ERROR_FILE_NOT_FOUND) || (err==ERROR_PATH_NOT_FOUND) || (err==ERROR_FILE_EXISTS) || (err==ERROR_CANNOT_MAKE))
  231. break;
  232. else if (err==ERROR_ALREADY_EXISTS) {
  233. DWORD attr = GetFileAttributes(path);
  234. if ((attr != -1)&&( attr & FILE_ATTRIBUTE_DIRECTORY))
  235. return true;
  236. return false;
  237. }
  238. if ((retry++==10)|| // some or all of the following can occur when the domain controller gets busy
  239. // retrying improves chance of success
  240. ((err!=ERROR_NETNAME_DELETED)&&(err!=ERROR_DEV_NOT_EXIST)&&(err!=ERROR_GEN_FAILURE)&&(err!=ERROR_NETWORK_BUSY)&&(err!=ERROR_BAD_NET_NAME)))
  241. throw MakeOsException(err,"WindowsCreateDirectory %s", path);
  242. //PROGLOG("Retrying(%d) WindowsCreateDirectory %s, err=%d",retry,filename,err);
  243. Sleep(retry*100);
  244. }
  245. return false;
  246. }
  247. #else
  248. bool LinuxCreateDirectory(const char * path)
  249. {
  250. if (!path)
  251. return false;
  252. if (CreateDirectory(path, NULL))
  253. return true;
  254. else
  255. {
  256. if (EEXIST == errno)
  257. {
  258. struct stat info;
  259. if (stat(path, &info) != 0)
  260. return false;
  261. return S_ISDIR(info.st_mode);
  262. }
  263. }
  264. return false;
  265. }
  266. #endif
  267. bool localCreateDirectory(const char *name)
  268. {
  269. if (!name)
  270. return false;
  271. size32_t l = (size32_t)strlen(name);
  272. if (l==0)
  273. return true;
  274. if (isPathSepChar(name[0])&&((l==1)||(isPathSepChar(name[1])&&!containsPathSepChar(name+2))))
  275. return true;
  276. #ifdef _WIN32
  277. if (name[1]==':') {
  278. if ((l==2)||((l==3)&&isPathSepChar(name[2])))
  279. return true;
  280. }
  281. #endif
  282. if (checkDirExists(name))
  283. return true;
  284. #ifdef _WIN32
  285. if (WindowsCreateDirectory(name))
  286. #else
  287. if (LinuxCreateDirectory(name))
  288. #endif
  289. return true;
  290. if (isPathSepChar(name[l-1])) l--;
  291. while (l&&!isPathSepChar(name[l-1]))
  292. l--;
  293. if (l<=1)
  294. return true;
  295. StringAttr parent(name,l-1);
  296. if (!localCreateDirectory(parent.get()))
  297. return false;
  298. #ifdef _WIN32
  299. return (WindowsCreateDirectory(name));
  300. #else
  301. return (LinuxCreateDirectory(name));
  302. #endif
  303. }
  304. bool CFile::createDirectory()
  305. {
  306. return localCreateDirectory(filename);
  307. }
  308. #ifdef _WIN32
  309. union TimeIntegerUnion
  310. {
  311. FILETIME ft;
  312. ULARGE_INTEGER l;
  313. };
  314. void FILETIMEtoIDateTime(CDateTime * target, const FILETIME & ft)
  315. {
  316. if (target)
  317. {
  318. TimeIntegerUnion u;
  319. memcpy(&u, &ft, sizeof(ft));
  320. unsigned __int64 hundredNanoseconds = (u.l.QuadPart % 10000000);
  321. u.l.QuadPart -= hundredNanoseconds;
  322. SYSTEMTIME systime;
  323. FileTimeToSystemTime(&u.ft, &systime);
  324. target->set(systime.wYear, systime.wMonth, systime.wDay, systime.wHour, systime.wMinute, systime.wSecond, (unsigned)(hundredNanoseconds*100));
  325. }
  326. }
  327. FILETIME * IDateTimetoFILETIME(FILETIME & ft, const CDateTime * dt)
  328. {
  329. if (!dt)
  330. return NULL;
  331. SYSTEMTIME systime;
  332. TimeIntegerUnion u;
  333. unsigned wYear, wMonth, wDay, wHour, wMinute, wSecond, nanoSeconds;
  334. dt->getDate(wYear, wMonth, wDay);
  335. dt->getTime(wHour, wMinute, wSecond, nanoSeconds);
  336. systime.wYear = wYear;
  337. systime.wMonth = wMonth;
  338. systime.wDay = wDay;
  339. systime.wHour = wHour;
  340. systime.wMinute = wMinute;
  341. systime.wSecond = wSecond;
  342. systime.wMilliseconds = 0;
  343. SystemTimeToFileTime(&systime, &u.ft);
  344. //Adjust the fractions of a second ourselves because the function above is only accurate to milli seconds.
  345. u.l.QuadPart += (nanoSeconds / 100);
  346. memcpy(&ft, &u, sizeof(ft));
  347. return &ft;
  348. }
  349. #endif
  350. bool CFile::getTime(CDateTime * createTime, CDateTime * modifiedTime, CDateTime * accessedTime)
  351. {
  352. #ifdef _WIN32
  353. //MORE could use GetFileAttributesEx() if we were allowed...
  354. FILETIME timeCreated, timeModified, timeAccessed;
  355. HANDLE handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  356. if (handle==(HANDLE)-1)
  357. return false;
  358. GetFileTime(handle, &timeCreated, &timeAccessed, &timeModified);
  359. CloseHandle(handle);
  360. FILETIMEtoIDateTime(createTime, timeCreated);
  361. FILETIMEtoIDateTime(modifiedTime, timeModified);
  362. FILETIMEtoIDateTime(accessedTime, timeAccessed);
  363. #else
  364. struct stat info;
  365. if (stat(filename, &info) != 0)
  366. return false;
  367. timetToIDateTime(accessedTime, info.st_atime);
  368. timetToIDateTime(createTime, info.st_ctime);
  369. timetToIDateTime(modifiedTime, info.st_mtime);
  370. #endif
  371. return true;
  372. }
  373. bool CFile::setTime(const CDateTime * createTime, const CDateTime * modifiedTime, const CDateTime * accessedTime)
  374. {
  375. #ifdef _WIN32
  376. FILETIME timeCreated, timeModified, timeAccessed;
  377. FILETIME *pTimeCreated, *pTimeModified, *pTimeAccessed;
  378. pTimeCreated = IDateTimetoFILETIME(timeCreated, createTime);
  379. pTimeModified = IDateTimetoFILETIME(timeModified, modifiedTime);
  380. pTimeAccessed = IDateTimetoFILETIME(timeAccessed, accessedTime);
  381. HANDLE handle = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  382. if (!handle)
  383. return false;
  384. SetFileTime(handle, pTimeCreated, pTimeAccessed, pTimeModified);
  385. CloseHandle(handle);
  386. return true;
  387. #elif defined (__linux__)
  388. struct utimbuf am;
  389. if (!accessedTime||!modifiedTime) {
  390. struct stat info;
  391. if (stat(filename, &info) != 0)
  392. return false;
  393. am.actime = info.st_atime;
  394. am.modtime = info.st_mtime;
  395. }
  396. if (accessedTime)
  397. am.actime = timetFromIDateTime (accessedTime);
  398. if (modifiedTime)
  399. am.modtime = timetFromIDateTime (modifiedTime);
  400. return (utime(filename, &am)==0);
  401. #else
  402. UNIMPLEMENTED;
  403. #endif
  404. }
  405. fileBool CFile::isDirectory()
  406. {
  407. #ifdef _WIN32
  408. DWORD attr = GetFileAttributes(filename);
  409. if (attr == -1)
  410. return notFound;
  411. return ( attr & FILE_ATTRIBUTE_DIRECTORY) ? foundYes : foundNo;
  412. #else
  413. struct stat info;
  414. if (stat(filename, &info) != 0)
  415. return notFound;
  416. return S_ISDIR(info.st_mode) ? foundYes : foundNo;
  417. #endif
  418. }
  419. fileBool CFile::isFile()
  420. {
  421. if (stdIoHandle(filename)>=0)
  422. return foundYes;
  423. #ifdef _WIN32
  424. DWORD attr = GetFileAttributes(filename);
  425. if (attr == -1)
  426. return notFound;
  427. return ( attr & FILE_ATTRIBUTE_DIRECTORY) ? foundNo : foundYes;
  428. #else
  429. struct stat info;
  430. if (stat(filename, &info) != 0)
  431. return notFound;
  432. return S_ISREG(info.st_mode) ? foundYes : foundNo;
  433. #endif
  434. }
  435. fileBool CFile::isReadOnly()
  436. {
  437. #ifdef _WIN32
  438. DWORD attr = GetFileAttributes(filename);
  439. if (attr == -1)
  440. return notFound;
  441. return ( attr & FILE_ATTRIBUTE_READONLY) ? foundYes : foundNo;
  442. #else
  443. struct stat info;
  444. if (stat(filename, &info) != 0)
  445. return notFound;
  446. //MORE: I think this is correct, but someone with better unix knowledge should check!
  447. return (info.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) ? foundNo : foundYes;
  448. #endif
  449. }
  450. #ifndef _WIN32
  451. static bool setShareLock(int fd,IFSHmode share)
  452. {
  453. struct flock fl;
  454. do {
  455. memset(&fl,0,sizeof(fl));
  456. if (share==IFSHnone)
  457. fl.l_type = F_WRLCK;
  458. else if (share==IFSHread)
  459. fl.l_type = F_RDLCK;
  460. else
  461. fl.l_type = F_UNLCK;
  462. fl.l_whence = SEEK_SET;
  463. fl.l_pid = getpid();
  464. if (fcntl(fd, F_SETLK, &fl) != -1)
  465. return true;
  466. } while (errno==EINTR);
  467. if ((errno!=EAGAIN)&&(errno!=EACCES))
  468. ERRLOG("setShareLock failure %d, mode %d", errno,(int)share);
  469. return (share==IFSHfull); // always return true for full
  470. }
  471. #endif
  472. HANDLE CFile::openHandle(IFOmode mode, IFSHmode sharemode, bool async, int stdh)
  473. {
  474. HANDLE handle = NULLFILE;
  475. #ifdef _WIN32
  476. if (stdh>=0) {
  477. DWORD mode;
  478. switch (stdh) {
  479. case 0: mode = STD_INPUT_HANDLE; break;
  480. case 1: mode = STD_OUTPUT_HANDLE; break;
  481. case 2: mode = STD_ERROR_HANDLE; break;
  482. default:
  483. return handle;
  484. }
  485. DuplicateHandle(GetCurrentProcess(), GetStdHandle(mode), GetCurrentProcess(), &handle , 0, FALSE, DUPLICATE_SAME_ACCESS);
  486. return handle;
  487. }
  488. DWORD share = 0;
  489. switch (sharemode) {
  490. case (IFSHfull|IFSHread):
  491. case IFSHfull:
  492. share |= FILE_SHARE_WRITE;
  493. // fall through
  494. case IFSHread:
  495. share |= FILE_SHARE_READ;
  496. }
  497. DWORD fflags = async?FILE_FLAG_OVERLAPPED:0;
  498. if (async&&(mode==IFOread))
  499. fflags |= FILE_FLAG_SEQUENTIAL_SCAN;
  500. switch (mode) {
  501. case IFOcreate:
  502. handle = CreateFile(filename, GENERIC_WRITE, share, NULL, CREATE_ALWAYS, fflags, NULL);
  503. break;
  504. case IFOread:
  505. handle = CreateFile(filename, GENERIC_READ, share, NULL, OPEN_EXISTING, fflags, NULL);
  506. break;
  507. case IFOwrite:
  508. handle = CreateFile(filename, GENERIC_WRITE, share, NULL, OPEN_ALWAYS, fflags, NULL);
  509. break;
  510. case IFOcreaterw:
  511. handle = CreateFile(filename, GENERIC_WRITE|GENERIC_READ, share, NULL, CREATE_ALWAYS, fflags, NULL);
  512. break;
  513. case IFOreadwrite:
  514. handle = CreateFile(filename, GENERIC_WRITE|GENERIC_READ, share, NULL, OPEN_ALWAYS, fflags, NULL);
  515. break;
  516. }
  517. if (handle == NULLFILE)
  518. {
  519. DWORD err = GetLastError();
  520. if ((IFOread!=mode) || ((err!=ERROR_FILE_NOT_FOUND) && (err!=ERROR_PATH_NOT_FOUND)))
  521. throw MakeOsException(err,"CFile::open %s (%x, %x)", filename.get(), mode, share);
  522. return NULLFILE;
  523. }
  524. #else
  525. if (stdh>=0)
  526. return (HANDLE)dup(stdh);
  527. unsigned fileflags = (flags>>16) & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
  528. unsigned openflags;
  529. switch (mode) {
  530. case IFOcreate:
  531. openflags = O_WRONLY | O_CREAT | O_TRUNC;
  532. break;
  533. case IFOread:
  534. openflags = O_RDONLY;
  535. break;
  536. case IFOwrite:
  537. openflags = O_WRONLY | O_CREAT;
  538. break;
  539. case IFOcreaterw:
  540. openflags = O_RDWR | O_CREAT | O_TRUNC;
  541. break;
  542. case IFOreadwrite:
  543. openflags = O_RDWR | O_CREAT;
  544. break;
  545. default:
  546. return NULLFILE;
  547. }
  548. handle = _lopen(filename.get(), openflags, fileflags);
  549. if (handle == -1)
  550. {
  551. if ((IFOread!=mode) || (errno != ENOENT))
  552. throw MakeErrnoException(errno, "CFile::open %s", filename.get());
  553. return NULLFILE;
  554. }
  555. // check not a directory (compatible with windows)
  556. struct stat info;
  557. if (fstat(handle, &info) == -1) {
  558. int err = errno;
  559. close(handle);
  560. handle = NULLFILE;
  561. throw MakeErrnoException(err, "CFile::open fstat %s", filename.get());
  562. }
  563. if (S_ISDIR(info.st_mode)) {
  564. close(handle);
  565. handle = NULLFILE;
  566. throw MakeErrnoException(EISDIR, "CFile::open %s", filename.get());
  567. }
  568. #endif
  569. return handle;
  570. }
  571. IFileIO * CFile::open(IFOmode mode)
  572. {
  573. // we may want mode dependant defaults later
  574. return openShared(mode,(IFSHmode)(flags&(IFSHfull|IFSHread)));
  575. }
  576. IFileAsyncIO * CFile::openAsync(IFOmode mode)
  577. {
  578. HANDLE handle = openHandle(mode,IFSHread,true); // I don't think we want shared write to an async file
  579. #ifndef _WIN32
  580. set_inherit(handle, false);
  581. #endif
  582. return new CFileAsyncIO(handle,IFSHread);
  583. }
  584. const char * CFile::queryFilename()
  585. {
  586. return filename;
  587. }
  588. bool CFile::remove()
  589. {
  590. #ifdef _WIN32
  591. unsigned retry = 0;
  592. loop {
  593. if (isDirectory()==foundYes) {
  594. if (RemoveDirectory(filename) != 0)
  595. return true;
  596. }
  597. else {
  598. if (DeleteFile(filename) != 0)
  599. return true;
  600. }
  601. DWORD err = GetLastError();
  602. if ( (err==ERROR_FILE_NOT_FOUND) || (err==ERROR_PATH_NOT_FOUND) )
  603. break;
  604. if ((retry++==10)|| // some or all of the following can occur when the domain controller gets busy
  605. // retrying improves chance of success
  606. ((err!=ERROR_NETNAME_DELETED)&&(err!=ERROR_DEV_NOT_EXIST)&&(err!=ERROR_GEN_FAILURE)&&(err!=ERROR_NETWORK_BUSY)&&(err!=ERROR_BAD_NET_NAME)))
  607. throw MakeOsException(err,"CFile::remove %s", filename.get());
  608. //PROGLOG("Retrying(%d) DeleteFile %s, err=%d",retry,filename,err);
  609. Sleep(retry*100);
  610. }
  611. return false;
  612. #else
  613. if (isDirectory()==foundYes) {
  614. if (rmdir(filename) == 0)
  615. return true;
  616. }
  617. else {
  618. if (unlink(filename) == 0)
  619. return true;
  620. }
  621. if (ENOENT!=errno)
  622. throw MakeErrnoException("CFile::remove %s", filename.get());
  623. return false;
  624. #endif
  625. }
  626. void CFile::rename(const char *newname)
  627. {
  628. // now hopefully newname is just file tail
  629. // however we do allow full paths and extract tail, warning if the directory appears to mismatch
  630. StringBuffer path;
  631. splitDirTail(filename,path);
  632. StringBuffer newdir;
  633. const char *tail = splitDirTail(newname,newdir);
  634. if (path.length()&&newdir.length()) {
  635. if (strcmp(newdir.str(),path.str())!=0)
  636. WARNLOG("CFile::rename '%s' to '%s' : directory mismatch",filename.get(),newname);
  637. }
  638. const char *dst = path.append(tail);
  639. if (isPathSepChar(dst[0])&&(dst[1]==dst[0])) { // hmm is share - convert to local path
  640. RemoteFilename rfn;
  641. rfn.setRemotePath(dst);
  642. if (rfn.isLocal())
  643. dst = rfn.getLocalPath(path.clear()).str();
  644. }
  645. if (-1 == ::rename(filename, dst))
  646. throw MakeErrnoException("CFile::rename(%s, %s)", filename.get(),dst);
  647. filename.set(path);
  648. }
  649. void CFile::move(const char *newname)
  650. {
  651. if (!newname||!*newname)
  652. return;
  653. StringBuffer path;
  654. if (isPathSepChar(newname[0])&&(newname[1]==newname[0])) { // hmm is share - convert to local path
  655. RemoteFilename rfn;
  656. rfn.setRemotePath(newname);
  657. if (rfn.isLocal())
  658. newname = rfn.getLocalPath(path.clear()).str();
  659. }
  660. #ifdef _WIN32
  661. unsigned retry = 0;
  662. loop {
  663. if (MoveFileEx(filename.get(),newname,0))
  664. return;
  665. DWORD err = GetLastError();
  666. if ((retry++==10)|| // some or all of the following can occur when the domain controller gets busy
  667. // retrying improves chance of success
  668. ((err!=ERROR_NETNAME_DELETED)&&(err!=ERROR_DEV_NOT_EXIST)&&(err!=ERROR_GEN_FAILURE)&&(err!=ERROR_NETWORK_BUSY)&&(err!=ERROR_BAD_NET_NAME)))
  669. throw MakeOsException(err,"CFile::move(%s, %s)", filename.get(), newname);
  670. Sleep(retry*100);
  671. }
  672. #else
  673. int ret=::rename(filename.get(),newname);
  674. if (ret==-1)
  675. throw MakeErrnoException("CFile::move(%s, %s)", filename.get(), newname);
  676. #endif
  677. filename.set(newname);
  678. }
  679. #ifdef _WIN32
  680. DWORD CALLBACK fastCopyProgressRoutine(
  681. LARGE_INTEGER TotalFileSize,
  682. LARGE_INTEGER TotalBytesTransferred,
  683. LARGE_INTEGER StreamSize,
  684. LARGE_INTEGER StreamBytesTransferred,
  685. DWORD dwStreamNumber,
  686. DWORD dwCallbackReason,
  687. HANDLE hSourceFile,
  688. HANDLE hDestinationFile,
  689. LPVOID lpData
  690. )
  691. {
  692. if (TotalBytesTransferred.QuadPart<=TotalFileSize.QuadPart)
  693. {
  694. CFPmode status = ((ICopyFileProgress *)lpData)->onProgress(TotalBytesTransferred.QuadPart,TotalFileSize.QuadPart);
  695. switch (status)
  696. {
  697. case CFPcontinue:
  698. return PROGRESS_CONTINUE;
  699. case CFPstop:
  700. return PROGRESS_STOP;
  701. default:
  702. return PROGRESS_CANCEL;
  703. }
  704. }
  705. return PROGRESS_CONTINUE;
  706. }
  707. #endif
  708. bool CFile::fastCopyFile(CFile &target, size32_t buffersize, ICopyFileProgress *progress)
  709. {
  710. // only currently supported for windows
  711. #ifdef _WIN32
  712. unsigned retry = 0;
  713. loop {
  714. BOOL cancel=FALSE;
  715. if (CopyFileEx(queryFilename(),target.queryFilename(),progress?fastCopyProgressRoutine:NULL,progress?progress:NULL,&cancel,0))
  716. break;
  717. DWORD err = GetLastError();
  718. if ( (err==ERROR_FILE_NOT_FOUND) || (err==ERROR_PATH_NOT_FOUND) )
  719. return false;
  720. if ((retry++==10)|| // some or all of the following can occur when the domain controller gets busy
  721. // retrying improves chance of success
  722. ((err!=ERROR_NETNAME_DELETED)&&(err!=ERROR_DEV_NOT_EXIST)&&(err!=ERROR_GEN_FAILURE)&&(err!=ERROR_NETWORK_BUSY)&&(err!=ERROR_BAD_NET_NAME)))
  723. return false;
  724. Sleep(retry*100);
  725. }
  726. return true;
  727. #else
  728. return false;
  729. #endif
  730. }
  731. void CFile::copySection(const RemoteFilename &dest, offset_t toOfs, offset_t fromOfs, offset_t size, ICopyFileProgress *progress)
  732. {
  733. // check to see if src and target are remote
  734. Owned<IFile> target = createIFile(dest);
  735. const size32_t buffersize = 0x100000;
  736. IFOmode omode = IFOwrite;
  737. if (toOfs==(offset_t)-1) {
  738. if (fromOfs==0) {
  739. copyFile(target,this,buffersize,progress);
  740. return;
  741. }
  742. omode = IFOcreate;
  743. toOfs = 0;
  744. }
  745. OwnedIFileIO targetIO = target->open(IFOwrite);
  746. if (!targetIO)
  747. throw MakeStringException(-1, "copyFile: target path '%s' could not be created", target->queryFilename());
  748. MemoryAttr mb;
  749. void * buffer = mb.allocate(buffersize);
  750. OwnedIFileIO sourceIO = open(IFOread);
  751. if (!sourceIO)
  752. throw MakeStringException(-1, "copySection: source '%s' not found", queryFilename());
  753. offset_t offset = 0;
  754. offset_t total;
  755. try
  756. {
  757. total = sourceIO->size();
  758. if (total<fromOfs)
  759. total = 0;
  760. else
  761. total -= fromOfs;
  762. if (total>size)
  763. total = size;
  764. while (offset<total)
  765. {
  766. size32_t got = sourceIO->read(fromOfs+offset, (buffersize>total-offset)?((size32_t)(total-offset)):buffersize, buffer);
  767. if (got == 0)
  768. break;
  769. targetIO->write(offset+toOfs, got, buffer);
  770. offset += got;
  771. if (progress && progress->onProgress(offset, total) != CFPcontinue)
  772. break;
  773. }
  774. }
  775. catch (IException *e)
  776. {
  777. StringBuffer s;
  778. s.append("copyFile target=").append(target->queryFilename()).append(" source=").append(queryFilename()).append("; read/write failure").append(": ");
  779. e->errorMessage(s);
  780. IException *e2 = MakeOsException(e->errorCode(), "%s", s.str());
  781. e->Release();
  782. throw e2;
  783. }
  784. }
  785. void CFile::copyTo(IFile *dest, size32_t buffersize, ICopyFileProgress *progress,bool usetmp)
  786. {
  787. doCopyFile(dest,this,buffersize,progress,NULL,usetmp);
  788. }
  789. void CFile::setReadOnly(bool ro)
  790. {
  791. #ifdef _WIN32
  792. if (!SetFileAttributes(filename, ro ? FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL))
  793. {
  794. DWORD err = GetLastError();
  795. if ( (err!=ERROR_FILE_NOT_FOUND) && (err!=ERROR_PATH_NOT_FOUND) )
  796. throw MakeOsException(err, "CFile::setReadOnly %s", filename.get());
  797. }
  798. #else
  799. struct stat info;
  800. if (stat(filename, &info) != 0)
  801. throw MakeErrnoException("CFile::setReadOnly() %s", filename.get());
  802. // not sure correct but consistant with isReadOnly
  803. if (ro)
  804. info.st_mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
  805. else
  806. info.st_mode |= (S_IWUSR|S_IWGRP|S_IWOTH);
  807. chmod(filename, info.st_mode);
  808. #endif
  809. }
  810. offset_t CFile::size()
  811. {
  812. if (stdIoHandle(filename)>=0)
  813. return 0; // dummy value
  814. #ifdef _WIN32
  815. WIN32_FILE_ATTRIBUTE_DATA info;
  816. if (GetFileAttributesEx(filename, GetFileExInfoStandard, &info) != 0) {
  817. LARGE_INTEGER x;
  818. x.LowPart = info.nFileSizeLow;
  819. x.HighPart = info.nFileSizeHigh;
  820. return (offset_t)x.QuadPart;
  821. }
  822. #else
  823. struct stat info;
  824. if (stat(filename, &info) == 0)
  825. return info.st_size;
  826. #endif
  827. #if 0
  828. // we could try opening but I don't think needed
  829. Owned<IFileIO>io = openShared(IFOread,IFSHfull);
  830. if (io)
  831. return io->size();
  832. #endif
  833. return (offset_t)-1;
  834. }
  835. bool CFile::setCompression(bool set)
  836. {
  837. #ifdef _WIN32
  838. DWORD attr=::GetFileAttributes(filename.get());
  839. if(attr==-1)
  840. throw MakeOsException(::GetLastError(), "CFile::setCompression %s", filename.get());
  841. if (((attr & FILE_ATTRIBUTE_COMPRESSED) != 0) == set)
  842. return true;
  843. HANDLE handle=::CreateFile(filename.get(),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
  844. if(handle==INVALID_HANDLE_VALUE)
  845. throw MakeOsException(::GetLastError(), "CFile::setCompression %s", filename.get());
  846. USHORT compression=set ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE;
  847. DWORD bytes;
  848. if(::DeviceIoControl(handle, FSCTL_SET_COMPRESSION, &compression, sizeof(compression), NULL, 0, &bytes, NULL))
  849. {
  850. ::CloseHandle(handle);
  851. return true;
  852. }
  853. DWORD err=::GetLastError();
  854. ::CloseHandle(handle);
  855. throw MakeOsException(err, "CFile::setCompression %s", filename.get());
  856. #else
  857. return false;
  858. #endif
  859. }
  860. offset_t CFile::compressedSize()
  861. {
  862. #ifdef _WIN32
  863. DWORD hi, lo=::GetCompressedFileSize(filename.get(),&hi), err;
  864. if(lo==INVALID_FILE_SIZE && (err=::GetLastError())!=NO_ERROR)
  865. {
  866. if ( (err!=ERROR_FILE_NOT_FOUND) && (err!=ERROR_PATH_NOT_FOUND) )
  867. throw MakeOsException(err,"CFile::compressedSize %s", filename.get());
  868. return -1;
  869. }
  870. return makeint64(hi,lo);
  871. #else
  872. return size();
  873. #endif
  874. }
  875. class CDiscretionaryFileLock: public CInterface, implements IDiscretionaryLock
  876. {
  877. bool locked;
  878. bool excllock;
  879. Linked<IFile> file;
  880. Linked<IFileIO> fileio;
  881. HANDLE handle;
  882. public:
  883. CDiscretionaryFileLock(IFile *_file)
  884. : file(_file)
  885. {
  886. excllock = false;
  887. locked = false;
  888. #ifdef _WIN32
  889. handle=::CreateFile(file->queryFilename(),GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
  890. if(handle==INVALID_HANDLE_VALUE) {
  891. handle = NULLFILE;
  892. throw MakeOsException(GetLastError(),"CDiscretionaryFileLock::openhandle %s", file->queryFilename());
  893. }
  894. #else
  895. handle = _lopen(file->queryFilename(), O_RDONLY, 0);
  896. if (handle == -1) {
  897. handle = NULLFILE;
  898. throw MakeErrnoException(errno, "CDiscretionaryFileLock::openhandle %s",file->queryFilename());
  899. }
  900. #endif
  901. }
  902. CDiscretionaryFileLock(IFileIO *_fileio)
  903. : fileio(_fileio)
  904. {
  905. excllock = false;
  906. locked = false;
  907. handle = NULLFILE;
  908. CFileIO *cfileio = QUERYINTERFACE(_fileio,CFileIO);
  909. if (cfileio)
  910. handle = cfileio->queryHandle();
  911. if (handle==NULLFILE)
  912. throw MakeStringException(-1,"CDiscretionaryFileLock - invalid parameter");
  913. }
  914. ~CDiscretionaryFileLock()
  915. {
  916. if (locked)
  917. unlock();
  918. if ((handle!=NULLFILE)&&!fileio.get())
  919. #ifdef _WIN32
  920. CloseHandle(handle);
  921. #else
  922. _lclose(handle);
  923. #endif
  924. }
  925. IMPLEMENT_IINTERFACE;
  926. virtual bool isLocked() { return (handle!=NULLFILE); }
  927. virtual bool isExclusiveLocked() { return excllock; }
  928. bool lock(bool exclusive=true, unsigned timeout=INFINITE)
  929. {
  930. if (locked) {
  931. if (exclusive) {
  932. if (excllock)
  933. return true;
  934. unlock(); // have to unlock - unfortunate as means no window-less change mode
  935. }
  936. else {
  937. if (!excllock)
  938. return true;
  939. unlock(); // have to unlock - unfortunate as means no window-less change mode
  940. }
  941. }
  942. unsigned interval = 1;
  943. unsigned start = msTick();
  944. loop {
  945. #ifdef _WIN32
  946. OVERLAPPED overlapped;
  947. memset(&overlapped,0,sizeof(overlapped));
  948. if (LockFileEx(handle,
  949. (exclusive?LOCKFILE_EXCLUSIVE_LOCK:0)|((timeout==INFINITE)?0:LOCKFILE_FAIL_IMMEDIATELY),
  950. 0,1,0,&overlapped))
  951. break;
  952. DWORD err = ::GetLastError();
  953. if (err!=ERROR_LOCK_VIOLATION)
  954. throw MakeOsException(err, "CDiscretionaryFileLock::lock");
  955. #else
  956. if (setShareLock(handle,exclusive?IFSHnone:IFSHread))
  957. break;
  958. #endif
  959. unsigned now = msTick();
  960. if (now-start>=timeout)
  961. return false;
  962. if (interval>timeout-now+start)
  963. interval = timeout-now+start;
  964. Sleep(interval);
  965. interval *= 2;
  966. if (interval>1000)
  967. interval = 1000;
  968. }
  969. excllock = exclusive;
  970. locked = true;
  971. return true;
  972. }
  973. void unlock()
  974. {
  975. if (handle!=NULLFILE) {
  976. #ifdef _WIN32
  977. OVERLAPPED overlapped;
  978. memset(&overlapped,0,sizeof(overlapped));
  979. if (!UnlockFileEx(handle,0,1,0,&overlapped))
  980. throw MakeOsException(::GetLastError(), "CDiscretionaryFileLock::unlockhandle");
  981. #else
  982. setShareLock(handle,IFSHfull);
  983. #endif
  984. }
  985. }
  986. };
  987. IDiscretionaryLock *createDiscretionaryLock(IFile *file)
  988. {
  989. return new CDiscretionaryFileLock(file);
  990. }
  991. IDiscretionaryLock *createDiscretionaryLock(IFileIO *fileio)
  992. {
  993. return new CDiscretionaryFileLock(fileio);
  994. }
  995. unsigned CFile::getCRC()
  996. {
  997. if (stdIoHandle(filename)>=0)
  998. return 0; // dummy value
  999. unsigned crc=~0;
  1000. MemoryAttr ma;
  1001. void *buf = ma.allocate(0x10000);
  1002. Owned<IFileIO> fileio = open(IFOread);
  1003. if (fileio) {
  1004. offset_t pos=0;
  1005. loop {
  1006. size32_t rd = fileio->read(pos,0x10000,buf);
  1007. if (!rd)
  1008. break;
  1009. crc=crc32((const char *)buf,rd,crc);
  1010. pos += rd;
  1011. }
  1012. }
  1013. return ~crc;
  1014. }
  1015. //---------------------------------------------------------------------------
  1016. static Linked<IPasswordProvider> passwordProvider;
  1017. MODULE_INIT(INIT_PRIORITY_JFILE)
  1018. {
  1019. return true;
  1020. }
  1021. MODULE_EXIT()
  1022. {
  1023. passwordProvider.clear();
  1024. }
  1025. static bool parseShare(const char *filename,IpAddress &machine,StringBuffer &share)
  1026. { // windows share parsing
  1027. if (!filename||!isPathSepChar(filename[0])||(filename[0]!=filename[1]))
  1028. return false;
  1029. const char *start = filename+2;
  1030. const char * end = start;
  1031. while (*end && !isPathSepChar(*end))
  1032. end++;
  1033. if (!*end)
  1034. return false;
  1035. StringBuffer ipText(end-start, start);
  1036. machine.ipset(ipText.str());
  1037. end++;
  1038. while (*end && !isPathSepChar(*end))
  1039. end++;
  1040. start = filename;
  1041. while (start!=end) {
  1042. if (*start=='/')
  1043. share.append('\\');
  1044. else
  1045. share.append(*start);
  1046. start++;
  1047. }
  1048. return true;
  1049. }
  1050. static CriticalSection connectcrit;
  1051. static bool connectToExternalDrive(const char * const filename)
  1052. {
  1053. CriticalBlock block(connectcrit);
  1054. if (!passwordProvider)
  1055. return false;
  1056. #ifdef _WIN32
  1057. StringBuffer share, username, password;
  1058. IpAddress ip;
  1059. if (!parseShare(filename,ip,share))
  1060. return false;
  1061. if (!passwordProvider->getPassword(ip, username, password))
  1062. return false;
  1063. // first see if connected
  1064. char buf[255];
  1065. buf[0] = 0;
  1066. DWORD len = sizeof(buf);
  1067. DWORD err = WNetGetUser(share.str(),buf,&len);
  1068. if ((err==0)&&(stricmp(username.str(),buf)==0)) {
  1069. // check can access share as well
  1070. for (unsigned i=0;i<10;i++) {
  1071. DWORD ret = (DWORD)GetFileAttributes(share.str());
  1072. DWORD err = (ret==(DWORD)-1)?GetLastError():0;
  1073. if (err!=ERROR_IO_INCOMPLETE)
  1074. break;
  1075. Sleep(100*i);
  1076. }
  1077. if (err==0)
  1078. return true;
  1079. }
  1080. for (unsigned retry=0;retry<5;retry++) {
  1081. NETRESOURCE res;
  1082. memset(&res, 0, sizeof(res));
  1083. res.dwType = RESOURCETYPE_DISK;
  1084. res.lpRemoteName = (char *)share.str();
  1085. err = WNetAddConnection2(&res, password.str(), username.str(), 0);
  1086. if (err) {
  1087. if (err==ERROR_SESSION_CREDENTIAL_CONFLICT) {
  1088. WNetCancelConnection2(res.lpRemoteName, 0, false);
  1089. err = WNetAddConnection2(&res, password.str(), username.str(), 0);
  1090. }
  1091. if (err)
  1092. ERRLOG("WNetAddConnection2(%d): connecting to %s, User: %s",err,res.lpRemoteName,username.str());
  1093. }
  1094. if (err==0)
  1095. return true;
  1096. Sleep(retry*100);
  1097. }
  1098. #endif
  1099. return false;
  1100. }
  1101. static void disconnectFromExternalDrive(const char * const filename)
  1102. {
  1103. #ifdef _WIN32
  1104. CriticalBlock block(connectcrit);
  1105. StringBuffer share;
  1106. IpAddress ip;
  1107. if (!parseShare(filename,ip,share))
  1108. return;
  1109. if (share.length()&&isShareChar(share.charAt(share.length())))
  1110. WNetCancelConnection2((char *)share.str(), 0, 0);
  1111. #else
  1112. //TODO: fill in for linux
  1113. #endif
  1114. }
  1115. #ifdef _WIN32
  1116. class CWindowsRemoteFile : public CInterface, implements IFile
  1117. {
  1118. IFile *ifile;
  1119. StringAttr filename;
  1120. bool connected;
  1121. inline bool connect()
  1122. {
  1123. if (!connected)
  1124. connected = connectToExternalDrive(filename);
  1125. return connected;
  1126. }
  1127. public:
  1128. CWindowsRemoteFile(const char * _filename)
  1129. : filename(_filename)
  1130. {
  1131. connected = false;
  1132. ifile = new CFile(_filename);
  1133. }
  1134. ~CWindowsRemoteFile()
  1135. {
  1136. ifile->Release();
  1137. #ifdef REMOTE_DISCONNECT_ON_DESTRUCTOR
  1138. if (connected)
  1139. disconnectFromExternalDrive(filename);
  1140. #endif
  1141. }
  1142. IMPLEMENT_IINTERFACE
  1143. virtual bool exists()
  1144. {
  1145. connect();
  1146. return ifile->exists();
  1147. }
  1148. virtual bool getTime(CDateTime * createTime, CDateTime * modifiedTime, CDateTime * accessedTime)
  1149. {
  1150. connect();
  1151. return ifile->getTime(createTime, modifiedTime, accessedTime);
  1152. }
  1153. virtual bool setTime(const CDateTime * createTime, const CDateTime * modifiedTime, const CDateTime * accessedTime)
  1154. {
  1155. connect();
  1156. if (ifile->setTime(createTime, modifiedTime, accessedTime))
  1157. return true;
  1158. if (connected||!connect())
  1159. return false;
  1160. return ifile->setTime(createTime, modifiedTime, accessedTime);
  1161. }
  1162. virtual fileBool isDirectory()
  1163. {
  1164. connect();
  1165. fileBool ok = ifile->isDirectory();
  1166. if (ok == notFound && !connected && connect())
  1167. ok = ifile->isDirectory();
  1168. return ok;
  1169. }
  1170. virtual fileBool isFile()
  1171. {
  1172. connect();
  1173. fileBool ok = ifile->isFile();
  1174. if (ok == notFound && !connected && connect())
  1175. ok = ifile->isFile();
  1176. return ok;
  1177. }
  1178. virtual fileBool isReadOnly()
  1179. {
  1180. connect();
  1181. fileBool ok = ifile->isReadOnly();
  1182. if (ok == notFound && !connected && connect())
  1183. ok = ifile->isFile();
  1184. return ok;
  1185. }
  1186. virtual IFileIO * open(IFOmode mode)
  1187. {
  1188. connect();
  1189. return ifile->open(mode);
  1190. }
  1191. virtual IFileIO * openShared(IFOmode mode,IFSHmode shared)
  1192. {
  1193. connect();
  1194. return ifile->openShared(mode,shared);
  1195. }
  1196. virtual IFileAsyncIO * openAsync(IFOmode mode)
  1197. {
  1198. connect();
  1199. return ifile->openAsync(mode);
  1200. }
  1201. virtual const char * queryFilename()
  1202. {
  1203. return ifile->queryFilename();
  1204. }
  1205. virtual bool remove()
  1206. {
  1207. connect();
  1208. unsigned attempt=0; \
  1209. return ifile->remove();
  1210. }
  1211. virtual void rename(const char *newname)
  1212. {
  1213. connect();
  1214. StringBuffer path;
  1215. splitDirTail(filename,path);
  1216. StringBuffer newdir;
  1217. const char *tail = splitDirTail(newname,newdir);
  1218. if ((newname[0]=='\\')&&(newname[1]=='\\')) { // rename to remote
  1219. if (path.length()&&newdir.length()) {
  1220. if (strcmp(newdir.str(),path.str())!=0)
  1221. WARNLOG("CWindowsRemoteFile '%s' to '%s' : directory mismatch",filename.get(),newname);
  1222. }
  1223. newname = tail; // just rename using tail
  1224. }
  1225. ifile->rename(newname);
  1226. path.append(tail);
  1227. filename.set(path);
  1228. }
  1229. virtual void move(const char *newName)
  1230. {
  1231. connect();
  1232. ifile->move(newName);
  1233. filename.set(ifile->queryFilename());
  1234. }
  1235. virtual void setReadOnly(bool ro)
  1236. {
  1237. connect();
  1238. ifile->setReadOnly(ro);
  1239. }
  1240. virtual offset_t size()
  1241. {
  1242. connect();
  1243. offset_t ret=ifile->size();
  1244. if ((ret==(offset_t)-1)&&!connected&&connect())
  1245. ret=ifile->size();
  1246. return ret;
  1247. }
  1248. virtual bool setCompression(bool set)
  1249. {
  1250. connect();
  1251. return ifile->setCompression(set);
  1252. }
  1253. virtual offset_t compressedSize()
  1254. {
  1255. connect();
  1256. return ifile->compressedSize();
  1257. }
  1258. bool fastCopyFile(CWindowsRemoteFile &target, size32_t buffersize, ICopyFileProgress *progress)
  1259. {
  1260. #ifdef _WIN32
  1261. CFile *src = QUERYINTERFACE(ifile,CFile);
  1262. if (!src)
  1263. return false;
  1264. CFile *dst = QUERYINTERFACE(target.ifile,CFile);
  1265. if (!dst)
  1266. return false;
  1267. target.isFile(); // encourage connect on target
  1268. connect();
  1269. return src->fastCopyFile(*dst, buffersize, progress);
  1270. #else
  1271. return false;
  1272. #endif
  1273. }
  1274. bool fastCopyFile(CFile &target, size32_t buffersize, ICopyFileProgress *progress)
  1275. {
  1276. #ifdef _WIN32
  1277. CFile *src = QUERYINTERFACE(ifile,CFile);
  1278. if (!src)
  1279. return false;
  1280. connect();
  1281. return src->fastCopyFile(target, buffersize, progress);
  1282. #else
  1283. return false;
  1284. #endif
  1285. }
  1286. bool fastCopyFileRev(CFile &src, size32_t buffersize, ICopyFileProgress *progress)
  1287. {
  1288. #ifdef _WIN32
  1289. CFile *dst = QUERYINTERFACE(ifile,CFile);
  1290. if (!dst)
  1291. return false;
  1292. connect();
  1293. return src.fastCopyFile(*dst, buffersize, progress);
  1294. #else
  1295. return false;
  1296. #endif
  1297. }
  1298. void copyTo(IFile *dest, size32_t buffersize, ICopyFileProgress *progress, bool usetmp)
  1299. {
  1300. doCopyFile(dest,this,buffersize,progress,NULL,usetmp);
  1301. }
  1302. bool createDirectory()
  1303. {
  1304. connect();
  1305. return localCreateDirectory(filename);
  1306. }
  1307. IDirectoryIterator *directoryFiles(const char *mask,bool sub,bool includedirs)
  1308. {
  1309. connect();
  1310. return ifile->directoryFiles(mask,sub,includedirs);
  1311. }
  1312. IDirectoryDifferenceIterator *monitorDirectory(
  1313. IDirectoryIterator *prev=NULL, // in (NULL means use current as baseline)
  1314. const char *mask=NULL,
  1315. bool sub=false,
  1316. bool includedirs=false,
  1317. unsigned checkinterval=60*1000,
  1318. unsigned timeout=(unsigned)-1,
  1319. Semaphore *abortsem=NULL) // returns NULL if timed out or abortsem signalled
  1320. {
  1321. connect();
  1322. return ifile->monitorDirectory(prev,mask,sub,includedirs,checkinterval,timeout,abortsem);
  1323. }
  1324. bool getInfo(bool &isdir,offset_t &size,CDateTime &modtime)
  1325. {
  1326. connect();
  1327. return ifile->getInfo(isdir,size,modtime);
  1328. }
  1329. unsigned getCRC()
  1330. {
  1331. connect();
  1332. return ifile->getCRC();
  1333. }
  1334. void setCreateFlags(unsigned cflags)
  1335. {
  1336. ifile->setCreateFlags(cflags);
  1337. }
  1338. void setShareMode(IFSHmode shmode)
  1339. {
  1340. ifile->setShareMode(shmode);
  1341. }
  1342. void copySection(const RemoteFilename &dest, offset_t toOfs, offset_t fromOfs, offset_t size, ICopyFileProgress *progress)
  1343. {
  1344. connect();
  1345. ifile->copySection(dest,toOfs,fromOfs,size,progress);
  1346. }
  1347. IMemoryMappedFile *openMemoryMapped(offset_t ofs, memsize_t len, bool write)
  1348. {
  1349. throw MakeStringException(-1,"Remote file cannot be memory mapped");
  1350. return NULL;
  1351. }
  1352. void treeCopyTo(IFile *dest,IpSubNet &subnet,IpAddress &resfrom, bool usetmp)
  1353. {
  1354. // no special action for windows
  1355. GetHostIp(resfrom);
  1356. copyTo(dest,0x100000,NULL,usetmp);
  1357. }
  1358. };
  1359. #endif
  1360. IFileIO *_createIFileIO(const void *buffer, unsigned sz, bool readOnly)
  1361. {
  1362. class CMemoryBufferIO : public CInterface, implements IFileIO
  1363. {
  1364. MemoryBuffer mb;
  1365. void *buffer;
  1366. size32_t sz;
  1367. bool readOnly;
  1368. public:
  1369. IMPLEMENT_IINTERFACE;
  1370. CMemoryBufferIO(const void *_buffer, unsigned _sz, bool _readOnly) : readOnly(_readOnly)
  1371. {
  1372. // JCSMORE - should probably have copy as option
  1373. mb.append(_sz, _buffer);
  1374. buffer = (void *)mb.toByteArray();
  1375. sz = mb.length();
  1376. }
  1377. virtual size32_t read(offset_t pos, size32_t len, void * data)
  1378. {
  1379. if (pos>sz)
  1380. throw MakeStringException(-1, "CMemoryBufferIO: read beyond end of buffer pos=%"I64F"d, len=%d, buffer length=%d", pos, len, mb.length());
  1381. if (pos+len > sz)
  1382. len = (size32_t)(sz-pos);
  1383. memcpy(data, (byte *)buffer+pos, len);
  1384. return len;
  1385. }
  1386. virtual offset_t size() { return sz; }
  1387. virtual size32_t write(offset_t pos, size32_t len, const void * data)
  1388. {
  1389. assertex(!readOnly);
  1390. if (pos+len>sz)
  1391. throw MakeStringException(-1, "CMemoryBufferIO: UNIMPLEMENTED, writing beyond buffer, pos=%"I64F"d, len=%d, buffer length=%d", pos, len, mb.length());
  1392. memcpy((byte *)buffer+pos, data, len);
  1393. return len;
  1394. }
  1395. virtual void flush() {}
  1396. virtual void close() {}
  1397. virtual void setSize(offset_t size)
  1398. {
  1399. if (size > mb.length())
  1400. throw MakeStringException(-1, "CMemoryBufferIO: UNIMPLEMENTED, setting size %"I64F"d beyond end of buffer, buffer length=%d", size, mb.length());
  1401. mb.setLength((size32_t)size);
  1402. }
  1403. offset_t appendFile(IFile *file,offset_t pos,offset_t len)
  1404. {
  1405. if (!file)
  1406. return 0;
  1407. const size32_t buffsize = 0x10000;
  1408. void * buffer = mb.reserve(buffsize);
  1409. Owned<IFileIO> fileio = file->open(IFOread);
  1410. offset_t ret=0;
  1411. while (len) {
  1412. size32_t toread = (len>=buffsize)?buffsize:(size32_t)len;
  1413. size32_t read = fileio->read(pos,toread,buffer);
  1414. if (read<buffsize)
  1415. mb.setLength(mb.length()+read-buffsize);
  1416. if (read==0)
  1417. break;
  1418. pos += read;
  1419. len -= read;
  1420. ret += read;
  1421. }
  1422. return ret;
  1423. }
  1424. };
  1425. return new CMemoryBufferIO(buffer, sz, readOnly);
  1426. }
  1427. IFileIO * createIFileI(unsigned len, const void * buffer)
  1428. {
  1429. return _createIFileIO((void *)buffer, len, true);
  1430. }
  1431. IFileIO * createIFileIO(unsigned len, void * buffer)
  1432. {
  1433. return _createIFileIO(buffer, len, false);
  1434. }
  1435. IFileIO * createIFileIO(StringBuffer & buffer)
  1436. {
  1437. return _createIFileIO((void *)buffer.toCharArray(), buffer.length(), true);
  1438. }
  1439. IFileIO * createIFileIO(MemoryBuffer & buffer)
  1440. {
  1441. return _createIFileIO(buffer.toByteArray(), buffer.length(), false);
  1442. }
  1443. //---------------------------------------------------------------------------
  1444. class jlib_decl CSequentialFileIO : public CFileIO
  1445. {
  1446. offset_t pos;
  1447. void checkPos(const char *fn,offset_t _pos)
  1448. {
  1449. if (_pos!=pos)
  1450. throw MakeStringException(-1, "CSequentialFileIO %s out of sequence (%"I64F"d,%"I64F"d)",fn,pos,_pos);
  1451. }
  1452. public:
  1453. CSequentialFileIO(HANDLE h,IFSHmode _sharemode)
  1454. : CFileIO(h,_sharemode)
  1455. {
  1456. pos = 0;
  1457. }
  1458. ~CSequentialFileIO()
  1459. {
  1460. }
  1461. IMPLEMENT_IINTERFACE
  1462. size32_t read(offset_t _pos, size32_t len, void * data)
  1463. {
  1464. checkPos("read",_pos);
  1465. #ifdef _WIN32
  1466. // Can't use checked_read because don't have the c fileno for it
  1467. DWORD numRead;
  1468. if (ReadFile(file,data,len,&numRead,NULL) == 0) {
  1469. DWORD err = GetLastError();
  1470. if (err==ERROR_BROKEN_PIPE) // windows returns this at end of pipe
  1471. return 0;
  1472. throw MakeOsException(GetLastError(),"CSequentialFileIO::read");
  1473. }
  1474. size32_t ret = (size32_t)numRead;
  1475. #else
  1476. size32_t ret = checked_read(file, data, len);
  1477. #endif
  1478. pos += ret;
  1479. return ret;
  1480. }
  1481. virtual size32_t write(offset_t _pos, size32_t len, const void * data)
  1482. {
  1483. checkPos("write",_pos);
  1484. size32_t ret;
  1485. #ifdef _WIN32
  1486. DWORD numWritten;
  1487. if (!WriteFile(file,data,len,&numWritten,NULL))
  1488. throw MakeOsException(GetLastError(),"CSequentialFileIO::write");
  1489. if (numWritten != len)
  1490. throw MakeOsException(DISK_FULL_EXCEPTION_CODE,"CSequentialFileIO::write");
  1491. ret = (size32_t)numWritten;
  1492. #else
  1493. ret = ::write(file,data,len);
  1494. if (ret==(size32_t)-1) {
  1495. PrintStackReport();
  1496. ERRLOG("errno(%d): %"I64F"d %u",errno,pos,len);
  1497. throw MakeErrnoException(errno,"CFileIO::write");
  1498. }
  1499. if (ret<len)
  1500. throw MakeOsException(DISK_FULL_EXCEPTION_CODE,"CSequentialFileIO::write");
  1501. #endif
  1502. pos += ret;
  1503. return ret;
  1504. }
  1505. };
  1506. IFileIO * CFile::openShared(IFOmode mode,IFSHmode share)
  1507. {
  1508. int stdh = stdIoHandle(filename);
  1509. HANDLE handle = openHandle(mode,share,false, stdh);
  1510. if (handle==NULLFILE)
  1511. return NULL;
  1512. #ifndef _WIN32
  1513. set_inherit(handle, false);
  1514. #endif
  1515. if (stdh>=0)
  1516. return new CSequentialFileIO(handle,share);
  1517. return new CFileIO(handle,share);
  1518. }
  1519. //---------------------------------------------------------------------------
  1520. extern jlib_decl IFileIO *createIFileIO(HANDLE handle)
  1521. {
  1522. return new CFileIO(handle,IFSHfull);
  1523. }
  1524. offset_t CFileIO::appendFile(IFile *file,offset_t pos,offset_t len)
  1525. {
  1526. if (!file)
  1527. return 0;
  1528. CriticalBlock procedure(cs);
  1529. MemoryAttr mb;
  1530. const size32_t buffsize = 0x10000;
  1531. void * buffer = mb.allocate(buffsize);
  1532. Owned<IFileIO> fileio = file->open(IFOread);
  1533. offset_t ret=0;
  1534. offset_t outp = size();
  1535. while (len) {
  1536. size32_t toread = (len>=buffsize) ? buffsize : (size32_t)len;
  1537. size32_t read = fileio->read(pos,toread,buffer);
  1538. if (read==0)
  1539. break;
  1540. size32_t wr = write(outp,read,buffer);
  1541. pos += read;
  1542. outp += wr;
  1543. len -= read;
  1544. ret += wr;
  1545. if (wr!=read)
  1546. break;
  1547. }
  1548. return ret;
  1549. }
  1550. #ifdef _WIN32
  1551. //-- Windows implementation -------------------------------------------------
  1552. CFileIO::CFileIO(HANDLE handle, IFSHmode _sharemode)
  1553. {
  1554. assertex(handle != NULLFILE);
  1555. throwOnError = false;
  1556. file = handle;
  1557. sharemode = _sharemode;
  1558. }
  1559. CFileIO::~CFileIO()
  1560. {
  1561. try
  1562. {
  1563. //note this will not call the virtual close() if anyone ever derived from this class.
  1564. //the clean fix is to move this code to beforeDispose()
  1565. close();
  1566. }
  1567. catch (IException * e)
  1568. {
  1569. EXCLOG(e, "CFileIO::~CFileIO");
  1570. e->Release();
  1571. }
  1572. }
  1573. void CFileIO::close()
  1574. {
  1575. if (file != NULLFILE)
  1576. {
  1577. if (!CloseHandle(file))
  1578. throw MakeOsException(GetLastError(),"CFileIO::close");
  1579. }
  1580. file = NULLFILE;
  1581. }
  1582. void CFileIO::flush()
  1583. {
  1584. if (!FlushFileBuffers(file))
  1585. throw MakeOsException(GetLastError(),"CFileIO::flush");
  1586. }
  1587. offset_t CFileIO::size()
  1588. {
  1589. LARGE_INTEGER pos;
  1590. pos.LowPart = GetFileSize(file, (unsigned long *)&pos.HighPart);
  1591. if (pos.LowPart==-1) {
  1592. DWORD err = GetLastError();
  1593. if (err!=0)
  1594. throw MakeOsException(err,"CFileIO::size");
  1595. }
  1596. return pos.QuadPart;
  1597. }
  1598. size32_t CFileIO::read(offset_t pos, size32_t len, void * data)
  1599. {
  1600. CriticalBlock procedure(cs);
  1601. DWORD numRead;
  1602. setPos(pos);
  1603. if (ReadFile(file,data,len,&numRead,NULL) == 0)
  1604. throw MakeOsException(GetLastError(),"CFileIO::read");
  1605. return (size32_t)numRead;
  1606. }
  1607. void CFileIO::setPos(offset_t newPos)
  1608. {
  1609. LARGE_INTEGER tempPos;
  1610. tempPos.QuadPart = newPos;
  1611. tempPos.LowPart = SetFilePointer(file, tempPos.LowPart, &tempPos.HighPart, FILE_BEGIN);
  1612. }
  1613. size32_t CFileIO::write(offset_t pos, size32_t len, const void * data)
  1614. {
  1615. CriticalBlock procedure(cs);
  1616. DWORD numWritten;
  1617. setPos(pos);
  1618. if (!WriteFile(file,data,len,&numWritten,NULL))
  1619. throw MakeOsException(GetLastError(),"CFileIO::write");
  1620. if (numWritten != len)
  1621. throw MakeOsException(DISK_FULL_EXCEPTION_CODE,"CFileIO::write");
  1622. return (size32_t)numWritten;
  1623. }
  1624. void CFileIO::setSize(offset_t pos)
  1625. {
  1626. CriticalBlock procedure(cs);
  1627. setPos(pos);
  1628. if (!SetEndOfFile(file))
  1629. throw MakeOsException(GetLastError(), "CFileIO::setSize");
  1630. }
  1631. #else
  1632. //-- Unix implementation ----------------------------------------------------
  1633. // More errorno checking TBD
  1634. CFileIO::CFileIO(HANDLE handle, IFSHmode _sharemode)
  1635. {
  1636. assertex(handle != NULLFILE);
  1637. throwOnError = false;
  1638. file = handle;
  1639. sharemode = _sharemode;
  1640. }
  1641. CFileIO::~CFileIO()
  1642. {
  1643. try
  1644. {
  1645. close();
  1646. }
  1647. catch (IException * e)
  1648. {
  1649. EXCLOG(e, "CFileIO::~CFileIO");
  1650. e->Release();
  1651. }
  1652. }
  1653. void CFileIO::close()
  1654. {
  1655. if (file != NULLFILE) {
  1656. if (::close(file) < 0)
  1657. throw MakeErrnoException(errno,"CFileIO::close");
  1658. file=NULLFILE;
  1659. }
  1660. }
  1661. void CFileIO::flush()
  1662. {
  1663. CriticalBlock procedure(cs);
  1664. #ifdef F_FULLFSYNC
  1665. if (fcntl(file, F_FULLFSYNC) != 0)
  1666. #else
  1667. if (fdatasync(file) != 0)
  1668. #endif
  1669. throw MakeOsException(DISK_FULL_EXCEPTION_CODE,"CFileIO::flush");
  1670. }
  1671. offset_t CFileIO::size()
  1672. {
  1673. CriticalBlock procedure(cs);
  1674. offset_t savedPos = lseek(file,0,SEEK_CUR);
  1675. offset_t length = lseek(file,0,SEEK_END);
  1676. setPos(savedPos);
  1677. return length;
  1678. }
  1679. size32_t CFileIO::read(offset_t pos, size32_t len, void * data)
  1680. {
  1681. if (0==len) return 0;
  1682. return checked_pread(file, data, len, pos);
  1683. }
  1684. void CFileIO::setPos(offset_t newPos)
  1685. {
  1686. if (file != NULLFILE)
  1687. _llseek(file,newPos,SEEK_SET);
  1688. }
  1689. size32_t CFileIO::write(offset_t pos, size32_t len, const void * data)
  1690. {
  1691. size32_t ret = pwrite(file,data,len,pos);
  1692. if (ret==(size32_t)-1) {
  1693. throw MakeErrnoException(errno,"CFileIO::write");
  1694. }
  1695. if (ret<len)
  1696. throw MakeOsException(DISK_FULL_EXCEPTION_CODE,"CFileIO::write");
  1697. return ret;
  1698. }
  1699. void CFileIO::setSize(offset_t pos)
  1700. {
  1701. if (0 != ftruncate(file, pos))
  1702. throw MakeErrnoException(errno, "CFileIO::setSize");
  1703. }
  1704. #endif
  1705. //---------------------------------------------------------------------------
  1706. CFileRangeIO::CFileRangeIO(IFileIO * _io, offset_t _headerSize, offset_t _maxLength)
  1707. {
  1708. io.set(_io);
  1709. headerSize = _headerSize;
  1710. maxLength = _maxLength;
  1711. }
  1712. size32_t CFileRangeIO::read(offset_t pos, size32_t len, void * data)
  1713. {
  1714. if (pos + len > maxLength)
  1715. {
  1716. if (pos > maxLength)
  1717. pos = maxLength;
  1718. len = (size32_t)(maxLength - pos);
  1719. }
  1720. return io->read(pos+headerSize, len, data);
  1721. }
  1722. offset_t CFileRangeIO::size()
  1723. {
  1724. return maxLength;
  1725. }
  1726. size32_t CFileRangeIO::write(offset_t pos, size32_t len, const void * data)
  1727. {
  1728. if (pos + len > maxLength)
  1729. {
  1730. if (pos > maxLength)
  1731. pos = maxLength;
  1732. len = (size32_t)(maxLength - pos);
  1733. }
  1734. return io->write(pos+headerSize, len, data);
  1735. }
  1736. //--------------------------------------------------------------------------
  1737. CFileAsyncIO::~CFileAsyncIO()
  1738. {
  1739. try
  1740. {
  1741. close();
  1742. }
  1743. catch (IException * e)
  1744. {
  1745. EXCLOG(e, "CFileAsyncIO::~CFileAsyncIO");
  1746. e->Release();
  1747. }
  1748. }
  1749. void CFileAsyncIO::flush()
  1750. {
  1751. // wait for all outstanding results
  1752. CriticalBlock block(cs);
  1753. ForEachItemInRev(i,results) {
  1754. size32_t dummy;
  1755. results.item(i).getResult(dummy,true);
  1756. }
  1757. }
  1758. offset_t CFileAsyncIO::appendFile(IFile *file,offset_t pos,offset_t len)
  1759. {
  1760. // will implemented if needed
  1761. UNIMPLEMENTED;
  1762. }
  1763. #ifdef _WIN32
  1764. //-- Windows implementation -------------------------------------------------
  1765. class CFileAsyncResult: public CInterface, implements IFileAsyncResult
  1766. {
  1767. protected: friend class CFileAsyncIO;
  1768. OVERLAPPED overlapped;
  1769. DWORD value;
  1770. DWORD wrsize;
  1771. CFileAsyncIO *parent;
  1772. public:
  1773. IMPLEMENT_IINTERFACE;
  1774. CFileAsyncResult(offset_t offset,size32_t _wrsize)
  1775. {
  1776. parent = NULL;
  1777. memset(&overlapped,0,sizeof(overlapped));
  1778. overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1779. overlapped.Offset = (DWORD)offset;
  1780. overlapped.OffsetHigh = (DWORD)(offset>>32);
  1781. value = (size32_t)-1;
  1782. wrsize = _wrsize;
  1783. }
  1784. ~CFileAsyncResult()
  1785. {
  1786. size32_t dummy;
  1787. getResult(dummy,true);
  1788. CloseHandle(overlapped.hEvent);
  1789. }
  1790. bool getResult(size32_t &ret,bool wait)
  1791. {
  1792. if (value==(size32_t)-1) {
  1793. if (parent) {
  1794. if (GetOverlappedResult(parent->file,&overlapped,&value,wait)==0) {
  1795. int err = GetLastError();
  1796. if (err==ERROR_IO_INCOMPLETE)
  1797. return false;
  1798. if (err!=ERROR_HANDLE_EOF) {
  1799. CriticalBlock block(parent->cs);
  1800. parent->results.zap(*this,true); // don't delete as array does not own
  1801. parent = NULL;
  1802. throw MakeOsException(err,"CFileAsyncResult::getResult");
  1803. }
  1804. value = 0;
  1805. }
  1806. CriticalBlock block(parent->cs);
  1807. parent->results.zap(*this,true); // don't delete as array does not own
  1808. parent = NULL;
  1809. }
  1810. else
  1811. return false;
  1812. }
  1813. ret = value;
  1814. if (value<wrsize)
  1815. throw MakeOsException(DISK_FULL_EXCEPTION_CODE,"CFileAsyncResult::getResult");
  1816. return true;
  1817. }
  1818. };
  1819. CFileAsyncIO::CFileAsyncIO(HANDLE handle, IFSHmode _sharemode)
  1820. {
  1821. assertex(handle != NULLFILE);
  1822. throwOnError = false;
  1823. file = handle;
  1824. sharemode = _sharemode;
  1825. }
  1826. void CFileAsyncIO::close()
  1827. {
  1828. flush();
  1829. // wait for all outstanding results
  1830. if (file != NULLFILE)
  1831. {
  1832. if (!CloseHandle(file))
  1833. throw MakeOsException(GetLastError(),"CFileAsyncIO::close");
  1834. }
  1835. file = NULLFILE;
  1836. }
  1837. offset_t CFileAsyncIO::size()
  1838. {
  1839. LARGE_INTEGER pos;
  1840. pos.LowPart = GetFileSize(file, (unsigned long *)&pos.HighPart);
  1841. if (pos.LowPart==-1) {
  1842. DWORD err = GetLastError();
  1843. if (err!=0)
  1844. throw MakeOsException(GetLastError(),"CFileAsyncIO::size");
  1845. }
  1846. return pos.QuadPart;
  1847. }
  1848. size32_t CFileAsyncIO::read(offset_t pos, size32_t len, void * data)
  1849. {
  1850. Owned<IFileAsyncResult> res = readAsync(pos,len,data);
  1851. size32_t ret;
  1852. res->getResult(ret,true);
  1853. return ret;
  1854. }
  1855. size32_t CFileAsyncIO::write(offset_t pos, size32_t len, const void * data)
  1856. {
  1857. Owned<IFileAsyncResult> res = writeAsync(pos,len,data);
  1858. size32_t ret;
  1859. res->getResult(ret,true);
  1860. return ret;
  1861. }
  1862. void CFileAsyncIO::setSize(offset_t size)
  1863. {
  1864. LARGE_INTEGER tempPos;
  1865. tempPos.QuadPart = size;
  1866. tempPos.LowPart = SetFilePointer(file, tempPos.LowPart, &tempPos.HighPart, FILE_BEGIN);
  1867. if (!SetEndOfFile(file))
  1868. throw MakeOsException(GetLastError(), "CFileIO::setSize");
  1869. }
  1870. IFileAsyncResult *CFileAsyncIO::readAsync(offset_t pos, size32_t len, void * data)
  1871. {
  1872. CFileAsyncResult *res = new CFileAsyncResult(pos,0);
  1873. DWORD val;
  1874. if (ReadFile(file,data,len,&val,&res->overlapped) == 0) {
  1875. int err = GetLastError();
  1876. if (err == ERROR_HANDLE_EOF) { // bit weird
  1877. res->value = 0; // won't need to wait
  1878. }
  1879. else if (err == ERROR_IO_PENDING) {
  1880. CriticalBlock block(cs);
  1881. res->parent = this;
  1882. results.append(*res);
  1883. }
  1884. else
  1885. throw MakeOsException(GetLastError(),"CFileIO::readAsync");
  1886. }
  1887. else {
  1888. res->value = val; // won't need to wait
  1889. }
  1890. return res;
  1891. }
  1892. IFileAsyncResult *CFileAsyncIO::writeAsync(offset_t pos, size32_t len, const void * data)
  1893. {
  1894. CFileAsyncResult *res = new CFileAsyncResult(pos,len);
  1895. DWORD val;
  1896. if (WriteFile(file,data,len,&val,&res->overlapped) == 0) {
  1897. int err = GetLastError();
  1898. if (err != ERROR_IO_PENDING)
  1899. throw MakeOsException(GetLastError(),"CFileIO::writeAsync");
  1900. CriticalBlock block(cs);
  1901. res->parent = this;
  1902. results.append(*res);
  1903. }
  1904. else {
  1905. res->value = val; // wont need to wait
  1906. }
  1907. return res;
  1908. }
  1909. #else
  1910. //-- Unix implementation ----------------------------------------------------
  1911. class CFileAsyncResult: public CInterface, implements IFileAsyncResult
  1912. {
  1913. protected:
  1914. friend class CFileAsyncIO;
  1915. DWORD value;
  1916. DWORD wrsize;
  1917. aiocb cb;
  1918. public:
  1919. IMPLEMENT_IINTERFACE;
  1920. CFileAsyncResult(offset_t offset,size32_t _wrsize)
  1921. {
  1922. value = (size32_t)-1;
  1923. wrsize = _wrsize;
  1924. }
  1925. ~CFileAsyncResult()
  1926. {
  1927. size32_t dummy;
  1928. getResult(dummy,true);
  1929. }
  1930. bool getResult(size32_t &ret,bool wait)
  1931. {
  1932. if (value==(size32_t)-1) {
  1933. loop {
  1934. int aio_errno = aio_error(&cb);
  1935. if (aio_errno==ECANCELED)
  1936. return false;
  1937. if (aio_errno != EINPROGRESS)
  1938. {
  1939. if (aio_errno)
  1940. throw MakeErrnoException(aio_errno,"CFileAsyncResult::getResult");
  1941. value = aio_return(&cb);
  1942. if (value<wrsize)
  1943. throw MakeOsException(DISK_FULL_EXCEPTION_CODE,"CFileAsyncResult::getResult");
  1944. break;
  1945. }
  1946. if (!wait)
  1947. return false;
  1948. loop {
  1949. struct timespec timeout;
  1950. timeout.tv_sec = 60*60*24; // a long time
  1951. timeout.tv_nsec = 0;
  1952. aiocb * cb_list[1];
  1953. cb_list[0] = &cb;
  1954. int rc = aio_suspend(cb_list, 1, &timeout);
  1955. if ((rc == 0)||(errno != EAGAIN))
  1956. break;
  1957. if (rc==ECANCELED)
  1958. return false;
  1959. }
  1960. }
  1961. }
  1962. ret = value;
  1963. return true;
  1964. }
  1965. };
  1966. CFileAsyncIO::CFileAsyncIO(HANDLE handle, IFSHmode _sharemode)
  1967. {
  1968. assertex(handle != NULLFILE);
  1969. throwOnError = false;
  1970. file = handle;
  1971. sharemode = _sharemode;
  1972. }
  1973. void CFileAsyncIO::close()
  1974. {
  1975. if (file != NULLFILE) {
  1976. aio_cancel(file,NULL);
  1977. if (_lclose(file) < 0)
  1978. throw MakeErrnoException(errno, "CFileAsyncIO::close");
  1979. }
  1980. file=NULLFILE;
  1981. }
  1982. offset_t CFileAsyncIO::size()
  1983. {
  1984. CriticalBlock procedure(cs);
  1985. offset_t savedPos = _llseek(file,0,SEEK_CUR);
  1986. offset_t length = _llseek(file,0,SEEK_END);
  1987. _llseek(file, savedPos, SEEK_SET);
  1988. return length;
  1989. }
  1990. size32_t CFileAsyncIO::read(offset_t pos, size32_t len, void * data)
  1991. {
  1992. CriticalBlock procedure(cs);
  1993. _llseek(file,pos,SEEK_SET);
  1994. size32_t ret = _lread(file,data,len);
  1995. if (ret==(size32_t)-1)
  1996. throw MakeErrnoException(errno,"CFileAsyncIO::read");
  1997. return ret;
  1998. }
  1999. size32_t CFileAsyncIO::write(offset_t pos, size32_t len, const void * data)
  2000. {
  2001. CriticalBlock procedure(cs);
  2002. _llseek(file,pos,SEEK_SET);
  2003. size32_t ret = _lwrite(file,data,len);
  2004. if (ret==(size32_t)-1)
  2005. throw MakeErrnoException(errno,"CFileAsyncIO::write");
  2006. return ret;
  2007. }
  2008. void CFileAsyncIO::setSize(offset_t pos)
  2009. {
  2010. CriticalBlock procedure(cs);
  2011. if ((file != NULLFILE)&&(0 != ftruncate(file, pos)))
  2012. throw MakeErrnoException(errno, "CFileIO::setSize");
  2013. }
  2014. IFileAsyncResult *CFileAsyncIO::readAsync(offset_t pos, size32_t len, void * data)
  2015. {
  2016. CFileAsyncResult *res = new CFileAsyncResult(pos,0);
  2017. bzero( &(res->cb), sizeof (struct aiocb));
  2018. res->cb.aio_fildes = file;
  2019. res->cb.aio_offset = pos;
  2020. res->cb.aio_buf = data;
  2021. res->cb.aio_nbytes = len;
  2022. res->cb.aio_sigevent.sigev_notify = SIGEV_NONE;
  2023. int retval = aio_read(&(res->cb));
  2024. if (retval==-1)
  2025. throw MakeErrnoException(errno,"CFileAsyncIO::readAsync");
  2026. return res;
  2027. }
  2028. IFileAsyncResult *CFileAsyncIO::writeAsync(offset_t pos, size32_t len, const void * data)
  2029. {
  2030. CFileAsyncResult *res = new CFileAsyncResult(pos,len);
  2031. bzero( &(res->cb), sizeof (struct aiocb));
  2032. res->cb.aio_fildes = file;
  2033. res->cb.aio_offset = pos;
  2034. res->cb.aio_buf = (void*)data;
  2035. res->cb.aio_nbytes = len;
  2036. res->cb.aio_sigevent.sigev_signo = SIGUSR1;
  2037. res->cb.aio_sigevent.sigev_notify = SIGEV_NONE;
  2038. res->cb.aio_sigevent.sigev_value.sival_ptr = (void*)res;
  2039. int retval = aio_write(&(res->cb));
  2040. if (retval==-1)
  2041. throw MakeErrnoException(errno,"CFileAsyncIO::writeAsync");
  2042. return res;
  2043. }
  2044. #endif
  2045. //---------------------------------------------------------------------------
  2046. CFileIOStream::CFileIOStream(IFileIO * _io)
  2047. {
  2048. io.set(_io);
  2049. curOffset = 0;
  2050. }
  2051. void CFileIOStream::flush()
  2052. {
  2053. }
  2054. size32_t CFileIOStream::read(size32_t len, void * data)
  2055. {
  2056. size32_t numRead = io->read(curOffset, len, data);
  2057. curOffset += numRead;
  2058. return numRead;
  2059. }
  2060. void CFileIOStream::seek(offset_t pos, IFSmode origin)
  2061. {
  2062. switch (origin)
  2063. {
  2064. case IFScurrent:
  2065. curOffset += pos;
  2066. break;
  2067. case IFSend:
  2068. curOffset = io->size() + pos;
  2069. break;
  2070. case IFSbegin:
  2071. curOffset = pos;
  2072. break;
  2073. }
  2074. }
  2075. offset_t CFileIOStream::size()
  2076. {
  2077. return io->size();
  2078. }
  2079. offset_t CFileIOStream::tell()
  2080. {
  2081. return curOffset;
  2082. }
  2083. size32_t CFileIOStream::write(size32_t len, const void * data)
  2084. {
  2085. size32_t numWritten = io->write(curOffset, len, data);
  2086. curOffset += numWritten;
  2087. return numWritten;
  2088. }
  2089. //---------------------------------------------------------------------------
  2090. class CBufferedFileIOStreamBase : public CBufferedIOStreamBase, implements IFileIOStream
  2091. {
  2092. protected:
  2093. virtual offset_t directSize() = 0;
  2094. offset_t curOffset;
  2095. public:
  2096. IMPLEMENT_IINTERFACE;
  2097. CBufferedFileIOStreamBase(unsigned bufSize) : CBufferedIOStreamBase(bufSize), curOffset(0) { }
  2098. virtual void flush() { doflush(); }
  2099. void seek(offset_t pos, IFSmode origin)
  2100. {
  2101. offset_t newOffset = 0;
  2102. switch (origin)
  2103. {
  2104. case IFScurrent:
  2105. newOffset = tell() + pos;
  2106. break;
  2107. case IFSend:
  2108. newOffset = size() + pos;
  2109. break;
  2110. case IFSbegin:
  2111. newOffset = pos;
  2112. break;
  2113. default:
  2114. throwUnexpected();
  2115. }
  2116. if (reading)
  2117. {
  2118. // slightly weird but curoffset is end of buffer when reading
  2119. if ((newOffset >= curOffset-numInBuffer) && (newOffset <= curOffset))
  2120. {
  2121. curBufferOffset = (size32_t)(newOffset - (curOffset-numInBuffer));
  2122. return;
  2123. }
  2124. }
  2125. else
  2126. {
  2127. if ((newOffset >= curOffset) && (newOffset <= curOffset + numInBuffer))
  2128. {
  2129. curBufferOffset = (size32_t)(newOffset - curOffset);
  2130. return;
  2131. }
  2132. flush();
  2133. }
  2134. curOffset = newOffset;
  2135. numInBuffer = 0;
  2136. curBufferOffset = 0;
  2137. }
  2138. offset_t size()
  2139. {
  2140. offset_t curSize = directSize();
  2141. if (!reading)
  2142. curSize = std::max(curSize, curOffset + numInBuffer);
  2143. return curSize;
  2144. }
  2145. offset_t tell()
  2146. {
  2147. if (reading)
  2148. return curOffset - numInBuffer + curBufferOffset;
  2149. return curOffset + curBufferOffset;
  2150. }
  2151. size32_t read(size32_t len, void * data)
  2152. {
  2153. return CBufferedIOStreamBase::doread(len, data);
  2154. }
  2155. size32_t write(size32_t len, const void * data)
  2156. {
  2157. if (reading)
  2158. curOffset -= bytesRemaining();
  2159. return CBufferedIOStreamBase::dowrite(len, data);
  2160. }
  2161. };
  2162. class CBufferedFileIOStream : public CBufferedFileIOStreamBase
  2163. {
  2164. public:
  2165. CBufferedFileIOStream(IFileIO * _io, unsigned _bufferSize) : CBufferedFileIOStreamBase(_bufferSize), io(_io)
  2166. {
  2167. buffer = new byte[_bufferSize];
  2168. }
  2169. ~CBufferedFileIOStream()
  2170. {
  2171. flush();
  2172. delete [] buffer;
  2173. }
  2174. protected:
  2175. virtual void doflush()
  2176. {
  2177. if (!reading && numInBuffer)
  2178. {
  2179. try {
  2180. io->write(curOffset, numInBuffer, buffer);
  2181. }
  2182. catch (IException *) {
  2183. // if we get exception, clear buffer so doen't reoccur on destructor as well
  2184. numInBuffer = 0;
  2185. curBufferOffset = 0;
  2186. throw;
  2187. }
  2188. curOffset += curBufferOffset;
  2189. numInBuffer = 0;
  2190. curBufferOffset = 0;
  2191. }
  2192. }
  2193. virtual bool fillBuffer()
  2194. {
  2195. reading = true;
  2196. numInBuffer = io->read(curOffset, bufferSize, buffer);
  2197. curOffset += numInBuffer;
  2198. curBufferOffset = 0;
  2199. return numInBuffer!=0;
  2200. }
  2201. virtual size32_t directRead(size32_t len, void * data)
  2202. {
  2203. size32_t sz = io->read(curOffset,len,data);
  2204. curOffset += sz;
  2205. return sz;
  2206. }
  2207. virtual size32_t directWrite(size32_t len, const void * data)
  2208. {
  2209. size32_t sz = io->write(curOffset,len,data);
  2210. curOffset += sz;
  2211. return sz;
  2212. }
  2213. virtual offset_t directSize() { return io->size(); }
  2214. protected:
  2215. IFileIOAttr io;
  2216. };
  2217. //---------------------------------------------------------------------------
  2218. class CBufferedAsyncIOStream: public CBufferedFileIOStreamBase
  2219. {
  2220. Linked<IFileAsyncIO> io;
  2221. byte * blk1;
  2222. byte * blk2;
  2223. IFileAsyncResult *readasyncres;
  2224. IFileAsyncResult *writeasyncres;
  2225. bool readeof;
  2226. public:
  2227. IMPLEMENT_IINTERFACE
  2228. CBufferedAsyncIOStream(IFileAsyncIO * _io, size32_t _bufferSize)
  2229. : CBufferedFileIOStreamBase(_bufferSize/2), io(_io)
  2230. {
  2231. blk1 = new byte[bufferSize];
  2232. blk2 = new byte[bufferSize];
  2233. buffer = blk1;
  2234. readasyncres = NULL;
  2235. writeasyncres = NULL;
  2236. readeof = false;
  2237. minDirectSize = (size32_t)-1; // async always writes using buffer
  2238. }
  2239. ~CBufferedAsyncIOStream()
  2240. {
  2241. flush();
  2242. waitAsyncWrite();
  2243. waitAsyncRead();
  2244. delete [] blk1;
  2245. delete [] blk2;
  2246. }
  2247. void waitAsyncWrite()
  2248. {
  2249. if (writeasyncres) {
  2250. size32_t res;
  2251. writeasyncres->getResult(res,true);
  2252. writeasyncres->Release();
  2253. writeasyncres = NULL;
  2254. }
  2255. }
  2256. size32_t waitAsyncRead()
  2257. {
  2258. size32_t res = 0;
  2259. if (readasyncres) {
  2260. readasyncres->getResult(res,true);
  2261. readasyncres->Release();
  2262. readasyncres = NULL;
  2263. }
  2264. return res;
  2265. }
  2266. void primeAsyncRead(offset_t pos,size32_t size, void *dst)
  2267. {
  2268. assertex(!readasyncres);
  2269. readasyncres = io->readAsync(pos, size, dst);
  2270. }
  2271. void primeAsyncWrite(offset_t pos,size32_t size, const void *src)
  2272. {
  2273. assertex(!writeasyncres);
  2274. writeasyncres = io->writeAsync(pos, size, src);
  2275. }
  2276. // CBufferedFileIOStream overloads
  2277. virtual bool fillBuffer()
  2278. {
  2279. if (!reading) {
  2280. waitAsyncWrite();
  2281. reading = true;
  2282. }
  2283. if (readeof) {
  2284. numInBuffer = 0;
  2285. curBufferOffset = 0;
  2286. return false;
  2287. }
  2288. buffer=(buffer==blk1)?blk2:blk1;
  2289. if (readasyncres==NULL) // first time
  2290. primeAsyncRead(curOffset, bufferSize, buffer);
  2291. numInBuffer = waitAsyncRead();
  2292. curOffset += numInBuffer;
  2293. curBufferOffset = 0;
  2294. if (numInBuffer)
  2295. primeAsyncRead(curOffset, bufferSize, (buffer==blk1)?blk2:blk1);
  2296. else
  2297. readeof = true;
  2298. return !readeof;
  2299. }
  2300. virtual void doflush()
  2301. {
  2302. if (!reading && numInBuffer)
  2303. {
  2304. waitAsyncWrite();
  2305. primeAsyncWrite(curOffset, numInBuffer, buffer);
  2306. buffer=(buffer==blk1)?blk2:blk1;
  2307. curOffset += curBufferOffset;
  2308. numInBuffer = 0;
  2309. curBufferOffset = 0;
  2310. }
  2311. }
  2312. virtual size32_t directRead(size32_t len, void * data) { assertex(false); return 0; } // shouldn't get called
  2313. virtual size32_t directWrite(size32_t len, const void * data) { assertex(false); return 0; } // shouldn't get called
  2314. virtual offset_t directSize() { waitAsyncWrite(); return io->size(); }
  2315. };
  2316. //-- Helper routines --------------------------------------------------------
  2317. static inline size32_t doread(IFileIOStream * stream,void *dst, size32_t size)
  2318. {
  2319. size32_t toread=size;
  2320. while (toread)
  2321. {
  2322. int read = stream->read(toread, dst);
  2323. if (!read)
  2324. return size-toread;
  2325. toread -= read;
  2326. dst = (char *) dst + read;
  2327. }
  2328. return size;
  2329. }
  2330. CIOStreamReadWriteSeq::CIOStreamReadWriteSeq(IFileIOStream * _stream, offset_t _offset, size32_t _size)
  2331. {
  2332. stream.set(_stream);
  2333. // stream->setThrowOnError(true);
  2334. size = _size;
  2335. offset = _offset; // assumption that stream at correct location already
  2336. }
  2337. void CIOStreamReadWriteSeq::put(const void *src)
  2338. {
  2339. stream->write(size, src);
  2340. }
  2341. void CIOStreamReadWriteSeq::putn(const void *src, unsigned n)
  2342. {
  2343. stream->write(size*n, src);
  2344. }
  2345. void CIOStreamReadWriteSeq::flush()
  2346. {
  2347. stream->flush();
  2348. }
  2349. offset_t CIOStreamReadWriteSeq::getPosition()
  2350. {
  2351. return stream->tell();
  2352. }
  2353. bool CIOStreamReadWriteSeq::get(void *dst)
  2354. {
  2355. return doread(stream,dst,size)==size;
  2356. }
  2357. unsigned CIOStreamReadWriteSeq::getn(void *dst, unsigned n)
  2358. {
  2359. return doread(stream,dst,size*n)/size;
  2360. }
  2361. void CIOStreamReadWriteSeq::reset()
  2362. {
  2363. stream->seek(offset, IFSbegin);
  2364. }
  2365. //-- Helper routines --------------------------------------------------------
  2366. size32_t read(IFileIO * in, offset_t pos, size32_t len, MemoryBuffer & buffer)
  2367. {
  2368. const size32_t checkLengthLimit = 0x1000;
  2369. if (len >= checkLengthLimit)
  2370. {
  2371. //Don't allocate a stupid amount of memory....
  2372. offset_t fileLength = in->size();
  2373. if (pos > fileLength)
  2374. pos = fileLength;
  2375. if ((len == (size32_t)-1) || (pos + len > fileLength))
  2376. len = (size32_t)(fileLength - pos);
  2377. }
  2378. void * data = buffer.reserve(len);
  2379. size32_t lenRead = in->read(pos, len, data);
  2380. if (lenRead != len)
  2381. buffer.rewrite(buffer.length() - (len - lenRead));
  2382. return lenRead;
  2383. }
  2384. void copyFile(const char *target, const char *source, size32_t buffersize, ICopyFileProgress *progress)
  2385. {
  2386. OwnedIFile src = createIFile(source);
  2387. if (!src)
  2388. throw MakeStringException(-1, "copyFile: source '%s' not found", source);
  2389. OwnedIFile tgt = createIFile(target);
  2390. if (!tgt)
  2391. throw MakeStringException(-1, "copyFile: target path '%s' could not be created", target);
  2392. copyFile(tgt, src, buffersize,progress);
  2393. }
  2394. void copyFile(IFile * target, IFile * source, size32_t buffersize, ICopyFileProgress *progress)
  2395. {
  2396. source->copyTo(target,buffersize,progress);
  2397. }
  2398. void doCopyFile(IFile * target, IFile * source, size32_t buffersize, ICopyFileProgress *progress, ICopyFileIntercept *copyintercept, bool usetmp)
  2399. {
  2400. if (!buffersize)
  2401. buffersize = 0x100000;
  2402. #ifdef _WIN32
  2403. if (!usetmp) {
  2404. CFile *src = QUERYINTERFACE(source,CFile);
  2405. CFile *dst = QUERYINTERFACE(target,CFile);
  2406. if (src) {
  2407. if (dst) {
  2408. if (src->fastCopyFile(*dst, buffersize, progress))
  2409. return;
  2410. }
  2411. CWindowsRemoteFile *dst2 = QUERYINTERFACE(target,CWindowsRemoteFile);
  2412. if (dst2) {
  2413. if (dst2->fastCopyFileRev(*src, buffersize, progress))
  2414. return;
  2415. }
  2416. }
  2417. CWindowsRemoteFile *src2 = QUERYINTERFACE(source,CWindowsRemoteFile);
  2418. if (src2) {
  2419. if (dst) {
  2420. if (src2->fastCopyFile(*dst,buffersize, progress))
  2421. return;
  2422. }
  2423. CWindowsRemoteFile *dst2 = QUERYINTERFACE(target,CWindowsRemoteFile);
  2424. if (dst2) {
  2425. if (src2->fastCopyFile(*dst2, buffersize, progress))
  2426. return;
  2427. }
  2428. }
  2429. }
  2430. #endif
  2431. OwnedIFileIO sourceIO = source->open(IFOread);
  2432. if (!sourceIO)
  2433. throw MakeStringException(-1, "copyFile: source '%s' not found", source->queryFilename());
  2434. #ifdef __linux__
  2435. // this is not really needed in windows - if it is we will have to
  2436. // test the file extenstion - .exe, .bat
  2437. struct stat info;
  2438. if (stat(source->queryFilename(), &info) == 0) // cannot fail - exception would have been thrown above
  2439. target->setCreateFlags(info.st_mode&(S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH|S_IXUSR|S_IXGRP|S_IXOTH));
  2440. #endif
  2441. Owned<IFileIO> targetIO;
  2442. Owned<IFile> tmpfile;
  2443. IFile *dest;
  2444. if (usetmp) {
  2445. StringBuffer tmpname;
  2446. makeTempCopyName(tmpname,target->queryFilename());
  2447. tmpfile.setown(createIFile(tmpname.str()));
  2448. dest = tmpfile;
  2449. }
  2450. else
  2451. dest = target;
  2452. targetIO.setown(dest->open(IFOcreate));
  2453. if (!targetIO)
  2454. throw MakeStringException(-1, "copyFile: target path '%s' could not be created", dest->queryFilename());
  2455. MemoryAttr mb;
  2456. void * buffer = copyintercept?NULL:mb.allocate(buffersize);
  2457. offset_t offset = 0;
  2458. offset_t total = 0;
  2459. Owned<IException> exc;
  2460. try
  2461. {
  2462. if (progress)
  2463. total = sourceIO->size(); // only needed for progress
  2464. loop
  2465. {
  2466. size32_t got;
  2467. if (copyintercept) {
  2468. got = (size32_t)copyintercept->copy(sourceIO,targetIO,offset,buffersize);
  2469. if (got == 0)
  2470. break;
  2471. }
  2472. else {
  2473. got = sourceIO->read(offset, buffersize, buffer);
  2474. if (got == 0)
  2475. break;
  2476. targetIO->write(offset, got, buffer);
  2477. }
  2478. offset += got;
  2479. if (progress && progress->onProgress(offset, total) != CFPcontinue)
  2480. break;
  2481. }
  2482. targetIO.clear();
  2483. if (usetmp) {
  2484. StringAttr tail(pathTail(target->queryFilename()));
  2485. target->remove();
  2486. dest->rename(tail);
  2487. }
  2488. }
  2489. catch (IException *e)
  2490. {
  2491. // try to delete partial copy
  2492. StringBuffer s;
  2493. s.append("copyFile target=").append(dest->queryFilename()).append(" source=").append(source->queryFilename()).appendf("; read/write failure (%d): ",e->errorCode());
  2494. exc.setown(MakeStringException(e->errorCode(), "%s", s.str()));
  2495. e->Release();
  2496. EXCLOG(exc, "doCopyFile");
  2497. }
  2498. if (exc.get()) {
  2499. try {
  2500. sourceIO.clear();
  2501. }
  2502. catch (IException *e) {
  2503. EXCLOG(e, "doCopyFile closing source");
  2504. e->Release();
  2505. }
  2506. try {
  2507. targetIO.clear();
  2508. }
  2509. catch (IException *e) {
  2510. EXCLOG(e, "doCopyFile closing dest");
  2511. e->Release();
  2512. }
  2513. try {
  2514. dest->remove();
  2515. }
  2516. catch (IException *e) {
  2517. StringBuffer s;
  2518. EXCLOG(e, s.clear().append("Removing partial copy file: ").append(dest->queryFilename()).str());
  2519. e->Release();
  2520. }
  2521. throw exc.getClear();
  2522. }
  2523. CDateTime createTime, modifiedTime;
  2524. if (source->getTime(&createTime, &modifiedTime, NULL))
  2525. target->setTime(&createTime, &modifiedTime, NULL);
  2526. }
  2527. void makeTempCopyName(StringBuffer &tmpname,const char *destname)
  2528. {
  2529. // simple for the moment (maybe used uid later)
  2530. tmpname.append(destname).append("__");
  2531. #ifdef _WIN32
  2532. genUUID(tmpname,true);
  2533. #else
  2534. genUUID(tmpname);
  2535. #endif
  2536. tmpname.append(".tmp");
  2537. }
  2538. //---------------------------------------------------------------------------
  2539. #ifndef _WIN32
  2540. /// This code is dangerous - pass it arrays smaller than _MAX_xxx and it will write off the end even if you KNOW your strings would fit
  2541. // Should NOT use strncpy here
  2542. void _splitpath(const char *path, char *drive, char *dir, char *fname, char *ext)
  2543. {
  2544. strncpy(dir, path, (_MAX_DIR-1));
  2545. char* last = strrchr(dir, '.');
  2546. if (last != NULL)
  2547. {
  2548. if (strrchr(last, PATHSEPCHAR)==NULL)
  2549. {
  2550. strncpy(ext, last, (_MAX_EXT-1));
  2551. *last = '\0';
  2552. }
  2553. else
  2554. {
  2555. *ext = '\0';
  2556. }
  2557. }
  2558. else
  2559. {
  2560. *ext = '\0';
  2561. }
  2562. last = strrchr(dir, PATHSEPCHAR);
  2563. if (last != NULL)
  2564. {
  2565. strncpy(fname, ++last, (_MAX_FNAME-1));
  2566. *last = '\0';
  2567. }
  2568. else
  2569. {
  2570. strncpy(fname, dir, (_MAX_FNAME-1));
  2571. *dir = '\0';
  2572. }
  2573. *drive = '\0';
  2574. }
  2575. #endif
  2576. void splitFilename(const char * filename, StringBuffer * drive, StringBuffer * path, StringBuffer * tail, StringBuffer * ext, bool longExt)
  2577. {
  2578. char tdrive[_MAX_DRIVE];
  2579. char tdir[_MAX_DIR];
  2580. char ttail[_MAX_FNAME];
  2581. char text[_MAX_EXT];
  2582. ::_splitpath(filename, tdrive, tdir, ttail, text);
  2583. char *longExtStart = longExt ? strchr(ttail, '.') : NULL;
  2584. if (drive)
  2585. drive->append(tdrive);
  2586. if (path)
  2587. path->append(tdir);
  2588. if (tail)
  2589. tail->append(longExtStart ? longExtStart-ttail : strlen(ttail), ttail);
  2590. if (ext)
  2591. {
  2592. if (longExtStart)
  2593. ext->append(longExtStart);
  2594. ext->append(text);
  2595. }
  2596. }
  2597. StringBuffer &createUNCFilename(const char * filename, StringBuffer &UNC, bool useHostNames)
  2598. {
  2599. char buf[255];
  2600. #ifdef _WIN32
  2601. char *dummy;
  2602. GetFullPathName(filename, sizeof(buf), buf, &dummy);
  2603. if (buf[1]==':')
  2604. {
  2605. // MORE - assumes it's a local drive not a mapped one
  2606. UNC.append("\\\\");
  2607. if (useHostNames)
  2608. UNC.append(GetCachedHostName());
  2609. else
  2610. queryHostIP().getIpText(UNC);
  2611. UNC.append("\\").append((char)tolower(buf[0])).append(getShareChar()).append(buf+2);
  2612. }
  2613. else
  2614. {
  2615. assertex(buf[0]=='\\' && buf[1]=='\\');
  2616. UNC.append(buf);
  2617. }
  2618. return UNC;
  2619. #else
  2620. if (filename[0]=='/' && filename[1]=='/')
  2621. UNC.append(filename);
  2622. else
  2623. {
  2624. UNC.append("//");
  2625. if (useHostNames)
  2626. UNC.append(GetCachedHostName());
  2627. else
  2628. queryHostIP().getIpText(UNC);
  2629. if (*filename != '/')
  2630. {
  2631. if (!GetCurrentDirectory(sizeof(buf), buf)) {
  2632. ERRLOG("createUNCFilename: Current directory path too big, bailing out");
  2633. throwUnexpected();
  2634. }
  2635. UNC.append(buf).append("/");
  2636. }
  2637. UNC.append(filename);
  2638. }
  2639. return UNC;
  2640. #endif
  2641. }
  2642. bool splitUNCFilename(const char * filename, StringBuffer * machine, StringBuffer * path, StringBuffer * tail, StringBuffer * ext)
  2643. {
  2644. if (!filename || !isPathSepChar(filename[0]) || !isPathSepChar(filename[1]))
  2645. return false;
  2646. const char * cur = filename+2;
  2647. while (*cur && !isPathSepChar(*cur))
  2648. cur++;
  2649. if (!*cur)
  2650. return false;
  2651. const char * startPath = cur;
  2652. const char * lastExt = NULL;
  2653. const char * startTail = NULL;
  2654. char next;
  2655. while ((next = *cur) != 0)
  2656. {
  2657. if (isPathSepChar(next))
  2658. {
  2659. lastExt = NULL;
  2660. startTail = cur+1;
  2661. }
  2662. else if (next == '.')
  2663. lastExt = cur;
  2664. cur++;
  2665. }
  2666. assertex(startTail);
  2667. if (machine)
  2668. machine->append(startPath-filename, filename);
  2669. if (path)
  2670. path->append(startTail - startPath, startPath);
  2671. if (lastExt)
  2672. {
  2673. if (tail)
  2674. tail->append(lastExt - startTail, startTail);
  2675. if (ext)
  2676. ext->append(lastExt);
  2677. }
  2678. else
  2679. {
  2680. if (tail)
  2681. tail->append(startTail);
  2682. }
  2683. return true;
  2684. }
  2685. /**
  2686. * Ensure the filename has desired extension.
  2687. * If it has no extension, add the desired extension, return true.
  2688. * If it has an extension different from the desiredExtension, return false.
  2689. * Otherwise, return true.
  2690. */
  2691. bool ensureFileExtension(StringBuffer& filename, const char* desiredExtension)
  2692. {
  2693. char drive[_MAX_DRIVE];
  2694. char dir[_MAX_DIR];
  2695. char fname[_MAX_FNAME];
  2696. char ext[_MAX_EXT];
  2697. _splitpath(filename.str(), drive, dir, fname, ext);
  2698. if (ext[0]==0)
  2699. {
  2700. filename.append(desiredExtension);
  2701. return true;
  2702. }
  2703. if (strcmp(ext,desiredExtension)!=0)
  2704. return false;
  2705. return true;
  2706. }
  2707. /* Get full file name. If noExtension is true, the extension (if any) will be trimmed */
  2708. StringBuffer& getFullFileName(StringBuffer& filename, bool noExtension)
  2709. {
  2710. char drive[_MAX_DRIVE];
  2711. char dir[_MAX_DIR];
  2712. char fname[_MAX_FNAME];
  2713. char ext[_MAX_EXT];
  2714. _splitpath(filename.str(), drive, dir, fname, ext);
  2715. filename.clear();
  2716. filename.append(drive).append(dir).append(fname);
  2717. if (!noExtension)
  2718. filename.append(ext);
  2719. return filename;
  2720. }
  2721. /* Get the file name only. If noExtension is true, the extension (if any) will be trimmed */
  2722. StringBuffer& getFileNameOnly(StringBuffer& filename, bool noExtension)
  2723. {
  2724. char drive[_MAX_DRIVE];
  2725. char dir[_MAX_DIR];
  2726. char fname[_MAX_FNAME];
  2727. char ext[_MAX_EXT];
  2728. _splitpath(filename.str(), drive, dir, fname, ext);
  2729. filename.clear();
  2730. filename.append(fname);
  2731. if (!noExtension)
  2732. filename.append(ext);
  2733. return filename;
  2734. }
  2735. //---------------------------------------------------------------------------
  2736. class CNullDirectoryIterator : public CInterface, implements IDirectoryIterator
  2737. {
  2738. public:
  2739. IMPLEMENT_IINTERFACE;
  2740. bool first()
  2741. {
  2742. return false;
  2743. }
  2744. bool next()
  2745. {
  2746. return false;
  2747. }
  2748. StringBuffer &getName(StringBuffer &buf)
  2749. {
  2750. return buf;
  2751. }
  2752. __int64 getFileSize()
  2753. {
  2754. return -1;
  2755. }
  2756. bool getModifiedTime(CDateTime &ret)
  2757. {
  2758. return false;
  2759. }
  2760. virtual bool isValid() { return false; }
  2761. virtual IFile & query() { return *(IFile *)NULL; }
  2762. virtual bool isDir() { return false; }
  2763. };
  2764. extern jlib_decl IDirectoryIterator *createNullDirectoryIterator()
  2765. {
  2766. return new CNullDirectoryIterator;
  2767. }
  2768. class CDirectoryIterator : public CInterface, implements IDirectoryIterator
  2769. {
  2770. public:
  2771. CDirectoryIterator(const char * _path, const char * _mask, bool _sub, bool _includedir)
  2772. {
  2773. StringBuffer tmp;
  2774. if (!_path || !*_path)
  2775. _path = "." PATHSEPSTR;
  2776. else if (_path[strlen(_path)-1] != PATHSEPCHAR)
  2777. _path = tmp.append(_path).append(PATHSEPCHAR);
  2778. path.set(_path);
  2779. mask.set(_mask);
  2780. sub = _sub;
  2781. includedir = _includedir;
  2782. subidx = 0;
  2783. curisdir = false;
  2784. }
  2785. IMPLEMENT_IINTERFACE
  2786. virtual bool first()=0;
  2787. virtual bool next()=0;
  2788. virtual bool isValid() { return cur != NULL; }
  2789. virtual IFile & query() { return *cur; }
  2790. virtual StringBuffer &getName(StringBuffer &buf)=0;
  2791. virtual bool isDir() { return curisdir; }
  2792. protected:
  2793. Owned<IFile> cur;
  2794. bool curisdir;
  2795. StringAttr path;
  2796. StringAttr mask;
  2797. bool includedir;
  2798. bool sub; // TBD
  2799. StringAttrArray subpaths;
  2800. unsigned subidx; // -1 to index subpaths
  2801. };
  2802. #ifdef _WIN32
  2803. class CWindowsDirectoryIterator : public CDirectoryIterator
  2804. {
  2805. WIN32_FIND_DATA info;
  2806. HANDLE handle;
  2807. bool setCurrent()
  2808. {
  2809. if (strcmp(info.cFileName, ".") == 0 || strcmp(info.cFileName, "..") == 0)
  2810. return false;
  2811. bool match = (!mask.length() || WildMatch(info.cFileName, mask, true));
  2812. curisdir = (info.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)!=0;
  2813. if (!match&&!curisdir)
  2814. return false;
  2815. StringBuffer f(path);
  2816. if (subidx)
  2817. f.append(subpaths.item(subidx-1).text).append('\\');
  2818. f.append(info.cFileName);
  2819. if (curisdir) {
  2820. if (sub) {
  2821. const char *s = f.str()+path.length();
  2822. unsigned i;
  2823. for (i=0;i<subpaths.ordinality();i++)
  2824. if (stricmp(subpaths.item(i).text,s)==0)
  2825. break;
  2826. if (i==subpaths.ordinality())
  2827. subpaths.append(*new StringAttrItem(s));
  2828. }
  2829. if (!includedir)
  2830. return false;
  2831. }
  2832. if (!match)
  2833. return false;
  2834. cur.setown(createIFile(f.str()));
  2835. return true;
  2836. }
  2837. bool open()
  2838. {
  2839. close();
  2840. while (subidx<=subpaths.ordinality()) {
  2841. StringBuffer location(path);
  2842. if (subidx)
  2843. location.append(subpaths.item(subidx-1).text).append('\\');
  2844. location.append("*");
  2845. handle = FindFirstFile(location.str(), &info);
  2846. if (handle != INVALID_HANDLE_VALUE)
  2847. return true;
  2848. subidx++;
  2849. }
  2850. return false;
  2851. }
  2852. void close()
  2853. {
  2854. cur.clear();
  2855. if (handle != INVALID_HANDLE_VALUE) {
  2856. FindClose(handle);
  2857. handle = INVALID_HANDLE_VALUE;
  2858. }
  2859. };
  2860. public:
  2861. CWindowsDirectoryIterator(const char * _path, const char * _mask, bool _sub, bool _includedir)
  2862. : CDirectoryIterator(_path,_mask,_sub,_includedir)
  2863. {
  2864. handle = INVALID_HANDLE_VALUE;
  2865. }
  2866. ~CWindowsDirectoryIterator()
  2867. {
  2868. close();
  2869. }
  2870. bool first()
  2871. {
  2872. subpaths.kill();
  2873. subidx = 0;
  2874. if (!open())
  2875. return false;
  2876. if (setCurrent())
  2877. return true;
  2878. return next();
  2879. }
  2880. bool next()
  2881. {
  2882. loop {
  2883. loop {
  2884. if (!FindNextFile(handle, &info))
  2885. break;
  2886. if (setCurrent())
  2887. return true;
  2888. }
  2889. subidx++;
  2890. if (!open())
  2891. break;
  2892. if (setCurrent())
  2893. return true;
  2894. }
  2895. return false;
  2896. }
  2897. StringBuffer &getName(StringBuffer &buf)
  2898. {
  2899. if (subidx)
  2900. buf.append(subpaths.item(subidx-1).text).append('\\');
  2901. return buf.append(info.cFileName);
  2902. }
  2903. __int64 getFileSize()
  2904. {
  2905. if (curisdir)
  2906. return -1;
  2907. LARGE_INTEGER x;
  2908. x.LowPart = info.nFileSizeLow;
  2909. x.HighPart = info.nFileSizeHigh;
  2910. return x.QuadPart;
  2911. }
  2912. bool getModifiedTime(CDateTime &ret)
  2913. {
  2914. FILETIMEtoIDateTime(&ret, info.ftLastWriteTime);
  2915. return true;
  2916. }
  2917. };
  2918. IDirectoryIterator * createDirectoryIterator(const char * path, const char * mask)
  2919. {
  2920. if (mask&&!*mask) // only NULL is wild
  2921. return new CNullDirectoryIterator;
  2922. if (!path || !*path) // cur directory so no point in checking for remote etc.
  2923. return new CWindowsDirectoryIterator(path, mask,false,true);
  2924. OwnedIFile iFile = createIFile(path);
  2925. if (!iFile||(iFile->isDirectory()!=foundYes))
  2926. return new CNullDirectoryIterator;
  2927. return iFile->directoryFiles(mask, false, true);
  2928. }
  2929. IDirectoryIterator *CFile::directoryFiles(const char *mask,bool sub,bool includedirs)
  2930. {
  2931. if ((mask&&!*mask)|| // only NULL is wild
  2932. (isDirectory()!=foundYes))
  2933. return new CNullDirectoryIterator;
  2934. return new CWindowsDirectoryIterator(filename, mask,sub,includedirs);
  2935. }
  2936. bool CFile::getInfo(bool &isdir,offset_t &size,CDateTime &modtime)
  2937. {
  2938. WIN32_FILE_ATTRIBUTE_DATA info;
  2939. if (GetFileAttributesEx(filename, GetFileExInfoStandard, &info) != 0) {
  2940. LARGE_INTEGER x;
  2941. x.LowPart = info.nFileSizeLow;
  2942. x.HighPart = info.nFileSizeHigh;
  2943. size = (offset_t)x.QuadPart;
  2944. isdir = (info.dwFileAttributes != (DWORD)-1)&&(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  2945. FILETIMEtoIDateTime(&modtime, info.ftLastWriteTime);
  2946. return true;
  2947. }
  2948. size = 0;
  2949. modtime.clear();
  2950. isdir = false;
  2951. return false;
  2952. }
  2953. #else
  2954. class CLinuxDirectoryIterator : public CDirectoryIterator
  2955. {
  2956. StringAttr tail;
  2957. DIR * handle;
  2958. struct stat st;
  2959. bool gotst;
  2960. bool loadst()
  2961. {
  2962. if (!gotst&&cur)
  2963. gotst = (stat(cur->queryFilename(), &st) == 0);
  2964. return gotst;
  2965. }
  2966. public:
  2967. CLinuxDirectoryIterator(const char * _path, const char * _mask, bool _sub,bool _includedir)
  2968. : CDirectoryIterator(_path,_mask,_sub,_includedir)
  2969. {
  2970. handle = NULL;
  2971. gotst = false;
  2972. }
  2973. ~CLinuxDirectoryIterator()
  2974. {
  2975. close();
  2976. }
  2977. bool open()
  2978. {
  2979. close();
  2980. while (subidx<=subpaths.ordinality()) {
  2981. StringBuffer location(path);
  2982. if (subidx)
  2983. location.append(subpaths.item(subidx-1).text);
  2984. // not sure if should remove trailing '/'
  2985. handle = ::opendir(location.str());
  2986. // better error handling here?
  2987. if (handle)
  2988. return true;
  2989. subidx++;
  2990. }
  2991. return false;
  2992. }
  2993. void close()
  2994. {
  2995. cur.clear();
  2996. if (handle) {
  2997. closedir(handle);
  2998. handle = NULL;
  2999. }
  3000. }
  3001. bool first()
  3002. {
  3003. subpaths.kill();
  3004. subidx = 0;
  3005. if (open())
  3006. return next();
  3007. return false;
  3008. }
  3009. bool next()
  3010. {
  3011. loop {
  3012. struct dirent dirEntry;
  3013. struct dirent *entry;
  3014. loop {
  3015. gotst = false;
  3016. readdir_r(handle, &dirEntry, &entry);
  3017. // need better checking here?
  3018. if (!entry)
  3019. break;
  3020. if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
  3021. continue;
  3022. bool match = (!mask.length() || WildMatch(entry->d_name, mask, false));
  3023. curisdir = (entry->d_type==DT_DIR);
  3024. bool islnk = (entry->d_type==DT_LNK);
  3025. bool isunknown = (entry->d_type==DT_UNKNOWN); // to work around xfs bug
  3026. if (match||curisdir||islnk||isunknown) {
  3027. StringBuffer f(path);
  3028. if (subidx)
  3029. f.append(subpaths.item(subidx-1).text).append('/');
  3030. f.append(entry->d_name);
  3031. if (islnk||isunknown) {
  3032. struct stat info;
  3033. if (stat(f.str(), &info) == 0) // will follow link
  3034. curisdir = S_ISDIR(info.st_mode);
  3035. else
  3036. curisdir = false;
  3037. }
  3038. if (curisdir) {
  3039. if (sub) {
  3040. const char *s = f.str()+path.length();
  3041. unsigned i;
  3042. for (i=0;i<subpaths.ordinality();i++)
  3043. if (strcmp(subpaths.item(i).text,s)==0)
  3044. break;
  3045. if (i==subpaths.ordinality())
  3046. subpaths.append(*new StringAttrItem(s));
  3047. }
  3048. if (!includedir)
  3049. match = false;
  3050. }
  3051. if (match) {
  3052. tail.set(entry->d_name);
  3053. cur.setown(createIFile(f.str()));
  3054. return true;
  3055. }
  3056. }
  3057. }
  3058. subidx++;
  3059. if (!open())
  3060. break;
  3061. }
  3062. return false;
  3063. }
  3064. StringBuffer &getName(StringBuffer &buf)
  3065. {
  3066. if (subidx)
  3067. buf.append(subpaths.item(subidx-1).text).append('/');
  3068. return buf.append(tail);
  3069. }
  3070. __int64 getFileSize()
  3071. {
  3072. if (curisdir)
  3073. return -1;
  3074. if (!loadst())
  3075. return -1;
  3076. return st.st_size ;
  3077. }
  3078. bool getModifiedTime(CDateTime &ret)
  3079. {
  3080. if (!loadst())
  3081. return false;
  3082. timetToIDateTime(&ret,st.st_mtime);
  3083. return true;
  3084. }
  3085. };
  3086. IDirectoryIterator * createDirectoryIterator(const char * path, const char * mask)
  3087. {
  3088. if (mask&&!*mask) // only NULL is wild
  3089. return new CNullDirectoryIterator;
  3090. if (!path || !*path) // no point in checking for remote etc.
  3091. return new CLinuxDirectoryIterator(path, mask,false,true);
  3092. OwnedIFile iFile = createIFile(path);
  3093. if (!iFile||(iFile->isDirectory()!=foundYes))
  3094. return new CNullDirectoryIterator;
  3095. return iFile->directoryFiles(mask, false, true);
  3096. }
  3097. IDirectoryIterator *CFile::directoryFiles(const char *mask,bool sub,bool includedirs)
  3098. {
  3099. if ((mask&&!*mask)|| // only NULL is wild
  3100. (isDirectory()!=foundYes))
  3101. return new CNullDirectoryIterator;
  3102. return new CLinuxDirectoryIterator(filename, mask,sub,includedirs);
  3103. }
  3104. bool CFile::getInfo(bool &isdir,offset_t &size,CDateTime &modtime)
  3105. {
  3106. struct stat info;
  3107. if (stat(filename, &info) == 0) {
  3108. size = (offset_t)info.st_size;
  3109. isdir = S_ISDIR(info.st_mode);
  3110. timetToIDateTime(&modtime, info.st_mtime);
  3111. return true;
  3112. }
  3113. size = 0;
  3114. modtime.clear();
  3115. isdir = false;
  3116. return false;
  3117. }
  3118. #endif
  3119. class CDirEntry: extends CInterface
  3120. {
  3121. public:
  3122. StringAttr name;
  3123. Owned<IFile> file;
  3124. __int64 size;
  3125. CDateTime modified;
  3126. byte flags; // IDDI*
  3127. bool isdir;
  3128. CDirEntry(IDirectoryIterator *iter)
  3129. {
  3130. StringBuffer n;
  3131. name.set(iter->getName(n).str());
  3132. size = iter->getFileSize();
  3133. iter->getModifiedTime(modified);
  3134. file.set(&iter->query());
  3135. flags = 0;
  3136. isdir = iter->isDir();
  3137. }
  3138. bool match(byte f)
  3139. {
  3140. return (flags==0)||((f&flags)!=0);
  3141. }
  3142. int compare(const CDirEntry *e)
  3143. {
  3144. return strcmp(name.get(),e->name.get());
  3145. }
  3146. int compareProp(const CDirEntry *e)
  3147. {
  3148. int c = compare(e);
  3149. if (c)
  3150. return c;
  3151. if (isdir!=e->isdir)
  3152. return isdir?-1:1;
  3153. if (size!=e->size)
  3154. return (size<e->size)?-1:1;
  3155. return modified.compare(e->modified,false);
  3156. }
  3157. };
  3158. class CDirectoryDifferenceIterator : public CIArrayOf<CDirEntry>, extends CInterface, implements IDirectoryDifferenceIterator
  3159. {
  3160. static int compare(CInterface **_a, CInterface **_b)
  3161. {
  3162. CDirEntry *a = *(CDirEntry **)_a;
  3163. CDirEntry *b = *(CDirEntry **)_b;
  3164. return a->compare(b);
  3165. }
  3166. unsigned idx;
  3167. byte mask;
  3168. public:
  3169. IMPLEMENT_IINTERFACE;
  3170. CDirectoryDifferenceIterator(IDirectoryIterator *iter, CDirectoryDifferenceIterator *cmp)
  3171. {
  3172. mask = IDDIstandard;
  3173. idx = 0;
  3174. ForEach(*iter)
  3175. append(*new CDirEntry(iter));
  3176. CIArrayOf<CDirEntry>::sort(compare);
  3177. if (cmp) { // assumes cmp sorted
  3178. unsigned i = 0;
  3179. unsigned ni = ordinality();
  3180. unsigned j = 0;
  3181. unsigned nj = cmp->ordinality();
  3182. loop {
  3183. CDirEntry *a = NULL;
  3184. CDirEntry *b = NULL;
  3185. if (i>=ni) {
  3186. if (j>=nj)
  3187. break;
  3188. b = &cmp->item(j++);
  3189. }
  3190. else if (j>=nj) {
  3191. a = &item(i++);
  3192. }
  3193. else {
  3194. a = &item(i);
  3195. b = &cmp->item(j);
  3196. int c = a->compare(b);
  3197. if (c==0) {
  3198. if (a->compareProp(b)==0) {
  3199. a->flags = IDDIunchanged;
  3200. a = NULL;
  3201. b = NULL;
  3202. }
  3203. i++;
  3204. j++;
  3205. }
  3206. else if (c<0) {
  3207. b = NULL;
  3208. i++;
  3209. }
  3210. else {
  3211. a = NULL;
  3212. j++;
  3213. }
  3214. }
  3215. if (a) {
  3216. if (b)
  3217. a->flags|=IDDImodified;
  3218. else
  3219. a->flags|=IDDIadded;
  3220. }
  3221. else if (b) {
  3222. b->Link();
  3223. b->flags = IDDIdeleted;
  3224. add(*b,i);
  3225. i++;
  3226. ni++;
  3227. }
  3228. }
  3229. }
  3230. }
  3231. virtual bool first()
  3232. {
  3233. idx = 0;
  3234. while (idx<ordinality()) {
  3235. if (item(idx).match(mask))
  3236. return true;
  3237. idx++;
  3238. }
  3239. return false;
  3240. }
  3241. virtual bool next()
  3242. {
  3243. idx++;
  3244. while (idx<ordinality()) {
  3245. if (item(idx).match(mask))
  3246. return true;
  3247. idx++;
  3248. }
  3249. return false;
  3250. }
  3251. virtual bool isValid()
  3252. {
  3253. return (idx<ordinality());
  3254. }
  3255. virtual IFile & query()
  3256. {
  3257. if (isValid())
  3258. return *item(idx).file;
  3259. return *(IFile *) NULL;
  3260. }
  3261. virtual StringBuffer &getName(StringBuffer &buf)
  3262. {
  3263. if (isValid())
  3264. buf.append(item(idx).name);
  3265. return buf;
  3266. }
  3267. virtual bool isDir()
  3268. {
  3269. if (isValid())
  3270. return item(idx).isdir;
  3271. return false;
  3272. }
  3273. __int64 getFileSize()
  3274. {
  3275. if (isValid())
  3276. return item(idx).isdir?0:item(idx).size;
  3277. return 0;
  3278. }
  3279. virtual bool getModifiedTime(CDateTime &ret)
  3280. {
  3281. if (isValid()) {
  3282. ret.set(item(idx).modified);
  3283. }
  3284. return false;
  3285. }
  3286. virtual void setMask(unsigned _mask)
  3287. {
  3288. mask = (byte)_mask;
  3289. }
  3290. virtual unsigned getFlags()
  3291. {
  3292. if (isValid())
  3293. return item(idx).flags;
  3294. return 0;
  3295. }
  3296. unsigned numChanges()
  3297. {
  3298. unsigned ret=0;
  3299. ForEachItemIn(i,*this) {
  3300. byte f = item(i).flags;
  3301. if ((f!=0)&&(f!=IDDIunchanged))
  3302. ret++;
  3303. }
  3304. return ret;
  3305. }
  3306. };
  3307. IDirectoryDifferenceIterator *CFile::monitorDirectory(IDirectoryIterator *_prev, // in
  3308. const char *mask,
  3309. bool sub,
  3310. bool includedirs,
  3311. unsigned checkinterval,
  3312. unsigned timeout,
  3313. Semaphore *abortsem)
  3314. {
  3315. Linked<IDirectoryIterator> prev;
  3316. if (_prev)
  3317. prev.set(_prev);
  3318. else
  3319. prev.setown(directoryFiles(mask,sub,includedirs));
  3320. if (!prev)
  3321. return NULL;
  3322. Owned<CDirectoryDifferenceIterator> base = new CDirectoryDifferenceIterator(prev,NULL);
  3323. prev.clear(); // not needed now
  3324. unsigned start=msTick();
  3325. loop {
  3326. if (abortsem) {
  3327. if (abortsem->wait(checkinterval))
  3328. break;
  3329. }
  3330. else
  3331. Sleep(checkinterval);
  3332. Owned<IDirectoryIterator> current = directoryFiles(mask,sub,includedirs);
  3333. if (!current)
  3334. break;
  3335. Owned<CDirectoryDifferenceIterator> cmp = new CDirectoryDifferenceIterator(current,base);
  3336. current.clear();
  3337. if (cmp->numChanges())
  3338. return cmp.getClear();
  3339. if (msTick()-start>timeout)
  3340. break;
  3341. }
  3342. return NULL; // timed out
  3343. }
  3344. //---------------------------------------------------------------------------
  3345. class FixedPasswordProvider : public CInterface, implements IPasswordProvider
  3346. {
  3347. public:
  3348. FixedPasswordProvider(const char * _username, const char * _password) { username.set(_username); password.set(_password); }
  3349. IMPLEMENT_IINTERFACE;
  3350. virtual bool getPassword(const IpAddress & ip, StringBuffer & _username, StringBuffer & _password)
  3351. {
  3352. _username.append(username.get());
  3353. _password.append(password.get());
  3354. return true;
  3355. }
  3356. protected:
  3357. StringAttr username;
  3358. StringAttr password;
  3359. };
  3360. IPasswordProvider * queryPasswordProvider()
  3361. {
  3362. return passwordProvider;
  3363. }
  3364. void setPasswordProvider(IPasswordProvider * provider)
  3365. {
  3366. passwordProvider.set(provider);
  3367. }
  3368. void setDefaultUser(const char * username,const char *password)
  3369. {
  3370. Owned<IPasswordProvider> provider = new FixedPasswordProvider(username, password);
  3371. setPasswordProvider(provider);
  3372. }
  3373. //---------------------------------------------------------------------------
  3374. bool recursiveCreateDirectory(const char * path)
  3375. {
  3376. #ifndef _WIN32
  3377. #ifdef USE_SAMBA
  3378. return localCreateDirectory(path);
  3379. #endif
  3380. #endif
  3381. Owned<IFile> file = createIFile(path);
  3382. return file->createDirectory();
  3383. }
  3384. bool recursiveCreateDirectoryForFile(const char *fullFileName)
  3385. {
  3386. StringBuffer path;
  3387. splitFilename(fullFileName, &path, &path, NULL, NULL);
  3388. return recursiveCreateDirectory(path.str());
  3389. }
  3390. //---------------------------------------------------------------------------
  3391. size32_t DirectBufferI::read(offset_t pos, size32_t len, void * data)
  3392. {
  3393. if (pos + len > buffLen)
  3394. {
  3395. if (pos > buffLen)
  3396. pos = buffLen;
  3397. len = (size32_t)(buffLen - pos);
  3398. }
  3399. memcpy(data, buffer+pos, len);
  3400. return len;
  3401. }
  3402. size32_t DirectBufferI::write(offset_t pos, size32_t len, const void * data)
  3403. {
  3404. UNIMPLEMENTED;
  3405. }
  3406. size32_t DirectBufferIO::write(offset_t pos, size32_t len, const void * data)
  3407. {
  3408. if (pos + len > buffLen)
  3409. {
  3410. if (pos > buffLen)
  3411. pos = buffLen;
  3412. len = (size32_t)(buffLen - pos);
  3413. }
  3414. memcpy(buffer+pos, data, len);
  3415. return len;
  3416. }
  3417. //---------------------------------------------------------------------------
  3418. IFile * createIFile(const char * filename)
  3419. {
  3420. if (!filename)
  3421. return NULL;
  3422. IFile *ret = createContainedIFileByHook(filename);
  3423. if (ret)
  3424. return ret;
  3425. bool linremote=(memcmp(filename,"//",2)==0);
  3426. if (!linremote&&(memcmp(filename,"\\\\",2)!=0)) // see if remote looking
  3427. return new CFile(filename);
  3428. RemoteFilename rfn;
  3429. rfn.setRemotePath(filename);
  3430. if (rfn.isNull())
  3431. throw MakeStringException(-1, "CreateIFile cannot resolve %s", filename);
  3432. if (rfn.isLocal()) { // ignore dafilesrv request if local and standard port
  3433. StringBuffer tmplocal;
  3434. return new CFile(rfn.getLocalPath(tmplocal).str());
  3435. }
  3436. #ifdef _WIN32
  3437. StringBuffer tmplocal;
  3438. if (linremote||(rfn.queryEndpoint().port!=0)) {
  3439. ret = createIFileByHook(rfn); // use daliservix in preference
  3440. if (ret)
  3441. return ret;
  3442. while (*filename) { // no daliservix so swap '/' for '\' and hope for best
  3443. if (*filename=='/')
  3444. tmplocal.append('\\');
  3445. else
  3446. tmplocal.append(*filename);
  3447. filename++;
  3448. }
  3449. filename =tmplocal.str();
  3450. }
  3451. return new CWindowsRemoteFile(filename);
  3452. #else
  3453. #ifdef USE_SAMBA
  3454. if(strncmp(filename, "smb://", 6) == 0)
  3455. return new CSambaRemoteFile(filename);
  3456. if(memcmp(filename, "//", 2) == 0) {
  3457. StringBuffer smbfile("smb:");
  3458. smbfile.append(filename);
  3459. return new CSambaRemoteFile(smbfile.str());
  3460. }
  3461. if(memcmp(filename, "\\\\", 2) == 0) {
  3462. StringBuffer smbfile("smb:");
  3463. int i = 0;
  3464. while(filename[i]) {
  3465. if(filename[i] == '\\')
  3466. smbfile.append('/');
  3467. else
  3468. smbfile.append(filename[i]);
  3469. i++;
  3470. }
  3471. return new CSambaRemoteFile(smbfile.str());
  3472. }
  3473. #else
  3474. if (memcmp(filename,"smb://",6)==0) // don't support samba - try remote
  3475. return createIFile(filename+4);
  3476. ret = createIFileByHook(rfn);
  3477. if (!ret)
  3478. throw MakeStringException(-1, "CreateIFile::cannot attach to %s. (remote.so not linked?)", filename);
  3479. return ret;
  3480. #endif
  3481. return new CFile(filename);
  3482. #endif
  3483. }
  3484. IFileIOStream * createIOStream(IFileIO * file)
  3485. {
  3486. return new CFileIOStream(file);
  3487. }
  3488. IFileIO * createIORange(IFileIO * io, offset_t header, offset_t length)
  3489. {
  3490. return new CFileRangeIO(io, header, length);
  3491. }
  3492. IFileIOStream * createBufferedIOStream(IFileIO * io, unsigned bufsize)
  3493. {
  3494. if (bufsize == (unsigned)-1)
  3495. bufsize = DEFAULT_BUFFER_SIZE;
  3496. return new CBufferedFileIOStream(io, bufsize);
  3497. }
  3498. IFileIOStream * createBufferedAsyncIOStream(IFileAsyncIO * io, unsigned bufsize)
  3499. {
  3500. if (bufsize == (unsigned)-1)
  3501. bufsize = DEFAULT_BUFFER_SIZE*2;
  3502. return new CBufferedAsyncIOStream(io, bufsize);
  3503. }
  3504. IReadSeq *createReadSeq(IFileIOStream * stream, offset_t offset, size32_t size)
  3505. {
  3506. return new CIOStreamReadWriteSeq(stream, offset, size);
  3507. }
  3508. IWriteSeq *createWriteSeq(IFileIOStream * stream, size32_t size)
  3509. {
  3510. return new CIOStreamReadWriteSeq(stream, 0, size);
  3511. }
  3512. extern jlib_decl offset_t filesize(const char *name)
  3513. {
  3514. CFile f(name);
  3515. return f.size();
  3516. }
  3517. extern jlib_decl offset_t getFreeSpace(const char* name)
  3518. {
  3519. offset_t freeBytesToCaller;
  3520. #ifdef _WIN32
  3521. offset_t totalBytes;
  3522. offset_t freeBytes;
  3523. int fResult = GetDiskFreeSpaceEx (name,
  3524. (PULARGE_INTEGER)&freeBytesToCaller,
  3525. (PULARGE_INTEGER)&totalBytes,
  3526. (PULARGE_INTEGER)&freeBytes);
  3527. if (fResult == 0) // error
  3528. {
  3529. return 0;
  3530. }
  3531. #elif defined (__linux__)
  3532. struct statfs buf;
  3533. int fResult = statfs(name, &buf);
  3534. if (fResult == -1) // error
  3535. {
  3536. return 0;
  3537. }
  3538. // from "man statfs the def of f_bavail and f_bsize...
  3539. // mult "free blocks avail to non-superuser" by "optimal transfer block size" to get the available size
  3540. freeBytesToCaller = (offset_t)buf.f_bavail * buf.f_bsize;
  3541. #else
  3542. UNIMPLEMENTED;
  3543. #endif
  3544. return freeBytesToCaller;
  3545. }
  3546. extern jlib_decl void createHardLink(const char* fileName, const char* existingFileName)
  3547. {
  3548. #ifdef _WIN32
  3549. // requirements...need to make sure that the directory already exists
  3550. // and the 2 directories need to be on the same drive
  3551. if (!CreateHardLink(fileName, existingFileName, NULL))
  3552. {
  3553. LPVOID lpMsgBuf;
  3554. FormatMessage(
  3555. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  3556. FORMAT_MESSAGE_FROM_SYSTEM |
  3557. FORMAT_MESSAGE_IGNORE_INSERTS,
  3558. NULL,
  3559. GetLastError(),
  3560. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  3561. (LPTSTR) &lpMsgBuf,
  3562. 0,
  3563. NULL
  3564. );
  3565. StringBuffer err;
  3566. err.appendf("Failed to create log alias %s for %s: %s", fileName, existingFileName, lpMsgBuf);
  3567. LocalFree( lpMsgBuf );
  3568. throw MakeStringException(-1, "createHardLink:: %s.",err.str());
  3569. }
  3570. #else
  3571. if (link(existingFileName, fileName) != 0) // error
  3572. throw MakeStringException(-1, "Failed to create log alias %s for %s: error code = %d", fileName, existingFileName, errno);
  3573. #endif
  3574. }
  3575. #if 0
  3576. void testDirectory()
  3577. {
  3578. //setDefaultUser("administrator", "password");
  3579. Owned<IFile> dir = createIFile("\\test1\\gavin\\home\\base");
  3580. dir->createDirectory();
  3581. Owned<IFile> dir2 = createIFile("\\test1\\gavin\\home\\base2\\");
  3582. dir2->createDirectory();
  3583. dir2->createDirectory();
  3584. Owned<IFile> dir3 = createIFile("c:\\test1\\gavin\\home2\\base");
  3585. dir3->createDirectory();
  3586. Owned<IFile> dir4 = createIFile("\\\\ghalliday\\c$\\test1\\gavin\\home3\\base");
  3587. dir4->createDirectory();
  3588. Owned<IFile> dir5 = createIFile("\\\\192.168.1.3\\d$\\test2\\gavin\\home3\\base");
  3589. dir5->createDirectory();
  3590. StringBuffer x;
  3591. x.clear();
  3592. splitUNCFilename("\\\\127.0.0.1\\gavin\\abc.ext", &x, &x, &x, &x);
  3593. x.clear();
  3594. splitUNCFilename("\\\\127.0.0.1\\gavin\\abc.ext\\etc", &x, &x, &x, &x);
  3595. x.clear();
  3596. splitUNCFilename("\\\\127.0.0.1\\", &x, &x, &x, &x);
  3597. }
  3598. #endif
  3599. // -- RemoteFilename class (file location encapsulation)
  3600. //#define _TRACERFN
  3601. void RemoteFilename::badFilename(const char * filename)
  3602. {
  3603. throw MakeStringException(-1, "Badly formatted file entry %s", filename);
  3604. }
  3605. bool RemoteFilename::equals(const RemoteFilename & other) const
  3606. {
  3607. if (isNull())
  3608. return other.isNull();
  3609. if (other.isNull())
  3610. return false;
  3611. if (!ep.ipequals(other.ep)) // only use ip for compare
  3612. return false;
  3613. if (isUnixPath()) { // case sensitive
  3614. if (localhead.length()&&other.localhead.length()) {
  3615. if (strcmp(localhead.get(),other.localhead.get())!=0)
  3616. return false;
  3617. }
  3618. else if (sharehead.length()&&other.sharehead.length()) {
  3619. if (strcmp(sharehead.get(),other.sharehead.get())!=0)
  3620. return false;
  3621. }
  3622. else if (sharehead.length()!=other.sharehead.length())
  3623. return false;
  3624. if (strcmp(tailpath.get(),other.tailpath.get())!=0)
  3625. return false;
  3626. }
  3627. else {
  3628. if (localhead.length()&&other.localhead.length()) {
  3629. if (stricmp(localhead.get(),other.localhead.get())!=0)
  3630. return false;
  3631. }
  3632. else if (sharehead.length()&&other.sharehead.length()) {
  3633. if (stricmp(sharehead.get(),other.sharehead.get())!=0)
  3634. return false;
  3635. }
  3636. else if (sharehead.length()!=other.sharehead.length())
  3637. return false;
  3638. if (stricmp(tailpath.get(),other.tailpath.get())!=0)
  3639. return false;
  3640. }
  3641. return true;
  3642. }
  3643. bool RemoteFilename::isNull() const
  3644. {
  3645. return (ep.isNull()||(tailpath.length()==0));
  3646. }
  3647. void RemoteFilename::clear()
  3648. {
  3649. ep.set(NULL, 0);
  3650. localhead.clear();
  3651. sharehead.clear();
  3652. tailpath.clear();
  3653. }
  3654. void RemoteFilename::serialize(MemoryBuffer & out)
  3655. {
  3656. ep.serialize(out);
  3657. out.append(localhead);
  3658. out.append(sharehead);
  3659. out.append(tailpath);
  3660. }
  3661. void RemoteFilename::deserialize(MemoryBuffer & in)
  3662. {
  3663. ep.deserialize(in);
  3664. in.read(localhead);
  3665. in.read(sharehead);
  3666. in.read(tailpath);
  3667. }
  3668. StringBuffer & RemoteFilename::getTail(StringBuffer &name) const
  3669. {
  3670. split(NULL, NULL, &name, &name);
  3671. return name;
  3672. }
  3673. StringBuffer & RemoteFilename::getPath(StringBuffer & name) const
  3674. {
  3675. if (isLocal())
  3676. return getLocalPath(name);
  3677. else
  3678. return getRemotePath(name);
  3679. }
  3680. bool RemoteFilename::isUnixPath() const // bit arbitrary
  3681. {
  3682. if (tailpath.length()!=0) {
  3683. char c = tailpath[0];
  3684. if (c=='/')
  3685. return true;
  3686. if (c=='\\')
  3687. return false;
  3688. }
  3689. if (sharehead.length()!=0) {
  3690. char c = sharehead[0];
  3691. if (c=='/')
  3692. return true;
  3693. if (c=='\\')
  3694. return false;
  3695. }
  3696. if (localhead.length()!=0) {
  3697. const char *s=localhead;
  3698. if (*s=='/')
  3699. return true;
  3700. if ((s[1]==':')&&(s[2]=='/'))
  3701. return false;
  3702. // those are the expected cases, otherwise look for first separator (legacy)
  3703. while (*s) {
  3704. if (*s=='\\')
  3705. return false;
  3706. s++;
  3707. if (*s=='/')
  3708. return true;
  3709. }
  3710. }
  3711. #ifdef _WIN32 // assume local on starting machine
  3712. return false;
  3713. #else
  3714. return true;
  3715. #endif
  3716. }
  3717. char RemoteFilename::getPathSeparator() const
  3718. {
  3719. return isUnixPath()?'/':'\\';
  3720. }
  3721. StringBuffer & RemoteFilename::getLocalPath(StringBuffer & out) const
  3722. {
  3723. if (tailpath.length()!=0) {
  3724. out.append(localhead).append(tailpath);
  3725. }
  3726. #ifdef _TRACERFN
  3727. StringBuffer eps;
  3728. PROGLOG("getLocalPath '%s' '%s' '%s' -> '%s'",sharehead.get()?sharehead.get():"NULL",localhead.get()?localhead.get():"NULL",tailpath.get()?tailpath.get():"NULL",out.str());
  3729. #endif
  3730. return out;
  3731. }
  3732. StringBuffer & RemoteFilename::getRemotePath(StringBuffer & out) const
  3733. { // this creates a name that can be used by windows or linux
  3734. // note - no longer loses port
  3735. char c=getPathSeparator();
  3736. out.append(c).append(c);
  3737. ep.getUrlStr(out);
  3738. const char *fn;
  3739. StringBuffer loc;
  3740. if (sharehead.length())
  3741. fn = loc.append(sharehead).append(tailpath).str();
  3742. else // try and guess from just tail (may likely fail other than for windows)
  3743. fn=getLocalPath(loc).str();
  3744. if ((c=='\\')&&(fn[1]==':')) { // windows \\d$
  3745. out.append((char)tolower(c)).append(*fn).append(getShareChar());
  3746. fn+=2;
  3747. }
  3748. out.append(fn);
  3749. #ifdef _TRACERFN
  3750. StringBuffer eps;
  3751. PROGLOG("getRemotePath '%s' '%s' '%s' -> '%s'",sharehead.get()?sharehead.get():"NULL",localhead.get()?localhead.get():"NULL",tailpath.get()?tailpath.get():"NULL",out.str());
  3752. #endif
  3753. return out;
  3754. }
  3755. bool RemoteFilename::isLocal() const
  3756. {
  3757. if (ep.port&&(ep.port!=DAFILESRV_PORT))
  3758. return false; // treat non-dafilesrv port as remote
  3759. return ep.isLocal() || ep.isNull();
  3760. }
  3761. void RemoteFilename::set(const RemoteFilename & other)
  3762. {
  3763. ep.set(other.ep);
  3764. localhead.set(other.localhead);
  3765. sharehead.set(other.sharehead);
  3766. tailpath.set(other.tailpath);
  3767. }
  3768. void RemoteFilename::setExtension(const char * newext)
  3769. {
  3770. const char * dot = NULL;
  3771. const char * start = tailpath;
  3772. const char * cur = start;
  3773. while (*cur)
  3774. {
  3775. if (*cur == '.')
  3776. dot = cur;
  3777. cur++;
  3778. }
  3779. StringBuffer newtail;
  3780. if (dot)
  3781. newtail.append(dot-start,start);
  3782. else
  3783. newtail.append(start);
  3784. if (newext)
  3785. {
  3786. if (newext[0] != '.')
  3787. newtail.append('.');
  3788. newtail.append(newext);
  3789. }
  3790. tailpath.set(newtail);
  3791. }
  3792. void RemoteFilename::setPath(const SocketEndpoint & _ep, const char * _filename)
  3793. {
  3794. const char * filename=_filename;
  3795. StringBuffer full;
  3796. ep.set(_ep);
  3797. localhead.clear();
  3798. sharehead.clear();
  3799. if (filename&&*filename) {
  3800. if (isSpecialPath(filename)) {
  3801. tailpath.set(filename);
  3802. return;
  3803. }
  3804. if (isLocal()&&!isAbsolutePath(filename)) {
  3805. char dir[_MAX_PATH];
  3806. if (!GetCurrentDirectory(sizeof(dir), dir)) {
  3807. ERRLOG("RemoteFilename::setPath: Current directory path too big, bailing out");
  3808. throwUnexpected();
  3809. }
  3810. if (*filename==PATHSEPCHAR) {
  3811. #ifdef _WIN32
  3812. if (*dir && (dir[1]==':'))
  3813. dir[2] = 0;
  3814. #endif
  3815. filename++;
  3816. }
  3817. full.append(dir);
  3818. if (full.charAt(full.length()-1) != PATHSEPCHAR)
  3819. full.append(PATHSEPCHAR);
  3820. full.append(filename);
  3821. filename = full.str();
  3822. }
  3823. bool isunix;
  3824. if (filename[0]=='/')
  3825. isunix=true;
  3826. else if (filename[0]=='\\')
  3827. isunix=false;
  3828. else if ((filename[1]==':')&&(filename[2]=='\\'))
  3829. isunix = false;
  3830. else // invalid call really as supposed to be full local path!
  3831. #ifdef _WIN32
  3832. isunix = false;
  3833. #else
  3834. isunix = true;
  3835. #endif
  3836. StringBuffer tmpxlat;
  3837. char sepchar = isunix?'/':'\\';
  3838. char altchar = isunix?'\\':'/';
  3839. // fix mixed separator path
  3840. if (strchr(filename,altchar)) {
  3841. while (*filename) {
  3842. if (*filename==altchar)
  3843. tmpxlat.append(sepchar);
  3844. else
  3845. tmpxlat.append(*filename);
  3846. filename++;
  3847. }
  3848. filename = tmpxlat.str();
  3849. }
  3850. if (isunix) {
  3851. if (filename[0]=='/') {
  3852. const char *tail;
  3853. tail=strchr(filename+1,'/');
  3854. if (tail) {
  3855. sharehead.set(filename,tail-filename); // we don't know share so guess same as leading dir
  3856. localhead.set(filename,tail-filename);
  3857. filename = tail;
  3858. }
  3859. }
  3860. }
  3861. else { // windows can guess anonymous share
  3862. StringBuffer sharestr;
  3863. if ((filename[1]==':')&&(filename[2]=='\\')) { // this should be always true if true full windows path
  3864. localhead.set(filename,2);
  3865. sharestr.append('\\').append((char)tolower(filename[0])).append(getShareChar());
  3866. filename += 2;
  3867. }
  3868. else if (filename[0]=='\\') {
  3869. const char *tail = strchr(filename+1,'\\');
  3870. if (tail) {
  3871. sharestr.append(tail-filename,filename);
  3872. localhead.set(filename,tail-filename);
  3873. filename = tail;
  3874. }
  3875. else {
  3876. localhead.set("c:");
  3877. sharestr.append("\\c").append(getShareChar());
  3878. }
  3879. }
  3880. sharehead.set(sharestr);
  3881. }
  3882. tailpath.set(filename);
  3883. }
  3884. else
  3885. tailpath.clear();
  3886. #ifdef _TRACERFN
  3887. StringBuffer eps;
  3888. PROGLOG("setPath (%s,%s) -> '%s' '%s' '%s'",ep.getUrlStr(eps).str(),_filename?_filename:"NULL",sharehead.get()?sharehead.get():"NULL",localhead.get()?localhead.get():"NULL",tailpath.get()?tailpath.get():"NULL");
  3889. #endif
  3890. }
  3891. void RemoteFilename::setRemotePath(const char * _url,const char *localpath)
  3892. {
  3893. // url should be full (share) path including ep
  3894. const char *url=_url;
  3895. // if ep not included then assume local (bit weird though)
  3896. char sep = url?url[0]:0;
  3897. if (isPathSepChar(sep)&&(sep==url[1])) {
  3898. url+=2;
  3899. const char *end = findPathSepChar(url);
  3900. if (end) {
  3901. StringBuffer eps(end-url,url);
  3902. ep.set(eps.str());
  3903. url = end;
  3904. }
  3905. else {
  3906. ep.set(NULL,0);
  3907. url--; // don't know what is going on!
  3908. }
  3909. if (isPathSepChar(*url)||!*url)
  3910. sep = *url;
  3911. }
  3912. else
  3913. ep.setLocalHost(0);
  3914. if (localpath&&*localpath)
  3915. setPath(ep,localpath);
  3916. else {
  3917. localhead.clear();
  3918. sharehead.clear();
  3919. tailpath.clear();
  3920. }
  3921. if (sep&&*url) {
  3922. // url should point to the share now
  3923. const char *tail=findPathSepChar(url+1);
  3924. if (tail) { // hopefully must be!
  3925. sharehead.set(url,tail-url);
  3926. url = tail;
  3927. }
  3928. if (localhead.length()==0) { // we don't know so guess
  3929. if (sharehead.length()!=0) {
  3930. const char *share=sharehead.get();
  3931. if (sep=='\\') {
  3932. StringBuffer locstr;
  3933. if (sharehead.length()>2) {
  3934. if (isShareChar(share[2])) {
  3935. locstr.append(share[1]).append(':');
  3936. share+=3;
  3937. }
  3938. else // we haven't a clue!
  3939. locstr.append("c:");
  3940. }
  3941. else if (sharehead[1]!='$') // data format
  3942. locstr.append("c:");
  3943. locstr.append(share);
  3944. localhead.set(locstr);
  3945. }
  3946. else { // we haven't a clue so assume same as share
  3947. localhead.set(sharehead);
  3948. }
  3949. }
  3950. }
  3951. }
  3952. if (tailpath.length()==0)
  3953. tailpath.set(url);
  3954. #ifdef _TRACERFN
  3955. StringBuffer eps;
  3956. PROGLOG("setRemotePath ('%s','%s') -> '%s' '%s' '%s'",_url,localpath?localpath:"NULL",sharehead.get()?sharehead.get():"NULL",localhead.get()?localhead.get():"NULL",tailpath.get()?tailpath.get():"NULL");
  3957. #endif
  3958. }
  3959. void RemoteFilename::setLocalPath(const char *filename)
  3960. {
  3961. SocketEndpoint localEP;
  3962. localEP.setLocalHost(0);
  3963. setPath(localEP, filename);
  3964. }
  3965. void RemoteFilename::split(StringBuffer * drive, StringBuffer * path, StringBuffer * tail, StringBuffer * ext) const
  3966. {
  3967. StringBuffer localname;
  3968. const char *s = getLocalPath(localname).str();
  3969. char sep;
  3970. if (isUnixPath())
  3971. sep = '/';
  3972. else {
  3973. sep = '\\';
  3974. if (s[1]==':') {
  3975. if (drive)
  3976. drive->append(2,s);
  3977. s+=2;
  3978. }
  3979. }
  3980. const char *t=NULL;
  3981. const char *e=s;
  3982. while (*e) {
  3983. if (*e==sep)
  3984. t = e+1;
  3985. e++;
  3986. }
  3987. if (t) {
  3988. if (path)
  3989. path->append(t-s, s);
  3990. s=t;
  3991. }
  3992. if (!tail&&!ext)
  3993. return;
  3994. t = NULL;
  3995. e=s;
  3996. while (*e) {
  3997. if (*e=='.') // NB final extension
  3998. t = e;
  3999. e++;
  4000. }
  4001. if (t) {
  4002. if (tail)
  4003. tail->append(t-s,s);
  4004. if (ext)
  4005. ext->append(e-t,t);
  4006. }
  4007. else if (tail)
  4008. tail->append(e-s,s);
  4009. }
  4010. void RemoteMultiFilename::append(const char *mpath,const char *defaultdir)
  4011. {
  4012. StringArray paths;
  4013. expand(mpath, paths);
  4014. StringBuffer fullpath;
  4015. StringBuffer deftmp;
  4016. if (paths.ordinality()&&(defaultdir==NULL)&&isAbsolutePath(paths.item(0))) {
  4017. splitDirTail(paths.item(0),deftmp);
  4018. defaultdir = deftmp.str();
  4019. }
  4020. ForEachItemIn(i,paths) {
  4021. const char *filename = paths.item(i);
  4022. RemoteFilename rfn;
  4023. if (isPathSepChar(*filename)&&isPathSepChar(filename[1])) // full URL
  4024. rfn.setRemotePath(filename);
  4025. else {
  4026. if (defaultdir&&!isAbsolutePath(filename))
  4027. filename = addPathSepChar(fullpath.clear().append(defaultdir)).append(filename).str();
  4028. rfn.setPath(ep,filename);
  4029. }
  4030. append(rfn);
  4031. }
  4032. }
  4033. void RemoteMultiFilename::append(const RemoteFilename &inrfn)
  4034. {
  4035. RemoteFilename rfn;
  4036. rfn.set(inrfn);
  4037. if (ep.isNull())
  4038. ep = rfn.queryEndpoint();
  4039. else if (rfn.queryIP().isNull()) {
  4040. rfn.setEp(ep);
  4041. }
  4042. else if (!rfn.queryIP().ipequals(ep)) {
  4043. StringBuffer path;
  4044. rfn.getRemotePath(path);
  4045. throw MakeStringException(-1, "Component file IP does not match: %s", path.str());
  4046. }
  4047. RemoteFilenameArray::append(rfn);
  4048. }
  4049. void RemoteMultiFilename::deserialize(MemoryBuffer & in)
  4050. {
  4051. clear();
  4052. ep.deserialize(in);
  4053. unsigned n;
  4054. in.read(n);
  4055. StringBuffer last;
  4056. StringBuffer filename;
  4057. RemoteFilename rfn;
  4058. for (unsigned i=0;i<n;i++) {
  4059. byte l;
  4060. in.read(l);
  4061. filename.clear();
  4062. if (l)
  4063. filename.append((size32_t)l,last.str());
  4064. StringAttr s;
  4065. in.read(s);
  4066. filename.append(s);
  4067. rfn.setPath(ep,filename.str());
  4068. RemoteFilenameArray::append(rfn);
  4069. last.swapWith(filename);
  4070. }
  4071. }
  4072. void RemoteMultiFilename::serialize(MemoryBuffer & out)
  4073. {
  4074. ep.serialize(out);
  4075. unsigned n=ordinality();
  4076. out.append(n);
  4077. // do simple compression
  4078. StringBuffer last;
  4079. StringBuffer filename;
  4080. for (unsigned i=0;i<n;i++) {
  4081. item(i).getLocalPath(filename.clear());
  4082. const char *s1 = last.str();
  4083. const char *s2 = filename.str();
  4084. byte l=0;
  4085. while ((l<255)&&*s1&&*s2&&(*s1==*s2)) {
  4086. s1++;
  4087. s2++;
  4088. l++;
  4089. }
  4090. out.append(l);
  4091. out.append(s2);
  4092. last.swapWith(filename);
  4093. }
  4094. }
  4095. bool RemoteMultiFilename::isWild(unsigned idx) const
  4096. {
  4097. if (idx==(unsigned)-1) {
  4098. ForEachItem(i)
  4099. if (isWild(i))
  4100. return true;
  4101. }
  4102. else {
  4103. StringBuffer s;
  4104. item(idx).getTail(s);
  4105. if (containsFileWildcard(s.str()))
  4106. return true;
  4107. }
  4108. return false;
  4109. }
  4110. void RemoteMultiFilename::expandWild()
  4111. {
  4112. bool anywild = false;
  4113. BoolArray iswild;
  4114. ForEachItem(i1) {
  4115. if (isWild(i1)) {
  4116. anywild = true;
  4117. iswild.append(true);
  4118. }
  4119. else
  4120. iswild.append(false);
  4121. }
  4122. if (!anywild)
  4123. return; // nothing to do
  4124. // first cache old values (bit long winded but want to preserve order)
  4125. RemoteFilenameArray tmpa;
  4126. Int64Array tmpsz;
  4127. ForEachItem(i2) {
  4128. RemoteFilename rfn(item(i2));
  4129. tmpa.append(rfn);
  4130. if (i2<sizescache.ordinality())
  4131. tmpsz.append(sizescache.item(i2));
  4132. else
  4133. tmpsz.append(-1);
  4134. }
  4135. RemoteFilenameArray::kill();
  4136. sizescache.kill();
  4137. ForEachItemIn(i3,tmpa) {
  4138. RemoteFilename rfn(tmpa.item(i3));
  4139. if (iswild.item(i3)) {
  4140. StringBuffer name;
  4141. rfn.getLocalPath(name);
  4142. const char *s=name.str();
  4143. const char *t=s;
  4144. loop {
  4145. const char *sep=findPathSepChar(t);
  4146. if (!sep)
  4147. break;
  4148. t = sep+1;
  4149. }
  4150. StringAttr tail(t);
  4151. name.setLength(t-s);
  4152. rfn.setPath(rfn.queryEndpoint(),name);
  4153. Owned<IFile> dir = createIFile(rfn);
  4154. Owned<IDirectoryIterator> iter = dir->directoryFiles(tail.get());
  4155. ForEach(*iter) {
  4156. append(iter->query().queryFilename());
  4157. sizescache.append(iter->getFileSize());
  4158. }
  4159. }
  4160. else {
  4161. append(rfn);
  4162. sizescache.append(tmpsz.item(i3));
  4163. }
  4164. }
  4165. }
  4166. offset_t RemoteMultiFilename::getSize(unsigned i)
  4167. {
  4168. __int64 ret = (i<sizescache.ordinality())?sizescache.item(i):-1;
  4169. if (ret==-1) {
  4170. RemoteFilename rfn(item(i));
  4171. Owned<IFile> file = createIFile(rfn);
  4172. ret = file->size();
  4173. }
  4174. return (offset_t)ret;
  4175. }
  4176. void RemoteMultiFilename::setIp(const IpAddress & ip)
  4177. {
  4178. ep.ipset(ip);
  4179. ForEachItem(i)
  4180. item(i).setIp(ip);
  4181. }
  4182. void RemoteMultiFilename::setEp(const SocketEndpoint & _ep)
  4183. {
  4184. ep.set(_ep);
  4185. ForEachItem(i)
  4186. item(i).setEp(_ep);
  4187. }
  4188. void RemoteMultiFilename::setPort(unsigned short port)
  4189. {
  4190. ep.port = port;
  4191. ForEachItem(i)
  4192. item(i).setPort(port);
  4193. }
  4194. void RemoteMultiFilename::set(const RemoteMultiFilename & other)
  4195. {
  4196. clear();
  4197. ep.set(other.ep);
  4198. ForEachItemIn(i,other) {
  4199. append(other.item(i));
  4200. if (i<other.sizescache.ordinality())
  4201. sizescache.append(other.sizescache.item(i));
  4202. }
  4203. }
  4204. bool RemoteMultiFilename::equals(const RemoteMultiFilename & other)
  4205. {
  4206. if (!ep.equals(other.ep))
  4207. return false;
  4208. if (ordinality()!=other.ordinality())
  4209. return false;
  4210. ForEachItem(i)
  4211. if (!item(i).equals(other.item(i)))
  4212. return false;
  4213. return true;
  4214. }
  4215. void RemoteMultiFilename::expand(const char *mpath, StringArray &array)
  4216. {
  4217. StringBuffer path;
  4218. StringBuffer fullpath;
  4219. loop {
  4220. while (isspace(*mpath))
  4221. mpath++;
  4222. if (!*mpath)
  4223. break;
  4224. bool inquote=false;
  4225. while (*mpath) {
  4226. if (*mpath=='"') {
  4227. mpath++;
  4228. if (inquote) {
  4229. if (*mpath!='"') {
  4230. inquote = false;
  4231. continue;
  4232. }
  4233. }
  4234. else {
  4235. inquote = true;
  4236. continue;
  4237. }
  4238. }
  4239. if ((*mpath==',')&&!inquote) {
  4240. mpath++;
  4241. break;
  4242. }
  4243. path.append(*mpath);
  4244. mpath++;
  4245. }
  4246. path.clip();
  4247. if (path.length()) {
  4248. array.append(path.str());
  4249. path.clear();
  4250. }
  4251. }
  4252. }
  4253. void RemoteMultiFilename::tostr(StringArray &array,StringBuffer &out)
  4254. {
  4255. ForEachItemIn(i,array) {
  4256. const char *s = array.item(i);
  4257. if (!s||!*s)
  4258. continue;
  4259. if (i!=0)
  4260. out.append(',');
  4261. bool needquote=false;
  4262. for (const char *e=s;*e;e++)
  4263. if (isspace(*e)||(*e==',')) {
  4264. needquote = true;
  4265. break;
  4266. }
  4267. if (needquote)
  4268. out.append('"');
  4269. out.append(s);
  4270. if (needquote)
  4271. out.append('"');
  4272. }
  4273. }
  4274. //===================================================================================================
  4275. static IArrayOf<IContainedFileHook> containedFileHooks;
  4276. static ReadWriteLock containedFileHookLock;
  4277. void addContainedFileHook(IContainedFileHook *hook)
  4278. {
  4279. if (hook)
  4280. {
  4281. hook->Link();
  4282. WriteLockBlock block(containedFileHookLock);
  4283. containedFileHooks.append(*hook);
  4284. }
  4285. }
  4286. void removeContainedFileHook(IContainedFileHook *hook)
  4287. {
  4288. WriteLockBlock block(containedFileHookLock);
  4289. containedFileHooks.zap(*hook);
  4290. }
  4291. static IFile *createContainedIFileByHook(const char *filename)
  4292. {
  4293. ReadLockBlock block(containedFileHookLock);
  4294. ForEachItemIn(i, containedFileHooks)
  4295. {
  4296. IFile * ret = containedFileHooks.item(i).createIFile(filename);
  4297. if (ret)
  4298. return ret;
  4299. }
  4300. return NULL;
  4301. }
  4302. static IArrayOf<IRemoteFileCreateHook> remoteFileHooks;
  4303. static ReadWriteLock remoteFileHookLock;
  4304. void addIFileCreateHook(IRemoteFileCreateHook *hook)
  4305. {
  4306. if (hook)
  4307. {
  4308. hook->Link();
  4309. WriteLockBlock block(remoteFileHookLock);
  4310. remoteFileHooks.append(*hook);
  4311. }
  4312. }
  4313. void removeIFileCreateHook(IRemoteFileCreateHook *hook)
  4314. {
  4315. WriteLockBlock block(remoteFileHookLock);
  4316. remoteFileHooks.zap(*hook);
  4317. }
  4318. static IFile *createIFileByHook(const RemoteFilename & filename)
  4319. {
  4320. ReadLockBlock block(remoteFileHookLock);
  4321. ForEachItemIn(i, remoteFileHooks)
  4322. {
  4323. IFile * ret = remoteFileHooks.item(i).createIFile(filename);
  4324. if (ret)
  4325. return ret;
  4326. }
  4327. return NULL;
  4328. }
  4329. IFile * createIFile(const RemoteFilename & filename)
  4330. {
  4331. IFile * ret = createIFileByHook(filename);
  4332. if (ret)
  4333. return ret;
  4334. StringBuffer name;
  4335. return createIFile(getLocalOrRemoteName(name,filename).str());
  4336. }
  4337. StringBuffer &makePathUniversal(const char *path, StringBuffer &out)
  4338. {
  4339. if (!path||!*path)
  4340. return out;
  4341. if (path[1]==':')
  4342. {
  4343. out.append('/').append(*path);
  4344. path+=2;
  4345. }
  4346. for (; *path; path++)
  4347. out.append(isPathSepChar(*path) ? '/' : *path);
  4348. return out;
  4349. }
  4350. StringBuffer &makeAbsolutePath(const char *relpath,StringBuffer &out, bool mustExist)
  4351. {
  4352. if (isPathSepChar(relpath[0])&&(relpath[0]==relpath[1]))
  4353. {
  4354. if (mustExist)
  4355. {
  4356. OwnedIFile iFile = createIFile(relpath);
  4357. if (!iFile->exists())
  4358. throw MakeStringException(-1, "makeAbsolutePath: could not resolve absolute path for %s", relpath);
  4359. }
  4360. return out.append(relpath); // if remote then already should be absolute
  4361. }
  4362. #ifdef _WIN32
  4363. char rPath[MAX_PATH];
  4364. char *filepart;
  4365. if (!relpath || '\0' == *relpath)
  4366. relpath = ".";
  4367. DWORD res = GetFullPathName(relpath, sizeof(rPath), rPath, &filepart);
  4368. if (0 == res)
  4369. throw MakeOsException(GetLastError(), "makeAbsolutePath: could not resolve absolute path for %s", relpath);
  4370. else if (mustExist)
  4371. {
  4372. OwnedIFile iFile = createIFile(rPath);
  4373. if (!iFile->exists())
  4374. throw MakeStringException(-1, "makeAbsolutePath: could not resolve absolute path for %s", rPath);
  4375. }
  4376. out.append(rPath);
  4377. #else
  4378. char rPath[PATH_MAX];
  4379. if (mustExist)
  4380. {
  4381. if (!realpath(relpath, rPath))
  4382. throw MakeErrnoException(errno, "makeAbsolutePath: could not resolve absolute path for %s", relpath);
  4383. out.append(rPath);
  4384. }
  4385. else
  4386. {
  4387. // no error, will attempt to resolve(realpath) as much of relpath as possible and append rest
  4388. if (strlen(relpath))
  4389. {
  4390. const char *end = relpath+strlen(relpath);
  4391. const char *path = relpath;
  4392. const char *tail = end;
  4393. StringBuffer head;
  4394. loop
  4395. {
  4396. if (realpath(path,rPath))
  4397. {
  4398. out.append(rPath);
  4399. if (tail != end)
  4400. out.append(tail);
  4401. return removeTrailingPathSepChar(out);
  4402. }
  4403. // mark next tail
  4404. loop
  4405. {
  4406. --tail;
  4407. if (tail == relpath)
  4408. break;
  4409. else if ('/' == *tail)
  4410. break;
  4411. }
  4412. if (tail == relpath)
  4413. break; // bail out and guess
  4414. head.clear().append(tail-relpath, relpath);
  4415. path = head.str();
  4416. }
  4417. }
  4418. if (isAbsolutePath(relpath))
  4419. out.append(relpath);
  4420. else
  4421. {
  4422. appendCurrentDirectory(out, true);
  4423. if (strlen(relpath))
  4424. addPathSepChar(out).append(relpath);
  4425. }
  4426. }
  4427. #endif
  4428. return removeTrailingPathSepChar(out);
  4429. }
  4430. StringBuffer &makeAbsolutePath(StringBuffer &relpath,bool mustExist)
  4431. {
  4432. StringBuffer out;
  4433. makeAbsolutePath(relpath.str(),out,mustExist);
  4434. relpath.swapWith(out);
  4435. return relpath;
  4436. }
  4437. StringBuffer &makeAbsolutePath(const char *relpath, const char *basedir, StringBuffer &out)
  4438. {
  4439. StringBuffer combined;
  4440. if (basedir && !isAbsolutePath(relpath))
  4441. relpath = combined.append(basedir).append(relpath);
  4442. return makeAbsolutePath(relpath, out);
  4443. }
  4444. const char *splitRelativePath(const char *full,const char *basedir,StringBuffer &reldir)
  4445. {
  4446. if (basedir&&*basedir) {
  4447. size32_t bl = strlen(basedir);
  4448. if (isPathSepChar(basedir[bl-1]))
  4449. bl--;
  4450. if ((memicmp(full,basedir,bl)==0)&&isPathSepChar(full[bl]))
  4451. full += bl+1;
  4452. }
  4453. const char *t = full;
  4454. loop {
  4455. const char *n = findPathSepChar(t);
  4456. if (!n)
  4457. break;
  4458. t = n+1;
  4459. }
  4460. if (t!=full)
  4461. reldir.append(t-full,full);
  4462. return t;
  4463. }
  4464. const char *splitDirMultiTail(const char *multipath,StringBuffer &dir,StringBuffer &tail)
  4465. {
  4466. // the first directory is the significant one
  4467. // others only removed if same
  4468. dir.clear();
  4469. StringArray files;
  4470. RemoteMultiFilename::expand(multipath,files);
  4471. StringBuffer reldir;
  4472. ForEachItemIn(i,files) {
  4473. const char *s = files.item(i);
  4474. if (i==0) {
  4475. if (isAbsolutePath(s)) {
  4476. StringAttr tail(splitDirTail(s,dir));
  4477. if (dir.length())
  4478. files.replace(tail,i);
  4479. }
  4480. }
  4481. else if (dir.length()) {
  4482. s= splitRelativePath(s,dir.str(),reldir.clear());
  4483. if (reldir.length()) {
  4484. reldir.append(s);
  4485. files.replace(reldir.str(),i);
  4486. }
  4487. }
  4488. }
  4489. RemoteMultiFilename::tostr(files,tail);
  4490. return tail.str();
  4491. }
  4492. StringBuffer &mergeDirMultiTail(const char *dir,const char *tail, StringBuffer &multipath)
  4493. {
  4494. StringArray files;
  4495. RemoteMultiFilename::expand(tail,files);
  4496. StringBuffer reldir;
  4497. if (dir && *dir) {
  4498. ForEachItemIn(i,files) {
  4499. const char *s = files.item(i);
  4500. if (!isAbsolutePath(s)) {
  4501. reldir.clear().append(dir);
  4502. addPathSepChar(reldir).append(s);
  4503. files.replace(reldir.str(),i);
  4504. }
  4505. }
  4506. }
  4507. RemoteMultiFilename::tostr(files,multipath);
  4508. return multipath;
  4509. }
  4510. StringBuffer &removeRelativeMultiPath(const char *full,const char *reldir,StringBuffer &res)
  4511. {
  4512. StringArray files;
  4513. RemoteMultiFilename::expand(full,files);
  4514. StringBuffer tmp1;
  4515. StringBuffer tmp2;
  4516. StringBuffer dir;
  4517. ForEachItemIn(i,files) {
  4518. const char *s = files.item(i);
  4519. if (isAbsolutePath(s)) {
  4520. if (!dir.length())
  4521. splitDirTail(s,dir);
  4522. }
  4523. else if (dir.length()) {
  4524. tmp1.clear().append(dir);
  4525. addPathSepChar(tmp1).append(s);
  4526. s = tmp1.str();
  4527. }
  4528. s = splitRelativePath(s,reldir,tmp2.clear());
  4529. tmp2.append(s);
  4530. files.replace(tmp2.str(),i);
  4531. }
  4532. RemoteMultiFilename::tostr(files,res);
  4533. return res;
  4534. }
  4535. // removes basedir if matches, returns relative multipath
  4536. //===================================================================================================
  4537. ExtractedBlobInfo::ExtractedBlobInfo(const char * _filename, offset_t _length, offset_t _offset)
  4538. {
  4539. filename.set(_filename);
  4540. length = _length;
  4541. offset = _offset;
  4542. }
  4543. void ExtractedBlobInfo::serialize(MemoryBuffer & buffer)
  4544. {
  4545. ::serialize(buffer, filename.get());
  4546. buffer.append(length).append(offset);
  4547. }
  4548. void ExtractedBlobInfo::deserialize(MemoryBuffer & buffer)
  4549. {
  4550. ::deserialize(buffer, filename);
  4551. buffer.read(length).read(offset);
  4552. }
  4553. //----------------------------------------------------------------------------
  4554. #define DFTERR_InvalidSplitPrefixFormat 8091
  4555. #define DFTERR_InvalidSplitPrefixFormat_Text "Cannot process file %s using the splitprefix supplied"
  4556. static void * readLength(MemoryBuffer & buffer, IFileIOStream * in, size_t len, const char * filenameText)
  4557. {
  4558. void * ptr = buffer.clear().reserve(len);
  4559. if (in->read(len, ptr) != len)
  4560. throwError1(DFTERR_InvalidSplitPrefixFormat, filenameText);
  4561. return ptr;
  4562. }
  4563. void extractBlobElements(const char * prefix, const RemoteFilename &filename, ExtractedBlobArray & extracted)
  4564. {
  4565. StringBuffer filenameText;
  4566. filename.getPath(filenameText);
  4567. Owned<IFile> file = createIFile(filename);
  4568. Owned<IFileIO> inIO = file->open(IFOread);
  4569. if (!inIO)
  4570. throw MakeStringException(-1, "extractBlobElements: file '%s' not found", filenameText.str());
  4571. Owned<IFileIOStream> in = createIOStream(inIO);
  4572. MemoryBuffer buffer;
  4573. offset_t endOffset = in->size();
  4574. while (in->tell() != endOffset)
  4575. {
  4576. StringAttr blobFilename;
  4577. offset_t blobLength = (offset_t)-1;
  4578. const char * finger = prefix;
  4579. while (finger)
  4580. {
  4581. StringAttr command;
  4582. const char * comma = strchr(finger, ',');
  4583. if (comma)
  4584. {
  4585. command.set(finger, comma-finger);
  4586. finger = comma+1;
  4587. }
  4588. else
  4589. {
  4590. command.set(finger);
  4591. finger = NULL;
  4592. }
  4593. command.toUpperCase();
  4594. if (memcmp(command, "FILENAME", 8) == 0)
  4595. {
  4596. if (command[8] == ':')
  4597. {
  4598. unsigned maxLen = atoi(command+9);
  4599. const char * nameptr = (const char *)readLength(buffer, in, maxLen, filenameText.str());
  4600. blobFilename.set(nameptr, maxLen);
  4601. }
  4602. else
  4603. {
  4604. unsigned * lenptr = (unsigned *)readLength(buffer, in, sizeof(unsigned), filenameText.str());
  4605. #if __BYTE_ORDER != __LITTLE_ENDIAN
  4606. _rev(sizeof(*lenptr), lenptr);
  4607. #endif
  4608. unsigned filenamelen = *lenptr;
  4609. const char * nameptr = (const char *)readLength(buffer, in, filenamelen, filenameText.str());
  4610. blobFilename.set(nameptr, filenamelen);
  4611. }
  4612. }
  4613. else if ((memcmp(command, "FILESIZE", 8) == 0) || (command.length() == 2))
  4614. {
  4615. const char * format = command;
  4616. if (memcmp(format, "FILESIZE", 8) == 0)
  4617. {
  4618. if (format[8] == ':')
  4619. format = format+9;
  4620. else
  4621. format = "L4";
  4622. }
  4623. bool bigEndian;
  4624. char c = format[0];
  4625. if (c == 'B')
  4626. bigEndian = true;
  4627. else if (c == 'L')
  4628. bigEndian = false;
  4629. else
  4630. throwError1(DFTERR_InvalidSplitPrefixFormat, format);
  4631. c = format[1];
  4632. if ((c <= '0') || (c > '8'))
  4633. throwError1(DFTERR_InvalidSplitPrefixFormat, format);
  4634. unsigned length = (c - '0');
  4635. byte * lenptr = (byte *)readLength(buffer, in, length, filenameText.str());
  4636. if (!bigEndian)
  4637. _rev(length, lenptr);
  4638. blobLength = 0;
  4639. for (unsigned i=0; i<length; i++)
  4640. {
  4641. blobLength = (blobLength << 8) | lenptr[i];
  4642. }
  4643. }
  4644. else if (memcmp(command, "SKIP:", 5) == 0)
  4645. {
  4646. unsigned skipLen = atoi(command+5);
  4647. in->seek(in->tell()+skipLen, IFSbegin);
  4648. }
  4649. else if (memcmp(command, "SEQ:", 4) == 0)
  4650. {
  4651. unsigned skipLen = atoi(command+4);
  4652. in->seek(in->tell()+skipLen, IFSbegin);
  4653. }
  4654. else
  4655. throwError1(DFTERR_InvalidSplitPrefixFormat, command.get());
  4656. }
  4657. if ((blobLength == (offset_t)-1) || !blobFilename.get())
  4658. throwError1(DFTERR_InvalidSplitPrefixFormat, filenameText.str());
  4659. offset_t blobOffset = in->tell();
  4660. extracted.append(* new ExtractedBlobInfo(blobFilename, blobLength, blobOffset));
  4661. in->seek(blobOffset + blobLength, IFSbegin);
  4662. }
  4663. }
  4664. bool mountDrive(const char *drv,const RemoteFilename &rfn)
  4665. {
  4666. #ifdef _WIN32
  4667. return false;
  4668. #else
  4669. unmountDrive(drv);
  4670. localCreateDirectory(drv);
  4671. int ret;
  4672. for (unsigned vtry=0;vtry<2;vtry++) {
  4673. StringBuffer cmd;
  4674. cmd.append("mount ");
  4675. rfn.queryIP().getIpText(cmd);
  4676. cmd.append(':');
  4677. rfn.getLocalPath(cmd);
  4678. cmd.append(' ').append(drv).append(" -t nfs ");
  4679. if (vtry==0)
  4680. cmd.append("-o nfsvers=v3 "); // prefer v3
  4681. cmd.append("2> /dev/null");
  4682. ret = system(cmd.str());
  4683. if (ret==0)
  4684. break;
  4685. }
  4686. return (ret==0);
  4687. #endif
  4688. }
  4689. bool unmountDrive(const char *drv)
  4690. {
  4691. #ifdef _WIN32
  4692. return false;
  4693. #else
  4694. StringBuffer cmd;
  4695. cmd.append("umount ").append(drv).append(" 2> /dev/null");
  4696. int ret = system(cmd.str());
  4697. return (ret==0);
  4698. #endif
  4699. }
  4700. IFileIO *createUniqueFile(const char *dir, const char *prefix, const char *ext, StringBuffer &filename)
  4701. {
  4702. CDateTime dt;
  4703. dt.setNow();
  4704. unsigned t = (unsigned)dt.getSimple();
  4705. if (dir)
  4706. {
  4707. filename.append(dir);
  4708. addPathSepChar(filename);
  4709. }
  4710. if (prefix && *prefix)
  4711. filename.append(prefix);
  4712. else
  4713. filename.append("uniq");
  4714. if (!ext || !*ext)
  4715. ext = "tmp";
  4716. filename.appendf("_%"I64F"x.%x.%x.%s", (__int64)GetCurrentThreadId(), (unsigned)GetCurrentProcessId(), t, ext);
  4717. OwnedIFile iFile = createIFile(filename.str());
  4718. IFileIO *iFileIO = NULL;
  4719. unsigned attempts = 5; // max attempts
  4720. loop
  4721. {
  4722. if (!iFile->exists())
  4723. {
  4724. try { return iFile->openShared(IFOcreate, IFSHnone); } // NB: could be null if path not found
  4725. catch (IException *e)
  4726. {
  4727. EXCLOG(e, "createUniqueFile");
  4728. e->Release();
  4729. }
  4730. }
  4731. if (0 == --attempts)
  4732. break;
  4733. t += getRandom();
  4734. filename.clear().appendf("uniq_%"I64F"x.%x.%x.%s", (__int64)GetCurrentThreadId(), (unsigned)GetCurrentProcessId(), t, ext);
  4735. iFile.setown(createIFile(filename.str()));
  4736. }
  4737. return NULL;
  4738. }
  4739. unsigned sortDirectory( CIArrayOf<CDirectoryEntry> &sortedfiles,
  4740. IDirectoryIterator &iter,
  4741. SortDirectoryMode mode,
  4742. bool rev,
  4743. bool includedirs
  4744. )
  4745. {
  4746. sortedfiles.kill();
  4747. StringBuffer name;
  4748. ForEach(iter) {
  4749. if (!iter.isDir()||includedirs)
  4750. sortedfiles.append(*new CDirectoryEntry(iter));
  4751. }
  4752. if (mode!=SD_nosort) {
  4753. struct icmp: implements ICompare
  4754. {
  4755. SortDirectoryMode mode;
  4756. bool rev;
  4757. int docompare(const void *l,const void *r) const
  4758. {
  4759. int ret=0;
  4760. const CDirectoryEntry *dl = (const CDirectoryEntry *)l;
  4761. const CDirectoryEntry *dr = (const CDirectoryEntry *)r;
  4762. switch (mode) {
  4763. case SD_byname:
  4764. ret = strcmp(dl->name,dr->name);
  4765. break;
  4766. case SD_bynameNC:
  4767. ret = stricmp(dl->name,dr->name);
  4768. break;
  4769. case SD_bydate:
  4770. ret = dl->modifiedTime.compare(dr->modifiedTime);
  4771. break;
  4772. case SD_bysize:
  4773. ret = (dl->size>dr->size)?1:((dl->size<dr->size)?-1:0);
  4774. break;
  4775. }
  4776. if (rev)
  4777. ret = -ret;
  4778. return ret;
  4779. }
  4780. } cmp;
  4781. cmp.mode = mode;
  4782. cmp.rev = rev;
  4783. qsortvec((void **)sortedfiles.getArray(), sortedfiles.ordinality(), cmp);
  4784. }
  4785. return sortedfiles.ordinality();
  4786. }
  4787. class CReplicatedFile : public CInterface, implements IReplicatedFile
  4788. {
  4789. RemoteFilenameArray copies;
  4790. public:
  4791. IMPLEMENT_IINTERFACE;
  4792. RemoteFilenameArray &queryCopies()
  4793. {
  4794. return copies;
  4795. }
  4796. IFile *open()
  4797. {
  4798. StringBuffer locations;
  4799. Owned<IException> exc;
  4800. ForEachItemIn(copy,copies) {
  4801. RemoteFilename &rfn = copies.item(copy);
  4802. try {
  4803. OwnedIFile iFile = createIFile(rfn);
  4804. if (iFile->exists())
  4805. return iFile.getClear();
  4806. if (locations.length())
  4807. locations.append(", ");
  4808. rfn.getRemotePath(locations);
  4809. }
  4810. catch(IException *e) {
  4811. EXCLOG(e,"CReplicatedFile::open");
  4812. if (exc)
  4813. e->Release();
  4814. else
  4815. exc.setown(e);
  4816. }
  4817. }
  4818. if (exc.get())
  4819. throw exc.getClear();
  4820. throw MakeStringException(0, "%s: Failed to open part file at any of the following locations: ", locations.str());
  4821. }
  4822. };
  4823. IReplicatedFile *createReplicatedFile()
  4824. {
  4825. return new CReplicatedFile;
  4826. }
  4827. // ---------------------------------------------------------------------------------
  4828. class CSerialStreamBase : public CInterface, implements ISerialStream
  4829. {
  4830. private:
  4831. size32_t bufsize;
  4832. size32_t bufpos;
  4833. size32_t bufmax;
  4834. offset_t bufbase;
  4835. offset_t endpos; // -1 if not known
  4836. MemoryAttr ma;
  4837. byte *buf;
  4838. bool eoinput;
  4839. IFileSerialStreamCallback *tally;
  4840. inline size32_t doread(offset_t pos, size32_t max_size, void *ptr)
  4841. {
  4842. if (endpos!=(offset_t)-1) {
  4843. if (pos>=endpos)
  4844. return 0;
  4845. if (endpos-pos<max_size)
  4846. max_size = (size32_t)(endpos-pos);
  4847. }
  4848. size32_t size_read = rawread(pos, max_size, ptr);
  4849. if (tally)
  4850. tally->process(pos,size_read,ptr);
  4851. return size_read;
  4852. }
  4853. const void * dopeek(size32_t sz, size32_t &got)
  4854. {
  4855. loop
  4856. {
  4857. size32_t left = bufmax-bufpos;
  4858. got = left;
  4859. if (left>=sz)
  4860. return buf+bufpos;
  4861. if (eoinput)
  4862. return left?(buf+bufpos):NULL;
  4863. size32_t reqsz = sz+bufsize; // NB not sz-left as want some slack
  4864. if (ma.length()<reqsz) {
  4865. MemoryAttr ma2;
  4866. void *nb = ma2.allocate(reqsz);
  4867. memcpy(nb,buf+bufpos,left);
  4868. ma.setOwn(reqsz,ma2.detach());
  4869. buf = (byte *)nb;
  4870. }
  4871. else
  4872. memmove(buf,buf+bufpos,left);
  4873. bufbase += bufpos;
  4874. size32_t rd = doread(bufbase+left,bufsize,buf+left);
  4875. if (!rd)
  4876. eoinput = true;
  4877. bufmax = rd+left;
  4878. bufpos = 0;
  4879. }
  4880. }
  4881. protected:
  4882. virtual size32_t rawread(offset_t pos, size32_t max_size, void *ptr) = 0;
  4883. public:
  4884. IMPLEMENT_IINTERFACE;
  4885. CSerialStreamBase(offset_t _offset, offset_t _len, size32_t _bufsize, IFileSerialStreamCallback *_tally)
  4886. {
  4887. tally = _tally;
  4888. bufsize = _bufsize;
  4889. if (bufsize==(size32_t)-1)
  4890. bufsize = DEFAULT_STREAM_BUFFER_SIZE;
  4891. if (bufsize<4096)
  4892. bufsize = 4096;
  4893. else
  4894. bufsize = ((bufsize+4095)/4096)*4096;
  4895. buf = (byte *)ma.allocate(bufsize+4096); // 4K initial slack
  4896. bufpos = 0;
  4897. bufmax = 0;
  4898. bufbase = _offset;
  4899. if (_len==(offset_t)-1)
  4900. endpos = (offset_t)-1;
  4901. else
  4902. endpos = _offset+_len;
  4903. eoinput = (_len==0);
  4904. }
  4905. void reset(offset_t _offset, offset_t _len)
  4906. {
  4907. bufpos = 0;
  4908. bufmax = 0;
  4909. bufbase = _offset;
  4910. if (_len==(offset_t)-1)
  4911. endpos = (offset_t)-1;
  4912. else
  4913. endpos = _offset+_len;
  4914. eoinput = (_len==0);
  4915. }
  4916. const void *peek(size32_t sz,size32_t &got)
  4917. {
  4918. return dopeek(sz, got);
  4919. }
  4920. void get(size32_t len, void * ptr)
  4921. {
  4922. size32_t cpy = bufmax-bufpos;
  4923. if (cpy>len)
  4924. cpy = len;
  4925. memcpy(ptr,(const byte *)buf+bufpos,cpy);
  4926. len -= cpy;
  4927. if (len==0) {
  4928. bufpos += cpy;
  4929. return;
  4930. }
  4931. bufbase += bufmax;
  4932. bufpos = 0;
  4933. bufmax = 0;
  4934. size32_t rd = 0;
  4935. if (!eoinput) {
  4936. ptr = (byte *)ptr+cpy;
  4937. if (len>=bufsize) {
  4938. size32_t tord = (len/bufsize)*bufsize;
  4939. rd = doread(bufbase,tord,ptr); // copy directly if large
  4940. bufbase += rd;
  4941. if (rd!=tord) {
  4942. eoinput = true;
  4943. PrintStackReport();
  4944. ERRLOG("CFileSerialStream::get read past end of stream.1 (%u,%u) %s",rd,tord,eoinput?"eoinput":"");
  4945. throw MakeStringException(-1,"CFileSerialStream::get read past end of stream");
  4946. }
  4947. cpy += rd;
  4948. len -= rd;
  4949. if (!len)
  4950. return;
  4951. ptr = (byte *)ptr+rd;
  4952. }
  4953. const void *p = dopeek(len,rd);
  4954. if (len<=rd) {
  4955. memcpy(ptr,p,len);
  4956. bufpos += len;
  4957. return;
  4958. }
  4959. }
  4960. PrintStackReport();
  4961. ERRLOG("CFileSerialStream::get read past end of stream.2 (%u,%u) %s",len,rd,eoinput?"eoinput":"");
  4962. throw MakeStringException(-1,"CFileSerialStream::get read past end of stream");
  4963. }
  4964. bool eos()
  4965. {
  4966. if (bufmax-bufpos)
  4967. return false;
  4968. size32_t rd;
  4969. return dopeek(1,rd)==NULL;
  4970. }
  4971. void skip(size32_t len)
  4972. {
  4973. size32_t left = bufmax-bufpos;
  4974. if (left>=len) {
  4975. bufpos += len;
  4976. return;
  4977. }
  4978. len -= left;
  4979. bufbase += bufmax;
  4980. bufpos = 0;
  4981. bufmax = 0;
  4982. if (!eoinput) {
  4983. while (len>=bufsize) {
  4984. size32_t rd;
  4985. if (tally) {
  4986. rd = doread(bufbase,bufsize,buf);
  4987. if (rd!=bufsize) {
  4988. eoinput = true;
  4989. throw MakeStringException(-1,"CFileSerialStream::skip read past end of stream");
  4990. }
  4991. }
  4992. else {
  4993. rd = (len/bufsize)*bufsize;
  4994. //rd=doskip(bufbase,bufsize,buf); to cope with reading from sockets etc?
  4995. }
  4996. bufbase += rd;
  4997. len -= bufsize;
  4998. }
  4999. if (len==0)
  5000. return;
  5001. size32_t got;
  5002. dopeek(len,got);
  5003. if (len<=got) {
  5004. bufpos += got;
  5005. return;
  5006. }
  5007. }
  5008. throw MakeStringException(-1,"CFileSerialStream::skip read past end of stream");
  5009. }
  5010. offset_t tell()
  5011. {
  5012. return bufbase+bufpos;
  5013. }
  5014. };
  5015. class CFileSerialStream: public CSerialStreamBase
  5016. {
  5017. Linked<IFileIO> fileio;
  5018. virtual size32_t rawread(offset_t pos, size32_t max_size, void *ptr)
  5019. {
  5020. return fileio->read(pos,max_size,ptr);
  5021. }
  5022. public:
  5023. CFileSerialStream(IFileIO *_fileio,offset_t _offset, offset_t _len, size32_t _bufsize, IFileSerialStreamCallback *_tally)
  5024. : CSerialStreamBase(_offset, _len, _bufsize, _tally), fileio(_fileio)
  5025. {
  5026. }
  5027. };
  5028. ISerialStream *createFileSerialStream(IFileIO *fileio,offset_t ofs, offset_t flen, size32_t bufsize,IFileSerialStreamCallback *callback)
  5029. {
  5030. if (!fileio)
  5031. return NULL;
  5032. return new CFileSerialStream(fileio,ofs,flen,bufsize,callback);
  5033. }
  5034. class CIoSerialStream: public CSerialStreamBase
  5035. {
  5036. Linked<IFileIOStream> io;
  5037. offset_t lastpos;
  5038. virtual size32_t rawread(offset_t pos, size32_t max_size, void *ptr)
  5039. {
  5040. if (lastpos!=pos)
  5041. throw MakeStringException(-1,"CIoSerialStream: non-sequential read (%"I64F"d,%"I64F"d)",lastpos,pos);
  5042. size32_t rd = io->read(max_size,ptr);
  5043. lastpos = pos+rd;
  5044. return rd;
  5045. }
  5046. public:
  5047. CIoSerialStream(IFileIOStream * _io,offset_t _offset, size32_t _bufsize, IFileSerialStreamCallback *_tally)
  5048. : CSerialStreamBase(_offset, (offset_t)-1, _bufsize, _tally), io(_io)
  5049. {
  5050. lastpos = _offset;
  5051. }
  5052. };
  5053. ISerialStream *createFileSerialStream(IFileIOStream *io,size32_t bufsize,IFileSerialStreamCallback *callback)
  5054. {
  5055. if (!io)
  5056. return NULL;
  5057. return new CIoSerialStream(io,0,bufsize,callback);
  5058. }
  5059. class CSocketSerialStream: public CSerialStreamBase
  5060. {
  5061. Linked<ISocket> socket;
  5062. unsigned timeout;
  5063. offset_t lastpos;
  5064. virtual size32_t rawread(offset_t pos, size32_t max_size, void *ptr)
  5065. {
  5066. if (lastpos!=pos)
  5067. throw MakeStringException(-1,"CSocketSerialStream: non-sequential read (%"I64F"d,%"I64F"d)",lastpos,pos);
  5068. size32_t size_read;
  5069. socket->readtms(ptr, 0, max_size, size_read, timeout);
  5070. lastpos = pos+size_read;
  5071. return size_read;
  5072. }
  5073. public:
  5074. CSocketSerialStream(ISocket * _socket, unsigned _timeout, offset_t _offset, size32_t _bufsize, IFileSerialStreamCallback *_tally)
  5075. : CSerialStreamBase(_offset, (offset_t)-1, _bufsize, _tally), socket(_socket), timeout(_timeout)
  5076. {
  5077. lastpos = _offset;
  5078. }
  5079. };
  5080. ISerialStream *createSocketSerialStream(ISocket * socket, unsigned timeoutms, size32_t bufsize, IFileSerialStreamCallback *callback)
  5081. {
  5082. if (!socket)
  5083. return NULL;
  5084. return new CSocketSerialStream(socket,timeoutms,0,bufsize,callback);
  5085. }
  5086. class CSimpleReadSerialStream: public CSerialStreamBase
  5087. {
  5088. Linked<ISimpleReadStream> input;
  5089. offset_t lastpos;
  5090. virtual size32_t rawread(offset_t pos, size32_t max_size, void *ptr)
  5091. {
  5092. if (lastpos!=pos)
  5093. throw MakeStringException(-1,"CSimpleReadSerialStream: non-sequential read (%"I64F"d,%"I64F"d)",lastpos,pos);
  5094. size32_t rd = input->read(max_size, ptr);
  5095. lastpos += rd;
  5096. return rd;
  5097. }
  5098. public:
  5099. CSimpleReadSerialStream(ISimpleReadStream * _input, offset_t _offset, size32_t _bufsize, IFileSerialStreamCallback *_tally)
  5100. : CSerialStreamBase(_offset, (offset_t)-1, _bufsize, _tally), input(_input)
  5101. {
  5102. lastpos = _offset;
  5103. }
  5104. void reset(offset_t _ofs, offset_t _len)
  5105. {
  5106. // assume knows what doing
  5107. lastpos = _ofs;
  5108. CSerialStreamBase::reset(_ofs,_len);
  5109. }
  5110. };
  5111. ISerialStream *createSimpleSerialStream(ISimpleReadStream * input, size32_t bufsize, IFileSerialStreamCallback *callback)
  5112. {
  5113. if (!input)
  5114. return NULL;
  5115. return new CSimpleReadSerialStream(input,0,bufsize,callback);
  5116. }
  5117. class CMemoryMappedSerialStream: public CInterface, implements ISerialStream
  5118. {
  5119. Linked<IMemoryMappedFile> mmfile;
  5120. const byte *mmbase;
  5121. memsize_t mmsize;
  5122. memsize_t mmofs;
  5123. bool eoinput;
  5124. IFileSerialStreamCallback *tally;
  5125. public:
  5126. IMPLEMENT_IINTERFACE;
  5127. CMemoryMappedSerialStream(IMemoryMappedFile *_mmfile, offset_t ofs, offset_t flen, IFileSerialStreamCallback *_tally)
  5128. : mmfile(_mmfile)
  5129. {
  5130. tally = _tally;
  5131. offset_t fs = mmfile->fileSize();
  5132. if ((memsize_t)fs!=fs)
  5133. throw MakeStringException(-1,"CMemoryMappedSerialStream file too big to be mapped");
  5134. if ((flen!=(offset_t)-1)&&(fs>flen))
  5135. fs = flen;
  5136. mmsize = (memsize_t)fs;
  5137. mmofs = (memsize_t)((ofs<fs)?ofs:fs);
  5138. mmbase = (const byte *)mmfile->base();
  5139. eoinput = false;
  5140. }
  5141. void reset(offset_t _ofs, offset_t _len)
  5142. {
  5143. offset_t fs = mmfile->fileSize();
  5144. if ((_len!=(offset_t)-1)&&(fs>_len))
  5145. fs = _len;
  5146. mmsize = (memsize_t)fs;
  5147. mmofs = (memsize_t)((_ofs<fs)?_ofs:fs);
  5148. mmsize = (memsize_t)fs;
  5149. }
  5150. CMemoryMappedSerialStream(const void *buf, memsize_t len, IFileSerialStreamCallback *_tally)
  5151. {
  5152. tally = _tally;
  5153. mmsize = len;
  5154. mmofs = 0;
  5155. mmbase = (const byte *)buf;
  5156. eoinput = false;
  5157. }
  5158. const void *peek(size32_t sz,size32_t &got)
  5159. {
  5160. memsize_t left = mmsize-mmofs;
  5161. if (sz>left)
  5162. sz = (size32_t)left;
  5163. else if (left>=UINT_MAX)
  5164. got = UINT_MAX-1;
  5165. else
  5166. got = (size32_t)left;
  5167. return mmbase+mmofs;
  5168. }
  5169. void get(size32_t len, void * ptr)
  5170. {
  5171. memsize_t left = mmsize-mmofs;
  5172. if (len>left) {
  5173. PrintStackReport();
  5174. ERRLOG("CFileSerialStream::get read past end of stream.3 (%u,%u)",(unsigned)len,(unsigned)left);
  5175. throw MakeStringException(-1,"CMemoryMappedSerialStream::get read past end of stream (%u,%u)",(unsigned)len,(unsigned)left);
  5176. }
  5177. if (tally)
  5178. tally->process(mmofs,len,mmbase+mmofs);
  5179. memcpy(ptr,mmbase+mmofs,len);
  5180. mmofs += len;
  5181. }
  5182. bool eos()
  5183. {
  5184. return (mmsize<=mmofs);
  5185. }
  5186. void skip(size32_t len)
  5187. {
  5188. memsize_t left = mmsize-mmofs;
  5189. if (len>left)
  5190. throw MakeStringException(-1,"CMemoryMappedSerialStream::skip read past end of stream (%u,%u)",(unsigned)len,(unsigned)left);
  5191. if (tally)
  5192. tally->process(mmofs,len,mmbase+mmofs);
  5193. mmofs += len;
  5194. }
  5195. offset_t tell()
  5196. {
  5197. return mmofs;
  5198. }
  5199. virtual void reset(offset_t _offset)
  5200. {
  5201. mmofs = (memsize_t)((_offset<mmsize)?_offset:mmsize);
  5202. eoinput = false;
  5203. }
  5204. };
  5205. ISerialStream *createFileSerialStream(IMemoryMappedFile *mmfile, offset_t ofs, offset_t flen, IFileSerialStreamCallback *callback)
  5206. {
  5207. return new CMemoryMappedSerialStream(mmfile,ofs,flen,callback);
  5208. }
  5209. ISerialStream *createMemorySerialStream(const void *buffer, memsize_t len, IFileSerialStreamCallback *callback)
  5210. {
  5211. return new CMemoryMappedSerialStream(buffer,len,callback);
  5212. }
  5213. class CMemoryBufferSerialStream: public CInterface, implements ISerialStream
  5214. {
  5215. MemoryBuffer & buffer;
  5216. IFileSerialStreamCallback *tally;
  5217. public:
  5218. IMPLEMENT_IINTERFACE;
  5219. CMemoryBufferSerialStream(MemoryBuffer & _buffer, IFileSerialStreamCallback * _tally)
  5220. : buffer(_buffer), tally(_tally)
  5221. {
  5222. }
  5223. virtual const void *peek(size32_t sz,size32_t &got)
  5224. {
  5225. got = buffer.remaining();
  5226. return buffer.readDirect(0);
  5227. }
  5228. virtual void get(size32_t len, void * ptr)
  5229. {
  5230. if (len>buffer.remaining()) {
  5231. ERRLOG("CFileSerialStream::get read past end of stream.4(%u,%u)",(unsigned)len,(unsigned)buffer.remaining());
  5232. throw MakeStringException(-1,"CMemoryBufferSerialStream::get read past end of stream (%u,%u)",(unsigned)len,(unsigned)buffer.remaining());
  5233. }
  5234. const void * data = buffer.readDirect(len);
  5235. if (tally)
  5236. tally->process(buffer.getPos()-len,len,data);
  5237. memcpy(ptr,data,len);
  5238. }
  5239. virtual bool eos()
  5240. {
  5241. return buffer.remaining() == 0;
  5242. }
  5243. virtual void skip(size32_t len)
  5244. {
  5245. if (len>buffer.remaining())
  5246. throw MakeStringException(-1,"CMemoryBufferSerialStream::skip read past end of stream (%u,%u)",(unsigned)len,(unsigned)buffer.remaining());
  5247. const void * data = buffer.readDirect(len);
  5248. if (tally)
  5249. tally->process(buffer.getPos()-len,len,data);
  5250. }
  5251. virtual offset_t tell()
  5252. {
  5253. return buffer.getPos();
  5254. }
  5255. virtual void reset(offset_t _offset,offset_t _len)
  5256. {
  5257. size32_t ofs = (size32_t)_offset;
  5258. assertex(ofs==_offset);
  5259. assertex((_len==(offset_t)-1)||(_len>=buffer.length())); // don't support len on memory buffer
  5260. buffer.reset(ofs);
  5261. }
  5262. };
  5263. ISerialStream *createMemoryBufferSerialStream(MemoryBuffer & buffer, IFileSerialStreamCallback *callback)
  5264. {
  5265. return new CMemoryBufferSerialStream(buffer,callback);
  5266. }
  5267. // Memory Mapped Files
  5268. #define MEMORYMAP_PAGESIZE (0x1000) // could be different but won't ever be!
  5269. class CMemoryMappedFile: public CInterface, implements IMemoryMappedFile
  5270. {
  5271. byte *ptr; // base
  5272. offset_t ofs;
  5273. offset_t realofs; // rounded down to page size
  5274. offset_t filesize;
  5275. memsize_t size;
  5276. bool writeaccess;
  5277. memsize_t windowsize;
  5278. HANDLE hfile;
  5279. #ifdef _WIN32
  5280. static size32_t pagesize;
  5281. HANDLE hmap;
  5282. #endif
  5283. Linked<IFile> file;
  5284. inline offset_t pageround(offset_t o)
  5285. {
  5286. #ifdef _WIN32
  5287. return o-(o%pagesize);
  5288. #else
  5289. return o-(o%MEMORYMAP_PAGESIZE);
  5290. #endif
  5291. }
  5292. inline void * realptr()
  5293. {
  5294. return ptr?(ptr-(ofs-realofs)):NULL;
  5295. }
  5296. inline memsize_t realsize()
  5297. {
  5298. return (memsize_t)(size+(ofs-realofs));
  5299. }
  5300. public:
  5301. IMPLEMENT_IINTERFACE;
  5302. CMemoryMappedFile(HANDLE _hfile, offset_t _filesize, offset_t _ofs, memsize_t _size, bool _writeaccess)
  5303. {
  5304. hfile = _hfile;
  5305. #ifdef _WIN32
  5306. if (pagesize==0) {
  5307. SYSTEM_INFO sysinfo;
  5308. GetSystemInfo(&sysinfo);
  5309. pagesize = sysinfo.dwAllocationGranularity;
  5310. }
  5311. hmap = NULLFILE;
  5312. #endif
  5313. ptr = NULL;
  5314. filesize = _filesize;
  5315. windowsize = _size; // first open size assumed to be window size
  5316. reinit(_ofs, _size, _writeaccess);
  5317. }
  5318. ~CMemoryMappedFile()
  5319. {
  5320. #ifdef _WIN32
  5321. if (hmap!=NULLFILE)
  5322. CloseHandle(hmap);
  5323. CloseHandle(hfile);
  5324. #else
  5325. close(hfile);
  5326. #endif
  5327. }
  5328. byte *base() { return ptr; }
  5329. offset_t offset() { return ofs; }
  5330. virtual memsize_t length() { return size; }
  5331. virtual offset_t fileSize() { return filesize; }
  5332. virtual int compareWithin(const void *p)
  5333. {
  5334. if (p<ptr)
  5335. return -1;
  5336. if (p>=size+ptr)
  5337. return 1;
  5338. return 0;
  5339. }
  5340. bool writeAccess() { return writeaccess; }
  5341. void flush()
  5342. {
  5343. if (ptr) {
  5344. #ifdef _WIN32
  5345. FlushViewOfFile(realptr(),0);
  5346. #elif defined(__linux__)
  5347. msync(realptr(),realsize(),MS_SYNC);
  5348. #else
  5349. UNIMPLEMENTED;
  5350. #endif
  5351. }
  5352. }
  5353. byte *nextPtr(const void *p,offset_t skip, memsize_t req, memsize_t &got)
  5354. {
  5355. // for scanning in sequence
  5356. if (p==NULL)
  5357. p = ptr;
  5358. else if ((p<ptr)||(p>ptr+size))
  5359. throw MakeStringException(-1,"CMemoryMappedFile::nextPtr - outside map");
  5360. memsize_t d = (byte *)p-ptr;
  5361. offset_t o = ofs+d+skip;
  5362. if (o>=filesize) {
  5363. got = 0;
  5364. return NULL;
  5365. }
  5366. offset_t left = filesize-o;
  5367. if (left<req)
  5368. req = (memsize_t)left;
  5369. if (o+req-ofs>size) {
  5370. reinit(o,windowsize,writeaccess);
  5371. assertex(o==ofs);
  5372. got = windowsize;
  5373. if (left<got)
  5374. got = (memsize_t)left;
  5375. return ptr;
  5376. }
  5377. got = (memsize_t)(size+o-ofs);
  5378. if (left<got)
  5379. got = (memsize_t)left;
  5380. return (byte *)p+skip;
  5381. }
  5382. virtual void reinit(offset_t _ofs, memsize_t _size, bool _writeaccess)
  5383. {
  5384. writeaccess = _writeaccess;
  5385. if (ptr) {
  5386. #ifdef _WIN32
  5387. // no need to unmap view
  5388. if (hmap != INVALID_HANDLE_VALUE) {
  5389. CloseHandle(hmap);
  5390. hmap = INVALID_HANDLE_VALUE;
  5391. }
  5392. #elif defined(__linux__)
  5393. munmap(realptr(),realsize());
  5394. // error checking TBD
  5395. #else
  5396. UNIMPLEMENTED;
  5397. #endif
  5398. ptr = NULL;
  5399. }
  5400. if (_ofs>filesize)
  5401. ofs = filesize;
  5402. else
  5403. ofs = _ofs;
  5404. realofs = pageround(ofs);
  5405. if (_size==(memsize_t)-1) {
  5406. size = (memsize_t)(filesize-_ofs);
  5407. if (size!=(filesize-_ofs))
  5408. throw MakeStringException(-1,"CMemoryMappedFile::reinit file too big");
  5409. }
  5410. else
  5411. size = _size;
  5412. memsize_t mapsz = realsize();
  5413. if (filesize-realofs<mapsz)
  5414. mapsz = (memsize_t)(filesize-realofs);
  5415. #ifdef _WIN32
  5416. LARGE_INTEGER li;
  5417. if (hmap == INVALID_HANDLE_VALUE) {
  5418. hmap = CreateFileMapping(hfile,NULL,writeaccess?PAGE_READWRITE:PAGE_READONLY,0, 0, NULL);
  5419. if (!hmap) {
  5420. DWORD err = GetLastError();
  5421. throw MakeOsException(err,"CMemoryMappedFile::reinit");
  5422. }
  5423. }
  5424. li.QuadPart = realofs;
  5425. ptr = (byte *) MapViewOfFile(hmap, writeaccess?(FILE_MAP_READ|FILE_MAP_WRITE):FILE_MAP_READ, li.HighPart, li.LowPart, mapsz);
  5426. if (!ptr) {
  5427. DWORD err = GetLastError();
  5428. throw MakeOsException(err,"CMemoryMappedFile::reinit");
  5429. }
  5430. #elif defined (_linux__)
  5431. ptr = (byte *) mmap(NULL, mapsz, writeaccess?(PROT_READ|PROT_WRITE):PROT_READ, MAP_SHARED|MAP_NORESERVE, hfile, realofs);
  5432. // error checking TBD
  5433. #else
  5434. UNIMPLEMENTED;
  5435. #endif
  5436. if (ptr)
  5437. ptr += (ofs-realofs);
  5438. }
  5439. };
  5440. #ifdef _WIN32
  5441. size32_t CMemoryMappedFile::pagesize=0;
  5442. #endif
  5443. // TBD MADV_SEQUENTIAL & MADV_WILLNEED?
  5444. IMemoryMappedFile *CFile::openMemoryMapped(offset_t ofs, memsize_t len, bool write)
  5445. {
  5446. HANDLE hfile = openHandle(write?IFOcreaterw:IFOread, IFSHread, false);
  5447. if (hfile == NULLFILE)
  5448. return NULL;
  5449. return new CMemoryMappedFile(hfile,size(),ofs,len,write);
  5450. }
  5451. bool isSpecialPath(const char *path)
  5452. {
  5453. // used for remote queries
  5454. if (!path)
  5455. return false;
  5456. if (isPathSepChar(path[0])&&(path[0]==path[1])) {
  5457. path += 2;
  5458. loop {
  5459. if (*path=='/')
  5460. return (path[1]=='>');
  5461. if (!*path||(*path=='\\'))
  5462. return false;
  5463. path++;
  5464. }
  5465. }
  5466. return (path&&(*path=='/')&&(path[1]=='>'));
  5467. }
  5468. size32_t SendFile(ISocket *target, IFileIO *fileio,offset_t start,size32_t len)
  5469. {
  5470. assertex(target);
  5471. assertex(fileio);
  5472. offset_t fsz = fileio->size();
  5473. if (start>=fsz)
  5474. return 0;
  5475. if (start+len>fsz)
  5476. len = (size32_t)(fsz-start);
  5477. if (!len)
  5478. return 0;
  5479. CFileIO *cfile = QUERYINTERFACE(fileio,CFileIO);
  5480. if (cfile) {
  5481. HANDLE fh = cfile->queryHandle();
  5482. if ((fh!=(HANDLE)0)&&(fh!=(HANDLE)-1)) {
  5483. unsigned sh = target->OShandle();
  5484. if ((sh!=(unsigned)0)&&(sh!=(unsigned)-1)) {
  5485. #ifdef _WIN32
  5486. // MORE - should there be something here?
  5487. #elif defined(__linux__)
  5488. // TransmitFile not in std DLLs so don't bother with
  5489. off_t ofs = start;
  5490. ssize_t sent = ::sendfile(sh,fh,&ofs,len);
  5491. if (sent!=(ssize_t)-1)
  5492. return (size32_t)sent;
  5493. int err = errno;
  5494. if ((err!=EINVAL)&&(err!=ENOSYS))
  5495. throw MakeOsException(err,"sendfile");
  5496. #else
  5497. UNIMPLEMENTED;
  5498. #endif
  5499. }
  5500. }
  5501. }
  5502. #ifdef _DEBUG
  5503. #ifndef _WIN32
  5504. WARNLOG("SendFile falling back");
  5505. #endif
  5506. #endif
  5507. // fallback
  5508. MemoryAttr ma;
  5509. void *buf = ma.allocate(len);
  5510. size32_t rd = fileio->read(start,len,buf);
  5511. target->write(buf,rd);
  5512. return rd;
  5513. }
  5514. void asyncClose(IFileIO *io)
  5515. {
  5516. if (!io)
  5517. return;
  5518. static CriticalSection ADsect;
  5519. CriticalBlock block(ADsect);
  5520. static Owned<IWorkQueueThread> adwp = createWorkQueueThread();
  5521. class cWQI: public CInterface,implements IWorkQueueItem
  5522. {
  5523. Owned<IFileIO> io;
  5524. public:
  5525. IMPLEMENT_IINTERFACE;
  5526. cWQI(IFileIO *_io)
  5527. : io(_io)
  5528. {
  5529. }
  5530. void execute()
  5531. {
  5532. io.clear();
  5533. }
  5534. };
  5535. adwp->post(new cWQI(io));
  5536. };
  5537. int stdIoHandle(const char *path)
  5538. {
  5539. if (strcmp(path,"stdin:")==0)
  5540. return 0;
  5541. if (strcmp(path,"stdout:")==0)
  5542. return 1;
  5543. if (strcmp(path,"stderr:")==0)
  5544. return 2;
  5545. return -1;
  5546. }
  5547. extern jlib_decl bool containsFileWildcard(const char * path)
  5548. {
  5549. if (!path)
  5550. return false;
  5551. return strchr(path, '*') || strchr(path, '?');
  5552. }
  5553. extern jlib_decl bool isDirectory(const char * path)
  5554. {
  5555. Owned<IFile> file = createIFile(path);
  5556. return file->isDirectory() == foundYes;
  5557. }
  5558. // IFileIOCache
  5559. class CLazyFileIOCache;
  5560. class CCachedFileIO: public CInterface, implements IFileIO
  5561. {
  5562. CLazyFileIOCache &owner;
  5563. RemoteFilename filename;
  5564. CriticalSection &sect;
  5565. IFOmode mode;
  5566. void writeNotSupported(const char *s)
  5567. {
  5568. StringBuffer tmp;
  5569. filename.getRemotePath(tmp);
  5570. throw MakeStringException(-1, "CCachedFileIO(%s) %s not supported", tmp.str(), s);
  5571. }
  5572. public:
  5573. unsigned accesst;
  5574. Owned<IFileIO> cachedio;
  5575. CCachedFileIO(CLazyFileIOCache &_owner, CriticalSection &_sect, RemoteFilename &_filename, IFOmode _mode)
  5576. : owner(_owner), sect(_sect)
  5577. {
  5578. filename.set(_filename);
  5579. mode = _mode;
  5580. }
  5581. virtual void Link(void) const { CInterface::Link(); } \
  5582. virtual bool Release(void) const;
  5583. IFileIO *open();
  5584. size32_t read(offset_t pos, size32_t len, void * data)
  5585. {
  5586. CriticalBlock block(sect);
  5587. Owned<IFileIO> io = open();
  5588. return io->read(pos,len,data);
  5589. }
  5590. offset_t size()
  5591. {
  5592. CriticalBlock block(sect);
  5593. Owned<IFileIO> io = open();
  5594. return io->size();
  5595. }
  5596. size32_t write(offset_t pos, size32_t len, const void * data)
  5597. {
  5598. CriticalBlock block(sect);
  5599. Owned<IFileIO> io = open();
  5600. return io->write(pos,len,data);
  5601. }
  5602. virtual void flush()
  5603. {
  5604. CriticalBlock block(sect);
  5605. if (cachedio)
  5606. cachedio->flush();
  5607. }
  5608. virtual void close()
  5609. {
  5610. CriticalBlock block(sect);
  5611. if (cachedio)
  5612. {
  5613. cachedio->close();
  5614. cachedio.clear();
  5615. }
  5616. }
  5617. offset_t appendFile(IFile *file,offset_t pos,offset_t len)
  5618. {
  5619. CriticalBlock block(sect);
  5620. Owned<IFileIO> io = open();
  5621. return io->appendFile(file,pos,len);
  5622. }
  5623. void setSize(offset_t size)
  5624. {
  5625. CriticalBlock block(sect);
  5626. Owned<IFileIO> io = open();
  5627. io->setSize(size);
  5628. }
  5629. };
  5630. class CLazyFileIOCache: public CInterface, implements IFileIOCache
  5631. {
  5632. CriticalSection sect;
  5633. unsigned max;
  5634. PointerIArrayOf<CCachedFileIO> cache;
  5635. public:
  5636. IMPLEMENT_IINTERFACE;
  5637. CLazyFileIOCache(unsigned _max)
  5638. {
  5639. max = _max;
  5640. }
  5641. CCachedFileIO *addFile( RemoteFilename &filename, IFOmode mode )
  5642. {
  5643. // check exists
  5644. unsigned i = cache.ordinality();
  5645. ForEachItemIn(i2,cache) {
  5646. if (!cache.item(i2)) {
  5647. i = i2;
  5648. break;
  5649. }
  5650. }
  5651. Linked<CCachedFileIO> ret = new CCachedFileIO(*this,sect,filename,mode);
  5652. if (i<cache.ordinality())
  5653. cache.replace(ret,i);
  5654. else
  5655. cache.append(ret);
  5656. return ret.getClear();
  5657. }
  5658. void checkCache(unsigned wanted=1)
  5659. {
  5660. // called in sect
  5661. loop {
  5662. CCachedFileIO *oldest = NULL;
  5663. unsigned oldestt = 0;
  5664. unsigned t = msTick();
  5665. unsigned n = wanted;
  5666. ForEachItemIn(i1,cache) {
  5667. CCachedFileIO *cio = cache.item(i1);
  5668. if (cio&&cio->cachedio.get()) {
  5669. if (t-cio->accesst>=oldestt) {
  5670. oldest = cio;
  5671. oldestt = t-cio->accesst;
  5672. }
  5673. n++;
  5674. }
  5675. }
  5676. if (n<max)
  5677. break;
  5678. if (!oldest)
  5679. break;
  5680. oldest->cachedio.clear();
  5681. //If previously had max ios then we now have space.
  5682. if (n == max)
  5683. break;
  5684. }
  5685. }
  5686. const CCachedFileIO *removeFile(const CCachedFileIO *io)
  5687. {
  5688. CriticalBlock block(sect);
  5689. ForEachItemIn(idx, cache)
  5690. {
  5691. if (io == cache.item(idx))
  5692. {
  5693. cache.replace(NULL, idx, true);
  5694. break;
  5695. }
  5696. }
  5697. return io;
  5698. }
  5699. };
  5700. bool CCachedFileIO::Release(void) const
  5701. {
  5702. if (CInterface::Release())
  5703. return true;
  5704. if (!CInterface::IsShared())
  5705. return owner.removeFile(this)->Release();
  5706. return false;
  5707. }
  5708. IFileIO *CCachedFileIO::open()
  5709. {
  5710. // called in sect
  5711. if (!cachedio) {
  5712. owner.checkCache();
  5713. Owned<IFile> inFile = createIFile(filename);
  5714. cachedio.setown(inFile->open(mode));
  5715. if (!cachedio.get()) {
  5716. StringBuffer tmp;
  5717. filename.getRemotePath(tmp);
  5718. throw MakeStringException(-1, "CCachedFileIO::open(%d) '%s' failed", (int)mode, tmp.str());
  5719. }
  5720. }
  5721. accesst = msTick();
  5722. return cachedio.getLink();
  5723. }
  5724. IFileIOCache* createFileIOCache(unsigned max)
  5725. {
  5726. return new CLazyFileIOCache(max);
  5727. }
  5728. extern jlib_decl IFile * createSentinelTarget()
  5729. {
  5730. const char * sentinelFilename = getenv("SENTINEL");
  5731. if (sentinelFilename)
  5732. return createIFile(sentinelFilename);
  5733. else
  5734. return NULL;
  5735. }
  5736. extern jlib_decl void removeSentinelFile(IFile * sentinelFile)
  5737. {
  5738. if (sentinelFile)
  5739. {
  5740. if(sentinelFile->exists() && !sentinelFile->isDirectory())
  5741. {
  5742. DBGLOG("Removing sentinel file %s", sentinelFile->queryFilename());
  5743. try
  5744. {
  5745. sentinelFile->remove();
  5746. }
  5747. catch(IException *E)
  5748. {
  5749. StringBuffer s;
  5750. EXCLOG(E, s.appendf("Failed to remove sentinel file %s", sentinelFile->queryFilename()).str());
  5751. E->Release();
  5752. throw MakeOsException(errno, "removeSentinelFile - file cannot be removed.");
  5753. }
  5754. }
  5755. }
  5756. }
  5757. extern jlib_decl void writeSentinelFile(IFile * sentinelFile)
  5758. {
  5759. if ( sentinelFile )
  5760. {
  5761. DBGLOG("Creating sentinel file %s for rerun from script", sentinelFile->queryFilename());
  5762. try
  5763. {
  5764. Owned<IFileIO> sentinel = sentinelFile->open(IFOcreate);
  5765. sentinel->write(0, 5, "rerun");
  5766. }
  5767. catch(IException *E)
  5768. {
  5769. StringBuffer s;
  5770. EXCLOG(E, s.appendf("Failed to create sentinel file %s for rerun from script", sentinelFile->queryFilename()).str());
  5771. E->Release();
  5772. throw MakeOsException(errno, "writeSentinelFile - file not created.");
  5773. }
  5774. }
  5775. }
  5776. jlib_decl StringBuffer & appendCurrentDirectory(StringBuffer & target, bool blankIfFails)
  5777. {
  5778. char temp[_MAX_PATH+1];
  5779. if (!getcwd(temp,sizeof(temp)))
  5780. {
  5781. if (blankIfFails)
  5782. return target;
  5783. throw MakeStringException(JLIBERR_InternalError, "getcwd failed (%d)", errno);
  5784. }
  5785. return target.append(temp);
  5786. }