Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Steve French | 9527 | 30.26% | 173 | 31.12% |
Pavel Shilovsky | 9369 | 29.76% | 89 | 16.01% |
Ronnie Sahlberg | 3168 | 10.06% | 64 | 11.51% |
Shyam Prasad N | 2147 | 6.82% | 38 | 6.83% |
Aurelien Aptel | 1735 | 5.51% | 21 | 3.78% |
Sachin S. Prabhu | 1321 | 4.20% | 7 | 1.26% |
David Howells | 886 | 2.81% | 18 | 3.24% |
Paulo Alcantara | 742 | 2.36% | 24 | 4.32% |
Long Li | 553 | 1.76% | 8 | 1.44% |
Shirish Pargaonkar | 295 | 0.94% | 8 | 1.44% |
Stefan Metzmacher | 283 | 0.90% | 7 | 1.26% |
Jeff Layton | 239 | 0.76% | 21 | 3.78% |
Enzo Matsumiya | 206 | 0.65% | 10 | 1.80% |
Joe Perches | 156 | 0.50% | 2 | 0.36% |
Volker Lendecke | 151 | 0.48% | 4 | 0.72% |
Ross Lagerwall | 145 | 0.46% | 1 | 0.18% |
Colin Ian King | 63 | 0.20% | 4 | 0.72% |
Dan Carpenter | 55 | 0.17% | 2 | 0.36% |
Justin Maggard | 44 | 0.14% | 1 | 0.18% |
David Disseldorp | 39 | 0.12% | 3 | 0.54% |
Samuel Cabrero | 30 | 0.10% | 1 | 0.18% |
Kees Cook | 29 | 0.09% | 2 | 0.36% |
Germano Percossi | 28 | 0.09% | 1 | 0.18% |
Namjae Jeon | 28 | 0.09% | 3 | 0.54% |
Yue haibing | 23 | 0.07% | 4 | 0.72% |
Xiong Zhou | 23 | 0.07% | 1 | 0.18% |
Stefano Brivio | 20 | 0.06% | 2 | 0.36% |
ZhangXiaoxu | 19 | 0.06% | 3 | 0.54% |
David Alan Gilbert | 16 | 0.05% | 1 | 0.18% |
Eugene Korenevsky | 15 | 0.05% | 1 | 0.18% |
Christophe Jaillet | 13 | 0.04% | 1 | 0.18% |
Winston Wen | 13 | 0.04% | 2 | 0.36% |
Xiaomeng Tong | 12 | 0.04% | 1 | 0.18% |
Christos Gkekas | 9 | 0.03% | 1 | 0.18% |
Boris Protopopov | 9 | 0.03% | 2 | 0.36% |
Igor Mammedov | 8 | 0.03% | 1 | 0.18% |
ChenXiaoSong | 7 | 0.02% | 4 | 0.72% |
Jan-Marek Glogowski | 7 | 0.02% | 1 | 0.18% |
Dmitry Antipov | 6 | 0.02% | 1 | 0.18% |
Garry McNulty | 6 | 0.02% | 1 | 0.18% |
Zhao Hongjiang | 5 | 0.02% | 1 | 0.18% |
Al Viro | 4 | 0.01% | 1 | 0.18% |
Miaohe Lin | 4 | 0.01% | 1 | 0.18% |
Vincent Whitchurch | 3 | 0.01% | 1 | 0.18% |
Andrew Lunn | 3 | 0.01% | 1 | 0.18% |
Andrew Morton | 3 | 0.01% | 1 | 0.18% |
Linus Torvalds (pre-git) | 3 | 0.01% | 2 | 0.36% |
Suresh Jayaraman | 2 | 0.01% | 1 | 0.18% |
Shen Lichuan | 2 | 0.01% | 1 | 0.18% |
Gustavo A. R. Silva | 2 | 0.01% | 1 | 0.18% |
Jerome Marchand | 1 | 0.00% | 1 | 0.18% |
Ritvik Budhiraja | 1 | 0.00% | 1 | 0.18% |
Daniel N Pettersson | 1 | 0.00% | 1 | 0.18% |
zhengbin | 1 | 0.00% | 1 | 0.18% |
Wolfram Sang | 1 | 0.00% | 1 | 0.18% |
Fabian Frederick | 1 | 0.00% | 1 | 0.18% |
Total | 31482 | 556 |
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248
// SPDX-License-Identifier: LGPL-2.1 /* * * Copyright (C) International Business Machines Corp., 2009, 2013 * Etersoft, 2012 * Author(s): Steve French (sfrench@us.ibm.com) * Pavel Shilovsky (pshilovsky@samba.org) 2012 * * Contains the routines for constructing the SMB2 PDUs themselves * */ /* SMB2 PDU handling routines here - except for leftovers (eg session setup) */ /* Note that there are handle based routines which must be */ /* treated slightly differently for reconnection purposes since we never */ /* want to reuse a stale file handle and only the caller knows the file info */ #include <linux/fs.h> #include <linux/kernel.h> #include <linux/vfs.h> #include <linux/task_io_accounting_ops.h> #include <linux/uaccess.h> #include <linux/uuid.h> #include <linux/pagemap.h> #include <linux/xattr.h> #include <linux/netfs.h> #include <trace/events/netfs.h> #include "cifsglob.h" #include "cifsacl.h" #include "cifsproto.h" #include "smb2proto.h" #include "cifs_unicode.h" #include "cifs_debug.h" #include "ntlmssp.h" #include "../common/smb2status.h" #include "smb2glob.h" #include "cifspdu.h" #include "cifs_spnego.h" #include "smbdirect.h" #include "trace.h" #ifdef CONFIG_CIFS_DFS_UPCALL #include "dfs_cache.h" #endif #include "cached_dir.h" #include "compress.h" /* * The following table defines the expected "StructureSize" of SMB2 requests * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. * * Note that commands are defined in smb2pdu.h in le16 but the array below is * indexed by command in host byte order. */ static const int smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { /* SMB2_NEGOTIATE */ 36, /* SMB2_SESSION_SETUP */ 25, /* SMB2_LOGOFF */ 4, /* SMB2_TREE_CONNECT */ 9, /* SMB2_TREE_DISCONNECT */ 4, /* SMB2_CREATE */ 57, /* SMB2_CLOSE */ 24, /* SMB2_FLUSH */ 24, /* SMB2_READ */ 49, /* SMB2_WRITE */ 49, /* SMB2_LOCK */ 48, /* SMB2_IOCTL */ 57, /* SMB2_CANCEL */ 4, /* SMB2_ECHO */ 4, /* SMB2_QUERY_DIRECTORY */ 33, /* SMB2_CHANGE_NOTIFY */ 32, /* SMB2_QUERY_INFO */ 41, /* SMB2_SET_INFO */ 33, /* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */ }; int smb3_encryption_required(const struct cifs_tcon *tcon) { if (!tcon || !tcon->ses) return 0; if ((tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) || (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA)) return 1; if (tcon->seal && (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) return 1; if (((global_secflags & CIFSSEC_MUST_SEAL) == CIFSSEC_MUST_SEAL) && (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) return 1; return 0; } static void smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd, const struct cifs_tcon *tcon, struct TCP_Server_Info *server) { struct smb3_hdr_req *smb3_hdr; shdr->ProtocolId = SMB2_PROTO_NUMBER; shdr->StructureSize = cpu_to_le16(64); shdr->Command = smb2_cmd; if (server) { /* After reconnect SMB3 must set ChannelSequence on subsequent reqs */ if (server->dialect >= SMB30_PROT_ID) { smb3_hdr = (struct smb3_hdr_req *)shdr; /* * if primary channel is not set yet, use default * channel for chan sequence num */ if (SERVER_IS_CHAN(server)) smb3_hdr->ChannelSequence = cpu_to_le16(server->primary_server->channel_sequence_num); else smb3_hdr->ChannelSequence = cpu_to_le16(server->channel_sequence_num); } spin_lock(&server->req_lock); /* Request up to 10 credits but don't go over the limit. */ if (server->credits >= server->max_credits) shdr->CreditRequest = cpu_to_le16(0); else shdr->CreditRequest = cpu_to_le16( min_t(int, server->max_credits - server->credits, 10)); spin_unlock(&server->req_lock); } else { shdr->CreditRequest = cpu_to_le16(2); } shdr->Id.SyncId.ProcessId = cpu_to_le32((__u16)current->tgid); if (!tcon) goto out; /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */ /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */ if (server && (server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) shdr->CreditCharge = cpu_to_le16(1); /* else CreditCharge MBZ */ shdr->Id.SyncId.TreeId = cpu_to_le32(tcon->tid); /* Uid is not converted */ if (tcon->ses) shdr->SessionId = cpu_to_le64(tcon->ses->Suid); /* * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have * to pass the path on the Open SMB prefixed by \\server\share. * Not sure when we would need to do the augmented path (if ever) and * setting this flag breaks the SMB2 open operation since it is * illegal to send an empty path name (without \\server\share prefix) * when the DFS flag is set in the SMB open header. We could * consider setting the flag on all operations other than open * but it is safer to net set it for now. */ /* if (tcon->share_flags & SHI1005_FLAGS_DFS) shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ if (server && server->sign && !smb3_encryption_required(tcon)) shdr->Flags |= SMB2_FLAGS_SIGNED; out: return; } /* helper function for code reuse */ static int cifs_chan_skip_or_disable(struct cifs_ses *ses, struct TCP_Server_Info *server, bool from_reconnect) { struct TCP_Server_Info *pserver; unsigned int chan_index; if (SERVER_IS_CHAN(server)) { cifs_dbg(VFS, "server %s does not support multichannel anymore. Skip secondary channel\n", ses->server->hostname); spin_lock(&ses->chan_lock); chan_index = cifs_ses_get_chan_index(ses, server); if (chan_index == CIFS_INVAL_CHAN_INDEX) { spin_unlock(&ses->chan_lock); goto skip_terminate; } ses->chans[chan_index].server = NULL; server->terminate = true; spin_unlock(&ses->chan_lock); /* * the above reference of server by channel * needs to be dropped without holding chan_lock * as cifs_put_tcp_session takes a higher lock * i.e. cifs_tcp_ses_lock */ cifs_put_tcp_session(server, from_reconnect); cifs_signal_cifsd_for_reconnect(server, false); /* mark primary server as needing reconnect */ pserver = server->primary_server; cifs_signal_cifsd_for_reconnect(pserver, false); skip_terminate: return -EHOSTDOWN; } cifs_server_dbg(VFS, "server does not support multichannel anymore. Disable all other channels\n"); cifs_disable_secondary_channels(ses); return 0; } static int smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, struct TCP_Server_Info *server, bool from_reconnect) { int rc = 0; struct nls_table *nls_codepage = NULL; struct cifs_ses *ses; int xid; /* * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so * check for tcp and smb session status done differently * for those three - in the calling routine. */ if (tcon == NULL) return 0; /* * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in * cifs_tree_connect(). */ if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) return 0; spin_lock(&tcon->tc_lock); if (tcon->status == TID_EXITING) { /* * only tree disconnect allowed when disconnecting ... */ if (smb2_command != SMB2_TREE_DISCONNECT) { spin_unlock(&tcon->tc_lock); cifs_dbg(FYI, "can not send cmd %d while umounting\n", smb2_command); return -ENODEV; } } spin_unlock(&tcon->tc_lock); ses = tcon->ses; if (!ses) return -EIO; spin_lock(&ses->ses_lock); if (ses->ses_status == SES_EXITING) { spin_unlock(&ses->ses_lock); return -EIO; } spin_unlock(&ses->ses_lock); if (!ses->server || !server) return -EIO; spin_lock(&server->srv_lock); if (server->tcpStatus == CifsNeedReconnect) { /* * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE * here since they are implicitly done when session drops. */ switch (smb2_command) { /* * BB Should we keep oplock break and add flush to exceptions? */ case SMB2_TREE_DISCONNECT: case SMB2_CANCEL: case SMB2_CLOSE: case SMB2_OPLOCK_BREAK: spin_unlock(&server->srv_lock); return -EAGAIN; } } /* if server is marked for termination, cifsd will cleanup */ if (server->terminate) { spin_unlock(&server->srv_lock); return -EHOSTDOWN; } spin_unlock(&server->srv_lock); again: rc = cifs_wait_for_server_reconnect(server, tcon->retry); if (rc) return rc; spin_lock(&ses->chan_lock); if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) { spin_unlock(&ses->chan_lock); return 0; } spin_unlock(&ses->chan_lock); cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d", tcon->ses->chans_need_reconnect, tcon->need_reconnect); mutex_lock(&ses->session_mutex); /* * if this is called by delayed work, and the channel has been disabled * in parallel, the delayed work can continue to execute in parallel * there's a chance that this channel may not exist anymore */ spin_lock(&server->srv_lock); if (server->tcpStatus == CifsExiting) { spin_unlock(&server->srv_lock); mutex_unlock(&ses->session_mutex); rc = -EHOSTDOWN; goto out; } /* * Recheck after acquire mutex. If another thread is negotiating * and the server never sends an answer the socket will be closed * and tcpStatus set to reconnect. */ if (server->tcpStatus == CifsNeedReconnect) { spin_unlock(&server->srv_lock); mutex_unlock(&ses->session_mutex); if (tcon->retry) goto again; rc = -EHOSTDOWN; goto out; } spin_unlock(&server->srv_lock); nls_codepage = ses->local_nls; /* * need to prevent multiple threads trying to simultaneously * reconnect the same SMB session */ spin_lock(&ses->ses_lock); spin_lock(&ses->chan_lock); if (!cifs_chan_needs_reconnect(ses, server) && ses->ses_status == SES_GOOD) { spin_unlock(&ses->chan_lock); spin_unlock(&ses->ses_lock); /* this means that we only need to tree connect */ if (tcon->need_reconnect) goto skip_sess_setup; mutex_unlock(&ses->session_mutex); goto out; } spin_unlock(&ses->chan_lock); spin_unlock(&ses->ses_lock); rc = cifs_negotiate_protocol(0, ses, server); if (!rc) { /* * if server stopped supporting multichannel * and the first channel reconnected, disable all the others. */ if (ses->chan_count > 1 && !(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { rc = cifs_chan_skip_or_disable(ses, server, from_reconnect); if (rc) { mutex_unlock(&ses->session_mutex); goto out; } } rc = cifs_setup_session(0, ses, server, nls_codepage); if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) { /* * Try alternate password for next reconnect (key rotation * could be enabled on the server e.g.) if an alternate * password is available and the current password is expired, * but do not swap on non pwd related errors like host down */ if (ses->password2) swap(ses->password2, ses->password); } if ((rc == -EACCES) && !tcon->retry) { mutex_unlock(&ses->session_mutex); rc = -EHOSTDOWN; goto failed; } else if (rc) { mutex_unlock(&ses->session_mutex); goto out; } } else { mutex_unlock(&ses->session_mutex); goto out; } skip_sess_setup: if (!tcon->need_reconnect) { mutex_unlock(&ses->session_mutex); goto out; } cifs_mark_open_files_invalid(tcon); if (tcon->use_persistent) tcon->need_reopen_files = true; rc = cifs_tree_connect(0, tcon, nls_codepage); cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); if (rc) { /* If sess reconnected but tcon didn't, something strange ... */ mutex_unlock(&ses->session_mutex); cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc); goto out; } spin_lock(&ses->ses_lock); if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) { spin_unlock(&ses->ses_lock); mutex_unlock(&ses->session_mutex); goto skip_add_channels; } ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS; spin_unlock(&ses->ses_lock); if (!rc && (server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL) && server->ops->query_server_interfaces) { mutex_unlock(&ses->session_mutex); /* * query server network interfaces, in case they change */ xid = get_xid(); rc = server->ops->query_server_interfaces(xid, tcon, false); free_xid(xid); if (rc == -EOPNOTSUPP && ses->chan_count > 1) { /* * some servers like Azure SMB server do not advertise * that multichannel has been disabled with server * capabilities, rather return STATUS_NOT_IMPLEMENTED. * treat this as server not supporting multichannel */ rc = cifs_chan_skip_or_disable(ses, server, from_reconnect); goto skip_add_channels; } else if (rc) cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n", __func__, rc); if (ses->chan_max > ses->chan_count && ses->iface_count && !SERVER_IS_CHAN(server)) { if (ses->chan_count == 1) { cifs_server_dbg(VFS, "supports multichannel now\n"); queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, (SMB_INTERFACE_POLL_INTERVAL * HZ)); } cifs_try_adding_channels(ses); } } else { mutex_unlock(&ses->session_mutex); } skip_add_channels: spin_lock(&ses->ses_lock); ses->flags &= ~CIFS_SES_FLAG_SCALE_CHANNELS; spin_unlock(&ses->ses_lock); if (smb2_command != SMB2_INTERNAL_CMD) mod_delayed_work(cifsiod_wq, &server->reconnect, 0); atomic_inc(&tconInfoReconnectCount); out: /* * Check if handle based operation so we know whether we can continue * or not without returning to caller to reset file handle. */ /* * BB Is flush done by server on drop of tcp session? Should we special * case it and skip above? */ switch (smb2_command) { case SMB2_FLUSH: case SMB2_READ: case SMB2_WRITE: case SMB2_LOCK: case SMB2_QUERY_DIRECTORY: case SMB2_CHANGE_NOTIFY: case SMB2_QUERY_INFO: case SMB2_SET_INFO: rc = -EAGAIN; } failed: return rc; } static void fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, struct TCP_Server_Info *server, void *buf, unsigned int *total_len) { struct smb2_pdu *spdu = buf; /* lookup word count ie StructureSize from table */ __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)]; /* * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of * largest operations (Create) */ memset(buf, 0, 256); smb2_hdr_assemble(&spdu->hdr, smb2_command, tcon, server); spdu->StructureSize2 = cpu_to_le16(parmsize); *total_len = parmsize + sizeof(struct smb2_hdr); } /* * Allocate and return pointer to an SMB request hdr, and set basic * SMB information in the SMB header. If the return code is zero, this * function must have filled in request_buf pointer. */ static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, struct TCP_Server_Info *server, void **request_buf, unsigned int *total_len) { /* BB eventually switch this to SMB2 specific small buf size */ switch (smb2_command) { case SMB2_SET_INFO: case SMB2_QUERY_INFO: *request_buf = cifs_buf_get(); break; default: *request_buf = cifs_small_buf_get(); break; } if (*request_buf == NULL) { /* BB should we add a retry in here if not a writepage? */ return -ENOMEM; } fill_small_buf(smb2_command, tcon, server, (struct smb2_hdr *)(*request_buf), total_len); if (tcon != NULL) { uint16_t com_code = le16_to_cpu(smb2_command); cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); cifs_stats_inc(&tcon->num_smbs_sent); } return 0; } static int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, struct TCP_Server_Info *server, void **request_buf, unsigned int *total_len) { int rc; rc = smb2_reconnect(smb2_command, tcon, server, false); if (rc) return rc; return __smb2_plain_req_init(smb2_command, tcon, server, request_buf, total_len); } static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon, struct TCP_Server_Info *server, void **request_buf, unsigned int *total_len) { /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */ if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) { return __smb2_plain_req_init(SMB2_IOCTL, tcon, server, request_buf, total_len); } return smb2_plain_req_init(SMB2_IOCTL, tcon, server, request_buf, total_len); } /* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */ static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt) { pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; pneg_ctxt->DataLength = cpu_to_le16(38); pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512; } static void build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt) { pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; pneg_ctxt->DataLength = cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) - sizeof(struct smb2_neg_context)); pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(3); pneg_ctxt->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77; pneg_ctxt->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF; pneg_ctxt->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1; } static unsigned int build_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt) { unsigned int ctxt_len = sizeof(struct smb2_signing_capabilities); unsigned short num_algs = 1; /* number of signing algorithms sent */ pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; /* * Context Data length must be rounded to multiple of 8 for some servers */ pneg_ctxt->DataLength = cpu_to_le16(ALIGN(sizeof(struct smb2_signing_capabilities) - sizeof(struct smb2_neg_context) + (num_algs * sizeof(u16)), 8)); pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(num_algs); pneg_ctxt->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC); ctxt_len += sizeof(__le16) * num_algs; ctxt_len = ALIGN(ctxt_len, 8); return ctxt_len; /* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */ } static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt) { pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; if (require_gcm_256) { pneg_ctxt->DataLength = cpu_to_le16(4); /* Cipher Count + 1 cipher */ pneg_ctxt->CipherCount = cpu_to_le16(1); pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES256_GCM; } else if (enable_gcm_256) { pneg_ctxt->DataLength = cpu_to_le16(8); /* Cipher Count + 3 ciphers */ pneg_ctxt->CipherCount = cpu_to_le16(3); pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES256_GCM; pneg_ctxt->Ciphers[2] = SMB2_ENCRYPTION_AES128_CCM; } else { pneg_ctxt->DataLength = cpu_to_le16(6); /* Cipher Count + 2 ciphers */ pneg_ctxt->CipherCount = cpu_to_le16(2); pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES128_CCM; } } static unsigned int build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname) { struct nls_table *cp = load_nls_default(); pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID; /* copy up to max of first 100 bytes of server name to NetName field */ pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp)); /* context size is DataLength + minimal smb2_neg_context */ return ALIGN(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context), 8); } static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) { pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ pneg_ctxt->Name[0] = 0x93; pneg_ctxt->Name[1] = 0xAD; pneg_ctxt->Name[2] = 0x25; pneg_ctxt->Name[3] = 0x50; pneg_ctxt->Name[4] = 0x9C; pneg_ctxt->Name[5] = 0xB4; pneg_ctxt->Name[6] = 0x11; pneg_ctxt->Name[7] = 0xE7; pneg_ctxt->Name[8] = 0xB4; pneg_ctxt->Name[9] = 0x23; pneg_ctxt->Name[10] = 0x83; pneg_ctxt->Name[11] = 0xDE; pneg_ctxt->Name[12] = 0x96; pneg_ctxt->Name[13] = 0x8B; pneg_ctxt->Name[14] = 0xCD; pneg_ctxt->Name[15] = 0x7C; } static void assemble_neg_contexts(struct smb2_negotiate_req *req, struct TCP_Server_Info *server, unsigned int *total_len) { unsigned int ctxt_len, neg_context_count; struct TCP_Server_Info *pserver; char *pneg_ctxt; char *hostname; if (*total_len > 200) { /* In case length corrupted don't want to overrun smb buffer */ cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n"); return; } /* * round up total_len of fixed part of SMB3 negotiate request to 8 * byte boundary before adding negotiate contexts */ *total_len = ALIGN(*total_len, 8); pneg_ctxt = (*total_len) + (char *)req; req->NegotiateContextOffset = cpu_to_le32(*total_len); build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt); ctxt_len = ALIGN(sizeof(struct smb2_preauth_neg_context), 8); *total_len += ctxt_len; pneg_ctxt += ctxt_len; build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt); ctxt_len = ALIGN(sizeof(struct smb2_encryption_neg_context), 8); *total_len += ctxt_len; pneg_ctxt += ctxt_len; /* * secondary channels don't have the hostname field populated * use the hostname field in the primary channel instead */ pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; cifs_server_lock(pserver); hostname = pserver->hostname; if (hostname && (hostname[0] != 0)) { ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt, hostname); *total_len += ctxt_len; pneg_ctxt += ctxt_len; neg_context_count = 3; } else neg_context_count = 2; cifs_server_unlock(pserver); build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); *total_len += sizeof(struct smb2_posix_neg_context); pneg_ctxt += sizeof(struct smb2_posix_neg_context); neg_context_count++; if (server->compression.requested) { build_compression_ctxt((struct smb2_compression_capabilities_context *) pneg_ctxt); ctxt_len = ALIGN(sizeof(struct smb2_compression_capabilities_context), 8); *total_len += ctxt_len; pneg_ctxt += ctxt_len; neg_context_count++; } if (enable_negotiate_signing) { ctxt_len = build_signing_ctxt((struct smb2_signing_capabilities *) pneg_ctxt); *total_len += ctxt_len; pneg_ctxt += ctxt_len; neg_context_count++; } /* check for and add transport_capabilities and signing capabilities */ req->NegotiateContextCount = cpu_to_le16(neg_context_count); } /* If invalid preauth context warn but use what we requested, SHA-512 */ static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt) { unsigned int len = le16_to_cpu(ctxt->DataLength); /* * Caller checked that DataLength remains within SMB boundary. We still * need to confirm that one HashAlgorithms member is accounted for. */ if (len < MIN_PREAUTH_CTXT_DATA_LEN) { pr_warn_once("server sent bad preauth context\n"); return; } else if (len < MIN_PREAUTH_CTXT_DATA_LEN + le16_to_cpu(ctxt->SaltLength)) { pr_warn_once("server sent invalid SaltLength\n"); return; } if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1) pr_warn_once("Invalid SMB3 hash algorithm count\n"); if (ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) pr_warn_once("unknown SMB3 hash algorithm\n"); } static void decode_compress_ctx(struct TCP_Server_Info *server, struct smb2_compression_capabilities_context *ctxt) { unsigned int len = le16_to_cpu(ctxt->DataLength); __le16 alg; server->compression.enabled = false; /* * Caller checked that DataLength remains within SMB boundary. We still * need to confirm that one CompressionAlgorithms member is accounted * for. */ if (len < 10) { pr_warn_once("server sent bad compression cntxt\n"); return; } if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) { pr_warn_once("invalid SMB3 compress algorithm count\n"); return; } alg = ctxt->CompressionAlgorithms[0]; /* 'NONE' (0) compressor type is never negotiated */ if (alg == 0 || le16_to_cpu(alg) > 3) { pr_warn_once("invalid compression algorithm '%u'\n", alg); return; } server->compression.alg = alg; server->compression.enabled = true; } static int decode_encrypt_ctx(struct TCP_Server_Info *server, struct smb2_encryption_neg_context *ctxt) { unsigned int len = le16_to_cpu(ctxt->DataLength); cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len); /* * Caller checked that DataLength remains within SMB boundary. We still * need to confirm that one Cipher flexible array member is accounted * for. */ if (len < MIN_ENCRYPT_CTXT_DATA_LEN) { pr_warn_once("server sent bad crypto ctxt len\n"); return -EINVAL; } if (le16_to_cpu(ctxt->CipherCount) != 1) { pr_warn_once("Invalid SMB3.11 cipher count\n"); return -EINVAL; } cifs_dbg(FYI, "SMB311 cipher type:%d\n", le16_to_cpu(ctxt->Ciphers[0])); if (require_gcm_256) { if (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM) { cifs_dbg(VFS, "Server does not support requested encryption type (AES256 GCM)\n"); return -EOPNOTSUPP; } } else if (ctxt->Ciphers[0] == 0) { /* * e.g. if server only supported AES256_CCM (very unlikely) * or server supported no encryption types or had all disabled. * Since GLOBAL_CAP_ENCRYPTION will be not set, in the case * in which mount requested encryption ("seal") checks later * on during tree connection will return proper rc, but if * seal not requested by client, since server is allowed to * return 0 to indicate no supported cipher, we can't fail here */ server->cipher_type = 0; server->capabilities &= ~SMB2_GLOBAL_CAP_ENCRYPTION; pr_warn_once("Server does not support requested encryption types\n"); return 0; } else if ((ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_CCM) && (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_GCM) && (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM)) { /* server returned a cipher we didn't ask for */ pr_warn_once("Invalid SMB3.11 cipher returned\n"); return -EINVAL; } server->cipher_type = ctxt->Ciphers[0]; server->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; return 0; } static void decode_signing_ctx(struct TCP_Server_Info *server, struct smb2_signing_capabilities *pctxt) { unsigned int len = le16_to_cpu(pctxt->DataLength); /* * Caller checked that DataLength remains within SMB boundary. We still * need to confirm that one SigningAlgorithms flexible array member is * accounted for. */ if ((len < 4) || (len > 16)) { pr_warn_once("server sent bad signing negcontext\n"); return; } if (le16_to_cpu(pctxt->SigningAlgorithmCount) != 1) { pr_warn_once("Invalid signing algorithm count\n"); return; } if (le16_to_cpu(pctxt->SigningAlgorithms[0]) > 2) { pr_warn_once("unknown signing algorithm\n"); return; } server->signing_negotiated = true; server->signing_algorithm = le16_to_cpu(pctxt->SigningAlgorithms[0]); cifs_dbg(FYI, "signing algorithm %d chosen\n", server->signing_algorithm); } static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp, struct TCP_Server_Info *server, unsigned int len_of_smb) { struct smb2_neg_context *pctx; unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount); unsigned int len_of_ctxts, i; int rc = 0; cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt); if (len_of_smb <= offset) { cifs_server_dbg(VFS, "Invalid response: negotiate context offset\n"); return -EINVAL; } len_of_ctxts = len_of_smb - offset; for (i = 0; i < ctxt_cnt; i++) { int clen; /* check that offset is not beyond end of SMB */ if (len_of_ctxts < sizeof(struct smb2_neg_context)) break; pctx = (struct smb2_neg_context *)(offset + (char *)rsp); clen = sizeof(struct smb2_neg_context) + le16_to_cpu(pctx->DataLength); /* * 2.2.4 SMB2 NEGOTIATE Response * Subsequent negotiate contexts MUST appear at the first 8-byte * aligned offset following the previous negotiate context. */ if (i + 1 != ctxt_cnt) clen = ALIGN(clen, 8); if (clen > len_of_ctxts) break; if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) decode_preauth_context( (struct smb2_preauth_neg_context *)pctx); else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) rc = decode_encrypt_ctx(server, (struct smb2_encryption_neg_context *)pctx); else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) decode_compress_ctx(server, (struct smb2_compression_capabilities_context *)pctx); else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) server->posix_ext_supported = true; else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) decode_signing_ctx(server, (struct smb2_signing_capabilities *)pctx); else cifs_server_dbg(VFS, "unknown negcontext of type %d ignored\n", le16_to_cpu(pctx->ContextType)); if (rc) break; offset += clen; len_of_ctxts -= clen; } return rc; } static struct create_posix * create_posix_buf(umode_t mode) { struct create_posix *buf; buf = kzalloc(sizeof(struct create_posix), GFP_KERNEL); if (!buf) return NULL; buf->ccontext.DataOffset = cpu_to_le16(offsetof(struct create_posix, Mode)); buf->ccontext.DataLength = cpu_to_le32(4); buf->ccontext.NameOffset = cpu_to_le16(offsetof(struct create_posix, Name)); buf->ccontext.NameLength = cpu_to_le16(16); /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ buf->Name[0] = 0x93; buf->Name[1] = 0xAD; buf->Name[2] = 0x25; buf->Name[3] = 0x50; buf->Name[4] = 0x9C; buf->Name[5] = 0xB4; buf->Name[6] = 0x11; buf->Name[7] = 0xE7; buf->Name[8] = 0xB4; buf->Name[9] = 0x23; buf->Name[10] = 0x83; buf->Name[11] = 0xDE; buf->Name[12] = 0x96; buf->Name[13] = 0x8B; buf->Name[14] = 0xCD; buf->Name[15] = 0x7C; buf->Mode = cpu_to_le32(mode); cifs_dbg(FYI, "mode on posix create 0%o\n", mode); return buf; } static int add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) { unsigned int num = *num_iovec; iov[num].iov_base = create_posix_buf(mode); if (mode == ACL_NO_MODE) cifs_dbg(FYI, "%s: no mode\n", __func__); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = sizeof(struct create_posix); *num_iovec = num + 1; return 0; } /* * * SMB2 Worker functions follow: * * The general structure of the worker functions is: * 1) Call smb2_init (assembles SMB2 header) * 2) Initialize SMB2 command specific fields in fixed length area of SMB * 3) Call smb_sendrcv2 (sends request on socket and waits for response) * 4) Decode SMB2 command specific fields in the fixed length area * 5) Decode variable length data area (if any for this SMB2 command type) * 6) Call free smb buffer * 7) return * */ int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses, struct TCP_Server_Info *server) { struct smb_rqst rqst; struct smb2_negotiate_req *req; struct smb2_negotiate_rsp *rsp; struct kvec iov[1]; struct kvec rsp_iov; int rc; int resp_buftype; int blob_offset, blob_length; char *security_blob; int flags = CIFS_NEG_OP; unsigned int total_len; cifs_dbg(FYI, "Negotiate protocol\n"); if (!server) { WARN(1, "%s: server is NULL!\n", __func__); return -EIO; } rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, server, (void **) &req, &total_len); if (rc) return rc; req->hdr.SessionId = 0; memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); if (strcmp(server->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); req->Dialects[2] = cpu_to_le16(SMB311_PROT_ID); req->DialectCount = cpu_to_le16(3); total_len += 6; } else if (strcmp(server->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0) { req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); req->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); req->DialectCount = cpu_to_le16(4); total_len += 8; } else { /* otherwise send specific dialect */ req->Dialects[0] = cpu_to_le16(server->vals->protocol_id); req->DialectCount = cpu_to_le16(1); total_len += 2; } /* only one of SMB2 signing flags may be set in SMB2 request */ if (ses->sign) req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); else if (global_secflags & CIFSSEC_MAY_SIGN) req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); else req->SecurityMode = 0; req->Capabilities = cpu_to_le32(server->vals->req_capabilities); if (ses->chan_max > 1) req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); /* ClientGUID must be zero for SMB2.02 dialect */ if (server->vals->protocol_id == SMB20_PROT_ID) memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); else { memcpy(req->ClientGUID, server->client_guid, SMB2_CLIENT_GUID_SIZE); if ((server->vals->protocol_id == SMB311_PROT_ID) || (strcmp(server->vals->version_string, SMB3ANY_VERSION_STRING) == 0) || (strcmp(server->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0)) assemble_neg_contexts(req, server, &total_len); } iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = iov; rqst.rq_nvec = 1; rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base; /* * No tcon so can't do * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); */ if (rc == -EOPNOTSUPP) { cifs_server_dbg(VFS, "Dialect not supported by server. Consider specifying vers=1.0 or vers=2.0 on mount for accessing older servers\n"); goto neg_exit; } else if (rc != 0) goto neg_exit; rc = -EIO; if (strcmp(server->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { cifs_server_dbg(VFS, "SMB2 dialect returned but not requested\n"); goto neg_exit; } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { cifs_server_dbg(VFS, "SMB2.1 dialect returned but not requested\n"); goto neg_exit; } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { /* ops set to 3.0 by default for default so update */ server->ops = &smb311_operations; server->vals = &smb311_values; } } else if (strcmp(server->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0) { if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { cifs_server_dbg(VFS, "SMB2 dialect returned but not requested\n"); goto neg_exit; } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { /* ops set to 3.0 by default for default so update */ server->ops = &smb21_operations; server->vals = &smb21_values; } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { server->ops = &smb311_operations; server->vals = &smb311_values; } } else if (le16_to_cpu(rsp->DialectRevision) != server->vals->protocol_id) { /* if requested single dialect ensure returned dialect matched */ cifs_server_dbg(VFS, "Invalid 0x%x dialect returned: not requested\n", le16_to_cpu(rsp->DialectRevision)); goto neg_exit; } cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode); if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) cifs_dbg(FYI, "negotiated smb2.0 dialect\n"); else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) cifs_dbg(FYI, "negotiated smb2.1 dialect\n"); else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID)) cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); else { cifs_server_dbg(VFS, "Invalid dialect returned by server 0x%x\n", le16_to_cpu(rsp->DialectRevision)); goto neg_exit; } rc = 0; server->dialect = le16_to_cpu(rsp->DialectRevision); /* * Keep a copy of the hash after negprot. This hash will be * the starting hash value for all sessions made from this * server. */ memcpy(server->preauth_sha_hash, ses->preauth_sha_hash, SMB2_PREAUTH_HASH_SIZE); /* SMB2 only has an extended negflavor */ server->negflavor = CIFS_NEGFLAVOR_EXTENDED; /* set it to the maximum buffer size value we can send with 1 credit */ server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize), SMB2_MAX_BUFFER_SIZE); server->max_read = le32_to_cpu(rsp->MaxReadSize); server->max_write = le32_to_cpu(rsp->MaxWriteSize); server->sec_mode = le16_to_cpu(rsp->SecurityMode); if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode) cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n", server->sec_mode); server->capabilities = le32_to_cpu(rsp->Capabilities); /* Internal types */ server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES; /* * SMB3.0 supports only 1 cipher and doesn't have a encryption neg context * Set the cipher type manually. */ if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) server->cipher_type = SMB2_ENCRYPTION_AES128_CCM; security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, (struct smb2_hdr *)rsp); /* * See MS-SMB2 section 2.2.4: if no blob, client picks default which * for us will be * ses->sectype = RawNTLMSSP; * but for time being this is our only auth choice so doesn't matter. * We just found a server which sets blob length to zero expecting raw. */ if (blob_length == 0) { cifs_dbg(FYI, "missing security blob on negprot\n"); server->sec_ntlmssp = true; } rc = cifs_enable_signing(server, ses->sign); if (rc) goto neg_exit; if (blob_length) { rc = decode_negTokenInit(security_blob, blob_length, server); if (rc == 1) rc = 0; else if (rc == 0) rc = -EIO; } if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { if (rsp->NegotiateContextCount) rc = smb311_decode_neg_context(rsp, server, rsp_iov.iov_len); else cifs_server_dbg(VFS, "Missing expected negotiate contexts\n"); } if (server->cipher_type && !rc) { if (!SERVER_IS_CHAN(server)) { rc = smb3_crypto_aead_allocate(server); } else { /* For channels, just reuse the primary server crypto secmech. */ server->secmech.enc = server->primary_server->secmech.enc; server->secmech.dec = server->primary_server->secmech.dec; } } neg_exit: free_rsp_buf(resp_buftype, rsp); return rc; } int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) { int rc; struct validate_negotiate_info_req *pneg_inbuf; struct validate_negotiate_info_rsp *pneg_rsp = NULL; u32 rsplen; u32 inbuflen; /* max of 4 dialects */ struct TCP_Server_Info *server = tcon->ses->server; cifs_dbg(FYI, "validate negotiate\n"); /* In SMB3.11 preauth integrity supersedes validate negotiate */ if (server->dialect == SMB311_PROT_ID) return 0; /* * validation ioctl must be signed, so no point sending this if we * can not sign it (ie are not known user). Even if signing is not * required (enabled but not negotiated), in those cases we selectively * sign just this, the first and only signed request on a connection. * Having validation of negotiate info helps reduce attack vectors. */ if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) return 0; /* validation requires signing */ if (tcon->ses->user_name == NULL) { cifs_dbg(FYI, "Can't validate negotiate: null user mount\n"); return 0; /* validation requires signing */ } if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) cifs_tcon_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS); if (!pneg_inbuf) return -ENOMEM; pneg_inbuf->Capabilities = cpu_to_le32(server->vals->req_capabilities); if (tcon->ses->chan_max > 1) pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); memcpy(pneg_inbuf->Guid, server->client_guid, SMB2_CLIENT_GUID_SIZE); if (tcon->ses->sign) pneg_inbuf->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); else if (global_secflags & CIFSSEC_MAY_SIGN) pneg_inbuf->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); else pneg_inbuf->SecurityMode = 0; if (strcmp(server->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); pneg_inbuf->Dialects[2] = cpu_to_le16(SMB311_PROT_ID); pneg_inbuf->DialectCount = cpu_to_le16(3); /* SMB 2.1 not included so subtract one dialect from len */ inbuflen = sizeof(*pneg_inbuf) - (sizeof(pneg_inbuf->Dialects[0])); } else if (strcmp(server->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0) { pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); pneg_inbuf->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); pneg_inbuf->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); pneg_inbuf->DialectCount = cpu_to_le16(4); /* structure is big enough for 4 dialects */ inbuflen = sizeof(*pneg_inbuf); } else { /* otherwise specific dialect was requested */ pneg_inbuf->Dialects[0] = cpu_to_le16(server->vals->protocol_id); pneg_inbuf->DialectCount = cpu_to_le16(1); /* structure is big enough for 4 dialects, sending only 1 */ inbuflen = sizeof(*pneg_inbuf) - sizeof(pneg_inbuf->Dialects[0]) * 3; } rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, FSCTL_VALIDATE_NEGOTIATE_INFO, (char *)pneg_inbuf, inbuflen, CIFSMaxBufSize, (char **)&pneg_rsp, &rsplen); if (rc == -EOPNOTSUPP) { /* * Old Windows versions or Netapp SMB server can return * not supported error. Client should accept it. */ cifs_tcon_dbg(VFS, "Server does not support validate negotiate\n"); rc = 0; goto out_free_inbuf; } else if (rc != 0) { cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); rc = -EIO; goto out_free_inbuf; } rc = -EIO; if (rsplen != sizeof(*pneg_rsp)) { cifs_tcon_dbg(VFS, "Invalid protocol negotiate response size: %d\n", rsplen); /* relax check since Mac returns max bufsize allowed on ioctl */ if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp)) goto out_free_rsp; } /* check validate negotiate info response matches what we got earlier */ if (pneg_rsp->Dialect != cpu_to_le16(server->dialect)) goto vneg_out; if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode)) goto vneg_out; /* do not validate server guid because not saved at negprot time yet */ if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | SMB2_LARGE_FILES) != server->capabilities) goto vneg_out; /* validate negotiate successful */ rc = 0; cifs_dbg(FYI, "validate negotiate info successful\n"); goto out_free_rsp; vneg_out: cifs_tcon_dbg(VFS, "protocol revalidation - security settings mismatch\n"); out_free_rsp: kfree(pneg_rsp); out_free_inbuf: kfree(pneg_inbuf); return rc; } enum securityEnum smb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) { switch (requested) { case Kerberos: case RawNTLMSSP: return requested; case NTLMv2: return RawNTLMSSP; case Unspecified: if (server->sec_ntlmssp && (global_secflags & CIFSSEC_MAY_NTLMSSP)) return RawNTLMSSP; if ((server->sec_kerberos || server->sec_mskerberos) && (global_secflags & CIFSSEC_MAY_KRB5)) return Kerberos; fallthrough; default: return Unspecified; } } struct SMB2_sess_data { unsigned int xid; struct cifs_ses *ses; struct TCP_Server_Info *server; struct nls_table *nls_cp; void (*func)(struct SMB2_sess_data *); int result; u64 previous_session; /* we will send the SMB in three pieces: * a fixed length beginning part, an optional * SPNEGO blob (which can be zero length), and a * last part which will include the strings * and rest of bcc area. This allows us to avoid * a large buffer 17K allocation */ int buf0_type; struct kvec iov[2]; }; static int SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) { int rc; struct cifs_ses *ses = sess_data->ses; struct TCP_Server_Info *server = sess_data->server; struct smb2_sess_setup_req *req; unsigned int total_len; bool is_binding = false; rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, server, (void **) &req, &total_len); if (rc) return rc; spin_lock(&ses->ses_lock); is_binding = (ses->ses_status == SES_GOOD); spin_unlock(&ses->ses_lock); if (is_binding) { req->hdr.SessionId = cpu_to_le64(ses->Suid); req->hdr.Flags |= SMB2_FLAGS_SIGNED; req->PreviousSessionId = 0; req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; cifs_dbg(FYI, "Binding to sess id: %llx\n", ses->Suid); } else { /* First session, not a reauthenticate */ req->hdr.SessionId = 0; /* * if reconnect, we need to send previous sess id * otherwise it is 0 */ req->PreviousSessionId = cpu_to_le64(sess_data->previous_session); req->Flags = 0; /* MBZ */ cifs_dbg(FYI, "Fresh session. Previous: %llx\n", sess_data->previous_session); } /* enough to enable echos and oplocks and one max size write */ if (server->credits >= server->max_credits) req->hdr.CreditRequest = cpu_to_le16(0); else req->hdr.CreditRequest = cpu_to_le16( min_t(int, server->max_credits - server->credits, 130)); /* only one of SMB2 signing flags may be set in SMB2 request */ if (server->sign) req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */ req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; else req->SecurityMode = 0; #ifdef CONFIG_CIFS_DFS_UPCALL req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS); #else req->Capabilities = 0; #endif /* DFS_UPCALL */ req->Channel = 0; /* MBZ */ sess_data->iov[0].iov_base = (char *)req; /* 1 for pad */ sess_data->iov[0].iov_len = total_len - 1; /* * This variable will be used to clear the buffer * allocated above in case of any error in the calling function. */ sess_data->buf0_type = CIFS_SMALL_BUFFER; return 0; } static void SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data) { struct kvec *iov = sess_data->iov; /* iov[1] is already freed by caller */ if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base) memzero_explicit(iov[0].iov_base, iov[0].iov_len); free_rsp_buf(sess_data->buf0_type, iov[0].iov_base); sess_data->buf0_type = CIFS_NO_BUFFER; } static int SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) { int rc; struct smb_rqst rqst; struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base; struct kvec rsp_iov = { NULL, 0 }; /* Testing shows that buffer offset must be at location of Buffer[0] */ req->SecurityBufferOffset = cpu_to_le16(sizeof(struct smb2_sess_setup_req)); req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len); memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = sess_data->iov; rqst.rq_nvec = 2; /* BB add code to build os and lm fields */ rc = cifs_send_recv(sess_data->xid, sess_data->ses, sess_data->server, &rqst, &sess_data->buf0_type, CIFS_LOG_ERROR | CIFS_SESS_OP, &rsp_iov); cifs_small_buf_release(sess_data->iov[0].iov_base); if (rc == 0) sess_data->ses->expired_pwd = false; else if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) { if (sess_data->ses->expired_pwd == false) trace_smb3_key_expired(sess_data->server->hostname, sess_data->ses->user_name, sess_data->server->conn_id, &sess_data->server->dstaddr, rc); sess_data->ses->expired_pwd = true; } memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); return rc; } static int SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) { int rc = 0; struct cifs_ses *ses = sess_data->ses; struct TCP_Server_Info *server = sess_data->server; cifs_server_lock(server); if (server->ops->generate_signingkey) { rc = server->ops->generate_signingkey(ses, server); if (rc) { cifs_dbg(FYI, "SMB3 session key generation failed\n"); cifs_server_unlock(server); return rc; } } if (!server->session_estab) { server->sequence_number = 0x2; server->session_estab = true; } cifs_server_unlock(server); cifs_dbg(FYI, "SMB2/3 session established successfully\n"); return rc; } #ifdef CONFIG_CIFS_UPCALL static void SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) { int rc; struct cifs_ses *ses = sess_data->ses; struct TCP_Server_Info *server = sess_data->server; struct cifs_spnego_msg *msg; struct key *spnego_key = NULL; struct smb2_sess_setup_rsp *rsp = NULL; bool is_binding = false; rc = SMB2_sess_alloc_buffer(sess_data); if (rc) goto out; spnego_key = cifs_get_spnego_key(ses, server); if (IS_ERR(spnego_key)) { rc = PTR_ERR(spnego_key); if (rc == -ENOKEY) cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n"); spnego_key = NULL; goto out; } msg = spnego_key->payload.data[0]; /* * check version field to make sure that cifs.upcall is * sending us a response in an expected form */ if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { cifs_dbg(VFS, "bad cifs.upcall version. Expected %d got %d\n", CIFS_SPNEGO_UPCALL_VERSION, msg->version); rc = -EKEYREJECTED; goto out_put_spnego_key; } spin_lock(&ses->ses_lock); is_binding = (ses->ses_status == SES_GOOD); spin_unlock(&ses->ses_lock); /* keep session key if binding */ if (!is_binding) { kfree_sensitive(ses->auth_key.response); ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, GFP_KERNEL); if (!ses->auth_key.response) { cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", msg->sesskey_len); rc = -ENOMEM; goto out_put_spnego_key; } ses->auth_key.len = msg->sesskey_len; } sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; sess_data->iov[1].iov_len = msg->secblob_len; rc = SMB2_sess_sendreceive(sess_data); if (rc) goto out_put_spnego_key; rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; /* keep session id and flags if binding */ if (!is_binding) { ses->Suid = le64_to_cpu(rsp->hdr.SessionId); ses->session_flags = le16_to_cpu(rsp->SessionFlags); } rc = SMB2_sess_establish_session(sess_data); out_put_spnego_key: key_invalidate(spnego_key); key_put(spnego_key); if (rc) { kfree_sensitive(ses->auth_key.response); ses->auth_key.response = NULL; ses->auth_key.len = 0; } out: sess_data->result = rc; sess_data->func = NULL; SMB2_sess_free_buffer(sess_data); } #else static void SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) { cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); sess_data->result = -EOPNOTSUPP; sess_data->func = NULL; } #endif static void SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data); static void SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) { int rc; struct cifs_ses *ses = sess_data->ses; struct TCP_Server_Info *server = sess_data->server; struct smb2_sess_setup_rsp *rsp = NULL; unsigned char *ntlmssp_blob = NULL; bool use_spnego = false; /* else use raw ntlmssp */ u16 blob_length = 0; bool is_binding = false; /* * If memory allocation is successful, caller of this function * frees it. */ ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); if (!ses->ntlmssp) { rc = -ENOMEM; goto out_err; } ses->ntlmssp->sesskey_per_smbsess = true; rc = SMB2_sess_alloc_buffer(sess_data); if (rc) goto out_err; rc = build_ntlmssp_smb3_negotiate_blob(&ntlmssp_blob, &blob_length, ses, server, sess_data->nls_cp); if (rc) goto out; if (use_spnego) { /* BB eventually need to add this */ cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); rc = -EOPNOTSUPP; goto out; } sess_data->iov[1].iov_base = ntlmssp_blob; sess_data->iov[1].iov_len = blob_length; rc = SMB2_sess_sendreceive(sess_data); rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; /* If true, rc here is expected and not an error */ if (sess_data->buf0_type != CIFS_NO_BUFFER && rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) rc = 0; if (rc) goto out; if (offsetof(struct smb2_sess_setup_rsp, Buffer) != le16_to_cpu(rsp->SecurityBufferOffset)) { cifs_dbg(VFS, "Invalid security buffer offset %d\n", le16_to_cpu(rsp->SecurityBufferOffset)); rc = -EIO; goto out; } rc = decode_ntlmssp_challenge(rsp->Buffer, le16_to_cpu(rsp->SecurityBufferLength), ses); if (rc) goto out; cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); spin_lock(&ses->ses_lock); is_binding = (ses->ses_status == SES_GOOD); spin_unlock(&ses->ses_lock); /* keep existing ses id and flags if binding */ if (!is_binding) { ses->Suid = le64_to_cpu(rsp->hdr.SessionId); ses->session_flags = le16_to_cpu(rsp->SessionFlags); } out: kfree_sensitive(ntlmssp_blob); SMB2_sess_free_buffer(sess_data); if (!rc) { sess_data->result = 0; sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate; return; } out_err: kfree_sensitive(ses->ntlmssp); ses->ntlmssp = NULL; sess_data->result = rc; sess_data->func = NULL; } static void SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) { int rc; struct cifs_ses *ses = sess_data->ses; struct TCP_Server_Info *server = sess_data->server; struct smb2_sess_setup_req *req; struct smb2_sess_setup_rsp *rsp = NULL; unsigned char *ntlmssp_blob = NULL; bool use_spnego = false; /* else use raw ntlmssp */ u16 blob_length = 0; bool is_binding = false; rc = SMB2_sess_alloc_buffer(sess_data); if (rc) goto out; req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base; req->hdr.SessionId = cpu_to_le64(ses->Suid); rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses, server, sess_data->nls_cp); if (rc) { cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc); goto out; } if (use_spnego) { /* BB eventually need to add this */ cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); rc = -EOPNOTSUPP; goto out; } sess_data->iov[1].iov_base = ntlmssp_blob; sess_data->iov[1].iov_len = blob_length; rc = SMB2_sess_sendreceive(sess_data); if (rc) goto out; rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; spin_lock(&ses->ses_lock); is_binding = (ses->ses_status == SES_GOOD); spin_unlock(&ses->ses_lock); /* keep existing ses id and flags if binding */ if (!is_binding) { ses->Suid = le64_to_cpu(rsp->hdr.SessionId); ses->session_flags = le16_to_cpu(rsp->SessionFlags); } rc = SMB2_sess_establish_session(sess_data); #ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS if (ses->server->dialect < SMB30_PROT_ID) { cifs_dbg(VFS, "%s: dumping generated SMB2 session keys\n", __func__); /* * The session id is opaque in terms of endianness, so we can't * print it as a long long. we dump it as we got it on the wire */ cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid), &ses->Suid); cifs_dbg(VFS, "Session Key %*ph\n", SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); cifs_dbg(VFS, "Signing Key %*ph\n", SMB3_SIGN_KEY_SIZE, ses->auth_key.response); } #endif out: kfree_sensitive(ntlmssp_blob); SMB2_sess_free_buffer(sess_data); kfree_sensitive(ses->ntlmssp); ses->ntlmssp = NULL; sess_data->result = rc; sess_data->func = NULL; } static int SMB2_select_sec(struct SMB2_sess_data *sess_data) { int type; struct cifs_ses *ses = sess_data->ses; struct TCP_Server_Info *server = sess_data->server; type = smb2_select_sectype(server, ses->sectype); cifs_dbg(FYI, "sess setup type %d\n", type); if (type == Unspecified) { cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); return -EINVAL; } switch (type) { case Kerberos: sess_data->func = SMB2_auth_kerberos; break; case RawNTLMSSP: sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate; break; default: cifs_dbg(VFS, "secType %d not supported!\n", type); return -EOPNOTSUPP; } return 0; } int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, struct TCP_Server_Info *server, const struct nls_table *nls_cp) { int rc = 0; struct SMB2_sess_data *sess_data; cifs_dbg(FYI, "Session Setup\n"); if (!server) { WARN(1, "%s: server is NULL!\n", __func__); return -EIO; } sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL); if (!sess_data) return -ENOMEM; sess_data->xid = xid; sess_data->ses = ses; sess_data->server = server; sess_data->buf0_type = CIFS_NO_BUFFER; sess_data->nls_cp = (struct nls_table *) nls_cp; sess_data->previous_session = ses->Suid; rc = SMB2_select_sec(sess_data); if (rc) goto out; /* * Initialize the session hash with the server one. */ memcpy(ses->preauth_sha_hash, server->preauth_sha_hash, SMB2_PREAUTH_HASH_SIZE); while (sess_data->func) sess_data->func(sess_data); if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign)) cifs_server_dbg(VFS, "signing requested but authenticated as guest\n"); rc = sess_data->result; out: kfree_sensitive(sess_data); return rc; } int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) { struct smb_rqst rqst; struct smb2_logoff_req *req; /* response is also trivial struct */ int rc = 0; struct TCP_Server_Info *server; int flags = 0; unsigned int total_len; struct kvec iov[1]; struct kvec rsp_iov; int resp_buf_type; cifs_dbg(FYI, "disconnect session %p\n", ses); if (ses && (ses->server)) server = ses->server; else return -EIO; /* no need to send SMB logoff if uid already closed due to reconnect */ spin_lock(&ses->chan_lock); if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { spin_unlock(&ses->chan_lock); goto smb2_session_already_dead; } spin_unlock(&ses->chan_lock); rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, ses->server, (void **) &req, &total_len); if (rc) return rc; /* since no tcon, smb2_init can not do this, so do here */ req->hdr.SessionId = cpu_to_le64(ses->Suid); if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) flags |= CIFS_TRANSFORM_REQ; else if (server->sign) req->hdr.Flags |= SMB2_FLAGS_SIGNED; flags |= CIFS_NO_RSP_BUF; iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = iov; rqst.rq_nvec = 1; rc = cifs_send_recv(xid, ses, ses->server, &rqst, &resp_buf_type, flags, &rsp_iov); cifs_small_buf_release(req); /* * No tcon so can't do * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); */ smb2_session_already_dead: return rc; } static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) { cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_failed[code]); } #define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) /* These are similar values to what Windows uses */ static inline void init_copy_chunk_defaults(struct cifs_tcon *tcon) { tcon->max_chunks = 256; tcon->max_bytes_chunk = 1048576; tcon->max_bytes_copy = 16777216; } int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, struct cifs_tcon *tcon, const struct nls_table *cp) { struct smb_rqst rqst; struct smb2_tree_connect_req *req; struct smb2_tree_connect_rsp *rsp = NULL; struct kvec iov[2]; struct kvec rsp_iov = { NULL, 0 }; int rc = 0; int resp_buftype; int unc_path_len; __le16 *unc_path = NULL; int flags = 0; unsigned int total_len; struct TCP_Server_Info *server = cifs_pick_channel(ses); cifs_dbg(FYI, "TCON\n"); if (!server || !tree) return -EIO; unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); if (unc_path == NULL) return -ENOMEM; unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp); if (unc_path_len <= 0) { kfree(unc_path); return -EINVAL; } unc_path_len *= 2; /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */ tcon->tid = 0; atomic_set(&tcon->num_remote_opens, 0); rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, server, (void **) &req, &total_len); if (rc) { kfree(unc_path); return rc; } if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; iov[0].iov_base = (char *)req; /* 1 for pad */ iov[0].iov_len = total_len - 1; /* Testing shows that buffer offset must be at location of Buffer[0] */ req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req)); req->PathLength = cpu_to_le16(unc_path_len); iov[1].iov_base = unc_path; iov[1].iov_len = unc_path_len; /* * 3.11 tcon req must be signed if not encrypted. See MS-SMB2 3.2.4.1.1 * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1 * (Samba servers don't always set the flag so also check if null user) */ if ((server->dialect == SMB311_PROT_ID) && !smb3_encryption_required(tcon) && !(ses->session_flags & (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) && ((ses->user_name != NULL) || (ses->sectype == Kerberos))) req->hdr.Flags |= SMB2_FLAGS_SIGNED; memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = iov; rqst.rq_nvec = 2; /* Need 64 for max size write so ask for more in case not there yet */ if (server->credits >= server->max_credits) req->hdr.CreditRequest = cpu_to_le16(0); else req->hdr.CreditRequest = cpu_to_le16( min_t(int, server->max_credits - server->credits, 64)); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base; trace_smb3_tcon(xid, tcon->tid, ses->Suid, tree, rc); if ((rc != 0) || (rsp == NULL)) { cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE); tcon->need_reconnect = true; goto tcon_error_exit; } switch (rsp->ShareType) { case SMB2_SHARE_TYPE_DISK: cifs_dbg(FYI, "connection to disk share\n"); break; case SMB2_SHARE_TYPE_PIPE: tcon->pipe = true; cifs_dbg(FYI, "connection to pipe share\n"); break; case SMB2_SHARE_TYPE_PRINT: tcon->print = true; cifs_dbg(FYI, "connection to printer\n"); break; default: cifs_server_dbg(VFS, "unknown share type %d\n", rsp->ShareType); rc = -EOPNOTSUPP; goto tcon_error_exit; } tcon->share_flags = le32_to_cpu(rsp->ShareFlags); tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */ tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId); strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name)); if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) cifs_tcon_dbg(VFS, "DFS capability contradicts DFS flag\n"); if (tcon->seal && !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) cifs_tcon_dbg(VFS, "Encryption is requested but not supported\n"); init_copy_chunk_defaults(tcon); if (server->ops->validate_negotiate) rc = server->ops->validate_negotiate(xid, tcon); if (rc == 0) /* See MS-SMB2 2.2.10 and 3.2.5.5 */ if (tcon->share_flags & SMB2_SHAREFLAG_ISOLATED_TRANSPORT) server->nosharesock = true; tcon_exit: free_rsp_buf(resp_buftype, rsp); kfree(unc_path); return rc; tcon_error_exit: if (rsp && rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); goto tcon_exit; } int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) { struct smb_rqst rqst; struct smb2_tree_disconnect_req *req; /* response is trivial */ int rc = 0; struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server = cifs_pick_channel(ses); int flags = 0; unsigned int total_len; struct kvec iov[1]; struct kvec rsp_iov; int resp_buf_type; cifs_dbg(FYI, "Tree Disconnect\n"); if (!ses || !(ses->server)) return -EIO; trace_smb3_tdis_enter(xid, tcon->tid, ses->Suid, tcon->tree_name); spin_lock(&ses->chan_lock); if ((tcon->need_reconnect) || (CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses))) { spin_unlock(&ses->chan_lock); return 0; } spin_unlock(&ses->chan_lock); invalidate_all_cached_dirs(tcon); rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, server, (void **) &req, &total_len); if (rc) return rc; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; flags |= CIFS_NO_RSP_BUF; iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = iov; rqst.rq_nvec = 1; rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buf_type, flags, &rsp_iov); cifs_small_buf_release(req); if (rc) { cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE); trace_smb3_tdis_err(xid, tcon->tid, ses->Suid, rc); } trace_smb3_tdis_done(xid, tcon->tid, ses->Suid); return rc; } static struct create_durable * create_durable_buf(void) { struct create_durable *buf; buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); if (!buf) return NULL; buf->ccontext.DataOffset = cpu_to_le16(offsetof (struct create_durable, Data)); buf->ccontext.DataLength = cpu_to_le32(16); buf->ccontext.NameOffset = cpu_to_le16(offsetof (struct create_durable, Name)); buf->ccontext.NameLength = cpu_to_le16(4); /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */ buf->Name[0] = 'D'; buf->Name[1] = 'H'; buf->Name[2] = 'n'; buf->Name[3] = 'Q'; return buf; } static struct create_durable * create_reconnect_durable_buf(struct cifs_fid *fid) { struct create_durable *buf; buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); if (!buf) return NULL; buf->ccontext.DataOffset = cpu_to_le16(offsetof (struct create_durable, Data)); buf->ccontext.DataLength = cpu_to_le32(16); buf->ccontext.NameOffset = cpu_to_le16(offsetof (struct create_durable, Name)); buf->ccontext.NameLength = cpu_to_le16(4); buf->Data.Fid.PersistentFileId = fid->persistent_fid; buf->Data.Fid.VolatileFileId = fid->volatile_fid; /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */ buf->Name[0] = 'D'; buf->Name[1] = 'H'; buf->Name[2] = 'n'; buf->Name[3] = 'C'; return buf; } static void parse_query_id_ctxt(struct create_context *cc, struct smb2_file_all_info *buf) { struct create_disk_id_rsp *pdisk_id = (struct create_disk_id_rsp *)cc; cifs_dbg(FYI, "parse query id context 0x%llx 0x%llx\n", pdisk_id->DiskFileId, pdisk_id->VolumeId); buf->IndexNumber = pdisk_id->DiskFileId; } static void parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, struct create_posix_rsp *posix) { int sid_len; u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); u8 *end = beg + le32_to_cpu(cc->DataLength); u8 *sid; memset(posix, 0, sizeof(*posix)); posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0)); posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4)); posix->mode = le32_to_cpu(*(__le32 *)(beg + 8)); sid = beg + 12; sid_len = posix_info_sid_size(sid, end); if (sid_len < 0) { cifs_dbg(VFS, "bad owner sid in posix create response\n"); return; } memcpy(&posix->owner, sid, sid_len); sid = sid + sid_len; sid_len = posix_info_sid_size(sid, end); if (sid_len < 0) { cifs_dbg(VFS, "bad group sid in posix create response\n"); return; } memcpy(&posix->group, sid, sid_len); cifs_dbg(FYI, "nlink=%d mode=%o reparse_tag=%x\n", posix->nlink, posix->mode, posix->reparse_tag); } int smb2_parse_contexts(struct TCP_Server_Info *server, struct kvec *rsp_iov, unsigned int *epoch, char *lease_key, __u8 *oplock, struct smb2_file_all_info *buf, struct create_posix_rsp *posix) { struct smb2_create_rsp *rsp = rsp_iov->iov_base; struct create_context *cc; size_t rem, off, len; size_t doff, dlen; size_t noff, nlen; char *name; static const char smb3_create_tag_posix[] = { 0x93, 0xAD, 0x25, 0x50, 0x9C, 0xB4, 0x11, 0xE7, 0xB4, 0x23, 0x83, 0xDE, 0x96, 0x8B, 0xCD, 0x7C }; *oplock = 0; off = le32_to_cpu(rsp->CreateContextsOffset); rem = le32_to_cpu(rsp->CreateContextsLength); if (check_add_overflow(off, rem, &len) || len > rsp_iov->iov_len) return -EINVAL; cc = (struct create_context *)((u8 *)rsp + off); /* Initialize inode number to 0 in case no valid data in qfid context */ if (buf) buf->IndexNumber = 0; while (rem >= sizeof(*cc)) { doff = le16_to_cpu(cc->DataOffset); dlen = le32_to_cpu(cc->DataLength); if (check_add_overflow(doff, dlen, &len) || len > rem) return -EINVAL; noff = le16_to_cpu(cc->NameOffset); nlen = le16_to_cpu(cc->NameLength); if (noff + nlen > doff) return -EINVAL; name = (char *)cc + noff; switch (nlen) { case 4: if (!strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) { *oplock = server->ops->parse_lease_buf(cc, epoch, lease_key); } else if (buf && !strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4)) { parse_query_id_ctxt(cc, buf); } break; case 16: if (posix && !memcmp(name, smb3_create_tag_posix, 16)) parse_posix_ctxt(cc, buf, posix); break; default: cifs_dbg(FYI, "%s: unhandled context (nlen=%zu dlen=%zu)\n", __func__, nlen, dlen); if (IS_ENABLED(CONFIG_CIFS_DEBUG2)) cifs_dump_mem("context data: ", cc, dlen); break; } off = le32_to_cpu(cc->Next); if (!off) break; if (check_sub_overflow(rem, off, &rem)) return -EINVAL; cc = (struct create_context *)((u8 *)cc + off); } if (rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) *oplock = rsp->OplockLevel; return 0; } static int add_lease_context(struct TCP_Server_Info *server, struct smb2_create_req *req, struct kvec *iov, unsigned int *num_iovec, u8 *lease_key, __u8 *oplock) { unsigned int num = *num_iovec; iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = server->vals->create_lease_size; req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE; *num_iovec = num + 1; return 0; } static struct create_durable_v2 * create_durable_v2_buf(struct cifs_open_parms *oparms) { struct cifs_fid *pfid = oparms->fid; struct create_durable_v2 *buf; buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL); if (!buf) return NULL; buf->ccontext.DataOffset = cpu_to_le16(offsetof (struct create_durable_v2, dcontext)); buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2)); buf->ccontext.NameOffset = cpu_to_le16(offsetof (struct create_durable_v2, Name)); buf->ccontext.NameLength = cpu_to_le16(4); /* * NB: Handle timeout defaults to 0, which allows server to choose * (most servers default to 120 seconds) and most clients default to 0. * This can be overridden at mount ("handletimeout=") if the user wants * a different persistent (or resilient) handle timeout for all opens * on a particular SMB3 mount. */ buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); /* for replay, we should not overwrite the existing create guid */ if (!oparms->replay) { generate_random_uuid(buf->dcontext.CreateGuid); memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); } else memcpy(buf->dcontext.CreateGuid, pfid->create_guid, 16); /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ buf->Name[0] = 'D'; buf->Name[1] = 'H'; buf->Name[2] = '2'; buf->Name[3] = 'Q'; return buf; } static struct create_durable_handle_reconnect_v2 * create_reconnect_durable_v2_buf(struct cifs_fid *fid) { struct create_durable_handle_reconnect_v2 *buf; buf = kzalloc(sizeof(struct create_durable_handle_reconnect_v2), GFP_KERNEL); if (!buf) return NULL; buf->ccontext.DataOffset = cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, dcontext)); buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_reconnect_context_v2)); buf->ccontext.NameOffset = cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, Name)); buf->ccontext.NameLength = cpu_to_le16(4); buf->dcontext.Fid.PersistentFileId = fid->persistent_fid; buf->dcontext.Fid.VolatileFileId = fid->volatile_fid; buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16); /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */ buf->Name[0] = 'D'; buf->Name[1] = 'H'; buf->Name[2] = '2'; buf->Name[3] = 'C'; return buf; } static int add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec, struct cifs_open_parms *oparms) { unsigned int num = *num_iovec; iov[num].iov_base = create_durable_v2_buf(oparms); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = sizeof(struct create_durable_v2); *num_iovec = num + 1; return 0; } static int add_durable_reconnect_v2_context(struct kvec *iov, unsigned int *num_iovec, struct cifs_open_parms *oparms) { unsigned int num = *num_iovec; /* indicate that we don't need to relock the file */ oparms->reconnect = false; iov[num].iov_base = create_reconnect_durable_v2_buf(oparms->fid); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = sizeof(struct create_durable_handle_reconnect_v2); *num_iovec = num + 1; return 0; } static int add_durable_context(struct kvec *iov, unsigned int *num_iovec, struct cifs_open_parms *oparms, bool use_persistent) { unsigned int num = *num_iovec; if (use_persistent) { if (oparms->reconnect) return add_durable_reconnect_v2_context(iov, num_iovec, oparms); else return add_durable_v2_context(iov, num_iovec, oparms); } if (oparms->reconnect) { iov[num].iov_base = create_reconnect_durable_buf(oparms->fid); /* indicate that we don't need to relock the file */ oparms->reconnect = false; } else iov[num].iov_base = create_durable_buf(); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = sizeof(struct create_durable); *num_iovec = num + 1; return 0; } /* See MS-SMB2 2.2.13.2.7 */ static struct crt_twarp_ctxt * create_twarp_buf(__u64 timewarp) { struct crt_twarp_ctxt *buf; buf = kzalloc(sizeof(struct crt_twarp_ctxt), GFP_KERNEL); if (!buf) return NULL; buf->ccontext.DataOffset = cpu_to_le16(offsetof (struct crt_twarp_ctxt, Timestamp)); buf->ccontext.DataLength = cpu_to_le32(8); buf->ccontext.NameOffset = cpu_to_le16(offsetof (struct crt_twarp_ctxt, Name)); buf->ccontext.NameLength = cpu_to_le16(4); /* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */ buf->Name[0] = 'T'; buf->Name[1] = 'W'; buf->Name[2] = 'r'; buf->Name[3] = 'p'; buf->Timestamp = cpu_to_le64(timewarp); return buf; } /* See MS-SMB2 2.2.13.2.7 */ static int add_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp) { unsigned int num = *num_iovec; iov[num].iov_base = create_twarp_buf(timewarp); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = sizeof(struct crt_twarp_ctxt); *num_iovec = num + 1; return 0; } /* See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ static void setup_owner_group_sids(char *buf) { struct owner_group_sids *sids = (struct owner_group_sids *)buf; /* Populate the user ownership fields S-1-5-88-1 */ sids->owner.Revision = 1; sids->owner.NumAuth = 3; sids->owner.Authority[5] = 5; sids->owner.SubAuthorities[0] = cpu_to_le32(88); sids->owner.SubAuthorities[1] = cpu_to_le32(1); sids->owner.SubAuthorities[2] = cpu_to_le32(current_fsuid().val); /* Populate the group ownership fields S-1-5-88-2 */ sids->group.Revision = 1; sids->group.NumAuth = 3; sids->group.Authority[5] = 5; sids->group.SubAuthorities[0] = cpu_to_le32(88); sids->group.SubAuthorities[1] = cpu_to_le32(2); sids->group.SubAuthorities[2] = cpu_to_le32(current_fsgid().val); cifs_dbg(FYI, "owner S-1-5-88-1-%d, group S-1-5-88-2-%d\n", current_fsuid().val, current_fsgid().val); } /* See MS-SMB2 2.2.13.2.2 and MS-DTYP 2.4.6 */ static struct crt_sd_ctxt * create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) { struct crt_sd_ctxt *buf; __u8 *ptr, *aclptr; unsigned int acelen, acl_size, ace_count; unsigned int owner_offset = 0; unsigned int group_offset = 0; struct smb3_acl acl = {}; *len = round_up(sizeof(struct crt_sd_ctxt) + (sizeof(struct smb_ace) * 4), 8); if (set_owner) { /* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */ *len += sizeof(struct owner_group_sids); } buf = kzalloc(*len, GFP_KERNEL); if (buf == NULL) return buf; ptr = (__u8 *)&buf[1]; if (set_owner) { /* offset fields are from beginning of security descriptor not of create context */ owner_offset = ptr - (__u8 *)&buf->sd; buf->sd.OffsetOwner = cpu_to_le32(owner_offset); group_offset = owner_offset + offsetof(struct owner_group_sids, group); buf->sd.OffsetGroup = cpu_to_le32(group_offset); setup_owner_group_sids(ptr); ptr += sizeof(struct owner_group_sids); } else { buf->sd.OffsetOwner = 0; buf->sd.OffsetGroup = 0; } buf->ccontext.DataOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, sd)); buf->ccontext.NameOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, Name)); buf->ccontext.NameLength = cpu_to_le16(4); /* SMB2_CREATE_SD_BUFFER_TOKEN is "SecD" */ buf->Name[0] = 'S'; buf->Name[1] = 'e'; buf->Name[2] = 'c'; buf->Name[3] = 'D'; buf->sd.Revision = 1; /* Must be one see MS-DTYP 2.4.6 */ /* * ACL is "self relative" ie ACL is stored in contiguous block of memory * and "DP" ie the DACL is present */ buf->sd.Control = cpu_to_le16(ACL_CONTROL_SR | ACL_CONTROL_DP); /* offset owner, group and Sbz1 and SACL are all zero */ buf->sd.OffsetDacl = cpu_to_le32(ptr - (__u8 *)&buf->sd); /* Ship the ACL for now. we will copy it into buf later. */ aclptr = ptr; ptr += sizeof(struct smb3_acl); /* create one ACE to hold the mode embedded in reserved special SID */ acelen = setup_special_mode_ACE((struct smb_ace *)ptr, (__u64)mode); ptr += acelen; acl_size = acelen + sizeof(struct smb3_acl); ace_count = 1; if (set_owner) { /* we do not need to reallocate buffer to add the two more ACEs. plenty of space */ acelen = setup_special_user_owner_ACE((struct smb_ace *)ptr); ptr += acelen; acl_size += acelen; ace_count += 1; } /* and one more ACE to allow access for authenticated users */ acelen = setup_authusers_ACE((struct smb_ace *)ptr); ptr += acelen; acl_size += acelen; ace_count += 1; acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */ acl.AclSize = cpu_to_le16(acl_size); acl.AceCount = cpu_to_le16(ace_count); /* acl.Sbz1 and Sbz2 MBZ so are not set here, but initialized above */ memcpy(aclptr, &acl, sizeof(struct smb3_acl)); buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd); *len = round_up((unsigned int)(ptr - (__u8 *)buf), 8); return buf; } static int add_sd_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode, bool set_owner) { unsigned int num = *num_iovec; unsigned int len = 0; iov[num].iov_base = create_sd_buf(mode, set_owner, &len); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = len; *num_iovec = num + 1; return 0; } static struct crt_query_id_ctxt * create_query_id_buf(void) { struct crt_query_id_ctxt *buf; buf = kzalloc(sizeof(struct crt_query_id_ctxt), GFP_KERNEL); if (!buf) return NULL; buf->ccontext.DataOffset = cpu_to_le16(0); buf->ccontext.DataLength = cpu_to_le32(0); buf->ccontext.NameOffset = cpu_to_le16(offsetof (struct crt_query_id_ctxt, Name)); buf->ccontext.NameLength = cpu_to_le16(4); /* SMB2_CREATE_QUERY_ON_DISK_ID is "QFid" */ buf->Name[0] = 'Q'; buf->Name[1] = 'F'; buf->Name[2] = 'i'; buf->Name[3] = 'd'; return buf; } /* See MS-SMB2 2.2.13.2.9 */ static int add_query_id_context(struct kvec *iov, unsigned int *num_iovec) { unsigned int num = *num_iovec; iov[num].iov_base = create_query_id_buf(); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = sizeof(struct crt_query_id_ctxt); *num_iovec = num + 1; return 0; } static void add_ea_context(struct cifs_open_parms *oparms, struct kvec *rq_iov, unsigned int *num_iovs) { struct kvec *iov = oparms->ea_cctx; if (iov && iov->iov_base && iov->iov_len) { rq_iov[(*num_iovs)++] = *iov; memset(iov, 0, sizeof(*iov)); } } static int alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, const char *treename, const __le16 *path) { int treename_len, path_len; struct nls_table *cp; const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)}; /* * skip leading "\\" */ treename_len = strlen(treename); if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\')) return -EINVAL; treename += 2; treename_len -= 2; path_len = UniStrnlen((wchar_t *)path, PATH_MAX); /* make room for one path separator only if @path isn't empty */ *out_len = treename_len + (path[0] ? 1 : 0) + path_len; /* * final path needs to be 8-byte aligned as specified in * MS-SMB2 2.2.13 SMB2 CREATE Request. */ *out_size = round_up(*out_len * sizeof(__le16), 8); *out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL); if (!*out_path) return -ENOMEM; cp = load_nls_default(); cifs_strtoUTF16(*out_path, treename, treename_len, cp); /* Do not append the separator if the path is empty */ if (path[0] != cpu_to_le16(0x0000)) { UniStrcat((wchar_t *)*out_path, (wchar_t *)sep); UniStrcat((wchar_t *)*out_path, (wchar_t *)path); } unload_nls(cp); return 0; } int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, umode_t mode, struct cifs_tcon *tcon, const char *full_path, struct cifs_sb_info *cifs_sb) { struct smb_rqst rqst; struct smb2_create_req *req; struct smb2_create_rsp *rsp = NULL; struct cifs_ses *ses = tcon->ses; struct kvec iov[3]; /* make sure at least one for each open context */ struct kvec rsp_iov = {NULL, 0}; int resp_buftype; int uni_path_len; __le16 *copy_path = NULL; int copy_size; int rc = 0; unsigned int n_iov = 2; __u32 file_attributes = 0; char *pc_buf = NULL; int flags = 0; unsigned int total_len; __le16 *utf16_path = NULL; struct TCP_Server_Info *server; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = 0; n_iov = 2; server = cifs_pick_channel(ses); cifs_dbg(FYI, "mkdir\n"); /* resource #1: path allocation */ utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); if (!utf16_path) return -ENOMEM; if (!ses || !server) { rc = -EIO; goto err_free_path; } /* resource #2: request */ rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, (void **) &req, &total_len); if (rc) goto err_free_path; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; req->ImpersonationLevel = IL_IMPERSONATION; req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES); /* File attributes ignored on open (used in create though) */ req->FileAttributes = cpu_to_le32(file_attributes); req->ShareAccess = FILE_SHARE_ALL_LE; req->CreateDisposition = cpu_to_le32(FILE_CREATE); req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE); iov[0].iov_base = (char *)req; /* -1 since last byte is buf[0] which is sent below (path) */ iov[0].iov_len = total_len - 1; req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); /* [MS-SMB2] 2.2.13 NameOffset: * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of * the SMB2 header, the file name includes a prefix that will * be processed during DFS name normalization as specified in * section 3.3.5.9. Otherwise, the file name is relative to * the share that is identified by the TreeId in the SMB2 * header. */ if (tcon->share_flags & SHI1005_FLAGS_DFS) { int name_len; req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; rc = alloc_path_with_tree_prefix(©_path, ©_size, &name_len, tcon->tree_name, utf16_path); if (rc) goto err_free_req; req->NameLength = cpu_to_le16(name_len * 2); uni_path_len = copy_size; /* free before overwriting resource */ kfree(utf16_path); utf16_path = copy_path; } else { uni_path_len = (2 * UniStrnlen((wchar_t *)utf16_path, PATH_MAX)) + 2; /* MUST set path len (NameLength) to 0 opening root of share */ req->NameLength = cpu_to_le16(uni_path_len - 2); if (uni_path_len % 8 != 0) { copy_size = roundup(uni_path_len, 8); copy_path = kzalloc(copy_size, GFP_KERNEL); if (!copy_path) { rc = -ENOMEM; goto err_free_req; } memcpy((char *)copy_path, (const char *)utf16_path, uni_path_len); uni_path_len = copy_size; /* free before overwriting resource */ kfree(utf16_path); utf16_path = copy_path; } } iov[1].iov_len = uni_path_len; iov[1].iov_base = utf16_path; req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; if (tcon->posix_extensions) { /* resource #3: posix buf */ rc = add_posix_context(iov, &n_iov, mode); if (rc) goto err_free_req; req->CreateContextsOffset = cpu_to_le32( sizeof(struct smb2_create_req) + iov[1].iov_len); pc_buf = iov[n_iov-1].iov_base; } memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = iov; rqst.rq_nvec = n_iov; /* no need to inc num_remote_opens because we close it just below */ trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES); if (retries) smb2_set_replay(server, &rqst); /* resource #4: response buffer */ rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); if (rc) { cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid, CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES, rc); goto err_free_rsp_buf; } /* * Although unlikely to be possible for rsp to be null and rc not set, * adding check below is slightly safer long term (and quiets Coverity * warning) */ rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; if (rsp == NULL) { rc = -EIO; kfree(pc_buf); goto err_free_req; } trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES); SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId); /* Eventually save off posix specific response info and timestamps */ err_free_rsp_buf: free_rsp_buf(resp_buftype, rsp); kfree(pc_buf); err_free_req: cifs_small_buf_release(req); err_free_path: kfree(utf16_path); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } int SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, struct smb_rqst *rqst, __u8 *oplock, struct cifs_open_parms *oparms, __le16 *path) { struct smb2_create_req *req; unsigned int n_iov = 2; __u32 file_attributes = 0; int copy_size; int uni_path_len; unsigned int total_len; struct kvec *iov = rqst->rq_iov; __le16 *copy_path; int rc; rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, (void **) &req, &total_len); if (rc) return rc; iov[0].iov_base = (char *)req; /* -1 since last byte is buf[0] which is sent below (path) */ iov[0].iov_len = total_len - 1; if (oparms->create_options & CREATE_OPTION_READONLY) file_attributes |= ATTR_READONLY; if (oparms->create_options & CREATE_OPTION_SPECIAL) file_attributes |= ATTR_SYSTEM; req->ImpersonationLevel = IL_IMPERSONATION; req->DesiredAccess = cpu_to_le32(oparms->desired_access); /* File attributes ignored on open (used in create though) */ req->FileAttributes = cpu_to_le32(file_attributes); req->ShareAccess = FILE_SHARE_ALL_LE; req->CreateDisposition = cpu_to_le32(oparms->disposition); req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); /* [MS-SMB2] 2.2.13 NameOffset: * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of * the SMB2 header, the file name includes a prefix that will * be processed during DFS name normalization as specified in * section 3.3.5.9. Otherwise, the file name is relative to * the share that is identified by the TreeId in the SMB2 * header. */ if (tcon->share_flags & SHI1005_FLAGS_DFS) { int name_len; req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; rc = alloc_path_with_tree_prefix(©_path, ©_size, &name_len, tcon->tree_name, path); if (rc) return rc; req->NameLength = cpu_to_le16(name_len * 2); uni_path_len = copy_size; path = copy_path; } else { uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; /* MUST set path len (NameLength) to 0 opening root of share */ req->NameLength = cpu_to_le16(uni_path_len - 2); copy_size = round_up(uni_path_len, 8); copy_path = kzalloc(copy_size, GFP_KERNEL); if (!copy_path) return -ENOMEM; memcpy((char *)copy_path, (const char *)path, uni_path_len); uni_path_len = copy_size; path = copy_path; } iov[1].iov_len = uni_path_len; iov[1].iov_base = path; if ((!server->oplocks) || (tcon->no_lease)) *oplock = SMB2_OPLOCK_LEVEL_NONE; if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || *oplock == SMB2_OPLOCK_LEVEL_NONE) req->RequestedOplockLevel = *oplock; else if (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) && (oparms->create_options & CREATE_NOT_FILE)) req->RequestedOplockLevel = *oplock; /* no srv lease support */ else { rc = add_lease_context(server, req, iov, &n_iov, oparms->fid->lease_key, oplock); if (rc) return rc; } if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { rc = add_durable_context(iov, &n_iov, oparms, tcon->use_persistent); if (rc) return rc; } if (tcon->posix_extensions) { rc = add_posix_context(iov, &n_iov, oparms->mode); if (rc) return rc; } if (tcon->snapshot_time) { cifs_dbg(FYI, "adding snapshot context\n"); rc = add_twarp_context(iov, &n_iov, tcon->snapshot_time); if (rc) return rc; } if ((oparms->disposition != FILE_OPEN) && (oparms->cifs_sb)) { bool set_mode; bool set_owner; if ((oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) && (oparms->mode != ACL_NO_MODE)) set_mode = true; else { set_mode = false; oparms->mode = ACL_NO_MODE; } if (oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) set_owner = true; else set_owner = false; if (set_owner | set_mode) { cifs_dbg(FYI, "add sd with mode 0x%x\n", oparms->mode); rc = add_sd_context(iov, &n_iov, oparms->mode, set_owner); if (rc) return rc; } } add_query_id_context(iov, &n_iov); add_ea_context(oparms, iov, &n_iov); if (n_iov > 2) { /* * We have create contexts behind iov[1] (the file * name), point at them from the main create request */ req->CreateContextsOffset = cpu_to_le32( sizeof(struct smb2_create_req) + iov[1].iov_len); req->CreateContextsLength = 0; for (unsigned int i = 2; i < (n_iov-1); i++) { struct kvec *v = &iov[i]; size_t len = v->iov_len; struct create_context *cctx = (struct create_context *)v->iov_base; cctx->Next = cpu_to_le32(len); le32_add_cpu(&req->CreateContextsLength, len); } le32_add_cpu(&req->CreateContextsLength, iov[n_iov-1].iov_len); } rqst->rq_nvec = n_iov; return 0; } /* rq_iov[0] is the request and is released by cifs_small_buf_release(). * All other vectors are freed by kfree(). */ void SMB2_open_free(struct smb_rqst *rqst) { int i; if (rqst && rqst->rq_iov) { cifs_small_buf_release(rqst->rq_iov[0].iov_base); for (i = 1; i < rqst->rq_nvec; i++) if (rqst->rq_iov[i].iov_base != smb2_padding) kfree(rqst->rq_iov[i].iov_base); } } int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, __u8 *oplock, struct smb2_file_all_info *buf, struct create_posix_rsp *posix, struct kvec *err_iov, int *buftype) { struct smb_rqst rqst; struct smb2_create_rsp *rsp = NULL; struct cifs_tcon *tcon = oparms->tcon; struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server; struct kvec iov[SMB2_CREATE_IOV_SIZE]; struct kvec rsp_iov = {NULL, 0}; int resp_buftype = CIFS_NO_BUFFER; int rc = 0; int flags = 0; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = 0; server = cifs_pick_channel(ses); oparms->replay = !!(retries); cifs_dbg(FYI, "create/open\n"); if (!ses || !server) return -EIO; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; memset(&rqst, 0, sizeof(struct smb_rqst)); memset(&iov, 0, sizeof(iov)); rqst.rq_iov = iov; rqst.rq_nvec = SMB2_CREATE_IOV_SIZE; rc = SMB2_open_init(tcon, server, &rqst, oplock, oparms, path); if (rc) goto creat_exit; trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, oparms->path, oparms->create_options, oparms->desired_access); if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; if (rc != 0) { cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); if (err_iov && rsp) { *err_iov = rsp_iov; *buftype = resp_buftype; resp_buftype = CIFS_NO_BUFFER; rsp = NULL; } trace_smb3_open_err(xid, tcon->tid, ses->Suid, oparms->create_options, oparms->desired_access, rc); if (rc == -EREMCHG) { pr_warn_once("server share %s deleted\n", tcon->tree_name); tcon->need_reconnect = true; } goto creat_exit; } else if (rsp == NULL) /* unlikely to happen, but safer to check */ goto creat_exit; else trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, oparms->create_options, oparms->desired_access); atomic_inc(&tcon->num_remote_opens); oparms->fid->persistent_fid = rsp->PersistentFileId; oparms->fid->volatile_fid = rsp->VolatileFileId; oparms->fid->access = oparms->desired_access; #ifdef CONFIG_CIFS_DEBUG2 oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId); #endif /* CIFS_DEBUG2 */ if (buf) { buf->CreationTime = rsp->CreationTime; buf->LastAccessTime = rsp->LastAccessTime; buf->LastWriteTime = rsp->LastWriteTime; buf->ChangeTime = rsp->ChangeTime; buf->AllocationSize = rsp->AllocationSize; buf->EndOfFile = rsp->EndofFile; buf->Attributes = rsp->FileAttributes; buf->NumberOfLinks = cpu_to_le32(1); buf->DeletePending = 0; } rc = smb2_parse_contexts(server, &rsp_iov, &oparms->fid->epoch, oparms->fid->lease_key, oplock, buf, posix); creat_exit: SMB2_open_free(&rqst); free_rsp_buf(resp_buftype, rsp); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } int SMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, struct smb_rqst *rqst, u64 persistent_fid, u64 volatile_fid, u32 opcode, char *in_data, u32 indatalen, __u32 max_response_size) { struct smb2_ioctl_req *req; struct kvec *iov = rqst->rq_iov; unsigned int total_len; int rc; char *in_data_buf; rc = smb2_ioctl_req_init(opcode, tcon, server, (void **) &req, &total_len); if (rc) return rc; if (indatalen) { unsigned int len; if (WARN_ON_ONCE(smb3_encryption_required(tcon) && (check_add_overflow(total_len - 1, ALIGN(indatalen, 8), &len) || len > MAX_CIFS_SMALL_BUFFER_SIZE))) { cifs_small_buf_release(req); return -EIO; } /* * indatalen is usually small at a couple of bytes max, so * just allocate through generic pool */ in_data_buf = kmemdup(in_data, indatalen, GFP_NOFS); if (!in_data_buf) { cifs_small_buf_release(req); return -ENOMEM; } } req->CtlCode = cpu_to_le32(opcode); req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; iov[0].iov_base = (char *)req; /* * If no input data, the size of ioctl struct in * protocol spec still includes a 1 byte data buffer, * but if input data passed to ioctl, we do not * want to double count this, so we do not send * the dummy one byte of data in iovec[0] if sending * input data (in iovec[1]). */ if (indatalen) { req->InputCount = cpu_to_le32(indatalen); /* do not set InputOffset if no input data */ req->InputOffset = cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer)); rqst->rq_nvec = 2; iov[0].iov_len = total_len - 1; iov[1].iov_base = in_data_buf; iov[1].iov_len = indatalen; } else { rqst->rq_nvec = 1; iov[0].iov_len = total_len; } req->OutputOffset = 0; req->OutputCount = 0; /* MBZ */ /* * In most cases max_response_size is set to 16K (CIFSMaxBufSize) * We Could increase default MaxOutputResponse, but that could require * more credits. Windows typically sets this smaller, but for some * ioctls it may be useful to allow server to send more. No point * limiting what the server can send as long as fits in one credit * We can not handle more than CIFS_MAX_BUF_SIZE yet but may want * to increase this limit up in the future. * Note that for snapshot queries that servers like Azure expect that * the first query be minimal size (and just used to get the number/size * of previous versions) so response size must be specified as EXACTLY * sizeof(struct snapshot_array) which is 16 when rounded up to multiple * of eight bytes. Currently that is the only case where we set max * response size smaller. */ req->MaxOutputResponse = cpu_to_le32(max_response_size); req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(max(indatalen, max_response_size), SMB2_MAX_BUFFER_SIZE)); /* always an FSCTL (for now) */ req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */ if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) req->hdr.Flags |= SMB2_FLAGS_SIGNED; return 0; } void SMB2_ioctl_free(struct smb_rqst *rqst) { int i; if (rqst && rqst->rq_iov) { cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ for (i = 1; i < rqst->rq_nvec; i++) if (rqst->rq_iov[i].iov_base != smb2_padding) kfree(rqst->rq_iov[i].iov_base); } } /* * SMB2 IOCTL is used for both IOCTLs and FSCTLs */ int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, u32 opcode, char *in_data, u32 indatalen, u32 max_out_data_len, char **out_data, u32 *plen /* returned data len */) { struct smb_rqst rqst; struct smb2_ioctl_rsp *rsp = NULL; struct cifs_ses *ses; struct TCP_Server_Info *server; struct kvec iov[SMB2_IOCTL_IOV_SIZE]; struct kvec rsp_iov = {NULL, 0}; int resp_buftype = CIFS_NO_BUFFER; int rc = 0; int flags = 0; int retries = 0, cur_sleep = 1; if (!tcon) return -EIO; ses = tcon->ses; if (!ses) return -EIO; replay_again: /* reinitialize for possible replay */ flags = 0; server = cifs_pick_channel(ses); if (!server) return -EIO; cifs_dbg(FYI, "SMB2 IOCTL\n"); if (out_data != NULL) *out_data = NULL; /* zero out returned data len, in case of error */ if (plen) *plen = 0; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; memset(&rqst, 0, sizeof(struct smb_rqst)); memset(&iov, 0, sizeof(iov)); rqst.rq_iov = iov; rqst.rq_nvec = SMB2_IOCTL_IOV_SIZE; rc = SMB2_ioctl_init(tcon, server, &rqst, persistent_fid, volatile_fid, opcode, in_data, indatalen, max_out_data_len); if (rc) goto ioctl_exit; if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base; if (rc != 0) trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid, ses->Suid, 0, opcode, rc); if ((rc != 0) && (rc != -EINVAL) && (rc != -E2BIG)) { cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); goto ioctl_exit; } else if (rc == -EINVAL) { if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && (opcode != FSCTL_SRV_COPYCHUNK)) { cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); goto ioctl_exit; } } else if (rc == -E2BIG) { if (opcode != FSCTL_QUERY_ALLOCATED_RANGES) { cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); goto ioctl_exit; } } /* check if caller wants to look at return data or just return rc */ if ((plen == NULL) || (out_data == NULL)) goto ioctl_exit; /* * Although unlikely to be possible for rsp to be null and rc not set, * adding check below is slightly safer long term (and quiets Coverity * warning) */ if (rsp == NULL) { rc = -EIO; goto ioctl_exit; } *plen = le32_to_cpu(rsp->OutputCount); /* We check for obvious errors in the output buffer length and offset */ if (*plen == 0) goto ioctl_exit; /* server returned no data */ else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) { cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); *plen = 0; rc = -EIO; goto ioctl_exit; } if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) { cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, le32_to_cpu(rsp->OutputOffset)); *plen = 0; rc = -EIO; goto ioctl_exit; } *out_data = kmemdup((char *)rsp + le32_to_cpu(rsp->OutputOffset), *plen, GFP_KERNEL); if (*out_data == NULL) { rc = -ENOMEM; goto ioctl_exit; } ioctl_exit: SMB2_ioctl_free(&rqst); free_rsp_buf(resp_buftype, rsp); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } /* * Individual callers to ioctl worker function follow */ int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid) { int rc; struct compress_ioctl fsctl_input; char *ret_data = NULL; fsctl_input.CompressionState = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, FSCTL_SET_COMPRESSION, (char *)&fsctl_input /* data input */, 2 /* in data len */, CIFSMaxBufSize /* max out data */, &ret_data /* out data */, NULL); cifs_dbg(FYI, "set compression rc %d\n", rc); return rc; } int SMB2_close_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, struct smb_rqst *rqst, u64 persistent_fid, u64 volatile_fid, bool query_attrs) { struct smb2_close_req *req; struct kvec *iov = rqst->rq_iov; unsigned int total_len; int rc; rc = smb2_plain_req_init(SMB2_CLOSE, tcon, server, (void **) &req, &total_len); if (rc) return rc; req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; if (query_attrs) req->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; else req->Flags = 0; iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; return 0; } void SMB2_close_free(struct smb_rqst *rqst) { if (rqst && rqst->rq_iov) cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ } int __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, struct smb2_file_network_open_info *pbuf) { struct smb_rqst rqst; struct smb2_close_rsp *rsp = NULL; struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server; struct kvec iov[1]; struct kvec rsp_iov; int resp_buftype = CIFS_NO_BUFFER; int rc = 0; int flags = 0; bool query_attrs = false; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = 0; query_attrs = false; server = cifs_pick_channel(ses); cifs_dbg(FYI, "Close\n"); if (!ses || !server) return -EIO; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; memset(&rqst, 0, sizeof(struct smb_rqst)); memset(&iov, 0, sizeof(iov)); rqst.rq_iov = iov; rqst.rq_nvec = 1; /* check if need to ask server to return timestamps in close response */ if (pbuf) query_attrs = true; trace_smb3_close_enter(xid, persistent_fid, tcon->tid, ses->Suid); rc = SMB2_close_init(tcon, server, &rqst, persistent_fid, volatile_fid, query_attrs); if (rc) goto close_exit; if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); rsp = (struct smb2_close_rsp *)rsp_iov.iov_base; if (rc != 0) { cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE); trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid, rc); goto close_exit; } else { trace_smb3_close_done(xid, persistent_fid, tcon->tid, ses->Suid); if (pbuf) memcpy(&pbuf->network_open_info, &rsp->network_open_info, sizeof(pbuf->network_open_info)); atomic_dec(&tcon->num_remote_opens); } close_exit: SMB2_close_free(&rqst); free_rsp_buf(resp_buftype, rsp); /* retry close in a worker thread if this one is interrupted */ if (is_interrupt_error(rc)) { int tmp_rc; tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid, volatile_fid); if (tmp_rc) cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n", persistent_fid, tmp_rc); } if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid) { return __SMB2_close(xid, tcon, persistent_fid, volatile_fid, NULL); } int smb2_validate_iov(unsigned int offset, unsigned int buffer_length, struct kvec *iov, unsigned int min_buf_size) { unsigned int smb_len = iov->iov_len; char *end_of_smb = smb_len + (char *)iov->iov_base; char *begin_of_buf = offset + (char *)iov->iov_base; char *end_of_buf = begin_of_buf + buffer_length; if (buffer_length < min_buf_size) { cifs_dbg(VFS, "buffer length %d smaller than minimum size %d\n", buffer_length, min_buf_size); return -EINVAL; } /* check if beyond RFC1001 maximum length */ if ((smb_len > 0x7FFFFF) || (buffer_length > 0x7FFFFF)) { cifs_dbg(VFS, "buffer length %d or smb length %d too large\n", buffer_length, smb_len); return -EINVAL; } if ((begin_of_buf > end_of_smb) || (end_of_buf > end_of_smb)) { cifs_dbg(VFS, "Invalid server response, bad offset to data\n"); return -EINVAL; } return 0; } /* * If SMB buffer fields are valid, copy into temporary buffer to hold result. * Caller must free buffer. */ int smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, struct kvec *iov, unsigned int minbufsize, char *data) { char *begin_of_buf = offset + (char *)iov->iov_base; int rc; if (!data) return -EINVAL; rc = smb2_validate_iov(offset, buffer_length, iov, minbufsize); if (rc) return rc; memcpy(data, begin_of_buf, minbufsize); return 0; } int SMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, struct smb_rqst *rqst, u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type, u32 additional_info, size_t output_len, size_t input_len, void *input) { struct smb2_query_info_req *req; struct kvec *iov = rqst->rq_iov; unsigned int total_len; size_t len; int rc; if (unlikely(check_add_overflow(input_len, sizeof(*req), &len) || len > CIFSMaxBufSize)) return -EINVAL; rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, (void **) &req, &total_len); if (rc) return rc; req->InfoType = info_type; req->FileInfoClass = info_class; req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; req->AdditionalInformation = cpu_to_le32(additional_info); req->OutputBufferLength = cpu_to_le32(output_len); if (input_len) { req->InputBufferLength = cpu_to_le32(input_len); /* total_len for smb query request never close to le16 max */ req->InputBufferOffset = cpu_to_le16(total_len - 1); memcpy(req->Buffer, input, input_len); } iov[0].iov_base = (char *)req; /* 1 for Buffer */ iov[0].iov_len = len; return 0; } void SMB2_query_info_free(struct smb_rqst *rqst) { if (rqst && rqst->rq_iov) cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ } static int query_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type, u32 additional_info, size_t output_len, size_t min_len, void **data, u32 *dlen) { struct smb_rqst rqst; struct smb2_query_info_rsp *rsp = NULL; struct kvec iov[1]; struct kvec rsp_iov; int rc = 0; int resp_buftype = CIFS_NO_BUFFER; struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server; int flags = 0; bool allocated = false; int retries = 0, cur_sleep = 1; cifs_dbg(FYI, "Query Info\n"); if (!ses) return -EIO; replay_again: /* reinitialize for possible replay */ flags = 0; allocated = false; server = cifs_pick_channel(ses); if (!server) return -EIO; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; memset(&rqst, 0, sizeof(struct smb_rqst)); memset(&iov, 0, sizeof(iov)); rqst.rq_iov = iov; rqst.rq_nvec = 1; rc = SMB2_query_info_init(tcon, server, &rqst, persistent_fid, volatile_fid, info_class, info_type, additional_info, output_len, 0, NULL); if (rc) goto qinf_exit; trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid, ses->Suid, info_class, (__u32)info_type); if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; if (rc) { cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); trace_smb3_query_info_err(xid, persistent_fid, tcon->tid, ses->Suid, info_class, (__u32)info_type, rc); goto qinf_exit; } trace_smb3_query_info_done(xid, persistent_fid, tcon->tid, ses->Suid, info_class, (__u32)info_type); if (dlen) { *dlen = le32_to_cpu(rsp->OutputBufferLength); if (!*data) { *data = kmalloc(*dlen, GFP_KERNEL); if (!*data) { cifs_tcon_dbg(VFS, "Error %d allocating memory for acl\n", rc); *dlen = 0; rc = -ENOMEM; goto qinf_exit; } allocated = true; } } rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, dlen ? *dlen : min_len, *data); if (rc && allocated) { kfree(*data); *data = NULL; *dlen = 0; } qinf_exit: SMB2_query_info_free(&rqst); free_rsp_buf(resp_buftype, rsp); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data) { return query_info(xid, tcon, persistent_fid, volatile_fid, FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0, sizeof(struct smb2_file_all_info) + PATH_MAX * 2, sizeof(struct smb2_file_all_info), (void **)&data, NULL); } #if 0 /* currently unused, as now we are doing compounding instead (see smb311_posix_query_path_info) */ int SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen) { size_t output_len = sizeof(struct smb311_posix_qinfo *) + (sizeof(struct smb_sid) * 2) + (PATH_MAX * 2); *plen = 0; return query_info(xid, tcon, persistent_fid, volatile_fid, SMB_FIND_FILE_POSIX_INFO, SMB2_O_INFO_FILE, 0, output_len, sizeof(struct smb311_posix_qinfo), (void **)&data, plen); /* Note caller must free "data" (passed in above). It may be allocated in query_info call */ } #endif int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, void **data, u32 *plen, u32 extra_info) { __u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | extra_info; *plen = 0; return query_info(xid, tcon, persistent_fid, volatile_fid, 0, SMB2_O_INFO_SECURITY, additional_info, SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen); } int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid) { return query_info(xid, tcon, persistent_fid, volatile_fid, FILE_INTERNAL_INFORMATION, SMB2_O_INFO_FILE, 0, sizeof(struct smb2_file_internal_info), sizeof(struct smb2_file_internal_info), (void **)&uniqueid, NULL); } /* * CHANGE_NOTIFY Request is sent to get notifications on changes to a directory * See MS-SMB2 2.2.35 and 2.2.36 */ static int SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst, struct cifs_tcon *tcon, struct TCP_Server_Info *server, u64 persistent_fid, u64 volatile_fid, u32 completion_filter, bool watch_tree) { struct smb2_change_notify_req *req; struct kvec *iov = rqst->rq_iov; unsigned int total_len; int rc; rc = smb2_plain_req_init(SMB2_CHANGE_NOTIFY, tcon, server, (void **) &req, &total_len); if (rc) return rc; req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; /* See note 354 of MS-SMB2, 64K max */ req->OutputBufferLength = cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE); req->CompletionFilter = cpu_to_le32(completion_filter); if (watch_tree) req->Flags = cpu_to_le16(SMB2_WATCH_TREE); else req->Flags = 0; iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; return 0; } int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, bool watch_tree, u32 completion_filter, u32 max_out_data_len, char **out_data, u32 *plen /* returned data len */) { struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server; struct smb_rqst rqst; struct smb2_change_notify_rsp *smb_rsp; struct kvec iov[1]; struct kvec rsp_iov = {NULL, 0}; int resp_buftype = CIFS_NO_BUFFER; int flags = 0; int rc = 0; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = 0; server = cifs_pick_channel(ses); cifs_dbg(FYI, "change notify\n"); if (!ses || !server) return -EIO; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; memset(&rqst, 0, sizeof(struct smb_rqst)); memset(&iov, 0, sizeof(iov)); if (plen) *plen = 0; rqst.rq_iov = iov; rqst.rq_nvec = 1; rc = SMB2_notify_init(xid, &rqst, tcon, server, persistent_fid, volatile_fid, completion_filter, watch_tree); if (rc) goto cnotify_exit; trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid, (u8)watch_tree, completion_filter); if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); if (rc != 0) { cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE); trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid, (u8)watch_tree, completion_filter, rc); } else { trace_smb3_notify_done(xid, persistent_fid, tcon->tid, ses->Suid, (u8)watch_tree, completion_filter); /* validate that notify information is plausible */ if ((rsp_iov.iov_base == NULL) || (rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp) + 1)) goto cnotify_exit; smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base; smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset), le32_to_cpu(smb_rsp->OutputBufferLength), &rsp_iov, sizeof(struct file_notify_information)); *out_data = kmemdup((char *)smb_rsp + le16_to_cpu(smb_rsp->OutputBufferOffset), le32_to_cpu(smb_rsp->OutputBufferLength), GFP_KERNEL); if (*out_data == NULL) { rc = -ENOMEM; goto cnotify_exit; } else if (plen) *plen = le32_to_cpu(smb_rsp->OutputBufferLength); } cnotify_exit: if (rqst.rq_iov) cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */ free_rsp_buf(resp_buftype, rsp_iov.iov_base); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } /* * This is a no-op for now. We're not really interested in the reply, but * rather in the fact that the server sent one and that server->lstrp * gets updated. * * FIXME: maybe we should consider checking that the reply matches request? */ static void smb2_echo_callback(struct mid_q_entry *mid) { struct TCP_Server_Info *server = mid->callback_data; struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf; struct cifs_credits credits = { .value = 0, .instance = 0 }; if (mid->mid_state == MID_RESPONSE_RECEIVED || mid->mid_state == MID_RESPONSE_MALFORMED) { credits.value = le16_to_cpu(rsp->hdr.CreditRequest); credits.instance = server->reconnect_instance; } release_mid(mid); add_credits(server, &credits, CIFS_ECHO_OP); } void smb2_reconnect_server(struct work_struct *work) { struct TCP_Server_Info *server = container_of(work, struct TCP_Server_Info, reconnect.work); struct TCP_Server_Info *pserver; struct cifs_ses *ses, *ses2; struct cifs_tcon *tcon, *tcon2; struct list_head tmp_list, tmp_ses_list; bool ses_exist = false; bool tcon_selected = false; int rc; bool resched = false; /* first check if ref count has reached 0, if not inc ref count */ spin_lock(&cifs_tcp_ses_lock); if (!server->srv_count) { spin_unlock(&cifs_tcp_ses_lock); return; } server->srv_count++; spin_unlock(&cifs_tcp_ses_lock); /* If server is a channel, select the primary channel */ pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */ mutex_lock(&pserver->reconnect_mutex); /* if the server is marked for termination, drop the ref count here */ if (server->terminate) { cifs_put_tcp_session(server, true); mutex_unlock(&pserver->reconnect_mutex); return; } INIT_LIST_HEAD(&tmp_list); INIT_LIST_HEAD(&tmp_ses_list); cifs_dbg(FYI, "Reconnecting tcons and channels\n"); spin_lock(&cifs_tcp_ses_lock); list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { spin_lock(&ses->ses_lock); if (ses->ses_status == SES_EXITING) { spin_unlock(&ses->ses_lock); continue; } spin_unlock(&ses->ses_lock); tcon_selected = false; list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { if (tcon->need_reconnect || tcon->need_reopen_files) { tcon->tc_count++; trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, netfs_trace_tcon_ref_get_reconnect_server); list_add_tail(&tcon->rlist, &tmp_list); tcon_selected = true; } } /* * IPC has the same lifetime as its session and uses its * refcount. */ if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) { list_add_tail(&ses->tcon_ipc->rlist, &tmp_list); tcon_selected = true; cifs_smb_ses_inc_refcount(ses); } /* * handle the case where channel needs to reconnect * binding session, but tcon is healthy (some other channel * is active) */ spin_lock(&ses->chan_lock); if (!tcon_selected && cifs_chan_needs_reconnect(ses, server)) { list_add_tail(&ses->rlist, &tmp_ses_list); ses_exist = true; cifs_smb_ses_inc_refcount(ses); } spin_unlock(&ses->chan_lock); } spin_unlock(&cifs_tcp_ses_lock); list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) { rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server, true); if (!rc) cifs_reopen_persistent_handles(tcon); else resched = true; list_del_init(&tcon->rlist); if (tcon->ipc) cifs_put_smb_ses(tcon->ses); else cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_reconnect_server); } if (!ses_exist) goto done; /* allocate a dummy tcon struct used for reconnect */ tcon = tcon_info_alloc(false, netfs_trace_tcon_ref_new_reconnect_server); if (!tcon) { resched = true; list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { list_del_init(&ses->rlist); cifs_put_smb_ses(ses); } goto done; } tcon->status = TID_GOOD; tcon->retry = false; tcon->need_reconnect = false; /* now reconnect sessions for necessary channels */ list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { tcon->ses = ses; rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server, true); if (rc) resched = true; list_del_init(&ses->rlist); cifs_put_smb_ses(ses); } tconInfoFree(tcon, netfs_trace_tcon_ref_free_reconnect_server); done: cifs_dbg(FYI, "Reconnecting tcons and channels finished\n"); if (resched) queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ); mutex_unlock(&pserver->reconnect_mutex); /* now we can safely release srv struct */ cifs_put_tcp_session(server, true); } int SMB2_echo(struct TCP_Server_Info *server) { struct smb2_echo_req *req; int rc = 0; struct kvec iov[1]; struct smb_rqst rqst = { .rq_iov = iov, .rq_nvec = 1 }; unsigned int total_len; cifs_dbg(FYI, "In echo request for conn_id %lld\n", server->conn_id); spin_lock(&server->srv_lock); if (server->ops->need_neg && server->ops->need_neg(server)) { spin_unlock(&server->srv_lock); /* No need to send echo on newly established connections */ mod_delayed_work(cifsiod_wq, &server->reconnect, 0); return rc; } spin_unlock(&server->srv_lock); rc = smb2_plain_req_init(SMB2_ECHO, NULL, server, (void **)&req, &total_len); if (rc) return rc; req->hdr.CreditRequest = cpu_to_le16(1); iov[0].iov_len = total_len; iov[0].iov_base = (char *)req; rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL, server, CIFS_ECHO_OP, NULL); if (rc) cifs_dbg(FYI, "Echo request failed: %d\n", rc); cifs_small_buf_release(req); return rc; } void SMB2_flush_free(struct smb_rqst *rqst) { if (rqst && rqst->rq_iov) cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ } int SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, struct cifs_tcon *tcon, struct TCP_Server_Info *server, u64 persistent_fid, u64 volatile_fid) { struct smb2_flush_req *req; struct kvec *iov = rqst->rq_iov; unsigned int total_len; int rc; rc = smb2_plain_req_init(SMB2_FLUSH, tcon, server, (void **) &req, &total_len); if (rc) return rc; req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; return 0; } int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid) { struct cifs_ses *ses = tcon->ses; struct smb_rqst rqst; struct kvec iov[1]; struct kvec rsp_iov = {NULL, 0}; struct TCP_Server_Info *server; int resp_buftype = CIFS_NO_BUFFER; int flags = 0; int rc = 0; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = 0; server = cifs_pick_channel(ses); cifs_dbg(FYI, "flush\n"); if (!ses || !(ses->server)) return -EIO; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; memset(&rqst, 0, sizeof(struct smb_rqst)); memset(&iov, 0, sizeof(iov)); rqst.rq_iov = iov; rqst.rq_nvec = 1; rc = SMB2_flush_init(xid, &rqst, tcon, server, persistent_fid, volatile_fid); if (rc) goto flush_exit; trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid); if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); if (rc != 0) { cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE); trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid, rc); } else trace_smb3_flush_done(xid, persistent_fid, tcon->tid, ses->Suid); flush_exit: SMB2_flush_free(&rqst); free_rsp_buf(resp_buftype, rsp_iov.iov_base); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } #ifdef CONFIG_CIFS_SMB_DIRECT static inline bool smb3_use_rdma_offload(struct cifs_io_parms *io_parms) { struct TCP_Server_Info *server = io_parms->server; struct cifs_tcon *tcon = io_parms->tcon; /* we can only offload if we're connected */ if (!server || !tcon) return false; /* we can only offload on an rdma connection */ if (!server->rdma || !server->smbd_conn) return false; /* we don't support signed offload yet */ if (server->sign) return false; /* we don't support encrypted offload yet */ if (smb3_encryption_required(tcon)) return false; /* offload also has its overhead, so only do it if desired */ if (io_parms->length < server->smbd_conn->rdma_readwrite_threshold) return false; return true; } #endif /* CONFIG_CIFS_SMB_DIRECT */ /* * To form a chain of read requests, any read requests after the first should * have the end_of_chain boolean set to true. */ static int smb2_new_read_req(void **buf, unsigned int *total_len, struct cifs_io_parms *io_parms, struct cifs_io_subrequest *rdata, unsigned int remaining_bytes, int request_type) { int rc = -EACCES; struct smb2_read_req *req = NULL; struct smb2_hdr *shdr; struct TCP_Server_Info *server = io_parms->server; rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, server, (void **) &req, total_len); if (rc) return rc; if (server == NULL) return -ECONNABORTED; shdr = &req->hdr; shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); req->PersistentFileId = io_parms->persistent_fid; req->VolatileFileId = io_parms->volatile_fid; req->ReadChannelInfoOffset = 0; /* reserved */ req->ReadChannelInfoLength = 0; /* reserved */ req->Channel = 0; /* reserved */ req->MinimumCount = 0; req->Length = cpu_to_le32(io_parms->length); req->Offset = cpu_to_le64(io_parms->offset); trace_smb3_read_enter(rdata ? rdata->rreq->debug_id : 0, rdata ? rdata->subreq.debug_index : 0, rdata ? rdata->xid : 0, io_parms->persistent_fid, io_parms->tcon->tid, io_parms->tcon->ses->Suid, io_parms->offset, io_parms->length); #ifdef CONFIG_CIFS_SMB_DIRECT /* * If we want to do a RDMA write, fill in and append * smbd_buffer_descriptor_v1 to the end of read request */ if (rdata && smb3_use_rdma_offload(io_parms)) { struct smbd_buffer_descriptor_v1 *v1; bool need_invalidate = server->dialect == SMB30_PROT_ID; rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->subreq.io_iter, true, need_invalidate); if (!rdata->mr) return -EAGAIN; req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; if (need_invalidate) req->Channel = SMB2_CHANNEL_RDMA_V1; req->ReadChannelInfoOffset = cpu_to_le16(offsetof(struct smb2_read_req, Buffer)); req->ReadChannelInfoLength = cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; v1->offset = cpu_to_le64(rdata->mr->mr->iova); v1->token = cpu_to_le32(rdata->mr->mr->rkey); v1->length = cpu_to_le32(rdata->mr->mr->length); *total_len += sizeof(*v1) - 1; } #endif if (request_type & CHAINED_REQUEST) { if (!(request_type & END_OF_CHAIN)) { /* next 8-byte aligned request */ *total_len = ALIGN(*total_len, 8); shdr->NextCommand = cpu_to_le32(*total_len); } else /* END_OF_CHAIN */ shdr->NextCommand = 0; if (request_type & RELATED_REQUEST) { shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; /* * Related requests use info from previous read request * in chain. */ shdr->SessionId = cpu_to_le64(0xFFFFFFFFFFFFFFFF); shdr->Id.SyncId.TreeId = cpu_to_le32(0xFFFFFFFF); req->PersistentFileId = (u64)-1; req->VolatileFileId = (u64)-1; } } if (remaining_bytes > io_parms->length) req->RemainingBytes = cpu_to_le32(remaining_bytes); else req->RemainingBytes = 0; *buf = req; return rc; } static void smb2_readv_worker(struct work_struct *work) { struct cifs_io_subrequest *rdata = container_of(work, struct cifs_io_subrequest, subreq.work); netfs_read_subreq_terminated(&rdata->subreq, rdata->result, false); } static void smb2_readv_callback(struct mid_q_entry *mid) { struct cifs_io_subrequest *rdata = mid->callback_data; struct netfs_inode *ictx = netfs_inode(rdata->rreq->inode); struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink); struct TCP_Server_Info *server = rdata->server; struct smb2_hdr *shdr = (struct smb2_hdr *)rdata->iov[0].iov_base; struct cifs_credits credits = { .value = 0, .instance = 0, .rreq_debug_id = rdata->rreq->debug_id, .rreq_debug_index = rdata->subreq.debug_index, }; struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], .rq_nvec = 1 }; unsigned int rreq_debug_id = rdata->rreq->debug_id; unsigned int subreq_debug_index = rdata->subreq.debug_index; if (rdata->got_bytes) { rqst.rq_iter = rdata->subreq.io_iter; } WARN_ONCE(rdata->server != mid->server, "rdata server %p != mid server %p", rdata->server, mid->server); cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%zu/%zu\n", __func__, mid->mid, mid->mid_state, rdata->result, rdata->got_bytes, rdata->subreq.len - rdata->subreq.transferred); switch (mid->mid_state) { case MID_RESPONSE_RECEIVED: credits.value = le16_to_cpu(shdr->CreditRequest); credits.instance = server->reconnect_instance; /* result already set, check signature */ if (server->sign && !mid->decrypted) { int rc; iov_iter_truncate(&rqst.rq_iter, rdata->got_bytes); rc = smb2_verify_signature(&rqst, server); if (rc) cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n", rc); } /* FIXME: should this be counted toward the initiating task? */ task_io_account_read(rdata->got_bytes); cifs_stats_bytes_read(tcon, rdata->got_bytes); break; case MID_REQUEST_SUBMITTED: case MID_RETRY_NEEDED: __set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags); rdata->result = -EAGAIN; if (server->sign && rdata->got_bytes) /* reset bytes number since we can not check a sign */ rdata->got_bytes = 0; /* FIXME: should this be counted toward the initiating task? */ task_io_account_read(rdata->got_bytes); cifs_stats_bytes_read(tcon, rdata->got_bytes); break; case MID_RESPONSE_MALFORMED: credits.value = le16_to_cpu(shdr->CreditRequest); credits.instance = server->reconnect_instance; fallthrough; default: rdata->result = -EIO; } #ifdef CONFIG_CIFS_SMB_DIRECT /* * If this rdata has a memory registered, the MR can be freed * MR needs to be freed as soon as I/O finishes to prevent deadlock * because they have limited number and are used for future I/Os */ if (rdata->mr) { smbd_deregister_mr(rdata->mr); rdata->mr = NULL; } #endif if (rdata->result && rdata->result != -ENODATA) { cifs_stats_fail_inc(tcon, SMB2_READ_HE); trace_smb3_read_err(rdata->rreq->debug_id, rdata->subreq.debug_index, rdata->xid, rdata->req->cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, rdata->subreq.start + rdata->subreq.transferred, rdata->subreq.len - rdata->subreq.transferred, rdata->result); } else trace_smb3_read_done(rdata->rreq->debug_id, rdata->subreq.debug_index, rdata->xid, rdata->req->cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, rdata->subreq.start + rdata->subreq.transferred, rdata->got_bytes); if (rdata->result == -ENODATA) { __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); rdata->result = 0; } else { size_t trans = rdata->subreq.transferred + rdata->got_bytes; if (trans < rdata->subreq.len && rdata->subreq.start + trans == ictx->remote_i_size) { __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); rdata->result = 0; } } trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, rdata->credits.value, server->credits, server->in_flight, 0, cifs_trace_rw_credits_read_response_clear); rdata->credits.value = 0; rdata->subreq.transferred += rdata->got_bytes; trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_progress); INIT_WORK(&rdata->subreq.work, smb2_readv_worker); queue_work(cifsiod_wq, &rdata->subreq.work); release_mid(mid); trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, 0, server->credits, server->in_flight, credits.value, cifs_trace_rw_credits_read_response_add); add_credits(server, &credits, 0); } /* smb2_async_readv - send an async read, and set up mid to handle result */ int smb2_async_readv(struct cifs_io_subrequest *rdata) { int rc, flags = 0; char *buf; struct netfs_io_subrequest *subreq = &rdata->subreq; struct smb2_hdr *shdr; struct cifs_io_parms io_parms; struct smb_rqst rqst = { .rq_iov = rdata->iov, .rq_nvec = 1 }; struct TCP_Server_Info *server; struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink); unsigned int total_len; int credit_request; cifs_dbg(FYI, "%s: offset=%llu bytes=%zu\n", __func__, subreq->start, subreq->len); if (!rdata->server) rdata->server = cifs_pick_channel(tcon->ses); io_parms.tcon = tlink_tcon(rdata->req->cfile->tlink); io_parms.server = server = rdata->server; io_parms.offset = subreq->start + subreq->transferred; io_parms.length = subreq->len - subreq->transferred; io_parms.persistent_fid = rdata->req->cfile->fid.persistent_fid; io_parms.volatile_fid = rdata->req->cfile->fid.volatile_fid; io_parms.pid = rdata->req->pid; rc = smb2_new_read_req( (void **) &buf, &total_len, &io_parms, rdata, 0, 0); if (rc) return rc; if (smb3_encryption_required(io_parms.tcon)) flags |= CIFS_TRANSFORM_REQ; rdata->iov[0].iov_base = buf; rdata->iov[0].iov_len = total_len; rdata->got_bytes = 0; rdata->result = 0; shdr = (struct smb2_hdr *)buf; if (rdata->credits.value > 0) { shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(io_parms.length, SMB2_MAX_BUFFER_SIZE)); credit_request = le16_to_cpu(shdr->CreditCharge) + 8; if (server->credits >= server->max_credits) shdr->CreditRequest = cpu_to_le16(0); else shdr->CreditRequest = cpu_to_le16( min_t(int, server->max_credits - server->credits, credit_request)); rc = adjust_credits(server, rdata, cifs_trace_rw_credits_call_readv_adjust); if (rc) goto async_readv_out; flags |= CIFS_HAS_CREDITS; } rc = cifs_call_async(server, &rqst, cifs_readv_receive, smb2_readv_callback, smb3_handle_read_data, rdata, flags, &rdata->credits); if (rc) { cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE); trace_smb3_read_err(rdata->rreq->debug_id, subreq->debug_index, rdata->xid, io_parms.persistent_fid, io_parms.tcon->tid, io_parms.tcon->ses->Suid, io_parms.offset, subreq->len - subreq->transferred, rc); } async_readv_out: cifs_small_buf_release(buf); return rc; } int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, unsigned int *nbytes, char **buf, int *buf_type) { struct smb_rqst rqst; int resp_buftype, rc; struct smb2_read_req *req = NULL; struct smb2_read_rsp *rsp = NULL; struct kvec iov[1]; struct kvec rsp_iov; unsigned int total_len; int flags = CIFS_LOG_ERROR; struct cifs_ses *ses = io_parms->tcon->ses; if (!io_parms->server) io_parms->server = cifs_pick_channel(io_parms->tcon->ses); *nbytes = 0; rc = smb2_new_read_req((void **)&req, &total_len, io_parms, NULL, 0, 0); if (rc) return rc; if (smb3_encryption_required(io_parms->tcon)) flags |= CIFS_TRANSFORM_REQ; iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = iov; rqst.rq_nvec = 1; rc = cifs_send_recv(xid, ses, io_parms->server, &rqst, &resp_buftype, flags, &rsp_iov); rsp = (struct smb2_read_rsp *)rsp_iov.iov_base; if (rc) { if (rc != -ENODATA) { cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE); cifs_dbg(VFS, "Send error in read = %d\n", rc); trace_smb3_read_err(0, 0, xid, req->PersistentFileId, io_parms->tcon->tid, ses->Suid, io_parms->offset, io_parms->length, rc); } else trace_smb3_read_done(0, 0, xid, req->PersistentFileId, io_parms->tcon->tid, ses->Suid, io_parms->offset, 0); free_rsp_buf(resp_buftype, rsp_iov.iov_base); cifs_small_buf_release(req); return rc == -ENODATA ? 0 : rc; } else trace_smb3_read_done(0, 0, xid, req->PersistentFileId, io_parms->tcon->tid, ses->Suid, io_parms->offset, io_parms->length); cifs_small_buf_release(req); *nbytes = le32_to_cpu(rsp->DataLength); if ((*nbytes > CIFS_MAX_MSGSIZE) || (*nbytes > io_parms->length)) { cifs_dbg(FYI, "bad length %d for count %d\n", *nbytes, io_parms->length); rc = -EIO; *nbytes = 0; } if (*buf) { memcpy(*buf, (char *)rsp + rsp->DataOffset, *nbytes); free_rsp_buf(resp_buftype, rsp_iov.iov_base); } else if (resp_buftype != CIFS_NO_BUFFER) { *buf = rsp_iov.iov_base; if (resp_buftype == CIFS_SMALL_BUFFER) *buf_type = CIFS_SMALL_BUFFER; else if (resp_buftype == CIFS_LARGE_BUFFER) *buf_type = CIFS_LARGE_BUFFER; } return rc; } /* * Check the mid_state and signature on received buffer (if any), and queue the * workqueue completion task. */ static void smb2_writev_callback(struct mid_q_entry *mid) { struct cifs_io_subrequest *wdata = mid->callback_data; struct cifs_tcon *tcon = tlink_tcon(wdata->req->cfile->tlink); struct TCP_Server_Info *server = wdata->server; struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf; struct cifs_credits credits = { .value = 0, .instance = 0, .rreq_debug_id = wdata->rreq->debug_id, .rreq_debug_index = wdata->subreq.debug_index, }; unsigned int rreq_debug_id = wdata->rreq->debug_id; unsigned int subreq_debug_index = wdata->subreq.debug_index; ssize_t result = 0; size_t written; WARN_ONCE(wdata->server != mid->server, "wdata server %p != mid server %p", wdata->server, mid->server); switch (mid->mid_state) { case MID_RESPONSE_RECEIVED: credits.value = le16_to_cpu(rsp->hdr.CreditRequest); credits.instance = server->reconnect_instance; result = smb2_check_receive(mid, server, 0); if (result != 0) break; written = le32_to_cpu(rsp->DataLength); /* * Mask off high 16 bits when bytes written as returned * by the server is greater than bytes requested by the * client. OS/2 servers are known to set incorrect * CountHigh values. */ if (written > wdata->subreq.len) written &= 0xFFFF; if (written < wdata->subreq.len) wdata->result = -ENOSPC; else wdata->subreq.len = written; break; case MID_REQUEST_SUBMITTED: case MID_RETRY_NEEDED: result = -EAGAIN; break; case MID_RESPONSE_MALFORMED: credits.value = le16_to_cpu(rsp->hdr.CreditRequest); credits.instance = server->reconnect_instance; fallthrough; default: result = -EIO; break; } #ifdef CONFIG_CIFS_SMB_DIRECT /* * If this wdata has a memory registered, the MR can be freed * The number of MRs available is limited, it's important to recover * used MR as soon as I/O is finished. Hold MR longer in the later * I/O process can possibly result in I/O deadlock due to lack of MR * to send request on I/O retry */ if (wdata->mr) { smbd_deregister_mr(wdata->mr); wdata->mr = NULL; } #endif if (result) { cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); trace_smb3_write_err(wdata->rreq->debug_id, wdata->subreq.debug_index, wdata->xid, wdata->req->cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, wdata->subreq.start, wdata->subreq.len, wdata->result); if (wdata->result == -ENOSPC) pr_warn_once("Out of space writing to %s\n", tcon->tree_name); } else trace_smb3_write_done(wdata->rreq->debug_id, wdata->subreq.debug_index, wdata->xid, wdata->req->cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, wdata->subreq.start, wdata->subreq.len); trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, wdata->credits.value, server->credits, server->in_flight, 0, cifs_trace_rw_credits_write_response_clear); wdata->credits.value = 0; trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_progress); cifs_write_subrequest_terminated(wdata, result ?: written, true); release_mid(mid); trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, 0, server->credits, server->in_flight, credits.value, cifs_trace_rw_credits_write_response_add); add_credits(server, &credits, 0); } /* smb2_async_writev - send an async write, and set up mid to handle result */ void smb2_async_writev(struct cifs_io_subrequest *wdata) { int rc = -EACCES, flags = 0; struct smb2_write_req *req = NULL; struct smb2_hdr *shdr; struct cifs_tcon *tcon = tlink_tcon(wdata->req->cfile->tlink); struct TCP_Server_Info *server = wdata->server; struct kvec iov[1]; struct smb_rqst rqst = { }; unsigned int total_len, xid = wdata->xid; struct cifs_io_parms _io_parms; struct cifs_io_parms *io_parms = NULL; int credit_request; /* * in future we may get cifs_io_parms passed in from the caller, * but for now we construct it here... */ _io_parms = (struct cifs_io_parms) { .tcon = tcon, .server = server, .offset = wdata->subreq.start, .length = wdata->subreq.len, .persistent_fid = wdata->req->cfile->fid.persistent_fid, .volatile_fid = wdata->req->cfile->fid.volatile_fid, .pid = wdata->req->pid, }; io_parms = &_io_parms; rc = smb2_plain_req_init(SMB2_WRITE, tcon, server, (void **) &req, &total_len); if (rc) goto out; rqst.rq_iov = iov; rqst.rq_iter = wdata->subreq.io_iter; rqst.rq_iov[0].iov_len = total_len - 1; rqst.rq_iov[0].iov_base = (char *)req; rqst.rq_nvec += 1; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; shdr = (struct smb2_hdr *)req; shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); req->PersistentFileId = io_parms->persistent_fid; req->VolatileFileId = io_parms->volatile_fid; req->WriteChannelInfoOffset = 0; req->WriteChannelInfoLength = 0; req->Channel = SMB2_CHANNEL_NONE; req->Length = cpu_to_le32(io_parms->length); req->Offset = cpu_to_le64(io_parms->offset); req->DataOffset = cpu_to_le16( offsetof(struct smb2_write_req, Buffer)); req->RemainingBytes = 0; trace_smb3_write_enter(wdata->rreq->debug_id, wdata->subreq.debug_index, wdata->xid, io_parms->persistent_fid, io_parms->tcon->tid, io_parms->tcon->ses->Suid, io_parms->offset, io_parms->length); #ifdef CONFIG_CIFS_SMB_DIRECT /* * If we want to do a server RDMA read, fill in and append * smbd_buffer_descriptor_v1 to the end of write request */ if (smb3_use_rdma_offload(io_parms)) { struct smbd_buffer_descriptor_v1 *v1; bool need_invalidate = server->dialect == SMB30_PROT_ID; wdata->mr = smbd_register_mr(server->smbd_conn, &wdata->subreq.io_iter, false, need_invalidate); if (!wdata->mr) { rc = -EAGAIN; goto async_writev_out; } /* For RDMA read, I/O size is in RemainingBytes not in Length */ req->RemainingBytes = req->Length; req->Length = 0; req->DataOffset = 0; req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; if (need_invalidate) req->Channel = SMB2_CHANNEL_RDMA_V1; req->WriteChannelInfoOffset = cpu_to_le16(offsetof(struct smb2_write_req, Buffer)); req->WriteChannelInfoLength = cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; v1->offset = cpu_to_le64(wdata->mr->mr->iova); v1->token = cpu_to_le32(wdata->mr->mr->rkey); v1->length = cpu_to_le32(wdata->mr->mr->length); rqst.rq_iov[0].iov_len += sizeof(*v1); /* * We keep wdata->subreq.io_iter, * but we have to truncate rqst.rq_iter */ iov_iter_truncate(&rqst.rq_iter, 0); } #endif if (test_bit(NETFS_SREQ_RETRYING, &wdata->subreq.flags)) smb2_set_replay(server, &rqst); cifs_dbg(FYI, "async write at %llu %u bytes iter=%zx\n", io_parms->offset, io_parms->length, iov_iter_count(&wdata->subreq.io_iter)); if (wdata->credits.value > 0) { shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->subreq.len, SMB2_MAX_BUFFER_SIZE)); credit_request = le16_to_cpu(shdr->CreditCharge) + 8; if (server->credits >= server->max_credits) shdr->CreditRequest = cpu_to_le16(0); else shdr->CreditRequest = cpu_to_le16( min_t(int, server->max_credits - server->credits, credit_request)); rc = adjust_credits(server, wdata, cifs_trace_rw_credits_call_writev_adjust); if (rc) goto async_writev_out; flags |= CIFS_HAS_CREDITS; } /* XXX: compression + encryption is unsupported for now */ if (((flags & CIFS_TRANSFORM_REQ) != CIFS_TRANSFORM_REQ) && should_compress(tcon, &rqst)) flags |= CIFS_COMPRESS_REQ; rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL, wdata, flags, &wdata->credits); /* Can't touch wdata if rc == 0 */ if (rc) { trace_smb3_write_err(wdata->rreq->debug_id, wdata->subreq.debug_index, xid, io_parms->persistent_fid, io_parms->tcon->tid, io_parms->tcon->ses->Suid, io_parms->offset, io_parms->length, rc); cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); } async_writev_out: cifs_small_buf_release(req); out: if (rc) { trace_smb3_rw_credits(wdata->rreq->debug_id, wdata->subreq.debug_index, wdata->credits.value, server->credits, server->in_flight, -(int)wdata->credits.value, cifs_trace_rw_credits_write_response_clear); add_credits_and_wake_if(wdata->server, &wdata->credits, 0); cifs_write_subrequest_terminated(wdata, rc, true); } } /* * SMB2_write function gets iov pointer to kvec array with n_vec as a length. * The length field from io_parms must be at least 1 and indicates a number of * elements with data to write that begins with position 1 in iov array. All * data length is specified by count. */ int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, unsigned int *nbytes, struct kvec *iov, int n_vec) { struct smb_rqst rqst; int rc = 0; struct smb2_write_req *req = NULL; struct smb2_write_rsp *rsp = NULL; int resp_buftype; struct kvec rsp_iov; int flags = 0; unsigned int total_len; struct TCP_Server_Info *server; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = 0; *nbytes = 0; if (!io_parms->server) io_parms->server = cifs_pick_channel(io_parms->tcon->ses); server = io_parms->server; if (server == NULL) return -ECONNABORTED; if (n_vec < 1) return rc; rc = smb2_plain_req_init(SMB2_WRITE, io_parms->tcon, server, (void **) &req, &total_len); if (rc) return rc; if (smb3_encryption_required(io_parms->tcon)) flags |= CIFS_TRANSFORM_REQ; req->hdr.Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); req->PersistentFileId = io_parms->persistent_fid; req->VolatileFileId = io_parms->volatile_fid; req->WriteChannelInfoOffset = 0; req->WriteChannelInfoLength = 0; req->Channel = 0; req->Length = cpu_to_le32(io_parms->length); req->Offset = cpu_to_le64(io_parms->offset); req->DataOffset = cpu_to_le16( offsetof(struct smb2_write_req, Buffer)); req->RemainingBytes = 0; trace_smb3_write_enter(0, 0, xid, io_parms->persistent_fid, io_parms->tcon->tid, io_parms->tcon->ses->Suid, io_parms->offset, io_parms->length); iov[0].iov_base = (char *)req; /* 1 for Buffer */ iov[0].iov_len = total_len - 1; memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = iov; rqst.rq_nvec = n_vec + 1; if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, io_parms->tcon->ses, server, &rqst, &resp_buftype, flags, &rsp_iov); rsp = (struct smb2_write_rsp *)rsp_iov.iov_base; if (rc) { trace_smb3_write_err(0, 0, xid, req->PersistentFileId, io_parms->tcon->tid, io_parms->tcon->ses->Suid, io_parms->offset, io_parms->length, rc); cifs_stats_fail_inc(io_parms->tcon, SMB2_WRITE_HE); cifs_dbg(VFS, "Send error in write = %d\n", rc); } else { *nbytes = le32_to_cpu(rsp->DataLength); trace_smb3_write_done(0, 0, xid, req->PersistentFileId, io_parms->tcon->tid, io_parms->tcon->ses->Suid, io_parms->offset, *nbytes); } cifs_small_buf_release(req); free_rsp_buf(resp_buftype, rsp); if (is_replayable_error(rc) && smb2_should_replay(io_parms->tcon, &retries, &cur_sleep)) goto replay_again; return rc; } int posix_info_sid_size(const void *beg, const void *end) { size_t subauth; int total; if (beg + 1 > end) return -1; subauth = *(u8 *)(beg+1); if (subauth < 1 || subauth > 15) return -1; total = 1 + 1 + 6 + 4*subauth; if (beg + total > end) return -1; return total; } int posix_info_parse(const void *beg, const void *end, struct smb2_posix_info_parsed *out) { int total_len = 0; int owner_len, group_len; int name_len; const void *owner_sid; const void *group_sid; const void *name; /* if no end bound given, assume payload to be correct */ if (!end) { const struct smb2_posix_info *p = beg; end = beg + le32_to_cpu(p->NextEntryOffset); /* last element will have a 0 offset, pick a sensible bound */ if (end == beg) end += 0xFFFF; } /* check base buf */ if (beg + sizeof(struct smb2_posix_info) > end) return -1; total_len = sizeof(struct smb2_posix_info); /* check owner sid */ owner_sid = beg + total_len; owner_len = posix_info_sid_size(owner_sid, end); if (owner_len < 0) return -1; total_len += owner_len; /* check group sid */ group_sid = beg + total_len; group_len = posix_info_sid_size(group_sid, end); if (group_len < 0) return -1; total_len += group_len; /* check name len */ if (beg + total_len + 4 > end) return -1; name_len = le32_to_cpu(*(__le32 *)(beg + total_len)); if (name_len < 1 || name_len > 0xFFFF) return -1; total_len += 4; /* check name */ name = beg + total_len; if (name + name_len > end) return -1; total_len += name_len; if (out) { out->base = beg; out->size = total_len; out->name_len = name_len; out->name = name; memcpy(&out->owner, owner_sid, owner_len); memcpy(&out->group, group_sid, group_len); } return total_len; } static int posix_info_extra_size(const void *beg, const void *end) { int len = posix_info_parse(beg, end, NULL); if (len < 0) return -1; return len - sizeof(struct smb2_posix_info); } static unsigned int num_entries(int infotype, char *bufstart, char *end_of_buf, char **lastentry, size_t size) { int len; unsigned int entrycount = 0; unsigned int next_offset = 0; char *entryptr; FILE_DIRECTORY_INFO *dir_info; if (bufstart == NULL) return 0; entryptr = bufstart; while (1) { if (entryptr + next_offset < entryptr || entryptr + next_offset > end_of_buf || entryptr + next_offset + size > end_of_buf) { cifs_dbg(VFS, "malformed search entry would overflow\n"); break; } entryptr = entryptr + next_offset; dir_info = (FILE_DIRECTORY_INFO *)entryptr; if (infotype == SMB_FIND_FILE_POSIX_INFO) len = posix_info_extra_size(entryptr, end_of_buf); else len = le32_to_cpu(dir_info->FileNameLength); if (len < 0 || entryptr + len < entryptr || entryptr + len > end_of_buf || entryptr + len + size > end_of_buf) { cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n", end_of_buf); break; } *lastentry = entryptr; entrycount++; next_offset = le32_to_cpu(dir_info->NextEntryOffset); if (!next_offset) break; } return entrycount; } /* * Readdir/FindFirst */ int SMB2_query_directory_init(const unsigned int xid, struct cifs_tcon *tcon, struct TCP_Server_Info *server, struct smb_rqst *rqst, u64 persistent_fid, u64 volatile_fid, int index, int info_level) { struct smb2_query_directory_req *req; unsigned char *bufptr; __le16 asteriks = cpu_to_le16('*'); unsigned int output_size = CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - MAX_SMB2_CLOSE_RESPONSE_SIZE; unsigned int total_len; struct kvec *iov = rqst->rq_iov; int len, rc; rc = smb2_plain_req_init(SMB2_QUERY_DIRECTORY, tcon, server, (void **) &req, &total_len); if (rc) return rc; switch (info_level) { case SMB_FIND_FILE_DIRECTORY_INFO: req->FileInformationClass = FILE_DIRECTORY_INFORMATION; break; case SMB_FIND_FILE_ID_FULL_DIR_INFO: req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION; break; case SMB_FIND_FILE_POSIX_INFO: req->FileInformationClass = SMB_FIND_FILE_POSIX_INFO; break; case SMB_FIND_FILE_FULL_DIRECTORY_INFO: req->FileInformationClass = FILE_FULL_DIRECTORY_INFORMATION; break; default: cifs_tcon_dbg(VFS, "info level %u isn't supported\n", info_level); return -EINVAL; } req->FileIndex = cpu_to_le32(index); req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; len = 0x2; bufptr = req->Buffer; memcpy(bufptr, &asteriks, len); req->FileNameOffset = cpu_to_le16(sizeof(struct smb2_query_directory_req)); req->FileNameLength = cpu_to_le16(len); /* * BB could be 30 bytes or so longer if we used SMB2 specific * buffer lengths, but this is safe and close enough. */ output_size = min_t(unsigned int, output_size, server->maxBuf); output_size = min_t(unsigned int, output_size, 2 << 15); req->OutputBufferLength = cpu_to_le32(output_size); iov[0].iov_base = (char *)req; /* 1 for Buffer */ iov[0].iov_len = total_len - 1; iov[1].iov_base = (char *)(req->Buffer); iov[1].iov_len = len; trace_smb3_query_dir_enter(xid, persistent_fid, tcon->tid, tcon->ses->Suid, index, output_size); return 0; } void SMB2_query_directory_free(struct smb_rqst *rqst) { if (rqst && rqst->rq_iov) { cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ } } int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov, int resp_buftype, struct cifs_search_info *srch_inf) { struct smb2_query_directory_rsp *rsp; size_t info_buf_size; char *end_of_smb; int rc; rsp = (struct smb2_query_directory_rsp *)rsp_iov->iov_base; switch (srch_inf->info_level) { case SMB_FIND_FILE_DIRECTORY_INFO: info_buf_size = sizeof(FILE_DIRECTORY_INFO); break; case SMB_FIND_FILE_ID_FULL_DIR_INFO: info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO); break; case SMB_FIND_FILE_POSIX_INFO: /* note that posix payload are variable size */ info_buf_size = sizeof(struct smb2_posix_info); break; case SMB_FIND_FILE_FULL_DIRECTORY_INFO: info_buf_size = sizeof(FILE_FULL_DIRECTORY_INFO); break; default: cifs_tcon_dbg(VFS, "info level %u isn't supported\n", srch_inf->info_level); return -EINVAL; } rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), le32_to_cpu(rsp->OutputBufferLength), rsp_iov, info_buf_size); if (rc) { cifs_tcon_dbg(VFS, "bad info payload"); return rc; } srch_inf->unicode = true; if (srch_inf->ntwrk_buf_start) { if (srch_inf->smallBuf) cifs_small_buf_release(srch_inf->ntwrk_buf_start); else cifs_buf_release(srch_inf->ntwrk_buf_start); } srch_inf->ntwrk_buf_start = (char *)rsp; srch_inf->srch_entries_start = srch_inf->last_entry = (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset); end_of_smb = rsp_iov->iov_len + (char *)rsp; srch_inf->entries_in_buffer = num_entries( srch_inf->info_level, srch_inf->srch_entries_start, end_of_smb, &srch_inf->last_entry, info_buf_size); srch_inf->index_of_last_entry += srch_inf->entries_in_buffer; cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n", srch_inf->entries_in_buffer, srch_inf->index_of_last_entry, srch_inf->srch_entries_start, srch_inf->last_entry); if (resp_buftype == CIFS_LARGE_BUFFER) srch_inf->smallBuf = false; else if (resp_buftype == CIFS_SMALL_BUFFER) srch_inf->smallBuf = true; else cifs_tcon_dbg(VFS, "Invalid search buffer type\n"); return 0; } int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, int index, struct cifs_search_info *srch_inf) { struct smb_rqst rqst; struct kvec iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; struct smb2_query_directory_rsp *rsp = NULL; int resp_buftype = CIFS_NO_BUFFER; struct kvec rsp_iov; int rc = 0; struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server; int flags = 0; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = 0; server = cifs_pick_channel(ses); if (!ses || !(ses->server)) return -EIO; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; memset(&rqst, 0, sizeof(struct smb_rqst)); memset(&iov, 0, sizeof(iov)); rqst.rq_iov = iov; rqst.rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; rc = SMB2_query_directory_init(xid, tcon, server, &rqst, persistent_fid, volatile_fid, index, srch_inf->info_level); if (rc) goto qdir_exit; if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base; if (rc) { if (rc == -ENODATA && rsp->hdr.Status == STATUS_NO_MORE_FILES) { trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid, tcon->ses->Suid, index, 0); srch_inf->endOfSearch = true; rc = 0; } else { trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, tcon->ses->Suid, index, 0, rc); cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); } goto qdir_exit; } rc = smb2_parse_query_directory(tcon, &rsp_iov, resp_buftype, srch_inf); if (rc) { trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, tcon->ses->Suid, index, 0, rc); goto qdir_exit; } resp_buftype = CIFS_NO_BUFFER; trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid, tcon->ses->Suid, index, srch_inf->entries_in_buffer); qdir_exit: SMB2_query_directory_free(&rqst); free_rsp_buf(resp_buftype, rsp); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } int SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, struct smb_rqst *rqst, u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class, u8 info_type, u32 additional_info, void **data, unsigned int *size) { struct smb2_set_info_req *req; struct kvec *iov = rqst->rq_iov; unsigned int i, total_len; int rc; rc = smb2_plain_req_init(SMB2_SET_INFO, tcon, server, (void **) &req, &total_len); if (rc) return rc; req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); req->InfoType = info_type; req->FileInfoClass = info_class; req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; req->AdditionalInformation = cpu_to_le32(additional_info); req->BufferOffset = cpu_to_le16(sizeof(struct smb2_set_info_req)); req->BufferLength = cpu_to_le32(*size); memcpy(req->Buffer, *data, *size); total_len += *size; iov[0].iov_base = (char *)req; /* 1 for Buffer */ iov[0].iov_len = total_len - 1; for (i = 1; i < rqst->rq_nvec; i++) { le32_add_cpu(&req->BufferLength, size[i]); iov[i].iov_base = (char *)data[i]; iov[i].iov_len = size[i]; } return 0; } void SMB2_set_info_free(struct smb_rqst *rqst) { if (rqst && rqst->rq_iov) cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ } static int send_set_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class, u8 info_type, u32 additional_info, unsigned int num, void **data, unsigned int *size) { struct smb_rqst rqst; struct smb2_set_info_rsp *rsp = NULL; struct kvec *iov; struct kvec rsp_iov; int rc = 0; int resp_buftype; struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server; int flags = 0; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = 0; server = cifs_pick_channel(ses); if (!ses || !server) return -EIO; if (!num) return -EINVAL; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; iov = kmalloc_array(num, sizeof(struct kvec), GFP_KERNEL); if (!iov) return -ENOMEM; memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = iov; rqst.rq_nvec = num; rc = SMB2_set_info_init(tcon, server, &rqst, persistent_fid, volatile_fid, pid, info_class, info_type, additional_info, data, size); if (rc) { kfree(iov); return rc; } if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); SMB2_set_info_free(&rqst); rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base; if (rc != 0) { cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); trace_smb3_set_info_err(xid, persistent_fid, tcon->tid, ses->Suid, info_class, (__u32)info_type, rc); } free_rsp_buf(resp_buftype, rsp); kfree(iov); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, u32 pid, loff_t new_eof) { struct smb2_file_eof_info info; void *data; unsigned int size; info.EndOfFile = cpu_to_le64(new_eof); data = &info; size = sizeof(struct smb2_file_eof_info); trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, new_eof); return send_set_info(xid, tcon, persistent_fid, volatile_fid, pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE, 0, 1, &data, &size); } int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, struct smb_ntsd *pnntsd, int pacllen, int aclflag) { return send_set_info(xid, tcon, persistent_fid, volatile_fid, current->tgid, 0, SMB2_O_INFO_SECURITY, aclflag, 1, (void **)&pnntsd, &pacllen); } int SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, struct smb2_file_full_ea_info *buf, int len) { return send_set_info(xid, tcon, persistent_fid, volatile_fid, current->tgid, FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, 0, 1, (void **)&buf, &len); } int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, const u64 persistent_fid, const u64 volatile_fid, __u8 oplock_level) { struct smb_rqst rqst; int rc; struct smb2_oplock_break *req = NULL; struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server; int flags = CIFS_OBREAK_OP; unsigned int total_len; struct kvec iov[1]; struct kvec rsp_iov; int resp_buf_type; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = CIFS_OBREAK_OP; server = cifs_pick_channel(ses); cifs_dbg(FYI, "SMB2_oplock_break\n"); rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, (void **) &req, &total_len); if (rc) return rc; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; req->VolatileFid = volatile_fid; req->PersistentFid = persistent_fid; req->OplockLevel = oplock_level; req->hdr.CreditRequest = cpu_to_le16(1); flags |= CIFS_NO_RSP_BUF; iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = iov; rqst.rq_nvec = 1; if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buf_type, flags, &rsp_iov); cifs_small_buf_release(req); if (rc) { cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc); } if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } void smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, struct kstatfs *kst) { kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) * le32_to_cpu(pfs_inf->SectorsPerAllocationUnit); kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits); kst->f_bfree = kst->f_bavail = le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits); return; } static void copy_posix_fs_info_to_kstatfs(FILE_SYSTEM_POSIX_INFO *response_data, struct kstatfs *kst) { kst->f_bsize = le32_to_cpu(response_data->BlockSize); kst->f_blocks = le64_to_cpu(response_data->TotalBlocks); kst->f_bfree = le64_to_cpu(response_data->BlocksAvail); if (response_data->UserBlocksAvail == cpu_to_le64(-1)) kst->f_bavail = kst->f_bfree; else kst->f_bavail = le64_to_cpu(response_data->UserBlocksAvail); if (response_data->TotalFileNodes != cpu_to_le64(-1)) kst->f_files = le64_to_cpu(response_data->TotalFileNodes); if (response_data->FreeFileNodes != cpu_to_le64(-1)) kst->f_ffree = le64_to_cpu(response_data->FreeFileNodes); return; } static int build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, struct TCP_Server_Info *server, int level, int outbuf_len, u64 persistent_fid, u64 volatile_fid) { int rc; struct smb2_query_info_req *req; unsigned int total_len; cifs_dbg(FYI, "Query FSInfo level %d\n", level); if ((tcon->ses == NULL) || server == NULL) return -EIO; rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, (void **) &req, &total_len); if (rc) return rc; req->InfoType = SMB2_O_INFO_FILESYSTEM; req->FileInfoClass = level; req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; /* 1 for pad */ req->InputBufferOffset = cpu_to_le16(sizeof(struct smb2_query_info_req)); req->OutputBufferLength = cpu_to_le32( outbuf_len + sizeof(struct smb2_query_info_rsp)); iov->iov_base = (char *)req; iov->iov_len = total_len; return 0; } static inline void free_qfs_info_req(struct kvec *iov) { cifs_buf_release(iov->iov_base); } int SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) { struct smb_rqst rqst; struct smb2_query_info_rsp *rsp = NULL; struct kvec iov; struct kvec rsp_iov; int rc = 0; int resp_buftype; struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server; FILE_SYSTEM_POSIX_INFO *info = NULL; int flags = 0; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = 0; server = cifs_pick_channel(ses); rc = build_qfs_info_req(&iov, tcon, server, FS_POSIX_INFORMATION, sizeof(FILE_SYSTEM_POSIX_INFO), persistent_fid, volatile_fid); if (rc) return rc; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = &iov; rqst.rq_nvec = 1; if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); free_qfs_info_req(&iov); if (rc) { cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); goto posix_qfsinf_exit; } rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; info = (FILE_SYSTEM_POSIX_INFO *)( le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, sizeof(FILE_SYSTEM_POSIX_INFO)); if (!rc) copy_posix_fs_info_to_kstatfs(info, fsdata); posix_qfsinf_exit: free_rsp_buf(resp_buftype, rsp_iov.iov_base); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) { struct smb_rqst rqst; struct smb2_query_info_rsp *rsp = NULL; struct kvec iov; struct kvec rsp_iov; int rc = 0; int resp_buftype; struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server; struct smb2_fs_full_size_info *info = NULL; int flags = 0; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = 0; server = cifs_pick_channel(ses); rc = build_qfs_info_req(&iov, tcon, server, FS_FULL_SIZE_INFORMATION, sizeof(struct smb2_fs_full_size_info), persistent_fid, volatile_fid); if (rc) return rc; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = &iov; rqst.rq_nvec = 1; if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); free_qfs_info_req(&iov); if (rc) { cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); goto qfsinf_exit; } rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; info = (struct smb2_fs_full_size_info *)( le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, sizeof(struct smb2_fs_full_size_info)); if (!rc) smb2_copy_fs_info_to_kstatfs(info, fsdata); qfsinf_exit: free_rsp_buf(resp_buftype, rsp_iov.iov_base); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, int level) { struct smb_rqst rqst; struct smb2_query_info_rsp *rsp = NULL; struct kvec iov; struct kvec rsp_iov; int rc = 0; int resp_buftype, max_len, min_len; struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server; unsigned int rsp_len, offset; int flags = 0; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = 0; server = cifs_pick_channel(ses); if (level == FS_DEVICE_INFORMATION) { max_len = sizeof(FILE_SYSTEM_DEVICE_INFO); min_len = sizeof(FILE_SYSTEM_DEVICE_INFO); } else if (level == FS_ATTRIBUTE_INFORMATION) { max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); min_len = MIN_FS_ATTR_INFO_SIZE; } else if (level == FS_SECTOR_SIZE_INFORMATION) { max_len = sizeof(struct smb3_fs_ss_info); min_len = sizeof(struct smb3_fs_ss_info); } else if (level == FS_VOLUME_INFORMATION) { max_len = sizeof(struct smb3_fs_vol_info) + MAX_VOL_LABEL_LEN; min_len = sizeof(struct smb3_fs_vol_info); } else { cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); return -EINVAL; } rc = build_qfs_info_req(&iov, tcon, server, level, max_len, persistent_fid, volatile_fid); if (rc) return rc; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = &iov; rqst.rq_nvec = 1; if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); free_qfs_info_req(&iov); if (rc) { cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); goto qfsattr_exit; } rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; rsp_len = le32_to_cpu(rsp->OutputBufferLength); offset = le16_to_cpu(rsp->OutputBufferOffset); rc = smb2_validate_iov(offset, rsp_len, &rsp_iov, min_len); if (rc) goto qfsattr_exit; if (level == FS_ATTRIBUTE_INFORMATION) memcpy(&tcon->fsAttrInfo, offset + (char *)rsp, min_t(unsigned int, rsp_len, max_len)); else if (level == FS_DEVICE_INFORMATION) memcpy(&tcon->fsDevInfo, offset + (char *)rsp, sizeof(FILE_SYSTEM_DEVICE_INFO)); else if (level == FS_SECTOR_SIZE_INFORMATION) { struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *) (offset + (char *)rsp); tcon->ss_flags = le32_to_cpu(ss_info->Flags); tcon->perf_sector_size = le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); } else if (level == FS_VOLUME_INFORMATION) { struct smb3_fs_vol_info *vol_info = (struct smb3_fs_vol_info *) (offset + (char *)rsp); tcon->vol_serial_number = vol_info->VolumeSerialNumber; tcon->vol_create_time = vol_info->VolumeCreationTime; } qfsattr_exit: free_rsp_buf(resp_buftype, rsp_iov.iov_base); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, const __u32 num_lock, struct smb2_lock_element *buf) { struct smb_rqst rqst; int rc = 0; struct smb2_lock_req *req = NULL; struct kvec iov[2]; struct kvec rsp_iov; int resp_buf_type; unsigned int count; int flags = CIFS_NO_RSP_BUF; unsigned int total_len; struct TCP_Server_Info *server; int retries = 0, cur_sleep = 1; replay_again: /* reinitialize for possible replay */ flags = CIFS_NO_RSP_BUF; server = cifs_pick_channel(tcon->ses); cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock); rc = smb2_plain_req_init(SMB2_LOCK, tcon, server, (void **) &req, &total_len); if (rc) return rc; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); req->LockCount = cpu_to_le16(num_lock); req->PersistentFileId = persist_fid; req->VolatileFileId = volatile_fid; count = num_lock * sizeof(struct smb2_lock_element); iov[0].iov_base = (char *)req; iov[0].iov_len = total_len - sizeof(struct smb2_lock_element); iov[1].iov_base = (char *)buf; iov[1].iov_len = count; cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = iov; rqst.rq_nvec = 2; if (retries) smb2_set_replay(server, &rqst); rc = cifs_send_recv(xid, tcon->ses, server, &rqst, &resp_buf_type, flags, &rsp_iov); cifs_small_buf_release(req); if (rc) { cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc); cifs_stats_fail_inc(tcon, SMB2_LOCK_HE); trace_smb3_lock_err(xid, persist_fid, tcon->tid, tcon->ses->Suid, rc); } if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; return rc; } int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, const __u64 length, const __u64 offset, const __u32 lock_flags, const bool wait) { struct smb2_lock_element lock; lock.Offset = cpu_to_le64(offset); lock.Length = cpu_to_le64(length); lock.Flags = cpu_to_le32(lock_flags); if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK) lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY); return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock); } int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, __u8 *lease_key, const __le32 lease_state) { struct smb_rqst rqst; int rc; struct smb2_lease_ack *req = NULL; struct cifs_ses *ses = tcon->ses; int flags = CIFS_OBREAK_OP; unsigned int total_len; struct kvec iov[1]; struct kvec rsp_iov; int resp_buf_type; __u64 *please_key_high; __u64 *please_key_low; struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); cifs_dbg(FYI, "SMB2_lease_break\n"); rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, (void **) &req, &total_len); if (rc) return rc; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; req->hdr.CreditRequest = cpu_to_le16(1); req->StructureSize = cpu_to_le16(36); total_len += 12; memcpy(req->LeaseKey, lease_key, 16); req->LeaseState = lease_state; flags |= CIFS_NO_RSP_BUF; iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = iov; rqst.rq_nvec = 1; rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buf_type, flags, &rsp_iov); cifs_small_buf_release(req); please_key_low = (__u64 *)lease_key; please_key_high = (__u64 *)(lease_key+8); if (rc) { cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); trace_smb3_lease_err(le32_to_cpu(lease_state), tcon->tid, ses->Suid, *please_key_low, *please_key_high, rc); cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc); } else trace_smb3_lease_done(le32_to_cpu(lease_state), tcon->tid, ses->Suid, *please_key_low, *please_key_high); return rc; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1