Something went wrong. Try again.
Reactos
Something went wrong. Try again.
182 kB · 5048 lines
C
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049/* Copyright (c) Mark Harmstone 2016-17 * * This file is part of WinBtrfs. * * WinBtrfs is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public Licence as published by * the Free Software Foundation, either version 3 of the Licence, or * (at your option) any later version. * * WinBtrfs is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public Licence for more details. * * You should have received a copy of the GNU Lesser General Public Licence * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#ifndef __REACTOS__#include <sys/stat.h>#endif /* __REACTOS__ */#include "btrfs_drv.h"#include "crc32c.h"#include <ntddstor.h>
extern PDEVICE_OBJECT master_devobj;extern tFsRtlGetEcpListFromIrp fFsRtlGetEcpListFromIrp;extern tFsRtlGetNextExtraCreateParameter fFsRtlGetNextExtraCreateParameter;extern tFsRtlValidateReparsePointBuffer fFsRtlValidateReparsePointBuffer;
static const WCHAR datastring[] = L"::$DATA";
static const char root_dir[] = "$Root";static const WCHAR root_dir_utf16[] = L"$Root";
// Windows 10#define ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED 0x0002#define ATOMIC_CREATE_ECP_IN_FLAG_OP_FLAGS_SPECIFIED 0x0080#define ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT 0x0100
#define ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET 0x0002#define ATOMIC_CREATE_ECP_OUT_FLAG_OP_FLAGS_HONORED 0x0080
#define ATOMIC_CREATE_ECP_IN_OP_FLAG_CASE_SENSITIVE_FLAGS_SPECIFIED 1#define ATOMIC_CREATE_ECP_OUT_OP_FLAG_CASE_SENSITIVE_FLAGS_SET 1
#ifndef SL_IGNORE_READONLY_ATTRIBUTE#define SL_IGNORE_READONLY_ATTRIBUTE 0x40 // introduced in Windows 10, not in mingw#endif
typedef struct _FILE_TIMESTAMPS { LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime;} FILE_TIMESTAMPS, *PFILE_TIMESTAMPS;
typedef struct _ATOMIC_CREATE_ECP_CONTEXT { USHORT Size; USHORT InFlags; USHORT OutFlags; USHORT ReparseBufferLength; PREPARSE_DATA_BUFFER ReparseBuffer; LONGLONG FileSize; LONGLONG ValidDataLength; PFILE_TIMESTAMPS FileTimestamps; ULONG FileAttributes; ULONG UsnSourceInfo; USN Usn; ULONG SuppressFileAttributeInheritanceMask; ULONG InOpFlags; ULONG OutOpFlags; ULONG InGenFlags; ULONG OutGenFlags; ULONG CaseSensitiveFlagsMask; ULONG InCaseSensitiveFlags; ULONG OutCaseSensitiveFlags;} ATOMIC_CREATE_ECP_CONTEXT, *PATOMIC_CREATE_ECP_CONTEXT;
static const GUID GUID_ECP_ATOMIC_CREATE = { 0x4720bd83, 0x52ac, 0x4104, { 0xa1, 0x30, 0xd1, 0xec, 0x6a, 0x8c, 0xc8, 0xe5 } };static const GUID GUID_ECP_QUERY_ON_CREATE = { 0x1aca62e9, 0xabb4, 0x4ff2, { 0xbb, 0x5c, 0x1c, 0x79, 0x02, 0x5e, 0x41, 0x7f } };static const GUID GUID_ECP_CREATE_REDIRECTION = { 0x188d6bd6, 0xa126, 0x4fa8, { 0xbd, 0xf2, 0x1c, 0xcd, 0xf8, 0x96, 0xf3, 0xe0 } };
typedef struct { device_extension* Vcb; ACCESS_MASK granted_access; file_ref* fileref; NTSTATUS Status; KEVENT event;} oplock_context;
fcb* create_fcb(device_extension* Vcb, POOL_TYPE pool_type) { fcb* fcb;
if (pool_type == NonPagedPool) { fcb = ExAllocatePoolWithTag(pool_type, sizeof(struct _fcb), ALLOC_TAG); if (!fcb) { ERR("out of memory\n"); return NULL; } } else { fcb = ExAllocateFromPagedLookasideList(&Vcb->fcb_lookaside); if (!fcb) { ERR("out of memory\n"); return NULL; } }
#ifdef DEBUG_FCB_REFCOUNTS WARN("allocating fcb %p\n", fcb);#endif RtlZeroMemory(fcb, sizeof(struct _fcb)); fcb->pool_type = pool_type;
fcb->Header.NodeTypeCode = BTRFS_NODE_TYPE_FCB; fcb->Header.NodeByteSize = sizeof(struct _fcb);
fcb->nonpaged = ExAllocateFromNPagedLookasideList(&Vcb->fcb_np_lookaside); if (!fcb->nonpaged) { ERR("out of memory\n");
if (pool_type == NonPagedPool) ExFreePool(fcb); else ExFreeToPagedLookasideList(&Vcb->fcb_lookaside, fcb);
return NULL; } RtlZeroMemory(fcb->nonpaged, sizeof(struct _fcb_nonpaged));
ExInitializeResourceLite(&fcb->nonpaged->paging_resource); fcb->Header.PagingIoResource = &fcb->nonpaged->paging_resource;
ExInitializeFastMutex(&fcb->nonpaged->HeaderMutex); FsRtlSetupAdvancedHeader(&fcb->Header, &fcb->nonpaged->HeaderMutex);
fcb->refcount = 1;#ifdef DEBUG_FCB_REFCOUNTS WARN("fcb %p: refcount now %i\n", fcb, fcb->refcount);#endif
ExInitializeResourceLite(&fcb->nonpaged->resource); fcb->Header.Resource = &fcb->nonpaged->resource;
ExInitializeResourceLite(&fcb->nonpaged->dir_children_lock);
FsRtlInitializeFileLock(&fcb->lock, NULL, NULL); FsRtlInitializeOplock(fcb_oplock(fcb));
InitializeListHead(&fcb->extents); InitializeListHead(&fcb->hardlinks); InitializeListHead(&fcb->xattrs);
InitializeListHead(&fcb->dir_children_index); InitializeListHead(&fcb->dir_children_hash); InitializeListHead(&fcb->dir_children_hash_uc);
return fcb;}
file_ref* create_fileref(device_extension* Vcb) { file_ref* fr;
fr = ExAllocateFromPagedLookasideList(&Vcb->fileref_lookaside); if (!fr) { ERR("out of memory\n"); return NULL; }
RtlZeroMemory(fr, sizeof(file_ref));
fr->refcount = 1;
#ifdef DEBUG_FCB_REFCOUNTS WARN("fileref %p: refcount now 1\n", fr);#endif
InitializeListHead(&fr->children);
return fr;}
NTSTATUS find_file_in_dir(PUNICODE_STRING filename, fcb* fcb, root** subvol, uint64_t* inode, dir_child** pdc, bool case_sensitive) { NTSTATUS Status; UNICODE_STRING fnus; uint32_t hash; LIST_ENTRY* le; uint8_t c; bool locked = false;
if (!case_sensitive) { Status = RtlUpcaseUnicodeString(&fnus, filename, true);
if (!NT_SUCCESS(Status)) { ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); return Status; } } else fnus = *filename;
Status = check_file_name_valid(filename, false, false); if (!NT_SUCCESS(Status)) return Status;
hash = calc_crc32c(0xffffffff, (uint8_t*)fnus.Buffer, fnus.Length);
c = hash >> 24;
if (!ExIsResourceAcquiredSharedLite(&fcb->nonpaged->dir_children_lock)) { ExAcquireResourceSharedLite(&fcb->nonpaged->dir_children_lock, true); locked = true; }
if (case_sensitive) { if (!fcb->hash_ptrs[c]) { Status = STATUS_OBJECT_NAME_NOT_FOUND; goto end; }
le = fcb->hash_ptrs[c]; while (le != &fcb->dir_children_hash) { dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash);
if (dc->hash == hash) { if (dc->name.Length == fnus.Length && RtlCompareMemory(dc->name.Buffer, fnus.Buffer, fnus.Length) == fnus.Length) { if (dc->key.obj_type == TYPE_ROOT_ITEM) { LIST_ENTRY* le2;
*subvol = NULL;
le2 = fcb->Vcb->roots.Flink; while (le2 != &fcb->Vcb->roots) { root* r2 = CONTAINING_RECORD(le2, root, list_entry);
if (r2->id == dc->key.obj_id) { *subvol = r2; break; }
le2 = le2->Flink; }
*inode = SUBVOL_ROOT_INODE; } else { *subvol = fcb->subvol; *inode = dc->key.obj_id; }
*pdc = dc;
Status = STATUS_SUCCESS; goto end; } } else if (dc->hash > hash) { Status = STATUS_OBJECT_NAME_NOT_FOUND; goto end; }
le = le->Flink; } } else { if (!fcb->hash_ptrs_uc[c]) { Status = STATUS_OBJECT_NAME_NOT_FOUND; goto end; }
le = fcb->hash_ptrs_uc[c]; while (le != &fcb->dir_children_hash_uc) { dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
if (dc->hash_uc == hash) { if (dc->name_uc.Length == fnus.Length && RtlCompareMemory(dc->name_uc.Buffer, fnus.Buffer, fnus.Length) == fnus.Length) { if (dc->key.obj_type == TYPE_ROOT_ITEM) { LIST_ENTRY* le2;
*subvol = NULL;
le2 = fcb->Vcb->roots.Flink; while (le2 != &fcb->Vcb->roots) { root* r2 = CONTAINING_RECORD(le2, root, list_entry);
if (r2->id == dc->key.obj_id) { *subvol = r2; break; }
le2 = le2->Flink; }
*inode = SUBVOL_ROOT_INODE; } else { *subvol = fcb->subvol; *inode = dc->key.obj_id; }
*pdc = dc;
Status = STATUS_SUCCESS; goto end; } } else if (dc->hash_uc > hash) { Status = STATUS_OBJECT_NAME_NOT_FOUND; goto end; }
le = le->Flink; } }
Status = STATUS_OBJECT_NAME_NOT_FOUND;
end: if (locked) ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
if (!case_sensitive) ExFreePool(fnus.Buffer);
return Status;}
static NTSTATUS split_path(device_extension* Vcb, PUNICODE_STRING path, LIST_ENTRY* parts, bool* stream) { ULONG len, i; bool has_stream; WCHAR* buf; name_bit* nb; NTSTATUS Status;
len = path->Length / sizeof(WCHAR); if (len > 0 && (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\')) len--;
if (len == 0 || (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\')) { WARN("zero-length filename part\n"); return STATUS_OBJECT_NAME_INVALID; }
has_stream = false; for (i = 0; i < len; i++) { if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') { has_stream = false; } else if (path->Buffer[i] == ':') { has_stream = true; } }
buf = path->Buffer;
for (i = 0; i < len; i++) { if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') { if (buf[0] == '/' || buf[0] == '\\') { WARN("zero-length filename part\n"); Status = STATUS_OBJECT_NAME_INVALID; goto cleanup; }
nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside); if (!nb) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; }
nb->us.Buffer = buf; nb->us.Length = nb->us.MaximumLength = (USHORT)(&path->Buffer[i] - buf) * sizeof(WCHAR); InsertTailList(parts, &nb->list_entry);
buf = &path->Buffer[i+1]; } }
nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside); if (!nb) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; }
nb->us.Buffer = buf; nb->us.Length = nb->us.MaximumLength = (USHORT)(&path->Buffer[i] - buf) * sizeof(WCHAR); InsertTailList(parts, &nb->list_entry);
if (has_stream) { static const WCHAR datasuf[] = {':','$','D','A','T','A',0}; UNICODE_STRING dsus;
dsus.Buffer = (WCHAR*)datasuf; dsus.Length = dsus.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
for (i = 0; i < nb->us.Length / sizeof(WCHAR); i++) { if (nb->us.Buffer[i] == ':') { name_bit* nb2;
if (i + 1 == nb->us.Length / sizeof(WCHAR)) { WARN("zero-length stream name\n"); Status = STATUS_OBJECT_NAME_INVALID; goto cleanup; }
nb2 = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside); if (!nb2) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; }
nb2->us.Buffer = &nb->us.Buffer[i+1]; nb2->us.Length = nb2->us.MaximumLength = (uint16_t)(nb->us.Length - (i * sizeof(WCHAR)) - sizeof(WCHAR)); InsertTailList(parts, &nb2->list_entry);
nb->us.Length = (uint16_t)i * sizeof(WCHAR); nb->us.MaximumLength = nb->us.Length;
nb = nb2;
break; } }
// FIXME - should comparison be case-insensitive? // remove :$DATA suffix if (nb->us.Length >= dsus.Length && RtlCompareMemory(&nb->us.Buffer[(nb->us.Length - dsus.Length)/sizeof(WCHAR)], dsus.Buffer, dsus.Length) == dsus.Length) nb->us.Length -= dsus.Length;
if (nb->us.Length == 0) { RemoveTailList(parts); ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
has_stream = false; } }
// if path is just stream name, remove first empty item if (has_stream && path->Length >= sizeof(WCHAR) && path->Buffer[0] == ':') { name_bit *nb1 = CONTAINING_RECORD(RemoveHeadList(parts), name_bit, list_entry);
ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb1); }
*stream = has_stream;
return STATUS_SUCCESS;
cleanup: while (!IsListEmpty(parts)) { nb = CONTAINING_RECORD(RemoveHeadList(parts), name_bit, list_entry);
ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb); }
return Status;}
NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, void* csum, uint64_t start, uint64_t length, PIRP Irp) { NTSTATUS Status; KEY searchkey; traverse_ptr tp, next_tp; uint64_t i, j; bool b; void* ptr = csum;
searchkey.obj_id = EXTENT_CSUM_ID; searchkey.obj_type = TYPE_EXTENT_CSUM; searchkey.offset = start;
Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, false, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08lx\n", Status); return Status; }
i = 0; do { if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { ULONG readlen;
if (start < tp.item->key.offset) j = 0; else j = ((start - tp.item->key.offset) >> Vcb->sector_shift) + i;
if (j * Vcb->csum_size > tp.item->size || tp.item->key.offset > start + (i << Vcb->sector_shift)) { ERR("checksum not found for %I64x\n", start + (i << Vcb->sector_shift)); return STATUS_INTERNAL_ERROR; }
readlen = (ULONG)min((tp.item->size / Vcb->csum_size) - j, length - i); RtlCopyMemory(ptr, tp.item->data + (j * Vcb->csum_size), readlen * Vcb->csum_size);
ptr = (uint8_t*)ptr + (readlen * Vcb->csum_size); i += readlen;
if (i == length) break; }
b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
if (b) tp = next_tp; } while (b);
if (i < length) { ERR("could not read checksums: offset %I64x, length %I64x sectors\n", start, length); return STATUS_INTERNAL_ERROR; }
return STATUS_SUCCESS;}
NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, fcb* fcb, bool ignore_size, PIRP Irp) { KEY searchkey; traverse_ptr tp, next_tp; NTSTATUS Status; ULONG num_children = 0; uint64_t max_index = 2;
fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG); if (!fcb->hash_ptrs) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG); if (!fcb->hash_ptrs_uc) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
if (!ignore_size && fcb->inode_item.st_size == 0) return STATUS_SUCCESS;
searchkey.obj_id = fcb->inode; searchkey.obj_type = TYPE_DIR_INDEX; searchkey.offset = 2;
Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, false, Irp); if (!NT_SUCCESS(Status)) { ERR("find_item returned %08lx\n", Status); return Status; }
if (keycmp(tp.item->key, searchkey) == -1) { if (find_next_item(Vcb, &tp, &next_tp, false, Irp)) { tp = next_tp; TRACE("moving on to %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); } }
while (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { DIR_ITEM* di = (DIR_ITEM*)tp.item->data; dir_child* dc; ULONG utf16len;
if (tp.item->size < sizeof(DIR_ITEM)) { WARN("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM)); goto cont; }
if (di->n == 0) { WARN("(%I64x,%x,%I64x): DIR_ITEM name length is zero\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); goto cont; }
Status = utf8_to_utf16(NULL, 0, &utf16len, di->name, di->n); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 1 returned %08lx\n", Status); goto cont; }
dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG); if (!dc) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
dc->key = di->key; dc->index = tp.item->key.offset; dc->type = di->type; dc->fileref = NULL; dc->root_dir = false;
max_index = dc->index;
dc->utf8.MaximumLength = dc->utf8.Length = di->n; dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG); if (!dc->utf8.Buffer) { ERR("out of memory\n"); ExFreePool(dc); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(dc->utf8.Buffer, di->name, di->n);
dc->name.MaximumLength = dc->name.Length = (uint16_t)utf16len; dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.MaximumLength, ALLOC_TAG); if (!dc->name.Buffer) { ERR("out of memory\n"); ExFreePool(dc->utf8.Buffer); ExFreePool(dc); return STATUS_INSUFFICIENT_RESOURCES; }
Status = utf8_to_utf16(dc->name.Buffer, utf16len, &utf16len, di->name, di->n); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 2 returned %08lx\n", Status); ExFreePool(dc->utf8.Buffer); ExFreePool(dc->name.Buffer); ExFreePool(dc); goto cont; }
Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true); if (!NT_SUCCESS(Status)) { ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); ExFreePool(dc->utf8.Buffer); ExFreePool(dc->name.Buffer); ExFreePool(dc); goto cont; }
dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length); dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
insert_dir_child_into_hash_lists(fcb, dc);
num_children++;
cont: if (find_next_item(Vcb, &tp, &next_tp, false, Irp)) tp = next_tp; else break; }
if (!Vcb->options.no_root_dir && fcb->inode == SUBVOL_ROOT_INODE) { root* top_subvol;
if (Vcb->root_fileref && Vcb->root_fileref->fcb) top_subvol = Vcb->root_fileref->fcb->subvol; else top_subvol = find_default_subvol(Vcb, NULL);
if (fcb->subvol == top_subvol && top_subvol->id != BTRFS_ROOT_FSTREE) { dir_child* dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG); if (!dc) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
dc->key.obj_id = BTRFS_ROOT_FSTREE; dc->key.obj_type = TYPE_ROOT_ITEM; dc->key.offset = 0; dc->index = max_index + 1; dc->type = BTRFS_TYPE_DIRECTORY; dc->fileref = NULL; dc->root_dir = true;
dc->utf8.MaximumLength = dc->utf8.Length = sizeof(root_dir) - sizeof(char); dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(root_dir) - sizeof(char), ALLOC_TAG); if (!dc->utf8.Buffer) { ERR("out of memory\n"); ExFreePool(dc); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(dc->utf8.Buffer, root_dir, sizeof(root_dir) - sizeof(char));
dc->name.MaximumLength = dc->name.Length = sizeof(root_dir_utf16) - sizeof(WCHAR); dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(root_dir_utf16) - sizeof(WCHAR), ALLOC_TAG); if (!dc->name.Buffer) { ERR("out of memory\n"); ExFreePool(dc->utf8.Buffer); ExFreePool(dc); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(dc->name.Buffer, root_dir_utf16, sizeof(root_dir_utf16) - sizeof(WCHAR));
Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true); if (!NT_SUCCESS(Status)) { ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); ExFreePool(dc->utf8.Buffer); ExFreePool(dc->name.Buffer); ExFreePool(dc); goto cont; }
dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length); dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
insert_dir_child_into_hash_lists(fcb, dc); } }
return STATUS_SUCCESS;}
NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb, root* subvol, uint64_t inode, uint8_t type, PANSI_STRING utf8, bool always_add_hl, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, PIRP Irp) { KEY searchkey; traverse_ptr tp, next_tp; NTSTATUS Status; fcb *fcb, *deleted_fcb = NULL; bool atts_set = false, sd_set = false, no_data; LIST_ENTRY* lastle = NULL; EXTENT_DATA* ed = NULL; uint64_t fcbs_version = 0; uint32_t hash;
hash = calc_crc32c(0xffffffff, (uint8_t*)&inode, sizeof(uint64_t));
acquire_fcb_lock_shared(Vcb);
if (subvol->fcbs_ptrs[hash >> 24]) { LIST_ENTRY* le = subvol->fcbs_ptrs[hash >> 24];
while (le != &subvol->fcbs) { fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
if (fcb->inode == inode) { if (!fcb->ads) { if (fcb->deleted) deleted_fcb = fcb; else {#ifdef DEBUG_FCB_REFCOUNTS LONG rc = InterlockedIncrement(&fcb->refcount);
WARN("fcb %p: refcount now %i (subvol %I64x, inode %I64x)\n", fcb, rc, fcb->subvol->id, fcb->inode);#else InterlockedIncrement(&fcb->refcount);#endif
*pfcb = fcb; release_fcb_lock(Vcb); return STATUS_SUCCESS; } } } else if (fcb->hash > hash) { if (deleted_fcb) { InterlockedIncrement(&deleted_fcb->refcount); *pfcb = deleted_fcb; release_fcb_lock(Vcb); return STATUS_SUCCESS; }
lastle = le->Blink; fcbs_version = subvol->fcbs_version;
break; }
le = le->Flink; } }
release_fcb_lock(Vcb);
if (deleted_fcb) { InterlockedIncrement(&deleted_fcb->refcount); *pfcb = deleted_fcb; return STATUS_SUCCESS; }
fcb = create_fcb(Vcb, pooltype); if (!fcb) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
fcb->Vcb = Vcb;
fcb->subvol = subvol; fcb->inode = inode; fcb->hash = hash; fcb->type = type;
searchkey.obj_id = inode; searchkey.obj_type = TYPE_INODE_ITEM; searchkey.offset = 0xffffffffffffffff;
Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08lx\n", Status); reap_fcb(fcb); return Status; }
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { WARN("couldn't find INODE_ITEM for inode %I64x in subvol %I64x\n", inode, subvol->id); reap_fcb(fcb); return STATUS_INVALID_PARAMETER; }
if (tp.item->size > 0) RtlCopyMemory(&fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
if (fcb->type == 0) { // guess the type from the inode mode, if the caller doesn't know already if ((fcb->inode_item.st_mode & __S_IFDIR) == __S_IFDIR) fcb->type = BTRFS_TYPE_DIRECTORY; else if ((fcb->inode_item.st_mode & __S_IFCHR) == __S_IFCHR) fcb->type = BTRFS_TYPE_CHARDEV; else if ((fcb->inode_item.st_mode & __S_IFBLK) == __S_IFBLK) fcb->type = BTRFS_TYPE_BLOCKDEV; else if ((fcb->inode_item.st_mode & __S_IFIFO) == __S_IFIFO) fcb->type = BTRFS_TYPE_FIFO; else if ((fcb->inode_item.st_mode & __S_IFLNK) == __S_IFLNK) fcb->type = BTRFS_TYPE_SYMLINK; else if ((fcb->inode_item.st_mode & __S_IFSOCK) == __S_IFSOCK) fcb->type = BTRFS_TYPE_SOCKET; else fcb->type = BTRFS_TYPE_FILE; }
no_data = fcb->inode_item.st_size == 0 || (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK);
while (find_next_item(Vcb, &tp, &next_tp, false, Irp)) { tp = next_tp;
if (tp.item->key.obj_id > inode) break;
if ((no_data && tp.item->key.obj_type > TYPE_XATTR_ITEM) || tp.item->key.obj_type > TYPE_EXTENT_DATA) break;
if ((always_add_hl || fcb->inode_item.st_nlink > 1) && tp.item->key.obj_type == TYPE_INODE_REF) { ULONG len; INODE_REF* ir;
len = tp.item->size; ir = (INODE_REF*)tp.item->data;
while (len >= sizeof(INODE_REF) - 1) { hardlink* hl; ULONG stringlen;
hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG); if (!hl) { ERR("out of memory\n"); reap_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
hl->parent = tp.item->key.offset; hl->index = ir->index;
hl->utf8.Length = hl->utf8.MaximumLength = ir->n;
if (hl->utf8.Length > 0) { hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG); RtlCopyMemory(hl->utf8.Buffer, ir->name, ir->n); }
Status = utf8_to_utf16(NULL, 0, &stringlen, ir->name, ir->n); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 1 returned %08lx\n", Status); ExFreePool(hl); reap_fcb(fcb); return Status; }
hl->name.Length = hl->name.MaximumLength = (uint16_t)stringlen;
if (stringlen == 0) hl->name.Buffer = NULL; else { hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
if (!hl->name.Buffer) { ERR("out of memory\n"); ExFreePool(hl); reap_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
Status = utf8_to_utf16(hl->name.Buffer, stringlen, &stringlen, ir->name, ir->n); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 2 returned %08lx\n", Status); ExFreePool(hl->name.Buffer); ExFreePool(hl); reap_fcb(fcb); return Status; } }
InsertTailList(&fcb->hardlinks, &hl->list_entry);
len -= sizeof(INODE_REF) - 1 + ir->n; ir = (INODE_REF*)&ir->name[ir->n]; } } else if ((always_add_hl || fcb->inode_item.st_nlink > 1) && tp.item->key.obj_type == TYPE_INODE_EXTREF) { ULONG len; INODE_EXTREF* ier;
len = tp.item->size; ier = (INODE_EXTREF*)tp.item->data;
while (len >= sizeof(INODE_EXTREF) - 1) { hardlink* hl; ULONG stringlen;
hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG); if (!hl) { ERR("out of memory\n"); reap_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
hl->parent = ier->dir; hl->index = ier->index;
hl->utf8.Length = hl->utf8.MaximumLength = ier->n;
if (hl->utf8.Length > 0) { hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG); RtlCopyMemory(hl->utf8.Buffer, ier->name, ier->n); }
Status = utf8_to_utf16(NULL, 0, &stringlen, ier->name, ier->n); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 1 returned %08lx\n", Status); ExFreePool(hl); reap_fcb(fcb); return Status; }
hl->name.Length = hl->name.MaximumLength = (uint16_t)stringlen;
if (stringlen == 0) hl->name.Buffer = NULL; else { hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
if (!hl->name.Buffer) { ERR("out of memory\n"); ExFreePool(hl); reap_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
Status = utf8_to_utf16(hl->name.Buffer, stringlen, &stringlen, ier->name, ier->n); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 2 returned %08lx\n", Status); ExFreePool(hl->name.Buffer); ExFreePool(hl); reap_fcb(fcb); return Status; } }
InsertTailList(&fcb->hardlinks, &hl->list_entry);
len -= sizeof(INODE_EXTREF) - 1 + ier->n; ier = (INODE_EXTREF*)&ier->name[ier->n]; } } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) { ULONG len; DIR_ITEM* di;
static const char xapref[] = "user.";
if (tp.item->size < offsetof(DIR_ITEM, name[0])) { ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(DIR_ITEM, name[0])); continue; }
len = tp.item->size; di = (DIR_ITEM*)tp.item->data;
do { if (len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) break;
if (tp.item->key.offset == EA_REPARSE_HASH && di->n == sizeof(EA_REPARSE) - 1 && RtlCompareMemory(EA_REPARSE, di->name, di->n) == di->n) { if (di->m > 0) { fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG); if (!fcb->reparse_xattr.Buffer) { ERR("out of memory\n"); reap_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(fcb->reparse_xattr.Buffer, &di->name[di->n], di->m); } else fcb->reparse_xattr.Buffer = NULL;
fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = di->m; } else if (tp.item->key.offset == EA_EA_HASH && di->n == sizeof(EA_EA) - 1 && RtlCompareMemory(EA_EA, di->name, di->n) == di->n) { if (di->m > 0) { ULONG offset;
Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)&di->name[di->n], di->m, &offset);
if (!NT_SUCCESS(Status)) WARN("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset); else { FILE_FULL_EA_INFORMATION* eainfo;
fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG); if (!fcb->ea_xattr.Buffer) { ERR("out of memory\n"); reap_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(fcb->ea_xattr.Buffer, &di->name[di->n], di->m);
fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = di->m;
fcb->ealen = 4;
// calculate ealen eainfo = (FILE_FULL_EA_INFORMATION*)&di->name[di->n]; do { fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
if (eainfo->NextEntryOffset == 0) break;
eainfo = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)eainfo) + eainfo->NextEntryOffset); } while (true); } } } else if (tp.item->key.offset == EA_DOSATTRIB_HASH && di->n == sizeof(EA_DOSATTRIB) - 1 && RtlCompareMemory(EA_DOSATTRIB, di->name, di->n) == di->n) { if (di->m > 0) { if (get_file_attributes_from_xattr(&di->name[di->n], di->m, &fcb->atts)) { atts_set = true;
if (fcb->type == BTRFS_TYPE_DIRECTORY) fcb->atts |= FILE_ATTRIBUTE_DIRECTORY; else if (fcb->type == BTRFS_TYPE_SYMLINK) fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
if (fcb->type != BTRFS_TYPE_DIRECTORY) fcb->atts &= ~FILE_ATTRIBUTE_DIRECTORY;
if (inode == SUBVOL_ROOT_INODE) { if (subvol->root_item.flags & BTRFS_SUBVOL_READONLY) fcb->atts |= FILE_ATTRIBUTE_READONLY; else fcb->atts &= ~FILE_ATTRIBUTE_READONLY; } } } } else if (tp.item->key.offset == EA_NTACL_HASH && di->n == sizeof(EA_NTACL) - 1 && RtlCompareMemory(EA_NTACL, di->name, di->n) == di->n) { if (di->m > 0) { fcb->sd = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG); if (!fcb->sd) { ERR("out of memory\n"); reap_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(fcb->sd, &di->name[di->n], di->m);
// We have to test against our copy rather than the source, as RtlValidRelativeSecurityDescriptor // will fail if the ACLs aren't 32-bit aligned. if (!RtlValidRelativeSecurityDescriptor(fcb->sd, di->m, 0)) ExFreePool(fcb->sd); else sd_set = true; } } else if (tp.item->key.offset == EA_PROP_COMPRESSION_HASH && di->n == sizeof(EA_PROP_COMPRESSION) - 1 && RtlCompareMemory(EA_PROP_COMPRESSION, di->name, di->n) == di->n) { if (di->m > 0) { static const char lzo[] = "lzo"; static const char zlib[] = "zlib"; static const char zstd[] = "zstd";
if (di->m == sizeof(lzo) - 1 && RtlCompareMemory(&di->name[di->n], lzo, di->m) == di->m) fcb->prop_compression = PropCompression_LZO; else if (di->m == sizeof(zlib) - 1 && RtlCompareMemory(&di->name[di->n], zlib, di->m) == di->m) fcb->prop_compression = PropCompression_Zlib; else if (di->m == sizeof(zstd) - 1 && RtlCompareMemory(&di->name[di->n], zstd, di->m) == di->m) fcb->prop_compression = PropCompression_ZSTD; else fcb->prop_compression = PropCompression_None; } } else if (tp.item->key.offset == EA_CASE_SENSITIVE_HASH && di->n == sizeof(EA_CASE_SENSITIVE) - 1 && RtlCompareMemory(EA_CASE_SENSITIVE, di->name, di->n) == di->n) { if (di->m > 0) { fcb->case_sensitive = di->m == 1 && di->name[di->n] == '1'; fcb->case_sensitive_set = true; } } else if (di->n > sizeof(xapref) - 1 && RtlCompareMemory(xapref, di->name, sizeof(xapref) - 1) == sizeof(xapref) - 1) { dir_child* dc; ULONG utf16len;
Status = utf8_to_utf16(NULL, 0, &utf16len, &di->name[sizeof(xapref) - 1], di->n + 1 - sizeof(xapref)); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 1 returned %08lx\n", Status); reap_fcb(fcb); return Status; }
dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG); if (!dc) { ERR("out of memory\n"); reap_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(dc, sizeof(dir_child));
dc->utf8.MaximumLength = dc->utf8.Length = di->n + 1 - sizeof(xapref); dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.MaximumLength, ALLOC_TAG); if (!dc->utf8.Buffer) { ERR("out of memory\n"); ExFreePool(dc); reap_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(dc->utf8.Buffer, &di->name[sizeof(xapref) - 1], dc->utf8.Length);
dc->name.MaximumLength = dc->name.Length = (uint16_t)utf16len; dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.MaximumLength, ALLOC_TAG); if (!dc->name.Buffer) { ERR("out of memory\n"); ExFreePool(dc->utf8.Buffer); ExFreePool(dc); reap_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
Status = utf8_to_utf16(dc->name.Buffer, utf16len, &utf16len, dc->utf8.Buffer, dc->utf8.Length); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 2 returned %08lx\n", Status); ExFreePool(dc->utf8.Buffer); ExFreePool(dc->name.Buffer); ExFreePool(dc); reap_fcb(fcb); return Status; }
Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true); if (!NT_SUCCESS(Status)) { ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); ExFreePool(dc->utf8.Buffer); ExFreePool(dc->name.Buffer); ExFreePool(dc); reap_fcb(fcb); return Status; }
dc->size = di->m;
InsertTailList(&fcb->dir_children_index, &dc->list_entry_index); } else { xattr* xa;
xa = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + di->m + di->n, ALLOC_TAG); if (!xa) { ERR("out of memory\n"); reap_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
xa->namelen = di->n; xa->valuelen = di->m; xa->dirty = false; RtlCopyMemory(xa->data, di->name, di->m + di->n);
InsertTailList(&fcb->xattrs, &xa->list_entry); }
len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
if (len < offsetof(DIR_ITEM, name[0])) break;
di = (DIR_ITEM*)&di->name[di->m + di->n]; } while (true); } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) { extent* ext; bool unique = false;
ed = (EXTENT_DATA*)tp.item->data;
if (tp.item->size < sizeof(EXTENT_DATA)) { ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
reap_fcb(fcb); return STATUS_INTERNAL_ERROR; }
if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
reap_fcb(fcb); return STATUS_INTERNAL_ERROR; }
if (ed2->address == 0 || ed2->size == 0) // sparse continue;
if (ed2->size != 0 && is_tree_unique(Vcb, tp.tree, Irp)) unique = is_extent_unique(Vcb, ed2->address, ed2->size, Irp); }
ext = ExAllocatePoolWithTag(pooltype, offsetof(extent, extent_data) + tp.item->size, ALLOC_TAG); if (!ext) { ERR("out of memory\n"); reap_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
ext->offset = tp.item->key.offset; RtlCopyMemory(&ext->extent_data, tp.item->data, tp.item->size); ext->datalen = tp.item->size; ext->unique = unique; ext->ignore = false; ext->inserted = false; ext->csum = NULL;
InsertTailList(&fcb->extents, &ext->list_entry); } }
if (fcb->type == BTRFS_TYPE_DIRECTORY) { Status = load_dir_children(Vcb, fcb, false, Irp); if (!NT_SUCCESS(Status)) { ERR("load_dir_children returned %08lx\n", Status); reap_fcb(fcb); return Status; } }
if (no_data) { fcb->Header.AllocationSize.QuadPart = 0; fcb->Header.FileSize.QuadPart = 0; fcb->Header.ValidDataLength.QuadPart = 0; } else { if (ed && ed->type == EXTENT_TYPE_INLINE) fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size; else fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size; fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size; }
if (!atts_set) fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', true, Irp);
if (!sd_set) fcb_get_sd(fcb, parent, false, Irp);
acquire_fcb_lock_exclusive(Vcb);
if (lastle && subvol->fcbs_version == fcbs_version) { InsertHeadList(lastle, &fcb->list_entry);
if (!subvol->fcbs_ptrs[hash >> 24] || CONTAINING_RECORD(subvol->fcbs_ptrs[hash >> 24], struct _fcb, list_entry)->hash > hash) subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry; } else { lastle = NULL;
if (subvol->fcbs_ptrs[hash >> 24]) { LIST_ENTRY* le = subvol->fcbs_ptrs[hash >> 24];
while (le != &subvol->fcbs) { struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
if (fcb2->inode == inode) { if (!fcb2->ads) { if (fcb2->deleted) deleted_fcb = fcb2; else {#ifdef DEBUG_FCB_REFCOUNTS LONG rc = InterlockedIncrement(&fcb2->refcount);
WARN("fcb %p: refcount now %i (subvol %I64x, inode %I64x)\n", fcb2, rc, fcb2->subvol->id, fcb2->inode);#else InterlockedIncrement(&fcb2->refcount);#endif
*pfcb = fcb2; reap_fcb(fcb); release_fcb_lock(Vcb); return STATUS_SUCCESS; } } } else if (fcb2->hash > hash) { if (deleted_fcb) { InterlockedIncrement(&deleted_fcb->refcount); *pfcb = deleted_fcb; reap_fcb(fcb); release_fcb_lock(Vcb); return STATUS_SUCCESS; }
lastle = le->Blink; break; }
le = le->Flink; } }
if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT && fcb->reparse_xattr.Length == 0) { fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
if (!Vcb->readonly && !is_subvol_readonly(subvol, Irp)) { fcb->atts_changed = true; mark_fcb_dirty(fcb); } }
if (!lastle) { uint8_t c = hash >> 24;
if (c != 0xff) { uint8_t d = c + 1;
do { if (subvol->fcbs_ptrs[d]) { lastle = subvol->fcbs_ptrs[d]->Blink; break; }
d++; } while (d != 0); } }
if (lastle) { InsertHeadList(lastle, &fcb->list_entry);
if (lastle == &subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (hash >> 24)) subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry; } else { InsertTailList(&subvol->fcbs, &fcb->list_entry);
if (fcb->list_entry.Blink == &subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (hash >> 24)) subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry; } }
if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE && fcb->subvol != Vcb->root_fileref->fcb->subvol) fcb->atts |= FILE_ATTRIBUTE_HIDDEN;
subvol->fcbs_version++;
InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
release_fcb_lock(Vcb);
fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
*pfcb = fcb;
return STATUS_SUCCESS;}
static NTSTATUS open_fcb_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb, dir_child* dc, fcb* parent, fcb** pfcb, PIRP Irp) { fcb* fcb; uint8_t* xattrdata; uint16_t xattrlen, overhead; NTSTATUS Status; KEY searchkey; traverse_ptr tp; static const char xapref[] = "user."; ANSI_STRING xattr; uint32_t crc32;
xattr.Length = sizeof(xapref) - 1 + dc->utf8.Length; xattr.MaximumLength = xattr.Length + 1; xattr.Buffer = ExAllocatePoolWithTag(PagedPool, xattr.MaximumLength, ALLOC_TAG); if (!xattr.Buffer) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(xattr.Buffer, xapref, sizeof(xapref) - 1); RtlCopyMemory(&xattr.Buffer[sizeof(xapref) - 1], dc->utf8.Buffer, dc->utf8.Length); xattr.Buffer[xattr.Length] = 0;
fcb = create_fcb(Vcb, PagedPool); if (!fcb) { ERR("out of memory\n"); ExFreePool(xattr.Buffer); return STATUS_INSUFFICIENT_RESOURCES; }
fcb->Vcb = Vcb;
crc32 = calc_crc32c(0xfffffffe, (uint8_t*)xattr.Buffer, xattr.Length);
if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr.Buffer, crc32, &xattrdata, &xattrlen, Irp)) { ERR("get_xattr failed\n"); reap_fcb(fcb); ExFreePool(xattr.Buffer); return STATUS_INTERNAL_ERROR; }
fcb->subvol = parent->subvol; fcb->inode = parent->inode; fcb->type = parent->type; fcb->ads = true; fcb->adshash = crc32; fcb->adsxattr = xattr;
// find XATTR_ITEM overhead and hence calculate maximum length
searchkey.obj_id = parent->inode; searchkey.obj_type = TYPE_XATTR_ITEM; searchkey.offset = crc32;
Status = find_item(Vcb, parent->subvol, &tp, &searchkey, false, Irp); if (!NT_SUCCESS(Status)) { ERR("find_item returned %08lx\n", Status); reap_fcb(fcb); return Status; }
if (keycmp(tp.item->key, searchkey)) { ERR("error - could not find key for xattr\n"); reap_fcb(fcb); return STATUS_INTERNAL_ERROR; }
if (tp.item->size < xattrlen) { ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, xattrlen); reap_fcb(fcb); return STATUS_INTERNAL_ERROR; }
overhead = tp.item->size - xattrlen;
fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - overhead;
fcb->adsdata.Buffer = (char*)xattrdata; fcb->adsdata.Length = fcb->adsdata.MaximumLength = xattrlen;
fcb->Header.IsFastIoPossible = fast_io_possible(fcb); fcb->Header.AllocationSize.QuadPart = xattrlen; fcb->Header.FileSize.QuadPart = xattrlen; fcb->Header.ValidDataLength.QuadPart = xattrlen;
TRACE("stream found: size = %x, hash = %08x\n", xattrlen, fcb->adshash);
*pfcb = fcb;
return STATUS_SUCCESS;}
NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _In_ file_ref* sf, _In_ PUNICODE_STRING name, _In_ bool case_sensitive, _In_ bool lastpart, _In_ bool streampart, _In_ POOL_TYPE pooltype, _Out_ file_ref** psf2, _In_opt_ PIRP Irp) { NTSTATUS Status; file_ref* sf2;
if (sf->fcb == Vcb->dummy_fcb) return STATUS_OBJECT_NAME_NOT_FOUND;
if (streampart) { bool locked = false; LIST_ENTRY* le; UNICODE_STRING name_uc; dir_child* dc = NULL; fcb* fcb; struct _fcb* duff_fcb = NULL; file_ref* duff_fr = NULL;
if (!case_sensitive) { Status = RtlUpcaseUnicodeString(&name_uc, name, true); if (!NT_SUCCESS(Status)) { ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); return Status; } }
if (!ExIsResourceAcquiredSharedLite(&sf->fcb->nonpaged->dir_children_lock)) { ExAcquireResourceSharedLite(&sf->fcb->nonpaged->dir_children_lock, true); locked = true; }
le = sf->fcb->dir_children_index.Flink; while (le != &sf->fcb->dir_children_index) { dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_index);
if (dc2->index == 0) { if ((case_sensitive && dc2->name.Length == name->Length && RtlCompareMemory(dc2->name.Buffer, name->Buffer, dc2->name.Length) == dc2->name.Length) || (!case_sensitive && dc2->name_uc.Length == name_uc.Length && RtlCompareMemory(dc2->name_uc.Buffer, name_uc.Buffer, dc2->name_uc.Length) == dc2->name_uc.Length) ) { dc = dc2; break; } } else break;
le = le->Flink; }
if (!dc) { if (locked) ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
if (!case_sensitive) ExFreePool(name_uc.Buffer);
return STATUS_OBJECT_NAME_NOT_FOUND; }
if (dc->fileref) { if (locked) ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
if (!case_sensitive) ExFreePool(name_uc.Buffer);
increase_fileref_refcount(dc->fileref); *psf2 = dc->fileref; return STATUS_SUCCESS; }
if (locked) ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
if (!case_sensitive) ExFreePool(name_uc.Buffer);
Status = open_fcb_stream(Vcb, dc, sf->fcb, &fcb, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fcb_stream returned %08lx\n", Status); return Status; }
fcb->hash = sf->fcb->hash;
acquire_fcb_lock_exclusive(Vcb);
if (sf->fcb->subvol->fcbs_ptrs[fcb->hash >> 24]) { le = sf->fcb->subvol->fcbs_ptrs[fcb->hash >> 24];
while (le != &sf->fcb->subvol->fcbs) { struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
if (fcb2->inode == fcb->inode) { if (fcb2->ads && fcb2->adshash == fcb->adshash) { // FIXME - handle hash collisions duff_fcb = fcb; fcb = fcb2; break; } } else if (fcb2->hash > fcb->hash) break;
le = le->Flink; } }
if (!duff_fcb) { InsertHeadList(&sf->fcb->list_entry, &fcb->list_entry); InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all); fcb->subvol->fcbs_version++; }
release_fcb_lock(Vcb);
if (duff_fcb) { reap_fcb(duff_fcb); InterlockedIncrement(&fcb->refcount); }
sf2 = create_fileref(Vcb); if (!sf2) { ERR("out of memory\n"); free_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
ExAcquireResourceExclusiveLite(&sf->fcb->nonpaged->dir_children_lock, true);
if (dc->fileref) { duff_fr = sf2; sf2 = dc->fileref; increase_fileref_refcount(sf2); } else { sf2->fcb = fcb; sf2->parent = (struct _file_ref*)sf; sf2->dc = dc; dc->fileref = sf2; increase_fileref_refcount(sf); InsertTailList(&sf->children, &sf2->list_entry); }
ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
if (duff_fr) ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, duff_fr); } else { root* subvol; uint64_t inode; dir_child* dc;
Status = find_file_in_dir(name, sf->fcb, &subvol, &inode, &dc, case_sensitive); if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { TRACE("could not find %.*S\n", (int)(name->Length / sizeof(WCHAR)), name->Buffer);
return lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND; } else if (Status == STATUS_OBJECT_NAME_INVALID) { TRACE("invalid filename: %.*S\n", (int)(name->Length / sizeof(WCHAR)), name->Buffer); return Status; } else if (!NT_SUCCESS(Status)) { ERR("find_file_in_dir returned %08lx\n", Status); return Status; } else { fcb* fcb; file_ref* duff_fr = NULL;
if (dc->fileref) { if (!lastpart && dc->type != BTRFS_TYPE_DIRECTORY) { TRACE("passed path including file as subdirectory\n"); return STATUS_OBJECT_PATH_NOT_FOUND; }
InterlockedIncrement(&dc->fileref->refcount); *psf2 = dc->fileref; return STATUS_SUCCESS; }
if (!subvol || (subvol != Vcb->root_fileref->fcb->subvol && inode == SUBVOL_ROOT_INODE && subvol->parent != sf->fcb->subvol->id && !dc->root_dir)) { fcb = Vcb->dummy_fcb; InterlockedIncrement(&fcb->refcount); } else { Status = open_fcb(Vcb, subvol, inode, dc->type, &dc->utf8, false, sf->fcb, &fcb, pooltype, Irp);
if (!NT_SUCCESS(Status)) { ERR("open_fcb returned %08lx\n", Status); return Status; } }
if (dc->type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) { TRACE("passed path including file as subdirectory\n"); free_fcb(fcb); return STATUS_OBJECT_PATH_NOT_FOUND; }
sf2 = create_fileref(Vcb); if (!sf2) { ERR("out of memory\n"); free_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
sf2->fcb = fcb;
ExAcquireResourceExclusiveLite(&sf->fcb->nonpaged->dir_children_lock, true);
if (!dc->fileref) { sf2->parent = (struct _file_ref*)sf; sf2->dc = dc; dc->fileref = sf2; InsertTailList(&sf->children, &sf2->list_entry); increase_fileref_refcount(sf);
if (dc->type == BTRFS_TYPE_DIRECTORY) fcb->fileref = sf2; } else { duff_fr = sf2; sf2 = dc->fileref; increase_fileref_refcount(sf2); }
ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
if (duff_fr) reap_fileref(Vcb, duff_fr); } }
*psf2 = sf2;
return STATUS_SUCCESS;}
NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Out_ file_ref** pfr, _In_ PUNICODE_STRING fnus, _In_opt_ file_ref* related, _In_ bool parent, _Out_opt_ USHORT* parsed, _Out_opt_ ULONG* fn_offset, _In_ POOL_TYPE pooltype, _In_ bool case_sensitive, _In_opt_ PIRP Irp) { UNICODE_STRING fnus2; file_ref *dir, *sf, *sf2; LIST_ENTRY parts; bool has_stream = false; NTSTATUS Status; LIST_ENTRY* le;
TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, parsed);
if (Vcb->removing || Vcb->locked) return STATUS_ACCESS_DENIED;
fnus2 = *fnus;
if (fnus2.Length < sizeof(WCHAR) && !related) { ERR("error - fnus was too short\n"); return STATUS_INTERNAL_ERROR; }
if (related && fnus->Length == 0) { increase_fileref_refcount(related);
*pfr = related; return STATUS_SUCCESS; }
if (related) { dir = related; } else { if (fnus2.Buffer[0] != '\\') { ERR("error - filename %.*S did not begin with \\\n", (int)(fnus2.Length / sizeof(WCHAR)), fnus2.Buffer); return STATUS_OBJECT_PATH_NOT_FOUND; }
// if path starts with two backslashes, ignore one of them if (fnus2.Length >= 2 * sizeof(WCHAR) && fnus2.Buffer[1] == '\\') { fnus2.Buffer++; fnus2.Length -= sizeof(WCHAR); fnus2.MaximumLength -= sizeof(WCHAR); }
if (fnus2.Length == sizeof(WCHAR)) { if (Vcb->root_fileref->open_count == 0 && !(Vcb->Vpb->Flags & VPB_MOUNTED)) // don't allow root to be opened on unmounted FS return STATUS_DEVICE_NOT_READY;
increase_fileref_refcount(Vcb->root_fileref); *pfr = Vcb->root_fileref;
if (fn_offset) *fn_offset = 0;
return STATUS_SUCCESS; } else if (fnus2.Length >= 2 * sizeof(WCHAR) && fnus2.Buffer[1] == '\\') return STATUS_OBJECT_NAME_INVALID;
dir = Vcb->root_fileref;
fnus2.Buffer++; fnus2.Length -= sizeof(WCHAR); fnus2.MaximumLength -= sizeof(WCHAR); }
if (dir->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) { WARN("passed related fileref which isn't a directory (fnus = %.*S)\n", (int)(fnus->Length / sizeof(WCHAR)), fnus->Buffer); return STATUS_OBJECT_PATH_NOT_FOUND; }
InitializeListHead(&parts);
if (fnus->Length != 0 && (fnus->Length != sizeof(datastring) - sizeof(WCHAR) || RtlCompareMemory(fnus->Buffer, datastring, sizeof(datastring) - sizeof(WCHAR)) != sizeof(datastring) - sizeof(WCHAR))) { Status = split_path(Vcb, &fnus2, &parts, &has_stream); if (!NT_SUCCESS(Status)) { ERR("split_path returned %08lx\n", Status); return Status; } }
sf = dir; increase_fileref_refcount(dir);
if (parent && !IsListEmpty(&parts)) { name_bit* nb;
nb = CONTAINING_RECORD(RemoveTailList(&parts), name_bit, list_entry); ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
if (has_stream && !IsListEmpty(&parts)) { nb = CONTAINING_RECORD(RemoveTailList(&parts), name_bit, list_entry); ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
has_stream = false; } }
if (IsListEmpty(&parts)) { Status = STATUS_SUCCESS; *pfr = dir;
if (fn_offset) *fn_offset = 0;
goto end2; }
le = parts.Flink; do { name_bit* nb = CONTAINING_RECORD(le, name_bit, list_entry); bool lastpart = le->Flink == &parts || (has_stream && le->Flink->Flink == &parts); bool streampart = has_stream && le->Flink == &parts; bool cs = case_sensitive;
if (!cs) { if (streampart && sf->parent) cs = sf->parent->fcb->case_sensitive; else cs = sf->fcb->case_sensitive; }
Status = open_fileref_child(Vcb, sf, &nb->us, cs, lastpart, streampart, pooltype, &sf2, Irp);
if (!NT_SUCCESS(Status)) { if (Status == STATUS_OBJECT_PATH_NOT_FOUND || Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_NAME_INVALID) TRACE("open_fileref_child returned %08lx\n", Status); else ERR("open_fileref_child returned %08lx\n", Status);
goto end; }
if (le->Flink == &parts) { // last entry if (fn_offset) { if (has_stream) nb = CONTAINING_RECORD(le->Blink, name_bit, list_entry);
*fn_offset = (ULONG)(nb->us.Buffer - fnus->Buffer); }
break; }
if (sf2->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) { Status = STATUS_REPARSE;
if (parsed) { name_bit* nb2 = CONTAINING_RECORD(le->Flink, name_bit, list_entry);
*parsed = (USHORT)(nb2->us.Buffer - fnus->Buffer - 1) * sizeof(WCHAR); }
break; }
free_fileref(sf); sf = sf2;
le = le->Flink; } while (le != &parts);
if (Status != STATUS_REPARSE) Status = STATUS_SUCCESS; *pfr = sf2;
end: free_fileref(sf);
while (!IsListEmpty(&parts)) { name_bit* nb = CONTAINING_RECORD(RemoveHeadList(&parts), name_bit, list_entry); ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb); }
end2: TRACE("returning %08lx\n", Status);
return Status;}
NTSTATUS add_dir_child(fcb* fcb, uint64_t inode, bool subvol, PANSI_STRING utf8, PUNICODE_STRING name, uint8_t type, dir_child** pdc) { NTSTATUS Status; dir_child* dc; bool locked;
dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG); if (!dc) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(dc, sizeof(dir_child));
dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG); if (!dc->utf8.Buffer) { ERR("out of memory\n"); ExFreePool(dc); return STATUS_INSUFFICIENT_RESOURCES; }
dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, name->Length, ALLOC_TAG); if (!dc->name.Buffer) { ERR("out of memory\n"); ExFreePool(dc->utf8.Buffer); ExFreePool(dc); return STATUS_INSUFFICIENT_RESOURCES; }
dc->key.obj_id = inode; dc->key.obj_type = subvol ? TYPE_ROOT_ITEM : TYPE_INODE_ITEM; dc->key.offset = subvol ? 0xffffffffffffffff : 0; dc->type = type; dc->fileref = NULL;
dc->utf8.Length = dc->utf8.MaximumLength = utf8->Length; RtlCopyMemory(dc->utf8.Buffer, utf8->Buffer, utf8->Length);
dc->name.Length = dc->name.MaximumLength = name->Length; RtlCopyMemory(dc->name.Buffer, name->Buffer, name->Length);
Status = RtlUpcaseUnicodeString(&dc->name_uc, name, true); if (!NT_SUCCESS(Status)) { ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); ExFreePool(dc->utf8.Buffer); ExFreePool(dc->name.Buffer); ExFreePool(dc); return Status; }
dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length); dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
locked = ExIsResourceAcquiredExclusive(&fcb->nonpaged->dir_children_lock);
if (!locked) ExAcquireResourceExclusiveLite(&fcb->nonpaged->dir_children_lock, true);
if (IsListEmpty(&fcb->dir_children_index)) dc->index = 2; else { dir_child* dc2 = CONTAINING_RECORD(fcb->dir_children_index.Blink, dir_child, list_entry_index);
dc->index = max(2, dc2->index + 1); }
InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
insert_dir_child_into_hash_lists(fcb, dc);
if (!locked) ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
*pdc = dc;
return STATUS_SUCCESS;}
uint32_t inherit_mode(fcb* parfcb, bool is_dir) { uint32_t mode;
if (!parfcb) return 0755;
mode = parfcb->inode_item.st_mode & ~S_IFDIR; mode &= ~S_ISVTX; // clear sticky bit mode &= ~S_ISUID; // clear setuid bit
if (!is_dir) mode &= ~S_ISGID; // if not directory, clear setgid bit
return mode;}
static NTSTATUS file_create_parse_ea(fcb* fcb, FILE_FULL_EA_INFORMATION* ea) { NTSTATUS Status; LIST_ENTRY ealist, *le; uint16_t size = 0; char* buf;
InitializeListHead(&ealist);
do { STRING s; bool found = false;
s.Length = s.MaximumLength = ea->EaNameLength; s.Buffer = ea->EaName;
RtlUpperString(&s, &s);
le = ealist.Flink; while (le != &ealist) { ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
if (item->name.Length == s.Length && RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) { item->flags = ea->Flags; item->value.Length = item->value.MaximumLength = ea->EaValueLength; item->value.Buffer = &ea->EaName[ea->EaNameLength + 1]; found = true; break; }
le = le->Flink; }
if (!found) { ea_item* item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG); if (!item) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; }
item->name.Length = item->name.MaximumLength = ea->EaNameLength; item->name.Buffer = ea->EaName;
item->value.Length = item->value.MaximumLength = ea->EaValueLength; item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
item->flags = ea->Flags;
InsertTailList(&ealist, &item->list_entry); }
if (ea->NextEntryOffset == 0) break;
ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset); } while (true);
// handle LXSS values le = ealist.Flink; while (le != &ealist) { LIST_ENTRY* le2 = le->Flink; ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
if (item->name.Length == sizeof(lxuid) - 1 && RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) == item->name.Length) { if (item->value.Length < sizeof(uint32_t)) { ERR("uid value was shorter than expected\n"); Status = STATUS_INVALID_PARAMETER; goto end; }
RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer, sizeof(uint32_t)); fcb->sd_dirty = true; fcb->sd_deleted = false;
RemoveEntryList(&item->list_entry); ExFreePool(item); } else if (item->name.Length == sizeof(lxgid) - 1 && RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) == item->name.Length) { if (item->value.Length < sizeof(uint32_t)) { ERR("gid value was shorter than expected\n"); Status = STATUS_INVALID_PARAMETER; goto end; }
RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer, sizeof(uint32_t));
RemoveEntryList(&item->list_entry); ExFreePool(item); } else if (item->name.Length == sizeof(lxmod) - 1 && RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) == item->name.Length) { uint32_t allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH | S_ISGID | S_ISVTX | S_ISUID; uint32_t val;
if (item->value.Length < sizeof(uint32_t)) { ERR("mode value was shorter than expected\n"); Status = STATUS_INVALID_PARAMETER; goto end; }
val = *(uint32_t*)item->value.Buffer;
fcb->inode_item.st_mode &= ~allowed; fcb->inode_item.st_mode |= val & allowed;
if (fcb->type != BTRFS_TYPE_DIRECTORY) { if (__S_ISTYPE(val, __S_IFCHR)) { fcb->type = BTRFS_TYPE_CHARDEV; fcb->inode_item.st_mode &= ~__S_IFMT; fcb->inode_item.st_mode |= __S_IFCHR; } else if (__S_ISTYPE(val, __S_IFBLK)) { fcb->type = BTRFS_TYPE_BLOCKDEV; fcb->inode_item.st_mode &= ~__S_IFMT; fcb->inode_item.st_mode |= __S_IFBLK; } else if (__S_ISTYPE(val, __S_IFIFO)) { fcb->type = BTRFS_TYPE_FIFO; fcb->inode_item.st_mode &= ~__S_IFMT; fcb->inode_item.st_mode |= __S_IFIFO; } else if (__S_ISTYPE(val, __S_IFSOCK)) { fcb->type = BTRFS_TYPE_SOCKET; fcb->inode_item.st_mode &= ~__S_IFMT; fcb->inode_item.st_mode |= __S_IFSOCK; } }
RemoveEntryList(&item->list_entry); ExFreePool(item); } else if (item->name.Length == sizeof(lxdev) - 1 && RtlCompareMemory(item->name.Buffer, lxdev, item->name.Length) == item->name.Length) { uint32_t major, minor;
if (item->value.Length < sizeof(uint64_t)) { ERR("dev value was shorter than expected\n"); Status = STATUS_INVALID_PARAMETER; goto end; }
major = *(uint32_t*)item->value.Buffer; minor = *(uint32_t*)&item->value.Buffer[sizeof(uint32_t)];
fcb->inode_item.st_rdev = (minor & 0xFFFFF) | ((major & 0xFFFFFFFFFFF) << 20);
RemoveEntryList(&item->list_entry); ExFreePool(item); }
le = le2; }
if (fcb->type != BTRFS_TYPE_CHARDEV && fcb->type != BTRFS_TYPE_BLOCKDEV) fcb->inode_item.st_rdev = 0;
if (IsListEmpty(&ealist)) return STATUS_SUCCESS;
le = ealist.Flink; while (le != &ealist) { ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
if (size % 4 > 0) size += 4 - (size % 4);
size += (uint16_t)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length;
le = le->Flink; }
buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG); if (!buf) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; }
fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size; fcb->ea_xattr.Buffer = buf;
fcb->ealen = 4; ea = NULL;
le = ealist.Flink; while (le != &ealist) { ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
if (ea) { ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength;
if (ea->NextEntryOffset % 4 > 0) ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4);
ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset); } else ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
ea->NextEntryOffset = 0; ea->Flags = item->flags; ea->EaNameLength = (UCHAR)item->name.Length; ea->EaValueLength = item->value.Length;
RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length); ea->EaName[item->name.Length] = 0; RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length);
fcb->ealen += 5 + item->name.Length + item->value.Length;
le = le->Flink; }
fcb->ea_changed = true;
Status = STATUS_SUCCESS;
end: while (!IsListEmpty(&ealist)) { ea_item* item = CONTAINING_RECORD(RemoveHeadList(&ealist), ea_item, list_entry);
ExFreePool(item); }
return Status;}
static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _In_ PUNICODE_STRING fpus, _In_ file_ref* parfileref, _In_ ULONG options, _In_reads_bytes_opt_(ealen) FILE_FULL_EA_INFORMATION* ea, _In_ ULONG ealen, _Out_ file_ref** pfr, bool case_sensitive, _In_ LIST_ENTRY* rollback) { NTSTATUS Status; fcb* fcb; ULONG utf8len; char* utf8 = NULL; uint64_t inode; uint8_t type; LARGE_INTEGER time; BTRFS_TIME now; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool; USHORT defda; file_ref* fileref; dir_child* dc; ANSI_STRING utf8as; LIST_ENTRY* lastle = NULL; file_ref* existing_fileref = NULL;#ifdef DEBUG_FCB_REFCOUNTS LONG rc;#endif
if (parfileref->fcb == Vcb->dummy_fcb) return STATUS_ACCESS_DENIED;
if (options & FILE_DIRECTORY_FILE && IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_TEMPORARY) return STATUS_INVALID_PARAMETER;
Status = utf16_to_utf8(NULL, 0, &utf8len, fpus->Buffer, fpus->Length); if (!NT_SUCCESS(Status)) { ERR("utf16_to_utf8 returned %08lx\n", Status); return Status; }
utf8 = ExAllocatePoolWithTag(pool_type, utf8len + 1, ALLOC_TAG); if (!utf8) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
Status = utf16_to_utf8(utf8, utf8len, &utf8len, fpus->Buffer, fpus->Length); if (!NT_SUCCESS(Status)) { ERR("utf16_to_utf8 returned %08lx\n", Status); ExFreePool(utf8); return Status; }
utf8[utf8len] = 0;
KeQuerySystemTime(&time); win_time_to_unix(time, &now);
TRACE("create file %.*S\n", (int)(fpus->Length / sizeof(WCHAR)), fpus->Buffer); ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); TRACE("parfileref->fcb->inode_item.st_size (inode %I64x) was %I64x\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size); parfileref->fcb->inode_item.st_size += utf8len * 2; TRACE("parfileref->fcb->inode_item.st_size (inode %I64x) now %I64x\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size); parfileref->fcb->inode_item.transid = Vcb->superblock.generation; parfileref->fcb->inode_item.sequence++; parfileref->fcb->inode_item.st_ctime = now; parfileref->fcb->inode_item.st_mtime = now; ExReleaseResourceLite(parfileref->fcb->Header.Resource);
parfileref->fcb->inode_item_changed = true; mark_fcb_dirty(parfileref->fcb);
inode = InterlockedIncrement64(&parfileref->fcb->subvol->lastinode);
type = options & FILE_DIRECTORY_FILE ? BTRFS_TYPE_DIRECTORY : BTRFS_TYPE_FILE;
// FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
TRACE("requested attributes = %x\n", IrpSp->Parameters.Create.FileAttributes);
defda = 0;
if (utf8[0] == '.') defda |= FILE_ATTRIBUTE_HIDDEN;
if (options & FILE_DIRECTORY_FILE) { defda |= FILE_ATTRIBUTE_DIRECTORY; IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; } else IrpSp->Parameters.Create.FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
if (!(IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; defda |= FILE_ATTRIBUTE_ARCHIVE; }
TRACE("defda = %x\n", defda);
if (IrpSp->Parameters.Create.FileAttributes == FILE_ATTRIBUTE_NORMAL) IrpSp->Parameters.Create.FileAttributes = defda;
fcb = create_fcb(Vcb, pool_type); if (!fcb) { ERR("out of memory\n"); ExFreePool(utf8);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); parfileref->fcb->inode_item.st_size -= utf8len * 2; ExReleaseResourceLite(parfileref->fcb->Header.Resource);
return STATUS_INSUFFICIENT_RESOURCES; }
fcb->Vcb = Vcb;
if (IrpSp->Flags & SL_OPEN_PAGING_FILE) fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
fcb->inode_item.generation = Vcb->superblock.generation; fcb->inode_item.transid = Vcb->superblock.generation; fcb->inode_item.st_size = 0; fcb->inode_item.st_blocks = 0; fcb->inode_item.block_group = 0; fcb->inode_item.st_nlink = 1; fcb->inode_item.st_gid = GID_NOBODY; // FIXME? fcb->inode_item.st_mode = inherit_mode(parfileref->fcb, type == BTRFS_TYPE_DIRECTORY); // use parent's permissions by default fcb->inode_item.st_rdev = 0; fcb->inode_item.flags = 0; fcb->inode_item.sequence = 1; fcb->inode_item.st_atime = now; fcb->inode_item.st_ctime = now; fcb->inode_item.st_mtime = now; fcb->inode_item.otime = now;
if (type == BTRFS_TYPE_DIRECTORY) fcb->inode_item.st_mode |= S_IFDIR; else { fcb->inode_item.st_mode |= S_IFREG; fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory }
if (IrpSp->Flags & SL_OPEN_PAGING_FILE) { fcb->inode_item.flags = BTRFS_INODE_NODATACOW | BTRFS_INODE_NODATASUM | BTRFS_INODE_NOCOMPRESS; } else { // inherit nodatacow flag from parent directory if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW || Vcb->options.nodatacow) { fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
if (type != BTRFS_TYPE_DIRECTORY) fcb->inode_item.flags |= BTRFS_INODE_NODATASUM; }
if (parfileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS && !(fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) { fcb->inode_item.flags |= BTRFS_INODE_COMPRESS; } }
if (!(fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) { fcb->prop_compression = parfileref->fcb->prop_compression; fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None; } else fcb->prop_compression = PropCompression_None;
fcb->inode_item_changed = true;
fcb->Header.IsFastIoPossible = fast_io_possible(fcb); fcb->Header.AllocationSize.QuadPart = 0; fcb->Header.FileSize.QuadPart = 0; fcb->Header.ValidDataLength.QuadPart = 0;
fcb->atts = IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL; fcb->atts_changed = fcb->atts != defda;
#ifdef DEBUG_FCB_REFCOUNTS rc = InterlockedIncrement(&parfileref->fcb->refcount); WARN("fcb %p: refcount now %i\n", parfileref->fcb, rc);#else InterlockedIncrement(&parfileref->fcb->refcount);#endif fcb->subvol = parfileref->fcb->subvol; fcb->inode = inode; fcb->type = type; fcb->created = true; fcb->deleted = true;
fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&inode, sizeof(uint64_t));
acquire_fcb_lock_exclusive(Vcb);
if (fcb->subvol->fcbs_ptrs[fcb->hash >> 24]) { LIST_ENTRY* le = fcb->subvol->fcbs_ptrs[fcb->hash >> 24];
while (le != &fcb->subvol->fcbs) { struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
if (fcb2->hash > fcb->hash) { lastle = le->Blink; break; }
le = le->Flink; } }
if (!lastle) { uint8_t c = fcb->hash >> 24;
if (c != 0xff) { uint8_t d = c + 1;
do { if (fcb->subvol->fcbs_ptrs[d]) { lastle = fcb->subvol->fcbs_ptrs[d]->Blink; break; }
d++; } while (d != 0); } }
if (lastle) { InsertHeadList(lastle, &fcb->list_entry);
if (lastle == &fcb->subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (fcb->hash >> 24)) fcb->subvol->fcbs_ptrs[fcb->hash >> 24] = &fcb->list_entry; } else { InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
if (fcb->list_entry.Blink == &fcb->subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (fcb->hash >> 24)) fcb->subvol->fcbs_ptrs[fcb->hash >> 24] = &fcb->list_entry; }
InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
fcb->subvol->fcbs_version++;
release_fcb_lock(Vcb);
mark_fcb_dirty(fcb);
Status = fcb_get_new_sd(fcb, parfileref, IrpSp->Parameters.Create.SecurityContext->AccessState);
if (!NT_SUCCESS(Status)) { ERR("fcb_get_new_sd returned %08lx\n", Status); free_fcb(fcb);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); parfileref->fcb->inode_item.st_size -= utf8len * 2; ExReleaseResourceLite(parfileref->fcb->Header.Resource);
ExFreePool(utf8);
return Status; }
fcb->sd_dirty = true;
if (ea && ealen > 0) { Status = file_create_parse_ea(fcb, ea); if (!NT_SUCCESS(Status)) { ERR("file_create_parse_ea returned %08lx\n", Status); free_fcb(fcb);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); parfileref->fcb->inode_item.st_size -= utf8len * 2; ExReleaseResourceLite(parfileref->fcb->Header.Resource);
ExFreePool(utf8);
return Status; } }
fileref = create_fileref(Vcb); if (!fileref) { ERR("out of memory\n"); free_fcb(fcb);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); parfileref->fcb->inode_item.st_size -= utf8len * 2; ExReleaseResourceLite(parfileref->fcb->Header.Resource);
ExFreePool(utf8);
return STATUS_INSUFFICIENT_RESOURCES; }
fileref->fcb = fcb;
if (Irp->Overlay.AllocationSize.QuadPart > 0 && !write_fcb_compressed(fcb) && fcb->type != BTRFS_TYPE_DIRECTORY) { Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, true, NULL, rollback);
if (!NT_SUCCESS(Status)) { ERR("extend_file returned %08lx\n", Status); reap_fileref(Vcb, fileref);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); parfileref->fcb->inode_item.st_size -= utf8len * 2; ExReleaseResourceLite(parfileref->fcb->Header.Resource);
ExFreePool(utf8);
return Status; } }
if (fcb->type == BTRFS_TYPE_DIRECTORY) { fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG); if (!fcb->hash_ptrs) { ERR("out of memory\n"); reap_fileref(Vcb, fileref);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); parfileref->fcb->inode_item.st_size -= utf8len * 2; ExReleaseResourceLite(parfileref->fcb->Header.Resource);
ExFreePool(utf8);
return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG); if (!fcb->hash_ptrs_uc) { ERR("out of memory\n"); reap_fileref(Vcb, fileref);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); parfileref->fcb->inode_item.st_size -= utf8len * 2; ExReleaseResourceLite(parfileref->fcb->Header.Resource);
ExFreePool(utf8);
return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256); }
fcb->deleted = false;
fileref->created = true;
fcb->subvol->root_item.ctransid = Vcb->superblock.generation; fcb->subvol->root_item.ctime = now;
utf8as.Buffer = utf8; utf8as.Length = utf8as.MaximumLength = (uint16_t)utf8len;
ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true);
// check again doesn't already exist if (case_sensitive) { uint32_t dc_hash = calc_crc32c(0xffffffff, (uint8_t*)fpus->Buffer, fpus->Length);
if (parfileref->fcb->hash_ptrs[dc_hash >> 24]) { LIST_ENTRY* le = parfileref->fcb->hash_ptrs[dc_hash >> 24]; while (le != &parfileref->fcb->dir_children_hash) { dc = CONTAINING_RECORD(le, dir_child, list_entry_hash);
if (dc->hash == dc_hash && dc->name.Length == fpus->Length && RtlCompareMemory(dc->name.Buffer, fpus->Buffer, fpus->Length) == fpus->Length) { existing_fileref = dc->fileref; break; } else if (dc->hash > dc_hash) break;
le = le->Flink; } } } else { UNICODE_STRING fpusuc;
Status = RtlUpcaseUnicodeString(&fpusuc, fpus, true); if (!NT_SUCCESS(Status)) { ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock); ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); reap_fileref(Vcb, fileref);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); parfileref->fcb->inode_item.st_size -= utf8len * 2; ExReleaseResourceLite(parfileref->fcb->Header.Resource);
ExFreePool(utf8);
return Status; }
uint32_t dc_hash = calc_crc32c(0xffffffff, (uint8_t*)fpusuc.Buffer, fpusuc.Length);
if (parfileref->fcb->hash_ptrs_uc[dc_hash >> 24]) { LIST_ENTRY* le = parfileref->fcb->hash_ptrs_uc[dc_hash >> 24]; while (le != &parfileref->fcb->dir_children_hash_uc) { dc = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
if (dc->hash_uc == dc_hash && dc->name.Length == fpusuc.Length && RtlCompareMemory(dc->name.Buffer, fpusuc.Buffer, fpusuc.Length) == fpusuc.Length) { existing_fileref = dc->fileref; break; } else if (dc->hash_uc > dc_hash) break;
le = le->Flink; } }
ExFreePool(fpusuc.Buffer); }
if (existing_fileref) { ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock); reap_fileref(Vcb, fileref);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); parfileref->fcb->inode_item.st_size -= utf8len * 2; ExReleaseResourceLite(parfileref->fcb->Header.Resource);
ExFreePool(utf8);
increase_fileref_refcount(existing_fileref); *pfr = existing_fileref;
return STATUS_OBJECT_NAME_COLLISION; }
Status = add_dir_child(parfileref->fcb, fcb->inode, false, &utf8as, fpus, fcb->type, &dc); if (!NT_SUCCESS(Status)) { ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock); ERR("add_dir_child returned %08lx\n", Status); reap_fileref(Vcb, fileref);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); parfileref->fcb->inode_item.st_size -= utf8len * 2; ExReleaseResourceLite(parfileref->fcb->Header.Resource);
ExFreePool(utf8);
return Status; }
fileref->parent = parfileref; fileref->dc = dc; dc->fileref = fileref;
if (type == BTRFS_TYPE_DIRECTORY) fileref->fcb->fileref = fileref;
InsertTailList(&parfileref->children, &fileref->list_entry); ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
ExFreePool(utf8);
mark_fileref_dirty(fileref); increase_fileref_refcount(parfileref);
*pfr = fileref;
TRACE("created new file in subvol %I64x, inode %I64x\n", fcb->subvol->id, fcb->inode);
return STATUS_SUCCESS;}
static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb, file_ref** pfileref, file_ref** pparfileref, PUNICODE_STRING fpus, PUNICODE_STRING stream, PIRP Irp, ULONG options, POOL_TYPE pool_type, bool case_sensitive, LIST_ENTRY* rollback) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); file_ref *fileref, *newpar, *parfileref; fcb* fcb; static const char xapref[] = "user."; static const WCHAR DOSATTRIB[] = L"DOSATTRIB"; static const WCHAR EA[] = L"EA"; static const WCHAR reparse[] = L"reparse"; static const WCHAR casesensitive_str[] = L"casesensitive"; LARGE_INTEGER time; BTRFS_TIME now; ULONG utf8len, overhead; NTSTATUS Status; KEY searchkey; traverse_ptr tp; dir_child* dc; dir_child* existing_dc = NULL; ACCESS_MASK granted_access;#ifdef DEBUG_FCB_REFCOUNTS LONG rc;#endif
TRACE("fpus = %.*S\n", (int)(fpus->Length / sizeof(WCHAR)), fpus->Buffer); TRACE("stream = %.*S\n", (int)(stream->Length / sizeof(WCHAR)), stream->Buffer);
parfileref = *pparfileref;
if (parfileref->fcb == Vcb->dummy_fcb) return STATUS_ACCESS_DENIED;
Status = check_file_name_valid(stream, false, true); if (!NT_SUCCESS(Status)) return Status;
Status = open_fileref(Vcb, &newpar, fpus, parfileref, false, NULL, NULL, PagedPool, case_sensitive, Irp);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { UNICODE_STRING fpus2;
Status = check_file_name_valid(fpus, false, false); if (!NT_SUCCESS(Status)) return Status;
fpus2.Length = fpus2.MaximumLength = fpus->Length; fpus2.Buffer = ExAllocatePoolWithTag(pool_type, fpus2.MaximumLength, ALLOC_TAG);
if (!fpus2.Buffer) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(fpus2.Buffer, fpus->Buffer, fpus2.Length);
SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext, true, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL, IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &granted_access, &Status)) { SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext); return Status; }
SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, NULL, 0, &newpar, case_sensitive, rollback);
if (!NT_SUCCESS(Status)) { ERR("file_create2 returned %08lx\n", Status); ExFreePool(fpus2.Buffer); return Status; } else if (Status != STATUS_OBJECT_NAME_COLLISION) { send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL); queue_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); }
ExFreePool(fpus2.Buffer); } else if (!NT_SUCCESS(Status)) { ERR("open_fileref returned %08lx\n", Status); return Status; }
parfileref = newpar; *pparfileref = parfileref;
if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK && parfileref->fcb->type != BTRFS_TYPE_DIRECTORY) { WARN("parent not file, directory, or symlink\n"); free_fileref(parfileref); return STATUS_INVALID_PARAMETER; }
if (options & FILE_DIRECTORY_FILE) { WARN("tried to create directory as stream\n"); free_fileref(parfileref); return STATUS_INVALID_PARAMETER; }
if (parfileref->fcb->atts & FILE_ATTRIBUTE_READONLY && !(IrpSp->Flags & SL_IGNORE_READONLY_ATTRIBUTE)) { free_fileref(parfileref); return STATUS_ACCESS_DENIED; }
SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext, true, FILE_WRITE_DATA, 0, NULL, IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &granted_access, &Status)) { SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext); free_fileref(parfileref); return Status; }
SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
if ((stream->Length == sizeof(DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, DOSATTRIB, stream->Length) == stream->Length) || (stream->Length == sizeof(EA) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, EA, stream->Length) == stream->Length) || (stream->Length == sizeof(reparse) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, reparse, stream->Length) == stream->Length) || (stream->Length == sizeof(casesensitive_str) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, casesensitive_str, stream->Length) == stream->Length)) { free_fileref(parfileref); return STATUS_OBJECT_NAME_INVALID; }
fcb = create_fcb(Vcb, pool_type); if (!fcb) { ERR("out of memory\n"); free_fileref(parfileref); return STATUS_INSUFFICIENT_RESOURCES; }
fcb->Vcb = Vcb;
fcb->Header.IsFastIoPossible = fast_io_possible(fcb); fcb->Header.AllocationSize.QuadPart = 0; fcb->Header.FileSize.QuadPart = 0; fcb->Header.ValidDataLength.QuadPart = 0;
#ifdef DEBUG_FCB_REFCOUNTS rc = InterlockedIncrement(&parfileref->fcb->refcount); WARN("fcb %p: refcount now %i\n", parfileref->fcb, rc);#else InterlockedIncrement(&parfileref->fcb->refcount);#endif fcb->subvol = parfileref->fcb->subvol; fcb->inode = parfileref->fcb->inode; fcb->hash = parfileref->fcb->hash; fcb->type = parfileref->fcb->type;
fcb->ads = true;
Status = utf16_to_utf8(NULL, 0, &utf8len, stream->Buffer, stream->Length); if (!NT_SUCCESS(Status)) { ERR("utf16_to_utf8 1 returned %08lx\n", Status); reap_fcb(fcb); free_fileref(parfileref); return Status; }
fcb->adsxattr.Length = (uint16_t)utf8len + sizeof(xapref) - 1; fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1; fcb->adsxattr.Buffer = ExAllocatePoolWithTag(pool_type, fcb->adsxattr.MaximumLength, ALLOC_TAG); if (!fcb->adsxattr.Buffer) { ERR("out of memory\n"); reap_fcb(fcb); free_fileref(parfileref); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(fcb->adsxattr.Buffer, xapref, sizeof(xapref) - 1);
Status = utf16_to_utf8(&fcb->adsxattr.Buffer[sizeof(xapref) - 1], utf8len, &utf8len, stream->Buffer, stream->Length); if (!NT_SUCCESS(Status)) { ERR("utf16_to_utf8 2 returned %08lx\n", Status); reap_fcb(fcb); free_fileref(parfileref); return Status; }
fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
fcb->adshash = calc_crc32c(0xfffffffe, (uint8_t*)fcb->adsxattr.Buffer, fcb->adsxattr.Length); TRACE("adshash = %08x\n", fcb->adshash);
searchkey.obj_id = parfileref->fcb->inode; searchkey.obj_type = TYPE_XATTR_ITEM; searchkey.offset = fcb->adshash;
Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, false, Irp); if (!NT_SUCCESS(Status)) { ERR("find_item returned %08lx\n", Status); reap_fcb(fcb); free_fileref(parfileref); return Status; }
if (!keycmp(tp.item->key, searchkey)) overhead = tp.item->size; else overhead = 0;
fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - (sizeof(DIR_ITEM) - 1);
if (utf8len + sizeof(xapref) - 1 + overhead > fcb->adsmaxlen) { WARN("not enough room for new DIR_ITEM (%Iu + %lu > %lu)\n", utf8len + sizeof(xapref) - 1, overhead, fcb->adsmaxlen); reap_fcb(fcb); free_fileref(parfileref); return STATUS_DISK_FULL; } else fcb->adsmaxlen -= overhead + utf8len + sizeof(xapref) - 1;
fcb->created = true; fcb->deleted = true;
acquire_fcb_lock_exclusive(Vcb); InsertHeadList(&parfileref->fcb->list_entry, &fcb->list_entry); // insert in list after parent fcb InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all); parfileref->fcb->subvol->fcbs_version++; release_fcb_lock(Vcb);
mark_fcb_dirty(fcb);
fileref = create_fileref(Vcb); if (!fileref) { ERR("out of memory\n"); free_fcb(fcb); free_fileref(parfileref); return STATUS_INSUFFICIENT_RESOURCES; }
fileref->fcb = fcb;
dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG); if (!dc) { ERR("out of memory\n"); reap_fileref(Vcb, fileref); free_fileref(parfileref); return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(dc, sizeof(dir_child));
dc->utf8.MaximumLength = dc->utf8.Length = fcb->adsxattr.Length + 1 - sizeof(xapref); dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.MaximumLength, ALLOC_TAG); if (!dc->utf8.Buffer) { ERR("out of memory\n"); ExFreePool(dc); reap_fileref(Vcb, fileref); free_fileref(parfileref); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(dc->utf8.Buffer, &fcb->adsxattr.Buffer[sizeof(xapref) - 1], fcb->adsxattr.Length + 1 - sizeof(xapref));
dc->name.MaximumLength = dc->name.Length = stream->Length; dc->name.Buffer = ExAllocatePoolWithTag(pool_type, dc->name.MaximumLength, ALLOC_TAG); if (!dc->name.Buffer) { ERR("out of memory\n"); ExFreePool(dc->utf8.Buffer); ExFreePool(dc); reap_fileref(Vcb, fileref); free_fileref(parfileref); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(dc->name.Buffer, stream->Buffer, stream->Length);
Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true); if (!NT_SUCCESS(Status)) { ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); ExFreePool(dc->utf8.Buffer); ExFreePool(dc->name.Buffer); ExFreePool(dc); reap_fileref(Vcb, fileref); free_fileref(parfileref); return Status; }
KeQuerySystemTime(&time); win_time_to_unix(time, &now);
ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true);
LIST_ENTRY* le = parfileref->fcb->dir_children_index.Flink; while (le != &parfileref->fcb->dir_children_index) { dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_index);
if (dc2->index == 0) { if ((case_sensitive && dc2->name.Length == dc->name.Length && RtlCompareMemory(dc2->name.Buffer, dc->name.Buffer, dc2->name.Length) == dc2->name.Length) || (!case_sensitive && dc2->name_uc.Length == dc->name_uc.Length && RtlCompareMemory(dc2->name_uc.Buffer, dc->name_uc.Buffer, dc2->name_uc.Length) == dc2->name_uc.Length) ) { existing_dc = dc2; break; } } else break;
le = le->Flink; }
if (existing_dc) { ExFreePool(dc->utf8.Buffer); ExFreePool(dc->name.Buffer); ExFreePool(dc); reap_fileref(Vcb, fileref); free_fileref(parfileref);
increase_fileref_refcount(existing_dc->fileref); *pfileref = existing_dc->fileref;
return STATUS_OBJECT_NAME_COLLISION; }
dc->fileref = fileref; fileref->dc = dc; fileref->parent = (struct _file_ref*)parfileref; fcb->deleted = false;
InsertHeadList(&parfileref->fcb->dir_children_index, &dc->list_entry_index);
InsertTailList(&parfileref->children, &fileref->list_entry);
ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
mark_fileref_dirty(fileref);
parfileref->fcb->inode_item.transid = Vcb->superblock.generation; parfileref->fcb->inode_item.sequence++; parfileref->fcb->inode_item.st_ctime = now; parfileref->fcb->inode_item_changed = true;
mark_fcb_dirty(parfileref->fcb);
parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation; parfileref->fcb->subvol->root_item.ctime = now;
increase_fileref_refcount(parfileref);
*pfileref = fileref;
send_notification_fileref(parfileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_ADDED_STREAM, &fileref->dc->name);
return STATUS_SUCCESS;}
// LXSS programs can be distinguished by the fact they have a NULL PEB.#ifdef _AMD64_static __inline bool called_from_lxss() { NTSTATUS Status; PROCESS_BASIC_INFORMATION pbi; ULONG retlen;
Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &retlen);
if (!NT_SUCCESS(Status)) { ERR("ZwQueryInformationProcess returned %08lx\n", Status); return false; }
return !pbi.PebBaseAddress;}#else#define called_from_lxss() false#endif
static NTSTATUS file_create(PIRP Irp, _Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb, PFILE_OBJECT FileObject, file_ref* related, bool loaded_related, PUNICODE_STRING fnus, ULONG disposition, ULONG options, file_ref** existing_fileref, LIST_ENTRY* rollback) { NTSTATUS Status; file_ref *fileref, *parfileref = NULL; ULONG i, j; ccb* ccb; static const WCHAR datasuf[] = {':','$','D','A','T','A',0}; UNICODE_STRING dsus, fpus, stream; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool; ECP_LIST* ecp_list; ATOMIC_CREATE_ECP_CONTEXT* acec = NULL;#ifdef DEBUG_FCB_REFCOUNTS LONG oc;#endif
TRACE("(%p, %p, %p, %.*S, %lx, %lx)\n", Irp, Vcb, FileObject, (int)(fnus->Length / sizeof(WCHAR)), fnus->Buffer, disposition, options);
if (Vcb->readonly) return STATUS_MEDIA_WRITE_PROTECTED;
if (options & FILE_DELETE_ON_CLOSE && IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_READONLY && !(IrpSp->Flags & SL_IGNORE_READONLY_ATTRIBUTE)) { return STATUS_CANNOT_DELETE; }
if (fFsRtlGetEcpListFromIrp && fFsRtlGetNextExtraCreateParameter) { if (NT_SUCCESS(fFsRtlGetEcpListFromIrp(Irp, &ecp_list)) && ecp_list) { void* ctx = NULL; GUID type; ULONG ctxsize;
do { Status = fFsRtlGetNextExtraCreateParameter(ecp_list, ctx, &type, &ctx, &ctxsize);
if (NT_SUCCESS(Status)) { if (RtlCompareMemory(&type, &GUID_ECP_ATOMIC_CREATE, sizeof(GUID)) == sizeof(GUID)) { if (ctxsize >= sizeof(ATOMIC_CREATE_ECP_CONTEXT)) acec = ctx; else { ERR("GUID_ECP_ATOMIC_CREATE context was too short: %lu bytes, expected %Iu\n", ctxsize, sizeof(ATOMIC_CREATE_ECP_CONTEXT)); } } else if (RtlCompareMemory(&type, &GUID_ECP_QUERY_ON_CREATE, sizeof(GUID)) == sizeof(GUID)) WARN("unhandled ECP GUID_ECP_QUERY_ON_CREATE\n"); else if (RtlCompareMemory(&type, &GUID_ECP_CREATE_REDIRECTION, sizeof(GUID)) == sizeof(GUID)) WARN("unhandled ECP GUID_ECP_CREATE_REDIRECTION\n"); else { WARN("unhandled ECP {%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", type.Data1, type.Data2, type.Data3, type.Data4[0], type.Data4[1], type.Data4[2], type.Data4[3], type.Data4[4], type.Data4[5], type.Data4[6], type.Data4[7]); } } } while (NT_SUCCESS(Status)); } }
dsus.Buffer = (WCHAR*)datasuf; dsus.Length = dsus.MaximumLength = sizeof(datasuf) - sizeof(WCHAR); fpus.Buffer = NULL;
if (!loaded_related) { Status = open_fileref(Vcb, &parfileref, fnus, related, true, NULL, NULL, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
if (!NT_SUCCESS(Status)) goto end; } else parfileref = related;
if (parfileref->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) { Status = STATUS_OBJECT_PATH_NOT_FOUND; goto end; }
if (is_subvol_readonly(parfileref->fcb->subvol, Irp)) { Status = STATUS_ACCESS_DENIED; goto end; }
i = (fnus->Length / sizeof(WCHAR))-1; while ((fnus->Buffer[i] == '\\' || fnus->Buffer[i] == '/') && i > 0) { i--; }
j = i;
while (i > 0 && fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/') { i--; }
fpus.MaximumLength = (USHORT)((j - i + 2) * sizeof(WCHAR)); fpus.Buffer = ExAllocatePoolWithTag(pool_type, fpus.MaximumLength, ALLOC_TAG); if (!fpus.Buffer) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; }
fpus.Length = (USHORT)((j - i + 1) * sizeof(WCHAR));
RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR)); fpus.Buffer[j - i + 1] = 0;
if (fpus.Length > dsus.Length) { // check for :$DATA suffix UNICODE_STRING lb;
lb.Buffer = &fpus.Buffer[(fpus.Length - dsus.Length)/sizeof(WCHAR)]; lb.Length = lb.MaximumLength = dsus.Length;
TRACE("lb = %.*S\n", (int)(lb.Length/sizeof(WCHAR)), lb.Buffer);
if (FsRtlAreNamesEqual(&dsus, &lb, true, NULL)) { TRACE("ignoring :$DATA suffix\n");
fpus.Length -= lb.Length;
if (fpus.Length > sizeof(WCHAR) && fpus.Buffer[(fpus.Length-1)/sizeof(WCHAR)] == ':') fpus.Length -= sizeof(WCHAR);
TRACE("fpus = %.*S\n", (int)(fpus.Length / sizeof(WCHAR)), fpus.Buffer); } }
stream.Length = 0;
for (i = 0; i < fpus.Length / sizeof(WCHAR); i++) { if (fpus.Buffer[i] == ':') { stream.Length = (USHORT)(fpus.Length - (i * sizeof(WCHAR)) - sizeof(WCHAR)); stream.Buffer = &fpus.Buffer[i+1]; fpus.Buffer[i] = 0; fpus.Length = (USHORT)(i * sizeof(WCHAR)); break; } }
if (stream.Length > 0) { Status = create_stream(Vcb, &fileref, &parfileref, &fpus, &stream, Irp, options, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, rollback); if (!NT_SUCCESS(Status)) { ERR("create_stream returned %08lx\n", Status); goto end; }
IoSetShareAccess(IrpSp->Parameters.Create.SecurityContext->DesiredAccess, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access); } else { ACCESS_MASK granted_access;
Status = check_file_name_valid(&fpus, false, false); if (!NT_SUCCESS(Status)) goto end;
SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext, true, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL, IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &granted_access, &Status)) { SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext); goto end; }
SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) { ULONG offset;
Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, &offset); if (!NT_SUCCESS(Status)) { ERR("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset); goto end; } }
Status = file_create2(Irp, Vcb, &fpus, parfileref, options, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, &fileref, IrpSp->Flags & SL_CASE_SENSITIVE, rollback);
if (Status == STATUS_OBJECT_NAME_COLLISION) { *existing_fileref = fileref; goto end; } else if (!NT_SUCCESS(Status)) { ERR("file_create2 returned %08lx\n", Status); goto end; }
IoSetShareAccess(IrpSp->Parameters.Create.SecurityContext->DesiredAccess, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
send_notification_fileref(fileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL); queue_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); }
FileObject->FsContext = fileref->fcb;
ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG); if (!ccb) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; fileref->deleted = true; fileref->fcb->deleted = true;
if (stream.Length == 0) { ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); parfileref->fcb->inode_item.st_size -= fileref->dc->utf8.Length * 2; ExReleaseResourceLite(parfileref->fcb->Header.Resource); }
free_fileref(fileref); goto end; }
RtlZeroMemory(ccb, sizeof(*ccb));
ccb->fileref = fileref;
ccb->NodeType = BTRFS_NODE_TYPE_CCB; ccb->NodeSize = sizeof(*ccb); ccb->disposition = disposition; ccb->options = options; ccb->query_dir_offset = 0; RtlInitUnicodeString(&ccb->query_string, NULL); ccb->has_wildcard = false; ccb->specific_file = false; ccb->access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess; ccb->case_sensitive = IrpSp->Flags & SL_CASE_SENSITIVE; ccb->reserving = false; ccb->lxss = called_from_lxss();
#ifdef DEBUG_FCB_REFCOUNTS oc = InterlockedIncrement(&fileref->open_count); ERR("fileref %p: open_count now %i\n", fileref, oc);#else InterlockedIncrement(&fileref->open_count);#endif InterlockedIncrement(&Vcb->open_files);
FileObject->FsContext2 = ccb;
FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
// FIXME - ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT if (acec && acec->InFlags & ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED) { if (acec->ReparseBufferLength > sizeof(uint32_t) && *(uint32_t*)acec->ReparseBuffer == IO_REPARSE_TAG_SYMLINK) { fileref->fcb->inode_item.st_mode &= ~(__S_IFIFO | __S_IFCHR | __S_IFBLK | __S_IFSOCK); fileref->fcb->type = BTRFS_TYPE_FILE; fileref->fcb->atts &= ~FILE_ATTRIBUTE_DIRECTORY; }
if (fileref->fcb->type == BTRFS_TYPE_SOCKET || fileref->fcb->type == BTRFS_TYPE_FIFO || fileref->fcb->type == BTRFS_TYPE_CHARDEV || fileref->fcb->type == BTRFS_TYPE_BLOCKDEV) { // NOP. If called from LXSS, humour it - we hardcode the values elsewhere. } else { Status = set_reparse_point2(fileref->fcb, acec->ReparseBuffer, acec->ReparseBufferLength, NULL, NULL, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("set_reparse_point2 returned %08lx\n", Status); fileref->deleted = true; fileref->fcb->deleted = true;
if (stream.Length == 0) { ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true); parfileref->fcb->inode_item.st_size -= fileref->dc->utf8.Length * 2; ExReleaseResourceLite(parfileref->fcb->Header.Resource); }
free_fileref(fileref); return Status; } }
acec->OutFlags |= ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET; }
if (acec && acec->InFlags & ATOMIC_CREATE_ECP_IN_FLAG_OP_FLAGS_SPECIFIED) { if (acec->InOpFlags & ATOMIC_CREATE_ECP_IN_OP_FLAG_CASE_SENSITIVE_FLAGS_SPECIFIED && fileref->fcb->atts & FILE_ATTRIBUTE_DIRECTORY) { if ((acec->InCaseSensitiveFlags & acec->CaseSensitiveFlagsMask) & FILE_CS_FLAG_CASE_SENSITIVE_DIR) { acec->OutCaseSensitiveFlags = FILE_CS_FLAG_CASE_SENSITIVE_DIR; fileref->fcb->case_sensitive = true; ccb->case_sensitive = true; }
acec->OutOpFlags |= ATOMIC_CREATE_ECP_OUT_OP_FLAG_CASE_SENSITIVE_FLAGS_SET; }
acec->OutFlags |= ATOMIC_CREATE_ECP_OUT_FLAG_OP_FLAGS_HONORED; }
fileref->dc->type = fileref->fcb->type;
end: if (fpus.Buffer) ExFreePool(fpus.Buffer);
if (parfileref && !loaded_related) free_fileref(parfileref);
return Status;}
static __inline void debug_create_options(ULONG RequestedOptions) { if (RequestedOptions != 0) { ULONG options = RequestedOptions;
TRACE("requested options:\n");
if (options & FILE_DIRECTORY_FILE) { TRACE(" FILE_DIRECTORY_FILE\n"); options &= ~FILE_DIRECTORY_FILE; }
if (options & FILE_WRITE_THROUGH) { TRACE(" FILE_WRITE_THROUGH\n"); options &= ~FILE_WRITE_THROUGH; }
if (options & FILE_SEQUENTIAL_ONLY) { TRACE(" FILE_SEQUENTIAL_ONLY\n"); options &= ~FILE_SEQUENTIAL_ONLY; }
if (options & FILE_NO_INTERMEDIATE_BUFFERING) { TRACE(" FILE_NO_INTERMEDIATE_BUFFERING\n"); options &= ~FILE_NO_INTERMEDIATE_BUFFERING; }
if (options & FILE_SYNCHRONOUS_IO_ALERT) { TRACE(" FILE_SYNCHRONOUS_IO_ALERT\n"); options &= ~FILE_SYNCHRONOUS_IO_ALERT; }
if (options & FILE_SYNCHRONOUS_IO_NONALERT) { TRACE(" FILE_SYNCHRONOUS_IO_NONALERT\n"); options &= ~FILE_SYNCHRONOUS_IO_NONALERT; }
if (options & FILE_NON_DIRECTORY_FILE) { TRACE(" FILE_NON_DIRECTORY_FILE\n"); options &= ~FILE_NON_DIRECTORY_FILE; }
if (options & FILE_CREATE_TREE_CONNECTION) { TRACE(" FILE_CREATE_TREE_CONNECTION\n"); options &= ~FILE_CREATE_TREE_CONNECTION; }
if (options & FILE_COMPLETE_IF_OPLOCKED) { TRACE(" FILE_COMPLETE_IF_OPLOCKED\n"); options &= ~FILE_COMPLETE_IF_OPLOCKED; }
if (options & FILE_NO_EA_KNOWLEDGE) { TRACE(" FILE_NO_EA_KNOWLEDGE\n"); options &= ~FILE_NO_EA_KNOWLEDGE; }
if (options & FILE_OPEN_REMOTE_INSTANCE) { TRACE(" FILE_OPEN_REMOTE_INSTANCE\n"); options &= ~FILE_OPEN_REMOTE_INSTANCE; }
if (options & FILE_RANDOM_ACCESS) { TRACE(" FILE_RANDOM_ACCESS\n"); options &= ~FILE_RANDOM_ACCESS; }
if (options & FILE_DELETE_ON_CLOSE) { TRACE(" FILE_DELETE_ON_CLOSE\n"); options &= ~FILE_DELETE_ON_CLOSE; }
if (options & FILE_OPEN_BY_FILE_ID) { TRACE(" FILE_OPEN_BY_FILE_ID\n"); options &= ~FILE_OPEN_BY_FILE_ID; }
if (options & FILE_OPEN_FOR_BACKUP_INTENT) { TRACE(" FILE_OPEN_FOR_BACKUP_INTENT\n"); options &= ~FILE_OPEN_FOR_BACKUP_INTENT; }
if (options & FILE_NO_COMPRESSION) { TRACE(" FILE_NO_COMPRESSION\n"); options &= ~FILE_NO_COMPRESSION; }
#if NTDDI_VERSION >= NTDDI_WIN7 if (options & FILE_OPEN_REQUIRING_OPLOCK) { TRACE(" FILE_OPEN_REQUIRING_OPLOCK\n"); options &= ~FILE_OPEN_REQUIRING_OPLOCK; }
if (options & FILE_DISALLOW_EXCLUSIVE) { TRACE(" FILE_DISALLOW_EXCLUSIVE\n"); options &= ~FILE_DISALLOW_EXCLUSIVE; }#endif
if (options & FILE_RESERVE_OPFILTER) { TRACE(" FILE_RESERVE_OPFILTER\n"); options &= ~FILE_RESERVE_OPFILTER; }
if (options & FILE_OPEN_REPARSE_POINT) { TRACE(" FILE_OPEN_REPARSE_POINT\n"); options &= ~FILE_OPEN_REPARSE_POINT; }
if (options & FILE_OPEN_NO_RECALL) { TRACE(" FILE_OPEN_NO_RECALL\n"); options &= ~FILE_OPEN_NO_RECALL; }
if (options & FILE_OPEN_FOR_FREE_SPACE_QUERY) { TRACE(" FILE_OPEN_FOR_FREE_SPACE_QUERY\n"); options &= ~FILE_OPEN_FOR_FREE_SPACE_QUERY; }
if (options) TRACE(" unknown options: %lx\n", options); } else { TRACE("requested options: (none)\n"); }}
static NTSTATUS get_reparse_block(fcb* fcb, uint8_t** data) { NTSTATUS Status;
if (fcb->type == BTRFS_TYPE_FILE || fcb->type == BTRFS_TYPE_SYMLINK) { ULONG size, bytes_read, i;
if (fcb->type == BTRFS_TYPE_FILE && fcb->inode_item.st_size < sizeof(ULONG)) { WARN("file was too short to be a reparse point\n"); return STATUS_INVALID_PARAMETER; }
// 0x10007 = 0xffff (maximum length of data buffer) + 8 bytes header size = (ULONG)min(0x10007, fcb->inode_item.st_size);
if (size == 0) return STATUS_INVALID_PARAMETER;
*data = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG); if (!*data) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
Status = read_file(fcb, *data, 0, size, &bytes_read, NULL); if (!NT_SUCCESS(Status)) { ERR("read_file_fcb returned %08lx\n", Status); ExFreePool(*data); return Status; }
if (fcb->type == BTRFS_TYPE_SYMLINK) { ULONG stringlen, reqlen; uint16_t subnamelen, printnamelen; REPARSE_DATA_BUFFER* rdb;
Status = utf8_to_utf16(NULL, 0, &stringlen, (char*)*data, bytes_read); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 1 returned %08lx\n", Status); ExFreePool(*data); return Status; }
subnamelen = printnamelen = (USHORT)stringlen;
reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
rdb = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
if (!rdb) { ERR("out of memory\n"); ExFreePool(*data); return STATUS_INSUFFICIENT_RESOURCES; }
rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK; rdb->ReparseDataLength = (USHORT)(reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer)); rdb->Reserved = 0;
rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0; rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen; rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen; rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen; rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
Status = utf8_to_utf16(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], stringlen, &stringlen, (char*)*data, size);
if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 2 returned %08lx\n", Status); ExFreePool(rdb); ExFreePool(*data); return Status; }
for (i = 0; i < stringlen / sizeof(WCHAR); i++) { if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/') rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\'; }
RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
ExFreePool(*data);
*data = (uint8_t*)rdb; } else { Status = fFsRtlValidateReparsePointBuffer(bytes_read, (REPARSE_DATA_BUFFER*)*data); if (!NT_SUCCESS(Status)) { ERR("FsRtlValidateReparsePointBuffer returned %08lx\n", Status); ExFreePool(*data); return Status; } } } else if (fcb->type == BTRFS_TYPE_DIRECTORY) { if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length == 0) return STATUS_INTERNAL_ERROR;
if (fcb->reparse_xattr.Length < sizeof(ULONG)) { WARN("xattr was too short to be a reparse point\n"); return STATUS_INTERNAL_ERROR; }
Status = fFsRtlValidateReparsePointBuffer(fcb->reparse_xattr.Length, (REPARSE_DATA_BUFFER*)fcb->reparse_xattr.Buffer); if (!NT_SUCCESS(Status)) { ERR("FsRtlValidateReparsePointBuffer returned %08lx\n", Status); return Status; }
*data = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.Length, ALLOC_TAG); if (!*data) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(*data, fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length); } else return STATUS_INVALID_PARAMETER;
return STATUS_SUCCESS;}
static void fcb_load_csums(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, fcb* fcb, PIRP Irp) { LIST_ENTRY* le; NTSTATUS Status;
if (fcb->csum_loaded) return;
if (IsListEmpty(&fcb->extents) || fcb->inode_item.flags & BTRFS_INODE_NODATASUM) goto end;
le = fcb->extents.Flink; while (le != &fcb->extents) { extent* ext = CONTAINING_RECORD(le, extent, list_entry);
if (!ext->ignore && ext->extent_data.type == EXTENT_TYPE_REGULAR) { EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ext->extent_data.data[0]; uint64_t len;
len = (ext->extent_data.compression == BTRFS_COMPRESSION_NONE ? ed2->num_bytes : ed2->size) >> Vcb->sector_shift;
ext->csum = ExAllocatePoolWithTag(NonPagedPool, (ULONG)(len * Vcb->csum_size), ALLOC_TAG); if (!ext->csum) { ERR("out of memory\n"); goto end; }
Status = load_csum(Vcb, ext->csum, ed2->address + (ext->extent_data.compression == BTRFS_COMPRESSION_NONE ? ed2->offset : 0), len, Irp);
if (!NT_SUCCESS(Status)) { ERR("load_csum returned %08lx\n", Status); goto end; } }
le = le->Flink; }
end: fcb->csum_loaded = true;}
static NTSTATUS open_file3(device_extension* Vcb, PIRP Irp, ACCESS_MASK granted_access, file_ref* fileref, LIST_ENTRY* rollback) { NTSTATUS Status; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); ULONG options = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS; ULONG RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff); PFILE_OBJECT FileObject = IrpSp->FileObject; POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool; ccb* ccb;
if (granted_access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) { if (!MmFlushImageSection(&fileref->fcb->nonpaged->segment_object, MmFlushForWrite)) return (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION; }
if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) { ULONG defda, oldatts, filter; LARGE_INTEGER time; BTRFS_TIME now;
if (!fileref->fcb->ads && (IrpSp->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != ((fileref->fcb->atts & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)))) return STATUS_ACCESS_DENIED;
if (fileref->fcb->ads) { Status = stream_set_end_of_file_information(Vcb, 0, fileref->fcb, fileref, false); if (!NT_SUCCESS(Status)) { ERR("stream_set_end_of_file_information returned %08lx\n", Status); return Status; } } else { Status = truncate_file(fileref->fcb, 0, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("truncate_file returned %08lx\n", Status); return Status; } }
if (Irp->Overlay.AllocationSize.QuadPart > 0) { Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, true, NULL, rollback);
if (!NT_SUCCESS(Status)) { ERR("extend_file returned %08lx\n", Status); return Status; } }
if (!fileref->fcb->ads) { LIST_ENTRY* le;
if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) { ULONG offset; FILE_FULL_EA_INFORMATION* eainfo;
Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, &offset); if (!NT_SUCCESS(Status)) { ERR("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset); return Status; }
fileref->fcb->ealen = 4;
// capitalize EA name eainfo = Irp->AssociatedIrp.SystemBuffer; do { STRING s;
s.Length = s.MaximumLength = eainfo->EaNameLength; s.Buffer = eainfo->EaName;
RtlUpperString(&s, &s);
fileref->fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
if (eainfo->NextEntryOffset == 0) break;
eainfo = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)eainfo) + eainfo->NextEntryOffset); } while (true);
if (fileref->fcb->ea_xattr.Buffer) ExFreePool(fileref->fcb->ea_xattr.Buffer);
fileref->fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(pool_type, IrpSp->Parameters.Create.EaLength, ALLOC_TAG); if (!fileref->fcb->ea_xattr.Buffer) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = (USHORT)IrpSp->Parameters.Create.EaLength; RtlCopyMemory(fileref->fcb->ea_xattr.Buffer, Irp->AssociatedIrp.SystemBuffer, fileref->fcb->ea_xattr.Length); } else { if (fileref->fcb->ea_xattr.Length > 0) { ExFreePool(fileref->fcb->ea_xattr.Buffer); fileref->fcb->ea_xattr.Buffer = NULL; fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = 0;
fileref->fcb->ea_changed = true; fileref->fcb->ealen = 0; } }
// remove streams and send notifications le = fileref->fcb->dir_children_index.Flink; while (le != &fileref->fcb->dir_children_index) { dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index); LIST_ENTRY* le2 = le->Flink;
if (dc->index == 0) { if (!dc->fileref) { file_ref* fr2;
Status = open_fileref_child(Vcb, fileref, &dc->name, true, true, true, PagedPool, &fr2, NULL); if (!NT_SUCCESS(Status)) WARN("open_fileref_child returned %08lx\n", Status); }
if (dc->fileref) { queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_REMOVED_STREAM, &dc->name);
Status = delete_fileref(dc->fileref, NULL, false, NULL, rollback); if (!NT_SUCCESS(Status)) { ERR("delete_fileref returned %08lx\n", Status); return Status; } } } else break;
le = le2; } }
KeQuerySystemTime(&time); win_time_to_unix(time, &now);
filter = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
if (fileref->fcb->ads) { fileref->parent->fcb->inode_item.st_mtime = now; fileref->parent->fcb->inode_item_changed = true; mark_fcb_dirty(fileref->parent->fcb);
queue_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED, &fileref->dc->name); } else { mark_fcb_dirty(fileref->fcb);
oldatts = fileref->fcb->atts;
defda = get_file_attributes(Vcb, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type, fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', true, Irp);
if (RequestedDisposition == FILE_SUPERSEDE) fileref->fcb->atts = IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE; else fileref->fcb->atts |= IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
if (fileref->fcb->atts != oldatts) { fileref->fcb->atts_changed = true; fileref->fcb->atts_deleted = IrpSp->Parameters.Create.FileAttributes == defda; filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; }
fileref->fcb->inode_item.transid = Vcb->superblock.generation; fileref->fcb->inode_item.sequence++; fileref->fcb->inode_item.st_ctime = now; fileref->fcb->inode_item.st_mtime = now; fileref->fcb->inode_item_changed = true;
queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL); } } else { if (options & FILE_NO_EA_KNOWLEDGE && fileref->fcb->ea_xattr.Length > 0) { FILE_FULL_EA_INFORMATION* ffei = (FILE_FULL_EA_INFORMATION*)fileref->fcb->ea_xattr.Buffer;
do { if (ffei->Flags & FILE_NEED_EA) { WARN("returning STATUS_ACCESS_DENIED as no EA knowledge\n");
return STATUS_ACCESS_DENIED; }
if (ffei->NextEntryOffset == 0) break;
ffei = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ffei) + ffei->NextEntryOffset); } while (true); } }
FileObject->FsContext = fileref->fcb;
ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG); if (!ccb) { ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(ccb, sizeof(*ccb));
ccb->NodeType = BTRFS_NODE_TYPE_CCB; ccb->NodeSize = sizeof(*ccb); ccb->disposition = RequestedDisposition; ccb->options = options; ccb->query_dir_offset = 0; RtlInitUnicodeString(&ccb->query_string, NULL); ccb->has_wildcard = false; ccb->specific_file = false; ccb->access = granted_access; ccb->case_sensitive = IrpSp->Flags & SL_CASE_SENSITIVE; ccb->reserving = false; ccb->lxss = called_from_lxss();
ccb->fileref = fileref;
FileObject->FsContext2 = ccb; FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
switch (RequestedDisposition) { case FILE_SUPERSEDE: Irp->IoStatus.Information = FILE_SUPERSEDED; break;
case FILE_OPEN: case FILE_OPEN_IF: Irp->IoStatus.Information = FILE_OPENED; break;
case FILE_OVERWRITE: case FILE_OVERWRITE_IF: Irp->IoStatus.Information = FILE_OVERWRITTEN; break; }
// Make sure paging files don't have any extents marked as being prealloc, // as this would mean we'd have to lock exclusively when writing. if (IrpSp->Flags & SL_OPEN_PAGING_FILE) { LIST_ENTRY* le; bool changed = false;
ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, true);
le = fileref->fcb->extents.Flink;
while (le != &fileref->fcb->extents) { extent* ext = CONTAINING_RECORD(le, extent, list_entry);
if (ext->extent_data.type == EXTENT_TYPE_PREALLOC) { ext->extent_data.type = EXTENT_TYPE_REGULAR; changed = true; }
le = le->Flink; }
ExReleaseResourceLite(fileref->fcb->Header.Resource);
if (changed) { fileref->fcb->extents_changed = true; mark_fcb_dirty(fileref->fcb); }
fileref->fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE; }
#ifdef DEBUG_FCB_REFCOUNTS LONG oc = InterlockedIncrement(&fileref->open_count); ERR("fileref %p: open_count now %i\n", fileref, oc);#else InterlockedIncrement(&fileref->open_count);#endif InterlockedIncrement(&Vcb->open_files);
return STATUS_SUCCESS;}
static void __stdcall oplock_complete(PVOID Context, PIRP Irp) { NTSTATUS Status; LIST_ENTRY rollback; bool skip_lock; oplock_context* ctx = Context; device_extension* Vcb = ctx->Vcb;
TRACE("(%p, %p)\n", Context, Irp);
InitializeListHead(&rollback);
skip_lock = ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock);
if (!skip_lock) ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
ExAcquireResourceSharedLite(&Vcb->fileref_lock, true);
// FIXME - trans Status = open_file3(Vcb, Irp, ctx->granted_access, ctx->fileref, &rollback);
if (!NT_SUCCESS(Status)) { free_fileref(ctx->fileref); do_rollback(ctx->Vcb, &rollback); } else clear_rollback(&rollback);
ExReleaseResourceLite(&Vcb->fileref_lock);
if (Status == STATUS_SUCCESS) { fcb* fcb2; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); PFILE_OBJECT FileObject = IrpSp->FileObject; bool skip_fcb_lock;
IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess |= ctx->granted_access; IrpSp->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess &= ~(ctx->granted_access | MAXIMUM_ALLOWED);
if (!FileObject->Vpb) FileObject->Vpb = Vcb->devobj->Vpb;
fcb2 = FileObject->FsContext;
if (fcb2->ads) { struct _ccb* ccb2 = FileObject->FsContext2;
fcb2 = ccb2->fileref->parent->fcb; }
skip_fcb_lock = ExIsResourceAcquiredExclusiveLite(fcb2->Header.Resource);
if (!skip_fcb_lock) ExAcquireResourceExclusiveLite(fcb2->Header.Resource, true);
fcb_load_csums(Vcb, fcb2, Irp);
if (!skip_fcb_lock) ExReleaseResourceLite(fcb2->Header.Resource); }
if (!skip_lock) ExReleaseResourceLite(&Vcb->tree_lock);
// FIXME - call free_trans if failed and within transaction
Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
ctx->Status = Status;
KeSetEvent(&ctx->event, 0, false);}
static NTSTATUS open_file2(device_extension* Vcb, ULONG RequestedDisposition, file_ref* fileref, ACCESS_MASK* granted_access, PFILE_OBJECT FileObject, UNICODE_STRING* fn, ULONG options, PIRP Irp, LIST_ENTRY* rollback, oplock_context** opctx) { NTSTATUS Status; file_ref* sf; bool readonly; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) { LARGE_INTEGER zero;
if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || is_subvol_readonly(fileref->fcb->subvol, Irp)) { Status = STATUS_ACCESS_DENIED; goto end; }
if (Vcb->readonly) { Status = STATUS_MEDIA_WRITE_PROTECTED; goto end; }
zero.QuadPart = 0; if (!MmCanFileBeTruncated(&fileref->fcb->nonpaged->segment_object, &zero)) { Status = STATUS_USER_MAPPED_FILE; goto end; } }
if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess != 0) { SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
if (!SeAccessCheck((fileref->fcb->ads || fileref->fcb == Vcb->dummy_fcb) ? fileref->parent->fcb->sd : fileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext, true, IrpSp->Parameters.Create.SecurityContext->DesiredAccess, 0, NULL, IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, granted_access, &Status)) { SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext); TRACE("SeAccessCheck failed, returning %08lx\n", Status); goto end; }
SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext); } else *granted_access = 0;
TRACE("deleted = %s\n", fileref->deleted ? "true" : "false");
sf = fileref; while (sf) { if (sf->delete_on_close) { TRACE("could not open as deletion pending\n"); Status = STATUS_DELETE_PENDING; goto end; } sf = sf->parent; }
readonly = (!fileref->fcb->ads && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY && !(IrpSp->Flags & SL_IGNORE_READONLY_ATTRIBUTE)) || (fileref->fcb->ads && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY && !(IrpSp->Flags & SL_IGNORE_READONLY_ATTRIBUTE)) || is_subvol_readonly(fileref->fcb->subvol, Irp) || fileref->fcb == Vcb->dummy_fcb || Vcb->readonly;
if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || readonly)) { Status = STATUS_CANNOT_DELETE; goto end; }
readonly |= fileref->fcb->inode_item.flags_ro & BTRFS_INODE_RO_VERITY;
if (readonly) { ACCESS_MASK allowed;
allowed = READ_CONTROL | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY | FILE_TRAVERSE;
if (!Vcb->readonly && (fileref->fcb == Vcb->dummy_fcb || fileref->fcb->inode == SUBVOL_ROOT_INODE)) allowed |= DELETE;
if (fileref->fcb != Vcb->dummy_fcb && !is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) { allowed |= DELETE | WRITE_OWNER | WRITE_DAC | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
if (!fileref->fcb->ads && fileref->fcb->type == BTRFS_TYPE_DIRECTORY) allowed |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD; } else if (fileref->fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) { // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
allowed |= FILE_WRITE_ATTRIBUTES; }
if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess & MAXIMUM_ALLOWED) { *granted_access &= allowed; IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess &= allowed; } else if (*granted_access & ~allowed) { Status = Vcb->readonly ? STATUS_MEDIA_WRITE_PROTECTED : STATUS_ACCESS_DENIED; goto end; }
if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) { WARN("cannot overwrite readonly file\n"); Status = STATUS_ACCESS_DENIED; goto end; } }
if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT)) { REPARSE_DATA_BUFFER* data;
/* How reparse points work from the point of view of the filesystem appears to * undocumented. When returning STATUS_REPARSE, MSDN encourages us to return * IO_REPARSE in Irp->IoStatus.Information, but that means we have to do our own * translation. If we instead return the reparse tag in Information, and store * a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer, * IopSymlinkProcessReparse will do the translation for us. */
Status = get_reparse_block(fileref->fcb, (uint8_t**)&data); if (!NT_SUCCESS(Status)) { ERR("get_reparse_block returned %08lx\n", Status); Status = STATUS_SUCCESS; } else { Irp->IoStatus.Information = data->ReparseTag;
if (fn->Buffer[(fn->Length / sizeof(WCHAR)) - 1] == '\\') data->Reserved = sizeof(WCHAR);
Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
Status = STATUS_REPARSE; goto end; } }
if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && !fileref->fcb->ads) { if (options & FILE_NON_DIRECTORY_FILE && !(fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) { Status = STATUS_FILE_IS_A_DIRECTORY; goto end; } } else if (options & FILE_DIRECTORY_FILE) { TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u)\n", fileref->fcb->type); Status = STATUS_NOT_A_DIRECTORY; goto end; }
if (fileref->open_count > 0) { oplock_context* ctx;
Status = IoCheckShareAccess(*granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, false);
if (!NT_SUCCESS(Status)) { if (Status == STATUS_SHARING_VIOLATION) TRACE("IoCheckShareAccess failed, returning %08lx\n", Status); else WARN("IoCheckShareAccess failed, returning %08lx\n", Status);
goto end; }
ctx = ExAllocatePoolWithTag(NonPagedPool, sizeof(oplock_context), ALLOC_TAG); if (!ctx) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; }
ctx->Vcb = Vcb; ctx->granted_access = *granted_access; ctx->fileref = fileref; KeInitializeEvent(&ctx->event, NotificationEvent, false);#ifdef __REACTOS__ Status = FsRtlCheckOplock(fcb_oplock(fileref->fcb), Irp, ctx, (POPLOCK_WAIT_COMPLETE_ROUTINE) oplock_complete, NULL);#else Status = FsRtlCheckOplock(fcb_oplock(fileref->fcb), Irp, ctx, oplock_complete, NULL);#endif /* __REACTOS__ */ if (Status == STATUS_PENDING) { *opctx = ctx; return Status; }
ExFreePool(ctx);
if (!NT_SUCCESS(Status)) { WARN("FsRtlCheckOplock returned %08lx\n", Status); goto end; }
IoUpdateShareAccess(FileObject, &fileref->fcb->share_access); } else IoSetShareAccess(*granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
Status = open_file3(Vcb, Irp, *granted_access, fileref, rollback);
if (!NT_SUCCESS(Status)) IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
end: if (!NT_SUCCESS(Status)) free_fileref(fileref);
return Status;}
NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb, root* subvol, uint64_t inode, file_ref** pfr, PIRP Irp) { NTSTATUS Status; fcb* fcb; uint64_t parent = 0; UNICODE_STRING name; bool hl_alloc = false; file_ref *parfr, *fr;
Status = open_fcb(Vcb, subvol, inode, 0, NULL, true, NULL, &fcb, PagedPool, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fcb returned %08lx\n", Status); return Status; }
ExAcquireResourceSharedLite(fcb->Header.Resource, true);
if (fcb->inode_item.st_nlink == 0 || fcb->deleted) { ExReleaseResourceLite(fcb->Header.Resource); free_fcb(fcb); return STATUS_OBJECT_NAME_NOT_FOUND; }
if (fcb->fileref) { *pfr = fcb->fileref; increase_fileref_refcount(fcb->fileref); free_fcb(fcb); ExReleaseResourceLite(fcb->Header.Resource); return STATUS_SUCCESS; }
if (IsListEmpty(&fcb->hardlinks)) { ExReleaseResourceLite(fcb->Header.Resource);
ExAcquireResourceSharedLite(&Vcb->dirty_filerefs_lock, true);
if (!IsListEmpty(&Vcb->dirty_filerefs)) { LIST_ENTRY* le = Vcb->dirty_filerefs.Flink; while (le != &Vcb->dirty_filerefs) { fr = CONTAINING_RECORD(le, file_ref, list_entry_dirty);
if (fr->fcb == fcb) { ExReleaseResourceLite(&Vcb->dirty_filerefs_lock); increase_fileref_refcount(fr); free_fcb(fcb); *pfr = fr; return STATUS_SUCCESS; }
le = le->Flink; } }
ExReleaseResourceLite(&Vcb->dirty_filerefs_lock);
{ KEY searchkey; traverse_ptr tp;
searchkey.obj_id = fcb->inode; searchkey.obj_type = TYPE_INODE_REF; searchkey.offset = 0;
Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp); if (!NT_SUCCESS(Status)) { ERR("find_item returned %08lx\n", Status); free_fcb(fcb); return Status; }
do { traverse_ptr next_tp;
if (tp.item->key.obj_id > fcb->inode || (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type > TYPE_INODE_EXTREF)) break;
if (tp.item->key.obj_id == fcb->inode) { if (tp.item->key.obj_type == TYPE_INODE_REF) { INODE_REF* ir = (INODE_REF*)tp.item->data;
if (tp.item->size < offsetof(INODE_REF, name[0]) || tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) { ERR("INODE_REF was too short\n"); free_fcb(fcb); return STATUS_INTERNAL_ERROR; }
ULONG stringlen;
Status = utf8_to_utf16(NULL, 0, &stringlen, ir->name, ir->n); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 1 returned %08lx\n", Status); free_fcb(fcb); return Status; }
name.Length = name.MaximumLength = (uint16_t)stringlen;
if (stringlen == 0) name.Buffer = NULL; else { name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
if (!name.Buffer) { ERR("out of memory\n"); free_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
Status = utf8_to_utf16(name.Buffer, stringlen, &stringlen, ir->name, ir->n); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 2 returned %08lx\n", Status); ExFreePool(name.Buffer); free_fcb(fcb); return Status; }
hl_alloc = true; }
parent = tp.item->key.offset;
break; } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) { INODE_EXTREF* ier = (INODE_EXTREF*)tp.item->data;
if (tp.item->size < offsetof(INODE_EXTREF, name[0]) || tp.item->size < offsetof(INODE_EXTREF, name[0]) + ier->n) { ERR("INODE_EXTREF was too short\n"); free_fcb(fcb); return STATUS_INTERNAL_ERROR; }
ULONG stringlen;
Status = utf8_to_utf16(NULL, 0, &stringlen, ier->name, ier->n); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 1 returned %08lx\n", Status); free_fcb(fcb); return Status; }
name.Length = name.MaximumLength = (uint16_t)stringlen;
if (stringlen == 0) name.Buffer = NULL; else { name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
if (!name.Buffer) { ERR("out of memory\n"); free_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
Status = utf8_to_utf16(name.Buffer, stringlen, &stringlen, ier->name, ier->n); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 2 returned %08lx\n", Status); ExFreePool(name.Buffer); free_fcb(fcb); return Status; }
hl_alloc = true; }
parent = ier->dir;
break; } }
if (find_next_item(Vcb, &tp, &next_tp, false, Irp)) tp = next_tp; else break; } while (true); }
if (parent == 0) { WARN("trying to open inode with no references\n"); free_fcb(fcb); return STATUS_INVALID_PARAMETER; } } else { hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
name = hl->name; parent = hl->parent;
ExReleaseResourceLite(fcb->Header.Resource); }
if (parent == inode) { // subvolume root KEY searchkey; traverse_ptr tp;
searchkey.obj_id = subvol->id; searchkey.obj_type = TYPE_ROOT_BACKREF; searchkey.offset = 0xffffffffffffffff;
Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); if (!NT_SUCCESS(Status)) { ERR("find_item returned %08lx\n", Status); free_fcb(fcb); return Status; }
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { ROOT_REF* rr = (ROOT_REF*)tp.item->data; LIST_ENTRY* le; root* r = NULL; ULONG stringlen;
if (tp.item->size < sizeof(ROOT_REF)) { ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF)); free_fcb(fcb); return STATUS_INTERNAL_ERROR; }
if (tp.item->size < offsetof(ROOT_REF, name[0]) + rr->n) { ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(ROOT_REF, name[0]) + rr->n); free_fcb(fcb); return STATUS_INTERNAL_ERROR; }
le = Vcb->roots.Flink; while (le != &Vcb->roots) { root* r2 = CONTAINING_RECORD(le, root, list_entry);
if (r2->id == tp.item->key.offset) { r = r2; break; }
le = le->Flink; }
if (!r) { ERR("couldn't find subvol %I64x\n", tp.item->key.offset); free_fcb(fcb); return STATUS_INTERNAL_ERROR; }
Status = open_fileref_by_inode(Vcb, r, rr->dir, &parfr, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fileref_by_inode returned %08lx\n", Status); free_fcb(fcb); return Status; }
Status = utf8_to_utf16(NULL, 0, &stringlen, rr->name, rr->n); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 1 returned %08lx\n", Status); free_fcb(fcb); return Status; }
name.Length = name.MaximumLength = (uint16_t)stringlen;
if (stringlen == 0) name.Buffer = NULL; else { if (hl_alloc) ExFreePool(name.Buffer);
name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
if (!name.Buffer) { ERR("out of memory\n"); free_fcb(fcb); return STATUS_INSUFFICIENT_RESOURCES; }
Status = utf8_to_utf16(name.Buffer, stringlen, &stringlen, rr->name, rr->n); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 2 returned %08lx\n", Status); ExFreePool(name.Buffer); free_fcb(fcb); return Status; }
hl_alloc = true; } } else { if (!Vcb->options.no_root_dir && subvol->id == BTRFS_ROOT_FSTREE && Vcb->root_fileref->fcb->subvol != subvol) { Status = open_fileref_by_inode(Vcb, Vcb->root_fileref->fcb->subvol, SUBVOL_ROOT_INODE, &parfr, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fileref_by_inode returned %08lx\n", Status); free_fcb(fcb); return Status; }
name.Length = name.MaximumLength = sizeof(root_dir_utf16) - sizeof(WCHAR); name.Buffer = (WCHAR*)root_dir_utf16; } else { ERR("couldn't find parent for subvol %I64x\n", subvol->id); free_fcb(fcb); return STATUS_INTERNAL_ERROR; } } } else { Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fileref_by_inode returned %08lx\n", Status); free_fcb(fcb); return Status; } }
Status = open_fileref_child(Vcb, parfr, &name, true, true, false, PagedPool, &fr, Irp);
if (hl_alloc) ExFreePool(name.Buffer);
if (!NT_SUCCESS(Status)) { ERR("open_fileref_child returned %08lx\n", Status);
free_fcb(fcb); free_fileref(parfr);
return Status; }
*pfr = fr;
free_fcb(fcb); free_fileref(parfr);
return STATUS_SUCCESS;}
static NTSTATUS open_file(PDEVICE_OBJECT DeviceObject, _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback, oplock_context** opctx) { PFILE_OBJECT FileObject = NULL; ULONG RequestedDisposition; ULONG options; NTSTATUS Status; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); USHORT parsed; ULONG fn_offset = 0; file_ref *related, *fileref = NULL; POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool; ACCESS_MASK granted_access; bool loaded_related = false; UNICODE_STRING fn;
Irp->IoStatus.Information = 0;
RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff); options = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
if (options & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) { WARN("error - supersede requested with FILE_DIRECTORY_FILE\n"); return STATUS_INVALID_PARAMETER; }
FileObject = IrpSp->FileObject;
if (!FileObject) { ERR("FileObject was NULL\n"); return STATUS_INVALID_PARAMETER; }
if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) { struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
related = relatedccb->fileref; } else related = NULL;
debug_create_options(options);
switch (RequestedDisposition) { case FILE_SUPERSEDE: TRACE("requested disposition: FILE_SUPERSEDE\n"); break;
case FILE_CREATE: TRACE("requested disposition: FILE_CREATE\n"); break;
case FILE_OPEN: TRACE("requested disposition: FILE_OPEN\n"); break;
case FILE_OPEN_IF: TRACE("requested disposition: FILE_OPEN_IF\n"); break;
case FILE_OVERWRITE: TRACE("requested disposition: FILE_OVERWRITE\n"); break;
case FILE_OVERWRITE_IF: TRACE("requested disposition: FILE_OVERWRITE_IF\n"); break;
default: ERR("unknown disposition: %lx\n", RequestedDisposition); Status = STATUS_NOT_IMPLEMENTED; goto exit; }
fn = FileObject->FileName;
TRACE("(%.*S)\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer); TRACE("FileObject = %p\n", FileObject);
if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) { Status = STATUS_MEDIA_WRITE_PROTECTED; goto exit; }
if (options & FILE_OPEN_BY_FILE_ID) { if (RequestedDisposition != FILE_OPEN) { WARN("FILE_OPEN_BY_FILE_ID not supported for anything other than FILE_OPEN\n"); Status = STATUS_INVALID_PARAMETER; goto exit; }
if (fn.Length == sizeof(uint64_t)) { uint64_t inode;
if (!related) { WARN("cannot open by short file ID unless related fileref also provided"\n); Status = STATUS_INVALID_PARAMETER; goto exit; }
inode = (*(uint64_t*)fn.Buffer) & 0xffffffffff;
if (related->fcb == Vcb->root_fileref->fcb && inode == 0) inode = Vcb->root_fileref->fcb->inode;
if (inode == 0) { // we use 0 to mean the parent of a subvolume fileref = related->parent; increase_fileref_refcount(fileref); Status = STATUS_SUCCESS; } else Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref, Irp);
goto loaded; } else if (fn.Length == sizeof(FILE_ID_128)) { uint64_t inode, subvol_id; root* subvol = NULL;
RtlCopyMemory(&inode, fn.Buffer, sizeof(uint64_t)); RtlCopyMemory(&subvol_id, (uint8_t*)fn.Buffer + sizeof(uint64_t), sizeof(uint64_t));
if (subvol_id == BTRFS_ROOT_FSTREE || (subvol_id >= 0x100 && subvol_id < 0x8000000000000000)) { LIST_ENTRY* le = Vcb->roots.Flink; while (le != &Vcb->roots) { root* r = CONTAINING_RECORD(le, root, list_entry);
if (r->id == subvol_id) { subvol = r; break; }
le = le->Flink; } }
if (!subvol) { WARN("subvol %I64x not found\n", subvol_id); Status = STATUS_OBJECT_NAME_NOT_FOUND; } else Status = open_fileref_by_inode(Vcb, subvol, inode, &fileref, Irp);
goto loaded; } else { WARN("invalid ID size for FILE_OPEN_BY_FILE_ID\n"); Status = STATUS_INVALID_PARAMETER; goto exit; } }
if (related && fn.Length != 0 && fn.Buffer[0] == '\\') { Status = STATUS_INVALID_PARAMETER; goto exit; }
if (!related && RequestedDisposition != FILE_OPEN && !(IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY)) { ULONG fnoff;
Status = open_fileref(Vcb, &related, &fn, NULL, true, &parsed, &fnoff, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) Status = STATUS_OBJECT_PATH_NOT_FOUND; else if (Status == STATUS_REPARSE) fileref = related; else if (NT_SUCCESS(Status)) { fnoff *= sizeof(WCHAR); fnoff += (related->dc ? related->dc->name.Length : 0) + sizeof(WCHAR);
if (related->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) { Status = STATUS_REPARSE; fileref = related; parsed = (USHORT)fnoff - sizeof(WCHAR); } else { fn.Buffer = &fn.Buffer[fnoff / sizeof(WCHAR)]; fn.Length -= (USHORT)fnoff;
Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
loaded_related = true; } } } else { Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp); }
loaded: if (Status == STATUS_REPARSE) { REPARSE_DATA_BUFFER* data;
ExAcquireResourceSharedLite(fileref->fcb->Header.Resource, true); Status = get_reparse_block(fileref->fcb, (uint8_t**)&data); ExReleaseResourceLite(fileref->fcb->Header.Resource);
if (!NT_SUCCESS(Status)) { ERR("get_reparse_block returned %08lx\n", Status);
Status = STATUS_SUCCESS; } else { Status = STATUS_REPARSE; RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
data->Reserved = FileObject->FileName.Length - parsed;
Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
free_fileref(fileref);
goto exit; } }
if (NT_SUCCESS(Status) && fileref->deleted) Status = STATUS_OBJECT_NAME_NOT_FOUND;
if (NT_SUCCESS(Status)) { if (RequestedDisposition == FILE_CREATE) { TRACE("file already exists, returning STATUS_OBJECT_NAME_COLLISION\n"); Status = STATUS_OBJECT_NAME_COLLISION;
free_fileref(fileref);
goto exit; } } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { if (RequestedDisposition == FILE_OPEN || RequestedDisposition == FILE_OVERWRITE) { TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n"); goto exit; } } else if (Status == STATUS_OBJECT_PATH_NOT_FOUND || Status == STATUS_OBJECT_NAME_INVALID) { TRACE("open_fileref returned %08lx\n", Status); goto exit; } else { ERR("open_fileref returned %08lx\n", Status); goto exit; }
if (NT_SUCCESS(Status)) { // file already exists Status = open_file2(Vcb, RequestedDisposition, fileref, &granted_access, FileObject, &fn, options, Irp, rollback, opctx); } else { file_ref* existing_file = NULL;
Status = file_create(Irp, Vcb, FileObject, related, loaded_related, &fn, RequestedDisposition, options, &existing_file, rollback);
if (Status == STATUS_OBJECT_NAME_COLLISION) { // already exists fileref = existing_file;
Status = open_file2(Vcb, RequestedDisposition, fileref, &granted_access, FileObject, &fn, options, Irp, rollback, opctx); } else { Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0; granted_access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess; } }
if (NT_SUCCESS(Status) && !(options & FILE_NO_INTERMEDIATE_BUFFERING)) FileObject->Flags |= FO_CACHE_SUPPORTED;
exit: if (loaded_related) free_fileref(related);
if (Status == STATUS_SUCCESS) { fcb* fcb2;
IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess |= granted_access; IrpSp->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess &= ~(granted_access | MAXIMUM_ALLOWED);
if (!FileObject->Vpb) FileObject->Vpb = DeviceObject->Vpb;
fcb2 = FileObject->FsContext;
if (fcb2->ads) { struct _ccb* ccb2 = FileObject->FsContext2;
fcb2 = ccb2->fileref->parent->fcb; }
ExAcquireResourceExclusiveLite(fcb2->Header.Resource, true); fcb_load_csums(Vcb, fcb2, Irp); ExReleaseResourceLite(fcb2->Header.Resource); } else if (Status != STATUS_REPARSE && Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_NOT_FOUND) TRACE("returning %08lx\n", Status);
return Status;}
static NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp) { NTSTATUS Status; LIST_ENTRY* le; bool need_verify = false;
ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
le = Vcb->devices.Flink; while (le != &Vcb->devices) { device* dev = CONTAINING_RECORD(le, device, list_entry);
if (dev->devobj && dev->removable) { ULONG cc; IO_STATUS_BLOCK iosb;
Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), true, &iosb);
if (IoIsErrorUserInduced(Status)) { ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08lx (user-induced)\n", Status); need_verify = true; } else if (!NT_SUCCESS(Status)) { ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08lx\n", Status); goto end; } else if (iosb.Information < sizeof(ULONG)) { ERR("iosb.Information was too short\n"); Status = STATUS_INTERNAL_ERROR; } else if (cc != dev->change_count) { dev->devobj->Flags |= DO_VERIFY_VOLUME; need_verify = true; } }
le = le->Flink; }
Status = STATUS_SUCCESS;
end: ExReleaseResourceLite(&Vcb->tree_lock);
if (need_verify) { PDEVICE_OBJECT devobj;
devobj = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread); IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
if (!devobj) { devobj = IoGetDeviceToVerify(PsGetCurrentThread()); IoSetDeviceToVerify(PsGetCurrentThread(), NULL); }
devobj = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
if (devobj) Status = IoVerifyVolume(devobj, false); else Status = STATUS_VERIFY_REQUIRED; }
return Status;}
static bool has_manage_volume_privilege(ACCESS_STATE* access_state, KPROCESSOR_MODE processor_mode) { PRIVILEGE_SET privset;
privset.PrivilegeCount = 1; privset.Control = PRIVILEGE_SET_ALL_NECESSARY; privset.Privilege[0].Luid = RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE); privset.Privilege[0].Attributes = 0;
return SePrivilegeCheck(&privset, &access_state->SubjectSecurityContext, processor_mode) ? true : false;}
_Dispatch_type_(IRP_MJ_CREATE)_Function_class_(DRIVER_DISPATCH)NTSTATUS __stdcall drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS Status; PIO_STACK_LOCATION IrpSp; device_extension* Vcb = DeviceObject->DeviceExtension; bool top_level, locked = false; oplock_context* opctx = NULL;
FsRtlEnterFileSystem();
TRACE("create (flags = %lx)\n", Irp->Flags);
top_level = is_top_level(Irp);
/* return success if just called for FS device object */ if (DeviceObject == master_devobj) { TRACE("create called for FS device object\n");
Irp->IoStatus.Information = FILE_OPENED; Status = STATUS_SUCCESS;
goto exit; } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { Status = vol_create(DeviceObject, Irp); goto exit; } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { Status = STATUS_INVALID_PARAMETER; goto exit; }
if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) { Status = STATUS_DEVICE_NOT_READY; goto exit; }
if (Vcb->removing) { Status = STATUS_ACCESS_DENIED; goto exit; }
Status = verify_vcb(Vcb, Irp); if (!NT_SUCCESS(Status)) { ERR("verify_vcb returned %08lx\n", Status); goto exit; }
ExAcquireResourceSharedLite(&Vcb->load_lock, true); locked = true;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (IrpSp->Flags != 0) { uint32_t flags = IrpSp->Flags;
TRACE("flags:\n");
if (flags & SL_CASE_SENSITIVE) { TRACE("SL_CASE_SENSITIVE\n"); flags &= ~SL_CASE_SENSITIVE; }
if (flags & SL_FORCE_ACCESS_CHECK) { TRACE("SL_FORCE_ACCESS_CHECK\n"); flags &= ~SL_FORCE_ACCESS_CHECK; }
if (flags & SL_OPEN_PAGING_FILE) { TRACE("SL_OPEN_PAGING_FILE\n"); flags &= ~SL_OPEN_PAGING_FILE; }
if (flags & SL_OPEN_TARGET_DIRECTORY) { TRACE("SL_OPEN_TARGET_DIRECTORY\n"); flags &= ~SL_OPEN_TARGET_DIRECTORY; }
if (flags & SL_STOP_ON_SYMLINK) { TRACE("SL_STOP_ON_SYMLINK\n"); flags &= ~SL_STOP_ON_SYMLINK; }
if (flags & SL_IGNORE_READONLY_ATTRIBUTE) { TRACE("SL_IGNORE_READONLY_ATTRIBUTE\n"); flags &= ~SL_IGNORE_READONLY_ATTRIBUTE; }
if (flags) WARN("unknown flags: %x\n", flags); } else { TRACE("flags: (none)\n"); }
if (!IrpSp->FileObject) { ERR("FileObject was NULL\n"); Status = STATUS_INVALID_PARAMETER; goto exit; }
if (IrpSp->FileObject->RelatedFileObject) { fcb* relatedfcb = IrpSp->FileObject->RelatedFileObject->FsContext;
if (relatedfcb && relatedfcb->Vcb != Vcb) { WARN("RelatedFileObject was for different device\n"); Status = STATUS_INVALID_PARAMETER; goto exit; } }
// opening volume if (IrpSp->FileObject->FileName.Length == 0 && !IrpSp->FileObject->RelatedFileObject) { ULONG RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff); ULONG RequestedOptions = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;#ifdef DEBUG_FCB_REFCOUNTS LONG rc;#endif ccb* ccb;
TRACE("open operation for volume\n");
if (RequestedDisposition != FILE_OPEN && RequestedDisposition != FILE_OPEN_IF) { Status = STATUS_ACCESS_DENIED; goto exit; }
if (RequestedOptions & FILE_DIRECTORY_FILE) { Status = STATUS_NOT_A_DIRECTORY; goto exit; }
ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG); if (!ccb) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto exit; }
RtlZeroMemory(ccb, sizeof(*ccb));
ccb->NodeType = BTRFS_NODE_TYPE_CCB; ccb->NodeSize = sizeof(*ccb); ccb->disposition = RequestedDisposition; ccb->options = RequestedOptions; ccb->access = IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess; ccb->manage_volume_privilege = has_manage_volume_privilege(IrpSp->Parameters.Create.SecurityContext->AccessState, IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode); ccb->reserving = false; ccb->lxss = called_from_lxss();
#ifdef DEBUG_FCB_REFCOUNTS rc = InterlockedIncrement(&Vcb->volume_fcb->refcount); WARN("fcb %p: refcount now %i (volume)\n", Vcb->volume_fcb, rc);#else InterlockedIncrement(&Vcb->volume_fcb->refcount);#endif IrpSp->FileObject->FsContext = Vcb->volume_fcb; IrpSp->FileObject->FsContext2 = ccb;
IrpSp->FileObject->SectionObjectPointer = &Vcb->volume_fcb->nonpaged->segment_object;
if (!IrpSp->FileObject->Vpb) IrpSp->FileObject->Vpb = DeviceObject->Vpb;
InterlockedIncrement(&Vcb->open_files);
Irp->IoStatus.Information = FILE_OPENED; Status = STATUS_SUCCESS; } else { LIST_ENTRY rollback; bool skip_lock;
InitializeListHead(&rollback);
TRACE("file name: %.*S\n", (int)(IrpSp->FileObject->FileName.Length / sizeof(WCHAR)), IrpSp->FileObject->FileName.Buffer);
if (IrpSp->FileObject->RelatedFileObject) TRACE("related file = %p\n", IrpSp->FileObject->RelatedFileObject);
// Don't lock again if we're being called from within CcCopyRead etc. skip_lock = ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock);
if (!skip_lock) ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
ExAcquireResourceSharedLite(&Vcb->fileref_lock, true);
Status = open_file(DeviceObject, Vcb, Irp, &rollback, &opctx);
if (!NT_SUCCESS(Status)) do_rollback(Vcb, &rollback); else clear_rollback(&rollback);
ExReleaseResourceLite(&Vcb->fileref_lock);
if (!skip_lock) ExReleaseResourceLite(&Vcb->tree_lock); }
exit: if (Status != STATUS_PENDING) { Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT); }
if (locked) ExReleaseResourceLite(&Vcb->load_lock);
if (Status == STATUS_PENDING) { KeWaitForSingleObject(&opctx->event, Executive, KernelMode, false, NULL); Status = opctx->Status; ExFreePool(opctx); }
TRACE("create returning %08lx\n", Status);
if (top_level) IoSetTopLevelIrp(NULL);
FsRtlExitFileSystem();
return Status;}