| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455 | diff -Nur linux-2.6.31.5.orig/Documentation/ABI/testing/debugfs-aufs linux-2.6.31.5/Documentation/ABI/testing/debugfs-aufs--- linux-2.6.31.5.orig/Documentation/ABI/testing/debugfs-aufs	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/Documentation/ABI/testing/debugfs-aufs	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,40 @@+What:		/debug/aufs/si_<id>/+Date:		March 2009+Contact:	J. R. Okajima <hooanon05@yahoo.co.jp>+Description:+		Under /debug/aufs, a directory named si_<id> is created+		per aufs mount, where <id> is a unique id generated+		internally.++What:		/debug/aufs/si_<id>/xib+Date:		March 2009+Contact:	J. R. Okajima <hooanon05@yahoo.co.jp>+Description:+		It shows the consumed blocks by xib (External Inode Number+		Bitmap), its block size and file size.+		When the aufs mount option 'noxino' is specified, it+		will be empty. About XINO files, see+		Documentation/filesystems/aufs/aufs.5 in detail.++What:		/debug/aufs/si_<id>/xino0, xino1 ... xinoN+Date:		March 2009+Contact:	J. R. Okajima <hooanon05@yahoo.co.jp>+Description:+		It shows the consumed blocks by xino (External Inode Number+		Translation Table), its link count, block size and file+		size.+		When the aufs mount option 'noxino' is specified, it+		will be empty. About XINO files, see+		Documentation/filesystems/aufs/aufs.5 in detail.++What:		/debug/aufs/si_<id>/xigen+Date:		March 2009+Contact:	J. R. Okajima <hooanon05@yahoo.co.jp>+Description:+		It shows the consumed blocks by xigen (External Inode+		Generation Table), its block size and file size.+		If CONFIG_AUFS_EXPORT is disabled, this entry will not+		be created.+		When the aufs mount option 'noxino' is specified, it+		will be empty. About XINO files, see+		Documentation/filesystems/aufs/aufs.5 in detail.diff -Nur linux-2.6.31.5.orig/Documentation/ABI/testing/sysfs-aufs linux-2.6.31.5/Documentation/ABI/testing/sysfs-aufs--- linux-2.6.31.5.orig/Documentation/ABI/testing/sysfs-aufs	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/Documentation/ABI/testing/sysfs-aufs	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,25 @@+What:		/sys/fs/aufs/si_<id>/+Date:		March 2009+Contact:	J. R. Okajima <hooanon05@yahoo.co.jp>+Description:+		Under /sys/fs/aufs, a directory named si_<id> is created+		per aufs mount, where <id> is a unique id generated+		internally.++What:		/sys/fs/aufs/si_<id>/br0, br1 ... brN+Date:		March 2009+Contact:	J. R. Okajima <hooanon05@yahoo.co.jp>+Description:+		It shows the abolute path of a member directory (which+		is called branch) in aufs, and its permission.++What:		/sys/fs/aufs/si_<id>/xi_path+Date:		March 2009+Contact:	J. R. Okajima <hooanon05@yahoo.co.jp>+Description:+		It shows the abolute path of XINO (External Inode Number+		Bitmap, Translation Table and Generation Table) file+		even if it is the default path.+		When the aufs mount option 'noxino' is specified, it+		will be empty. About XINO files, see+		Documentation/filesystems/aufs/aufs.5 in detail.diff -Nur linux-2.6.31.5.orig/fs/aufs/aufs.h linux-2.6.31.5/fs/aufs/aufs.h--- linux-2.6.31.5.orig/fs/aufs/aufs.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/aufs.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,51 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * all header files+ */++#ifndef __AUFS_H__+#define __AUFS_H__++#ifdef __KERNEL__++#include "debug.h"++#include "branch.h"+#include "cpup.h"+#include "dcsub.h"+#include "dbgaufs.h"+#include "dentry.h"+#include "dir.h"+#include "file.h"+#include "fstype.h"+#include "inode.h"+#include "loop.h"+#include "module.h"+#include "opts.h"+#include "rwsem.h"+#include "spl.h"+#include "super.h"+#include "sysaufs.h"+#include "vfsub.h"+#include "whout.h"+#include "wkq.h"++#endif /* __KERNEL__ */+#endif /* __AUFS_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/branch.c linux-2.6.31.5/fs/aufs/branch.c--- linux-2.6.31.5.orig/fs/aufs/branch.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/branch.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,974 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * branch management+ */++#include <linux/file.h>+#include "aufs.h"++/*+ * free a single branch+ */+static void au_br_do_free(struct au_branch *br)+{+	int i;+	struct au_wbr *wbr;++	if (br->br_xino.xi_file)+		fput(br->br_xino.xi_file);+	mutex_destroy(&br->br_xino.xi_nondir_mtx);++	AuDebugOn(atomic_read(&br->br_count));++	wbr = br->br_wbr;+	if (wbr) {+		for (i = 0; i < AuBrWh_Last; i++)+			dput(wbr->wbr_wh[i]);+		AuDebugOn(atomic_read(&wbr->wbr_wh_running));+		AuRwDestroy(&wbr->wbr_wh_rwsem);+	}++	/* some filesystems acquire extra lock */+	lockdep_off();+	mntput(br->br_mnt);+	lockdep_on();++	kfree(wbr);+	kfree(br);+}++/*+ * frees all branches+ */+void au_br_free(struct au_sbinfo *sbinfo)+{+	aufs_bindex_t bmax;+	struct au_branch **br;++	AuRwMustWriteLock(&sbinfo->si_rwsem);++	bmax = sbinfo->si_bend + 1;+	br = sbinfo->si_branch;+	while (bmax--)+		au_br_do_free(*br++);+}++/*+ * find the index of a branch which is specified by @br_id.+ */+int au_br_index(struct super_block *sb, aufs_bindex_t br_id)+{+	aufs_bindex_t bindex, bend;++	bend = au_sbend(sb);+	for (bindex = 0; bindex <= bend; bindex++)+		if (au_sbr_id(sb, bindex) == br_id)+			return bindex;+	return -1;+}++/* ---------------------------------------------------------------------- */++/*+ * add a branch+ */++static int test_overlap(struct super_block *sb, struct dentry *h_d1,+			struct dentry *h_d2)+{+	if (unlikely(h_d1 == h_d2))+		return 1;+	return !!au_test_subdir(h_d1, h_d2)+		|| !!au_test_subdir(h_d2, h_d1)+		|| au_test_loopback_overlap(sb, h_d1, h_d2)+		|| au_test_loopback_overlap(sb, h_d2, h_d1);+}++/*+ * returns a newly allocated branch. @new_nbranch is a number of branches+ * after adding a branch.+ */+static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch,+				     int perm)+{+	struct au_branch *add_branch;+	struct dentry *root;++	root = sb->s_root;+	add_branch = kmalloc(sizeof(*add_branch), GFP_NOFS);+	if (unlikely(!add_branch))+		goto out;++	add_branch->br_wbr = NULL;+	if (au_br_writable(perm)) {+		/* may be freed separately at changing the branch permission */+		add_branch->br_wbr = kmalloc(sizeof(*add_branch->br_wbr),+					     GFP_NOFS);+		if (unlikely(!add_branch->br_wbr))+			goto out_br;+	}++	if (unlikely(au_sbr_realloc(au_sbi(sb), new_nbranch)+		     || au_di_realloc(au_di(root), new_nbranch)+		     || au_ii_realloc(au_ii(root->d_inode), new_nbranch)))+		goto out_wbr;+	return add_branch; /* success */++ out_wbr:+	kfree(add_branch->br_wbr);+ out_br:+	kfree(add_branch);+ out:+	return ERR_PTR(-ENOMEM);+}++/*+ * test if the branch permission is legal or not.+ */+static int test_br(struct inode *inode, int brperm, char *path)+{+	int err;++	err = 0;+	if (unlikely(au_br_writable(brperm) && IS_RDONLY(inode))) {+		AuErr("write permission for readonly mount or inode, %s\n",+		      path);+		err = -EINVAL;+	}++	return err;+}++/*+ * returns:+ * 0: success, the caller will add it+ * plus: success, it is already unified, the caller should ignore it+ * minus: error+ */+static int test_add(struct super_block *sb, struct au_opt_add *add, int remount)+{+	int err;+	aufs_bindex_t bend, bindex;+	struct dentry *root;+	struct inode *inode, *h_inode;++	root = sb->s_root;+	bend = au_sbend(sb);+	if (unlikely(bend >= 0+		     && au_find_dbindex(root, add->path.dentry) >= 0)) {+		err = 1;+		if (!remount) {+			err = -EINVAL;+			AuErr("%s duplicated\n", add->pathname);+		}+		goto out;+	}++	err = -ENOSPC; /* -E2BIG; */+	if (unlikely(AUFS_BRANCH_MAX <= add->bindex+		     || AUFS_BRANCH_MAX - 1 <= bend)) {+		AuErr("number of branches exceeded %s\n", add->pathname);+		goto out;+	}++	err = -EDOM;+	if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) {+		AuErr("bad index %d\n", add->bindex);+		goto out;+	}++	inode = add->path.dentry->d_inode;+	err = -ENOENT;+	if (unlikely(!inode->i_nlink)) {+		AuErr("no existence %s\n", add->pathname);+		goto out;+	}++	err = -EINVAL;+	if (unlikely(inode->i_sb == sb)) {+		AuErr("%s must be outside\n", add->pathname);+		goto out;+	}++	if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) {+		AuErr("unsupported filesystem, %s (%s)\n",+		      add->pathname, au_sbtype(inode->i_sb));+		goto out;+	}++	err = test_br(add->path.dentry->d_inode, add->perm, add->pathname);+	if (unlikely(err))+		goto out;++	if (bend < 0)+		return 0; /* success */++	err = -EINVAL;+	for (bindex = 0; bindex <= bend; bindex++)+		if (unlikely(test_overlap(sb, add->path.dentry,+					  au_h_dptr(root, bindex)))) {+			AuErr("%s is overlapped\n", add->pathname);+			goto out;+		}++	err = 0;+	if (au_opt_test(au_mntflags(sb), WARN_PERM)) {+		h_inode = au_h_dptr(root, 0)->d_inode;+		if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO)+		    || h_inode->i_uid != inode->i_uid+		    || h_inode->i_gid != inode->i_gid)+			AuWarn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n",+			       add->pathname,+			       inode->i_uid, inode->i_gid,+			       (inode->i_mode & S_IALLUGO),+			       h_inode->i_uid, h_inode->i_gid,+			       (h_inode->i_mode & S_IALLUGO));+	}++ out:+	return err;+}++/*+ * initialize or clean the whiteouts for an adding branch+ */+static int au_br_init_wh(struct super_block *sb, struct au_branch *br,+			 int new_perm, struct dentry *h_root)+{+	int err, old_perm;+	aufs_bindex_t bindex;+	struct mutex *h_mtx;+	struct au_wbr *wbr;+	struct au_hinode *hdir;++	wbr = br->br_wbr;+	old_perm = br->br_perm;+	br->br_perm = new_perm;+	hdir = NULL;+	h_mtx = NULL;+	bindex = au_br_index(sb, br->br_id);+	if (0 <= bindex) {+		hdir = au_hi(sb->s_root->d_inode, bindex);+		au_hin_imtx_lock_nested(hdir, AuLsc_I_PARENT);+	} else {+		h_mtx = &h_root->d_inode->i_mutex;+		mutex_lock_nested(h_mtx, AuLsc_I_PARENT);+	}+	if (!wbr)+		err = au_wh_init(h_root, br, sb);+	else {+		wbr_wh_write_lock(wbr);+		err = au_wh_init(h_root, br, sb);+		wbr_wh_write_unlock(wbr);+	}+	if (hdir)+		au_hin_imtx_unlock(hdir);+	else+		mutex_unlock(h_mtx);+	br->br_perm = old_perm;++	if (!err && wbr && !au_br_writable(new_perm)) {+		kfree(wbr);+		br->br_wbr = NULL;+	}++	return err;+}++static int au_wbr_init(struct au_branch *br, struct super_block *sb,+		       int perm, struct path *path)+{+	int err;+	struct au_wbr *wbr;++	wbr = br->br_wbr;+	au_rw_init(&wbr->wbr_wh_rwsem);+	memset(wbr->wbr_wh, 0, sizeof(wbr->wbr_wh));+	atomic_set(&wbr->wbr_wh_running, 0);+	wbr->wbr_bytes = 0;++	err = au_br_init_wh(sb, br, perm, path->dentry);++	return err;+}++/* intialize a new branch */+static int au_br_init(struct au_branch *br, struct super_block *sb,+		      struct au_opt_add *add)+{+	int err;++	err = 0;+	memset(&br->br_xino, 0, sizeof(br->br_xino));+	mutex_init(&br->br_xino.xi_nondir_mtx);+	br->br_perm = add->perm;+	br->br_mnt = add->path.mnt; /* set first, mntget() later */+	atomic_set(&br->br_count, 0);+	br->br_xino_upper = AUFS_XINO_TRUNC_INIT;+	atomic_set(&br->br_xino_running, 0);+	br->br_id = au_new_br_id(sb);++	if (au_br_writable(add->perm)) {+		err = au_wbr_init(br, sb, add->perm, &add->path);+		if (unlikely(err))+			goto out;+	}++	if (au_opt_test(au_mntflags(sb), XINO)) {+		err = au_xino_br(sb, br, add->path.dentry->d_inode->i_ino,+				 au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1);+		if (unlikely(err)) {+			AuDebugOn(br->br_xino.xi_file);+			goto out;+		}+	}++	sysaufs_br_init(br);+	mntget(add->path.mnt);++ out:+	return err;+}++static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex,+			     struct au_branch *br, aufs_bindex_t bend,+			     aufs_bindex_t amount)+{+	struct au_branch **brp;++	AuRwMustWriteLock(&sbinfo->si_rwsem);++	brp = sbinfo->si_branch + bindex;+	memmove(brp + 1, brp, sizeof(*brp) * amount);+	*brp = br;+	sbinfo->si_bend++;+	if (unlikely(bend < 0))+		sbinfo->si_bend = 0;+}++static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex,+			     aufs_bindex_t bend, aufs_bindex_t amount)+{+	struct au_hdentry *hdp;++	AuRwMustWriteLock(&dinfo->di_rwsem);++	hdp = dinfo->di_hdentry + bindex;+	memmove(hdp + 1, hdp, sizeof(*hdp) * amount);+	au_h_dentry_init(hdp);+	dinfo->di_bend++;+	if (unlikely(bend < 0))+		dinfo->di_bstart = 0;+}++static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex,+			     aufs_bindex_t bend, aufs_bindex_t amount)+{+	struct au_hinode *hip;++	AuRwMustWriteLock(&iinfo->ii_rwsem);++	hip = iinfo->ii_hinode + bindex;+	memmove(hip + 1, hip, sizeof(*hip) * amount);+	hip->hi_inode = NULL;+	au_hin_init(hip, NULL);+	iinfo->ii_bend++;+	if (unlikely(bend < 0))+		iinfo->ii_bstart = 0;+}++static void au_br_do_add(struct super_block *sb, struct dentry *h_dentry,+			 struct au_branch *br, aufs_bindex_t bindex)+{+	struct dentry *root;+	struct inode *root_inode;+	aufs_bindex_t bend, amount;++	root = sb->s_root;+	root_inode = root->d_inode;+	au_plink_block_maintain(sb);+	bend = au_sbend(sb);+	amount = bend + 1 - bindex;+	au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount);+	au_br_do_add_hdp(au_di(root), bindex, bend, amount);+	au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount);+	au_set_h_dptr(root, bindex, dget(h_dentry));+	au_set_h_iptr(root_inode, bindex, au_igrab(h_dentry->d_inode),+		      /*flags*/0);+}++int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount)+{+	int err;+	unsigned long long maxb;+	aufs_bindex_t bend, add_bindex;+	struct dentry *root, *h_dentry;+	struct inode *root_inode;+	struct au_branch *add_branch;++	root = sb->s_root;+	root_inode = root->d_inode;+	IMustLock(root_inode);+	err = test_add(sb, add, remount);+	if (unlikely(err < 0))+		goto out;+	if (err) {+		err = 0;+		goto out; /* success */+	}++	bend = au_sbend(sb);+	add_branch = au_br_alloc(sb, bend + 2, add->perm);+	err = PTR_ERR(add_branch);+	if (IS_ERR(add_branch))+		goto out;++	err = au_br_init(add_branch, sb, add);+	if (unlikely(err)) {+		au_br_do_free(add_branch);+		goto out;+	}++	add_bindex = add->bindex;+	h_dentry = add->path.dentry;+	if (!remount)+		au_br_do_add(sb, h_dentry, add_branch, add_bindex);+	else {+		sysaufs_brs_del(sb, add_bindex);+		au_br_do_add(sb, h_dentry, add_branch, add_bindex);+		sysaufs_brs_add(sb, add_bindex);+	}++	if (!add_bindex)+		au_cpup_attr_all(root_inode, /*force*/1);+	else+		au_add_nlink(root_inode, h_dentry->d_inode);+	maxb = h_dentry->d_sb->s_maxbytes;+	if (sb->s_maxbytes < maxb)+		sb->s_maxbytes = maxb;++	/*+	 * this test/set prevents aufs from handling unnecesary inotify events+	 * of xino files, in a case of re-adding a writable branch which was+	 * once detached from aufs.+	 */+	if (au_xino_brid(sb) < 0+	    && au_br_writable(add_branch->br_perm)+	    && !au_test_fs_bad_xino(h_dentry->d_sb)+	    && add_branch->br_xino.xi_file+	    && add_branch->br_xino.xi_file->f_dentry->d_parent == h_dentry)+		au_xino_brid_set(sb, add_branch->br_id);++ out:+	return err;+}++/* ---------------------------------------------------------------------- */++/*+ * delete a branch+ */++/* to show the line number, do not make it inlined function */+#define AuVerbose(do_info, fmt, args...) do { \+	if (do_info) \+		AuInfo(fmt, ##args); \+} while (0)++/*+ * test if the branch is deletable or not.+ */+static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex,+			    unsigned int sigen)+{+	int err, i, j, ndentry;+	aufs_bindex_t bstart, bend;+	unsigned char verbose;+	struct au_dcsub_pages dpages;+	struct au_dpage *dpage;+	struct dentry *d;+	struct inode *inode;++	err = au_dpages_init(&dpages, GFP_NOFS);+	if (unlikely(err))+		goto out;+	err = au_dcsub_pages(&dpages, root, NULL, NULL);+	if (unlikely(err))+		goto out_dpages;++	verbose = !!au_opt_test(au_mntflags(root->d_sb), VERBOSE);+	for (i = 0; !err && i < dpages.ndpage; i++) {+		dpage = dpages.dpages + i;+		ndentry = dpage->ndentry;+		for (j = 0; !err && j < ndentry; j++) {+			d = dpage->dentries[j];+			AuDebugOn(!atomic_read(&d->d_count));+			inode = d->d_inode;+			if (au_digen(d) == sigen && au_iigen(inode) == sigen)+				di_read_lock_child(d, AuLock_IR);+			else {+				di_write_lock_child(d);+				err = au_reval_dpath(d, sigen);+				if (!err)+					di_downgrade_lock(d, AuLock_IR);+				else {+					di_write_unlock(d);+					break;+				}+			}++			bstart = au_dbstart(d);+			bend = au_dbend(d);+			if (bstart <= bindex+			    && bindex <= bend+			    && au_h_dptr(d, bindex)+			    && (!S_ISDIR(inode->i_mode) || bstart == bend)) {+				err = -EBUSY;+				AuVerbose(verbose, "busy %.*s\n", AuDLNPair(d));+			}+			di_read_unlock(d, AuLock_IR);+		}+	}++ out_dpages:+	au_dpages_free(&dpages);+ out:+	return err;+}++static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex,+			   unsigned int sigen)+{+	int err;+	struct inode *i;+	aufs_bindex_t bstart, bend;+	unsigned char verbose;++	err = 0;+	verbose = !!au_opt_test(au_mntflags(sb), VERBOSE);+	list_for_each_entry(i, &sb->s_inodes, i_sb_list) {+		AuDebugOn(!atomic_read(&i->i_count));+		if (!list_empty(&i->i_dentry))+			continue;++		if (au_iigen(i) == sigen)+			ii_read_lock_child(i);+		else {+			ii_write_lock_child(i);+			err = au_refresh_hinode_self(i, /*do_attr*/1);+			if (!err)+				ii_downgrade_lock(i);+			else {+				ii_write_unlock(i);+				break;+			}+		}++		bstart = au_ibstart(i);+		bend = au_ibend(i);+		if (bstart <= bindex+		    && bindex <= bend+		    && au_h_iptr(i, bindex)+		    && (!S_ISDIR(i->i_mode) || bstart == bend)) {+			err = -EBUSY;+			AuVerbose(verbose, "busy i%lu\n", i->i_ino);+			ii_read_unlock(i);+			break;+		}+		ii_read_unlock(i);+	}++	return err;+}++static int test_children_busy(struct dentry *root, aufs_bindex_t bindex)+{+	int err;+	unsigned int sigen;++	sigen = au_sigen(root->d_sb);+	DiMustNoWaiters(root);+	IiMustNoWaiters(root->d_inode);+	di_write_unlock(root);+	err = test_dentry_busy(root, bindex, sigen);+	if (!err)+		err = test_inode_busy(root->d_sb, bindex, sigen);+	di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */++	return err;+}++static void au_br_do_del_brp(struct au_sbinfo *sbinfo,+			     const aufs_bindex_t bindex,+			     const aufs_bindex_t bend)+{+	struct au_branch **brp, **p;++	AuRwMustWriteLock(&sbinfo->si_rwsem);++	brp = sbinfo->si_branch + bindex;+	if (bindex < bend)+		memmove(brp, brp + 1, sizeof(*brp) * (bend - bindex));+	sbinfo->si_branch[0 + bend] = NULL;+	sbinfo->si_bend--;++	p = krealloc(sbinfo->si_branch, sizeof(*p) * bend, GFP_NOFS);+	if (p)+		sbinfo->si_branch = p;+}++static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex,+			     const aufs_bindex_t bend)+{+	struct au_hdentry *hdp, *p;++	AuRwMustWriteLock(&dinfo->di_rwsem);++	hdp = dinfo->di_hdentry + bindex;+	if (bindex < bend)+		memmove(hdp, hdp + 1, sizeof(*hdp) * (bend - bindex));+	dinfo->di_hdentry[0 + bend].hd_dentry = NULL;+	dinfo->di_bend--;++	p = krealloc(dinfo->di_hdentry, sizeof(*p) * bend, GFP_NOFS);+	if (p)+		dinfo->di_hdentry = p;+}++static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex,+			     const aufs_bindex_t bend)+{+	struct au_hinode *hip, *p;++	AuRwMustWriteLock(&iinfo->ii_rwsem);++	hip = iinfo->ii_hinode + bindex;+	if (bindex < bend)+		memmove(hip, hip + 1, sizeof(*hip) * (bend - bindex));+	iinfo->ii_hinode[0 + bend].hi_inode = NULL;+	au_hin_init(iinfo->ii_hinode + bend, NULL);+	iinfo->ii_bend--;++	p = krealloc(iinfo->ii_hinode, sizeof(*p) * bend, GFP_NOFS);+	if (p)+		iinfo->ii_hinode = p;+}++static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex,+			 struct au_branch *br)+{+	aufs_bindex_t bend;+	struct au_sbinfo *sbinfo;+	struct dentry *root;+	struct inode *inode;++	SiMustWriteLock(sb);++	root = sb->s_root;+	inode = root->d_inode;+	au_plink_block_maintain(sb);+	sbinfo = au_sbi(sb);+	bend = sbinfo->si_bend;++	dput(au_h_dptr(root, bindex));+	au_hiput(au_hi(inode, bindex));+	au_br_do_free(br);++	au_br_do_del_brp(sbinfo, bindex, bend);+	au_br_do_del_hdp(au_di(root), bindex, bend);+	au_br_do_del_hip(au_ii(inode), bindex, bend);+}++int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount)+{+	int err, rerr, i;+	unsigned int mnt_flags;+	aufs_bindex_t bindex, bend, br_id;+	unsigned char do_wh, verbose;+	struct au_branch *br;+	struct au_wbr *wbr;++	err = 0;+	bindex = au_find_dbindex(sb->s_root, del->h_path.dentry);+	if (bindex < 0) {+		if (remount)+			goto out; /* success */+		err = -ENOENT;+		AuErr("%s no such branch\n", del->pathname);+		goto out;+	}+	AuDbg("bindex b%d\n", bindex);++	err = -EBUSY;+	mnt_flags = au_mntflags(sb);+	verbose = !!au_opt_test(mnt_flags, VERBOSE);+	bend = au_sbend(sb);+	if (unlikely(!bend)) {+		AuVerbose(verbose, "no more branches left\n");+		goto out;+	}+	br = au_sbr(sb, bindex);+	i = atomic_read(&br->br_count);+	if (unlikely(i)) {+		AuVerbose(verbose, "%d file(s) opened\n", i);+		goto out;+	}++	wbr = br->br_wbr;+	do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph);+	if (do_wh) {+		/* instead of WbrWhMustWriteLock(wbr) */+		SiMustWriteLock(sb);+		for (i = 0; i < AuBrWh_Last; i++) {+			dput(wbr->wbr_wh[i]);+			wbr->wbr_wh[i] = NULL;+		}+	}++	err = test_children_busy(sb->s_root, bindex);+	if (unlikely(err)) {+		if (do_wh)+			goto out_wh;+		goto out;+	}++	err = 0;+	br_id = br->br_id;+	if (!remount)+		au_br_do_del(sb, bindex, br);+	else {+		sysaufs_brs_del(sb, bindex);+		au_br_do_del(sb, bindex, br);+		sysaufs_brs_add(sb, bindex);+	}++	if (!bindex)+		au_cpup_attr_all(sb->s_root->d_inode, /*force*/1);+	else+		au_sub_nlink(sb->s_root->d_inode, del->h_path.dentry->d_inode);+	if (au_opt_test(mnt_flags, PLINK))+		au_plink_half_refresh(sb, br_id);++	if (sb->s_maxbytes == del->h_path.dentry->d_sb->s_maxbytes) {+		bend--;+		sb->s_maxbytes = 0;+		for (bindex = 0; bindex <= bend; bindex++) {+			unsigned long long maxb;++			maxb = au_sbr_sb(sb, bindex)->s_maxbytes;+			if (sb->s_maxbytes < maxb)+				sb->s_maxbytes = maxb;+		}+	}++	if (au_xino_brid(sb) == br->br_id)+		au_xino_brid_set(sb, -1);+	goto out; /* success */++ out_wh:+	/* revert */+	rerr = au_br_init_wh(sb, br, br->br_perm, del->h_path.dentry);+	if (rerr)+		AuWarn("failed re-creating base whiteout, %s. (%d)\n",+		       del->pathname, rerr);+ out:+	return err;+}++/* ---------------------------------------------------------------------- */++/*+ * change a branch permission+ */++static int do_need_sigen_inc(int a, int b)+{+	return au_br_whable(a) && !au_br_whable(b);+}++static int need_sigen_inc(int old, int new)+{+	return do_need_sigen_inc(old, new)+		|| do_need_sigen_inc(new, old);+}++static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex)+{+	int err;+	unsigned long n, ul, bytes, files;+	aufs_bindex_t bstart;+	struct file *file, *hf, **a;+	const int step_bytes = 1024, /* memory allocation unit */+		step_files = step_bytes / sizeof(*a);++	err = -ENOMEM;+	n = 0;+	bytes = step_bytes;+	files = step_files;+	a = kmalloc(bytes, GFP_NOFS);+	if (unlikely(!a))+		goto out;++	/* no need file_list_lock() since sbinfo is locked? defered? */+	list_for_each_entry(file, &sb->s_files, f_u.fu_list) {+		if (special_file(file->f_dentry->d_inode->i_mode))+			continue;++		AuDbg("%.*s\n", AuDLNPair(file->f_dentry));+		fi_read_lock(file);+		if (unlikely(au_test_mmapped(file))) {+			err = -EBUSY;+			FiMustNoWaiters(file);+			fi_read_unlock(file);+			goto out_free;+		}++		bstart = au_fbstart(file);+		if (!S_ISREG(file->f_dentry->d_inode->i_mode)+		    || !(file->f_mode & FMODE_WRITE)+		    || bstart != bindex) {+			FiMustNoWaiters(file);+			fi_read_unlock(file);+			continue;+		}++		hf = au_h_fptr(file, bstart);+		FiMustNoWaiters(file);+		fi_read_unlock(file);++		if (n < files)+			a[n++] = hf;+		else {+			void *p;++			err = -ENOMEM;+			bytes += step_bytes;+			files += step_files;+			p = krealloc(a, bytes, GFP_NOFS);+			if (p) {+				a = p;+				a[n++] = hf;+			} else+				goto out_free;+		}+	}++	err = 0;+	for (ul = 0; ul < n; ul++) {+		/* todo: already flushed? */+		/* cf. fs/super.c:mark_files_ro() */+		hf = a[ul];+		hf->f_mode &= ~FMODE_WRITE;+		if (!file_check_writeable(hf)) {+			file_release_write(hf);+			mnt_drop_write(hf->f_vfsmnt);+		}+	}++ out_free:+	kfree(a);+ out:+	return err;+}++int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,+	      int *do_update)+{+	int err, rerr;+	aufs_bindex_t bindex;+	struct dentry *root;+	struct au_branch *br;++	root = sb->s_root;+	au_plink_block_maintain(sb);+	bindex = au_find_dbindex(root, mod->h_root);+	if (bindex < 0) {+		if (remount)+			return 0; /* success */+		err = -ENOENT;+		AuErr("%s no such branch\n", mod->path);+		goto out;+	}+	AuDbg("bindex b%d\n", bindex);++	err = test_br(mod->h_root->d_inode, mod->perm, mod->path);+	if (unlikely(err))+		goto out;++	br = au_sbr(sb, bindex);+	if (br->br_perm == mod->perm)+		return 0; /* success */++	if (au_br_writable(br->br_perm)) {+		/* remove whiteout base */+		err = au_br_init_wh(sb, br, mod->perm, mod->h_root);+		if (unlikely(err))+			goto out;++		if (!au_br_writable(mod->perm)) {+			/* rw --> ro, file might be mmapped */+			DiMustNoWaiters(root);+			IiMustNoWaiters(root->d_inode);+			di_write_unlock(root);+			err = au_br_mod_files_ro(sb, bindex);+			/* aufs_write_lock() calls ..._child() */+			di_write_lock_child(root);++			if (unlikely(err)) {+				rerr = -ENOMEM;+				br->br_wbr = kmalloc(sizeof(*br->br_wbr),+						     GFP_NOFS);+				if (br->br_wbr)+					rerr = au_br_init_wh+						(sb, br, br->br_perm,+						 mod->h_root);+				if (unlikely(rerr)) {+					AuIOErr("nested error %d (%d)\n",+						rerr, err);+					br->br_perm = mod->perm;+				}+			}+		}+	} else if (au_br_writable(mod->perm)) {+		/* ro --> rw */+		err = -ENOMEM;+		br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS);+		if (br->br_wbr) {+			struct path path = {+				.mnt	= br->br_mnt,+				.dentry	= mod->h_root+			};++			err = au_wbr_init(br, sb, mod->perm, &path);+			if (unlikely(err)) {+				kfree(br->br_wbr);+				br->br_wbr = NULL;+			}+		}+	}++	if (!err) {+		*do_update |= need_sigen_inc(br->br_perm, mod->perm);+		br->br_perm = mod->perm;+	}++ out:+	return err;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/branch.h linux-2.6.31.5/fs/aufs/branch.h--- linux-2.6.31.5.orig/fs/aufs/branch.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/branch.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,219 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * branch filesystems and xino for them+ */++#ifndef __AUFS_BRANCH_H__+#define __AUFS_BRANCH_H__++#ifdef __KERNEL__++#include <linux/fs.h>+#include <linux/mount.h>+#include <linux/aufs_type.h>+#include "rwsem.h"+#include "super.h"++/* ---------------------------------------------------------------------- */++/* a xino file */+struct au_xino_file {+	struct file		*xi_file;+	struct mutex		xi_nondir_mtx;++	/* todo: make xino files an array to support huge inode number */++#ifdef CONFIG_DEBUG_FS+	struct dentry		 *xi_dbgaufs;+#endif+};++/* members for writable branch only */+enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last};+struct au_wbr {+	struct au_rwsem		wbr_wh_rwsem;+	struct dentry		*wbr_wh[AuBrWh_Last];+	atomic_t 		wbr_wh_running;+#define wbr_whbase		wbr_wh[AuBrWh_BASE]	/* whiteout base */+#define wbr_plink		wbr_wh[AuBrWh_PLINK]	/* pseudo-link dir */+#define wbr_orph		wbr_wh[AuBrWh_ORPH]	/* dir for orphans */++	/* mfs mode */+	unsigned long long	wbr_bytes;+};++/* protected by superblock rwsem */+struct au_branch {+	struct au_xino_file	br_xino;++	aufs_bindex_t		br_id;++	int			br_perm;+	struct vfsmount		*br_mnt;+	atomic_t		br_count;++	struct au_wbr		*br_wbr;++	/* xino truncation */+	blkcnt_t		br_xino_upper;	/* watermark in blocks */+	atomic_t		br_xino_running;++#ifdef CONFIG_SYSFS+	/* an entry under sysfs per mount-point */+	char			br_name[8];+	struct attribute	br_attr;+#endif+};++/* ---------------------------------------------------------------------- */++/* branch permission and attribute */+enum {+	AuBrPerm_RW,		/* writable, linkable wh */+	AuBrPerm_RO,		/* readonly, no wh */+	AuBrPerm_RR,		/* natively readonly, no wh */++	AuBrPerm_RWNoLinkWH,	/* un-linkable whiteouts */++	AuBrPerm_ROWH,		/* whiteout-able */+	AuBrPerm_RRWH,		/* whiteout-able */++	AuBrPerm_Last+};++static inline int au_br_writable(int brperm)+{+	return brperm == AuBrPerm_RW || brperm == AuBrPerm_RWNoLinkWH;+}++static inline int au_br_whable(int brperm)+{+	return brperm == AuBrPerm_RW+		|| brperm == AuBrPerm_ROWH+		|| brperm == AuBrPerm_RRWH;+}++static inline int au_br_rdonly(struct au_branch *br)+{+	return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY)+		|| !au_br_writable(br->br_perm))+		? -EROFS : 0;+}++static inline int au_br_hinotifyable(int brperm __maybe_unused)+{+#ifdef CONFIG_AUFS_HINOTIFY+	return brperm != AuBrPerm_RR && brperm != AuBrPerm_RRWH;+#else+	return 0;+#endif+}++/* ---------------------------------------------------------------------- */++/* branch.c */+struct au_sbinfo;+void au_br_free(struct au_sbinfo *sinfo);+int au_br_index(struct super_block *sb, aufs_bindex_t br_id);+struct au_opt_add;+int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount);+struct au_opt_del;+int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount);+struct au_opt_mod;+int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,+	      int *do_update);++/* xino.c */+static const loff_t au_loff_max = LLONG_MAX;++int au_xib_trunc(struct super_block *sb);+ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, size_t size,+		   loff_t *pos);+ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size,+		    loff_t *pos);+struct file *au_xino_create2(struct file *base_file, struct file *copy_src);+struct file *au_xino_create(struct super_block *sb, char *fname, int silent);+ino_t au_xino_new_ino(struct super_block *sb);+int au_xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,+		   ino_t ino);+int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,+		  ino_t ino);+int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,+		 ino_t *ino);+int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino,+	       struct file *base_file, int do_test);+int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex);++struct au_opt_xino;+int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount);+void au_xino_clr(struct super_block *sb);+struct file *au_xino_def(struct super_block *sb);+int au_xino_path(struct seq_file *seq, struct file *file);++/* ---------------------------------------------------------------------- */++/* Superblock to branch */+static inline+aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex)+{+	return au_sbr(sb, bindex)->br_id;+}++static inline+struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex)+{+	return au_sbr(sb, bindex)->br_mnt;+}++static inline+struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex)+{+	return au_sbr_mnt(sb, bindex)->mnt_sb;+}++static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex)+{+	atomic_dec_return(&au_sbr(sb, bindex)->br_count);+}++static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex)+{+	return au_sbr(sb, bindex)->br_perm;+}++static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex)+{+	return au_br_whable(au_sbr_perm(sb, bindex));+}++/* ---------------------------------------------------------------------- */++/*+ * wbr_wh_read_lock, wbr_wh_write_lock+ * wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock+ */+AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &wbr->wbr_wh_rwsem);++#define WbrWhMustNoWaiters(wbr)	AuRwMustNoWaiters(&wbr->wbr_wh_rwsem)+#define WbrWhMustAnyLock(wbr)	AuRwMustAnyLock(&wbr->wbr_wh_rwsem)+#define WbrWhMustWriteLock(wbr)	AuRwMustWriteLock(&wbr->wbr_wh_rwsem)++#endif /* __KERNEL__ */+#endif /* __AUFS_BRANCH_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/cpup.c linux-2.6.31.5/fs/aufs/cpup.c--- linux-2.6.31.5.orig/fs/aufs/cpup.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/cpup.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,1048 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * copy-up functions, see wbr_policy.c for copy-down+ */++#include <linux/file.h>+#include <linux/fs_stack.h>+#include <linux/mm.h>+#include <linux/uaccess.h>+#include "aufs.h"++void au_cpup_attr_flags(struct inode *dst, struct inode *src)+{+	const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE+		| S_NOATIME | S_NOCMTIME;++	dst->i_flags |= src->i_flags & ~mask;+	if (au_test_fs_notime(dst->i_sb))+		dst->i_flags |= S_NOATIME | S_NOCMTIME;+}++void au_cpup_attr_timesizes(struct inode *inode)+{+	struct inode *h_inode;++	h_inode = au_h_iptr(inode, au_ibstart(inode));+	fsstack_copy_attr_times(inode, h_inode);+	vfsub_copy_inode_size(inode, h_inode);+}++void au_cpup_attr_nlink(struct inode *inode, int force)+{+	struct inode *h_inode;+	struct super_block *sb;+	aufs_bindex_t bindex, bend;++	sb = inode->i_sb;+	bindex = au_ibstart(inode);+	h_inode = au_h_iptr(inode, bindex);+	if (!force+	    && !S_ISDIR(h_inode->i_mode)+	    && au_opt_test(au_mntflags(sb), PLINK)+	    && au_plink_test(inode))+		return;++	inode->i_nlink = h_inode->i_nlink;++	/*+	 * fewer nlink makes find(1) noisy, but larger nlink doesn't.+	 * it may includes whplink directory.+	 */+	if (S_ISDIR(h_inode->i_mode)) {+		bend = au_ibend(inode);+		for (bindex++; bindex <= bend; bindex++) {+			h_inode = au_h_iptr(inode, bindex);+			if (h_inode)+				au_add_nlink(inode, h_inode);+		}+	}+}++void au_cpup_attr_changeable(struct inode *inode)+{+	struct inode *h_inode;++	h_inode = au_h_iptr(inode, au_ibstart(inode));+	inode->i_mode = h_inode->i_mode;+	inode->i_uid = h_inode->i_uid;+	inode->i_gid = h_inode->i_gid;+	au_cpup_attr_timesizes(inode);+	au_cpup_attr_flags(inode, h_inode);+}++void au_cpup_igen(struct inode *inode, struct inode *h_inode)+{+	struct au_iinfo *iinfo = au_ii(inode);++	IiMustWriteLock(inode);++	iinfo->ii_higen = h_inode->i_generation;+	iinfo->ii_hsb1 = h_inode->i_sb;+}++void au_cpup_attr_all(struct inode *inode, int force)+{+	struct inode *h_inode;++	h_inode = au_h_iptr(inode, au_ibstart(inode));+	au_cpup_attr_changeable(inode);+	if (inode->i_nlink > 0)+		au_cpup_attr_nlink(inode, force);+	inode->i_rdev = h_inode->i_rdev;+	inode->i_blkbits = h_inode->i_blkbits;+	au_cpup_igen(inode, h_inode);+}++/* ---------------------------------------------------------------------- */++/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */++/* keep the timestamps of the parent dir when cpup */+void au_dtime_store(struct au_dtime *dt, struct dentry *dentry,+		    struct path *h_path)+{+	struct inode *h_inode;++	dt->dt_dentry = dentry;+	dt->dt_h_path = *h_path;+	h_inode = h_path->dentry->d_inode;+	dt->dt_atime = h_inode->i_atime;+	dt->dt_mtime = h_inode->i_mtime;+	/* smp_mb(); */+}++void au_dtime_revert(struct au_dtime *dt)+{+	struct iattr attr;+	int err;++	attr.ia_atime = dt->dt_atime;+	attr.ia_mtime = dt->dt_mtime;+	attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET+		| ATTR_ATIME | ATTR_ATIME_SET;++	err = vfsub_notify_change(&dt->dt_h_path, &attr);+	if (unlikely(err))+		AuWarn("restoring timestamps failed(%d). ignored\n", err);+}++/* ---------------------------------------------------------------------- */++static noinline_for_stack+int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src)+{+	int err, sbits;+	struct iattr ia;+	struct path h_path;+	struct inode *h_isrc, *h_idst;++	h_path.dentry = au_h_dptr(dst, bindex);+	h_idst = h_path.dentry->d_inode;+	h_path.mnt = au_sbr_mnt(dst->d_sb, bindex);+	h_isrc = h_src->d_inode;+	ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID+		| ATTR_ATIME | ATTR_MTIME+		| ATTR_ATIME_SET | ATTR_MTIME_SET;+	ia.ia_uid = h_isrc->i_uid;+	ia.ia_gid = h_isrc->i_gid;+	ia.ia_atime = h_isrc->i_atime;+	ia.ia_mtime = h_isrc->i_mtime;+	if (h_idst->i_mode != h_isrc->i_mode+	    && !S_ISLNK(h_idst->i_mode)) {+		ia.ia_valid |= ATTR_MODE;+		ia.ia_mode = h_isrc->i_mode;+	}+	sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID));+	au_cpup_attr_flags(h_idst, h_isrc);+	err = vfsub_notify_change(&h_path, &ia);++	/* is this nfs only? */+	if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) {+		ia.ia_valid = ATTR_FORCE | ATTR_MODE;+		ia.ia_mode = h_isrc->i_mode;+		err = vfsub_notify_change(&h_path, &ia);+	}++	return err;+}++/* ---------------------------------------------------------------------- */++static int au_do_copy_file(struct file *dst, struct file *src, loff_t len,+			   char *buf, unsigned long blksize)+{+	int err;+	size_t sz, rbytes, wbytes;+	unsigned char all_zero;+	char *p, *zp;+	struct mutex *h_mtx;+	/* reduce stack usage */+	struct iattr *ia;++	zp = page_address(ZERO_PAGE(0));+	if (unlikely(!zp))+		return -ENOMEM; /* possible? */++	err = 0;+	all_zero = 0;+	while (len) {+		AuDbg("len %lld\n", len);+		sz = blksize;+		if (len < blksize)+			sz = len;++		rbytes = 0;+		/* todo: signal_pending? */+		while (!rbytes || err == -EAGAIN || err == -EINTR) {+			rbytes = vfsub_read_k(src, buf, sz, &src->f_pos);+			err = rbytes;+		}+		if (unlikely(err < 0))+			break;++		all_zero = 0;+		if (len >= rbytes && rbytes == blksize)+			all_zero = !memcmp(buf, zp, rbytes);+		if (!all_zero) {+			wbytes = rbytes;+			p = buf;+			while (wbytes) {+				size_t b;++				b = vfsub_write_k(dst, p, wbytes, &dst->f_pos);+				err = b;+				/* todo: signal_pending? */+				if (unlikely(err == -EAGAIN || err == -EINTR))+					continue;+				if (unlikely(err < 0))+					break;+				wbytes -= b;+				p += b;+			}+		} else {+			loff_t res;++			AuLabel(hole);+			res = vfsub_llseek(dst, rbytes, SEEK_CUR);+			err = res;+			if (unlikely(res < 0))+				break;+		}+		len -= rbytes;+		err = 0;+	}++	/* the last block may be a hole */+	if (!err && all_zero) {+		AuLabel(last hole);++		err = 1;+		if (au_test_nfs(dst->f_dentry->d_sb)) {+			/* nfs requires this step to make last hole */+			/* is this only nfs? */+			do {+				/* todo: signal_pending? */+				err = vfsub_write_k(dst, "\0", 1, &dst->f_pos);+			} while (err == -EAGAIN || err == -EINTR);+			if (err == 1)+				dst->f_pos--;+		}++		if (err == 1) {+			ia = (void *)buf;+			ia->ia_size = dst->f_pos;+			ia->ia_valid = ATTR_SIZE | ATTR_FILE;+			ia->ia_file = dst;+			h_mtx = &dst->f_dentry->d_inode->i_mutex;+			mutex_lock_nested(h_mtx, AuLsc_I_CHILD2);+			err = vfsub_notify_change(&dst->f_path, ia);+			mutex_unlock(h_mtx);+		}+	}++	return err;+}++int au_copy_file(struct file *dst, struct file *src, loff_t len)+{+	int err;+	unsigned long blksize;+	unsigned char do_kfree;+	char *buf;++	err = -ENOMEM;+	blksize = dst->f_dentry->d_sb->s_blocksize;+	if (!blksize || PAGE_SIZE < blksize)+		blksize = PAGE_SIZE;+	AuDbg("blksize %lu\n", blksize);+	do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *));+	if (do_kfree)+		buf = kmalloc(blksize, GFP_NOFS);+	else+		buf = (void *)__get_free_page(GFP_NOFS);+	if (unlikely(!buf))+		goto out;++	if (len > (1 << 22))+		AuDbg("copying a large file %lld\n", (long long)len);++	src->f_pos = 0;+	dst->f_pos = 0;+	err = au_do_copy_file(dst, src, len, buf, blksize);+	if (do_kfree)+		kfree(buf);+	else+		free_page((unsigned long)buf);++ out:+	return err;+}++/*+ * to support a sparse file which is opened with O_APPEND,+ * we need to close the file.+ */+static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst,+			aufs_bindex_t bsrc, loff_t len)+{+	int err, i;+	enum { SRC, DST };+	struct {+		aufs_bindex_t bindex;+		unsigned int flags;+		struct dentry *dentry;+		struct file *file;+		void *label, *label_file;+	} *f, file[] = {+		{+			.bindex = bsrc,+			.flags = O_RDONLY | O_NOATIME | O_LARGEFILE,+			.file = NULL,+			.label = &&out,+			.label_file = &&out_src+		},+		{+			.bindex = bdst,+			.flags = O_WRONLY | O_NOATIME | O_LARGEFILE,+			.file = NULL,+			.label = &&out_src,+			.label_file = &&out_dst+		}+	};+	struct super_block *sb;++	/* bsrc branch can be ro/rw. */+	sb = dentry->d_sb;+	f = file;+	for (i = 0; i < 2; i++, f++) {+		f->dentry = au_h_dptr(dentry, f->bindex);+		f->file = au_h_open(dentry, f->bindex, f->flags, /*file*/NULL);+		err = PTR_ERR(f->file);+		if (IS_ERR(f->file))+			goto *f->label;+		err = -EINVAL;+		if (unlikely(!f->file->f_op))+			goto *f->label_file;+	}++	/* try stopping to update while we copyup */+	IMustLock(file[SRC].dentry->d_inode);+	err = au_copy_file(file[DST].file, file[SRC].file, len);++ out_dst:+	fput(file[DST].file);+	au_sbr_put(sb, file[DST].bindex);+ out_src:+	fput(file[SRC].file);+	au_sbr_put(sb, file[SRC].bindex);+ out:+	return err;+}++static int au_do_cpup_regular(struct dentry *dentry, aufs_bindex_t bdst,+			      aufs_bindex_t bsrc, loff_t len,+			      struct inode *h_dir, struct path *h_path)+{+	int err, rerr;+	loff_t l;++	err = 0;+	l = i_size_read(au_h_iptr(dentry->d_inode, bsrc));+	if (len == -1 || l < len)+		len = l;+	if (len)+		err = au_cp_regular(dentry, bdst, bsrc, len);+	if (!err)+		goto out; /* success */++	rerr = vfsub_unlink(h_dir, h_path, /*force*/0);+	if (rerr) {+		AuIOErr("failed unlinking cpup-ed %.*s(%d, %d)\n",+			AuDLNPair(h_path->dentry), err, rerr);+		err = -EIO;+	}++ out:+	return err;+}++static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src,+			      struct inode *h_dir)+{+	int err, symlen;+	mm_segment_t old_fs;+	char *sym;++	err = -ENOSYS;+	if (unlikely(!h_src->d_inode->i_op->readlink))+		goto out;++	err = -ENOMEM;+	sym = __getname();+	if (unlikely(!sym))+		goto out;++	old_fs = get_fs();+	set_fs(KERNEL_DS);+	symlen = h_src->d_inode->i_op->readlink(h_src, (char __user *)sym,+						PATH_MAX);+	err = symlen;+	set_fs(old_fs);++	if (symlen > 0) {+		sym[symlen] = 0;+		err = vfsub_symlink(h_dir, h_path, sym);+	}+	__putname(sym);++ out:+	return err;+}++/* return with the lower dst inode is locked */+static noinline_for_stack+int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,+	       aufs_bindex_t bsrc, loff_t len, unsigned int flags,+	       struct dentry *dst_parent)+{+	int err;+	umode_t mode;+	unsigned int mnt_flags;+	unsigned char isdir;+	const unsigned char do_dt = !!au_ftest_cpup(flags, DTIME);+	struct au_dtime dt;+	struct path h_path;+	struct dentry *h_src, *h_dst, *h_parent;+	struct inode *h_inode, *h_dir;+	struct super_block *sb;++	/* bsrc branch can be ro/rw. */+	h_src = au_h_dptr(dentry, bsrc);+	h_inode = h_src->d_inode;+	AuDebugOn(h_inode != au_h_iptr(dentry->d_inode, bsrc));++	/* try stopping to be referenced while we are creating */+	h_dst = au_h_dptr(dentry, bdst);+	h_parent = h_dst->d_parent; /* dir inode is locked */+	h_dir = h_parent->d_inode;+	IMustLock(h_dir);+	AuDebugOn(h_parent != h_dst->d_parent);++	sb = dentry->d_sb;+	h_path.mnt = au_sbr_mnt(sb, bdst);+	if (do_dt) {+		h_path.dentry = h_parent;+		au_dtime_store(&dt, dst_parent, &h_path);+	}+	h_path.dentry = h_dst;++	isdir = 0;+	mode = h_inode->i_mode;+	switch (mode & S_IFMT) {+	case S_IFREG:+		/* try stopping to update while we are referencing */+		IMustLock(h_inode);+		err = vfsub_create(h_dir, &h_path, mode | S_IWUSR);+		if (!err)+			err = au_do_cpup_regular+				(dentry, bdst, bsrc, len,+				 au_h_iptr(dst_parent->d_inode, bdst), &h_path);+		break;+	case S_IFDIR:+		isdir = 1;+		err = vfsub_mkdir(h_dir, &h_path, mode);+		if (!err) {+			/*+			 * strange behaviour from the users view,+			 * particularry setattr case+			 */+			if (au_ibstart(dst_parent->d_inode) == bdst)+				au_cpup_attr_nlink(dst_parent->d_inode,+						   /*force*/1);+			au_cpup_attr_nlink(dentry->d_inode, /*force*/1);+		}+		break;+	case S_IFLNK:+		err = au_do_cpup_symlink(&h_path, h_src, h_dir);+		break;+	case S_IFCHR:+	case S_IFBLK:+		AuDebugOn(!capable(CAP_MKNOD));+		/*FALLTHROUGH*/+	case S_IFIFO:+	case S_IFSOCK:+		err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev);+		break;+	default:+		AuIOErr("Unknown inode type 0%o\n", mode);+		err = -EIO;+	}++	mnt_flags = au_mntflags(sb);+	if (!au_opt_test(mnt_flags, UDBA_NONE)+	    && !isdir+	    && au_opt_test(mnt_flags, XINO)+	    && h_inode->i_nlink == 1+	    /* todo: unnecessary? */+	    /* && dentry->d_inode->i_nlink == 1 */+	    && bdst < bsrc+	    && !au_ftest_cpup(flags, KEEPLINO))+		au_xino_write(sb, bsrc, h_inode->i_ino, /*ino*/0);+		/* ignore this error */++	if (do_dt)+		au_dtime_revert(&dt);+	return err;+}++/*+ * copyup the @dentry from @bsrc to @bdst.+ * the caller must set the both of lower dentries.+ * @len is for truncating when it is -1 copyup the entire file.+ * in link/rename cases, @dst_parent may be different from the real one.+ */+static int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,+			  aufs_bindex_t bsrc, loff_t len, unsigned int flags,+			  struct dentry *dst_parent)+{+	int err, rerr;+	aufs_bindex_t old_ibstart;+	unsigned char isdir, plink;+	struct au_dtime dt;+	struct path h_path;+	struct dentry *h_src, *h_dst, *h_parent;+	struct inode *dst_inode, *h_dir, *inode;+	struct super_block *sb;++	AuDebugOn(bsrc <= bdst);++	sb = dentry->d_sb;+	h_path.mnt = au_sbr_mnt(sb, bdst);+	h_dst = au_h_dptr(dentry, bdst);+	h_parent = h_dst->d_parent; /* dir inode is locked */+	h_dir = h_parent->d_inode;+	IMustLock(h_dir);++	h_src = au_h_dptr(dentry, bsrc);+	inode = dentry->d_inode;++	if (!dst_parent)+		dst_parent = dget_parent(dentry);+	else+		dget(dst_parent);++	plink = !!au_opt_test(au_mntflags(sb), PLINK);+	dst_inode = au_h_iptr(inode, bdst);+	if (dst_inode) {+		if (unlikely(!plink)) {+			err = -EIO;+			AuIOErr("i%lu exists on a upper branch "+				"but plink is disabled\n", inode->i_ino);+			goto out;+		}++		if (dst_inode->i_nlink) {+			const int do_dt = au_ftest_cpup(flags, DTIME);++			h_src = au_plink_lkup(inode, bdst);+			err = PTR_ERR(h_src);+			if (IS_ERR(h_src))+				goto out;+			if (unlikely(!h_src->d_inode)) {+				err = -EIO;+				AuIOErr("i%lu exists on a upper branch "+					"but plink is broken\n", inode->i_ino);+				dput(h_src);+				goto out;+			}++			if (do_dt) {+				h_path.dentry = h_parent;+				au_dtime_store(&dt, dst_parent, &h_path);+			}+			h_path.dentry = h_dst;+			err = vfsub_link(h_src, h_dir, &h_path);+			if (do_dt)+				au_dtime_revert(&dt);+			dput(h_src);+			goto out;+		} else+			/* todo: cpup_wh_file? */+			/* udba work */+			au_update_brange(inode, 1);+	}++	old_ibstart = au_ibstart(inode);+	err = cpup_entry(dentry, bdst, bsrc, len, flags, dst_parent);+	if (unlikely(err))+		goto out;+	dst_inode = h_dst->d_inode;+	mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2);++	err = cpup_iattr(dentry, bdst, h_src);+	isdir = S_ISDIR(dst_inode->i_mode);+	if (!err) {+		if (bdst < old_ibstart)+			au_set_ibstart(inode, bdst);+		au_set_h_iptr(inode, bdst, au_igrab(dst_inode),+			      au_hi_flags(inode, isdir));+		mutex_unlock(&dst_inode->i_mutex);+		if (!isdir+		    && h_src->d_inode->i_nlink > 1+		    && plink)+			au_plink_append(inode, bdst, h_dst);+		goto out; /* success */+	}++	/* revert */+	h_path.dentry = h_parent;+	mutex_unlock(&dst_inode->i_mutex);+	au_dtime_store(&dt, dst_parent, &h_path);+	h_path.dentry = h_dst;+	if (!isdir)+		rerr = vfsub_unlink(h_dir, &h_path, /*force*/0);+	else+		rerr = vfsub_rmdir(h_dir, &h_path);+	au_dtime_revert(&dt);+	if (rerr) {+		AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr);+		err = -EIO;+	}++ out:+	dput(dst_parent);+	return err;+}++struct au_cpup_single_args {+	int *errp;+	struct dentry *dentry;+	aufs_bindex_t bdst, bsrc;+	loff_t len;+	unsigned int flags;+	struct dentry *dst_parent;+};++static void au_call_cpup_single(void *args)+{+	struct au_cpup_single_args *a = args;+	*a->errp = au_cpup_single(a->dentry, a->bdst, a->bsrc, a->len,+				  a->flags, a->dst_parent);+}++int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,+		       aufs_bindex_t bsrc, loff_t len, unsigned int flags,+		       struct dentry *dst_parent)+{+	int err, wkq_err;+	umode_t mode;+	struct dentry *h_dentry;++	h_dentry = au_h_dptr(dentry, bsrc);+	mode = h_dentry->d_inode->i_mode & S_IFMT;+	if ((mode != S_IFCHR && mode != S_IFBLK)+	    || capable(CAP_MKNOD))+		err = au_cpup_single(dentry, bdst, bsrc, len, flags,+				     dst_parent);+	else {+		struct au_cpup_single_args args = {+			.errp		= &err,+			.dentry		= dentry,+			.bdst		= bdst,+			.bsrc		= bsrc,+			.len		= len,+			.flags		= flags,+			.dst_parent	= dst_parent+		};+		wkq_err = au_wkq_wait(au_call_cpup_single, &args);+		if (unlikely(wkq_err))+			err = wkq_err;+	}++	return err;+}++/*+ * copyup the @dentry from the first active lower branch to @bdst,+ * using au_cpup_single().+ */+static int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,+			  unsigned int flags)+{+	int err;+	aufs_bindex_t bsrc, bend;++	bend = au_dbend(dentry);+	for (bsrc = bdst + 1; bsrc <= bend; bsrc++)+		if (au_h_dptr(dentry, bsrc))+			break;++	err = au_lkup_neg(dentry, bdst);+	if (!err) {+		err = au_cpup_single(dentry, bdst, bsrc, len, flags, NULL);+		if (!err)+			return 0; /* success */++		/* revert */+		au_set_h_dptr(dentry, bdst, NULL);+		au_set_dbstart(dentry, bsrc);+	}++	return err;+}++struct au_cpup_simple_args {+	int *errp;+	struct dentry *dentry;+	aufs_bindex_t bdst;+	loff_t len;+	unsigned int flags;+};++static void au_call_cpup_simple(void *args)+{+	struct au_cpup_simple_args *a = args;+	*a->errp = au_cpup_simple(a->dentry, a->bdst, a->len, a->flags);+}++int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,+		       unsigned int flags)+{+	int err, wkq_err;+	unsigned char do_sio;+	struct dentry *parent;+	struct inode *h_dir;++	parent = dget_parent(dentry);+	h_dir = au_h_iptr(parent->d_inode, bdst);+	do_sio = !!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE);+	if (!do_sio) {+		/*+		 * testing CAP_MKNOD is for generic fs,+		 * but CAP_FSETID is for xfs only, currently.+		 */+		umode_t mode = dentry->d_inode->i_mode;+		do_sio = (((mode & (S_IFCHR | S_IFBLK))+			   && !capable(CAP_MKNOD))+			  || ((mode & (S_ISUID | S_ISGID))+			      && !capable(CAP_FSETID)));+	}+	if (!do_sio)+		err = au_cpup_simple(dentry, bdst, len, flags);+	else {+		struct au_cpup_simple_args args = {+			.errp		= &err,+			.dentry		= dentry,+			.bdst		= bdst,+			.len		= len,+			.flags		= flags+		};+		wkq_err = au_wkq_wait(au_call_cpup_simple, &args);+		if (unlikely(wkq_err))+			err = wkq_err;+	}++	dput(parent);+	return err;+}++/* ---------------------------------------------------------------------- */++/*+ * copyup the deleted file for writing.+ */+static int au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst,+			 struct dentry *wh_dentry, struct file *file,+			 loff_t len)+{+	int err;+	aufs_bindex_t bstart;+	struct au_dinfo *dinfo;+	struct dentry *h_d_dst, *h_d_start;++	dinfo = au_di(dentry);+	AuRwMustWriteLock(&dinfo->di_rwsem);++	bstart = dinfo->di_bstart;+	h_d_dst = dinfo->di_hdentry[0 + bdst].hd_dentry;+	dinfo->di_bstart = bdst;+	dinfo->di_hdentry[0 + bdst].hd_dentry = wh_dentry;+	h_d_start = dinfo->di_hdentry[0 + bstart].hd_dentry;+	if (file)+		dinfo->di_hdentry[0 + bstart].hd_dentry+			= au_h_fptr(file, au_fbstart(file))->f_dentry;+	err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME,+			     /*h_parent*/NULL);+	if (!err && file) {+		err = au_reopen_nondir(file);+		dinfo->di_hdentry[0 + bstart].hd_dentry = h_d_start;+	}+	dinfo->di_hdentry[0 + bdst].hd_dentry = h_d_dst;+	dinfo->di_bstart = bstart;++	return err;+}++static int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,+		      struct file *file)+{+	int err;+	struct au_dtime dt;+	struct dentry *parent, *h_parent, *wh_dentry;+	struct au_branch *br;+	struct path h_path;++	br = au_sbr(dentry->d_sb, bdst);+	parent = dget_parent(dentry);+	h_parent = au_h_dptr(parent, bdst);+	wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name);+	err = PTR_ERR(wh_dentry);+	if (IS_ERR(wh_dentry))+		goto out;++	h_path.dentry = h_parent;+	h_path.mnt = br->br_mnt;+	au_dtime_store(&dt, parent, &h_path);+	err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len);+	if (unlikely(err))+		goto out_wh;++	dget(wh_dentry);+	h_path.dentry = wh_dentry;+	err = vfsub_unlink(h_parent->d_inode, &h_path, /*force*/0);+	if (unlikely(err)) {+		AuIOErr("failed remove copied-up tmp file %.*s(%d)\n",+			AuDLNPair(wh_dentry), err);+		err = -EIO;+	}+	au_dtime_revert(&dt);+	au_set_hi_wh(dentry->d_inode, bdst, wh_dentry);++ out_wh:+	dput(wh_dentry);+ out:+	dput(parent);+	return err;+}++struct au_cpup_wh_args {+	int *errp;+	struct dentry *dentry;+	aufs_bindex_t bdst;+	loff_t len;+	struct file *file;+};++static void au_call_cpup_wh(void *args)+{+	struct au_cpup_wh_args *a = args;+	*a->errp = au_cpup_wh(a->dentry, a->bdst, a->len, a->file);+}++int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,+		   struct file *file)+{+	int err, wkq_err;+	struct dentry *parent, *h_orph, *h_parent, *h_dentry;+	struct inode *dir, *h_dir, *h_tmpdir, *h_inode;+	struct au_wbr *wbr;++	parent = dget_parent(dentry);+	dir = parent->d_inode;+	h_orph = NULL;+	h_parent = NULL;+	h_dir = au_igrab(au_h_iptr(dir, bdst));+	h_tmpdir = h_dir;+	if (!h_dir->i_nlink) {+		wbr = au_sbr(dentry->d_sb, bdst)->br_wbr;+		h_orph = wbr->wbr_orph;++		h_parent = dget(au_h_dptr(parent, bdst));+		au_set_h_dptr(parent, bdst, NULL);+		au_set_h_dptr(parent, bdst, dget(h_orph));+		h_tmpdir = h_orph->d_inode;+		au_set_h_iptr(dir, bdst, NULL, 0);+		au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0);++		/* this temporary unlock is safe */+		if (file)+			h_dentry = au_h_fptr(file, au_fbstart(file))->f_dentry;+		else+			h_dentry = au_h_dptr(dentry, au_dbstart(dentry));+		h_inode = h_dentry->d_inode;+		IMustLock(h_inode);+		mutex_unlock(&h_inode->i_mutex);+		mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3);+		mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);+	}++	if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE))+		err = au_cpup_wh(dentry, bdst, len, file);+	else {+		struct au_cpup_wh_args args = {+			.errp	= &err,+			.dentry	= dentry,+			.bdst	= bdst,+			.len	= len,+			.file	= file+		};+		wkq_err = au_wkq_wait(au_call_cpup_wh, &args);+		if (unlikely(wkq_err))+			err = wkq_err;+	}++	if (h_orph) {+		mutex_unlock(&h_tmpdir->i_mutex);+		au_set_h_iptr(dir, bdst, NULL, 0);+		au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0);+		au_set_h_dptr(parent, bdst, NULL);+		au_set_h_dptr(parent, bdst, h_parent);+	}+	iput(h_dir);+	dput(parent);++	return err;+}++/* ---------------------------------------------------------------------- */++/*+ * generic routine for both of copy-up and copy-down.+ */+/* cf. revalidate function in file.c */+int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst,+	       int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,+			 struct dentry *h_parent, void *arg),+	       void *arg)+{+	int err;+	struct au_pin pin;+	struct dentry *d, *parent, *h_parent, *real_parent;++	err = 0;+	parent = dget_parent(dentry);+	if (IS_ROOT(parent))+		goto out;++	au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2,+		    au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE);++	/* do not use au_dpage */+	real_parent = parent;+	while (1) {+		dput(parent);+		parent = dget_parent(dentry);+		h_parent = au_h_dptr(parent, bdst);+		if (h_parent)+			goto out; /* success */++		/* find top dir which is necessary to cpup */+		do {+			d = parent;+			dput(parent);+			parent = dget_parent(d);+			di_read_lock_parent3(parent, !AuLock_IR);+			h_parent = au_h_dptr(parent, bdst);+			di_read_unlock(parent, !AuLock_IR);+		} while (!h_parent);++		if (d != real_parent)+			di_write_lock_child3(d);++		/* somebody else might create while we were sleeping */+		if (!au_h_dptr(d, bdst) || !au_h_dptr(d, bdst)->d_inode) {+			if (au_h_dptr(d, bdst))+				au_update_dbstart(d);++			au_pin_set_dentry(&pin, d);+			err = au_do_pin(&pin);+			if (!err) {+				err = cp(d, bdst, h_parent, arg);+				au_unpin(&pin);+			}+		}++		if (d != real_parent)+			di_write_unlock(d);+		if (unlikely(err))+			break;+	}++ out:+	dput(parent);+	return err;+}++static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst,+		       struct dentry *h_parent __maybe_unused ,+		       void *arg __maybe_unused)+{+	return au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME);+}++int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst)+{+	return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL);+}++int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst)+{+	int err;+	struct dentry *parent;+	struct inode *dir;++	parent = dget_parent(dentry);+	dir = parent->d_inode;+	err = 0;+	if (au_h_iptr(dir, bdst))+		goto out;++	di_read_unlock(parent, AuLock_IR);+	di_write_lock_parent(parent);+	/* someone else might change our inode while we were sleeping */+	if (!au_h_iptr(dir, bdst))+		err = au_cpup_dirs(dentry, bdst);+	di_downgrade_lock(parent, AuLock_IR);++ out:+	dput(parent);+	return err;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/cpup.h linux-2.6.31.5/fs/aufs/cpup.h--- linux-2.6.31.5.orig/fs/aufs/cpup.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/cpup.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,81 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * copy-up/down functions+ */++#ifndef __AUFS_CPUP_H__+#define __AUFS_CPUP_H__++#ifdef __KERNEL__++#include <linux/path.h>+#include <linux/time.h>+#include <linux/aufs_type.h>++struct inode;+struct file;++void au_cpup_attr_flags(struct inode *dst, struct inode *src);+void au_cpup_attr_timesizes(struct inode *inode);+void au_cpup_attr_nlink(struct inode *inode, int force);+void au_cpup_attr_changeable(struct inode *inode);+void au_cpup_igen(struct inode *inode, struct inode *h_inode);+void au_cpup_attr_all(struct inode *inode, int force);++/* ---------------------------------------------------------------------- */++/* cpup flags */+#define AuCpup_DTIME	1		/* do dtime_store/revert */+#define AuCpup_KEEPLINO	(1 << 1)	/* do not clear the lower xino,+					   for link(2) */+#define au_ftest_cpup(flags, name)	((flags) & AuCpup_##name)+#define au_fset_cpup(flags, name)	{ (flags) |= AuCpup_##name; }+#define au_fclr_cpup(flags, name)	{ (flags) &= ~AuCpup_##name; }++int au_copy_file(struct file *dst, struct file *src, loff_t len);+int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,+		       aufs_bindex_t bsrc, loff_t len, unsigned int flags,+		       struct dentry *dst_parent);+int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,+		       unsigned int flags);+int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,+		   struct file *file);++int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst,+	       int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,+			 struct dentry *h_parent, void *arg),+	       void *arg);+int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst);+int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst);++/* ---------------------------------------------------------------------- */++/* keep timestamps when copyup */+struct au_dtime {+	struct dentry *dt_dentry;+	struct path dt_h_path;+	struct timespec dt_atime, dt_mtime;+};+void au_dtime_store(struct au_dtime *dt, struct dentry *dentry,+		    struct path *h_path);+void au_dtime_revert(struct au_dtime *dt);++#endif /* __KERNEL__ */+#endif /* __AUFS_CPUP_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/dbgaufs.c linux-2.6.31.5/fs/aufs/dbgaufs.c--- linux-2.6.31.5.orig/fs/aufs/dbgaufs.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/dbgaufs.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,331 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * debugfs interface+ */++#include <linux/debugfs.h>+#include "aufs.h"++#ifndef CONFIG_SYSFS+#error DEBUG_FS depends upon SYSFS+#endif++static struct dentry *dbgaufs;+static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH;++/* 20 is max digits length of ulong 64 */+struct dbgaufs_arg {+	int n;+	char a[20 * 4];+};++/*+ * common function for all XINO files+ */+static int dbgaufs_xi_release(struct inode *inode __maybe_unused,+			      struct file *file)+{+	kfree(file->private_data);+	return 0;+}++static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt)+{+	int err;+	struct kstat st;+	struct dbgaufs_arg *p;++	err = -ENOMEM;+	p = kmalloc(sizeof(*p), GFP_NOFS);+	if (unlikely(!p))+		goto out;++	err = 0;+	p->n = 0;+	file->private_data = p;+	if (!xf)+		goto out;++	err = vfs_getattr(xf->f_vfsmnt, xf->f_dentry, &st);+	if (!err) {+		if (do_fcnt)+			p->n = snprintf+				(p->a, sizeof(p->a), "%ld, %llux%lu %lld\n",+				 (long)file_count(xf), st.blocks, st.blksize,+				 (long long)st.size);+		else+			p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n",+					st.blocks, st.blksize,+					(long long)st.size);+		AuDebugOn(p->n >= sizeof(p->a));+	} else {+		p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err);+		err = 0;+	}++ out:+	return err;++}++static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf,+			       size_t count, loff_t *ppos)+{+	struct dbgaufs_arg *p;++	p = file->private_data;+	return simple_read_from_buffer(buf, count, ppos, p->a, p->n);+}++/* ---------------------------------------------------------------------- */++static int dbgaufs_xib_open(struct inode *inode, struct file *file)+{+	int err;+	struct au_sbinfo *sbinfo;+	struct super_block *sb;++	sbinfo = inode->i_private;+	sb = sbinfo->si_sb;+	si_noflush_read_lock(sb);+	err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0);+	si_read_unlock(sb);+	return err;+}++static const struct file_operations dbgaufs_xib_fop = {+	.open		= dbgaufs_xib_open,+	.release	= dbgaufs_xi_release,+	.read		= dbgaufs_xi_read+};++/* ---------------------------------------------------------------------- */++#define DbgaufsXi_PREFIX "xi"++static int dbgaufs_xino_open(struct inode *inode, struct file *file)+{+	int err;+	long l;+	struct au_sbinfo *sbinfo;+	struct super_block *sb;+	struct file *xf;+	struct qstr *name;++	err = -ENOENT;+	xf = NULL;+	name = &file->f_dentry->d_name;+	if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX)+		     || memcmp(name->name, DbgaufsXi_PREFIX,+			       sizeof(DbgaufsXi_PREFIX) - 1)))+		goto out;+	err = strict_strtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l);+	if (unlikely(err))+		goto out;++	sbinfo = inode->i_private;+	sb = sbinfo->si_sb;+	si_noflush_read_lock(sb);+	if (l <= au_sbend(sb)) {+		xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file;+		err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1);+	} else+		err = -ENOENT;+	si_read_unlock(sb);++ out:+	return err;+}++static const struct file_operations dbgaufs_xino_fop = {+	.open		= dbgaufs_xino_open,+	.release	= dbgaufs_xi_release,+	.read		= dbgaufs_xi_read+};++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)+{+	aufs_bindex_t bend;+	struct au_branch *br;+	struct au_xino_file *xi;++	if (!au_sbi(sb)->si_dbgaufs)+		return;++	bend = au_sbend(sb);+	for (; bindex <= bend; bindex++) {+		br = au_sbr(sb, bindex);+		xi = &br->br_xino;+		if (xi->xi_dbgaufs) {+			debugfs_remove(xi->xi_dbgaufs);+			xi->xi_dbgaufs = NULL;+		}+	}+}++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)+{+	struct au_sbinfo *sbinfo;+	struct dentry *parent;+	struct au_branch *br;+	struct au_xino_file *xi;+	aufs_bindex_t bend;+	char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */++	sbinfo = au_sbi(sb);+	parent = sbinfo->si_dbgaufs;+	if (!parent)+		return;++	bend = au_sbend(sb);+	for (; bindex <= bend; bindex++) {+		snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex);+		br = au_sbr(sb, bindex);+		xi = &br->br_xino;+		AuDebugOn(xi->xi_dbgaufs);+		xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent,+						     sbinfo, &dbgaufs_xino_fop);+		/* ignore an error */+		if (unlikely(!xi->xi_dbgaufs))+			AuWarn1("failed %s under debugfs\n", name);+	}+}++/* ---------------------------------------------------------------------- */++#ifdef CONFIG_AUFS_EXPORT+static int dbgaufs_xigen_open(struct inode *inode, struct file *file)+{+	int err;+	struct au_sbinfo *sbinfo;+	struct super_block *sb;++	sbinfo = inode->i_private;+	sb = sbinfo->si_sb;+	si_noflush_read_lock(sb);+	err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0);+	si_read_unlock(sb);+	return err;+}++static const struct file_operations dbgaufs_xigen_fop = {+	.open		= dbgaufs_xigen_open,+	.release	= dbgaufs_xi_release,+	.read		= dbgaufs_xi_read+};++static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo)+{+	int err;++	/*+	 * This function is a dynamic '__init' fucntion actually,+	 * so the tiny check for si_rwsem is unnecessary.+	 */+	/* AuRwMustWriteLock(&sbinfo->si_rwsem); */++	err = -EIO;+	sbinfo->si_dbgaufs_xigen = debugfs_create_file+		("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo,+		 &dbgaufs_xigen_fop);+	if (sbinfo->si_dbgaufs_xigen)+		err = 0;++	return err;+}+#else+static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo)+{+	return 0;+}+#endif /* CONFIG_AUFS_EXPORT */++/* ---------------------------------------------------------------------- */++void dbgaufs_si_fin(struct au_sbinfo *sbinfo)+{+	/*+	 * This function is a dynamic '__init' fucntion actually,+	 * so the tiny check for si_rwsem is unnecessary.+	 */+	/* AuRwMustWriteLock(&sbinfo->si_rwsem); */++	debugfs_remove_recursive(sbinfo->si_dbgaufs);+	sbinfo->si_dbgaufs = NULL;+	kobject_put(&sbinfo->si_kobj);+}++int dbgaufs_si_init(struct au_sbinfo *sbinfo)+{+	int err;+	char name[SysaufsSiNameLen];++	/*+	 * This function is a dynamic '__init' fucntion actually,+	 * so the tiny check for si_rwsem is unnecessary.+	 */+	/* AuRwMustWriteLock(&sbinfo->si_rwsem); */++	err = -ENOENT;+	if (!dbgaufs) {+		AuErr1("/debug/aufs is uninitialized\n");+		goto out;+	}++	err = -EIO;+	sysaufs_name(sbinfo, name);+	sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs);+	if (unlikely(!sbinfo->si_dbgaufs))+		goto out;+	kobject_get(&sbinfo->si_kobj);++	sbinfo->si_dbgaufs_xib = debugfs_create_file+		("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo,+		 &dbgaufs_xib_fop);+	if (unlikely(!sbinfo->si_dbgaufs_xib))+		goto out_dir;++	err = dbgaufs_xigen_init(sbinfo);+	if (!err)+		goto out; /* success */++ out_dir:+	dbgaufs_si_fin(sbinfo);+ out:+	return err;+}++/* ---------------------------------------------------------------------- */++void dbgaufs_fin(void)+{+	debugfs_remove(dbgaufs);+}++int __init dbgaufs_init(void)+{+	int err;++	err = -EIO;+	dbgaufs = debugfs_create_dir(AUFS_NAME, NULL);+	if (dbgaufs)+		err = 0;+	return err;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/dbgaufs.h linux-2.6.31.5/fs/aufs/dbgaufs.h--- linux-2.6.31.5.orig/fs/aufs/dbgaufs.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/dbgaufs.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,79 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * debugfs interface+ */++#ifndef __DBGAUFS_H__+#define __DBGAUFS_H__++#ifdef __KERNEL__++#include <linux/init.h>+#include <linux/aufs_type.h>++struct super_block;+struct au_sbinfo;++#ifdef CONFIG_DEBUG_FS+/* dbgaufs.c */+void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex);+void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex);+void dbgaufs_si_fin(struct au_sbinfo *sbinfo);+int dbgaufs_si_init(struct au_sbinfo *sbinfo);+void dbgaufs_fin(void);+int __init dbgaufs_init(void);++#else++static inline+void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)+{+	/* empty */+}++static inline+void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)+{+	/* empty */+}++static inline+void dbgaufs_si_fin(struct au_sbinfo *sbinfo)+{+	/* empty */+}++static inline+int dbgaufs_si_init(struct au_sbinfo *sbinfo)+{+	return 0;+}++#define dbgaufs_fin()	do {} while (0)++static inline+int __init dbgaufs_init(void)+{+	return 0;+}+#endif /* CONFIG_DEBUG_FS */++#endif /* __KERNEL__ */+#endif /* __DBGAUFS_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/dcsub.c linux-2.6.31.5/fs/aufs/dcsub.c--- linux-2.6.31.5.orig/fs/aufs/dcsub.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/dcsub.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,223 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * sub-routines for dentry cache+ */++#include "aufs.h"++static void au_dpage_free(struct au_dpage *dpage)+{+	int i;+	struct dentry **p;++	p = dpage->dentries;+	for (i = 0; i < dpage->ndentry; i++)+		dput(*p++);+	free_page((unsigned long)dpage->dentries);+}++int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp)+{+	int err;+	void *p;++	err = -ENOMEM;+	dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp);+	if (unlikely(!dpages->dpages))+		goto out;++	p = (void *)__get_free_page(gfp);+	if (unlikely(!p))+		goto out_dpages;++	dpages->dpages[0].ndentry = 0;+	dpages->dpages[0].dentries = p;+	dpages->ndpage = 1;+	return 0; /* success */++ out_dpages:+	kfree(dpages->dpages);+ out:+	return err;+}++void au_dpages_free(struct au_dcsub_pages *dpages)+{+	int i;+	struct au_dpage *p;++	p = dpages->dpages;+	for (i = 0; i < dpages->ndpage; i++)+		au_dpage_free(p++);+	kfree(dpages->dpages);+}++static int au_dpages_append(struct au_dcsub_pages *dpages,+			    struct dentry *dentry, gfp_t gfp)+{+	int err, sz;+	struct au_dpage *dpage;+	void *p;++	dpage = dpages->dpages + dpages->ndpage - 1;+	sz = PAGE_SIZE / sizeof(dentry);+	if (unlikely(dpage->ndentry >= sz)) {+		AuLabel(new dpage);+		err = -ENOMEM;+		sz = dpages->ndpage * sizeof(*dpages->dpages);+		p = au_kzrealloc(dpages->dpages, sz,+				 sz + sizeof(*dpages->dpages), gfp);+		if (unlikely(!p))+			goto out;++		dpages->dpages = p;+		dpage = dpages->dpages + dpages->ndpage;+		p = (void *)__get_free_page(gfp);+		if (unlikely(!p))+			goto out;++		dpage->ndentry = 0;+		dpage->dentries = p;+		dpages->ndpage++;+	}++	dpage->dentries[dpage->ndentry++] = dget(dentry);+	return 0; /* success */++ out:+	return err;+}++int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,+		   au_dpages_test test, void *arg)+{+	int err;+	struct dentry *this_parent = root;+	struct list_head *next;+	struct super_block *sb = root->d_sb;++	err = 0;+	spin_lock(&dcache_lock);+ repeat:+	next = this_parent->d_subdirs.next;+ resume:+	if (this_parent->d_sb == sb+	    && !IS_ROOT(this_parent)+	    && atomic_read(&this_parent->d_count)+	    && this_parent->d_inode+	    && (!test || test(this_parent, arg))) {+		err = au_dpages_append(dpages, this_parent, GFP_ATOMIC);+		if (unlikely(err))+			goto out;+	}++	while (next != &this_parent->d_subdirs) {+		struct list_head *tmp = next;+		struct dentry *dentry = list_entry(tmp, struct dentry,+						   d_u.d_child);+		next = tmp->next;+		if (/*d_unhashed(dentry) || */!dentry->d_inode)+			continue;+		if (!list_empty(&dentry->d_subdirs)) {+			this_parent = dentry;+			goto repeat;+		}+		if (dentry->d_sb == sb+		    && atomic_read(&dentry->d_count)+		    && (!test || test(dentry, arg))) {+			err = au_dpages_append(dpages, dentry, GFP_ATOMIC);+			if (unlikely(err))+				goto out;+		}+	}++	if (this_parent != root) {+		next = this_parent->d_u.d_child.next;+		this_parent = this_parent->d_parent; /* dcache_lock is locked */+		goto resume;+	}+ out:+	spin_unlock(&dcache_lock);+	return err;+}++int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry,+		       int do_include, au_dpages_test test, void *arg)+{+	int err;++	err = 0;+	spin_lock(&dcache_lock);+	if (do_include && (!test || test(dentry, arg))) {+		err = au_dpages_append(dpages, dentry, GFP_ATOMIC);+		if (unlikely(err))+			goto out;+	}+	while (!IS_ROOT(dentry)) {+		dentry = dentry->d_parent; /* dcache_lock is locked */+		if (!test || test(dentry, arg)) {+			err = au_dpages_append(dpages, dentry, GFP_ATOMIC);+			if (unlikely(err))+				break;+		}+	}++ out:+	spin_unlock(&dcache_lock);++	return err;+}++struct dentry *au_test_subdir(struct dentry *d1, struct dentry *d2)+{+	struct dentry *trap, **dentries;+	int err, i, j;+	struct au_dcsub_pages dpages;+	struct au_dpage *dpage;++	trap = ERR_PTR(-ENOMEM);+	err = au_dpages_init(&dpages, GFP_NOFS);+	if (unlikely(err))+		goto out;+	err = au_dcsub_pages_rev(&dpages, d1, /*do_include*/1, NULL, NULL);+	if (unlikely(err))+		goto out_dpages;++	trap = d1;+	for (i = 0; !err && i < dpages.ndpage; i++) {+		dpage = dpages.dpages + i;+		dentries = dpage->dentries;+		for (j = 0; !err && j < dpage->ndentry; j++) {+			struct dentry *d;++			d = dentries[j];+			err = (d == d2);+			if (!err)+				trap = d;+		}+	}+	if (!err)+		trap = NULL;++ out_dpages:+	au_dpages_free(&dpages);+ out:+	return trap;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/dcsub.h linux-2.6.31.5/fs/aufs/dcsub.h--- linux-2.6.31.5.orig/fs/aufs/dcsub.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/dcsub.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,54 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * sub-routines for dentry cache+ */++#ifndef __AUFS_DCSUB_H__+#define __AUFS_DCSUB_H__++#ifdef __KERNEL__++#include <linux/types.h>++struct dentry;++struct au_dpage {+	int ndentry;+	struct dentry **dentries;+};++struct au_dcsub_pages {+	int ndpage;+	struct au_dpage *dpages;+};++/* ---------------------------------------------------------------------- */++int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp);+void au_dpages_free(struct au_dcsub_pages *dpages);+typedef int (*au_dpages_test)(struct dentry *dentry, void *arg);+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,+		   au_dpages_test test, void *arg);+int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry,+		       int do_include, au_dpages_test test, void *arg);+struct dentry *au_test_subdir(struct dentry *d1, struct dentry *d2);++#endif /* __KERNEL__ */+#endif /* __AUFS_DCSUB_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/debug.c linux-2.6.31.5/fs/aufs/debug.c--- linux-2.6.31.5.orig/fs/aufs/debug.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/debug.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,427 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * debug print functions+ */++#include <linux/module.h>+#include <linux/vt_kern.h>+#include "aufs.h"++int aufs_debug;+MODULE_PARM_DESC(debug, "debug print");+module_param_named(debug, aufs_debug, int, S_IRUGO | S_IWUSR | S_IWGRP);++char *au_plevel = KERN_DEBUG;+#define dpri(fmt, arg...) do { \+	if (au_debug_test()) \+		printk("%s" fmt, au_plevel, ##arg); \+} while (0)++/* ---------------------------------------------------------------------- */++void au_dpri_whlist(struct au_nhash *whlist)+{+	unsigned long ul, n;+	struct hlist_head *head;+	struct au_vdir_wh *tpos;+	struct hlist_node *pos;++	n = whlist->nh_num;+	head = whlist->nh_head;+	for (ul = 0; ul < n; ul++) {+		hlist_for_each_entry(tpos, pos, head, wh_hash)+			dpri("b%d, %.*s, %d\n",+			     tpos->wh_bindex,+			     tpos->wh_str.len, tpos->wh_str.name,+			     tpos->wh_str.len);+		head++;+	}+}++void au_dpri_vdir(struct au_vdir *vdir)+{+	unsigned long ul;+	union au_vdir_deblk_p p;+	unsigned char *o;++	if (!vdir || IS_ERR(vdir)) {+		dpri("err %ld\n", PTR_ERR(vdir));+		return;+	}++	dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n",+	     vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk,+	     vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version);+	for (ul = 0; ul < vdir->vd_nblk; ul++) {+		p.deblk = vdir->vd_deblk[ul];+		o = p.deblk;+		dpri("[%lu]: %p\n", ul, o);+	}+}++static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode,+			struct dentry *wh)+{+	char *n = NULL;+	int l = 0;++	if (!inode || IS_ERR(inode)) {+		dpri("i%d: err %ld\n", bindex, PTR_ERR(inode));+		return -1;+	}++	/* the type of i_blocks depends upon CONFIG_LSF */+	BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long)+		     && sizeof(inode->i_blocks) != sizeof(u64));+	if (wh) {+		n = (void *)wh->d_name.name;+		l = wh->d_name.len;+	}++	dpri("i%d: i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu,"+	     " ct %lld, np %lu, st 0x%lx, f 0x%x, g %x%s%.*s\n",+	     bindex,+	     inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??",+	     atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode,+	     i_size_read(inode), (unsigned long long)inode->i_blocks,+	     (long long)timespec_to_ns(&inode->i_ctime) & 0x0ffff,+	     inode->i_mapping ? inode->i_mapping->nrpages : 0,+	     inode->i_state, inode->i_flags, inode->i_generation,+	     l ? ", wh " : "", l, n);+	return 0;+}++void au_dpri_inode(struct inode *inode)+{+	struct au_iinfo *iinfo;+	aufs_bindex_t bindex;+	int err;++	err = do_pri_inode(-1, inode, NULL);+	if (err || !au_test_aufs(inode->i_sb))+		return;++	iinfo = au_ii(inode);+	if (!iinfo)+		return;+	dpri("i-1: bstart %d, bend %d, gen %d\n",+	     iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode));+	if (iinfo->ii_bstart < 0)+		return;+	for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++)+		do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode,+			     iinfo->ii_hinode[0 + bindex].hi_whdentry);+}++static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry)+{+	struct dentry *wh = NULL;++	if (!dentry || IS_ERR(dentry)) {+		dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry));+		return -1;+	}+	/* do not call dget_parent() here */+	dpri("d%d: %.*s?/%.*s, %s, cnt %d, flags 0x%x\n",+	     bindex,+	     AuDLNPair(dentry->d_parent), AuDLNPair(dentry),+	     dentry->d_sb ? au_sbtype(dentry->d_sb) : "??",+	     atomic_read(&dentry->d_count), dentry->d_flags);+	if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) {+		struct au_iinfo *iinfo = au_ii(dentry->d_inode);+		if (iinfo)+			wh = iinfo->ii_hinode[0 + bindex].hi_whdentry;+	}+	do_pri_inode(bindex, dentry->d_inode, wh);+	return 0;+}++void au_dpri_dentry(struct dentry *dentry)+{+	struct au_dinfo *dinfo;+	aufs_bindex_t bindex;+	int err;++	err = do_pri_dentry(-1, dentry);+	if (err || !au_test_aufs(dentry->d_sb))+		return;++	dinfo = au_di(dentry);+	if (!dinfo)+		return;+	dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d\n",+	     dinfo->di_bstart, dinfo->di_bend,+	     dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry));+	if (dinfo->di_bstart < 0)+		return;+	for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++)+		do_pri_dentry(bindex, dinfo->di_hdentry[0 + bindex].hd_dentry);+}++static int do_pri_file(aufs_bindex_t bindex, struct file *file)+{+	char a[32];++	if (!file || IS_ERR(file)) {+		dpri("f%d: err %ld\n", bindex, PTR_ERR(file));+		return -1;+	}+	a[0] = 0;+	if (bindex < 0+	    && file->f_dentry+	    && au_test_aufs(file->f_dentry->d_sb)+	    && au_fi(file))+		snprintf(a, sizeof(a), ", mmapped %d", au_test_mmapped(file));+	dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, pos %llu%s\n",+	     bindex, file->f_mode, file->f_flags, (long)file_count(file),+	     file->f_pos, a);+	if (file->f_dentry)+		do_pri_dentry(bindex, file->f_dentry);+	return 0;+}++void au_dpri_file(struct file *file)+{+	struct au_finfo *finfo;+	aufs_bindex_t bindex;+	int err;++	err = do_pri_file(-1, file);+	if (err || !file->f_dentry || !au_test_aufs(file->f_dentry->d_sb))+		return;++	finfo = au_fi(file);+	if (!finfo)+		return;+	if (finfo->fi_bstart < 0)+		return;+	for (bindex = finfo->fi_bstart; bindex <= finfo->fi_bend; bindex++) {+		struct au_hfile *hf;++		hf = finfo->fi_hfile + bindex;+		do_pri_file(bindex, hf ? hf->hf_file : NULL);+	}+}++static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br)+{+	struct vfsmount *mnt;+	struct super_block *sb;++	if (!br || IS_ERR(br))+		goto out;+	mnt = br->br_mnt;+	if (!mnt || IS_ERR(mnt))+		goto out;+	sb = mnt->mnt_sb;+	if (!sb || IS_ERR(sb))+		goto out;++	dpri("s%d: {perm 0x%x, cnt %d, wbr %p}, "+	     "%s, dev 0x%02x%02x, flags 0x%lx, cnt(BIAS) %d, active %d, "+	     "xino %d\n",+	     bindex, br->br_perm, atomic_read(&br->br_count), br->br_wbr,+	     au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev),+	     sb->s_flags, sb->s_count - S_BIAS,+	     atomic_read(&sb->s_active), !!br->br_xino.xi_file);+	return 0;++ out:+	dpri("s%d: err %ld\n", bindex, PTR_ERR(br));+	return -1;+}++void au_dpri_sb(struct super_block *sb)+{+	struct au_sbinfo *sbinfo;+	aufs_bindex_t bindex;+	int err;+	/* to reuduce stack size */+	struct {+		struct vfsmount mnt;+		struct au_branch fake;+	} *a;++	/* this function can be called from magic sysrq */+	a = kzalloc(sizeof(*a), GFP_ATOMIC);+	if (unlikely(!a)) {+		dpri("no memory\n");+		return;+	}++	a->mnt.mnt_sb = sb;+	a->fake.br_perm = 0;+	a->fake.br_mnt = &a->mnt;+	a->fake.br_xino.xi_file = NULL;+	atomic_set(&a->fake.br_count, 0);+	smp_mb(); /* atomic_set */+	err = do_pri_br(-1, &a->fake);+	kfree(a);+	dpri("dev 0x%x\n", sb->s_dev);+	if (err || !au_test_aufs(sb))+		return;++	sbinfo = au_sbi(sb);+	if (!sbinfo)+		return;+	dpri("nw %d, gen %u, kobj %d\n",+	     atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation,+	     atomic_read(&sbinfo->si_kobj.kref.refcount));+	for (bindex = 0; bindex <= sbinfo->si_bend; bindex++)+		do_pri_br(bindex, sbinfo->si_branch[0 + bindex]);+}++/* ---------------------------------------------------------------------- */++void au_dbg_sleep_jiffy(int jiffy)+{+	while (jiffy)+		jiffy = schedule_timeout_uninterruptible(jiffy);+}++void au_dbg_iattr(struct iattr *ia)+{+#define AuBit(name)	if (ia->ia_valid & ATTR_ ## name) \+				dpri(#name "\n")+	AuBit(MODE);+	AuBit(UID);+	AuBit(GID);+	AuBit(SIZE);+	AuBit(ATIME);+	AuBit(MTIME);+	AuBit(CTIME);+	AuBit(ATIME_SET);+	AuBit(MTIME_SET);+	AuBit(FORCE);+	AuBit(ATTR_FLAG);+	AuBit(KILL_SUID);+	AuBit(KILL_SGID);+	AuBit(FILE);+	AuBit(KILL_PRIV);+	AuBit(OPEN);+	AuBit(TIMES_SET);+#undef	AuBit+	dpri("ia_file %p\n", ia->ia_file);+}++/* ---------------------------------------------------------------------- */++void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen)+{+	struct dentry *parent;++	parent = dget_parent(dentry);+	AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode)+		  || IS_ROOT(dentry)+		  || au_digen(parent) != sigen);+	dput(parent);+}++void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen)+{+	struct dentry *parent;++	parent = dget_parent(dentry);+	AuDebugOn(S_ISDIR(dentry->d_inode->i_mode)+		  || au_digen(parent) != sigen);+	dput(parent);+}++void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen)+{+	int err, i, j;+	struct au_dcsub_pages dpages;+	struct au_dpage *dpage;+	struct dentry **dentries;++	err = au_dpages_init(&dpages, GFP_NOFS);+	AuDebugOn(err);+	err = au_dcsub_pages_rev(&dpages, parent, /*do_include*/1, NULL, NULL);+	AuDebugOn(err);+	for (i = dpages.ndpage - 1; !err && i >= 0; i--) {+		dpage = dpages.dpages + i;+		dentries = dpage->dentries;+		for (j = dpage->ndentry - 1; !err && j >= 0; j--)+			AuDebugOn(au_digen(dentries[j]) != sigen);+	}+	au_dpages_free(&dpages);+}++void au_dbg_verify_hf(struct au_finfo *finfo)+{+	struct au_hfile *hf;+	aufs_bindex_t bend, bindex;++	if (finfo->fi_bstart >= 0) {+		bend = finfo->fi_bend;+		for (bindex = finfo->fi_bstart; bindex <= bend; bindex++) {+			hf = finfo->fi_hfile + bindex;+			AuDebugOn(hf->hf_file || hf->hf_br);+		}+	}+}++void au_dbg_verify_kthread(void)+{+	if (au_test_wkq(current)) {+		au_dbg_blocked();+		BUG();+	}+}++/* ---------------------------------------------------------------------- */++void au_debug_sbinfo_init(struct au_sbinfo *sbinfo __maybe_unused)+{+#ifdef AuForceNoPlink+	au_opt_clr(sbinfo->si_mntflags, PLINK);+#endif+#ifdef AuForceNoXino+	au_opt_clr(sbinfo->si_mntflags, XINO);+#endif+#ifdef AuForceNoRefrof+	au_opt_clr(sbinfo->si_mntflags, REFROF);+#endif+#ifdef AuForceHinotify+	au_opt_set_udba(sbinfo->si_mntflags, UDBA_HINOTIFY);+#endif+}++int __init au_debug_init(void)+{+	aufs_bindex_t bindex;+	struct au_vdir_destr destr;++	bindex = -1;+	AuDebugOn(bindex >= 0);++	destr.len = -1;+	AuDebugOn(destr.len < NAME_MAX);++#ifdef CONFIG_4KSTACKS+	AuWarn("CONFIG_4KSTACKS is defined.\n");+#endif++#ifdef AuForceNoBrs+	sysaufs_brs = 0;+#endif++	return 0;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/debug.h linux-2.6.31.5/fs/aufs/debug.h--- linux-2.6.31.5.orig/fs/aufs/debug.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/debug.h	2009-11-15 22:16:14.000000000 +0100@@ -0,0 +1,261 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * debug print functions+ */++#ifndef __AUFS_DEBUG_H__+#define __AUFS_DEBUG_H__++#ifdef __KERNEL__++#include <asm/system.h>+#include <linux/bug.h>+/* #include <linux/err.h> */+#include <linux/init.h>+/* #include <linux/kernel.h> */+#include <linux/delay.h>+/* #include <linux/kd.h> */+/* #include <linux/vt_kern.h> */+#include <linux/sysrq.h>+#include <linux/aufs_type.h>++#ifdef CONFIG_AUFS_DEBUG+#define AuDebugOn(a)		BUG_ON(a)++/* module parameter */+extern int aufs_debug;+static inline void au_debug(int n)+{+	aufs_debug = n;+	smp_mb();+}++static inline int au_debug_test(void)+{+	return aufs_debug;+}+#else+#define AuDebugOn(a)		do {} while (0)+#define au_debug()		do {} while (0)+static inline int au_debug_test(void)+{+	return 0;+}+#endif /* CONFIG_AUFS_DEBUG */++/* ---------------------------------------------------------------------- */++/* debug print */++#define AuDpri(lvl, fmt, arg...) \+	printk(lvl AUFS_NAME " %s:%d:%s[%d]: " fmt, \+	       __func__, __LINE__, current->comm, current->pid, ##arg)+#define AuDbg(fmt, arg...) do { \+	if (au_debug_test()) \+		AuDpri(KERN_DEBUG, "DEBUG: " fmt, ##arg); \+} while (0)+#define AuLabel(l) 		AuDbg(#l "\n")+#define AuInfo(fmt, arg...)	AuDpri(KERN_INFO, fmt, ##arg)+#define AuWarn(fmt, arg...)	AuDpri(KERN_WARNING, fmt, ##arg)+#define AuErr(fmt, arg...)	AuDpri(KERN_ERR, fmt, ##arg)+#define AuIOErr(fmt, arg...)	AuErr("I/O Error, " fmt, ##arg)+#define AuWarn1(fmt, arg...) do { \+	static unsigned char _c; \+	if (!_c++) \+		AuWarn(fmt, ##arg); \+} while (0)++#define AuErr1(fmt, arg...) do { \+	static unsigned char _c; \+	if (!_c++) \+		AuErr(fmt, ##arg); \+} while (0)++#define AuIOErr1(fmt, arg...) do { \+	static unsigned char _c; \+	if (!_c++) \+		AuIOErr(fmt, ##arg); \+} while (0)++#define AuUnsupportMsg	"This operation is not supported." \+			" Please report this application to aufs-users ML."+#define AuUnsupport(fmt, args...) do { \+	AuErr(AuUnsupportMsg "\n" fmt, ##args); \+	dump_stack(); \+} while (0)++#define AuTraceErr(e) do { \+	if (unlikely((e) < 0)) \+		AuDbg("err %d\n", (int)(e)); \+} while (0)++#define AuTraceErrPtr(p) do { \+	if (IS_ERR(p)) \+		AuDbg("err %ld\n", PTR_ERR(p)); \+} while (0)++/* dirty macros for debug print, use with "%.*s" and caution */+#define AuLNPair(qstr)		(qstr)->len, (qstr)->name+#define AuDLNPair(d)		AuLNPair(&(d)->d_name)++/* ---------------------------------------------------------------------- */++struct au_sbinfo;+struct au_finfo;+struct dentry;+#ifdef CONFIG_AUFS_DEBUG+extern char *au_plevel;+struct au_nhash;+void au_dpri_whlist(struct au_nhash *whlist);+struct au_vdir;+void au_dpri_vdir(struct au_vdir *vdir);+struct inode;+void au_dpri_inode(struct inode *inode);+void au_dpri_dentry(struct dentry *dentry);+struct file;+void au_dpri_file(struct file *filp);+struct super_block;+void au_dpri_sb(struct super_block *sb);++void au_dbg_sleep_jiffy(int jiffy);+struct iattr;+void au_dbg_iattr(struct iattr *ia);++void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen);+void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen);+void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen);+void au_dbg_verify_hf(struct au_finfo *finfo);+void au_dbg_verify_kthread(void);++int __init au_debug_init(void);+void au_debug_sbinfo_init(struct au_sbinfo *sbinfo);+#define AuDbgWhlist(w) do { \+	AuDbg(#w "\n"); \+	au_dpri_whlist(w); \+} while (0)++#define AuDbgVdir(v) do { \+	AuDbg(#v "\n"); \+	au_dpri_vdir(v); \+} while (0)++#define AuDbgInode(i) do { \+	AuDbg(#i "\n"); \+	au_dpri_inode(i); \+} while (0)++#define AuDbgDentry(d) do { \+	AuDbg(#d "\n"); \+	au_dpri_dentry(d); \+} while (0)++#define AuDbgFile(f) do { \+	AuDbg(#f "\n"); \+	au_dpri_file(f); \+} while (0)++#define AuDbgSb(sb) do { \+	AuDbg(#sb "\n"); \+	au_dpri_sb(sb); \+} while (0)++#define AuDbgSleep(sec) do { \+	AuDbg("sleep %d sec\n", sec); \+	ssleep(sec); \+} while (0)++#define AuDbgSleepJiffy(jiffy) do { \+	AuDbg("sleep %d jiffies\n", jiffy); \+	au_dbg_sleep_jiffy(jiffy); \+} while (0)++#define AuDbgIAttr(ia) do { \+	AuDbg("ia_valid 0x%x\n", (ia)->ia_valid); \+	au_dbg_iattr(ia); \+} while (0)+#else+static inline void au_dbg_verify_dir_parent(struct dentry *dentry,+					    unsigned int sigen)+{+	/* empty */+}+static inline void au_dbg_verify_nondir_parent(struct dentry *dentry,+					       unsigned int sigen)+{+	/* empty */+}+static inline void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen)+{+	/* empty */+}+static inline void au_dbg_verify_hf(struct au_finfo *finfo)+{+	/* empty */+}+static inline void au_dbg_verify_kthread(void)+{+	/* empty */+}++static inline int au_debug_init(void)+{+	return 0;+}+static inline void au_debug_sbinfo_init(struct au_sbinfo *sbinfo)+{+	/* empty */+}+#define AuDbgWhlist(w)		do {} while (0)+#define AuDbgVdir(v)		do {} while (0)+#define AuDbgInode(i)		do {} while (0)+#define AuDbgDentry(d)		do {} while (0)+#define AuDbgFile(f)		do {} while (0)+#define AuDbgSb(sb)		do {} while (0)+#define AuDbgSleep(sec)		do {} while (0)+#define AuDbgSleepJiffy(jiffy)	do {} while (0)+#define AuDbgIAttr(ia)		do {} while (0)+#endif /* CONFIG_AUFS_DEBUG */++/* ---------------------------------------------------------------------- */++#ifdef CONFIG_AUFS_MAGIC_SYSRQ+int __init au_sysrq_init(void);+void au_sysrq_fin(void);++#ifdef CONFIG_HW_CONSOLE+#define au_dbg_blocked() do { \+	WARN_ON(1); \+	handle_sysrq('w', vc_cons[fg_console].d->vc_tty); \+} while (0)+#else+#define au_dbg_blocked()	do {} while (0)+#endif++#else+static inline int au_sysrq_init(void)+{+	return 0;+}+#define au_sysrq_fin()		do {} while (0)+#define au_dbg_blocked()	do {} while (0)+#endif /* CONFIG_AUFS_MAGIC_SYSRQ */++#endif /* __KERNEL__ */+#endif /* __AUFS_DEBUG_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/dentry.c linux-2.6.31.5/fs/aufs/dentry.c--- linux-2.6.31.5.orig/fs/aufs/dentry.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/dentry.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,880 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * lookup and dentry operations+ */++#include <linux/namei.h>+#include "aufs.h"++static void au_h_nd(struct nameidata *h_nd, struct nameidata *nd)+{+	if (nd) {+		*h_nd = *nd;++		/*+		 * gave up supporting LOOKUP_CREATE/OPEN for lower fs,+		 * due to whiteout and branch permission.+		 */+		h_nd->flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE+				 | LOOKUP_FOLLOW);+		/* unnecessary? */+		h_nd->intent.open.file = NULL;+	} else+		memset(h_nd, 0, sizeof(*h_nd));+}++struct au_lkup_one_args {+	struct dentry **errp;+	struct qstr *name;+	struct dentry *h_parent;+	struct au_branch *br;+	struct nameidata *nd;+};++struct dentry *au_lkup_one(struct qstr *name, struct dentry *h_parent,+			   struct au_branch *br, struct nameidata *nd)+{+	struct dentry *h_dentry;+	int err;+	struct nameidata h_nd;++	if (au_test_fs_null_nd(h_parent->d_sb))+		return vfsub_lookup_one_len(name->name, h_parent, name->len);++	au_h_nd(&h_nd, nd);+	h_nd.path.dentry = h_parent;+	h_nd.path.mnt = br->br_mnt;++	err = __lookup_one_len(name->name, &h_nd.last, NULL, name->len);+	h_dentry = ERR_PTR(err);+	if (!err) {+		path_get(&h_nd.path);+		h_dentry = vfsub_lookup_hash(&h_nd);+		path_put(&h_nd.path);+	}++	return h_dentry;+}++static void au_call_lkup_one(void *args)+{+	struct au_lkup_one_args *a = args;+	*a->errp = au_lkup_one(a->name, a->h_parent, a->br, a->nd);+}++#define AuLkup_ALLOW_NEG	1+#define au_ftest_lkup(flags, name)	((flags) & AuLkup_##name)+#define au_fset_lkup(flags, name)	{ (flags) |= AuLkup_##name; }+#define au_fclr_lkup(flags, name)	{ (flags) &= ~AuLkup_##name; }++struct au_do_lookup_args {+	unsigned int		flags;+	mode_t			type;+	struct nameidata	*nd;+};++/*+ * returns positive/negative dentry, NULL or an error.+ * NULL means whiteout-ed or not-found.+ */+static struct dentry*+au_do_lookup(struct dentry *h_parent, struct dentry *dentry,+	     aufs_bindex_t bindex, struct qstr *wh_name,+	     struct au_do_lookup_args *args)+{+	struct dentry *h_dentry;+	struct inode *h_inode, *inode;+	struct qstr *name;+	struct au_branch *br;+	int wh_found, opq;+	unsigned char wh_able;+	const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG);++	name = &dentry->d_name;+	wh_found = 0;+	br = au_sbr(dentry->d_sb, bindex);+	wh_able = !!au_br_whable(br->br_perm);+	if (wh_able)+		wh_found = au_wh_test(h_parent, wh_name, br, /*try_sio*/0);+	h_dentry = ERR_PTR(wh_found);+	if (!wh_found)+		goto real_lookup;+	if (unlikely(wh_found < 0))+		goto out;++	/* We found a whiteout */+	/* au_set_dbend(dentry, bindex); */+	au_set_dbwh(dentry, bindex);+	if (!allow_neg)+		return NULL; /* success */++ real_lookup:+	h_dentry = au_lkup_one(name, h_parent, br, args->nd);+	if (IS_ERR(h_dentry))+		goto out;++	h_inode = h_dentry->d_inode;+	if (!h_inode) {+		if (!allow_neg)+			goto out_neg;+	} else if (wh_found+		   || (args->type && args->type != (h_inode->i_mode & S_IFMT)))+		goto out_neg;++	if (au_dbend(dentry) <= bindex)+		au_set_dbend(dentry, bindex);+	if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry))+		au_set_dbstart(dentry, bindex);+	au_set_h_dptr(dentry, bindex, h_dentry);++	inode = dentry->d_inode;+	if (!h_inode || !S_ISDIR(h_inode->i_mode) || !wh_able+	    || (inode && !S_ISDIR(inode->i_mode)))+		goto out; /* success */++	mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);+	opq = au_diropq_test(h_dentry, br);+	mutex_unlock(&h_inode->i_mutex);+	if (opq > 0)+		au_set_dbdiropq(dentry, bindex);+	else if (unlikely(opq < 0)) {+		au_set_h_dptr(dentry, bindex, NULL);+		h_dentry = ERR_PTR(opq);+	}+	goto out;++ out_neg:+	dput(h_dentry);+	h_dentry = NULL;+ out:+	return h_dentry;+}++static int au_test_shwh(struct super_block *sb, const struct qstr *name)+{+	if (unlikely(!au_opt_test(au_mntflags(sb), SHWH)+		     && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))+		return -EPERM;+	return 0;+}++/*+ * returns the number of lower positive dentries,+ * otherwise an error.+ * can be called at unlinking with @type is zero.+ */+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,+		   struct nameidata *nd)+{+	int npositive, err;+	aufs_bindex_t bindex, btail, bdiropq;+	unsigned char isdir;+	struct qstr whname;+	struct au_do_lookup_args args = {+		.flags	= 0,+		.type	= type,+		.nd	= nd+	};+	const struct qstr *name = &dentry->d_name;+	struct dentry *parent;+	struct inode *inode;++	parent = dget_parent(dentry);+	err = au_test_shwh(dentry->d_sb, name);+	if (unlikely(err))+		goto out;++	err = au_wh_name_alloc(&whname, name);+	if (unlikely(err))+		goto out;++	inode = dentry->d_inode;+	isdir = !!(inode && S_ISDIR(inode->i_mode));+	if (!type)+		au_fset_lkup(args.flags, ALLOW_NEG);++	npositive = 0;+	btail = au_dbtaildir(parent);+	for (bindex = bstart; bindex <= btail; bindex++) {+		struct dentry *h_parent, *h_dentry;+		struct inode *h_inode, *h_dir;++		h_dentry = au_h_dptr(dentry, bindex);+		if (h_dentry) {+			if (h_dentry->d_inode)+				npositive++;+			if (type != S_IFDIR)+				break;+			continue;+		}+		h_parent = au_h_dptr(parent, bindex);+		if (!h_parent)+			continue;+		h_dir = h_parent->d_inode;+		if (!h_dir || !S_ISDIR(h_dir->i_mode))+			continue;++		mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);+		h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname,+					&args);+		mutex_unlock(&h_dir->i_mutex);+		err = PTR_ERR(h_dentry);+		if (IS_ERR(h_dentry))+			goto out_wh;+		au_fclr_lkup(args.flags, ALLOW_NEG);++		if (au_dbwh(dentry) >= 0)+			break;+		if (!h_dentry)+			continue;+		h_inode = h_dentry->d_inode;+		if (!h_inode)+			continue;+		npositive++;+		if (!args.type)+			args.type = h_inode->i_mode & S_IFMT;+		if (args.type != S_IFDIR)+			break;+		else if (isdir) {+			/* the type of lower may be different */+			bdiropq = au_dbdiropq(dentry);+			if (bdiropq >= 0 && bdiropq <= bindex)+				break;+		}+	}++	if (npositive) {+		AuLabel(positive);+		au_update_dbstart(dentry);+	}+	err = npositive;+	if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE)+		     && au_dbstart(dentry) < 0))+		/* both of real entry and whiteout found */+		err = -EIO;++ out_wh:+	kfree(whname.name);+ out:+	dput(parent);+	return err;+}++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent,+			       struct au_branch *br)+{+	struct dentry *dentry;+	int wkq_err;++	if (!au_test_h_perm_sio(parent->d_inode, MAY_EXEC))+		dentry = au_lkup_one(name, parent, br, /*nd*/NULL);+	else {+		struct au_lkup_one_args args = {+			.errp		= &dentry,+			.name		= name,+			.h_parent	= parent,+			.br		= br,+			.nd		= NULL+		};++		wkq_err = au_wkq_wait(au_call_lkup_one, &args);+		if (unlikely(wkq_err))+			dentry = ERR_PTR(wkq_err);+	}++	return dentry;+}++/*+ * lookup @dentry on @bindex which should be negative.+ */+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex)+{+	int err;+	struct dentry *parent, *h_parent, *h_dentry;+	struct qstr *name;++	name = &dentry->d_name;+	parent = dget_parent(dentry);+	h_parent = au_h_dptr(parent, bindex);+	h_dentry = au_sio_lkup_one(name, h_parent,+				   au_sbr(dentry->d_sb, bindex));+	err = PTR_ERR(h_dentry);+	if (IS_ERR(h_dentry))+		goto out;+	if (unlikely(h_dentry->d_inode)) {+		err = -EIO;+		AuIOErr("b%d %.*s should be negative.\n",+			bindex, AuDLNPair(h_dentry));+		dput(h_dentry);+		goto out;+	}++	if (bindex < au_dbstart(dentry))+		au_set_dbstart(dentry, bindex);+	if (au_dbend(dentry) < bindex)+		au_set_dbend(dentry, bindex);+	au_set_h_dptr(dentry, bindex, h_dentry);+	err = 0;++ out:+	dput(parent);+	return err;+}++/* ---------------------------------------------------------------------- */++/* subset of struct inode */+struct au_iattr {+	unsigned long		i_ino;+	/* unsigned int		i_nlink; */+	uid_t			i_uid;+	gid_t			i_gid;+	u64			i_version;+/*+	loff_t			i_size;+	blkcnt_t		i_blocks;+*/+	umode_t			i_mode;+};++static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode)+{+	ia->i_ino = h_inode->i_ino;+	/* ia->i_nlink = h_inode->i_nlink; */+	ia->i_uid = h_inode->i_uid;+	ia->i_gid = h_inode->i_gid;+	ia->i_version = h_inode->i_version;+/*+	ia->i_size = h_inode->i_size;+	ia->i_blocks = h_inode->i_blocks;+*/+	ia->i_mode = (h_inode->i_mode & S_IFMT);+}++static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode)+{+	return ia->i_ino != h_inode->i_ino+		/* || ia->i_nlink != h_inode->i_nlink */+		|| ia->i_uid != h_inode->i_uid+		|| ia->i_gid != h_inode->i_gid+		|| ia->i_version != h_inode->i_version+/*+		|| ia->i_size != h_inode->i_size+		|| ia->i_blocks != h_inode->i_blocks+*/+		|| ia->i_mode != (h_inode->i_mode & S_IFMT);+}++static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent,+			      struct au_branch *br)+{+	int err;+	struct au_iattr ia;+	struct inode *h_inode;+	struct dentry *h_d;+	struct super_block *h_sb;++	err = 0;+	memset(&ia, -1, sizeof(ia));+	h_sb = h_dentry->d_sb;+	h_inode = h_dentry->d_inode;+	if (h_inode)+		au_iattr_save(&ia, h_inode);+	else if (au_test_nfs(h_sb) || au_test_fuse(h_sb))+		/* nfs d_revalidate may return 0 for negative dentry */+		/* fuse d_revalidate always return 0 for negative dentry */+		goto out;++	/* main purpose is namei.c:cached_lookup() and d_revalidate */+	h_d = au_lkup_one(&h_dentry->d_name, h_parent, br, /*nd*/NULL);+	err = PTR_ERR(h_d);+	if (IS_ERR(h_d))+		goto out;++	err = 0;+	if (unlikely(h_d != h_dentry+		     || h_d->d_inode != h_inode+		     || (h_inode && au_iattr_test(&ia, h_inode))))+		err = au_busy_or_stale();+	dput(h_d);++ out:+	AuTraceErr(err);+	return err;+}++int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir,+		struct dentry *h_parent, struct au_branch *br)+{+	int err;++	err = 0;+	if (udba == AuOpt_UDBA_REVAL) {+		IMustLock(h_dir);+		err = (h_dentry->d_parent->d_inode != h_dir);+	} else if (udba == AuOpt_UDBA_HINOTIFY)+		err = au_h_verify_dentry(h_dentry, h_parent, br);++	return err;+}++/* ---------------------------------------------------------------------- */++static void au_do_refresh_hdentry(struct au_hdentry *p, struct au_dinfo *dinfo,+				  struct dentry *parent)+{+	struct dentry *h_d, *h_dp;+	struct au_hdentry tmp, *q;+	struct super_block *sb;+	aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq;++	AuRwMustWriteLock(&dinfo->di_rwsem);++	bend = dinfo->di_bend;+	bwh = dinfo->di_bwh;+	bdiropq = dinfo->di_bdiropq;+	for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) {+		h_d = p->hd_dentry;+		if (!h_d)+			continue;++		h_dp = dget_parent(h_d);+		if (h_dp == au_h_dptr(parent, bindex)) {+			dput(h_dp);+			continue;+		}++		new_bindex = au_find_dbindex(parent, h_dp);+		dput(h_dp);+		if (dinfo->di_bwh == bindex)+			bwh = new_bindex;+		if (dinfo->di_bdiropq == bindex)+			bdiropq = new_bindex;+		if (new_bindex < 0) {+			au_hdput(p);+			p->hd_dentry = NULL;+			continue;+		}++		/* swap two lower dentries, and loop again */+		q = dinfo->di_hdentry + new_bindex;+		tmp = *q;+		*q = *p;+		*p = tmp;+		if (tmp.hd_dentry) {+			bindex--;+			p--;+		}+	}++	sb = parent->d_sb;+	dinfo->di_bwh = -1;+	if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh))+		dinfo->di_bwh = bwh;++	dinfo->di_bdiropq = -1;+	if (bdiropq >= 0+	    && bdiropq <= au_sbend(sb)+	    && au_sbr_whable(sb, bdiropq))+		dinfo->di_bdiropq = bdiropq;++	bend = au_dbend(parent);+	p = dinfo->di_hdentry;+	for (bindex = 0; bindex <= bend; bindex++, p++)+		if (p->hd_dentry) {+			dinfo->di_bstart = bindex;+			break;+		}++	p = dinfo->di_hdentry + bend;+	for (bindex = bend; bindex >= 0; bindex--, p--)+		if (p->hd_dentry) {+			dinfo->di_bend = bindex;+			break;+		}+}++/*+ * returns the number of found lower positive dentries,+ * otherwise an error.+ */+int au_refresh_hdentry(struct dentry *dentry, mode_t type)+{+	int npositive, err;+	unsigned int sigen;+	aufs_bindex_t bstart;+	struct au_dinfo *dinfo;+	struct super_block *sb;+	struct dentry *parent;++	DiMustWriteLock(dentry);++	sb = dentry->d_sb;+	AuDebugOn(IS_ROOT(dentry));+	sigen = au_sigen(sb);+	parent = dget_parent(dentry);+	AuDebugOn(au_digen(parent) != sigen+		  || au_iigen(parent->d_inode) != sigen);++	dinfo = au_di(dentry);+	err = au_di_realloc(dinfo, au_sbend(sb) + 1);+	npositive = err;+	if (unlikely(err))+		goto out;+	au_do_refresh_hdentry(dinfo->di_hdentry + dinfo->di_bstart, dinfo,+			      parent);++	npositive = 0;+	bstart = au_dbstart(parent);+	if (type != S_IFDIR && dinfo->di_bstart == bstart)+		goto out_dgen; /* success */++	npositive = au_lkup_dentry(dentry, bstart, type, /*nd*/NULL);+	if (npositive < 0)+		goto out;+	if (dinfo->di_bwh >= 0 && dinfo->di_bwh <= dinfo->di_bstart)+		d_drop(dentry);++ out_dgen:+	au_update_digen(dentry);+ out:+	dput(parent);+	AuTraceErr(npositive);+	return npositive;+}++static noinline_for_stack+int au_do_h_d_reval(struct dentry *h_dentry, struct nameidata *nd,+		    struct dentry *dentry, aufs_bindex_t bindex)+{+	int err, valid;+	int (*reval)(struct dentry *, struct nameidata *);++	err = 0;+	reval = NULL;+	if (h_dentry->d_op)+		reval = h_dentry->d_op->d_revalidate;+	if (!reval)+		goto out;++	AuDbg("b%d\n", bindex);+	if (au_test_fs_null_nd(h_dentry->d_sb))+		/* it may return tri-state */+		valid = reval(h_dentry, NULL);+	else {+		struct nameidata h_nd;+		int locked;+		struct dentry *parent;++		au_h_nd(&h_nd, nd);+		parent = nd->path.dentry;+		locked = (nd && nd->path.dentry != dentry);+		if (locked)+			di_read_lock_parent(parent, AuLock_IR);+		BUG_ON(bindex > au_dbend(parent));+		h_nd.path.dentry = au_h_dptr(parent, bindex);+		BUG_ON(!h_nd.path.dentry);+		h_nd.path.mnt = au_sbr(parent->d_sb, bindex)->br_mnt;+		path_get(&h_nd.path);+		valid = reval(h_dentry, &h_nd);+		path_put(&h_nd.path);+		if (locked)+			di_read_unlock(parent, AuLock_IR);+	}++	if (unlikely(valid < 0))+		err = valid;+	else if (!valid)+		err = -EINVAL;++ out:+	AuTraceErr(err);+	return err;+}++/* todo: remove this */+static int h_d_revalidate(struct dentry *dentry, struct inode *inode,+			  struct nameidata *nd, int do_udba)+{+	int err;+	umode_t mode, h_mode;+	aufs_bindex_t bindex, btail, bstart, ibs, ibe;+	unsigned char plus, unhashed, is_root, h_plus;+	struct inode *first, *h_inode, *h_cached_inode;+	struct dentry *h_dentry;+	struct qstr *name, *h_name;++	err = 0;+	plus = 0;+	mode = 0;+	first = NULL;+	ibs = -1;+	ibe = -1;+	unhashed = !!d_unhashed(dentry);+	is_root = !!IS_ROOT(dentry);+	name = &dentry->d_name;++	/*+	 * Theoretically, REVAL test should be unnecessary in case of INOTIFY.+	 * But inotify doesn't fire some necessary events,+	 *	IN_ATTRIB for atime/nlink/pageio+	 *	IN_DELETE for NFS dentry+	 * Let's do REVAL test too.+	 */+	if (do_udba && inode) {+		mode = (inode->i_mode & S_IFMT);+		plus = (inode->i_nlink > 0);+		first = au_h_iptr(inode, au_ibstart(inode));+		ibs = au_ibstart(inode);+		ibe = au_ibend(inode);+	}++	bstart = au_dbstart(dentry);+	btail = bstart;+	if (inode && S_ISDIR(inode->i_mode))+		btail = au_dbtaildir(dentry);+	for (bindex = bstart; bindex <= btail; bindex++) {+		h_dentry = au_h_dptr(dentry, bindex);+		if (!h_dentry)+			continue;++		AuDbg("b%d, %.*s\n", bindex, AuDLNPair(h_dentry));+		h_name = &h_dentry->d_name;+		if (unlikely(do_udba+			     && !is_root+			     && (unhashed != !!d_unhashed(h_dentry)+				 || name->len != h_name->len+				 || memcmp(name->name, h_name->name, name->len))+			    )) {+			AuDbg("unhash 0x%x 0x%x, %.*s %.*s\n",+				  unhashed, d_unhashed(h_dentry),+				  AuDLNPair(dentry), AuDLNPair(h_dentry));+			goto err;+		}++		err = au_do_h_d_reval(h_dentry, nd, dentry, bindex);+		if (unlikely(err))+			/* do not goto err, to keep the errno */+			break;++		/* todo: plink too? */+		if (!do_udba)+			continue;++		/* UDBA tests */+		h_inode = h_dentry->d_inode;+		if (unlikely(!!inode != !!h_inode))+			goto err;++		h_plus = plus;+		h_mode = mode;+		h_cached_inode = h_inode;+		if (h_inode) {+			h_mode = (h_inode->i_mode & S_IFMT);+			h_plus = (h_inode->i_nlink > 0);+		}+		if (inode && ibs <= bindex && bindex <= ibe)+			h_cached_inode = au_h_iptr(inode, bindex);++		if (unlikely(plus != h_plus+			     || mode != h_mode+			     || h_cached_inode != h_inode))+			goto err;+		continue;++	err:+		err = -EINVAL;+		break;+	}++	return err;+}++static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen)+{+	int err;+	struct dentry *parent;+	struct inode *inode;++	inode = dentry->d_inode;+	if (au_digen(dentry) == sigen && au_iigen(inode) == sigen)+		return 0;++	parent = dget_parent(dentry);+	di_read_lock_parent(parent, AuLock_IR);+	AuDebugOn(au_digen(parent) != sigen+		  || au_iigen(parent->d_inode) != sigen);+	au_dbg_verify_gen(parent, sigen);++	/* returns a number of positive dentries */+	err = au_refresh_hdentry(dentry, inode->i_mode & S_IFMT);+	if (err >= 0)+		err = au_refresh_hinode(inode, dentry);++	di_read_unlock(parent, AuLock_IR);+	dput(parent);+	return err;+}++int au_reval_dpath(struct dentry *dentry, unsigned int sigen)+{+	int err;+	struct dentry *d, *parent;+	struct inode *inode;++	if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS))+		return simple_reval_dpath(dentry, sigen);++	/* slow loop, keep it simple and stupid */+	/* cf: au_cpup_dirs() */+	err = 0;+	parent = NULL;+	while (au_digen(dentry) != sigen+	       || au_iigen(dentry->d_inode) != sigen) {+		d = dentry;+		while (1) {+			dput(parent);+			parent = dget_parent(d);+			if (au_digen(parent) == sigen+			    && au_iigen(parent->d_inode) == sigen)+				break;+			d = parent;+		}++		inode = d->d_inode;+		if (d != dentry)+			di_write_lock_child(d);++		/* someone might update our dentry while we were sleeping */+		if (au_digen(d) != sigen || au_iigen(d->d_inode) != sigen) {+			di_read_lock_parent(parent, AuLock_IR);+			/* returns a number of positive dentries */+			err = au_refresh_hdentry(d, inode->i_mode & S_IFMT);+			if (err >= 0)+				err = au_refresh_hinode(inode, d);+			di_read_unlock(parent, AuLock_IR);+		}++		if (d != dentry)+			di_write_unlock(d);+		dput(parent);+		if (unlikely(err))+			break;+	}++	return err;+}++/*+ * if valid returns 1, otherwise 0.+ */+static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd)+{+	int valid, err;+	unsigned int sigen;+	unsigned char do_udba;+	struct super_block *sb;+	struct inode *inode;++	err = -EINVAL;+	sb = dentry->d_sb;+	inode = dentry->d_inode;+	aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW);+	sigen = au_sigen(sb);+	if (au_digen(dentry) != sigen) {+		AuDebugOn(IS_ROOT(dentry));+		if (inode)+			err = au_reval_dpath(dentry, sigen);+		if (unlikely(err))+			goto out_dgrade;+		AuDebugOn(au_digen(dentry) != sigen);+	}+	if (inode && au_iigen(inode) != sigen) {+		AuDebugOn(IS_ROOT(dentry));+		err = au_refresh_hinode(inode, dentry);+		if (unlikely(err))+			goto out_dgrade;+		AuDebugOn(au_iigen(inode) != sigen);+	}+	di_downgrade_lock(dentry, AuLock_IR);++	AuDebugOn(au_digen(dentry) != sigen);+	AuDebugOn(inode && au_iigen(inode) != sigen);+	err = -EINVAL;+	do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE);+	if (do_udba && inode) {+		aufs_bindex_t bstart = au_ibstart(inode);++		if (bstart >= 0+		    && au_test_higen(inode, au_h_iptr(inode, bstart)))+			goto out;+	}++	err = h_d_revalidate(dentry, inode, nd, do_udba);+	if (unlikely(!err && do_udba && au_dbstart(dentry) < 0))+		/* both of real entry and whiteout found */+		err = -EIO;+	goto out;++ out_dgrade:+	di_downgrade_lock(dentry, AuLock_IR);+ out:+	au_store_oflag(nd, inode);+	aufs_read_unlock(dentry, AuLock_IR);+	AuTraceErr(err);+	valid = !err;+	if (!valid)+		AuDbg("%.*s invalid\n", AuDLNPair(dentry));+	return valid;+}++static void aufs_d_release(struct dentry *dentry)+{+	struct au_dinfo *dinfo;+	aufs_bindex_t bend, bindex;++	dinfo = dentry->d_fsdata;+	if (!dinfo)+		return;++	/* dentry may not be revalidated */+	bindex = dinfo->di_bstart;+	if (bindex >= 0) {+		struct au_hdentry *p;++		bend = dinfo->di_bend;+		p = dinfo->di_hdentry + bindex;+		while (bindex++ <= bend) {+			if (p->hd_dentry)+				au_hdput(p);+			p++;+		}+	}+	kfree(dinfo->di_hdentry);+	AuRwDestroy(&dinfo->di_rwsem);+	au_cache_free_dinfo(dinfo);+	au_hin_di_reinit(dentry);+}++struct dentry_operations aufs_dop = {+	.d_revalidate	= aufs_d_revalidate,+	.d_release	= aufs_d_release+};diff -Nur linux-2.6.31.5.orig/fs/aufs/dentry.h linux-2.6.31.5/fs/aufs/dentry.h--- linux-2.6.31.5.orig/fs/aufs/dentry.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/dentry.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,231 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * lookup and dentry operations+ */++#ifndef __AUFS_DENTRY_H__+#define __AUFS_DENTRY_H__++#ifdef __KERNEL__++#include <linux/dcache.h>+#include <linux/aufs_type.h>+#include "rwsem.h"++/* make a single member structure for future use */+/* todo: remove this structure */+struct au_hdentry {+	struct dentry		*hd_dentry;+};++struct au_dinfo {+	atomic_t		di_generation;++	struct au_rwsem		di_rwsem;+	aufs_bindex_t		di_bstart, di_bend, di_bwh, di_bdiropq;+	struct au_hdentry	*di_hdentry;+};++/* ---------------------------------------------------------------------- */++/* dentry.c */+extern struct dentry_operations aufs_dop;+struct au_branch;+struct dentry *au_lkup_one(struct qstr *name, struct dentry *h_parent,+			   struct au_branch *br, struct nameidata *nd);+struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent,+			       struct au_branch *br);+int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir,+		struct dentry *h_parent, struct au_branch *br);++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,+		   struct nameidata *nd);+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex);+int au_refresh_hdentry(struct dentry *dentry, mode_t type);+int au_reval_dpath(struct dentry *dentry, unsigned int sigen);++/* dinfo.c */+int au_alloc_dinfo(struct dentry *dentry);+int au_di_realloc(struct au_dinfo *dinfo, int nbr);++void di_read_lock(struct dentry *d, int flags, unsigned int lsc);+void di_read_unlock(struct dentry *d, int flags);+void di_downgrade_lock(struct dentry *d, int flags);+void di_write_lock(struct dentry *d, unsigned int lsc);+void di_write_unlock(struct dentry *d);+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir);+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir);+void di_write_unlock2(struct dentry *d1, struct dentry *d2);++struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex);+aufs_bindex_t au_dbtail(struct dentry *dentry);+aufs_bindex_t au_dbtaildir(struct dentry *dentry);++void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,+		   struct dentry *h_dentry);+void au_update_digen(struct dentry *dentry);+void au_update_dbrange(struct dentry *dentry, int do_put_zero);+void au_update_dbstart(struct dentry *dentry);+void au_update_dbend(struct dentry *dentry);+int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry);++/* ---------------------------------------------------------------------- */++static inline struct au_dinfo *au_di(struct dentry *dentry)+{+	return dentry->d_fsdata;+}++/* ---------------------------------------------------------------------- */++/* lock subclass for dinfo */+enum {+	AuLsc_DI_CHILD,		/* child first */+	AuLsc_DI_CHILD2,	/* rename(2), link(2), and cpup at hinotify */+	AuLsc_DI_CHILD3,	/* copyup dirs */+	AuLsc_DI_PARENT,+	AuLsc_DI_PARENT2,+	AuLsc_DI_PARENT3+};++/*+ * di_read_lock_child, di_write_lock_child,+ * di_read_lock_child2, di_write_lock_child2,+ * di_read_lock_child3, di_write_lock_child3,+ * di_read_lock_parent, di_write_lock_parent,+ * di_read_lock_parent2, di_write_lock_parent2,+ * di_read_lock_parent3, di_write_lock_parent3,+ */+#define AuReadLockFunc(name, lsc) \+static inline void di_read_lock_##name(struct dentry *d, int flags) \+{ di_read_lock(d, flags, AuLsc_DI_##lsc); }++#define AuWriteLockFunc(name, lsc) \+static inline void di_write_lock_##name(struct dentry *d) \+{ di_write_lock(d, AuLsc_DI_##lsc); }++#define AuRWLockFuncs(name, lsc) \+	AuReadLockFunc(name, lsc) \+	AuWriteLockFunc(name, lsc)++AuRWLockFuncs(child, CHILD);+AuRWLockFuncs(child2, CHILD2);+AuRWLockFuncs(child3, CHILD3);+AuRWLockFuncs(parent, PARENT);+AuRWLockFuncs(parent2, PARENT2);+AuRWLockFuncs(parent3, PARENT3);++#undef AuReadLockFunc+#undef AuWriteLockFunc+#undef AuRWLockFuncs++#define DiMustNoWaiters(d)	AuRwMustNoWaiters(&au_di(d)->di_rwsem)+#define DiMustAnyLock(d)	AuRwMustAnyLock(&au_di(d)->di_rwsem)+#define DiMustWriteLock(d)	AuRwMustWriteLock(&au_di(d)->di_rwsem)++/* ---------------------------------------------------------------------- */++/* todo: memory barrier? */+static inline unsigned int au_digen(struct dentry *d)+{+	return atomic_read(&au_di(d)->di_generation);+}++static inline void au_h_dentry_init(struct au_hdentry *hdentry)+{+	hdentry->hd_dentry = NULL;+}++static inline void au_hdput(struct au_hdentry *hd)+{+	dput(hd->hd_dentry);+}++static inline aufs_bindex_t au_dbstart(struct dentry *dentry)+{+	DiMustAnyLock(dentry);+	return au_di(dentry)->di_bstart;+}++static inline aufs_bindex_t au_dbend(struct dentry *dentry)+{+	DiMustAnyLock(dentry);+	return au_di(dentry)->di_bend;+}++static inline aufs_bindex_t au_dbwh(struct dentry *dentry)+{+	DiMustAnyLock(dentry);+	return au_di(dentry)->di_bwh;+}++static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry)+{+	DiMustAnyLock(dentry);+	return au_di(dentry)->di_bdiropq;+}++/* todo: hard/soft set? */+static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex)+{+	DiMustWriteLock(dentry);+	au_di(dentry)->di_bstart = bindex;+}++static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex)+{+	DiMustWriteLock(dentry);+	au_di(dentry)->di_bend = bindex;+}++static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex)+{+	DiMustWriteLock(dentry);+	/* dbwh can be outside of bstart - bend range */+	au_di(dentry)->di_bwh = bindex;+}++static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex)+{+	DiMustWriteLock(dentry);+	au_di(dentry)->di_bdiropq = bindex;+}++/* ---------------------------------------------------------------------- */++#ifdef CONFIG_AUFS_HINOTIFY+static inline void au_digen_dec(struct dentry *d)+{+	atomic_dec_return(&au_di(d)->di_generation);+}++static inline void au_hin_di_reinit(struct dentry *dentry)+{+	dentry->d_fsdata = NULL;+}+#else+static inline void au_hin_di_reinit(struct dentry *dentry __maybe_unused)+{+	/* empty */+}+#endif /* CONFIG_AUFS_HINOTIFY */++#endif /* __KERNEL__ */+#endif /* __AUFS_DENTRY_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/dinfo.c linux-2.6.31.5/fs/aufs/dinfo.c--- linux-2.6.31.5.orig/fs/aufs/dinfo.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/dinfo.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,367 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * dentry private data+ */++#include "aufs.h"++int au_alloc_dinfo(struct dentry *dentry)+{+	struct au_dinfo *dinfo;+	struct super_block *sb;+	int nbr;++	dinfo = au_cache_alloc_dinfo();+	if (unlikely(!dinfo))+		goto out;++	sb = dentry->d_sb;+	nbr = au_sbend(sb) + 1;+	if (nbr <= 0)+		nbr = 1;+	dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS);+	if (unlikely(!dinfo->di_hdentry))+		goto out_dinfo;++	atomic_set(&dinfo->di_generation, au_sigen(sb));+	/* smp_mb(); */ /* atomic_set */+	au_rw_init_wlock_nested(&dinfo->di_rwsem, AuLsc_DI_CHILD);+	dinfo->di_bstart = -1;+	dinfo->di_bend = -1;+	dinfo->di_bwh = -1;+	dinfo->di_bdiropq = -1;++	dentry->d_fsdata = dinfo;+	dentry->d_op = &aufs_dop;+	return 0; /* success */++ out_dinfo:+	au_cache_free_dinfo(dinfo);+ out:+	return -ENOMEM;+}++int au_di_realloc(struct au_dinfo *dinfo, int nbr)+{+	int err, sz;+	struct au_hdentry *hdp;++	AuRwMustWriteLock(&dinfo->di_rwsem);++	err = -ENOMEM;+	sz = sizeof(*hdp) * (dinfo->di_bend + 1);+	if (!sz)+		sz = sizeof(*hdp);+	hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS);+	if (hdp) {+		dinfo->di_hdentry = hdp;+		err = 0;+	}++	return err;+}++/* ---------------------------------------------------------------------- */++static void do_ii_write_lock(struct inode *inode, unsigned int lsc)+{+	switch (lsc) {+	case AuLsc_DI_CHILD:+		ii_write_lock_child(inode);+		break;+	case AuLsc_DI_CHILD2:+		ii_write_lock_child2(inode);+		break;+	case AuLsc_DI_CHILD3:+		ii_write_lock_child3(inode);+		break;+	case AuLsc_DI_PARENT:+		ii_write_lock_parent(inode);+		break;+	case AuLsc_DI_PARENT2:+		ii_write_lock_parent2(inode);+		break;+	case AuLsc_DI_PARENT3:+		ii_write_lock_parent3(inode);+		break;+	default:+		BUG();+	}+}++static void do_ii_read_lock(struct inode *inode, unsigned int lsc)+{+	switch (lsc) {+	case AuLsc_DI_CHILD:+		ii_read_lock_child(inode);+		break;+	case AuLsc_DI_CHILD2:+		ii_read_lock_child2(inode);+		break;+	case AuLsc_DI_CHILD3:+		ii_read_lock_child3(inode);+		break;+	case AuLsc_DI_PARENT:+		ii_read_lock_parent(inode);+		break;+	case AuLsc_DI_PARENT2:+		ii_read_lock_parent2(inode);+		break;+	case AuLsc_DI_PARENT3:+		ii_read_lock_parent3(inode);+		break;+	default:+		BUG();+	}+}++void di_read_lock(struct dentry *d, int flags, unsigned int lsc)+{+	au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc);+	if (d->d_inode) {+		if (au_ftest_lock(flags, IW))+			do_ii_write_lock(d->d_inode, lsc);+		else if (au_ftest_lock(flags, IR))+			do_ii_read_lock(d->d_inode, lsc);+	}+}++void di_read_unlock(struct dentry *d, int flags)+{+	if (d->d_inode) {+		if (au_ftest_lock(flags, IW))+			ii_write_unlock(d->d_inode);+		else if (au_ftest_lock(flags, IR))+			ii_read_unlock(d->d_inode);+	}+	au_rw_read_unlock(&au_di(d)->di_rwsem);+}++void di_downgrade_lock(struct dentry *d, int flags)+{+	if (d->d_inode && au_ftest_lock(flags, IR))+		ii_downgrade_lock(d->d_inode);+	au_rw_dgrade_lock(&au_di(d)->di_rwsem);+}++void di_write_lock(struct dentry *d, unsigned int lsc)+{+	au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc);+	if (d->d_inode)+		do_ii_write_lock(d->d_inode, lsc);+}++void di_write_unlock(struct dentry *d)+{+	if (d->d_inode)+		ii_write_unlock(d->d_inode);+	au_rw_write_unlock(&au_di(d)->di_rwsem);+}++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir)+{+	AuDebugOn(d1 == d2+		  || d1->d_inode == d2->d_inode+		  || d1->d_sb != d2->d_sb);++	if (isdir && au_test_subdir(d1, d2)) {+		di_write_lock_child(d1);+		di_write_lock_child2(d2);+	} else {+		/* there should be no races */+		di_write_lock_child(d2);+		di_write_lock_child2(d1);+	}+}++void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir)+{+	AuDebugOn(d1 == d2+		  || d1->d_inode == d2->d_inode+		  || d1->d_sb != d2->d_sb);++	if (isdir && au_test_subdir(d1, d2)) {+		di_write_lock_parent(d1);+		di_write_lock_parent2(d2);+	} else {+		/* there should be no races */+		di_write_lock_parent(d2);+		di_write_lock_parent2(d1);+	}+}++void di_write_unlock2(struct dentry *d1, struct dentry *d2)+{+	di_write_unlock(d1);+	if (d1->d_inode == d2->d_inode)+		au_rw_write_unlock(&au_di(d2)->di_rwsem);+	else+		di_write_unlock(d2);+}++/* ---------------------------------------------------------------------- */++struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex)+{+	struct dentry *d;++	DiMustAnyLock(dentry);++	if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry))+		return NULL;+	AuDebugOn(bindex < 0);+	d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry;+	AuDebugOn(d && (atomic_read(&d->d_count) <= 0));+	return d;+}++aufs_bindex_t au_dbtail(struct dentry *dentry)+{+	aufs_bindex_t bend, bwh;++	bend = au_dbend(dentry);+	if (0 <= bend) {+		bwh = au_dbwh(dentry);+		if (!bwh)+			return bwh;+		if (0 < bwh && bwh < bend)+			return bwh - 1;+	}+	return bend;+}++aufs_bindex_t au_dbtaildir(struct dentry *dentry)+{+	aufs_bindex_t bend, bopq;++	bend = au_dbtail(dentry);+	if (0 <= bend) {+		bopq = au_dbdiropq(dentry);+		if (0 <= bopq && bopq < bend)+			bend = bopq;+	}+	return bend;+}++/* ---------------------------------------------------------------------- */++void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,+		   struct dentry *h_dentry)+{+	struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex;++	DiMustWriteLock(dentry);++	if (hd->hd_dentry)+		au_hdput(hd);+	hd->hd_dentry = h_dentry;+}++void au_update_digen(struct dentry *dentry)+{+	atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb));+	/* smp_mb(); */ /* atomic_set */+}++void au_update_dbrange(struct dentry *dentry, int do_put_zero)+{+	struct au_dinfo *dinfo;+	struct dentry *h_d;++	DiMustWriteLock(dentry);++	dinfo = au_di(dentry);+	if (!dinfo || dinfo->di_bstart < 0)+		return;++	if (do_put_zero) {+		aufs_bindex_t bindex, bend;++		bend = dinfo->di_bend;+		for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) {+			h_d = dinfo->di_hdentry[0 + bindex].hd_dentry;+			if (h_d && !h_d->d_inode)+				au_set_h_dptr(dentry, bindex, NULL);+		}+	}++	dinfo->di_bstart = -1;+	while (++dinfo->di_bstart <= dinfo->di_bend)+		if (dinfo->di_hdentry[0 + dinfo->di_bstart].hd_dentry)+			break;+	if (dinfo->di_bstart > dinfo->di_bend) {+		dinfo->di_bstart = -1;+		dinfo->di_bend = -1;+		return;+	}++	dinfo->di_bend++;+	while (0 <= --dinfo->di_bend)+		if (dinfo->di_hdentry[0 + dinfo->di_bend].hd_dentry)+			break;+	AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0);+}++void au_update_dbstart(struct dentry *dentry)+{+	aufs_bindex_t bindex, bend;+	struct dentry *h_dentry;++	bend = au_dbend(dentry);+	for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) {+		h_dentry = au_h_dptr(dentry, bindex);+		if (!h_dentry)+			continue;+		if (h_dentry->d_inode) {+			au_set_dbstart(dentry, bindex);+			return;+		}+		au_set_h_dptr(dentry, bindex, NULL);+	}+}++void au_update_dbend(struct dentry *dentry)+{+	aufs_bindex_t bindex, bstart;+	struct dentry *h_dentry;++	bstart = au_dbstart(dentry);+	for (bindex = au_dbend(dentry); bindex <= bstart; bindex--) {+		h_dentry = au_h_dptr(dentry, bindex);+		if (!h_dentry)+			continue;+		if (h_dentry->d_inode) {+			au_set_dbend(dentry, bindex);+			return;+		}+		au_set_h_dptr(dentry, bindex, NULL);+	}+}++int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry)+{+	aufs_bindex_t bindex, bend;++	bend = au_dbend(dentry);+	for (bindex = au_dbstart(dentry); bindex <= bend; bindex++)+		if (au_h_dptr(dentry, bindex) == h_dentry)+			return bindex;+	return -1;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/dir.c linux-2.6.31.5/fs/aufs/dir.c--- linux-2.6.31.5.orig/fs/aufs/dir.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/dir.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,538 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * directory operations+ */++#include <linux/file.h>+#include <linux/fs_stack.h>+#include "aufs.h"++void au_add_nlink(struct inode *dir, struct inode *h_dir)+{+	AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));++	dir->i_nlink += h_dir->i_nlink - 2;+	if (h_dir->i_nlink < 2)+		dir->i_nlink += 2;+}++void au_sub_nlink(struct inode *dir, struct inode *h_dir)+{+	AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));++	dir->i_nlink -= h_dir->i_nlink - 2;+	if (h_dir->i_nlink < 2)+		dir->i_nlink -= 2;+}++/* ---------------------------------------------------------------------- */++static int reopen_dir(struct file *file)+{+	int err;+	unsigned int flags;+	aufs_bindex_t bindex, btail, bstart;+	struct dentry *dentry, *h_dentry;+	struct file *h_file;++	/* open all lower dirs */+	dentry = file->f_dentry;+	bstart = au_dbstart(dentry);+	for (bindex = au_fbstart(file); bindex < bstart; bindex++)+		au_set_h_fptr(file, bindex, NULL);+	au_set_fbstart(file, bstart);++	btail = au_dbtaildir(dentry);+	for (bindex = au_fbend(file); btail < bindex; bindex--)+		au_set_h_fptr(file, bindex, NULL);+	au_set_fbend(file, btail);++	flags = file->f_flags;+	for (bindex = bstart; bindex <= btail; bindex++) {+		h_dentry = au_h_dptr(dentry, bindex);+		if (!h_dentry)+			continue;+		h_file = au_h_fptr(file, bindex);+		if (h_file)+			continue;++		h_file = au_h_open(dentry, bindex, flags, file);+		err = PTR_ERR(h_file);+		if (IS_ERR(h_file))+			goto out; /* close all? */+		au_set_h_fptr(file, bindex, h_file);+	}+	au_update_figen(file);+	/* todo: necessary? */+	/* file->f_ra = h_file->f_ra; */+	err = 0;++ out:+	return err;+}++static int do_open_dir(struct file *file, int flags)+{+	int err;+	aufs_bindex_t bindex, btail;+	struct dentry *dentry, *h_dentry;+	struct file *h_file;++	FiMustWriteLock(file);++	err = 0;+	dentry = file->f_dentry;+	au_set_fvdir_cache(file, NULL);+	au_fi(file)->fi_maintain_plink = 0;+	file->f_version = dentry->d_inode->i_version;+	bindex = au_dbstart(dentry);+	au_set_fbstart(file, bindex);+	btail = au_dbtaildir(dentry);+	au_set_fbend(file, btail);+	for (; !err && bindex <= btail; bindex++) {+		h_dentry = au_h_dptr(dentry, bindex);+		if (!h_dentry)+			continue;++		h_file = au_h_open(dentry, bindex, flags, file);+		if (IS_ERR(h_file)) {+			err = PTR_ERR(h_file);+			break;+		}+		au_set_h_fptr(file, bindex, h_file);+	}+	au_update_figen(file);+	/* todo: necessary? */+	/* file->f_ra = h_file->f_ra; */+	if (!err)+		return 0; /* success */++	/* close all */+	for (bindex = au_fbstart(file); bindex <= btail; bindex++)+		au_set_h_fptr(file, bindex, NULL);+	au_set_fbstart(file, -1);+	au_set_fbend(file, -1);+	return err;+}++static int aufs_open_dir(struct inode *inode __maybe_unused,+			 struct file *file)+{+	return au_do_open(file, do_open_dir);+}++static int aufs_release_dir(struct inode *inode __maybe_unused,+			    struct file *file)+{+	struct au_vdir *vdir_cache;+	struct super_block *sb;+	struct au_sbinfo *sbinfo;++	sb = file->f_dentry->d_sb;+	si_noflush_read_lock(sb);+	fi_write_lock(file);+	vdir_cache = au_fvdir_cache(file);+	if (vdir_cache)+		au_vdir_free(vdir_cache);+	if (au_fi(file)->fi_maintain_plink) {+		sbinfo = au_sbi(sb);+		/* clear the flag without write-lock */+		sbinfo->au_si_status &= ~AuSi_MAINTAIN_PLINK;+		smp_mb();+		wake_up_all(&sbinfo->si_plink_wq);+	}+	fi_write_unlock(file);+	au_finfo_fin(file);+	si_read_unlock(sb);+	return 0;+}++/* ---------------------------------------------------------------------- */++static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync)+{+	int err;+	aufs_bindex_t bend, bindex;+	struct inode *inode;+	struct super_block *sb;++	err = 0;+	sb = dentry->d_sb;+	inode = dentry->d_inode;+	IMustLock(inode);+	bend = au_dbend(dentry);+	for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) {+		struct path h_path;+		struct inode *h_inode;++		if (au_test_ro(sb, bindex, inode))+			continue;+		h_path.dentry = au_h_dptr(dentry, bindex);+		if (!h_path.dentry)+			continue;+		h_inode = h_path.dentry->d_inode;+		if (!h_inode)+			continue;++		/* no mnt_want_write() */+		/* cf. fs/nsfd/vfs.c and fs/nfsd/nfs4recover.c */+		/* todo: inotiry fired? */+		h_path.mnt = au_sbr_mnt(sb, bindex);+		mutex_lock(&h_inode->i_mutex);+		err = filemap_fdatawrite(h_inode->i_mapping);+		AuDebugOn(!h_inode->i_fop);+		if (!err && h_inode->i_fop->fsync)+			err = h_inode->i_fop->fsync(NULL, h_path.dentry,+						    datasync);+		if (!err)+			err = filemap_fdatawrite(h_inode->i_mapping);+		if (!err)+			vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/+		mutex_unlock(&h_inode->i_mutex);+	}++	return err;+}++static int au_do_fsync_dir(struct file *file, int datasync)+{+	int err;+	aufs_bindex_t bend, bindex;+	struct file *h_file;+	struct super_block *sb;+	struct inode *inode;+	struct mutex *h_mtx;++	err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1);+	if (unlikely(err))+		goto out;++	sb = file->f_dentry->d_sb;+	inode = file->f_dentry->d_inode;+	bend = au_fbend(file);+	for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) {+		h_file = au_h_fptr(file, bindex);+		if (!h_file || au_test_ro(sb, bindex, inode))+			continue;++		err = vfs_fsync(h_file, h_file->f_dentry, datasync);+		if (!err) {+			h_mtx = &h_file->f_dentry->d_inode->i_mutex;+			mutex_lock(h_mtx);+			vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL);+			/*ignore*/+			mutex_unlock(h_mtx);+		}+	}++ out:+	return err;+}++/*+ * @file may be NULL+ */+static int aufs_fsync_dir(struct file *file, struct dentry *dentry,+			  int datasync)+{+	int err;+	struct super_block *sb;++	IMustLock(dentry->d_inode);++	err = 0;+	sb = dentry->d_sb;+	si_noflush_read_lock(sb);+	if (file)+		err = au_do_fsync_dir(file, datasync);+	else {+		di_write_lock_child(dentry);+		err = au_do_fsync_dir_no_file(dentry, datasync);+	}+	au_cpup_attr_timesizes(dentry->d_inode);+	di_write_unlock(dentry);+	if (file)+		fi_write_unlock(file);++	si_read_unlock(sb);+	return err;+}++/* ---------------------------------------------------------------------- */++static int aufs_readdir(struct file *file, void *dirent, filldir_t filldir)+{+	int err;+	struct dentry *dentry;+	struct inode *inode;+	struct super_block *sb;++	dentry = file->f_dentry;+	inode = dentry->d_inode;+	IMustLock(inode);++	sb = dentry->d_sb;+	si_read_lock(sb, AuLock_FLUSH);+	err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1);+	if (unlikely(err))+		goto out;+	err = au_vdir_init(file);+	di_downgrade_lock(dentry, AuLock_IR);+	if (unlikely(err))+		goto out_unlock;++	if (!au_test_nfsd(current)) {+		err = au_vdir_fill_de(file, dirent, filldir);+		fsstack_copy_attr_atime(inode,+					au_h_iptr(inode, au_ibstart(inode)));+	} else {+		/*+		 * nfsd filldir may call lookup_one_len(), vfs_getattr(),+		 * encode_fh() and others.+		 */+		struct inode *h_inode = au_h_iptr(inode, au_ibstart(inode));++		di_read_unlock(dentry, AuLock_IR);+		si_read_unlock(sb);+		lockdep_off();+		err = au_vdir_fill_de(file, dirent, filldir);+		lockdep_on();+		fsstack_copy_attr_atime(inode, h_inode);+		fi_write_unlock(file);++		AuTraceErr(err);+		return err;+	}++ out_unlock:+	di_read_unlock(dentry, AuLock_IR);+	fi_write_unlock(file);+ out:+	si_read_unlock(sb);+	return err;+}++/* ---------------------------------------------------------------------- */++#define AuTestEmpty_WHONLY	1+#define AuTestEmpty_CALLED	(1 << 1)+#define AuTestEmpty_SHWH	(1 << 2)+#define au_ftest_testempty(flags, name)	((flags) & AuTestEmpty_##name)+#define au_fset_testempty(flags, name)	{ (flags) |= AuTestEmpty_##name; }+#define au_fclr_testempty(flags, name)	{ (flags) &= ~AuTestEmpty_##name; }++#ifndef CONFIG_AUFS_SHWH+#undef AuTestEmpty_SHWH+#define AuTestEmpty_SHWH	0+#endif++struct test_empty_arg {+	struct au_nhash whlist;+	unsigned int flags;+	int err;+	aufs_bindex_t bindex;+};++static int test_empty_cb(void *__arg, const char *__name, int namelen,+			 loff_t offset __maybe_unused, u64 ino,+			 unsigned int d_type)+{+	struct test_empty_arg *arg = __arg;+	char *name = (void *)__name;++	arg->err = 0;+	au_fset_testempty(arg->flags, CALLED);+	/* smp_mb(); */+	if (name[0] == '.'+	    && (namelen == 1 || (name[1] == '.' && namelen == 2)))+		goto out; /* success */++	if (namelen <= AUFS_WH_PFX_LEN+	    || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {+		if (au_ftest_testempty(arg->flags, WHONLY)+		    && !au_nhash_test_known_wh(&arg->whlist, name, namelen))+			arg->err = -ENOTEMPTY;+		goto out;+	}++	name += AUFS_WH_PFX_LEN;+	namelen -= AUFS_WH_PFX_LEN;+	if (!au_nhash_test_known_wh(&arg->whlist, name, namelen))+		arg->err = au_nhash_append_wh+			(&arg->whlist, name, namelen, ino, d_type, arg->bindex,+			 au_ftest_testempty(arg->flags, SHWH));++ out:+	/* smp_mb(); */+	AuTraceErr(arg->err);+	return arg->err;+}++static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg)+{+	int err;+	struct file *h_file;++	h_file = au_h_open(dentry, arg->bindex,+			   O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE,+			   /*file*/NULL);+	err = PTR_ERR(h_file);+	if (IS_ERR(h_file))+		goto out;++	err = 0;+	if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE)+	    && !h_file->f_dentry->d_inode->i_nlink)+		goto out_put;++	do {+		arg->err = 0;+		au_fclr_testempty(arg->flags, CALLED);+		/* smp_mb(); */+		err = vfsub_readdir(h_file, test_empty_cb, arg);+		if (err >= 0)+			err = arg->err;+	} while (!err && au_ftest_testempty(arg->flags, CALLED));++ out_put:+	fput(h_file);+	au_sbr_put(dentry->d_sb, arg->bindex);+ out:+	return err;+}++struct do_test_empty_args {+	int *errp;+	struct dentry *dentry;+	struct test_empty_arg *arg;+};++static void call_do_test_empty(void *args)+{+	struct do_test_empty_args *a = args;+	*a->errp = do_test_empty(a->dentry, a->arg);+}++static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg)+{+	int err, wkq_err;+	struct dentry *h_dentry;+	struct inode *h_inode;++	h_dentry = au_h_dptr(dentry, arg->bindex);+	h_inode = h_dentry->d_inode;+	mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);+	err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ);+	mutex_unlock(&h_inode->i_mutex);+	if (!err)+		err = do_test_empty(dentry, arg);+	else {+		struct do_test_empty_args args = {+			.errp	= &err,+			.dentry	= dentry,+			.arg	= arg+		};+		unsigned int flags = arg->flags;++		wkq_err = au_wkq_wait(call_do_test_empty, &args);+		if (unlikely(wkq_err))+			err = wkq_err;+		arg->flags = flags;+	}++	return err;+}++int au_test_empty_lower(struct dentry *dentry)+{+	int err;+	aufs_bindex_t bindex, bstart, btail;+	struct test_empty_arg arg;++	SiMustAnyLock(dentry->d_sb);++	err = au_nhash_alloc(&arg.whlist, au_sbi(dentry->d_sb)->si_rdhash,+			     GFP_NOFS);+	if (unlikely(err))+		goto out;++	bstart = au_dbstart(dentry);+	arg.flags = 0;+	if (au_opt_test(au_mntflags(dentry->d_sb), SHWH))+		au_fset_testempty(arg.flags, SHWH);+	arg.bindex = bstart;+	err = do_test_empty(dentry, &arg);+	if (unlikely(err))+		goto out_whlist;++	au_fset_testempty(arg.flags, WHONLY);+	btail = au_dbtaildir(dentry);+	for (bindex = bstart + 1; !err && bindex <= btail; bindex++) {+		struct dentry *h_dentry;++		h_dentry = au_h_dptr(dentry, bindex);+		if (h_dentry && h_dentry->d_inode) {+			arg.bindex = bindex;+			err = do_test_empty(dentry, &arg);+		}+	}++ out_whlist:+	au_nhash_wh_free(&arg.whlist);+ out:+	return err;+}++int au_test_empty(struct dentry *dentry, struct au_nhash *whlist)+{+	int err;+	struct test_empty_arg arg;+	aufs_bindex_t bindex, btail;++	err = 0;+	arg.whlist = *whlist;+	arg.flags = AuTestEmpty_WHONLY;+	if (au_opt_test(au_mntflags(dentry->d_sb), SHWH))+		au_fset_testempty(arg.flags, SHWH);+	btail = au_dbtaildir(dentry);+	for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) {+		struct dentry *h_dentry;++		h_dentry = au_h_dptr(dentry, bindex);+		if (h_dentry && h_dentry->d_inode) {+			arg.bindex = bindex;+			err = sio_test_empty(dentry, &arg);+		}+	}++	return err;+}++/* ---------------------------------------------------------------------- */++const struct file_operations aufs_dir_fop = {+	.read		= generic_read_dir,+	.readdir	= aufs_readdir,+	.unlocked_ioctl	= aufs_ioctl_dir,+	.open		= aufs_open_dir,+	.release	= aufs_release_dir,+	.flush		= aufs_flush,+	.fsync		= aufs_fsync_dir+};diff -Nur linux-2.6.31.5.orig/fs/aufs/dir.h linux-2.6.31.5/fs/aufs/dir.h--- linux-2.6.31.5.orig/fs/aufs/dir.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/dir.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,114 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * directory operations+ */++#ifndef __AUFS_DIR_H__+#define __AUFS_DIR_H__++#ifdef __KERNEL__++#include <linux/fs.h>+#include <linux/aufs_type.h>++/* ---------------------------------------------------------------------- */++/* need to be faster and smaller */++struct au_nhash {+	unsigned int		nh_num;+	struct hlist_head	*nh_head;+};++struct au_vdir_destr {+	unsigned char	len;+	unsigned char	name[0];+} __packed;++struct au_vdir_dehstr {+	struct hlist_node	hash;+	struct au_vdir_destr	*str;+};++struct au_vdir_de {+	ino_t			de_ino;+	unsigned char		de_type;+	/* caution: packed */+	struct au_vdir_destr	de_str;+} __packed;++struct au_vdir_wh {+	struct hlist_node	wh_hash;+#ifdef CONFIG_AUFS_SHWH+	ino_t			wh_ino;+	aufs_bindex_t		wh_bindex;+	unsigned char		wh_type;+#else+	aufs_bindex_t		wh_bindex;+#endif+	/* caution: packed */+	struct au_vdir_destr	wh_str;+} __packed;++union au_vdir_deblk_p {+	unsigned char		*deblk;+	struct au_vdir_de	*de;+};++struct au_vdir {+	unsigned char	**vd_deblk;+	unsigned long	vd_nblk;+	struct {+		unsigned long		ul;+		union au_vdir_deblk_p	p;+	} vd_last;++	unsigned long	vd_version;+	unsigned int	vd_deblk_sz;+	unsigned long	vd_jiffy;+};++/* ---------------------------------------------------------------------- */++/* dir.c */+extern const struct file_operations aufs_dir_fop;+void au_add_nlink(struct inode *dir, struct inode *h_dir);+void au_sub_nlink(struct inode *dir, struct inode *h_dir);+int au_test_empty_lower(struct dentry *dentry);+int au_test_empty(struct dentry *dentry, struct au_nhash *whlist);++/* vdir.c */+int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp);+void au_nhash_wh_free(struct au_nhash *whlist);+int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt,+			    int limit);+int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen);+int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino,+		       unsigned int d_type, aufs_bindex_t bindex,+		       unsigned char shwh);+void au_vdir_free(struct au_vdir *vdir);+int au_vdir_init(struct file *file);+int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir);++/* ioctl.c */+long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg);++#endif /* __KERNEL__ */+#endif /* __AUFS_DIR_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/export.c linux-2.6.31.5/fs/aufs/export.c--- linux-2.6.31.5.orig/fs/aufs/export.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/export.c	2009-11-15 22:27:33.000000000 +0100@@ -0,0 +1,746 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * export via nfs+ */++#include <linux/exportfs.h>+#include <linux/file.h>+#include <linux/mnt_namespace.h>+#include <linux/nsproxy.h>+#include <linux/namei.h>+#include <linux/random.h>+#include "aufs.h"++union conv {+#ifdef CONFIG_AUFS_INO_T_64+	__u32 a[2];+#else+	__u32 a[1];+#endif+	ino_t ino;+};++static ino_t decode_ino(__u32 *a)+{+	union conv u;++	BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a));+	u.a[0] = a[0];+#ifdef CONFIG_AUFS_INO_T_64+	u.a[1] = a[1];+#endif+	return u.ino;+}++static void encode_ino(__u32 *a, ino_t ino)+{+	union conv u;++	u.ino = ino;+	a[0] = u.a[0];+#ifdef CONFIG_AUFS_INO_T_64+	a[1] = u.a[1];+#endif+}++/* NFS file handle */+enum {+	Fh_br_id,+	Fh_sigen,+#ifdef CONFIG_AUFS_INO_T_64+	/* support 64bit inode number */+	Fh_ino1,+	Fh_ino2,+	Fh_dir_ino1,+	Fh_dir_ino2,+#else+	Fh_ino1,+	Fh_dir_ino1,+#endif+	Fh_igen,+	Fh_h_type,+	Fh_tail,++	Fh_ino = Fh_ino1,+	Fh_dir_ino = Fh_dir_ino1+};++static int au_test_anon(struct dentry *dentry)+{+	return !!(dentry->d_flags & DCACHE_DISCONNECTED);+}++/* ---------------------------------------------------------------------- */+/* inode generation external table */++int au_xigen_inc(struct inode *inode)+{+	int err;+	loff_t pos;+	ssize_t sz;+	__u32 igen;+	struct super_block *sb;+	struct au_sbinfo *sbinfo;++	err = 0;+	sb = inode->i_sb;+	sbinfo = au_sbi(sb);+	/*+	 * temporary workaround for escaping from SiMustAnyLock() in+	 * au_mntflags(), since this function is called from au_iinfo_fin().+	 */+	if (unlikely(!au_opt_test(sbinfo->si_mntflags, XINO)))+		goto out;++	pos = inode->i_ino;+	pos *= sizeof(igen);+	igen = inode->i_generation + 1;+	sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen,+			 sizeof(igen), &pos);+	if (sz == sizeof(igen))+		goto out; /* success */++	err = sz;+	if (unlikely(sz >= 0)) {+		err = -EIO;+		AuIOErr("xigen error (%zd)\n", sz);+	}++ out:+	return err;+}++int au_xigen_new(struct inode *inode)+{+	int err;+	loff_t pos;+	ssize_t sz;+	struct super_block *sb;+	struct au_sbinfo *sbinfo;+	struct file *file;++	err = 0;+	/* todo: dirty, at mount time */+	if (inode->i_ino == AUFS_ROOT_INO)+		goto out;+	sb = inode->i_sb;+	SiMustAnyLock(sb);+	if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))+		goto out;++	err = -EFBIG;+	pos = inode->i_ino;+	if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) {+		AuIOErr1("too large i%lld\n", pos);+		goto out;+	}+	pos *= sizeof(inode->i_generation);++	err = 0;+	sbinfo = au_sbi(sb);+	file = sbinfo->si_xigen;+	BUG_ON(!file);++	if (i_size_read(file->f_dentry->d_inode)+	    < pos + sizeof(inode->i_generation)) {+		inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next);+		sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation,+				 sizeof(inode->i_generation), &pos);+	} else+		sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation,+				sizeof(inode->i_generation), &pos);+	if (sz == sizeof(inode->i_generation))+		goto out; /* success */++	err = sz;+	if (unlikely(sz >= 0)) {+		err = -EIO;+		AuIOErr("xigen error (%zd)\n", sz);+	}++ out:+	return err;+}++int au_xigen_set(struct super_block *sb, struct file *base)+{+	int err;+	struct au_sbinfo *sbinfo;+	struct file *file;++	SiMustWriteLock(sb);++	sbinfo = au_sbi(sb);+	file = au_xino_create2(base, sbinfo->si_xigen);+	err = PTR_ERR(file);+	if (IS_ERR(file))+		goto out;+	err = 0;+	if (sbinfo->si_xigen)+		fput(sbinfo->si_xigen);+	sbinfo->si_xigen = file;++ out:+	return err;+}++void au_xigen_clr(struct super_block *sb)+{+	struct au_sbinfo *sbinfo;++	SiMustWriteLock(sb);++	sbinfo = au_sbi(sb);+	if (sbinfo->si_xigen) {+		fput(sbinfo->si_xigen);+		sbinfo->si_xigen = NULL;+	}+}++/* ---------------------------------------------------------------------- */++static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,+				    ino_t dir_ino)+{+	struct dentry *dentry, *d;+	struct inode *inode;+	unsigned int sigen;++	dentry = NULL;+	inode = ilookup(sb, ino);+	if (!inode)+		goto out;++	dentry = ERR_PTR(-ESTALE);+	sigen = au_sigen(sb);+	if (unlikely(is_bad_inode(inode)+		     || IS_DEADDIR(inode)+		     || sigen != au_iigen(inode)))+		goto out_iput;++	dentry = NULL;+	if (!dir_ino || S_ISDIR(inode->i_mode))+		dentry = d_find_alias(inode);+	else {+		spin_lock(&dcache_lock);+		list_for_each_entry(d, &inode->i_dentry, d_alias)+			if (!au_test_anon(d)+			    && d->d_parent->d_inode->i_ino == dir_ino) {+				dentry = dget_locked(d);+				break;+			}+		spin_unlock(&dcache_lock);+	}+	if (unlikely(dentry && sigen != au_digen(dentry))) {+		dput(dentry);+		dentry = ERR_PTR(-ESTALE);+	}++ out_iput:+	iput(inode);+ out:+	return dentry;+}++/* ---------------------------------------------------------------------- */++/* todo: dirty? */+/* if exportfs_decode_fh() passed vfsmount*, we could be happy */+static struct vfsmount *au_mnt_get(struct super_block *sb)+{+	struct mnt_namespace *ns;+	struct vfsmount *pos, *mnt;++	spin_lock(&vfsmount_lock);+	/* no get/put ?? */+	AuDebugOn(!current->nsproxy);+	ns = current->nsproxy->mnt_ns;+	AuDebugOn(!ns);+	mnt = NULL;+	/* the order (reverse) will not be a problem */+	list_for_each_entry(pos, &ns->list, mnt_list)+		if (pos->mnt_sb == sb) {+			mnt = mntget(pos);+			break;+		}+	spin_unlock(&vfsmount_lock);+	AuDebugOn(!mnt);++	return mnt;+}++struct au_nfsd_si_lock {+	const unsigned int sigen;+	const aufs_bindex_t br_id;+	unsigned char force_lock;+};++static aufs_bindex_t si_nfsd_read_lock(struct super_block *sb,+				       struct au_nfsd_si_lock *nsi_lock)+{+	aufs_bindex_t bindex;++	si_read_lock(sb, AuLock_FLUSH);++	/* branch id may be wrapped around */+	bindex = au_br_index(sb, nsi_lock->br_id);+	if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb))+		goto out; /* success */++	if (!nsi_lock->force_lock)+		si_read_unlock(sb);+	bindex = -1;++ out:+	return bindex;+}++struct find_name_by_ino {+	int called, found;+	ino_t ino;+	char *name;+	int namelen;+};++static int+find_name_by_ino(void *arg, const char *name, int namelen, loff_t offset,+		 u64 ino, unsigned int d_type)+{+	struct find_name_by_ino *a = arg;++	a->called++;+	if (a->ino != ino)+		return 0;++	memcpy(a->name, name, namelen);+	a->namelen = namelen;+	a->found = 1;+	return 1;+}++static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino,+				     struct au_nfsd_si_lock *nsi_lock)+{+	struct dentry *dentry, *parent;+	struct file *file;+	struct inode *dir;+	struct find_name_by_ino arg;+	int err;++	parent = path->dentry;+	if (nsi_lock)+		si_read_unlock(parent->d_sb);+	path_get(path);+	file = dentry_open(parent, path->mnt, au_dir_roflags, current_cred());+	dentry = (void *)file;+	if (IS_ERR(file))+		goto out;++	dentry = ERR_PTR(-ENOMEM);+	arg.name = __getname();+	if (unlikely(!arg.name))+		goto out_file;+	arg.ino = ino;+	arg.found = 0;+	do {+		arg.called = 0;+		/* smp_mb(); */+		err = vfsub_readdir(file, find_name_by_ino, &arg);+	} while (!err && !arg.found && arg.called);+	dentry = ERR_PTR(err);+	if (unlikely(err))+		goto out_name;+	dentry = ERR_PTR(-ENOENT);+	if (!arg.found)+		goto out_name;++	/* do not call au_lkup_one() */+	dir = parent->d_inode;+	mutex_lock(&dir->i_mutex);+	dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen);+	mutex_unlock(&dir->i_mutex);+	AuTraceErrPtr(dentry);+	if (IS_ERR(dentry))+		goto out_name;+	AuDebugOn(au_test_anon(dentry));+	if (unlikely(!dentry->d_inode)) {+		dput(dentry);+		dentry = ERR_PTR(-ENOENT);+	}++ out_name:+	__putname(arg.name);+ out_file:+	fput(file);+ out:+	if (unlikely(nsi_lock+		     && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0))+		if (!IS_ERR(dentry)) {+			dput(dentry);+			dentry = ERR_PTR(-ESTALE);+		}+	AuTraceErrPtr(dentry);+	return dentry;+}++static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino,+					ino_t dir_ino,+					struct au_nfsd_si_lock *nsi_lock)+{+	struct dentry *dentry;+	struct path path;++	if (dir_ino != AUFS_ROOT_INO) {+		path.dentry = decode_by_ino(sb, dir_ino, 0);+		dentry = path.dentry;+		if (!path.dentry || IS_ERR(path.dentry))+			goto out;+		AuDebugOn(au_test_anon(path.dentry));+	} else+		path.dentry = dget(sb->s_root);++	path.mnt = au_mnt_get(sb);+	dentry = au_lkup_by_ino(&path, ino, nsi_lock);+	path_put(&path);++ out:+	AuTraceErrPtr(dentry);+	return dentry;+}++/* ---------------------------------------------------------------------- */++static int h_acceptable(void *expv, struct dentry *dentry)+{+	return 1;+}++static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath,+			   char *buf, int len, struct super_block *sb)+{+	char *p;+	int n;+	struct path path;++	p = d_path(h_rootpath, buf, len);+	if (IS_ERR(p))+		goto out;+	n = strlen(p);++	path.mnt = h_rootpath->mnt;+	path.dentry = h_parent;+	p = d_path(&path, buf, len);+	if (IS_ERR(p))+		goto out;+	if (n != 1)+		p += n;++	path.mnt = au_mnt_get(sb);+	path.dentry = sb->s_root;+	p = d_path(&path, buf, len - strlen(p));+	mntput(path.mnt);+	if (IS_ERR(p))+		goto out;+	if (n != 1)+		p[strlen(p)] = '/';++ out:+	AuTraceErrPtr(p);+	return p;+}++static+struct dentry *decode_by_path(struct super_block *sb, aufs_bindex_t bindex,+			      ino_t ino, __u32 *fh, int fh_len,+			      struct au_nfsd_si_lock *nsi_lock)+{+	struct dentry *dentry, *h_parent, *root;+	struct super_block *h_sb;+	char *pathname, *p;+	struct vfsmount *h_mnt;+	struct au_branch *br;+	int err;+	struct path path;++	br = au_sbr(sb, bindex);+	/* au_br_get(br); */+	h_mnt = br->br_mnt;+	h_sb = h_mnt->mnt_sb;+	/* todo: call lower fh_to_dentry()? fh_to_parent()? */+	h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail),+				      fh_len - Fh_tail, fh[Fh_h_type],+				      h_acceptable, /*context*/NULL);+	dentry = h_parent;+	if (unlikely(!h_parent || IS_ERR(h_parent))) {+		AuWarn1("%s decode_fh failed, %ld\n",+			au_sbtype(h_sb), PTR_ERR(h_parent));+		goto out;+	}+	dentry = NULL;+	if (unlikely(au_test_anon(h_parent))) {+		AuWarn1("%s decode_fh returned a disconnected dentry\n",+			au_sbtype(h_sb));+		goto out_h_parent;+	}++	dentry = ERR_PTR(-ENOMEM);+	pathname = (void *)__get_free_page(GFP_NOFS);+	if (unlikely(!pathname))+		goto out_h_parent;++	root = sb->s_root;+	path.mnt = h_mnt;+	di_read_lock_parent(root, !AuLock_IR);+	path.dentry = au_h_dptr(root, bindex);+	di_read_unlock(root, !AuLock_IR);+	p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb);+	dentry = (void *)p;+	if (IS_ERR(p))+		goto out_pathname;++	si_read_unlock(sb);+	err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);+	dentry = ERR_PTR(err);+	if (unlikely(err))+		goto out_relock;++	dentry = ERR_PTR(-ENOENT);+	AuDebugOn(au_test_anon(path.dentry));+	if (unlikely(!path.dentry->d_inode))+		goto out_path;++	if (ino != path.dentry->d_inode->i_ino)+		dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL);+	else+		dentry = dget(path.dentry);++ out_path:+	path_put(&path);+ out_relock:+	if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0))+		if (!IS_ERR(dentry)) {+			dput(dentry);+			dentry = ERR_PTR(-ESTALE);+		}+ out_pathname:+	free_page((unsigned long)pathname);+ out_h_parent:+	dput(h_parent);+ out:+	/* au_br_put(br); */+	AuTraceErrPtr(dentry);+	return dentry;+}++/* ---------------------------------------------------------------------- */++static struct dentry *+aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,+		  int fh_type)+{+	struct dentry *dentry;+	__u32 *fh = fid->raw;+	ino_t ino, dir_ino;+	aufs_bindex_t bindex;+	struct au_nfsd_si_lock nsi_lock = {+		.sigen		= fh[Fh_sigen],+		.br_id		= fh[Fh_br_id],+		.force_lock	= 0+	};++	AuDebugOn(fh_len < Fh_tail);++	dentry = ERR_PTR(-ESTALE);+	/* branch id may be wrapped around */+	bindex = si_nfsd_read_lock(sb, &nsi_lock);+	if (unlikely(bindex < 0))+		goto out;+	nsi_lock.force_lock = 1;++	/* is this inode still cached? */+	ino = decode_ino(fh + Fh_ino);+	AuDebugOn(ino == AUFS_ROOT_INO);+	dir_ino = decode_ino(fh + Fh_dir_ino);+	dentry = decode_by_ino(sb, ino, dir_ino);+	if (IS_ERR(dentry))+		goto out_unlock;+	if (dentry)+		goto accept;++	/* is the parent dir cached? */+	dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock);+	if (IS_ERR(dentry))+		goto out_unlock;+	if (dentry)+		goto accept;++	/* lookup path */+	dentry = decode_by_path(sb, bindex, ino, fh, fh_len, &nsi_lock);+	if (IS_ERR(dentry))+		goto out_unlock;+	if (unlikely(!dentry))+		/* todo?: make it ESTALE */+		goto out_unlock;++ accept:+	if (dentry->d_inode->i_generation == fh[Fh_igen])+		goto out_unlock; /* success */++	dput(dentry);+	dentry = ERR_PTR(-ESTALE);+ out_unlock:+	si_read_unlock(sb);+ out:+	AuTraceErrPtr(dentry);+	return dentry;+}++#if 0 /* reserved for future use */+/* support subtreecheck option */+static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid,+					int fh_len, int fh_type)+{+	struct dentry *parent;+	__u32 *fh = fid->raw;+	ino_t dir_ino;++	dir_ino = decode_ino(fh + Fh_dir_ino);+	parent = decode_by_ino(sb, dir_ino, 0);+	if (IS_ERR(parent))+		goto out;+	if (!parent)+		parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]),+					dir_ino, fh, fh_len);++ out:+	AuTraceErrPtr(parent);+	return parent;+}+#endif++/* ---------------------------------------------------------------------- */++static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len,+			  int connectable)+{+	int err;+	aufs_bindex_t bindex, bend;+	struct super_block *sb, *h_sb;+	struct inode *inode;+	struct dentry *parent, *h_parent;+	struct au_branch *br;++	AuDebugOn(au_test_anon(dentry));++	parent = NULL;+	err = -ENOSPC;+	if (unlikely(*max_len <= Fh_tail)) {+		AuWarn1("NFSv2 client (max_len %d)?\n", *max_len);+		goto out;+	}++	err = FILEID_ROOT;+	if (IS_ROOT(dentry)) {+		AuDebugOn(dentry->d_inode->i_ino != AUFS_ROOT_INO);+		goto out;+	}++	err = -EIO;+	h_parent = NULL;+	sb = dentry->d_sb;+	aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR);+	parent = dget_parent(dentry);+	di_read_lock_parent(parent, !AuLock_IR);+	inode = dentry->d_inode;+	AuDebugOn(!inode);+#ifdef CONFIG_AUFS_DEBUG+	if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))+		AuWarn1("NFS-exporting requires xino\n");+#endif++	bend = au_dbtaildir(parent);+	for (bindex = au_dbstart(parent); bindex <= bend; bindex++) {+		h_parent = au_h_dptr(parent, bindex);+		if (h_parent) {+			dget(h_parent);+			break;+		}+	}+	if (unlikely(!h_parent))+		goto out_unlock;++	err = -EPERM;+	br = au_sbr(sb, bindex);+	h_sb = br->br_mnt->mnt_sb;+	if (unlikely(!h_sb->s_export_op)) {+		AuErr1("%s branch is not exportable\n", au_sbtype(h_sb));+		goto out_dput;+	}++	fh[Fh_br_id] = br->br_id;+	fh[Fh_sigen] = au_sigen(sb);+	encode_ino(fh + Fh_ino, inode->i_ino);+	encode_ino(fh + Fh_dir_ino, parent->d_inode->i_ino);+	fh[Fh_igen] = inode->i_generation;++	*max_len -= Fh_tail;+	fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail),+					   max_len,+					   /*connectable or subtreecheck*/0);+	err = fh[Fh_h_type];+	*max_len += Fh_tail;+	/* todo: macros? */+	if (err != 255)+		err = 99;+	else+		AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb));++ out_dput:+	dput(h_parent);+ out_unlock:+	di_read_unlock(parent, !AuLock_IR);+	dput(parent);+	aufs_read_unlock(dentry, AuLock_IR);+ out:+	if (unlikely(err < 0))+		err = 255;+	return err;+}++/* ---------------------------------------------------------------------- */++static struct export_operations aufs_export_op = {+	.fh_to_dentry	= aufs_fh_to_dentry,+	/* .fh_to_parent	= aufs_fh_to_parent, */+	.encode_fh	= aufs_encode_fh+};++void au_export_init(struct super_block *sb)+{+	struct au_sbinfo *sbinfo;+	__u32 u;++	sb->s_export_op = &aufs_export_op;+	sbinfo = au_sbi(sb);+	sbinfo->si_xigen = NULL;+	get_random_bytes(&u, sizeof(u));+	BUILD_BUG_ON(sizeof(u) != sizeof(int));+	atomic_set(&sbinfo->si_xigen_next, u);+}diff -Nur linux-2.6.31.5.orig/fs/aufs/file.c linux-2.6.31.5/fs/aufs/file.c--- linux-2.6.31.5.orig/fs/aufs/file.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/file.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,578 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * handling file/dir, and address_space operation+ */++#include <linux/file.h>+#include <linux/fsnotify.h>+#include <linux/namei.h>+#include <linux/pagemap.h>+#include "aufs.h"++/*+ * a dirty trick for handling deny_write_access().+ * because FMODE_EXEC flag is not passed to f_op->open(),+ * set it to file->private_data temporary.+ */+void au_store_oflag(struct nameidata *nd, struct inode *inode)+{+	if (nd+	    /* && !(nd->flags & LOOKUP_CONTINUE) */+	    && (nd->flags & LOOKUP_OPEN)+	    && (nd->intent.open.flags & vfsub_fmode_to_uint(FMODE_EXEC))+	    && inode+	    && S_ISREG(inode->i_mode)) {+		/* suppress a warning in lp64 */+		unsigned long flags = nd->intent.open.flags;+		nd->intent.open.file->private_data = (void *)flags;+		/* smp_mb(); */+	}+}++/* drop flags for writing */+unsigned int au_file_roflags(unsigned int flags)+{+	flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC);+	flags |= O_RDONLY | O_NOATIME;+	return flags;+}++/* common functions to regular file and dir */+struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,+		       struct file *file)+{+	struct file *h_file;+	struct dentry *h_dentry;+	struct inode *h_inode;+	struct super_block *sb;+	struct au_branch *br;+	int err;++	/* a race condition can happen between open and unlink/rmdir */+	h_file = ERR_PTR(-ENOENT);+	h_dentry = au_h_dptr(dentry, bindex);+	if (au_test_nfsd(current) && !h_dentry)+		goto out;+	h_inode = h_dentry->d_inode;+	if (au_test_nfsd(current) && !h_inode)+		goto out;+	if (unlikely((!d_unhashed(dentry) && d_unhashed(h_dentry))+		     || !h_inode))+		goto out;++	sb = dentry->d_sb;+	br = au_sbr(sb, bindex);+	h_file = ERR_PTR(-EACCES);+	if (file && (file->f_mode & FMODE_EXEC)+	    && (br->br_mnt->mnt_flags & MNT_NOEXEC))+		goto out;++	/* drop flags for writing */+	if (au_test_ro(sb, bindex, dentry->d_inode))+		flags = au_file_roflags(flags);+	flags &= ~O_CREAT;+	atomic_inc(&br->br_count);+	h_file = dentry_open(dget(h_dentry), mntget(br->br_mnt), flags,+			     current_cred());+	if (IS_ERR(h_file))+		goto out_br;++	if (file && (file->f_mode & FMODE_EXEC)) {+		h_file->f_mode |= FMODE_EXEC;+		err = deny_write_access(h_file);+		if (unlikely(err)) {+			fput(h_file);+			h_file = ERR_PTR(err);+			goto out_br;+		}+	}+	fsnotify_open(h_dentry);+	goto out; /* success */++ out_br:+	atomic_dec(&br->br_count);+ out:+	return h_file;+}++int au_do_open(struct file *file, int (*open)(struct file *file, int flags))+{+	int err;+	struct dentry *dentry;+	struct super_block *sb;++	dentry = file->f_dentry;+	sb = dentry->d_sb;+	si_read_lock(sb, AuLock_FLUSH);+	err = au_finfo_init(file);+	if (unlikely(err))+		goto out;++	di_read_lock_child(dentry, AuLock_IR);+	err = open(file, file->f_flags);+	di_read_unlock(dentry, AuLock_IR);++	fi_write_unlock(file);+	if (unlikely(err))+		au_finfo_fin(file);+ out:+	si_read_unlock(sb);+	return err;+}++int au_reopen_nondir(struct file *file)+{+	int err;+	aufs_bindex_t bstart, bindex, bend;+	struct dentry *dentry;+	struct file *h_file, *h_file_tmp;++	dentry = file->f_dentry;+	bstart = au_dbstart(dentry);+	h_file_tmp = NULL;+	if (au_fbstart(file) == bstart) {+		h_file = au_h_fptr(file, bstart);+		if (file->f_mode == h_file->f_mode)+			return 0; /* success */+		h_file_tmp = h_file;+		get_file(h_file_tmp);+		au_set_h_fptr(file, bstart, NULL);+	}+	AuDebugOn(au_fbstart(file) < bstart+		  || au_fi(file)->fi_hfile[0 + bstart].hf_file);++	h_file = au_h_open(dentry, bstart, file->f_flags & ~O_TRUNC, file);+	err = PTR_ERR(h_file);+	if (IS_ERR(h_file))+		goto out; /* todo: close all? */++	err = 0;+	au_set_fbstart(file, bstart);+	au_set_h_fptr(file, bstart, h_file);+	au_update_figen(file);+	/* todo: necessary? */+	/* file->f_ra = h_file->f_ra; */++	/* close lower files */+	bend = au_fbend(file);+	for (bindex = bstart + 1; bindex <= bend; bindex++)+		au_set_h_fptr(file, bindex, NULL);+	au_set_fbend(file, bstart);++ out:+	if (h_file_tmp)+		fput(h_file_tmp);+	return err;+}++/* ---------------------------------------------------------------------- */++static int au_reopen_wh(struct file *file, aufs_bindex_t btgt,+			struct dentry *hi_wh)+{+	int err;+	aufs_bindex_t bstart;+	struct au_dinfo *dinfo;+	struct dentry *h_dentry;++	dinfo = au_di(file->f_dentry);+	AuRwMustWriteLock(&dinfo->di_rwsem);++	bstart = dinfo->di_bstart;+	dinfo->di_bstart = btgt;+	h_dentry = dinfo->di_hdentry[0 + btgt].hd_dentry;+	dinfo->di_hdentry[0 + btgt].hd_dentry = hi_wh;+	err = au_reopen_nondir(file);+	dinfo->di_hdentry[0 + btgt].hd_dentry = h_dentry;+	dinfo->di_bstart = bstart;++	return err;+}++static int au_ready_to_write_wh(struct file *file, loff_t len,+				aufs_bindex_t bcpup)+{+	int err;+	struct inode *inode;+	struct dentry *dentry, *hi_wh;+	struct super_block *sb;++	dentry = file->f_dentry;+	inode = dentry->d_inode;+	hi_wh = au_hi_wh(inode, bcpup);+	if (!hi_wh)+		err = au_sio_cpup_wh(dentry, bcpup, len, file);+	else+		/* already copied-up after unlink */+		err = au_reopen_wh(file, bcpup, hi_wh);++	sb = dentry->d_sb;+	if (!err && inode->i_nlink > 1 && au_opt_test(au_mntflags(sb), PLINK))+		au_plink_append(inode, bcpup, au_h_dptr(dentry, bcpup));++	return err;+}++/*+ * prepare the @file for writing.+ */+int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin)+{+	int err;+	aufs_bindex_t bstart, bcpup;+	struct dentry *dentry, *parent, *h_dentry;+	struct inode *h_inode, *inode;+	struct super_block *sb;++	dentry = file->f_dentry;+	sb = dentry->d_sb;+	bstart = au_fbstart(file);+	inode = dentry->d_inode;+	err = au_test_ro(sb, bstart, inode);+	if (!err && (au_h_fptr(file, bstart)->f_mode & FMODE_WRITE)) {+		err = au_pin(pin, dentry, bstart, AuOpt_UDBA_NONE, /*flags*/0);+		goto out;+	}++	/* need to cpup */+	parent = dget_parent(dentry);+	di_write_lock_parent(parent);+	err = AuWbrCopyup(au_sbi(sb), dentry);+	bcpup = err;+	if (unlikely(err < 0))+		goto out_dgrade;+	err = 0;++	if (!au_h_dptr(parent, bcpup)) {+		err = au_cpup_dirs(dentry, bcpup);+		if (unlikely(err))+			goto out_dgrade;+	}++	err = au_pin(pin, dentry, bcpup, AuOpt_UDBA_NONE,+		     AuPin_DI_LOCKED | AuPin_MNT_WRITE);+	if (unlikely(err))+		goto out_dgrade;++	h_dentry = au_h_fptr(file, bstart)->f_dentry;+	h_inode = h_dentry->d_inode;+	mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);+	if (d_unhashed(dentry) /* || d_unhashed(h_dentry) */+	    /* || !h_inode->i_nlink */) {+		err = au_ready_to_write_wh(file, len, bcpup);+		di_downgrade_lock(parent, AuLock_IR);+	} else {+		di_downgrade_lock(parent, AuLock_IR);+		if (!au_h_dptr(dentry, bcpup))+			err = au_sio_cpup_simple(dentry, bcpup, len,+						 AuCpup_DTIME);+		if (!err)+			err = au_reopen_nondir(file);+	}+	mutex_unlock(&h_inode->i_mutex);++	if (!err) {+		au_pin_set_parent_lflag(pin, /*lflag*/0);+		goto out_dput; /* success */+	}+	au_unpin(pin);+	goto out_unlock;++ out_dgrade:+	di_downgrade_lock(parent, AuLock_IR);+ out_unlock:+	di_read_unlock(parent, AuLock_IR);+ out_dput:+	dput(parent);+ out:+	return err;+}++/* ---------------------------------------------------------------------- */++static int au_file_refresh_by_inode(struct file *file, int *need_reopen)+{+	int err;+	aufs_bindex_t bstart;+	struct au_pin pin;+	struct au_finfo *finfo;+	struct dentry *dentry, *parent, *hi_wh;+	struct inode *inode;+	struct super_block *sb;++	FiMustWriteLock(file);++	err = 0;+	finfo = au_fi(file);+	dentry = file->f_dentry;+	sb = dentry->d_sb;+	inode = dentry->d_inode;+	bstart = au_ibstart(inode);+	if (bstart == finfo->fi_bstart)+		goto out;++	parent = dget_parent(dentry);+	if (au_test_ro(sb, bstart, inode)) {+		di_read_lock_parent(parent, !AuLock_IR);+		err = AuWbrCopyup(au_sbi(sb), dentry);+		bstart = err;+		di_read_unlock(parent, !AuLock_IR);+		if (unlikely(err < 0))+			goto out_parent;+		err = 0;+	}++	di_read_lock_parent(parent, AuLock_IR);+	hi_wh = au_hi_wh(inode, bstart);+	if (au_opt_test(au_mntflags(sb), PLINK)+	    && au_plink_test(inode)+	    && !d_unhashed(dentry)) {+		err = au_test_and_cpup_dirs(dentry, bstart);+		if (unlikely(err))+			goto out_unlock;++		/* always superio. */+		err = au_pin(&pin, dentry, bstart, AuOpt_UDBA_NONE,+			     AuPin_DI_LOCKED | AuPin_MNT_WRITE);+		if (!err)+			err = au_sio_cpup_simple(dentry, bstart, -1,+						 AuCpup_DTIME);+		au_unpin(&pin);+	} else if (hi_wh) {+		/* already copied-up after unlink */+		err = au_reopen_wh(file, bstart, hi_wh);+		*need_reopen = 0;+	}++ out_unlock:+	di_read_unlock(parent, AuLock_IR);+ out_parent:+	dput(parent);+ out:+	return err;+}++static void au_do_refresh_file(struct file *file)+{+	aufs_bindex_t bindex, bend, new_bindex, brid;+	struct au_hfile *p, tmp, *q;+	struct au_finfo *finfo;+	struct super_block *sb;++	FiMustWriteLock(file);++	sb = file->f_dentry->d_sb;+	finfo = au_fi(file);+	p = finfo->fi_hfile + finfo->fi_bstart;+	brid = p->hf_br->br_id;+	bend = finfo->fi_bend;+	for (bindex = finfo->fi_bstart; bindex <= bend; bindex++, p++) {+		if (!p->hf_file)+			continue;++		new_bindex = au_br_index(sb, p->hf_br->br_id);+		if (new_bindex == bindex)+			continue;+		if (new_bindex < 0) {+			au_set_h_fptr(file, bindex, NULL);+			continue;+		}++		/* swap two lower inode, and loop again */+		q = finfo->fi_hfile + new_bindex;+		tmp = *q;+		*q = *p;+		*p = tmp;+		if (tmp.hf_file) {+			bindex--;+			p--;+		}+	}++	p = finfo->fi_hfile;+	if (!au_test_mmapped(file) && !d_unhashed(file->f_dentry)) {+		bend = au_sbend(sb);+		for (finfo->fi_bstart = 0; finfo->fi_bstart <= bend;+		     finfo->fi_bstart++, p++)+			if (p->hf_file) {+				if (p->hf_file->f_dentry+				    && p->hf_file->f_dentry->d_inode)+					break;+				else+					au_hfput(p, file);+			}+	} else {+		bend = au_br_index(sb, brid);+		for (finfo->fi_bstart = 0; finfo->fi_bstart < bend;+		     finfo->fi_bstart++, p++)+			if (p->hf_file)+				au_hfput(p, file);+		bend = au_sbend(sb);+	}++	p = finfo->fi_hfile + bend;+	for (finfo->fi_bend = bend; finfo->fi_bend >= finfo->fi_bstart;+	     finfo->fi_bend--, p--)+		if (p->hf_file) {+			if (p->hf_file->f_dentry+			    && p->hf_file->f_dentry->d_inode)+				break;+			else+				au_hfput(p, file);+		}+	AuDebugOn(finfo->fi_bend < finfo->fi_bstart);+}++/*+ * after branch manipulating, refresh the file.+ */+static int refresh_file(struct file *file, int (*reopen)(struct file *file))+{+	int err, need_reopen;+	struct dentry *dentry;+	aufs_bindex_t bend, bindex;++	dentry = file->f_dentry;+	err = au_fi_realloc(au_fi(file), au_sbend(dentry->d_sb) + 1);+	if (unlikely(err))+		goto out;+	au_do_refresh_file(file);++	err = 0;+	need_reopen = 1;+	if (!au_test_mmapped(file))+		err = au_file_refresh_by_inode(file, &need_reopen);+	if (!err && need_reopen && !d_unhashed(dentry))+		err = reopen(file);+	if (!err) {+		au_update_figen(file);+		return 0; /* success */+	}++	/* error, close all lower files */+	bend = au_fbend(file);+	for (bindex = au_fbstart(file); bindex <= bend; bindex++)+		au_set_h_fptr(file, bindex, NULL);++ out:+	return err;+}++/* common function to regular file and dir */+int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),+			  int wlock)+{+	int err;+	unsigned int sigen, figen;+	aufs_bindex_t bstart;+	unsigned char pseudo_link;+	struct dentry *dentry;++	err = 0;+	dentry = file->f_dentry;+	sigen = au_sigen(dentry->d_sb);+	fi_write_lock(file);+	figen = au_figen(file);+	di_write_lock_child(dentry);+	bstart = au_dbstart(dentry);+	pseudo_link = (bstart != au_ibstart(dentry->d_inode));+	if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) {+		if (!wlock) {+			di_downgrade_lock(dentry, AuLock_IR);+			fi_downgrade_lock(file);+		}+		goto out; /* success */+	}++	AuDbg("sigen %d, figen %d\n", sigen, figen);+	if (sigen != au_digen(dentry)+	    || sigen != au_iigen(dentry->d_inode)) {+		err = au_reval_dpath(dentry, sigen);+		if (unlikely(err < 0))+			goto out;+		AuDebugOn(au_digen(dentry) != sigen+			  || au_iigen(dentry->d_inode) != sigen);+	}++	err = refresh_file(file, reopen);+	if (!err) {+		if (!wlock) {+			di_downgrade_lock(dentry, AuLock_IR);+			fi_downgrade_lock(file);+		}+	} else {+		di_write_unlock(dentry);+		fi_write_unlock(file);+	}++ out:+	return err;+}++/* ---------------------------------------------------------------------- */++/* cf. aufs_nopage() */+/* for madvise(2) */+static int aufs_readpage(struct file *file __maybe_unused, struct page *page)+{+	unlock_page(page);+	return 0;+}++/* they will never be called. */+#ifdef CONFIG_AUFS_DEBUG+static int aufs_write_begin(struct file *file, struct address_space *mapping,+			    loff_t pos, unsigned len, unsigned flags,+			    struct page **pagep, void **fsdata)+{ AuUnsupport(); return 0; }+static int aufs_write_end(struct file *file, struct address_space *mapping,+			  loff_t pos, unsigned len, unsigned copied,+			  struct page *page, void *fsdata)+{ AuUnsupport(); return 0; }+static int aufs_writepage(struct page *page, struct writeback_control *wbc)+{ AuUnsupport(); return 0; }+static void aufs_sync_page(struct page *page)+{ AuUnsupport(); }++static int aufs_set_page_dirty(struct page *page)+{ AuUnsupport(); return 0; }+static void aufs_invalidatepage(struct page *page, unsigned long offset)+{ AuUnsupport(); }+static int aufs_releasepage(struct page *page, gfp_t gfp)+{ AuUnsupport(); return 0; }+static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb,+			      const struct iovec *iov, loff_t offset,+			      unsigned long nr_segs)+{ AuUnsupport(); return 0; }+#endif /* CONFIG_AUFS_DEBUG */++struct address_space_operations aufs_aop = {+	.readpage	= aufs_readpage,+#ifdef CONFIG_AUFS_DEBUG+	.writepage	= aufs_writepage,+	.sync_page	= aufs_sync_page,+	.set_page_dirty	= aufs_set_page_dirty,+	.write_begin	= aufs_write_begin,+	.write_end	= aufs_write_end,+	.invalidatepage	= aufs_invalidatepage,+	.releasepage	= aufs_releasepage,+	.direct_IO	= aufs_direct_IO,+#endif /* CONFIG_AUFS_DEBUG */+};diff -Nur linux-2.6.31.5.orig/fs/aufs/file.h linux-2.6.31.5/fs/aufs/file.h--- linux-2.6.31.5.orig/fs/aufs/file.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/file.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,175 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * file operations+ */++#ifndef __AUFS_FILE_H__+#define __AUFS_FILE_H__++#ifdef __KERNEL__++#include <linux/fs.h>+#include <linux/poll.h>+#include <linux/aufs_type.h>+#include "rwsem.h"++struct au_branch;+struct au_hfile {+	struct file		*hf_file;+	struct au_branch	*hf_br;+};++struct au_vdir;+struct au_finfo {+	atomic_t		fi_generation;++	struct au_rwsem		fi_rwsem;+	struct au_hfile		*fi_hfile;+	aufs_bindex_t		fi_bstart, fi_bend;++	union {+		/* non-dir only */+		struct {+			struct vm_operations_struct	*fi_h_vm_ops;+			struct vm_operations_struct	*fi_vm_ops;+		};++		/* dir only */+		struct {+			struct au_vdir		*fi_vdir_cache;+			int			fi_maintain_plink;+		};+	};+};++/* ---------------------------------------------------------------------- */++/* file.c */+extern struct address_space_operations aufs_aop;+void au_store_oflag(struct nameidata *nd, struct inode *inode);+unsigned int au_file_roflags(unsigned int flags);+struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,+		       struct file *file);+int au_do_open(struct file *file, int (*open)(struct file *file, int flags));+int au_reopen_nondir(struct file *file);+struct au_pin;+int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin);+int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),+			  int wlock);++/* poll.c */+#ifdef CONFIG_AUFS_POLL+unsigned int aufs_poll(struct file *file, poll_table *wait);+#endif++/* f_op.c */+extern const struct file_operations aufs_file_fop;+int aufs_flush(struct file *file, fl_owner_t id);++/* finfo.c */+void au_hfput(struct au_hfile *hf, struct file *file);+void au_set_h_fptr(struct file *file, aufs_bindex_t bindex,+		   struct file *h_file);++void au_update_figen(struct file *file);++void au_finfo_fin(struct file *file);+int au_finfo_init(struct file *file);+int au_fi_realloc(struct au_finfo *finfo, int nbr);++/* ---------------------------------------------------------------------- */++static inline struct au_finfo *au_fi(struct file *file)+{+	return file->private_data;+}++/* ---------------------------------------------------------------------- */++/*+ * fi_read_lock, fi_write_lock,+ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock+ */+AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem);++#define FiMustNoWaiters(f)	AuRwMustNoWaiters(&au_fi(f)->fi_rwsem)+#define FiMustAnyLock(f)	AuRwMustAnyLock(&au_fi(f)->fi_rwsem)+#define FiMustWriteLock(f)	AuRwMustWriteLock(&au_fi(f)->fi_rwsem)++/* ---------------------------------------------------------------------- */++/* todo: hard/soft set? */+static inline aufs_bindex_t au_fbstart(struct file *file)+{+	FiMustAnyLock(file);+	return au_fi(file)->fi_bstart;+}++static inline aufs_bindex_t au_fbend(struct file *file)+{+	FiMustAnyLock(file);+	return au_fi(file)->fi_bend;+}++static inline struct au_vdir *au_fvdir_cache(struct file *file)+{+	FiMustAnyLock(file);+	return au_fi(file)->fi_vdir_cache;+}++static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex)+{+	FiMustWriteLock(file);+	au_fi(file)->fi_bstart = bindex;+}++static inline void au_set_fbend(struct file *file, aufs_bindex_t bindex)+{+	FiMustWriteLock(file);+	au_fi(file)->fi_bend = bindex;+}++static inline void au_set_fvdir_cache(struct file *file,+				      struct au_vdir *vdir_cache)+{+	FiMustWriteLock(file);+	au_fi(file)->fi_vdir_cache = vdir_cache;+}++static inline struct file *au_h_fptr(struct file *file, aufs_bindex_t bindex)+{+	FiMustAnyLock(file);+	return au_fi(file)->fi_hfile[0 + bindex].hf_file;+}++/* todo: memory barrier? */+static inline unsigned int au_figen(struct file *f)+{+	return atomic_read(&au_fi(f)->fi_generation);+}++static inline int au_test_mmapped(struct file *f)+{+	FiMustAnyLock(f);+	return !!(au_fi(f)->fi_h_vm_ops);+}++#endif /* __KERNEL__ */+#endif /* __AUFS_FILE_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/finfo.c linux-2.6.31.5/fs/aufs/finfo.c--- linux-2.6.31.5.orig/fs/aufs/finfo.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/finfo.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,133 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * file private data+ */++#include <linux/file.h>+#include "aufs.h"++void au_hfput(struct au_hfile *hf, struct file *file)+{+	if (file->f_mode & FMODE_EXEC)+		allow_write_access(hf->hf_file);+	fput(hf->hf_file);+	hf->hf_file = NULL;+	atomic_dec_return(&hf->hf_br->br_count);+	hf->hf_br = NULL;+}++void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val)+{+	struct au_finfo *finfo = au_fi(file);+	struct au_hfile *hf;++	hf = finfo->fi_hfile + bindex;+	if (hf->hf_file)+		au_hfput(hf, file);+	if (val) {+		hf->hf_file = val;+		hf->hf_br = au_sbr(file->f_dentry->d_sb, bindex);+	}+}++void au_update_figen(struct file *file)+{+	atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_dentry));+	/* smp_mb(); */ /* atomic_set */+}++/* ---------------------------------------------------------------------- */++void au_finfo_fin(struct file *file)+{+	struct au_finfo *finfo;+	aufs_bindex_t bindex, bend;++	fi_write_lock(file);+	bend = au_fbend(file);+	bindex = au_fbstart(file);+	if (bindex >= 0)+		/*+		 * calls fput() instead of filp_close(),+		 * since no dnotify or lock for the lower file.+		 */+		for (; bindex <= bend; bindex++)+			au_set_h_fptr(file, bindex, NULL);++	finfo = au_fi(file);+	au_dbg_verify_hf(finfo);+	kfree(finfo->fi_hfile);+	fi_write_unlock(file);+	AuRwDestroy(&finfo->fi_rwsem);+	au_cache_free_finfo(finfo);+}++int au_finfo_init(struct file *file)+{+	struct au_finfo *finfo;+	struct dentry *dentry;+	unsigned long ul;++	dentry = file->f_dentry;+	finfo = au_cache_alloc_finfo();+	if (unlikely(!finfo))+		goto out;++	finfo->fi_hfile = kcalloc(au_sbend(dentry->d_sb) + 1,+				  sizeof(*finfo->fi_hfile), GFP_NOFS);+	if (unlikely(!finfo->fi_hfile))+		goto out_finfo;++	au_rw_init_wlock(&finfo->fi_rwsem);+	finfo->fi_bstart = -1;+	finfo->fi_bend = -1;+	atomic_set(&finfo->fi_generation, au_digen(dentry));+	/* smp_mb(); */ /* atomic_set */++	/* cf. au_store_oflag() */+	/* suppress a warning in lp64 */+	ul = (unsigned long)file->private_data;+	file->f_mode |= (vfsub_uint_to_fmode(ul) & FMODE_EXEC);+	file->private_data = finfo;+	return 0; /* success */++ out_finfo:+	au_cache_free_finfo(finfo);+ out:+	return -ENOMEM;+}++int au_fi_realloc(struct au_finfo *finfo, int nbr)+{+	int err, sz;+	struct au_hfile *hfp;++	err = -ENOMEM;+	sz = sizeof(*hfp) * (finfo->fi_bend + 1);+	if (!sz)+		sz = sizeof(*hfp);+	hfp = au_kzrealloc(finfo->fi_hfile, sz, sizeof(*hfp) * nbr, GFP_NOFS);+	if (hfp) {+		finfo->fi_hfile = hfp;+		err = 0;+	}++	return err;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/f_op.c linux-2.6.31.5/fs/aufs/f_op.c--- linux-2.6.31.5.orig/fs/aufs/f_op.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/f_op.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,802 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * file and vm operations+ */++#include <linux/file.h>+#include <linux/fs_stack.h>+#include <linux/mm.h>+#include <linux/security.h>+#include "aufs.h"++/* common function to regular file and dir */+int aufs_flush(struct file *file, fl_owner_t id)+{+	int err;+	aufs_bindex_t bindex, bend;+	struct dentry *dentry;+	struct file *h_file;++	dentry = file->f_dentry;+	si_noflush_read_lock(dentry->d_sb);+	fi_read_lock(file);+	di_read_lock_child(dentry, AuLock_IW);++	err = 0;+	bend = au_fbend(file);+	for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) {+		h_file = au_h_fptr(file, bindex);+		if (!h_file || !h_file->f_op || !h_file->f_op->flush)+			continue;++		err = h_file->f_op->flush(h_file, id);+		if (!err)+			vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL);+		/*ignore*/+	}+	au_cpup_attr_timesizes(dentry->d_inode);++	di_read_unlock(dentry, AuLock_IW);+	fi_read_unlock(file);+	si_read_unlock(dentry->d_sb);+	return err;+}++/* ---------------------------------------------------------------------- */++static int do_open_nondir(struct file *file, int flags)+{+	int err;+	aufs_bindex_t bindex;+	struct file *h_file;+	struct dentry *dentry;+	struct au_finfo *finfo;++	FiMustWriteLock(file);++	err = 0;+	dentry = file->f_dentry;+	finfo = au_fi(file);+	finfo->fi_h_vm_ops = NULL;+	finfo->fi_vm_ops = NULL;+	bindex = au_dbstart(dentry);+	/* O_TRUNC is processed already */+	BUG_ON(au_test_ro(dentry->d_sb, bindex, dentry->d_inode)+	       && (flags & O_TRUNC));++	h_file = au_h_open(dentry, bindex, flags, file);+	if (IS_ERR(h_file))+		err = PTR_ERR(h_file);+	else {+		au_set_fbstart(file, bindex);+		au_set_fbend(file, bindex);+		au_set_h_fptr(file, bindex, h_file);+		au_update_figen(file);+		/* todo: necessary? */+		/* file->f_ra = h_file->f_ra; */+	}+	return err;+}++static int aufs_open_nondir(struct inode *inode __maybe_unused,+			    struct file *file)+{+	return au_do_open(file, do_open_nondir);+}++static int aufs_release_nondir(struct inode *inode __maybe_unused,+			       struct file *file)+{+	struct super_block *sb = file->f_dentry->d_sb;++	si_noflush_read_lock(sb);+	kfree(au_fi(file)->fi_vm_ops);+	au_finfo_fin(file);+	si_read_unlock(sb);+	return 0;+}++/* ---------------------------------------------------------------------- */++static ssize_t aufs_read(struct file *file, char __user *buf, size_t count,+			 loff_t *ppos)+{+	ssize_t err;+	struct dentry *dentry;+	struct file *h_file;+	struct super_block *sb;++	dentry = file->f_dentry;+	sb = dentry->d_sb;+	si_read_lock(sb, AuLock_FLUSH);+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);+	if (unlikely(err))+		goto out;++	h_file = au_h_fptr(file, au_fbstart(file));+	err = vfsub_read_u(h_file, buf, count, ppos);+	/* todo: necessary? */+	/* file->f_ra = h_file->f_ra; */+	fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode);++	di_read_unlock(dentry, AuLock_IR);+	fi_read_unlock(file);+ out:+	si_read_unlock(sb);+	return err;+}++static ssize_t aufs_write(struct file *file, const char __user *ubuf,+			  size_t count, loff_t *ppos)+{+	ssize_t err;+	aufs_bindex_t bstart;+	struct au_pin pin;+	struct dentry *dentry;+	struct inode *inode;+	struct super_block *sb;+	struct file *h_file;+	char __user *buf = (char __user *)ubuf;++	dentry = file->f_dentry;+	sb = dentry->d_sb;+	inode = dentry->d_inode;+	mutex_lock(&inode->i_mutex);+	si_read_lock(sb, AuLock_FLUSH);++	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);+	if (unlikely(err))+		goto out;++	err = au_ready_to_write(file, -1, &pin);+	di_downgrade_lock(dentry, AuLock_IR);+	if (unlikely(err))+		goto out_unlock;++	bstart = au_fbstart(file);+	h_file = au_h_fptr(file, bstart);+	au_unpin(&pin);+	err = vfsub_write_u(h_file, buf, count, ppos);+	au_cpup_attr_timesizes(inode);+	inode->i_mode = h_file->f_dentry->d_inode->i_mode;++ out_unlock:+	di_read_unlock(dentry, AuLock_IR);+	fi_write_unlock(file);+ out:+	si_read_unlock(sb);+	mutex_unlock(&inode->i_mutex);+	return err;+}++static ssize_t aufs_aio_read(struct kiocb *kio, const struct iovec *iov,+			     unsigned long nv, loff_t pos)+{+	ssize_t err;+	struct file *file, *h_file;+	struct dentry *dentry;+	struct super_block *sb;++	file = kio->ki_filp;+	dentry = file->f_dentry;+	sb = dentry->d_sb;+	si_read_lock(sb, AuLock_FLUSH);+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);+	if (unlikely(err))+		goto out;++	err = -ENOSYS;+	h_file = au_h_fptr(file, au_fbstart(file));+	if (h_file->f_op && h_file->f_op->aio_read) {+		err = security_file_permission(h_file, MAY_READ);+		if (unlikely(err))+			goto out_unlock;+		if (!is_sync_kiocb(kio)) {+			get_file(h_file);+			fput(file);+		}+		kio->ki_filp = h_file;+		err = h_file->f_op->aio_read(kio, iov, nv, pos);+		/* todo: necessary? */+		/* file->f_ra = h_file->f_ra; */+		fsstack_copy_attr_atime(dentry->d_inode,+					h_file->f_dentry->d_inode);+	} else+		/* currently there is no such fs */+		WARN_ON_ONCE(h_file->f_op && h_file->f_op->read);++ out_unlock:+	di_read_unlock(dentry, AuLock_IR);+	fi_read_unlock(file);+ out:+	si_read_unlock(sb);+	return err;+}++static ssize_t aufs_aio_write(struct kiocb *kio, const struct iovec *iov,+			      unsigned long nv, loff_t pos)+{+	ssize_t err;+	aufs_bindex_t bstart;+	struct au_pin pin;+	struct dentry *dentry;+	struct inode *inode;+	struct super_block *sb;+	struct file *file, *h_file;++	file = kio->ki_filp;+	dentry = file->f_dentry;+	sb = dentry->d_sb;+	inode = dentry->d_inode;+	mutex_lock(&inode->i_mutex);+	si_read_lock(sb, AuLock_FLUSH);++	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);+	if (unlikely(err))+		goto out;++	err = au_ready_to_write(file, -1, &pin);+	di_downgrade_lock(dentry, AuLock_IR);+	if (unlikely(err))+		goto out_unlock;++	err = -ENOSYS;+	bstart = au_fbstart(file);+	h_file = au_h_fptr(file, bstart);+	au_unpin(&pin);+	if (h_file->f_op && h_file->f_op->aio_write) {+		err = security_file_permission(h_file, MAY_WRITE);+		if (unlikely(err))+			goto out_unlock;+		if (!is_sync_kiocb(kio)) {+			get_file(h_file);+			fput(file);+		}+		kio->ki_filp = h_file;+		err = h_file->f_op->aio_write(kio, iov, nv, pos);+		au_cpup_attr_timesizes(inode);+		inode->i_mode = h_file->f_dentry->d_inode->i_mode;+	} else+		/* currently there is no such fs */+		WARN_ON_ONCE(h_file->f_op && h_file->f_op->write);++ out_unlock:+	di_read_unlock(dentry, AuLock_IR);+	fi_write_unlock(file);+ out:+	si_read_unlock(sb);+	mutex_unlock(&inode->i_mutex);+	return err;+}++static ssize_t aufs_splice_read(struct file *file, loff_t *ppos,+				struct pipe_inode_info *pipe, size_t len,+				unsigned int flags)+{+	ssize_t err;+	struct file *h_file;+	struct dentry *dentry;+	struct super_block *sb;++	dentry = file->f_dentry;+	sb = dentry->d_sb;+	si_read_lock(sb, AuLock_FLUSH);+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);+	if (unlikely(err))+		goto out;++	err = -EINVAL;+	h_file = au_h_fptr(file, au_fbstart(file));+	if (au_test_loopback_kthread()) {+		file->f_mapping = h_file->f_mapping;+		smp_mb(); /* unnecessary? */+	}+	err = vfsub_splice_to(h_file, ppos, pipe, len, flags);+	/* todo: necessasry? */+	/* file->f_ra = h_file->f_ra; */+	fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode);++	di_read_unlock(dentry, AuLock_IR);+	fi_read_unlock(file);++ out:+	si_read_unlock(sb);+	return err;+}++static ssize_t+aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos,+		  size_t len, unsigned int flags)+{+	ssize_t err;+	struct au_pin pin;+	struct dentry *dentry;+	struct inode *inode;+	struct super_block *sb;+	struct file *h_file;++	dentry = file->f_dentry;+	inode = dentry->d_inode;+	mutex_lock(&inode->i_mutex);+	sb = dentry->d_sb;+	si_read_lock(sb, AuLock_FLUSH);++	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);+	if (unlikely(err))+		goto out;++	err = au_ready_to_write(file, -1, &pin);+	di_downgrade_lock(dentry, AuLock_IR);+	if (unlikely(err))+		goto out_unlock;++	h_file = au_h_fptr(file, au_fbstart(file));+	au_unpin(&pin);+	err = vfsub_splice_from(pipe, h_file, ppos, len, flags);+	au_cpup_attr_timesizes(inode);+	inode->i_mode = h_file->f_dentry->d_inode->i_mode;++ out_unlock:+	di_read_unlock(dentry, AuLock_IR);+	fi_write_unlock(file);+ out:+	si_read_unlock(sb);+	mutex_unlock(&inode->i_mutex);+	return err;+}++/* ---------------------------------------------------------------------- */++static struct file *au_safe_file(struct vm_area_struct *vma)+{+	struct file *file;++	file = vma->vm_file;+	if (file->private_data && au_test_aufs(file->f_dentry->d_sb))+		return file;+	return NULL;+}++static void au_reset_file(struct vm_area_struct *vma, struct file *file)+{+	vma->vm_file = file;+	/* smp_mb(); */ /* flush vm_file */+}++static int aufs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)+{+	int err;+	static DECLARE_WAIT_QUEUE_HEAD(wq);+	struct file *file, *h_file;+	struct au_finfo *finfo;++	/* todo: non-robr mode, user vm_file as it is? */+	wait_event(wq, (file = au_safe_file(vma)));++	/* do not revalidate, no si lock */+	finfo = au_fi(file);+	h_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;+	AuDebugOn(!h_file || !finfo->fi_h_vm_ops);++	fi_write_lock(file);+	vma->vm_file = h_file;+	err = finfo->fi_h_vm_ops->fault(vma, vmf);+	/* todo: necessary? */+	/* file->f_ra = h_file->f_ra; */+	au_reset_file(vma, file);+	fi_write_unlock(file);+#if 0 /* def CONFIG_SMP */+	/* wake_up_nr(&wq, online_cpu - 1); */+	wake_up_all(&wq);+#else+	wake_up(&wq);+#endif++	return err;+}++static int aufs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)+{+	int err;+	static DECLARE_WAIT_QUEUE_HEAD(wq);+	struct file *file, *h_file;+	struct au_finfo *finfo;++	wait_event(wq, (file = au_safe_file(vma)));++	finfo = au_fi(file);+	h_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;+	AuDebugOn(!h_file || !finfo->fi_h_vm_ops);++	fi_write_lock(file);+	vma->vm_file = h_file;+	err = finfo->fi_h_vm_ops->page_mkwrite(vma, vmf);+	au_reset_file(vma, file);+	fi_write_unlock(file);+	wake_up(&wq);++	return err;+}++static void aufs_vm_close(struct vm_area_struct *vma)+{+	static DECLARE_WAIT_QUEUE_HEAD(wq);+	struct file *file, *h_file;+	struct au_finfo *finfo;++	wait_event(wq, (file = au_safe_file(vma)));++	finfo = au_fi(file);+	h_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;+	AuDebugOn(!h_file || !finfo->fi_h_vm_ops);++	fi_write_lock(file);+	vma->vm_file = h_file;+	finfo->fi_h_vm_ops->close(vma);+	au_reset_file(vma, file);+	fi_write_unlock(file);+	wake_up(&wq);+}++static struct vm_operations_struct aufs_vm_ops = {+	/* .close and .page_mkwrite are not set by default */+	.fault		= aufs_fault,+};++/* ---------------------------------------------------------------------- */++static struct vm_operations_struct *au_vm_ops(struct file *h_file,+					      struct vm_area_struct *vma)+{+	struct vm_operations_struct *vm_ops;+	int err;++	vm_ops = ERR_PTR(-ENODEV);+	if (!h_file->f_op || !h_file->f_op->mmap)+		goto out;++	err = h_file->f_op->mmap(h_file, vma);+	vm_ops = ERR_PTR(err);+	if (unlikely(err))+		goto out;++	vm_ops = vma->vm_ops;+	err = do_munmap(current->mm, vma->vm_start,+			vma->vm_end - vma->vm_start);+	if (unlikely(err)) {+		AuIOErr("failed internal unmapping %.*s, %d\n",+			AuDLNPair(h_file->f_dentry), err);+		vm_ops = ERR_PTR(-EIO);+	}++ out:+	return vm_ops;+}++static int au_custom_vm_ops(struct au_finfo *finfo, struct vm_area_struct *vma)+{+	int err;+	struct vm_operations_struct *h_ops;++	AuRwMustAnyLock(&finfo->fi_rwsem);++	err = 0;+	h_ops = finfo->fi_h_vm_ops;+	AuDebugOn(!h_ops);+	if ((!h_ops->page_mkwrite && !h_ops->close)+	    || finfo->fi_vm_ops)+		goto out;++	err = -ENOMEM;+	finfo->fi_vm_ops = kmemdup(&aufs_vm_ops, sizeof(aufs_vm_ops), GFP_NOFS);+	if (unlikely(!finfo->fi_vm_ops))+		goto out;++	err = 0;+	if (h_ops->page_mkwrite)+		finfo->fi_vm_ops->page_mkwrite = aufs_page_mkwrite;+	if (h_ops->close)+		finfo->fi_vm_ops->close = aufs_vm_close;++	vma->vm_ops = finfo->fi_vm_ops;++ out:+	return err;+}++static int aufs_mmap(struct file *file, struct vm_area_struct *vma)+{+	int err;+	unsigned char wlock, mmapped;+	struct dentry *dentry;+	struct super_block *sb;+	struct file *h_file;+	struct vm_operations_struct *vm_ops;++	dentry = file->f_dentry;+	wlock = !!(file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED);+	sb = dentry->d_sb;+	si_read_lock(sb, AuLock_FLUSH);+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);+	if (unlikely(err))+		goto out;++	mmapped = !!au_test_mmapped(file);+	if (wlock) {+		struct au_pin pin;++		err = au_ready_to_write(file, -1, &pin);+		di_downgrade_lock(dentry, AuLock_IR);+		if (unlikely(err))+			goto out_unlock;+		au_unpin(&pin);+	} else+		di_downgrade_lock(dentry, AuLock_IR);++	h_file = au_h_fptr(file, au_fbstart(file));+	if (!mmapped && au_test_fs_bad_mapping(h_file->f_dentry->d_sb)) {+		/*+		 * by this assignment, f_mapping will differs from aufs inode+		 * i_mapping.+		 * if someone else mixes the use of f_dentry->d_inode and+		 * f_mapping->host, then a problem may arise.+		 */+		file->f_mapping = h_file->f_mapping;+	}++	vm_ops = NULL;+	if (!mmapped) {+		vm_ops = au_vm_ops(h_file, vma);+		err = PTR_ERR(vm_ops);+		if (IS_ERR(vm_ops))+			goto out_unlock;+	}++	/*+	 * unnecessary to handle MAP_DENYWRITE and deny_write_access()?+	 * currently MAP_DENYWRITE from userspace is ignored, but elf loader+	 * sets it. when FMODE_EXEC is set (by open_exec() or sys_uselib()),+	 * both of the aufs file and the lower file is deny_write_access()-ed.+	 * finally I hope we can skip handlling MAP_DENYWRITE here.+	 */+	err = generic_file_mmap(file, vma);+	if (unlikely(err))+		goto out_unlock;++	vma->vm_ops = &aufs_vm_ops;+	/* test again */+	if (!au_test_mmapped(file))+		au_fi(file)->fi_h_vm_ops = vm_ops;++	err = au_custom_vm_ops(au_fi(file), vma);+	if (unlikely(err))+		goto out_unlock;++	vfsub_file_accessed(h_file);+	fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode);++ out_unlock:+	di_read_unlock(dentry, AuLock_IR);+	fi_write_unlock(file);+ out:+	si_read_unlock(sb);+	return err;+}++/* ---------------------------------------------------------------------- */++static int aufs_fsync_nondir(struct file *file, struct dentry *dentry,+			     int datasync)+{+	int err;+	struct au_pin pin;+	struct inode *inode;+	struct file *h_file;+	struct super_block *sb;++	inode = dentry->d_inode;+	IMustLock(file->f_mapping->host);+	if (inode != file->f_mapping->host) {+		mutex_unlock(&file->f_mapping->host->i_mutex);+		mutex_lock(&inode->i_mutex);+	}+	IMustLock(inode);++	sb = dentry->d_sb;+	si_read_lock(sb, AuLock_FLUSH);++	err = 0; /* -EBADF; */ /* posix? */+	if (unlikely(!(file->f_mode & FMODE_WRITE)))+		goto out;+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);+	if (unlikely(err))+		goto out;++	err = au_ready_to_write(file, -1, &pin);+	di_downgrade_lock(dentry, AuLock_IR);+	if (unlikely(err))+		goto out_unlock;+	au_unpin(&pin);++	err = -EINVAL;+	h_file = au_h_fptr(file, au_fbstart(file));+	if (h_file->f_op && h_file->f_op->fsync) {+		struct dentry *h_d;+		struct mutex *h_mtx;++		/*+		 * no filemap_fdatawrite() since aufs file has no its own+		 * mapping, but dir.+		 */+		h_d = h_file->f_dentry;+		h_mtx = &h_d->d_inode->i_mutex;+		mutex_lock_nested(h_mtx, AuLsc_I_CHILD);+		err = h_file->f_op->fsync(h_file, h_d, datasync);+		if (!err)+			vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL);+		/*ignore*/+		au_cpup_attr_timesizes(inode);+		mutex_unlock(h_mtx);+	}++ out_unlock:+	di_read_unlock(dentry, AuLock_IR);+	fi_write_unlock(file);+ out:+	si_read_unlock(sb);+	if (inode != file->f_mapping->host) {+		mutex_unlock(&inode->i_mutex);+		mutex_lock(&file->f_mapping->host->i_mutex);+	}+	return err;+}++/* no one supports this operation, currently */+#if 0+static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync)+{+	int err;+	struct au_pin pin;+	struct dentry *dentry;+	struct inode *inode;+	struct file *file, *h_file;+	struct super_block *sb;++	file = kio->ki_filp;+	dentry = file->f_dentry;+	inode = dentry->d_inode;+	mutex_lock(&inode->i_mutex);++	sb = dentry->d_sb;+	si_read_lock(sb, AuLock_FLUSH);++	err = 0; /* -EBADF; */ /* posix? */+	if (unlikely(!(file->f_mode & FMODE_WRITE)))+		goto out;+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);+	if (unlikely(err))+		goto out;++	err = au_ready_to_write(file, -1, &pin);+	di_downgrade_lock(dentry, AuLock_IR);+	if (unlikely(err))+		goto out_unlock;+	au_unpin(&pin);++	err = -ENOSYS;+	h_file = au_h_fptr(file, au_fbstart(file));+	if (h_file->f_op && h_file->f_op->aio_fsync) {+		struct dentry *h_d;+		struct mutex *h_mtx;++		h_d = h_file->f_dentry;+		h_mtx = &h_d->d_inode->i_mutex;+		if (!is_sync_kiocb(kio)) {+			get_file(h_file);+			fput(file);+		}+		kio->ki_filp = h_file;+		err = h_file->f_op->aio_fsync(kio, datasync);+		mutex_lock_nested(h_mtx, AuLsc_I_CHILD);+		if (!err)+			vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL);+		/*ignore*/+		au_cpup_attr_timesizes(inode);+		mutex_unlock(h_mtx);+	}++ out_unlock:+	di_read_unlock(dentry, AuLock_IR);+	fi_write_unlock(file);+ out:+	si_read_unlock(sb);+	mutex_unlock(&inode->i_mutex);+	return err;+}+#endif++static int aufs_fasync(int fd, struct file *file, int flag)+{+	int err;+	struct file *h_file;+	struct dentry *dentry;+	struct super_block *sb;++	dentry = file->f_dentry;+	sb = dentry->d_sb;+	si_read_lock(sb, AuLock_FLUSH);+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);+	if (unlikely(err))+		goto out;++	h_file = au_h_fptr(file, au_fbstart(file));+	if (h_file->f_op && h_file->f_op->fasync)+		err = h_file->f_op->fasync(fd, h_file, flag);++	di_read_unlock(dentry, AuLock_IR);+	fi_read_unlock(file);++ out:+	si_read_unlock(sb);+	return err;+}++/* ---------------------------------------------------------------------- */++/* no one supports this operation, currently */+#if 0+static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset,+			     size_t len, loff_t *pos , int more)+{+}+#endif++/* ---------------------------------------------------------------------- */++const struct file_operations aufs_file_fop = {+	/*+	 * while generic_file_llseek/_unlocked() don't use BKL,+	 * don't use it since it operates file->f_mapping->host.+	 * in aufs, it may be a real file and may confuse users by UDBA.+	 */+	/* .llseek		= generic_file_llseek, */++	.read		= aufs_read,+	.write		= aufs_write,+	.aio_read	= aufs_aio_read,+	.aio_write	= aufs_aio_write,+#ifdef CONFIG_AUFS_POLL+	.poll		= aufs_poll,+#endif+	.mmap		= aufs_mmap,+	.open		= aufs_open_nondir,+	.flush		= aufs_flush,+	.release	= aufs_release_nondir,+	.fsync		= aufs_fsync_nondir,+	/* .aio_fsync	= aufs_aio_fsync_nondir, */+	.fasync		= aufs_fasync,+	/* .sendpage	= aufs_sendpage, */+	.splice_write	= aufs_splice_write,+	.splice_read	= aufs_splice_read,+#if 0+	.aio_splice_write = aufs_aio_splice_write,+	.aio_splice_read  = aufs_aio_splice_read+#endif+};diff -Nur linux-2.6.31.5.orig/fs/aufs/fstype.h linux-2.6.31.5/fs/aufs/fstype.h--- linux-2.6.31.5.orig/fs/aufs/fstype.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/fstype.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,474 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * judging filesystem type+ */++#ifndef __AUFS_FSTYPE_H__+#define __AUFS_FSTYPE_H__++#ifdef __KERNEL__++#include <linux/cramfs_fs.h>+#include <linux/fs.h>+#include <linux/magic.h>+#include <linux/romfs_fs.h>+#include <linux/aufs_type.h>++static inline int au_test_aufs(struct super_block *sb)+{+	return sb->s_magic == AUFS_SUPER_MAGIC;+}++static inline const char *au_sbtype(struct super_block *sb)+{+	return sb->s_type->name;+}++static inline int au_test_iso9660(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE)+	return sb->s_magic == ROMFS_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_romfs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE)+	return sb->s_magic == ISOFS_SUPER_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_cramfs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE)+	return sb->s_magic == CRAMFS_MAGIC;+#endif+	return 0;+}++static inline int au_test_nfs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE)+	return sb->s_magic == NFS_SUPER_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_fuse(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE)+	return sb->s_magic == FUSE_SUPER_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_xfs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE)+	return sb->s_magic == XFS_SB_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_tmpfs(struct super_block *sb __maybe_unused)+{+#ifdef CONFIG_TMPFS+	return sb->s_magic == TMPFS_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_ECRYPT_FS) || defined(CONFIG_ECRYPT_FS_MODULE)+	return !strcmp(au_sbtype(sb), "ecryptfs");+#else+	return 0;+#endif+}++static inline int au_test_smbfs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_SMB_FS) || defined(CONFIG_SMB_FS_MODULE)+	return sb->s_magic == SMB_SUPER_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_ocfs2(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_OCFS2_FS) || defined(CONFIG_OCFS2_FS_MODULE)+	return sb->s_magic == OCFS2_SUPER_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_ocfs2_dlmfs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_OCFS2_FS_O2CB) || defined(CONFIG_OCFS2_FS_O2CB_MODULE)+	return sb->s_magic == DLMFS_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_coda(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_CODA_FS) || defined(CONFIG_CODA_FS_MODULE)+	return sb->s_magic == CODA_SUPER_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_v9fs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_9P_FS) || defined(CONFIG_9P_FS_MODULE)+	return sb->s_magic == V9FS_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_ext4(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_EXT4DEV_FS) || defined(CONFIG_EXT4DEV_FS_MODULE)+	return sb->s_magic == EXT4_SUPER_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_sysv(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_SYSV_FS) || defined(CONFIG_SYSV_FS_MODULE)+	return !strcmp(au_sbtype(sb), "sysv");+#else+	return 0;+#endif+}++static inline int au_test_ramfs(struct super_block *sb)+{+	return sb->s_magic == RAMFS_MAGIC;+}++static inline int au_test_ubifs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_UBIFS_FS) || defined(CONFIG_UBIFS_FS_MODULE)+	return sb->s_magic == UBIFS_SUPER_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_procfs(struct super_block *sb __maybe_unused)+{+#ifdef CONFIG_PROC_FS+	return sb->s_magic == PROC_SUPER_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_sysfs(struct super_block *sb __maybe_unused)+{+#ifdef CONFIG_SYSFS+	return sb->s_magic == SYSFS_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_configfs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_CONFIGFS_FS) || defined(CONFIG_CONFIGFS_FS_MODULE)+	return sb->s_magic == CONFIGFS_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_minix(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_MINIX_FS) || defined(CONFIG_MINIX_FS_MODULE)+	return sb->s_magic == MINIX3_SUPER_MAGIC+		|| sb->s_magic == MINIX2_SUPER_MAGIC+		|| sb->s_magic == MINIX2_SUPER_MAGIC2+		|| sb->s_magic == MINIX_SUPER_MAGIC+		|| sb->s_magic == MINIX_SUPER_MAGIC2;+#else+	return 0;+#endif+}++static inline int au_test_cifs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_CIFS_FS) || defined(CONFIGCIFS_FS_MODULE)+	return sb->s_magic == CIFS_MAGIC_NUMBER;+#else+	return 0;+#endif+}++static inline int au_test_fat(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_FAT_FS) || defined(CONFIG_FAT_FS_MODULE)+	return sb->s_magic == MSDOS_SUPER_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_msdos(struct super_block *sb)+{+	return au_test_fat(sb);+}++static inline int au_test_vfat(struct super_block *sb)+{+	return au_test_fat(sb);+}++static inline int au_test_securityfs(struct super_block *sb __maybe_unused)+{+#ifdef CONFIG_SECURITYFS+	return sb->s_magic == SECURITYFS_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_squashfs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_SQUASHFS) || defined(CONFIG_SQUASHFS_MODULE)+	return sb->s_magic == SQUASHFS_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_btrfs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE)+	return sb->s_magic == BTRFS_SUPER_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_xenfs(struct super_block *sb __maybe_unused)+{+#if defined(CONFIG_XENFS) || defined(CONFIG_XENFS_MODULE)+	return sb->s_magic == XENFS_SUPER_MAGIC;+#else+	return 0;+#endif+}++static inline int au_test_debugfs(struct super_block *sb __maybe_unused)+{+#ifdef CONFIG_DEBUG_FS+	return sb->s_magic == DEBUGFS_MAGIC;+#else+	return 0;+#endif+}++/* ---------------------------------------------------------------------- */+/*+ * they can't be an aufs branch.+ */+static inline int au_test_fs_unsuppoted(struct super_block *sb)+{+	return+#ifndef CONFIG_AUFS_BR_RAMFS+		au_test_ramfs(sb) ||+#endif+		au_test_procfs(sb)+		|| au_test_sysfs(sb)+		|| au_test_configfs(sb)+		|| au_test_debugfs(sb)+		|| au_test_securityfs(sb)+		|| au_test_xenfs(sb)+		/* || !strcmp(au_sbtype(sb), "unionfs") */+		|| au_test_aufs(sb); /* will be supported in next version */+}++/*+ * If the filesystem supports NFS-export, then it has to support NULL as+ * a nameidata parameter for ->create(), ->lookup() and ->d_revalidate().+ * We can apply this principle when we handle a lower filesystem.+ */+static inline int au_test_fs_null_nd(struct super_block *sb)+{+	return !!sb->s_export_op;+}++static inline int au_test_fs_remote(struct super_block *sb)+{+	return !au_test_tmpfs(sb)+#ifdef CONFIG_AUFS_BR_RAMFS+		&& !au_test_ramfs(sb)+#endif+		&& !(sb->s_type->fs_flags & FS_REQUIRES_DEV);+}++/* ---------------------------------------------------------------------- */++/*+ * Note: these functions (below) are created after reading ->getattr() in all+ * filesystems under linux/fs. it means we have to do so in every update...+ */++/*+ * some filesystems require getattr to refresh the inode attributes before+ * referencing.+ * in most cases, we can rely on the inode attribute in NFS (or every remote fs)+ * and leave the work for d_revalidate()+ */+static inline int au_test_fs_refresh_iattr(struct super_block *sb)+{+	return au_test_nfs(sb)+		|| au_test_fuse(sb)+		/* || au_test_smbfs(sb) */	/* untested */+		/* || au_test_ocfs2(sb) */	/* untested */+		/* || au_test_btrfs(sb) */	/* untested */+		/* || au_test_coda(sb) */	/* untested */+		/* || au_test_v9fs(sb) */	/* untested */+		;+}++/*+ * filesystems which don't maintain i_size or i_blocks.+ */+static inline int au_test_fs_bad_iattr_size(struct super_block *sb)+{+	return au_test_xfs(sb)+		/* || au_test_ext4(sb) */	/* untested */+		/* || au_test_ocfs2(sb) */	/* untested */+		/* || au_test_ocfs2_dlmfs(sb) */ /* untested */+		/* || au_test_sysv(sb) */	/* untested */+		/* || au_test_ubifs(sb) */	/* untested */+		/* || au_test_minix(sb) */	/* untested */+		;+}++/*+ * filesystems which don't store the correct value in some of their inode+ * attributes.+ */+static inline int au_test_fs_bad_iattr(struct super_block *sb)+{+	return au_test_fs_bad_iattr_size(sb)+		/* || au_test_cifs(sb) */	/* untested */+		|| au_test_fat(sb)+		|| au_test_msdos(sb)+		|| au_test_vfat(sb);+}++/* they don't check i_nlink in link(2) */+static inline int au_test_fs_no_limit_nlink(struct super_block *sb)+{+	return au_test_tmpfs(sb)+#ifdef CONFIG_AUFS_BR_RAMFS+		|| au_test_ramfs(sb)+#endif+		|| au_test_ubifs(sb);+}++/*+ * filesystems which sets S_NOATIME and S_NOCMTIME.+ */+static inline int au_test_fs_notime(struct super_block *sb)+{+	return au_test_nfs(sb)+		|| au_test_fuse(sb)+		|| au_test_ubifs(sb)+		/* || au_test_cifs(sb) */	/* untested */+		;+}++/*+ * filesystems which requires replacing i_mapping.+ */+static inline int au_test_fs_bad_mapping(struct super_block *sb)+{+	return au_test_fuse(sb)+		|| au_test_ubifs(sb);+}++/* temporary support for i#1 in cramfs */+static inline int au_test_fs_unique_ino(struct inode *inode)+{+	if (au_test_cramfs(inode->i_sb))+		return inode->i_ino != 1;+	return 1;+}++/* ---------------------------------------------------------------------- */++/*+ * the filesystem where the xino files placed must support i/o after unlink and+ * maintain i_size and i_blocks.+ */+static inline int au_test_fs_bad_xino(struct super_block *sb)+{+	return au_test_fs_remote(sb)+		|| au_test_fs_bad_iattr_size(sb)+#ifdef CONFIG_AUFS_BR_RAMFS+		|| !(au_test_ramfs(sb) || au_test_fs_null_nd(sb))+#else+		|| !au_test_fs_null_nd(sb) /* to keep xino code simple */+#endif+		/* don't want unnecessary work for xino */+		|| au_test_aufs(sb)+		|| au_test_ecryptfs(sb);+}++static inline int au_test_fs_trunc_xino(struct super_block *sb)+{+	return au_test_tmpfs(sb)+		|| au_test_ramfs(sb);+}++/*+ * test if the @sb is real-readonly.+ */+static inline int au_test_fs_rr(struct super_block *sb)+{+	return au_test_squashfs(sb)+		|| au_test_iso9660(sb)+		|| au_test_cramfs(sb)+		|| au_test_romfs(sb);+}++#endif /* __KERNEL__ */+#endif /* __AUFS_FSTYPE_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/hinotify.c linux-2.6.31.5/fs/aufs/hinotify.c--- linux-2.6.31.5.orig/fs/aufs/hinotify.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/hinotify.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,755 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * inotify for the lower directories+ */++#include "aufs.h"++static const __u32 AuHinMask = (IN_MOVE | IN_DELETE | IN_CREATE);+static struct inotify_handle *au_hin_handle;++AuCacheFuncs(hinotify, HINOTIFY);++int au_hin_alloc(struct au_hinode *hinode, struct inode *inode,+		 struct inode *h_inode)+{+	int err;+	struct au_hinotify *hin;+	s32 wd;++	err = -ENOMEM;+	hin = au_cache_alloc_hinotify();+	if (hin) {+		AuDebugOn(hinode->hi_notify);+		hinode->hi_notify = hin;+		hin->hin_aufs_inode = inode;++		inotify_init_watch(&hin->hin_watch);+		wd = inotify_add_watch(au_hin_handle, &hin->hin_watch, h_inode,+				       AuHinMask);+		if (wd >= 0)+			return 0; /* success */++		err = wd;+		put_inotify_watch(&hin->hin_watch);+		au_cache_free_hinotify(hin);+		hinode->hi_notify = NULL;+	}++	return err;+}++void au_hin_free(struct au_hinode *hinode)+{+	int err;+	struct au_hinotify *hin;++	hin = hinode->hi_notify;+	if (hin) {+		err = 0;+		if (atomic_read(&hin->hin_watch.count))+			err = inotify_rm_watch(au_hin_handle, &hin->hin_watch);+		if (unlikely(err))+			/* it means the watch is already removed */+			AuWarn("failed inotify_rm_watch() %d\n", err);+		au_cache_free_hinotify(hin);+		hinode->hi_notify = NULL;+	}+}++/* ---------------------------------------------------------------------- */++void au_hin_ctl(struct au_hinode *hinode, int do_set)+{+	struct inode *h_inode;+	struct inotify_watch *watch;++	if (!hinode->hi_notify)+		return;++	h_inode = hinode->hi_inode;+	IMustLock(h_inode);++	/* todo: try inotify_find_update_watch()? */+	watch = &hinode->hi_notify->hin_watch;+	mutex_lock(&h_inode->inotify_mutex);+	/* mutex_lock(&watch->ih->mutex); */+	if (do_set) {+		AuDebugOn(watch->mask & AuHinMask);+		watch->mask |= AuHinMask;+	} else {+		AuDebugOn(!(watch->mask & AuHinMask));+		watch->mask &= ~AuHinMask;+	}+	/* mutex_unlock(&watch->ih->mutex); */+	mutex_unlock(&h_inode->inotify_mutex);+}++void au_reset_hinotify(struct inode *inode, unsigned int flags)+{+	aufs_bindex_t bindex, bend;+	struct inode *hi;+	struct dentry *iwhdentry;++	bend = au_ibend(inode);+	for (bindex = au_ibstart(inode); bindex <= bend; bindex++) {+		hi = au_h_iptr(inode, bindex);+		if (!hi)+			continue;++		/* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */+		iwhdentry = au_hi_wh(inode, bindex);+		if (iwhdentry)+			dget(iwhdentry);+		au_igrab(hi);+		au_set_h_iptr(inode, bindex, NULL, 0);+		au_set_h_iptr(inode, bindex, au_igrab(hi),+			      flags & ~AuHi_XINO);+		iput(hi);+		dput(iwhdentry);+		/* mutex_unlock(&hi->i_mutex); */+	}+}++/* ---------------------------------------------------------------------- */++static int hin_xino(struct inode *inode, struct inode *h_inode)+{+	int err;+	aufs_bindex_t bindex, bend, bfound, bstart;+	struct inode *h_i;++	err = 0;+	if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {+		AuWarn("branch root dir was changed\n");+		goto out;+	}++	bfound = -1;+	bend = au_ibend(inode);+	bstart = au_ibstart(inode);+#if 0 /* reserved for future use */+	if (bindex == bend) {+		/* keep this ino in rename case */+		goto out;+	}+#endif+	for (bindex = bstart; bindex <= bend; bindex++) {+		if (au_h_iptr(inode, bindex) == h_inode) {+			bfound = bindex;+			break;+		}+	}+	if (bfound < 0)+		goto out;++	for (bindex = bstart; bindex <= bend; bindex++) {+		h_i = au_h_iptr(inode, bindex);+		if (!h_i)+			continue;++		err = au_xino_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0);+		/* ignore this error */+		/* bad action? */+	}++	/* children inode number will be broken */++ out:+	AuTraceErr(err);+	return err;+}++static int hin_gen_tree(struct dentry *dentry)+{+	int err, i, j, ndentry;+	struct au_dcsub_pages dpages;+	struct au_dpage *dpage;+	struct dentry **dentries;++	err = au_dpages_init(&dpages, GFP_NOFS);+	if (unlikely(err))+		goto out;+	err = au_dcsub_pages(&dpages, dentry, NULL, NULL);+	if (unlikely(err))+		goto out_dpages;++	for (i = 0; i < dpages.ndpage; i++) {+		dpage = dpages.dpages + i;+		dentries = dpage->dentries;+		ndentry = dpage->ndentry;+		for (j = 0; j < ndentry; j++) {+			struct dentry *d;++			d = dentries[j];+			if (IS_ROOT(d))+				continue;++			d_drop(d);+			au_digen_dec(d);+			if (d->d_inode)+				/* todo: reset children xino?+				   cached children only? */+				au_iigen_dec(d->d_inode);+		}+	}++ out_dpages:+	au_dpages_free(&dpages);++	/* discard children */+	dentry_unhash(dentry);+	dput(dentry);+ out:+	return err;+}++/*+ * return 0 if processed.+ */+static int hin_gen_by_inode(char *name, unsigned int nlen, struct inode *inode,+			    const unsigned int isdir)+{+	int err;+	struct dentry *d;+	struct qstr *dname;++	err = 1;+	if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {+		AuWarn("branch root dir was changed\n");+		err = 0;+		goto out;+	}++	if (!isdir) {+		AuDebugOn(!name);+		au_iigen_dec(inode);+		spin_lock(&dcache_lock);+		list_for_each_entry(d, &inode->i_dentry, d_alias) {+			dname = &d->d_name;+			if (dname->len != nlen+			    && memcmp(dname->name, name, nlen))+				continue;+			err = 0;+			spin_lock(&d->d_lock);+			__d_drop(d);+			au_digen_dec(d);+			spin_unlock(&d->d_lock);+			break;+		}+		spin_unlock(&dcache_lock);+	} else {+		au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIRS);+		d = d_find_alias(inode);+		if (!d) {+			au_iigen_dec(inode);+			goto out;+		}++		dname = &d->d_name;+		if (dname->len == nlen && !memcmp(dname->name, name, nlen))+			err = hin_gen_tree(d);+		dput(d);+	}++ out:+	AuTraceErr(err);+	return err;+}++static int hin_gen_by_name(struct dentry *dentry, const unsigned int isdir)+{+	int err;+	struct inode *inode;++	inode = dentry->d_inode;+	if (IS_ROOT(dentry)+	    /* || (inode && inode->i_ino == AUFS_ROOT_INO) */+		) {+		AuWarn("branch root dir was changed\n");+		return 0;+	}++	err = 0;+	if (!isdir) {+		d_drop(dentry);+		au_digen_dec(dentry);+		if (inode)+			au_iigen_dec(inode);+	} else {+		au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS);+		if (inode)+			err = hin_gen_tree(dentry);+	}++	AuTraceErr(err);+	return err;+}++/* ---------------------------------------------------------------------- */++/* hinotify job flags */+#define AuHinJob_XINO0		1+#define AuHinJob_GEN		(1 << 1)+#define AuHinJob_DIRENT		(1 << 2)+#define AuHinJob_ISDIR		(1 << 3)+#define AuHinJob_TRYXINO0	(1 << 4)+#define AuHinJob_MNTPNT		(1 << 5)+#define au_ftest_hinjob(flags, name)	((flags) & AuHinJob_##name)+#define au_fset_hinjob(flags, name)	{ (flags) |= AuHinJob_##name; }+#define au_fclr_hinjob(flags, name)	{ (flags) &= ~AuHinJob_##name; }++struct hin_job_args {+	unsigned int flags;+	struct inode *inode, *h_inode, *dir, *h_dir;+	struct dentry *dentry;+	char *h_name;+	int h_nlen;+};++static int hin_job(struct hin_job_args *a)+{+	const unsigned int isdir = au_ftest_hinjob(a->flags, ISDIR);++	/* reset xino */+	if (au_ftest_hinjob(a->flags, XINO0) && a->inode)+		hin_xino(a->inode, a->h_inode); /* ignore this error */++	if (au_ftest_hinjob(a->flags, TRYXINO0)+	    && a->inode+	    && a->h_inode) {+		mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);+		if (!a->h_inode->i_nlink)+			hin_xino(a->inode, a->h_inode); /* ignore this error */+		mutex_unlock(&a->h_inode->i_mutex);+	}++	/* make the generation obsolete */+	if (au_ftest_hinjob(a->flags, GEN)) {+		int err = -1;+		if (a->inode)+			err = hin_gen_by_inode(a->h_name, a->h_nlen, a->inode,+					       isdir);+		if (err && a->dentry)+			hin_gen_by_name(a->dentry, isdir);+		/* ignore this error */+	}++	/* make dir entries obsolete */+	if (au_ftest_hinjob(a->flags, DIRENT) && a->inode) {+		struct au_vdir *vdir;++		vdir = au_ivdir(a->inode);+		if (vdir)+			vdir->vd_jiffy = 0;+		/* IMustLock(a->inode); */+		/* a->inode->i_version++; */+	}++	/* can do nothing but warn */+	if (au_ftest_hinjob(a->flags, MNTPNT)+	    && a->dentry+	    && d_mountpoint(a->dentry))+		AuWarn("mount-point %.*s is removed or renamed\n",+		       AuDLNPair(a->dentry));++	return 0;+}++/* ---------------------------------------------------------------------- */++static char *in_name(u32 mask)+{+#ifdef CONFIG_AUFS_DEBUG+#define test_ret(flag)	if (mask & flag) \+				return #flag;+	test_ret(IN_ACCESS);+	test_ret(IN_MODIFY);+	test_ret(IN_ATTRIB);+	test_ret(IN_CLOSE_WRITE);+	test_ret(IN_CLOSE_NOWRITE);+	test_ret(IN_OPEN);+	test_ret(IN_MOVED_FROM);+	test_ret(IN_MOVED_TO);+	test_ret(IN_CREATE);+	test_ret(IN_DELETE);+	test_ret(IN_DELETE_SELF);+	test_ret(IN_MOVE_SELF);+	test_ret(IN_UNMOUNT);+	test_ret(IN_Q_OVERFLOW);+	test_ret(IN_IGNORED);+	return "";+#undef test_ret+#else+	return "??";+#endif+}++static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen,+					   struct inode *dir)+{+	struct dentry *dentry, *d, *parent;+	struct qstr *dname;++	parent = d_find_alias(dir);+	if (!parent)+		return NULL;++	dentry = NULL;+	spin_lock(&dcache_lock);+	list_for_each_entry(d, &parent->d_subdirs, d_u.d_child) {+		/* AuDbg("%.*s\n", AuDLNPair(d)); */+		dname = &d->d_name;+		if (dname->len != nlen || memcmp(dname->name, name, nlen))+			continue;+		if (!atomic_read(&d->d_count) || !d->d_fsdata) {+			spin_lock(&d->d_lock);+			__d_drop(d);+			spin_unlock(&d->d_lock);+			continue;+		}++		dentry = dget(d);+		break;+	}+	spin_unlock(&dcache_lock);+	dput(parent);++	if (dentry)+		di_write_lock_child(dentry);++	return dentry;+}++static struct inode *lookup_wlock_by_ino(struct super_block *sb,+					 aufs_bindex_t bindex, ino_t h_ino)+{+	struct inode *inode;+	ino_t ino;+	int err;++	inode = NULL;+	err = au_xino_read(sb, bindex, h_ino, &ino);+	if (!err && ino)+		inode = ilookup(sb, ino);+	if (!inode)+		goto out;++	if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {+		AuWarn("wrong root branch\n");+		iput(inode);+		inode = NULL;+		goto out;+	}++	ii_write_lock_child(inode);++ out:+	return inode;+}++enum { CHILD, PARENT };+struct postproc_args {+	struct inode *h_dir, *dir, *h_child_inode;+	u32 mask;+	unsigned int flags[2];+	unsigned int h_child_nlen;+	char h_child_name[];+};++static void postproc(void *_args)+{+	struct postproc_args *a = _args;+	struct super_block *sb;+	aufs_bindex_t bindex, bend, bfound;+	unsigned char xino, try_iput;+	int err;+	struct inode *inode;+	ino_t h_ino;+	struct hin_job_args args;+	struct dentry *dentry;+	struct au_sbinfo *sbinfo;++	AuDebugOn(!_args);+	AuDebugOn(!a->h_dir);+	AuDebugOn(!a->dir);+	AuDebugOn(!a->mask);+	AuDbg("mask 0x%x %s, i%lu, hi%lu, hci%lu\n",+	      a->mask, in_name(a->mask), a->dir->i_ino, a->h_dir->i_ino,+	      a->h_child_inode ? a->h_child_inode->i_ino : 0);++	inode = NULL;+	dentry = NULL;+	/*+	 * do not lock a->dir->i_mutex here+	 * because of d_revalidate() may cause a deadlock.+	 */+	sb = a->dir->i_sb;+	AuDebugOn(!sb);+	sbinfo = au_sbi(sb);+	AuDebugOn(!sbinfo);+	/* big aufs lock */+	si_noflush_write_lock(sb);++	ii_read_lock_parent(a->dir);+	bfound = -1;+	bend = au_ibend(a->dir);+	for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++)+		if (au_h_iptr(a->dir, bindex) == a->h_dir) {+			bfound = bindex;+			break;+		}+	ii_read_unlock(a->dir);+	if (unlikely(bfound < 0))+		goto out;++	xino = !!au_opt_test(au_mntflags(sb), XINO);+	h_ino = 0;+	if (a->h_child_inode)+		h_ino = a->h_child_inode->i_ino;++	if (a->h_child_nlen+	    && (au_ftest_hinjob(a->flags[CHILD], GEN)+		|| au_ftest_hinjob(a->flags[CHILD], MNTPNT)))+		dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen,+					      a->dir);+	try_iput = 0;+	if (dentry)+		inode = dentry->d_inode;+	if (xino && !inode && h_ino+	    && (au_ftest_hinjob(a->flags[CHILD], XINO0)+		|| au_ftest_hinjob(a->flags[CHILD], TRYXINO0)+		|| au_ftest_hinjob(a->flags[CHILD], GEN))) {+		inode = lookup_wlock_by_ino(sb, bfound, h_ino);+		try_iput = 1;+	    }++	args.flags = a->flags[CHILD];+	args.dentry = dentry;+	args.inode = inode;+	args.h_inode = a->h_child_inode;+	args.dir = a->dir;+	args.h_dir = a->h_dir;+	args.h_name = a->h_child_name;+	args.h_nlen = a->h_child_nlen;+	err = hin_job(&args);+	if (dentry) {+		if (dentry->d_fsdata)+			di_write_unlock(dentry);+		dput(dentry);+	}+	if (inode && try_iput) {+		ii_write_unlock(inode);+		iput(inode);+	}++	ii_write_lock_parent(a->dir);+	args.flags = a->flags[PARENT];+	args.dentry = NULL;+	args.inode = a->dir;+	args.h_inode = a->h_dir;+	args.dir = NULL;+	args.h_dir = NULL;+	args.h_name = NULL;+	args.h_nlen = 0;+	err = hin_job(&args);+	ii_write_unlock(a->dir);++ out:+	au_nwt_done(&sbinfo->si_nowait);+	si_write_unlock(sb);++	iput(a->h_child_inode);+	iput(a->h_dir);+	iput(a->dir);+	kfree(a);+}++/* ---------------------------------------------------------------------- */++static void aufs_inotify(struct inotify_watch *watch, u32 wd __maybe_unused,+			 u32 mask, u32 cookie __maybe_unused,+			 const char *h_child_name, struct inode *h_child_inode)+{+	struct au_hinotify *hinotify;+	struct postproc_args *args;+	int len, wkq_err;+	unsigned char isdir, isroot, wh;+	char *p;+	struct inode *dir;+	unsigned int flags[2];++	/* if IN_UNMOUNT happens, there must be another bug */+	AuDebugOn(mask & IN_UNMOUNT);+	if (mask & (IN_IGNORED | IN_UNMOUNT)) {+		put_inotify_watch(watch);+		return;+	}+#ifdef AuDbgHinotify+	au_debug(1);+	if (1 || !h_child_name || strcmp(h_child_name, AUFS_XINO_FNAME)) {+		AuDbg("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s,"+		      " hi%lu\n",+		      watch->inode->i_ino, wd, mask, in_name(mask), cookie,+		      h_child_name ? h_child_name : "",+		      h_child_inode ? h_child_inode->i_ino : 0);+		WARN_ON(1);+	}+	au_debug(0);+#endif++	hinotify = container_of(watch, struct au_hinotify, hin_watch);+	AuDebugOn(!hinotify || !hinotify->hin_aufs_inode);+	dir = igrab(hinotify->hin_aufs_inode);+	if (!dir)+		return;++	isroot = (dir->i_ino == AUFS_ROOT_INO);+	len = 0;+	wh = 0;+	if (h_child_name) {+		len = strlen(h_child_name);+		if (!memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {+			h_child_name += AUFS_WH_PFX_LEN;+			len -= AUFS_WH_PFX_LEN;+			wh = 1;+		}+	}++	isdir = 0;+	if (h_child_inode)+		isdir = !!S_ISDIR(h_child_inode->i_mode);+	flags[PARENT] = AuHinJob_ISDIR;+	flags[CHILD] = 0;+	if (isdir)+		flags[CHILD] = AuHinJob_ISDIR;+	switch (mask & IN_ALL_EVENTS) {+	case IN_MOVED_FROM:+	case IN_MOVED_TO:+		AuDebugOn(!h_child_name || !h_child_inode);+		au_fset_hinjob(flags[CHILD], GEN);+		au_fset_hinjob(flags[CHILD], XINO0);+		au_fset_hinjob(flags[CHILD], MNTPNT);+		au_fset_hinjob(flags[PARENT], DIRENT);+		break;++	case IN_CREATE:+		AuDebugOn(!h_child_name || !h_child_inode);+		au_fset_hinjob(flags[PARENT], DIRENT);+		au_fset_hinjob(flags[CHILD], GEN);+		break;++	case IN_DELETE:+		/*+		 * aufs never be able to get this child inode.+		 * revalidation should be in d_revalidate()+		 * by checking i_nlink, i_generation or d_unhashed().+		 */+		AuDebugOn(!h_child_name);+		au_fset_hinjob(flags[PARENT], DIRENT);+		au_fset_hinjob(flags[CHILD], GEN);+		au_fset_hinjob(flags[CHILD], TRYXINO0);+		au_fset_hinjob(flags[CHILD], MNTPNT);+		break;++	default:+		AuDebugOn(1);+	}++	if (wh)+		h_child_inode = NULL;++	/* iput() and kfree() will be called in postproc() */+	/*+	 * inotify_mutex is already acquired and kmalloc/prune_icache may lock+	 * iprune_mutex. strange.+	 */+	lockdep_off();+	args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS);+	lockdep_on();+	if (unlikely(!args)) {+		AuErr1("no memory\n");+		iput(dir);+		return;+	}+	args->flags[PARENT] = flags[PARENT];+	args->flags[CHILD] = flags[CHILD];+	args->mask = mask;+	args->dir = dir;+	args->h_dir = igrab(watch->inode);+	if (h_child_inode)+		h_child_inode = igrab(h_child_inode); /* can be NULL */+	args->h_child_inode = h_child_inode;+	args->h_child_nlen = len;+	if (len) {+		p = (void *)args;+		p += sizeof(*args);+		memcpy(p, h_child_name, len + 1);+	}++	lockdep_off();+	wkq_err = au_wkq_nowait(postproc, args, dir->i_sb);+	lockdep_on();+	if (unlikely(wkq_err))+		AuErr("wkq %d\n", wkq_err);+}++static void aufs_inotify_destroy(struct inotify_watch *watch __maybe_unused)+{+	return;+}++static struct inotify_operations aufs_inotify_ops = {+	.handle_event	= aufs_inotify,+	.destroy_watch	= aufs_inotify_destroy+};++/* ---------------------------------------------------------------------- */++static void au_hin_destroy_cache(void)+{+	kmem_cache_destroy(au_cachep[AuCache_HINOTIFY]);+	au_cachep[AuCache_HINOTIFY] = NULL;+}++int __init au_hinotify_init(void)+{+	int err;++	err = -ENOMEM;+	au_cachep[AuCache_HINOTIFY] = AuCache(au_hinotify);+	if (au_cachep[AuCache_HINOTIFY]) {+		err = 0;+		au_hin_handle = inotify_init(&aufs_inotify_ops);+		if (IS_ERR(au_hin_handle)) {+			err = PTR_ERR(au_hin_handle);+			au_hin_destroy_cache();+		}+	}+	AuTraceErr(err);+	return err;+}++void au_hinotify_fin(void)+{+	inotify_destroy(au_hin_handle);+	if (au_cachep[AuCache_HINOTIFY])+		au_hin_destroy_cache();+}diff -Nur linux-2.6.31.5.orig/fs/aufs/iinfo.c linux-2.6.31.5/fs/aufs/iinfo.c--- linux-2.6.31.5.orig/fs/aufs/iinfo.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/iinfo.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,283 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * inode private data+ */++#include "aufs.h"++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex)+{+	struct inode *h_inode;++	IiMustAnyLock(inode);++	h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode;+	AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0);+	return h_inode;+}++/* todo: hard/soft set? */+void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex)+{+	struct au_iinfo *iinfo = au_ii(inode);+	struct inode *h_inode;++	IiMustWriteLock(inode);++	iinfo->ii_bstart = bindex;+	h_inode = iinfo->ii_hinode[bindex + 0].hi_inode;+	if (h_inode)+		au_cpup_igen(inode, h_inode);+}++void au_hiput(struct au_hinode *hinode)+{+	au_hin_free(hinode);+	dput(hinode->hi_whdentry);+	iput(hinode->hi_inode);+}++unsigned int au_hi_flags(struct inode *inode, int isdir)+{+	unsigned int flags;+	const unsigned int mnt_flags = au_mntflags(inode->i_sb);++	flags = 0;+	if (au_opt_test(mnt_flags, XINO))+		au_fset_hi(flags, XINO);+	if (isdir && au_opt_test(mnt_flags, UDBA_HINOTIFY))+		au_fset_hi(flags, HINOTIFY);+	return flags;+}++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,+		   struct inode *h_inode, unsigned int flags)+{+	struct au_hinode *hinode;+	struct inode *hi;+	struct au_iinfo *iinfo = au_ii(inode);++	IiMustWriteLock(inode);++	hinode = iinfo->ii_hinode + bindex;+	hi = hinode->hi_inode;+	AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0);+	AuDebugOn(h_inode && hi);++	if (hi)+		au_hiput(hinode);+	hinode->hi_inode = h_inode;+	if (h_inode) {+		int err;+		struct super_block *sb = inode->i_sb;+		struct au_branch *br;++		if (bindex == iinfo->ii_bstart)+			au_cpup_igen(inode, h_inode);+		br = au_sbr(sb, bindex);+		hinode->hi_id = br->br_id;+		if (au_ftest_hi(flags, XINO)) {+			err = au_xino_write(sb, bindex, h_inode->i_ino,+					    inode->i_ino);+			if (unlikely(err))+				AuIOErr1("failed au_xino_write() %d\n", err);+		}++		if (au_ftest_hi(flags, HINOTIFY)+		    && au_br_hinotifyable(br->br_perm)) {+			err = au_hin_alloc(hinode, inode, h_inode);+			if (unlikely(err))+				AuIOErr1("au_hin_alloc() %d\n", err);+		}+	}+}++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,+		  struct dentry *h_wh)+{+	struct au_hinode *hinode;++	IiMustWriteLock(inode);++	hinode = au_ii(inode)->ii_hinode + bindex;+	AuDebugOn(hinode->hi_whdentry);+	hinode->hi_whdentry = h_wh;+}++void au_update_iigen(struct inode *inode)+{+	atomic_set(&au_ii(inode)->ii_generation, au_sigen(inode->i_sb));+	/* smp_mb(); */ /* atomic_set */+}++/* it may be called at remount time, too */+void au_update_brange(struct inode *inode, int do_put_zero)+{+	struct au_iinfo *iinfo;++	iinfo = au_ii(inode);+	if (!iinfo || iinfo->ii_bstart < 0)+		return;++	IiMustWriteLock(inode);++	if (do_put_zero) {+		aufs_bindex_t bindex;++		for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;+		     bindex++) {+			struct inode *h_i;++			h_i = iinfo->ii_hinode[0 + bindex].hi_inode;+			if (h_i && !h_i->i_nlink)+				au_set_h_iptr(inode, bindex, NULL, 0);+		}+	}++	iinfo->ii_bstart = -1;+	while (++iinfo->ii_bstart <= iinfo->ii_bend)+		if (iinfo->ii_hinode[0 + iinfo->ii_bstart].hi_inode)+			break;+	if (iinfo->ii_bstart > iinfo->ii_bend) {+		iinfo->ii_bstart = -1;+		iinfo->ii_bend = -1;+		return;+	}++	iinfo->ii_bend++;+	while (0 <= --iinfo->ii_bend)+		if (iinfo->ii_hinode[0 + iinfo->ii_bend].hi_inode)+			break;+	AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend || iinfo->ii_bend < 0);+}++/* ---------------------------------------------------------------------- */++int au_iinfo_init(struct inode *inode)+{+	struct au_iinfo *iinfo;+	struct super_block *sb;+	int nbr, i;++	sb = inode->i_sb;+	iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo);+	nbr = au_sbend(sb) + 1;+	if (unlikely(nbr <= 0))+		nbr = 1;+	iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS);+	if (iinfo->ii_hinode) {+		for (i = 0; i < nbr; i++)+			iinfo->ii_hinode[i].hi_id = -1;++		atomic_set(&iinfo->ii_generation, au_sigen(sb));+		/* smp_mb(); */ /* atomic_set */+		au_rw_init(&iinfo->ii_rwsem);+		iinfo->ii_bstart = -1;+		iinfo->ii_bend = -1;+		iinfo->ii_vdir = NULL;+		return 0;+	}+	return -ENOMEM;+}++int au_ii_realloc(struct au_iinfo *iinfo, int nbr)+{+	int err, sz;+	struct au_hinode *hip;++	AuRwMustWriteLock(&iinfo->ii_rwsem);++	err = -ENOMEM;+	sz = sizeof(*hip) * (iinfo->ii_bend + 1);+	if (!sz)+		sz = sizeof(*hip);+	hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS);+	if (hip) {+		iinfo->ii_hinode = hip;+		err = 0;+	}++	return err;+}++static int au_iinfo_write0(struct super_block *sb, struct au_hinode *hinode,+			   ino_t ino)+{+	int err;+	aufs_bindex_t bindex;+	unsigned char locked;++	err = 0;+	locked = !!si_noflush_read_trylock(sb);+	bindex = au_br_index(sb, hinode->hi_id);+	if (bindex >= 0)+		err = au_xino_write0(sb, bindex, hinode->hi_inode->i_ino, ino);+	/* error action? */+	if (locked)+		si_read_unlock(sb);+	return err;+}++void au_iinfo_fin(struct inode *inode)+{+	ino_t ino;+	aufs_bindex_t bend;+	unsigned char unlinked = !inode->i_nlink;+	struct au_iinfo *iinfo;+	struct au_hinode *hi;+	struct super_block *sb;++	if (unlinked) {+		int err = au_xigen_inc(inode);+		if (unlikely(err))+			AuWarn1("failed resetting i_generation, %d\n", err);+	}++	iinfo = au_ii(inode);+	/* bad_inode case */+	if (!iinfo)+		return;++	if (iinfo->ii_vdir)+		au_vdir_free(iinfo->ii_vdir);++	if (iinfo->ii_bstart >= 0) {+		sb = inode->i_sb;+		ino = 0;+		if (unlinked)+			ino = inode->i_ino;+		hi = iinfo->ii_hinode + iinfo->ii_bstart;+		bend = iinfo->ii_bend;+		while (iinfo->ii_bstart++ <= bend) {+			if (hi->hi_inode) {+				if (unlinked || !hi->hi_inode->i_nlink) {+					au_iinfo_write0(sb, hi, ino);+					/* ignore this error */+					ino = 0;+				}+				au_hiput(hi);+			}+			hi++;+		}+	}++	kfree(iinfo->ii_hinode);+	AuRwDestroy(&iinfo->ii_rwsem);+}diff -Nur linux-2.6.31.5.orig/fs/aufs/inode.c linux-2.6.31.5/fs/aufs/inode.c--- linux-2.6.31.5.orig/fs/aufs/inode.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/inode.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,380 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * inode functions+ */++#include "aufs.h"++struct inode *au_igrab(struct inode *inode)+{+	if (inode) {+		AuDebugOn(!atomic_read(&inode->i_count));+		atomic_inc_return(&inode->i_count);+	}+	return inode;+}++static void au_refresh_hinode_attr(struct inode *inode, int do_version)+{+	au_cpup_attr_all(inode, /*force*/0);+	au_update_iigen(inode);+	if (do_version)+		inode->i_version++;+}++int au_refresh_hinode_self(struct inode *inode, int do_attr)+{+	int err;+	aufs_bindex_t bindex, new_bindex;+	unsigned char update;+	struct inode *first;+	struct au_hinode *p, *q, tmp;+	struct super_block *sb;+	struct au_iinfo *iinfo;++	IiMustWriteLock(inode);++	update = 0;+	sb = inode->i_sb;+	iinfo = au_ii(inode);+	err = au_ii_realloc(iinfo, au_sbend(sb) + 1);+	if (unlikely(err))+		goto out;++	p = iinfo->ii_hinode + iinfo->ii_bstart;+	first = p->hi_inode;+	err = 0;+	for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;+	     bindex++, p++) {+		if (!p->hi_inode)+			continue;++		new_bindex = au_br_index(sb, p->hi_id);+		if (new_bindex == bindex)+			continue;++		if (new_bindex < 0) {+			update++;+			au_hiput(p);+			p->hi_inode = NULL;+			continue;+		}++		if (new_bindex < iinfo->ii_bstart)+			iinfo->ii_bstart = new_bindex;+		if (iinfo->ii_bend < new_bindex)+			iinfo->ii_bend = new_bindex;+		/* swap two lower inode, and loop again */+		q = iinfo->ii_hinode + new_bindex;+		tmp = *q;+		*q = *p;+		*p = tmp;+		if (tmp.hi_inode) {+			bindex--;+			p--;+		}+	}+	au_update_brange(inode, /*do_put_zero*/0);+	if (do_attr)+		au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode));++ out:+	return err;+}++int au_refresh_hinode(struct inode *inode, struct dentry *dentry)+{+	int err, update;+	unsigned int flags;+	aufs_bindex_t bindex, bend;+	unsigned char isdir;+	struct inode *first;+	struct au_hinode *p;+	struct au_iinfo *iinfo;++	err = au_refresh_hinode_self(inode, /*do_attr*/0);+	if (unlikely(err))+		goto out;++	update = 0;+	iinfo = au_ii(inode);+	p = iinfo->ii_hinode + iinfo->ii_bstart;+	first = p->hi_inode;+	isdir = S_ISDIR(inode->i_mode);+	flags = au_hi_flags(inode, isdir);+	bend = au_dbend(dentry);+	for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) {+		struct inode *h_i;+		struct dentry *h_d;++		h_d = au_h_dptr(dentry, bindex);+		if (!h_d || !h_d->d_inode)+			continue;++		if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) {+			h_i = au_h_iptr(inode, bindex);+			if (h_i) {+				if (h_i == h_d->d_inode)+					continue;+				err = -EIO;+				break;+			}+		}+		if (bindex < iinfo->ii_bstart)+			iinfo->ii_bstart = bindex;+		if (iinfo->ii_bend < bindex)+			iinfo->ii_bend = bindex;+		au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags);+		update = 1;+	}+	au_update_brange(inode, /*do_put_zero*/0);++	if (unlikely(err))+		goto out;++	au_refresh_hinode_attr(inode, update && isdir);++ out:+	return err;+}++static int set_inode(struct inode *inode, struct dentry *dentry)+{+	int err;+	unsigned int flags;+	umode_t mode;+	aufs_bindex_t bindex, bstart, btail;+	unsigned char isdir;+	struct dentry *h_dentry;+	struct inode *h_inode;+	struct au_iinfo *iinfo;++	IiMustWriteLock(inode);++	err = 0;+	isdir = 0;+	bstart = au_dbstart(dentry);+	h_inode = au_h_dptr(dentry, bstart)->d_inode;+	mode = h_inode->i_mode;+	switch (mode & S_IFMT) {+	case S_IFREG:+		btail = au_dbtail(dentry);+		inode->i_op = &aufs_iop;+		inode->i_fop = &aufs_file_fop;+		inode->i_mapping->a_ops = &aufs_aop;+		break;+	case S_IFDIR:+		isdir = 1;+		btail = au_dbtaildir(dentry);+		inode->i_op = &aufs_dir_iop;+		inode->i_fop = &aufs_dir_fop;+		break;+	case S_IFLNK:+		btail = au_dbtail(dentry);+		inode->i_op = &aufs_symlink_iop;+		break;+	case S_IFBLK:+	case S_IFCHR:+	case S_IFIFO:+	case S_IFSOCK:+		btail = au_dbtail(dentry);+		inode->i_op = &aufs_iop;+		init_special_inode(inode, mode, h_inode->i_rdev);+		break;+	default:+		AuIOErr("Unknown file type 0%o\n", mode);+		err = -EIO;+		goto out;+	}++	/* do not set inotify for whiteouted dirs (SHWH mode) */+	flags = au_hi_flags(inode, isdir);+	if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)+	    && au_ftest_hi(flags, HINOTIFY)+	    && dentry->d_name.len > AUFS_WH_PFX_LEN+	    && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))+		au_fclr_hi(flags, HINOTIFY);+	iinfo = au_ii(inode);+	iinfo->ii_bstart = bstart;+	iinfo->ii_bend = btail;+	for (bindex = bstart; bindex <= btail; bindex++) {+		h_dentry = au_h_dptr(dentry, bindex);+		if (h_dentry)+			au_set_h_iptr(inode, bindex,+				      au_igrab(h_dentry->d_inode), flags);+	}+	au_cpup_attr_all(inode, /*force*/1);++ out:+	return err;+}++/* successful returns with iinfo write_locked */+static int reval_inode(struct inode *inode, struct dentry *dentry, int *matched)+{+	int err;+	aufs_bindex_t bindex, bend;+	struct inode *h_inode, *h_dinode;++	*matched = 0;++	/*+	 * before this function, if aufs got any iinfo lock, it must be only+	 * one, the parent dir.+	 * it can happen by UDBA and the obsoleted inode number.+	 */+	err = -EIO;+	if (unlikely(inode->i_ino == parent_ino(dentry)))+		goto out;++	err = 0;+	ii_write_lock_new_child(inode);+	h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode;+	bend = au_ibend(inode);+	for (bindex = au_ibstart(inode); bindex <= bend; bindex++) {+		h_inode = au_h_iptr(inode, bindex);+		if (h_inode && h_inode == h_dinode) {+			*matched = 1;+			err = 0;+			if (au_iigen(inode) != au_digen(dentry))+				err = au_refresh_hinode(inode, dentry);+			break;+		}+	}++	if (unlikely(err))+		ii_write_unlock(inode);+ out:+	return err;+}++/* successful returns with iinfo write_locked */+/* todo: return with unlocked? */+struct inode *au_new_inode(struct dentry *dentry, int must_new)+{+	struct inode *inode;+	struct dentry *h_dentry;+	struct super_block *sb;+	ino_t h_ino, ino;+	int err, match;+	aufs_bindex_t bstart;++	sb = dentry->d_sb;+	bstart = au_dbstart(dentry);+	h_dentry = au_h_dptr(dentry, bstart);+	h_ino = h_dentry->d_inode->i_ino;+	err = au_xino_read(sb, bstart, h_ino, &ino);+	inode = ERR_PTR(err);+	if (unlikely(err))+		goto out;+ new_ino:+	if (!ino) {+		ino = au_xino_new_ino(sb);+		if (unlikely(!ino)) {+			inode = ERR_PTR(-EIO);+			goto out;+		}+	}++	AuDbg("i%lu\n", (unsigned long)ino);+	inode = au_iget_locked(sb, ino);+	err = PTR_ERR(inode);+	if (IS_ERR(inode))+		goto out;++	AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));+	if (inode->i_state & I_NEW) {+		ii_write_lock_new_child(inode);+		err = set_inode(inode, dentry);+		unlock_new_inode(inode);+		if (!err)+			goto out; /* success */++		iget_failed(inode);+		ii_write_unlock(inode);+		goto out_iput;+	} else if (!must_new) {+		err = reval_inode(inode, dentry, &match);+		if (!err)+			goto out; /* success */+		else if (match)+			goto out_iput;+	}++	if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode)))+		AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir,"+			" b%d, %s, %.*s, hi%lu, i%lu.\n",+			bstart, au_sbtype(h_dentry->d_sb), AuDLNPair(dentry),+			(unsigned long)h_ino, (unsigned long)ino);+	ino = 0;+	err = au_xino_write(sb, bstart, h_ino, /*ino*/0);+	if (!err) {+		iput(inode);+		goto new_ino;+	}++ out_iput:+	iput(inode);+	inode = ERR_PTR(err);+ out:+	return inode;+}++/* ---------------------------------------------------------------------- */++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,+	       struct inode *inode)+{+	int err;++	err = au_br_rdonly(au_sbr(sb, bindex));++	/* pseudo-link after flushed may happen out of bounds */+	if (!err+	    && inode+	    && au_ibstart(inode) <= bindex+	    && bindex <= au_ibend(inode)) {+		/*+		 * permission check is unnecessary since vfsub routine+		 * will be called later+		 */+		struct inode *hi = au_h_iptr(inode, bindex);+		if (hi)+			err = IS_IMMUTABLE(hi) ? -EROFS : 0;+	}++	return err;+}++int au_test_h_perm(struct inode *h_inode, int mask)+{+	if (!current_fsuid())+		return 0;+	return inode_permission(h_inode, mask);+}++int au_test_h_perm_sio(struct inode *h_inode, int mask)+{+	if (au_test_nfs(h_inode->i_sb)+	    && (mask & MAY_WRITE)+	    && S_ISDIR(h_inode->i_mode))+		mask |= MAY_READ; /* force permission check */+	return au_test_h_perm(h_inode, mask);+}diff -Nur linux-2.6.31.5.orig/fs/aufs/inode.h linux-2.6.31.5/fs/aufs/inode.h--- linux-2.6.31.5.orig/fs/aufs/inode.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/inode.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,484 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * inode operations+ */++#ifndef __AUFS_INODE_H__+#define __AUFS_INODE_H__++#ifdef __KERNEL__++#include <linux/fs.h>+#include <linux/inotify.h>+#include <linux/aufs_type.h>+#include "rwsem.h"++struct vfsmount;++struct au_hinotify {+#ifdef CONFIG_AUFS_HINOTIFY+	struct inotify_watch	hin_watch;+	struct inode		*hin_aufs_inode;	/* no get/put */+#endif+};++struct au_hinode {+	struct inode		*hi_inode;+	aufs_bindex_t		hi_id;+#ifdef CONFIG_AUFS_HINOTIFY+	struct au_hinotify	*hi_notify;+#endif++	/* reference to the copied-up whiteout with get/put */+	struct dentry		*hi_whdentry;+};++struct au_vdir;+struct au_iinfo {+	atomic_t		ii_generation;+	struct super_block	*ii_hsb1;	/* no get/put */++	struct au_rwsem		ii_rwsem;+	aufs_bindex_t		ii_bstart, ii_bend;+	__u32			ii_higen;+	struct au_hinode	*ii_hinode;+	struct au_vdir		*ii_vdir;+};++struct au_icntnr {+	struct au_iinfo iinfo;+	struct inode vfs_inode;+};++/* au_pin flags */+#define AuPin_DI_LOCKED		1+#define AuPin_MNT_WRITE		(1 << 1)+#define au_ftest_pin(flags, name)	((flags) & AuPin_##name)+#define au_fset_pin(flags, name)	{ (flags) |= AuPin_##name; }+#define au_fclr_pin(flags, name)	{ (flags) &= ~AuPin_##name; }++struct au_pin {+	/* input */+	struct dentry *dentry;+	unsigned int udba;+	unsigned char lsc_di, lsc_hi, flags;+	aufs_bindex_t bindex;++	/* output */+	struct dentry *parent;+	struct au_hinode *hdir;+	struct vfsmount *h_mnt;+};++/* ---------------------------------------------------------------------- */++static inline struct au_iinfo *au_ii(struct inode *inode)+{+	struct au_iinfo *iinfo;++	iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo);+	if (iinfo->ii_hinode)+		return iinfo;+	return NULL; /* debugging bad_inode case */+}++/* ---------------------------------------------------------------------- */++/* inode.c */+struct inode *au_igrab(struct inode *inode);+int au_refresh_hinode_self(struct inode *inode, int do_attr);+int au_refresh_hinode(struct inode *inode, struct dentry *dentry);+struct inode *au_new_inode(struct dentry *dentry, int must_new);+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,+	       struct inode *inode);+int au_test_h_perm(struct inode *h_inode, int mask);+int au_test_h_perm_sio(struct inode *h_inode, int mask);++/* i_op.c */+extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop;++/* au_wr_dir flags */+#define AuWrDir_ADD_ENTRY	1+#define AuWrDir_ISDIR		(1 << 1)+#define au_ftest_wrdir(flags, name)	((flags) & AuWrDir_##name)+#define au_fset_wrdir(flags, name)	{ (flags) |= AuWrDir_##name; }+#define au_fclr_wrdir(flags, name)	{ (flags) &= ~AuWrDir_##name; }++struct au_wr_dir_args {+	aufs_bindex_t force_btgt;+	unsigned char flags;+};+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,+	      struct au_wr_dir_args *args);++struct dentry *au_pinned_h_parent(struct au_pin *pin);+void au_pin_init(struct au_pin *pin, struct dentry *dentry,+		 aufs_bindex_t bindex, int lsc_di, int lsc_hi,+		 unsigned int udba, unsigned char flags);+int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,+	   unsigned int udba, unsigned char flags) __must_check;+int au_do_pin(struct au_pin *pin) __must_check;+void au_unpin(struct au_pin *pin);++/* i_op_add.c */+int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,+	       struct dentry *h_parent, int isdir);+int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname);+int aufs_create(struct inode *dir, struct dentry *dentry, int mode,+		struct nameidata *nd);+int aufs_link(struct dentry *src_dentry, struct inode *dir,+	      struct dentry *dentry);+int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode);++/* i_op_del.c */+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup);+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,+	       struct dentry *h_parent, int isdir);+int aufs_unlink(struct inode *dir, struct dentry *dentry);+int aufs_rmdir(struct inode *dir, struct dentry *dentry);++/* i_op_ren.c */+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt);+int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,+		struct inode *dir, struct dentry *dentry);++/* iinfo.c */+struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);+void au_hiput(struct au_hinode *hinode);+void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex);+void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,+		  struct dentry *h_wh);+unsigned int au_hi_flags(struct inode *inode, int isdir);++/* hinode flags */+#define AuHi_XINO	1+#define AuHi_HINOTIFY	(1 << 1)+#define au_ftest_hi(flags, name)	((flags) & AuHi_##name)+#define au_fset_hi(flags, name)		{ (flags) |= AuHi_##name; }+#define au_fclr_hi(flags, name)		{ (flags) &= ~AuHi_##name; }++#ifndef CONFIG_AUFS_HINOTIFY+#undef AuHi_HINOTIFY+#define AuHi_HINOTIFY	0+#endif++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,+		   struct inode *h_inode, unsigned int flags);++void au_update_iigen(struct inode *inode);+void au_update_brange(struct inode *inode, int do_put_zero);++int au_iinfo_init(struct inode *inode);+void au_iinfo_fin(struct inode *inode);+int au_ii_realloc(struct au_iinfo *iinfo, int nbr);++/* plink.c */+void au_plink_block_maintain(struct super_block *sb);+#ifdef CONFIG_AUFS_DEBUG+void au_plink_list(struct super_block *sb);+#else+static inline void au_plink_list(struct super_block *sb)+{+	/* nothing */+}+#endif+int au_plink_test(struct inode *inode);+struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex);+void au_plink_append(struct inode *inode, aufs_bindex_t bindex,+		     struct dentry *h_dentry);+void au_plink_put(struct super_block *sb);+void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id);++/* ---------------------------------------------------------------------- */++/* lock subclass for iinfo */+enum {+	AuLsc_II_CHILD,		/* child first */+	AuLsc_II_CHILD2,	/* rename(2), link(2), and cpup at hinotify */+	AuLsc_II_CHILD3,	/* copyup dirs */+	AuLsc_II_PARENT,	/* see AuLsc_I_PARENT in vfsub.h */+	AuLsc_II_PARENT2,+	AuLsc_II_PARENT3,	/* copyup dirs */+	AuLsc_II_NEW_CHILD+};++/*+ * ii_read_lock_child, ii_write_lock_child,+ * ii_read_lock_child2, ii_write_lock_child2,+ * ii_read_lock_child3, ii_write_lock_child3,+ * ii_read_lock_parent, ii_write_lock_parent,+ * ii_read_lock_parent2, ii_write_lock_parent2,+ * ii_read_lock_parent3, ii_write_lock_parent3,+ * ii_read_lock_new_child, ii_write_lock_new_child,+ */+#define AuReadLockFunc(name, lsc) \+static inline void ii_read_lock_##name(struct inode *i) \+{ \+	au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \+}++#define AuWriteLockFunc(name, lsc) \+static inline void ii_write_lock_##name(struct inode *i) \+{ \+	au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \+}++#define AuRWLockFuncs(name, lsc) \+	AuReadLockFunc(name, lsc) \+	AuWriteLockFunc(name, lsc)++AuRWLockFuncs(child, CHILD);+AuRWLockFuncs(child2, CHILD2);+AuRWLockFuncs(child3, CHILD3);+AuRWLockFuncs(parent, PARENT);+AuRWLockFuncs(parent2, PARENT2);+AuRWLockFuncs(parent3, PARENT3);+AuRWLockFuncs(new_child, NEW_CHILD);++#undef AuReadLockFunc+#undef AuWriteLockFunc+#undef AuRWLockFuncs++/*+ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock+ */+AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem);++#define IiMustNoWaiters(i)	AuRwMustNoWaiters(&au_ii(i)->ii_rwsem)+#define IiMustAnyLock(i)	AuRwMustAnyLock(&au_ii(i)->ii_rwsem)+#define IiMustWriteLock(i)	AuRwMustWriteLock(&au_ii(i)->ii_rwsem)++/* ---------------------------------------------------------------------- */++static inline unsigned int au_iigen(struct inode *inode)+{+	return atomic_read(&au_ii(inode)->ii_generation);+}++/* tiny test for inode number */+/* tmpfs generation is too rough */+static inline int au_test_higen(struct inode *inode, struct inode *h_inode)+{+	struct au_iinfo *iinfo;++	iinfo = au_ii(inode);+	AuRwMustAnyLock(&iinfo->ii_rwsem);+	return !(iinfo->ii_hsb1 == h_inode->i_sb+		 && iinfo->ii_higen == h_inode->i_generation);+}++/* ---------------------------------------------------------------------- */++static inline aufs_bindex_t au_ii_br_id(struct inode *inode,+					aufs_bindex_t bindex)+{+	IiMustAnyLock(inode);+	return au_ii(inode)->ii_hinode[0 + bindex].hi_id;+}++static inline aufs_bindex_t au_ibstart(struct inode *inode)+{+	IiMustAnyLock(inode);+	return au_ii(inode)->ii_bstart;+}++static inline aufs_bindex_t au_ibend(struct inode *inode)+{+	IiMustAnyLock(inode);+	return au_ii(inode)->ii_bend;+}++static inline struct au_vdir *au_ivdir(struct inode *inode)+{+	IiMustAnyLock(inode);+	return au_ii(inode)->ii_vdir;+}++static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex)+{+	IiMustAnyLock(inode);+	return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry;+}++static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex)+{+	IiMustWriteLock(inode);+	au_ii(inode)->ii_bend = bindex;+}++static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir)+{+	IiMustWriteLock(inode);+	au_ii(inode)->ii_vdir = vdir;+}++static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex)+{+	IiMustAnyLock(inode);+	return au_ii(inode)->ii_hinode + bindex;+}++/* ---------------------------------------------------------------------- */++static inline struct dentry *au_pinned_parent(struct au_pin *pin)+{+	if (pin)+		return pin->parent;+	return NULL;+}++static inline struct inode *au_pinned_h_dir(struct au_pin *pin)+{+	if (pin && pin->hdir)+		return pin->hdir->hi_inode;+	return NULL;+}++static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin)+{+	if (pin)+		return pin->hdir;+	return NULL;+}++static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry)+{+	if (pin)+		pin->dentry = dentry;+}++static inline void au_pin_set_parent_lflag(struct au_pin *pin,+					   unsigned char lflag)+{+	if (pin) {+		/* dirty macros require brackets */+		if (lflag) {+			au_fset_pin(pin->flags, DI_LOCKED);+		} else {+			au_fclr_pin(pin->flags, DI_LOCKED);+		}+	}+}++static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent)+{+	if (pin) {+		dput(pin->parent);+		pin->parent = dget(parent);+	}+}++/* ---------------------------------------------------------------------- */++#ifdef CONFIG_AUFS_HINOTIFY+/* hinotify.c */+int au_hin_alloc(struct au_hinode *hinode, struct inode *inode,+		 struct inode *h_inode);+void au_hin_free(struct au_hinode *hinode);+void au_hin_ctl(struct au_hinode *hinode, int do_set);+void au_reset_hinotify(struct inode *inode, unsigned int flags);++int __init au_hinotify_init(void);+void au_hinotify_fin(void);++static inline+void au_hin_init(struct au_hinode *hinode, struct au_hinotify *val)+{+	hinode->hi_notify = val;+}++static inline void au_iigen_dec(struct inode *inode)+{+	atomic_dec_return(&au_ii(inode)->ii_generation);+}++#else+static inline+int au_hin_alloc(struct au_hinode *hinode __maybe_unused,+		 struct inode *inode __maybe_unused,+		 struct inode *h_inode __maybe_unused)+{+	return -EOPNOTSUPP;+}++static inline void au_hin_free(struct au_hinode *hinode __maybe_unused)+{+	/* nothing */+}++static inline void au_hin_ctl(struct au_hinode *hinode __maybe_unused,+			      int do_set __maybe_unused)+{+	/* nothing */+}++static inline void au_reset_hinotify(struct inode *inode __maybe_unused,+				     unsigned int flags __maybe_unused)+{+	/* nothing */+}++static inline int au_hinotify_init(void)+{+	return 0;+}++#define au_hinotify_fin()	do {} while (0)++static inline+void au_hin_init(struct au_hinode *hinode __maybe_unused,+		 struct au_hinotify *val __maybe_unused)+{+	/* empty */+}+#endif /* CONFIG_AUFS_HINOTIFY */++static inline void au_hin_suspend(struct au_hinode *hdir)+{+	au_hin_ctl(hdir, /*do_set*/0);+}++static inline void au_hin_resume(struct au_hinode *hdir)+{+	au_hin_ctl(hdir, /*do_set*/1);+}++static inline void au_hin_imtx_lock(struct au_hinode *hdir)+{+	mutex_lock(&hdir->hi_inode->i_mutex);+	au_hin_suspend(hdir);+}++static inline void au_hin_imtx_lock_nested(struct au_hinode *hdir,+					   unsigned int sc __maybe_unused)+{+	mutex_lock_nested(&hdir->hi_inode->i_mutex, sc);+	au_hin_suspend(hdir);+}++static inline void au_hin_imtx_unlock(struct au_hinode *hdir)+{+	au_hin_resume(hdir);+	mutex_unlock(&hdir->hi_inode->i_mutex);+}++#endif /* __KERNEL__ */+#endif /* __AUFS_INODE_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/ioctl.c linux-2.6.31.5/fs/aufs/ioctl.c--- linux-2.6.31.5.orig/fs/aufs/ioctl.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/ioctl.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,67 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * ioctl+ * currently plink-management only.+ */++#include <linux/uaccess.h>+#include "aufs.h"++long aufs_ioctl_dir(struct file *file, unsigned int cmd,+		    unsigned long arg __maybe_unused)+{+	long err;+	struct super_block *sb;+	struct au_sbinfo *sbinfo;++	err = -EACCES;+	if (!capable(CAP_SYS_ADMIN))+		goto out;++	err = 0;+	sb = file->f_dentry->d_sb;+	sbinfo = au_sbi(sb);+	switch (cmd) {+	case AUFS_CTL_PLINK_MAINT:+		/*+		 * pseudo-link maintenance mode,+		 * cleared by aufs_release_dir()+		 */+		si_write_lock(sb);+		if (!au_ftest_si(sbinfo, MAINTAIN_PLINK)) {+			au_fset_si(sbinfo, MAINTAIN_PLINK);+			au_fi(file)->fi_maintain_plink = 1;+		} else+			err = -EBUSY;+		si_write_unlock(sb);+		break;+	case AUFS_CTL_PLINK_CLEAN:+		aufs_write_lock(sb->s_root);+		if (au_opt_test(sbinfo->si_mntflags, PLINK))+			au_plink_put(sb);+		aufs_write_unlock(sb->s_root);+		break;+	default:+		err = -EINVAL;+	}++ out:+	return err;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/i_op_add.c linux-2.6.31.5/fs/aufs/i_op_add.c--- linux-2.6.31.5.orig/fs/aufs/i_op_add.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/i_op_add.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,649 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * inode operations (add entry)+ */++#include "aufs.h"++/*+ * final procedure of adding a new entry, except link(2).+ * remove whiteout, instantiate, copyup the parent dir's times and size+ * and update version.+ * if it failed, re-create the removed whiteout.+ */+static int epilog(struct inode *dir, aufs_bindex_t bindex,+		  struct dentry *wh_dentry, struct dentry *dentry)+{+	int err, rerr;+	aufs_bindex_t bwh;+	struct path h_path;+	struct inode *inode, *h_dir;+	struct dentry *wh;++	bwh = -1;+	if (wh_dentry) {+		h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */+		IMustLock(h_dir);+		AuDebugOn(au_h_iptr(dir, bindex) != h_dir);+		bwh = au_dbwh(dentry);+		h_path.dentry = wh_dentry;+		h_path.mnt = au_sbr_mnt(dir->i_sb, bindex);+		err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path,+					  dentry);+		if (unlikely(err))+			goto out;+	}++	inode = au_new_inode(dentry, /*must_new*/1);+	if (!IS_ERR(inode)) {+		d_instantiate(dentry, inode);+		dir = dentry->d_parent->d_inode; /* dir inode is locked */+		IMustLock(dir);+		if (au_ibstart(dir) == au_dbstart(dentry))+			au_cpup_attr_timesizes(dir);+		dir->i_version++;+		return 0; /* success */+	}++	err = PTR_ERR(inode);+	if (!wh_dentry)+		goto out;++	/* revert */+	/* dir inode is locked */+	wh = au_wh_create(dentry, bwh, wh_dentry->d_parent);+	rerr = PTR_ERR(wh);+	if (IS_ERR(wh)) {+		AuIOErr("%.*s reverting whiteout failed(%d, %d)\n",+			AuDLNPair(dentry), err, rerr);+		err = -EIO;+	} else+		dput(wh);++ out:+	return err;+}++/*+ * simple tests for the adding inode operations.+ * following the checks in vfs, plus the parent-child relationship.+ */+int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,+	       struct dentry *h_parent, int isdir)+{+	int err;+	umode_t h_mode;+	struct dentry *h_dentry;+	struct inode *h_inode;++	h_dentry = au_h_dptr(dentry, bindex);+	h_inode = h_dentry->d_inode;+	if (!dentry->d_inode) {+		err = -EEXIST;+		if (unlikely(h_inode))+			goto out;+	} else {+		/* rename(2) case */+		err = -EIO;+		if (unlikely(!h_inode || !h_inode->i_nlink))+			goto out;++		h_mode = h_inode->i_mode;+		if (!isdir) {+			err = -EISDIR;+			if (unlikely(S_ISDIR(h_mode)))+				goto out;+		} else if (unlikely(!S_ISDIR(h_mode))) {+			err = -ENOTDIR;+			goto out;+		}+	}++	err = -EIO;+	/* expected parent dir is locked */+	if (unlikely(h_parent != h_dentry->d_parent))+		goto out;+	err = 0;++ out:+	return err;+}++/*+ * initial procedure of adding a new entry.+ * prepare writable branch and the parent dir, lock it,+ * and lookup whiteout for the new entry.+ */+static struct dentry*+lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt,+		  struct dentry *src_dentry, struct au_pin *pin,+		  struct au_wr_dir_args *wr_dir_args)+{+	struct dentry *wh_dentry, *h_parent;+	struct super_block *sb;+	struct au_branch *br;+	int err;+	unsigned int udba;+	aufs_bindex_t bcpup;++	err = au_wr_dir(dentry, src_dentry, wr_dir_args);+	bcpup = err;+	wh_dentry = ERR_PTR(err);+	if (unlikely(err < 0))+		goto out;++	sb = dentry->d_sb;+	udba = au_opt_udba(sb);+	err = au_pin(pin, dentry, bcpup, udba,+		     AuPin_DI_LOCKED | AuPin_MNT_WRITE);+	wh_dentry = ERR_PTR(err);+	if (unlikely(err))+		goto out;++	h_parent = au_pinned_h_parent(pin);+	if (udba != AuOpt_UDBA_NONE+	    && au_dbstart(dentry) == bcpup) {+		err = au_may_add(dentry, bcpup, h_parent,+				 au_ftest_wrdir(wr_dir_args->flags, ISDIR));+		wh_dentry = ERR_PTR(err);+		if (unlikely(err))+			goto out_unpin;+	}++	br = au_sbr(sb, bcpup);+	if (dt) {+		struct path tmp = {+			.dentry	= h_parent,+			.mnt	= br->br_mnt+		};+		au_dtime_store(dt, au_pinned_parent(pin), &tmp);+	}++	wh_dentry = NULL;+	if (bcpup != au_dbwh(dentry))+		goto out; /* success */++	wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br);++ out_unpin:+	if (IS_ERR(wh_dentry))+		au_unpin(pin);+ out:+	return wh_dentry;+}++/* ---------------------------------------------------------------------- */++enum { Mknod, Symlink, Creat };+struct simple_arg {+	int type;+	union {+		struct {+			int mode;+			struct nameidata *nd;+		} c;+		struct {+			const char *symname;+		} s;+		struct {+			int mode;+			dev_t dev;+		} m;+	} u;+};++static int add_simple(struct inode *dir, struct dentry *dentry,+		      struct simple_arg *arg)+{+	int err;+	aufs_bindex_t bstart;+	unsigned char created;+	struct au_dtime dt;+	struct au_pin pin;+	struct path h_path;+	struct dentry *wh_dentry, *parent;+	struct inode *h_dir;+	struct au_wr_dir_args wr_dir_args = {+		.force_btgt	= -1,+		.flags		= AuWrDir_ADD_ENTRY+	};++	IMustLock(dir);++	parent = dentry->d_parent; /* dir inode is locked */+	aufs_read_lock(dentry, AuLock_DW);+	di_write_lock_parent(parent);+	wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, &pin,+				      &wr_dir_args);+	err = PTR_ERR(wh_dentry);+	if (IS_ERR(wh_dentry))+		goto out;++	bstart = au_dbstart(dentry);+	h_path.dentry = au_h_dptr(dentry, bstart);+	h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);+	h_dir = au_pinned_h_dir(&pin);+	switch (arg->type) {+	case Creat:+		err = vfsub_create(h_dir, &h_path, arg->u.c.mode);+		break;+	case Symlink:+		err = vfsub_symlink(h_dir, &h_path, arg->u.s.symname);+		break;+	case Mknod:+		err = vfsub_mknod(h_dir, &h_path, arg->u.m.mode, arg->u.m.dev);+		break;+	default:+		BUG();+	}+	created = !err;+	if (!err)+		err = epilog(dir, bstart, wh_dentry, dentry);++	/* revert */+	if (unlikely(created && err && h_path.dentry->d_inode)) {+		int rerr;+		rerr = vfsub_unlink(h_dir, &h_path, /*force*/0);+		if (rerr) {+			AuIOErr("%.*s revert failure(%d, %d)\n",+				AuDLNPair(dentry), err, rerr);+			err = -EIO;+		}+		au_dtime_revert(&dt);+		d_drop(dentry);+	}++	au_unpin(&pin);+	dput(wh_dentry);++ out:+	if (unlikely(err)) {+		au_update_dbstart(dentry);+		d_drop(dentry);+	}+	di_write_unlock(parent);+	aufs_read_unlock(dentry, AuLock_DW);+	return err;+}++int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)+{+	struct simple_arg arg = {+		.type = Mknod,+		.u.m = {+			.mode	= mode,+			.dev	= dev+		}+	};+	return add_simple(dir, dentry, &arg);+}++int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)+{+	struct simple_arg arg = {+		.type = Symlink,+		.u.s.symname = symname+	};+	return add_simple(dir, dentry, &arg);+}++int aufs_create(struct inode *dir, struct dentry *dentry, int mode,+		struct nameidata *nd)+{+	struct simple_arg arg = {+		.type = Creat,+		.u.c = {+			.mode	= mode,+			.nd	= nd+		}+	};+	return add_simple(dir, dentry, &arg);+}++/* ---------------------------------------------------------------------- */++struct au_link_args {+	aufs_bindex_t bdst, bsrc;+	struct au_pin pin;+	struct path h_path;+	struct dentry *src_parent, *parent;+};++static int au_cpup_before_link(struct dentry *src_dentry,+			       struct au_link_args *a)+{+	int err;+	struct dentry *h_src_dentry;+	struct mutex *h_mtx;++	di_read_lock_parent(a->src_parent, AuLock_IR);+	err = au_test_and_cpup_dirs(src_dentry, a->bdst);+	if (unlikely(err))+		goto out;++	h_src_dentry = au_h_dptr(src_dentry, a->bsrc);+	h_mtx = &h_src_dentry->d_inode->i_mutex;+	err = au_pin(&a->pin, src_dentry, a->bdst,+		     au_opt_udba(src_dentry->d_sb),+		     AuPin_DI_LOCKED | AuPin_MNT_WRITE);+	if (unlikely(err))+		goto out;+	mutex_lock_nested(h_mtx, AuLsc_I_CHILD);+	err = au_sio_cpup_simple(src_dentry, a->bdst, -1,+				 AuCpup_DTIME /* | AuCpup_KEEPLINO */);+	mutex_unlock(h_mtx);+	au_unpin(&a->pin);++ out:+	di_read_unlock(a->src_parent, AuLock_IR);+	return err;+}++static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a)+{+	int err;+	unsigned char plink;+	struct inode *h_inode, *inode;+	struct dentry *h_src_dentry;+	struct super_block *sb;++	plink = 0;+	h_inode = NULL;+	sb = src_dentry->d_sb;+	inode = src_dentry->d_inode;+	if (au_ibstart(inode) <= a->bdst)+		h_inode = au_h_iptr(inode, a->bdst);+	if (!h_inode || !h_inode->i_nlink) {+		/* copyup src_dentry as the name of dentry. */+		au_set_dbstart(src_dentry, a->bdst);+		au_set_h_dptr(src_dentry, a->bdst, dget(a->h_path.dentry));+		h_inode = au_h_dptr(src_dentry, a->bsrc)->d_inode;+		mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);+		err = au_sio_cpup_single(src_dentry, a->bdst, a->bsrc, -1,+					 AuCpup_KEEPLINO, a->parent);+		mutex_unlock(&h_inode->i_mutex);+		au_set_h_dptr(src_dentry, a->bdst, NULL);+		au_set_dbstart(src_dentry, a->bsrc);+	} else {+		/* the inode of src_dentry already exists on a.bdst branch */+		h_src_dentry = d_find_alias(h_inode);+		if (!h_src_dentry && au_plink_test(inode)) {+			plink = 1;+			h_src_dentry = au_plink_lkup(inode, a->bdst);+			err = PTR_ERR(h_src_dentry);+			if (IS_ERR(h_src_dentry))+				goto out;++			if (unlikely(!h_src_dentry->d_inode)) {+				dput(h_src_dentry);+				h_src_dentry = NULL;+			}++		}+		if (h_src_dentry) {+			err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin),+					 &a->h_path);+			dput(h_src_dentry);+		} else {+			AuIOErr("no dentry found for hi%lu on b%d\n",+				h_inode->i_ino, a->bdst);+			err = -EIO;+		}+	}++	if (!err && !plink)+		au_plink_append(inode, a->bdst, a->h_path.dentry);++out:+	return err;+}++int aufs_link(struct dentry *src_dentry, struct inode *dir,+	      struct dentry *dentry)+{+	int err, rerr;+	struct au_dtime dt;+	struct au_link_args *a;+	struct dentry *wh_dentry, *h_src_dentry;+	struct inode *inode;+	struct super_block *sb;+	struct au_wr_dir_args wr_dir_args = {+		/* .force_btgt	= -1, */+		.flags		= AuWrDir_ADD_ENTRY+	};++	IMustLock(dir);+	inode = src_dentry->d_inode;+	IMustLock(inode);++	err = -ENOENT;+	if (unlikely(!inode->i_nlink))+		goto out;++	err = -ENOMEM;+	a = kzalloc(sizeof(*a), GFP_NOFS);+	if (unlikely(!a))+		goto out;++	a->parent = dentry->d_parent; /* dir inode is locked */+	aufs_read_and_write_lock2(dentry, src_dentry, /*AuLock_FLUSH*/0);+	a->src_parent = dget_parent(src_dentry);+	wr_dir_args.force_btgt = au_dbstart(src_dentry);++	di_write_lock_parent(a->parent);+	wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt);+	wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin,+				      &wr_dir_args);+	err = PTR_ERR(wh_dentry);+	if (IS_ERR(wh_dentry))+		goto out_unlock;++	err = 0;+	sb = dentry->d_sb;+	a->bdst = au_dbstart(dentry);+	a->h_path.dentry = au_h_dptr(dentry, a->bdst);+	a->h_path.mnt = au_sbr_mnt(sb, a->bdst);+	a->bsrc = au_dbstart(src_dentry);+	if (au_opt_test(au_mntflags(sb), PLINK)) {+		if (a->bdst < a->bsrc+		    /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */)+			err = au_cpup_or_link(src_dentry, a);+		else {+			h_src_dentry = au_h_dptr(src_dentry, a->bdst);+			err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin),+					 &a->h_path);+		}+	} else {+		/*+		 * copyup src_dentry to the branch we process,+		 * and then link(2) to it.+		 */+		if (a->bdst < a->bsrc+		    /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) {+			au_unpin(&a->pin);+			di_write_unlock(a->parent);+			err = au_cpup_before_link(src_dentry, a);+			di_write_lock_parent(a->parent);+			if (!err)+				err = au_pin(&a->pin, dentry, a->bdst,+					     au_opt_udba(sb),+					     AuPin_DI_LOCKED | AuPin_MNT_WRITE);+			if (unlikely(err))+				goto out_wh;+		}+		if (!err) {+			h_src_dentry = au_h_dptr(src_dentry, a->bdst);+			err = -ENOENT;+			if (h_src_dentry && h_src_dentry->d_inode)+				err = vfsub_link(h_src_dentry,+						 au_pinned_h_dir(&a->pin),+						 &a->h_path);+		}+	}+	if (unlikely(err))+		goto out_unpin;++	if (wh_dentry) {+		a->h_path.dentry = wh_dentry;+		err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path,+					  dentry);+		if (unlikely(err))+			goto out_revert;+	}++	dir->i_version++;+	if (au_ibstart(dir) == au_dbstart(dentry))+		au_cpup_attr_timesizes(dir);+	inc_nlink(inode);+	inode->i_ctime = dir->i_ctime;+	if (!d_unhashed(a->h_path.dentry))+		d_instantiate(dentry, au_igrab(inode));+	else+		/* some filesystem calls d_drop() */+		d_drop(dentry);+	goto out_unpin; /* success */++ out_revert:+	rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, /*force*/0);+	if (!rerr)+		goto out_dt;+	AuIOErr("%.*s reverting failed(%d, %d)\n",+		AuDLNPair(dentry), err, rerr);+	err = -EIO;+ out_dt:+	d_drop(dentry);+	au_dtime_revert(&dt);+ out_unpin:+	au_unpin(&a->pin);+ out_wh:+	dput(wh_dentry);+ out_unlock:+	if (unlikely(err)) {+		au_update_dbstart(dentry);+		d_drop(dentry);+	}+	di_write_unlock(a->parent);+	dput(a->src_parent);+	aufs_read_and_write_unlock2(dentry, src_dentry);+	kfree(a);+ out:+	return err;+}++int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)+{+	int err, rerr;+	aufs_bindex_t bindex;+	unsigned char diropq;+	struct path h_path;+	struct dentry *wh_dentry, *parent, *opq_dentry;+	struct mutex *h_mtx;+	struct super_block *sb;+	struct {+		struct au_pin pin;+		struct au_dtime dt;+	} *a; /* reduce the stack usage */+	struct au_wr_dir_args wr_dir_args = {+		.force_btgt	= -1,+		.flags		= AuWrDir_ADD_ENTRY | AuWrDir_ISDIR+	};++	IMustLock(dir);++	err = -ENOMEM;+	a = kmalloc(sizeof(*a), GFP_NOFS);+	if (unlikely(!a))+		goto out;++	aufs_read_lock(dentry, AuLock_DW);+	parent = dentry->d_parent; /* dir inode is locked */+	di_write_lock_parent(parent);+	wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL,+				      &a->pin, &wr_dir_args);+	err = PTR_ERR(wh_dentry);+	if (IS_ERR(wh_dentry))+		goto out_free;++	sb = dentry->d_sb;+	bindex = au_dbstart(dentry);+	h_path.dentry = au_h_dptr(dentry, bindex);+	h_path.mnt = au_sbr_mnt(sb, bindex);+	err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode);+	if (unlikely(err))+		goto out_unlock;++	/* make the dir opaque */+	diropq = 0;+	h_mtx = &h_path.dentry->d_inode->i_mutex;+	if (wh_dentry+	    || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) {+		mutex_lock_nested(h_mtx, AuLsc_I_CHILD);+		opq_dentry = au_diropq_create(dentry, bindex);+		mutex_unlock(h_mtx);+		err = PTR_ERR(opq_dentry);+		if (IS_ERR(opq_dentry))+			goto out_dir;+		dput(opq_dentry);+		diropq = 1;+	}++	err = epilog(dir, bindex, wh_dentry, dentry);+	if (!err) {+		inc_nlink(dir);+		goto out_unlock; /* success */+	}++	/* revert */+	if (diropq) {+		AuLabel(revert opq);+		mutex_lock_nested(h_mtx, AuLsc_I_CHILD);+		rerr = au_diropq_remove(dentry, bindex);+		mutex_unlock(h_mtx);+		if (rerr) {+			AuIOErr("%.*s reverting diropq failed(%d, %d)\n",+				AuDLNPair(dentry), err, rerr);+			err = -EIO;+		}+	}++ out_dir:+	AuLabel(revert dir);+	rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path);+	if (rerr) {+		AuIOErr("%.*s reverting dir failed(%d, %d)\n",+			AuDLNPair(dentry), err, rerr);+		err = -EIO;+	}+	d_drop(dentry);+	au_dtime_revert(&a->dt);+ out_unlock:+	au_unpin(&a->pin);+	dput(wh_dentry);+ out_free:+	if (unlikely(err)) {+		au_update_dbstart(dentry);+		d_drop(dentry);+	}+	di_write_unlock(parent);+	aufs_read_unlock(dentry, AuLock_DW);+	kfree(a);+ out:+	return err;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/i_op.c linux-2.6.31.5/fs/aufs/i_op.c--- linux-2.6.31.5.orig/fs/aufs/i_op.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/i_op.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,872 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * inode operations (except add/del/rename)+ */++#include <linux/device_cgroup.h>+#include <linux/fs_stack.h>+#include <linux/mm.h>+#include <linux/namei.h>+#include <linux/security.h>+#include <linux/uaccess.h>+#include "aufs.h"++static int h_permission(struct inode *h_inode, int mask,+			struct vfsmount *h_mnt, int brperm)+{+	int err;+	const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND));++	err = -EACCES;+	if ((write_mask && IS_IMMUTABLE(h_inode))+	    || ((mask & MAY_EXEC)+		&& S_ISREG(h_inode->i_mode)+		&& ((h_mnt->mnt_flags & MNT_NOEXEC)+		    || !(h_inode->i_mode & S_IXUGO))))+		goto out;++	/*+	 * - skip the lower fs test in the case of write to ro branch.+	 * - nfs dir permission write check is optimized, but a policy for+	 *   link/rename requires a real check.+	 */+	if ((write_mask && !au_br_writable(brperm))+	    || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode)+		&& write_mask && !(mask & MAY_READ))+	    || !h_inode->i_op->permission) {+		/* AuLabel(generic_permission); */+		err = generic_permission(h_inode, mask, NULL);+	} else {+		/* AuLabel(h_inode->permission); */+		err = h_inode->i_op->permission(h_inode, mask);+		AuTraceErr(err);+	}++	if (!err)+		err = devcgroup_inode_permission(h_inode, mask);+	if (!err)+		err = security_inode_permission+			(h_inode, mask & (MAY_READ | MAY_WRITE | MAY_EXEC+					  | MAY_APPEND));++ out:+	return err;+}++static int aufs_permission(struct inode *inode, int mask)+{+	int err;+	aufs_bindex_t bindex, bend;+	const unsigned char isdir = !!S_ISDIR(inode->i_mode);+	const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND));+	struct inode *h_inode;+	struct super_block *sb;+	struct au_branch *br;++	sb = inode->i_sb;+	si_read_lock(sb, AuLock_FLUSH);+	ii_read_lock_child(inode);++	if (!isdir || write_mask) {+		h_inode = au_h_iptr(inode, au_ibstart(inode));+		AuDebugOn(!h_inode+			  || ((h_inode->i_mode & S_IFMT)+			      != (inode->i_mode & S_IFMT)));+		err = 0;+		bindex = au_ibstart(inode);+		br = au_sbr(sb, bindex);+		err = h_permission(h_inode, mask, br->br_mnt, br->br_perm);++		if (write_mask && !err) {+			/* test whether the upper writable branch exists */+			err = -EROFS;+			for (; bindex >= 0; bindex--)+				if (!au_br_rdonly(au_sbr(sb, bindex))) {+					err = 0;+					break;+				}+		}+		goto out;+	}++	/* non-write to dir */+	err = 0;+	bend = au_ibend(inode);+	for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) {+		h_inode = au_h_iptr(inode, bindex);+		if (h_inode) {+			AuDebugOn(!S_ISDIR(h_inode->i_mode));+			br = au_sbr(sb, bindex);+			err = h_permission(h_inode, mask, br->br_mnt,+					   br->br_perm);+		}+	}++ out:+	ii_read_unlock(inode);+	si_read_unlock(sb);+	return err;+}++/* ---------------------------------------------------------------------- */++static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,+				  struct nameidata *nd)+{+	struct dentry *ret, *parent;+	struct inode *inode, *h_inode;+	struct mutex *mtx;+	struct super_block *sb;+	int err, npositive;+	aufs_bindex_t bstart;++	/* temporary workaround for a bug in NFSD readdir */+	if (!au_test_nfsd(current))+		IMustLock(dir);+	else+		WARN_ONCE(!mutex_is_locked(&dir->i_mutex),+			  "a known problem of NFSD readdir since 2.6.28\n");++	sb = dir->i_sb;+	si_read_lock(sb, AuLock_FLUSH);+	err = au_alloc_dinfo(dentry);+	ret = ERR_PTR(err);+	if (unlikely(err))+		goto out;++	parent = dentry->d_parent; /* dir inode is locked */+	di_read_lock_parent(parent, AuLock_IR);+	npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0, nd);+	di_read_unlock(parent, AuLock_IR);+	err = npositive;+	ret = ERR_PTR(err);+	if (unlikely(err < 0))+		goto out_unlock;++	inode = NULL;+	if (npositive) {+		bstart = au_dbstart(dentry);+		h_inode = au_h_dptr(dentry, bstart)->d_inode;+		if (!S_ISDIR(h_inode->i_mode)) {+			/*+			 * stop 'race'-ing between hardlinks under different+			 * parents.+			 */+			mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx;+			mutex_lock(mtx);+			inode = au_new_inode(dentry, /*must_new*/0);+			mutex_unlock(mtx);+		} else+			inode = au_new_inode(dentry, /*must_new*/0);+		ret = (void *)inode;+	}+	if (IS_ERR(inode))+		goto out_unlock;++	ret = d_splice_alias(inode, dentry);+	if (unlikely(IS_ERR(ret) && inode))+		ii_write_unlock(inode);+	au_store_oflag(nd, inode);++ out_unlock:+	di_write_unlock(dentry);+ out:+	si_read_unlock(sb);+	return ret;+}++/* ---------------------------------------------------------------------- */++static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent,+			  const unsigned char add_entry, aufs_bindex_t bcpup,+			  aufs_bindex_t bstart)+{+	int err;+	struct dentry *h_parent;+	struct inode *h_dir;++	if (add_entry) {+		au_update_dbstart(dentry);+		IMustLock(parent->d_inode);+	} else+		di_write_lock_parent(parent);++	err = 0;+	if (!au_h_dptr(parent, bcpup)) {+		if (bstart < bcpup)+			err = au_cpdown_dirs(dentry, bcpup);+		else+			err = au_cpup_dirs(dentry, bcpup);+	}+	if (!err && add_entry) {+		h_parent = au_h_dptr(parent, bcpup);+		h_dir = h_parent->d_inode;+		mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);+		err = au_lkup_neg(dentry, bcpup);+		/* todo: no unlock here */+		mutex_unlock(&h_dir->i_mutex);+		if (bstart < bcpup && au_dbstart(dentry) < 0) {+			au_set_dbstart(dentry, 0);+			au_update_dbrange(dentry, /*do_put_zero*/0);+		}+	}++	if (!add_entry)+		di_write_unlock(parent);+	if (!err)+		err = bcpup; /* success */++	return err;+}++/*+ * decide the branch and the parent dir where we will create a new entry.+ * returns new bindex or an error.+ * copyup the parent dir if needed.+ */+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,+	      struct au_wr_dir_args *args)+{+	int err;+	aufs_bindex_t bcpup, bstart, src_bstart;+	const unsigned char add_entry = !!au_ftest_wrdir(args->flags,+							 ADD_ENTRY);+	struct super_block *sb;+	struct dentry *parent;+	struct au_sbinfo *sbinfo;++	sb = dentry->d_sb;+	sbinfo = au_sbi(sb);+	parent = dget_parent(dentry);+	bstart = au_dbstart(dentry);+	bcpup = bstart;+	if (args->force_btgt < 0) {+		if (src_dentry) {+			src_bstart = au_dbstart(src_dentry);+			if (src_bstart < bstart)+				bcpup = src_bstart;+		} else if (add_entry) {+			err = AuWbrCreate(sbinfo, dentry,+					  au_ftest_wrdir(args->flags, ISDIR));+			bcpup = err;+		}++		if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) {+			if (add_entry)+				err = AuWbrCopyup(sbinfo, dentry);+			else {+				if (!IS_ROOT(dentry)) {+					di_read_lock_parent(parent, !AuLock_IR);+					err = AuWbrCopyup(sbinfo, dentry);+					di_read_unlock(parent, !AuLock_IR);+				} else+					err = AuWbrCopyup(sbinfo, dentry);+			}+			bcpup = err;+			if (unlikely(err < 0))+				goto out;+		}+	} else {+		bcpup = args->force_btgt;+		AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode));+	}+	AuDbg("bstart %d, bcpup %d\n", bstart, bcpup);+	if (bstart < bcpup)+		au_update_dbrange(dentry, /*do_put_zero*/1);++	err = bcpup;+	if (bcpup == bstart)+		goto out; /* success */++	/* copyup the new parent into the branch we process */+	err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart);++ out:+	dput(parent);+	return err;+}++/* ---------------------------------------------------------------------- */++struct dentry *au_pinned_h_parent(struct au_pin *pin)+{+	if (pin && pin->parent)+		return au_h_dptr(pin->parent, pin->bindex);+	return NULL;+}++void au_unpin(struct au_pin *p)+{+	if (au_ftest_pin(p->flags, MNT_WRITE))+		mnt_drop_write(p->h_mnt);+	if (!p->hdir)+		return;++	au_hin_imtx_unlock(p->hdir);+	if (!au_ftest_pin(p->flags, DI_LOCKED))+		di_read_unlock(p->parent, AuLock_IR);+	iput(p->hdir->hi_inode);+	dput(p->parent);+	p->parent = NULL;+	p->hdir = NULL;+	p->h_mnt = NULL;+}++int au_do_pin(struct au_pin *p)+{+	int err;+	struct super_block *sb;+	struct dentry *h_dentry, *h_parent;+	struct au_branch *br;+	struct inode *h_dir;++	err = 0;+	sb = p->dentry->d_sb;+	br = au_sbr(sb, p->bindex);+	if (IS_ROOT(p->dentry)) {+		if (au_ftest_pin(p->flags, MNT_WRITE)) {+			p->h_mnt = br->br_mnt;+			err = mnt_want_write(p->h_mnt);+			if (unlikely(err)) {+				au_fclr_pin(p->flags, MNT_WRITE);+				goto out_err;+			}+		}+		goto out;+	}++	h_dentry = NULL;+	if (p->bindex <= au_dbend(p->dentry))+		h_dentry = au_h_dptr(p->dentry, p->bindex);++	p->parent = dget_parent(p->dentry);+	if (!au_ftest_pin(p->flags, DI_LOCKED))+		di_read_lock(p->parent, AuLock_IR, p->lsc_di);++	h_dir = NULL;+	h_parent = au_h_dptr(p->parent, p->bindex);+	p->hdir = au_hi(p->parent->d_inode, p->bindex);+	if (p->hdir)+		h_dir = p->hdir->hi_inode;++	/* udba case */+	if (unlikely(!p->hdir || !h_dir)) {+		if (!au_ftest_pin(p->flags, DI_LOCKED))+			di_read_unlock(p->parent, AuLock_IR);+		dput(p->parent);+		p->parent = NULL;+		goto out_err;+	}++	au_igrab(h_dir);+	au_hin_imtx_lock_nested(p->hdir, p->lsc_hi);++	if (unlikely(p->hdir->hi_inode != h_parent->d_inode)) {+		err = -EBUSY;+		goto out_unpin;+	}+	if (h_dentry) {+		err = au_h_verify(h_dentry, p->udba, h_dir, h_parent, br);+		if (unlikely(err)) {+			au_fclr_pin(p->flags, MNT_WRITE);+			goto out_unpin;+		}+	}++	if (au_ftest_pin(p->flags, MNT_WRITE)) {+		p->h_mnt = br->br_mnt;+		err = mnt_want_write(p->h_mnt);+		if (unlikely(err)) {+			au_fclr_pin(p->flags, MNT_WRITE);+			goto out_unpin;+		}+	}+	goto out; /* success */++ out_unpin:+	au_unpin(p);+ out_err:+	AuErr("err %d\n", err);+	err = au_busy_or_stale();+ out:+	return err;+}++void au_pin_init(struct au_pin *p, struct dentry *dentry,+		 aufs_bindex_t bindex, int lsc_di, int lsc_hi,+		 unsigned int udba, unsigned char flags)+{+	p->dentry = dentry;+	p->udba = udba;+	p->lsc_di = lsc_di;+	p->lsc_hi = lsc_hi;+	p->flags = flags;+	p->bindex = bindex;++	p->parent = NULL;+	p->hdir = NULL;+	p->h_mnt = NULL;+}++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,+	   unsigned int udba, unsigned char flags)+{+	au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2,+		    udba, flags);+	return au_do_pin(pin);+}++/* ---------------------------------------------------------------------- */++#define AuIcpup_DID_CPUP	1+#define au_ftest_icpup(flags, name)	((flags) & AuIcpup_##name)+#define au_fset_icpup(flags, name)	{ (flags) |= AuIcpup_##name; }+#define au_fclr_icpup(flags, name)	{ (flags) &= ~AuIcpup_##name; }++struct au_icpup_args {+	unsigned char flags;+	unsigned char pin_flags;+	aufs_bindex_t btgt;+	struct au_pin pin;+	struct path h_path;+	struct inode *h_inode;+};++static int au_lock_and_icpup(struct dentry *dentry, struct iattr *ia,+			     struct au_icpup_args *a)+{+	int err;+	unsigned int udba;+	loff_t sz;+	aufs_bindex_t bstart;+	struct dentry *hi_wh, *parent;+	struct inode *inode;+	struct au_wr_dir_args wr_dir_args = {+		.force_btgt	= -1,+		.flags		= 0+	};++	di_write_lock_child(dentry);+	bstart = au_dbstart(dentry);+	inode = dentry->d_inode;+	if (S_ISDIR(inode->i_mode))+		au_fset_wrdir(wr_dir_args.flags, ISDIR);+	/* plink or hi_wh() case */+	if (bstart != au_ibstart(inode))+		wr_dir_args.force_btgt = au_ibstart(inode);+	err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args);+	if (unlikely(err < 0))+		goto out_dentry;+	a->btgt = err;+	if (err != bstart)+		au_fset_icpup(a->flags, DID_CPUP);++	err = 0;+	a->pin_flags = AuPin_MNT_WRITE;+	parent = NULL;+	if (!IS_ROOT(dentry)) {+		au_fset_pin(a->pin_flags, DI_LOCKED);+		parent = dget_parent(dentry);+		di_write_lock_parent(parent);+	}++	udba = au_opt_udba(dentry->d_sb);+	if (d_unhashed(dentry) || (ia->ia_valid & ATTR_FILE))+		udba = AuOpt_UDBA_NONE;+	err = au_pin(&a->pin, dentry, a->btgt, udba, a->pin_flags);+	if (unlikely(err)) {+		if (parent) {+			di_write_unlock(parent);+			dput(parent);+		}+		goto out_dentry;+	}+	a->h_path.dentry = au_h_dptr(dentry, bstart);+	a->h_inode = a->h_path.dentry->d_inode;+	mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);+	sz = -1;+	if ((ia->ia_valid & ATTR_SIZE) && ia->ia_size < i_size_read(a->h_inode))+		sz = ia->ia_size;++	hi_wh = NULL;+	if (au_ftest_icpup(a->flags, DID_CPUP) && d_unhashed(dentry)) {+		hi_wh = au_hi_wh(inode, a->btgt);+		if (!hi_wh) {+			err = au_sio_cpup_wh(dentry, a->btgt, sz, /*file*/NULL);+			if (unlikely(err))+				goto out_unlock;+			hi_wh = au_hi_wh(inode, a->btgt);+			/* todo: revalidate hi_wh? */+		}+	}++	if (parent) {+		au_pin_set_parent_lflag(&a->pin, /*lflag*/0);+		di_downgrade_lock(parent, AuLock_IR);+		dput(parent);+	}+	if (!au_ftest_icpup(a->flags, DID_CPUP))+		goto out; /* success */++	if (!d_unhashed(dentry)) {+		err = au_sio_cpup_simple(dentry, a->btgt, sz, AuCpup_DTIME);+		if (!err)+			a->h_path.dentry = au_h_dptr(dentry, a->btgt);+	} else if (!hi_wh)+		a->h_path.dentry = au_h_dptr(dentry, a->btgt);+	else+		a->h_path.dentry = hi_wh; /* do not dget here */++ out_unlock:+	mutex_unlock(&a->h_inode->i_mutex);+	a->h_inode = a->h_path.dentry->d_inode;+	if (!err) {+		mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);+		goto out; /* success */+	}++	au_unpin(&a->pin);++ out_dentry:+	di_write_unlock(dentry);+ out:+	return err;+}++static int aufs_setattr(struct dentry *dentry, struct iattr *ia)+{+	int err;+	struct inode *inode;+	struct super_block *sb;+	struct file *file;+	struct au_icpup_args *a;++	err = -ENOMEM;+	a = kzalloc(sizeof(*a), GFP_NOFS);+	if (unlikely(!a))+		goto out;++	inode = dentry->d_inode;+	IMustLock(inode);+	sb = dentry->d_sb;+	si_read_lock(sb, AuLock_FLUSH);++	file = NULL;+	if (ia->ia_valid & ATTR_FILE) {+		/* currently ftruncate(2) only */+		file = ia->ia_file;+		fi_write_lock(file);+		ia->ia_file = au_h_fptr(file, au_fbstart(file));+	}++	if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))+		ia->ia_valid &= ~ATTR_MODE;++	err = au_lock_and_icpup(dentry, ia, a);+	if (unlikely(err < 0))+		goto out_si;+	if (au_ftest_icpup(a->flags, DID_CPUP)) {+		ia->ia_file = NULL;+		ia->ia_valid &= ~ATTR_FILE;+	}++	a->h_path.mnt = au_sbr_mnt(sb, a->btgt);+	if (ia->ia_valid & ATTR_SIZE) {+		struct file *f;++		if (ia->ia_size < i_size_read(inode)) {+			/* unmap only */+			err = vmtruncate(inode, ia->ia_size);+			if (unlikely(err))+				goto out_unlock;+		}++		f = NULL;+		if (ia->ia_valid & ATTR_FILE)+			f = ia->ia_file;+		mutex_unlock(&a->h_inode->i_mutex);+		err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f);+		mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);+	} else+		err = vfsub_notify_change(&a->h_path, ia);+	if (!err)+		au_cpup_attr_changeable(inode);++ out_unlock:+	mutex_unlock(&a->h_inode->i_mutex);+	au_unpin(&a->pin);+	di_write_unlock(dentry);+ out_si:+	if (file) {+		fi_write_unlock(file);+		ia->ia_file = file;+		ia->ia_valid |= ATTR_FILE;+	}+	si_read_unlock(sb);+	kfree(a);+ out:+	return err;+}++static int au_getattr_lock_reval(struct dentry *dentry, unsigned int sigen)+{+	int err;+	struct inode *inode;+	struct dentry *parent;++	err = 0;+	inode = dentry->d_inode;+	di_write_lock_child(dentry);+	if (au_digen(dentry) != sigen || au_iigen(inode) != sigen) {+		parent = dget_parent(dentry);+		di_read_lock_parent(parent, AuLock_IR);+		/* returns a number of positive dentries */+		err = au_refresh_hdentry(dentry, inode->i_mode & S_IFMT);+		if (err > 0)+			err = au_refresh_hinode(inode, dentry);+		di_read_unlock(parent, AuLock_IR);+		dput(parent);+		if (unlikely(!err))+			err = -EIO;+	}+	di_downgrade_lock(dentry, AuLock_IR);+	if (unlikely(err))+		di_read_unlock(dentry, AuLock_IR);++	return err;+}++static void au_refresh_iattr(struct inode *inode, struct kstat *st,+			     unsigned int nlink)+{+	inode->i_mode = st->mode;+	inode->i_uid = st->uid;+	inode->i_gid = st->gid;+	inode->i_atime = st->atime;+	inode->i_mtime = st->mtime;+	inode->i_ctime = st->ctime;++	au_cpup_attr_nlink(inode, /*force*/0);+	if (S_ISDIR(inode->i_mode)) {+		inode->i_nlink -= nlink;+		inode->i_nlink += st->nlink;+	}++	spin_lock(&inode->i_lock);+	inode->i_blocks = st->blocks;+	i_size_write(inode, st->size);+	spin_unlock(&inode->i_lock);+}++static int aufs_getattr(struct vfsmount *mnt __maybe_unused,+			struct dentry *dentry, struct kstat *st)+{+	int err;+	unsigned int mnt_flags;+	aufs_bindex_t bindex;+	unsigned char udba_none, positive;+	struct super_block *sb, *h_sb;+	struct inode *inode;+	struct vfsmount *h_mnt;+	struct dentry *h_dentry;++	err = 0;+	sb = dentry->d_sb;+	inode = dentry->d_inode;+	si_read_lock(sb, AuLock_FLUSH);+	mnt_flags = au_mntflags(sb);+	udba_none = !!au_opt_test(mnt_flags, UDBA_NONE);++	/* support fstat(2) */+	if (!d_unhashed(dentry) && !udba_none) {+		unsigned int sigen = au_sigen(sb);+		if (au_digen(dentry) == sigen && au_iigen(inode) == sigen)+			di_read_lock_child(dentry, AuLock_IR);+		else {+			AuDebugOn(!IS_ROOT(dentry));+			err = au_getattr_lock_reval(dentry, sigen);+			if (unlikely(err))+				goto out;+		}+	} else+		di_read_lock_child(dentry, AuLock_IR);++	bindex = au_ibstart(inode);+	h_mnt = au_sbr_mnt(sb, bindex);+	h_sb = h_mnt->mnt_sb;+	if (!au_test_fs_bad_iattr(h_sb) && udba_none)+		goto out_fill; /* success */++	h_dentry = NULL;+	if (au_dbstart(dentry) == bindex)+		h_dentry = dget(au_h_dptr(dentry, bindex));+	else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) {+		h_dentry = au_plink_lkup(inode, bindex);+		if (IS_ERR(h_dentry))+			goto out_fill; /* pretending success */+	}+	/* illegally overlapped or something */+	if (unlikely(!h_dentry))+		goto out_fill; /* pretending success */++	positive = !!h_dentry->d_inode;+	if (positive)+		err = vfs_getattr(h_mnt, h_dentry, st);+	dput(h_dentry);+	if (!err) {+		if (positive)+			au_refresh_iattr(inode, st, h_dentry->d_inode->i_nlink);+		goto out_fill; /* success */+	}+	goto out_unlock;++ out_fill:+	generic_fillattr(inode, st);+ out_unlock:+	di_read_unlock(dentry, AuLock_IR);+ out:+	si_read_unlock(sb);+	return err;+}++/* ---------------------------------------------------------------------- */++static int h_readlink(struct dentry *dentry, int bindex, char __user *buf,+		      int bufsiz)+{+	int err;+	struct super_block *sb;+	struct dentry *h_dentry;++	err = -EINVAL;+	h_dentry = au_h_dptr(dentry, bindex);+	if (unlikely(/* !h_dentry+		     || !h_dentry->d_inode+		     || !h_dentry->d_inode->i_op+		     || */ !h_dentry->d_inode->i_op->readlink))+		goto out;++	err = security_inode_readlink(h_dentry);+	if (unlikely(err))+		goto out;++	sb = dentry->d_sb;+	if (!au_test_ro(sb, bindex, dentry->d_inode)) {+		vfsub_touch_atime(au_sbr_mnt(sb, bindex), h_dentry);+		fsstack_copy_attr_atime(dentry->d_inode, h_dentry->d_inode);+	}+	err = h_dentry->d_inode->i_op->readlink(h_dentry, buf, bufsiz);++ out:+	return err;+}++static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)+{+	int err;++	aufs_read_lock(dentry, AuLock_IR);+	err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz);+	aufs_read_unlock(dentry, AuLock_IR);++	return err;+}++static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd)+{+	int err;+	char *buf;+	mm_segment_t old_fs;++	err = -ENOMEM;+	buf = __getname();+	if (unlikely(!buf))+		goto out;++	aufs_read_lock(dentry, AuLock_IR);+	old_fs = get_fs();+	set_fs(KERNEL_DS);+	err = h_readlink(dentry, au_dbstart(dentry), (char __user *)buf,+			 PATH_MAX);+	set_fs(old_fs);+	aufs_read_unlock(dentry, AuLock_IR);++	if (err >= 0) {+		buf[err] = 0;+		/* will be freed by put_link */+		nd_set_link(nd, buf);+		return NULL; /* success */+	}+	__putname(buf);++ out:+	path_put(&nd->path);+	AuTraceErr(err);+	return ERR_PTR(err);+}++static void aufs_put_link(struct dentry *dentry __maybe_unused,+			  struct nameidata *nd, void *cookie __maybe_unused)+{+	__putname(nd_get_link(nd));+}++/* ---------------------------------------------------------------------- */++static void aufs_truncate_range(struct inode *inode __maybe_unused,+				loff_t start __maybe_unused,+				loff_t end __maybe_unused)+{+	AuUnsupport();+}++/* ---------------------------------------------------------------------- */++struct inode_operations aufs_symlink_iop = {+	.permission	= aufs_permission,+	.setattr	= aufs_setattr,+	.getattr	= aufs_getattr,+	.readlink	= aufs_readlink,+	.follow_link	= aufs_follow_link,+	.put_link	= aufs_put_link+};++struct inode_operations aufs_dir_iop = {+	.create		= aufs_create,+	.lookup		= aufs_lookup,+	.link		= aufs_link,+	.unlink		= aufs_unlink,+	.symlink	= aufs_symlink,+	.mkdir		= aufs_mkdir,+	.rmdir		= aufs_rmdir,+	.mknod		= aufs_mknod,+	.rename		= aufs_rename,++	.permission	= aufs_permission,+	.setattr	= aufs_setattr,+	.getattr	= aufs_getattr+};++struct inode_operations aufs_iop = {+	.permission	= aufs_permission,+	.setattr	= aufs_setattr,+	.getattr	= aufs_getattr,+	.truncate_range	= aufs_truncate_range+};diff -Nur linux-2.6.31.5.orig/fs/aufs/i_op_del.c linux-2.6.31.5/fs/aufs/i_op_del.c--- linux-2.6.31.5.orig/fs/aufs/i_op_del.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/i_op_del.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,468 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * inode operations (del entry)+ */++#include "aufs.h"++/*+ * decide if a new whiteout for @dentry is necessary or not.+ * when it is necessary, prepare the parent dir for the upper branch whose+ * branch index is @bcpup for creation. the actual creation of the whiteout will+ * be done by caller.+ * return value:+ * 0: wh is unnecessary+ * plus: wh is necessary+ * minus: error+ */+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup)+{+	int need_wh, err;+	aufs_bindex_t bstart;+	struct super_block *sb;++	sb = dentry->d_sb;+	bstart = au_dbstart(dentry);+	if (*bcpup < 0) {+		*bcpup = bstart;+		if (au_test_ro(sb, bstart, dentry->d_inode)) {+			err = AuWbrCopyup(au_sbi(sb), dentry);+			*bcpup = err;+			if (unlikely(err < 0))+				goto out;+		}+	} else+		AuDebugOn(bstart < *bcpup+			  || au_test_ro(sb, *bcpup, dentry->d_inode));+	AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart);++	if (*bcpup != bstart) {+		err = au_cpup_dirs(dentry, *bcpup);+		if (unlikely(err))+			goto out;+		need_wh = 1;+	} else {+		aufs_bindex_t old_bend, new_bend, bdiropq = -1;++		old_bend = au_dbend(dentry);+		if (isdir) {+			bdiropq = au_dbdiropq(dentry);+			au_set_dbdiropq(dentry, -1);+		}+		need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0,+					 /*nd*/NULL);+		err = need_wh;+		if (isdir)+			au_set_dbdiropq(dentry, bdiropq);+		if (unlikely(err < 0))+			goto out;+		new_bend = au_dbend(dentry);+		if (!need_wh && old_bend != new_bend) {+			au_set_h_dptr(dentry, new_bend, NULL);+			au_set_dbend(dentry, old_bend);+		}+	}+	AuDbg("need_wh %d\n", need_wh);+	err = need_wh;++ out:+	return err;+}++/*+ * simple tests for the del-entry operations.+ * following the checks in vfs, plus the parent-child relationship.+ */+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,+	       struct dentry *h_parent, int isdir)+{+	int err;+	umode_t h_mode;+	struct dentry *h_dentry, *h_latest;+	struct inode *h_inode;++	h_dentry = au_h_dptr(dentry, bindex);+	h_inode = h_dentry->d_inode;+	if (dentry->d_inode) {+		err = -ENOENT;+		if (unlikely(!h_inode || !h_inode->i_nlink))+			goto out;++		h_mode = h_inode->i_mode;+		if (!isdir) {+			err = -EISDIR;+			if (unlikely(S_ISDIR(h_mode)))+				goto out;+		} else if (unlikely(!S_ISDIR(h_mode))) {+			err = -ENOTDIR;+			goto out;+		}+	} else {+		/* rename(2) case */+		err = -EIO;+		if (unlikely(h_inode))+			goto out;+	}++	err = -ENOENT;+	/* expected parent dir is locked */+	if (unlikely(h_parent != h_dentry->d_parent))+		goto out;+	err = 0;++	/*+	 * rmdir a dir may break the consistency on some filesystem.+	 * let's try heavy test.+	 */+	err = -EACCES;+	if (unlikely(au_test_h_perm(h_parent->d_inode, MAY_EXEC | MAY_WRITE)))+		goto out;++	h_latest = au_sio_lkup_one(&dentry->d_name, h_parent,+				   au_sbr(dentry->d_sb, bindex));+	err = -EIO;+	if (IS_ERR(h_latest))+		goto out;+	if (h_latest == h_dentry)+		err = 0;+	dput(h_latest);++ out:+	return err;+}++/*+ * decide the branch where we operate for @dentry. the branch index will be set+ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent+ * dir for reverting.+ * when a new whiteout is necessary, create it.+ */+static struct dentry*+lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup,+		    struct au_dtime *dt, struct au_pin *pin)+{+	struct dentry *wh_dentry;+	struct super_block *sb;+	struct path h_path;+	int err, need_wh;+	unsigned int udba;+	aufs_bindex_t bcpup;++	need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup);+	wh_dentry = ERR_PTR(need_wh);+	if (unlikely(need_wh < 0))+		goto out;++	sb = dentry->d_sb;+	udba = au_opt_udba(sb);+	bcpup = *rbcpup;+	err = au_pin(pin, dentry, bcpup, udba,+		     AuPin_DI_LOCKED | AuPin_MNT_WRITE);+	wh_dentry = ERR_PTR(err);+	if (unlikely(err))+		goto out;++	h_path.dentry = au_pinned_h_parent(pin);+	if (udba != AuOpt_UDBA_NONE+	    && au_dbstart(dentry) == bcpup) {+		err = au_may_del(dentry, bcpup, h_path.dentry, isdir);+		wh_dentry = ERR_PTR(err);+		if (unlikely(err))+			goto out_unpin;+	}++	h_path.mnt = au_sbr_mnt(sb, bcpup);+	au_dtime_store(dt, au_pinned_parent(pin), &h_path);+	wh_dentry = NULL;+	if (!need_wh)+		goto out; /* success, no need to create whiteout */++	wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry);+	if (!IS_ERR(wh_dentry))+		goto out; /* success */+	/* returns with the parent is locked and wh_dentry is dget-ed */++ out_unpin:+	au_unpin(pin);+ out:+	return wh_dentry;+}++/*+ * when removing a dir, rename it to a unique temporary whiteout-ed name first+ * in order to be revertible and save time for removing many child whiteouts+ * under the dir.+ * returns 1 when there are too many child whiteout and caller should remove+ * them asynchronously. returns 0 when the number of children is enough small to+ * remove now or the branch fs is a remote fs.+ * otherwise return an error.+ */+static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex,+			   struct au_nhash *whlist, struct inode *dir)+{+	int rmdir_later, err, dirwh;+	struct dentry *h_dentry;+	struct super_block *sb;++	sb = dentry->d_sb;+	SiMustAnyLock(sb);+	h_dentry = au_h_dptr(dentry, bindex);+	err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex));+	if (unlikely(err))+		goto out;++	/* stop monitoring */+	au_hin_free(au_hi(dentry->d_inode, bindex));++	if (!au_test_fs_remote(h_dentry->d_sb)) {+		dirwh = au_sbi(sb)->si_dirwh;+		rmdir_later = (dirwh <= 1);+		if (!rmdir_later)+			rmdir_later = au_nhash_test_longer_wh(whlist, bindex,+							      dirwh);+		if (rmdir_later)+			return rmdir_later;+	}++	err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist);+	if (unlikely(err)) {+		AuIOErr("rmdir %.*s, b%d failed, %d. ignored\n",+			AuDLNPair(h_dentry), bindex, err);+		err = 0;+	}++ out:+	return err;+}++/*+ * final procedure for deleting a entry.+ * maintain dentry and iattr.+ */+static void epilog(struct inode *dir, struct dentry *dentry,+		   aufs_bindex_t bindex)+{+	struct inode *inode;++	inode = dentry->d_inode;+	d_drop(dentry);+	inode->i_ctime = dir->i_ctime;++	if (atomic_read(&dentry->d_count) == 1) {+		au_set_h_dptr(dentry, au_dbstart(dentry), NULL);+		au_update_dbstart(dentry);+	}+	if (au_ibstart(dir) == bindex)+		au_cpup_attr_timesizes(dir);+	dir->i_version++;+}++/*+ * when an error happened, remove the created whiteout and revert everything.+ */+static int do_revert(int err, struct inode *dir, aufs_bindex_t bwh,+		     struct dentry *wh_dentry, struct dentry *dentry,+		     struct au_dtime *dt)+{+	int rerr;+	struct path h_path = {+		.dentry	= wh_dentry,+		.mnt	= au_sbr_mnt(dir->i_sb, bwh)+	};++	rerr = au_wh_unlink_dentry(au_h_iptr(dir, bwh), &h_path, dentry);+	if (!rerr) {+		au_set_dbwh(dentry, bwh);+		au_dtime_revert(dt);+		return 0;+	}++	AuIOErr("%.*s reverting whiteout failed(%d, %d)\n",+		AuDLNPair(dentry), err, rerr);+	return -EIO;+}++/* ---------------------------------------------------------------------- */++int aufs_unlink(struct inode *dir, struct dentry *dentry)+{+	int err;+	aufs_bindex_t bwh, bindex, bstart;+	struct au_dtime dt;+	struct au_pin pin;+	struct path h_path;+	struct inode *inode, *h_dir;+	struct dentry *parent, *wh_dentry;++	IMustLock(dir);+	inode = dentry->d_inode;+	if (unlikely(!inode))+		return -ENOENT; /* possible? */+	IMustLock(inode);++	aufs_read_lock(dentry, AuLock_DW);+	parent = dentry->d_parent; /* dir inode is locked */+	di_write_lock_parent(parent);++	bstart = au_dbstart(dentry);+	bwh = au_dbwh(dentry);+	bindex = -1;+	wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt, &pin);+	err = PTR_ERR(wh_dentry);+	if (IS_ERR(wh_dentry))+		goto out;++	h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);+	h_path.dentry = au_h_dptr(dentry, bstart);+	dget(h_path.dentry);+	if (bindex == bstart) {+		h_dir = au_pinned_h_dir(&pin);+		err = vfsub_unlink(h_dir, &h_path, /*force*/0);+	} else {+		/* dir inode is locked */+		h_dir = wh_dentry->d_parent->d_inode;+		IMustLock(h_dir);+		err = 0;+	}++	if (!err) {+		drop_nlink(inode);+		epilog(dir, dentry, bindex);++		/* update target timestamps */+		if (bindex == bstart) {+			vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/+			inode->i_ctime = h_path.dentry->d_inode->i_ctime;+		} else+			/* todo: this timestamp may be reverted later */+			inode->i_ctime = h_dir->i_ctime;+		goto out_unlock; /* success */+	}++	/* revert */+	if (wh_dentry) {+		int rerr;++		rerr = do_revert(err, dir, bwh, wh_dentry, dentry, &dt);+		if (rerr)+			err = rerr;+	}++ out_unlock:+	au_unpin(&pin);+	dput(wh_dentry);+	dput(h_path.dentry);+ out:+	di_write_unlock(parent);+	aufs_read_unlock(dentry, AuLock_DW);+	return err;+}++int aufs_rmdir(struct inode *dir, struct dentry *dentry)+{+	int err, rmdir_later;+	aufs_bindex_t bwh, bindex, bstart;+	struct au_dtime dt;+	struct au_pin pin;+	struct inode *inode;+	struct dentry *parent, *wh_dentry, *h_dentry;+	struct au_whtmp_rmdir *args;++	IMustLock(dir);+	inode = dentry->d_inode;+	err = -ENOENT; /* possible? */+	if (unlikely(!inode))+		goto out;+	IMustLock(inode);++	aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH);+	err = -ENOMEM;+	args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS);+	if (unlikely(!args))+		goto out_unlock;++	parent = dentry->d_parent; /* dir inode is locked */+	di_write_lock_parent(parent);+	err = au_test_empty(dentry, &args->whlist);+	if (unlikely(err))+		goto out_args;++	bstart = au_dbstart(dentry);+	bwh = au_dbwh(dentry);+	bindex = -1;+	wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt, &pin);+	err = PTR_ERR(wh_dentry);+	if (IS_ERR(wh_dentry))+		goto out_args;++	h_dentry = au_h_dptr(dentry, bstart);+	dget(h_dentry);+	rmdir_later = 0;+	if (bindex == bstart) {+		err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir);+		if (err > 0) {+			rmdir_later = err;+			err = 0;+		}+	} else {+		/* stop monitoring */+		au_hin_free(au_hi(inode, bstart));++		/* dir inode is locked */+		IMustLock(wh_dentry->d_parent->d_inode);+		err = 0;+	}++	if (!err) {+		clear_nlink(inode);+		au_set_dbdiropq(dentry, -1);+		epilog(dir, dentry, bindex);++		if (rmdir_later) {+			au_whtmp_kick_rmdir(dir, bstart, h_dentry, args);+			args = NULL;+		}++		goto out_unpin; /* success */+	}++	/* revert */+	AuLabel(revert);+	if (wh_dentry) {+		int rerr;++		rerr = do_revert(err, dir, bwh, wh_dentry, dentry, &dt);+		if (rerr)+			err = rerr;+	}++ out_unpin:+	au_unpin(&pin);+	dput(wh_dentry);+	dput(h_dentry);+ out_args:+	di_write_unlock(parent);+	if (args)+		au_whtmp_rmdir_free(args);+ out_unlock:+	aufs_read_unlock(dentry, AuLock_DW);+ out:+	return err;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/i_op_ren.c linux-2.6.31.5/fs/aufs/i_op_ren.c--- linux-2.6.31.5.orig/fs/aufs/i_op_ren.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/i_op_ren.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,948 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * inode operation (rename entry)+ * todo: this is crazy monster+ */++#include "aufs.h"++enum { AuSRC, AuDST, AuSrcDst };+enum { AuPARENT, AuCHILD, AuParentChild };++#define AuRen_ISDIR	1+#define AuRen_ISSAMEDIR	(1 << 1)+#define AuRen_WHSRC	(1 << 2)+#define AuRen_WHDST	(1 << 3)+#define AuRen_MNT_WRITE	(1 << 4)+#define AuRen_DT_DSTDIR	(1 << 5)+#define AuRen_DIROPQ	(1 << 6)+#define AuRen_CPUP	(1 << 7)+#define au_ftest_ren(flags, name)	((flags) & AuRen_##name)+#define au_fset_ren(flags, name)	{ (flags) |= AuRen_##name; }+#define au_fclr_ren(flags, name)	{ (flags) &= ~AuRen_##name; }++struct au_ren_args {+	struct {+		struct dentry *dentry, *h_dentry, *parent, *h_parent,+			*wh_dentry;+		struct inode *dir, *inode;+		struct au_hinode *hdir;+		struct au_dtime dt[AuParentChild];+		aufs_bindex_t bstart;+	} sd[AuSrcDst];++#define src_dentry	sd[AuSRC].dentry+#define src_dir		sd[AuSRC].dir+#define src_inode	sd[AuSRC].inode+#define src_h_dentry	sd[AuSRC].h_dentry+#define src_parent	sd[AuSRC].parent+#define src_h_parent	sd[AuSRC].h_parent+#define src_wh_dentry	sd[AuSRC].wh_dentry+#define src_hdir	sd[AuSRC].hdir+#define src_h_dir	sd[AuSRC].hdir->hi_inode+#define src_dt		sd[AuSRC].dt+#define src_bstart	sd[AuSRC].bstart++#define dst_dentry	sd[AuDST].dentry+#define dst_dir		sd[AuDST].dir+#define dst_inode	sd[AuDST].inode+#define dst_h_dentry	sd[AuDST].h_dentry+#define dst_parent	sd[AuDST].parent+#define dst_h_parent	sd[AuDST].h_parent+#define dst_wh_dentry	sd[AuDST].wh_dentry+#define dst_hdir	sd[AuDST].hdir+#define dst_h_dir	sd[AuDST].hdir->hi_inode+#define dst_dt		sd[AuDST].dt+#define dst_bstart	sd[AuDST].bstart++	struct dentry *h_trap;+	struct au_branch *br;+	struct au_hinode *src_hinode;+	struct path h_path;+	struct au_nhash whlist;+	aufs_bindex_t btgt;++	unsigned int flags;++	struct au_whtmp_rmdir *thargs;+	struct dentry *h_dst;+};++/* ---------------------------------------------------------------------- */++/*+ * functions for reverting.+ * when an error happened in a single rename systemcall, we should revert+ * everything as if nothing happend.+ * we don't need to revert the copied-up/down the parent dir since they are+ * harmless.+ */++#define RevertFailure(fmt, args...) do { \+	AuIOErr("revert failure: " fmt " (%d, %d)\n", \+		##args, err, rerr); \+	err = -EIO; \+} while (0)++static void au_ren_rev_diropq(int err, struct au_ren_args *a)+{+	int rerr;++	au_hin_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD);+	rerr = au_diropq_remove(a->src_dentry, a->btgt);+	au_hin_imtx_unlock(a->src_hinode);+	if (rerr)+		RevertFailure("remove diropq %.*s", AuDLNPair(a->src_dentry));+}+++static void au_ren_rev_rename(int err, struct au_ren_args *a)+{+	int rerr;++	a->h_path.dentry = au_lkup_one(&a->src_dentry->d_name, a->src_h_parent,+				       a->br, /*nd*/NULL);+	rerr = PTR_ERR(a->h_path.dentry);+	if (IS_ERR(a->h_path.dentry)) {+		RevertFailure("au_lkup_one %.*s", AuDLNPair(a->src_dentry));+		return;+	}++	rerr = vfsub_rename(a->dst_h_dir,+			    au_h_dptr(a->src_dentry, a->btgt),+			    a->src_h_dir, &a->h_path);+	d_drop(a->h_path.dentry);+	dput(a->h_path.dentry);+	/* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */+	if (rerr)+		RevertFailure("rename %.*s", AuDLNPair(a->src_dentry));+}++static void au_ren_rev_cpup(int err, struct au_ren_args *a)+{+	int rerr;++	a->h_path.dentry = a->dst_h_dentry;+	rerr = vfsub_unlink(a->dst_h_dir, &a->h_path, /*force*/0);+	au_set_h_dptr(a->src_dentry, a->btgt, NULL);+	au_set_dbstart(a->src_dentry, a->src_bstart);+	if (rerr)+		RevertFailure("unlink %.*s", AuDLNPair(a->dst_h_dentry));+}+++static void au_ren_rev_whtmp(int err, struct au_ren_args *a)+{+	int rerr;++	a->h_path.dentry = au_lkup_one(&a->dst_dentry->d_name, a->dst_h_parent,+				       a->br, /*nd*/NULL);+	rerr = PTR_ERR(a->h_path.dentry);+	if (IS_ERR(a->h_path.dentry)) {+		RevertFailure("lookup %.*s", AuDLNPair(a->dst_dentry));+		return;+	}+	if (a->h_path.dentry->d_inode) {+		d_drop(a->h_path.dentry);+		dput(a->h_path.dentry);+		return;+	}++	rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path);+	d_drop(a->h_path.dentry);+	dput(a->h_path.dentry);+	if (!rerr) {+		au_set_h_dptr(a->dst_dentry, a->btgt, NULL);+		au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst));+	} else+		RevertFailure("rename %.*s", AuDLNPair(a->h_dst));+}++static void au_ren_rev_whsrc(int err, struct au_ren_args *a)+{+	int rerr;++	a->h_path.dentry = a->src_wh_dentry;+	rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry);+	if (rerr)+		RevertFailure("unlink %.*s", AuDLNPair(a->src_wh_dentry));+}++static void au_ren_rev_drop(struct au_ren_args *a)+{+	struct dentry *d, *h_d;+	int i;+	aufs_bindex_t bend, bindex;++	for (i = 0; i < AuSrcDst; i++) {+		d = a->sd[i].dentry;+		d_drop(d);+		bend = au_dbend(d);+		for (bindex = au_dbstart(d); bindex <= bend; bindex++) {+			h_d = au_h_dptr(d, bindex);+			if (h_d)+				d_drop(h_d);+		}+	}++	au_update_dbstart(a->dst_dentry);+	if (a->thargs)+		d_drop(a->h_dst);+}+#undef RevertFailure++/* ---------------------------------------------------------------------- */++/*+ * when we have to copyup the renaming entry, do it with the rename-target name+ * in order to minimize the cost (the later actual rename is unnecessary).+ * otherwise rename it on the target branch.+ */+static int au_ren_or_cpup(struct au_ren_args *a)+{+	int err;+	struct dentry *d;++	d = a->src_dentry;+	if (au_dbstart(d) == a->btgt) {+		a->h_path.dentry = a->dst_h_dentry;+		if (au_ftest_ren(a->flags, DIROPQ)+		    && au_dbdiropq(d) == a->btgt)+			au_fclr_ren(a->flags, DIROPQ);+		AuDebugOn(au_dbstart(d) != a->btgt);+		err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt),+				   a->dst_h_dir, &a->h_path);+	} else {+		struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex;++		au_fset_ren(a->flags, CPUP);+		mutex_lock_nested(h_mtx, AuLsc_I_CHILD);+		au_set_dbstart(d, a->btgt);+		au_set_h_dptr(d, a->btgt, dget(a->dst_h_dentry));+		err = au_sio_cpup_single(d, a->btgt, a->src_bstart, -1,+					 !AuCpup_DTIME, a->dst_parent);+		if (unlikely(err)) {+			au_set_h_dptr(d, a->btgt, NULL);+			au_set_dbstart(d, a->src_bstart);+		}+		mutex_unlock(h_mtx);+	}++	return err;+}++/* cf. aufs_rmdir() */+static int au_ren_del_whtmp(struct au_ren_args *a)+{+	int err;+	struct inode *dir;++	dir = a->dst_dir;+	SiMustAnyLock(dir->i_sb);+	if (!au_nhash_test_longer_wh(&a->whlist, a->btgt,+				     au_sbi(dir->i_sb)->si_dirwh)+	    || au_test_fs_remote(a->h_dst->d_sb)) {+		err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist);+		if (unlikely(err))+			AuWarn("failed removing whtmp dir %.*s (%d), "+			       "ignored.\n", AuDLNPair(a->h_dst), err);+	} else {+		au_nhash_wh_free(&a->thargs->whlist);+		a->thargs->whlist = a->whlist;+		a->whlist.nh_num = 0;+		au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs);+		dput(a->h_dst);+		a->thargs = NULL;+	}++	return 0;+}++/* make it 'opaque' dir. */+static int au_ren_diropq(struct au_ren_args *a)+{+	int err;+	struct dentry *diropq;++	err = 0;+	a->src_hinode = au_hi(a->src_inode, a->btgt);+	au_hin_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD);+	diropq = au_diropq_create(a->src_dentry, a->btgt);+	au_hin_imtx_unlock(a->src_hinode);+	if (IS_ERR(diropq))+		err = PTR_ERR(diropq);+	dput(diropq);++	return err;+}++static int do_rename(struct au_ren_args *a)+{+	int err;+	struct dentry *d, *h_d;++	/* prepare workqueue args for asynchronous rmdir */+	h_d = a->dst_h_dentry;+	if (au_ftest_ren(a->flags, ISDIR) && h_d->d_inode) {+		err = -ENOMEM;+		a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS);+		if (unlikely(!a->thargs))+			goto out;+		a->h_dst = dget(h_d);+	}++	/* create whiteout for src_dentry */+	if (au_ftest_ren(a->flags, WHSRC)) {+		a->src_wh_dentry+			= au_wh_create(a->src_dentry, a->btgt, a->src_h_parent);+		err = PTR_ERR(a->src_wh_dentry);+		if (IS_ERR(a->src_wh_dentry))+			goto out_thargs;+	}++	/* lookup whiteout for dentry */+	if (au_ftest_ren(a->flags, WHDST)) {+		h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name,+				 a->br);+		err = PTR_ERR(h_d);+		if (IS_ERR(h_d))+			goto out_whsrc;+		if (!h_d->d_inode)+			dput(h_d);+		else+			a->dst_wh_dentry = h_d;+	}++	/* rename dentry to tmpwh */+	if (a->thargs) {+		err = au_whtmp_ren(a->dst_h_dentry, a->br);+		if (unlikely(err))+			goto out_whdst;++		d = a->dst_dentry;+		au_set_h_dptr(d, a->btgt, NULL);+		err = au_lkup_neg(d, a->btgt);+		if (unlikely(err))+			goto out_whtmp;+		a->dst_h_dentry = au_h_dptr(d, a->btgt);+	}++	/* cpup src */+	if (a->dst_h_dentry->d_inode && a->src_bstart != a->btgt) {+		struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex;++		mutex_lock_nested(h_mtx, AuLsc_I_CHILD);+		err = au_sio_cpup_simple(a->src_dentry, a->btgt, -1,+					 !AuCpup_DTIME);+		mutex_unlock(h_mtx);+		if (unlikely(err))+			goto out_whtmp;+	}++	/* rename by vfs_rename or cpup */+	d = a->dst_dentry;+	if (au_ftest_ren(a->flags, ISDIR)+	    && (a->dst_wh_dentry+		|| au_dbdiropq(d) == a->btgt+		/* hide the lower to keep xino */+		|| a->btgt < au_dbend(d)+		|| au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ)))+		au_fset_ren(a->flags, DIROPQ);+	err = au_ren_or_cpup(a);+	if (unlikely(err))+		/* leave the copied-up one */+		goto out_whtmp;++	/* make dir opaque */+	if (au_ftest_ren(a->flags, DIROPQ)) {+		err = au_ren_diropq(a);+		if (unlikely(err))+			goto out_rename;+	}++	/* update target timestamps */+	AuDebugOn(au_dbstart(a->src_dentry) != a->btgt);+	a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt);+	vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/+	a->src_inode->i_ctime = a->h_path.dentry->d_inode->i_ctime;++	/* remove whiteout for dentry */+	if (a->dst_wh_dentry) {+		a->h_path.dentry = a->dst_wh_dentry;+		err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path,+					  a->dst_dentry);+		if (unlikely(err))+			goto out_diropq;+	}++	/* remove whtmp */+	if (a->thargs)+		au_ren_del_whtmp(a); /* ignore this error */++	err = 0;+	goto out_success;++ out_diropq:+	if (au_ftest_ren(a->flags, DIROPQ))+		au_ren_rev_diropq(err, a);+ out_rename:+	if (!au_ftest_ren(a->flags, CPUP))+		au_ren_rev_rename(err, a);+	else+		au_ren_rev_cpup(err, a);+ out_whtmp:+	if (a->thargs)+		au_ren_rev_whtmp(err, a);+ out_whdst:+	dput(a->dst_wh_dentry);+	a->dst_wh_dentry = NULL;+ out_whsrc:+	if (a->src_wh_dentry)+		au_ren_rev_whsrc(err, a);+	au_ren_rev_drop(a);+ out_success:+	dput(a->src_wh_dentry);+	dput(a->dst_wh_dentry);+ out_thargs:+	if (a->thargs) {+		dput(a->h_dst);+		au_whtmp_rmdir_free(a->thargs);+		a->thargs = NULL;+	}+ out:+	return err;+}++/* ---------------------------------------------------------------------- */++/*+ * test if @dentry dir can be rename destination or not.+ * success means, it is a logically empty dir.+ */+static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist)+{+	return au_test_empty(dentry, whlist);+}++/*+ * test if @dentry dir can be rename source or not.+ * if it can, return 0 and @children is filled.+ * success means,+ * - it is a logically empty dir.+ * - or, it exists on writable branch and has no children including whiteouts+ *       on the lower branch.+ */+static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt)+{+	int err;+	aufs_bindex_t bstart;++	bstart = au_dbstart(dentry);+	if (bstart != btgt) {+		struct au_nhash whlist;++		SiMustAnyLock(dentry->d_sb);+		err = au_nhash_alloc(&whlist, au_sbi(dentry->d_sb)->si_rdhash,+				     GFP_NOFS);+		if (unlikely(err))+			goto out;+		err = au_test_empty(dentry, &whlist);+		au_nhash_wh_free(&whlist);+		goto out;+	}++	if (bstart == au_dbtaildir(dentry))+		return 0; /* success */++	err = au_test_empty_lower(dentry);++ out:+	if (err == -ENOTEMPTY) {+		AuWarn1("renaming dir who has child(ren) on multiple branches,"+			" is not supported\n");+		err = -EXDEV;+	}+	return err;+}++/* side effect: sets whlist and h_dentry */+static int au_ren_may_dir(struct au_ren_args *a)+{+	int err;+	struct dentry *d;++	d = a->dst_dentry;+	SiMustAnyLock(d->d_sb);+	err = au_nhash_alloc(&a->whlist, au_sbi(d->d_sb)->si_rdhash, GFP_NOFS);+	if (unlikely(err))+		goto out;++	err = 0;+	if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) {+		au_set_dbstart(d, a->dst_bstart);+		err = may_rename_dstdir(d, &a->whlist);+		au_set_dbstart(d, a->btgt);+	}+	a->dst_h_dentry = au_h_dptr(d, au_dbstart(d));+	if (unlikely(err))+		goto out;++	d = a->src_dentry;+	a->src_h_dentry = au_h_dptr(d, au_dbstart(d));+	if (au_ftest_ren(a->flags, ISDIR)) {+		err = may_rename_srcdir(d, a->btgt);+		if (unlikely(err)) {+			au_nhash_wh_free(&a->whlist);+			a->whlist.nh_num = 0;+		}+	}+ out:+	return err;+}++/* ---------------------------------------------------------------------- */++/*+ * simple tests for rename.+ * following the checks in vfs, plus the parent-child relationship.+ */+static int au_may_ren(struct au_ren_args *a)+{+	int err, isdir;+	struct inode *h_inode;++	if (a->src_bstart == a->btgt) {+		err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent,+				 au_ftest_ren(a->flags, ISDIR));+		if (unlikely(err))+			goto out;+		err = -EINVAL;+		if (unlikely(a->src_h_dentry == a->h_trap))+			goto out;+	}++	err = 0;+	if (a->dst_bstart != a->btgt)+		goto out;++	err = -EIO;+	h_inode = a->dst_h_dentry->d_inode;+	isdir = !!au_ftest_ren(a->flags, ISDIR);+	if (!a->dst_dentry->d_inode) {+		if (unlikely(h_inode))+			goto out;+		err = au_may_add(a->dst_dentry, a->btgt, a->dst_h_parent,+				 isdir);+	} else {+		if (unlikely(!h_inode || !h_inode->i_nlink))+			goto out;+		err = au_may_del(a->dst_dentry, a->btgt, a->dst_h_parent,+				 isdir);+		if (unlikely(err))+			goto out;+		err = -ENOTEMPTY;+		if (unlikely(a->dst_h_dentry == a->h_trap))+			goto out;+		err = 0;+	}++ out:+	if (unlikely(err == -ENOENT || err == -EEXIST))+		err = -EIO;+	return err;+}++/* ---------------------------------------------------------------------- */++/*+ * locking order+ * (VFS)+ * - src_dir and dir by lock_rename()+ * - inode if exitsts+ * (aufs)+ * - lock all+ *   + src_dentry and dentry by aufs_read_and_write_lock2() which calls,+ *     + si_read_lock+ *     + di_write_lock2_child()+ *       + di_write_lock_child()+ *	   + ii_write_lock_child()+ *       + di_write_lock_child2()+ *	   + ii_write_lock_child2()+ *     + src_parent and parent+ *       + di_write_lock_parent()+ *	   + ii_write_lock_parent()+ *       + di_write_lock_parent2()+ *	   + ii_write_lock_parent2()+ *   + lower src_dir and dir by vfsub_lock_rename()+ *   + verify the every relationships between child and parent. if any+ *     of them failed, unlock all and return -EBUSY.+ */+static void au_ren_unlock(struct au_ren_args *a)+{+	struct super_block *sb;++	sb = a->dst_dentry->d_sb;+	if (au_ftest_ren(a->flags, MNT_WRITE))+		mnt_drop_write(a->br->br_mnt);+	vfsub_unlock_rename(a->src_h_parent, a->src_hdir,+			    a->dst_h_parent, a->dst_hdir);+}++static int au_ren_lock(struct au_ren_args *a)+{+	int err;+	unsigned int udba;++	err = 0;+	a->src_h_parent = au_h_dptr(a->src_parent, a->btgt);+	a->src_hdir = au_hi(a->src_dir, a->btgt);+	a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt);+	a->dst_hdir = au_hi(a->dst_dir, a->btgt);+	a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir,+				      a->dst_h_parent, a->dst_hdir);+	udba = au_opt_udba(a->src_dentry->d_sb);+	if (unlikely(a->src_hdir->hi_inode != a->src_h_parent->d_inode+		     || a->dst_hdir->hi_inode != a->dst_h_parent->d_inode))+		err = au_busy_or_stale();+	if (!err && au_dbstart(a->src_dentry) == a->btgt)+		err = au_h_verify(a->src_h_dentry, udba,+				  a->src_h_parent->d_inode, a->src_h_parent,+				  a->br);+	if (!err && au_dbstart(a->dst_dentry) == a->btgt)+		err = au_h_verify(a->dst_h_dentry, udba,+				  a->dst_h_parent->d_inode, a->dst_h_parent,+				  a->br);+	if (!err) {+		err = mnt_want_write(a->br->br_mnt);+		if (unlikely(err))+			goto out_unlock;+		au_fset_ren(a->flags, MNT_WRITE);+		goto out; /* success */+	}++	err = au_busy_or_stale();++ out_unlock:+	au_ren_unlock(a);+ out:+	return err;+}++/* ---------------------------------------------------------------------- */++static void au_ren_refresh_dir(struct au_ren_args *a)+{+	struct inode *dir;++	dir = a->dst_dir;+	dir->i_version++;+	if (au_ftest_ren(a->flags, ISDIR)) {+		/* is this updating defined in POSIX? */+		au_cpup_attr_timesizes(a->src_inode);+		au_cpup_attr_nlink(dir, /*force*/1);+		if (a->dst_inode) {+			clear_nlink(a->dst_inode);+			au_cpup_attr_timesizes(a->dst_inode);+		}+	}+	if (au_ibstart(dir) == a->btgt)+		au_cpup_attr_timesizes(dir);++	if (au_ftest_ren(a->flags, ISSAMEDIR))+		return;++	dir = a->src_dir;+	dir->i_version++;+	if (au_ftest_ren(a->flags, ISDIR))+		au_cpup_attr_nlink(dir, /*force*/1);+	if (au_ibstart(dir) == a->btgt)+		au_cpup_attr_timesizes(dir);+}++static void au_ren_refresh(struct au_ren_args *a)+{+	aufs_bindex_t bend, bindex;+	struct dentry *d, *h_d;+	struct inode *i, *h_i;+	struct super_block *sb;++	d = a->src_dentry;+	au_set_dbwh(d, -1);+	bend = au_dbend(d);+	for (bindex = a->btgt + 1; bindex <= bend; bindex++) {+		h_d = au_h_dptr(d, bindex);+		if (h_d)+			au_set_h_dptr(d, bindex, NULL);+	}+	au_set_dbend(d, a->btgt);++	sb = d->d_sb;+	i = a->src_inode;+	if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i))+		return; /* success */++	bend = au_ibend(i);+	for (bindex = a->btgt + 1; bindex <= bend; bindex++) {+		h_i = au_h_iptr(i, bindex);+		if (h_i) {+			au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0);+			/* ignore this error */+			au_set_h_iptr(i, bindex, NULL, 0);+		}+	}+	au_set_ibend(i, a->btgt);+}++/* ---------------------------------------------------------------------- */++/* mainly for link(2) and rename(2) */+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt)+{+	aufs_bindex_t bdiropq, bwh;+	struct dentry *parent;+	struct au_branch *br;++	parent = dentry->d_parent;+	IMustLock(parent->d_inode); /* dir is locked */++	bdiropq = au_dbdiropq(parent);+	bwh = au_dbwh(dentry);+	br = au_sbr(dentry->d_sb, btgt);+	if (au_br_rdonly(br)+	    || (0 <= bdiropq && bdiropq < btgt)+	    || (0 <= bwh && bwh < btgt))+		btgt = -1;++	AuDbg("btgt %d\n", btgt);+	return btgt;+}++/* sets src_bstart, dst_bstart and btgt */+static int au_ren_wbr(struct au_ren_args *a)+{+	int err;+	struct au_wr_dir_args wr_dir_args = {+		/* .force_btgt	= -1, */+		.flags		= AuWrDir_ADD_ENTRY+	};++	a->src_bstart = au_dbstart(a->src_dentry);+	a->dst_bstart = au_dbstart(a->dst_dentry);+	if (au_ftest_ren(a->flags, ISDIR))+		au_fset_wrdir(wr_dir_args.flags, ISDIR);+	wr_dir_args.force_btgt = a->src_bstart;+	if (a->dst_inode && a->dst_bstart < a->src_bstart)+		wr_dir_args.force_btgt = a->dst_bstart;+	wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt);+	err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args);+	a->btgt = err;++	return err;+}++static void au_ren_dt(struct au_ren_args *a)+{+	a->h_path.dentry = a->src_h_parent;+	au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path);+	if (!au_ftest_ren(a->flags, ISSAMEDIR)) {+		a->h_path.dentry = a->dst_h_parent;+		au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path);+	}++	au_fclr_ren(a->flags, DT_DSTDIR);+	if (!au_ftest_ren(a->flags, ISDIR))+		return;++	a->h_path.dentry = a->src_h_dentry;+	au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path);+	if (a->dst_h_dentry->d_inode) {+		au_fset_ren(a->flags, DT_DSTDIR);+		a->h_path.dentry = a->dst_h_dentry;+		au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path);+	}+}++static void au_ren_rev_dt(int err, struct au_ren_args *a)+{+	struct dentry *h_d;+	struct mutex *h_mtx;++	au_dtime_revert(a->src_dt + AuPARENT);+	if (!au_ftest_ren(a->flags, ISSAMEDIR))+		au_dtime_revert(a->dst_dt + AuPARENT);++	if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) {+		h_d = a->src_dt[AuCHILD].dt_h_path.dentry;+		h_mtx = &h_d->d_inode->i_mutex;+		mutex_lock_nested(h_mtx, AuLsc_I_CHILD);+		au_dtime_revert(a->src_dt + AuCHILD);+		mutex_unlock(h_mtx);++		if (au_ftest_ren(a->flags, DT_DSTDIR)) {+			h_d = a->dst_dt[AuCHILD].dt_h_path.dentry;+			h_mtx = &h_d->d_inode->i_mutex;+			mutex_lock_nested(h_mtx, AuLsc_I_CHILD);+			au_dtime_revert(a->dst_dt + AuCHILD);+			mutex_unlock(h_mtx);+		}+	}+}++/* ---------------------------------------------------------------------- */++int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,+		struct inode *_dst_dir, struct dentry *_dst_dentry)+{+	int err;+	/* reduce stack space */+	struct au_ren_args *a;++	IMustLock(_src_dir);+	IMustLock(_dst_dir);++	err = -ENOMEM;+	BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE);+	a = kzalloc(sizeof(*a), GFP_NOFS);+	if (unlikely(!a))+		goto out;++	a->src_dir = _src_dir;+	a->src_dentry = _src_dentry;+	a->src_inode = a->src_dentry->d_inode;+	a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */+	a->dst_dir = _dst_dir;+	a->dst_dentry = _dst_dentry;+	a->dst_inode = a->dst_dentry->d_inode;+	a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */+	if (a->dst_inode) {+		IMustLock(a->dst_inode);+		au_igrab(a->dst_inode);+	}++	err = -ENOTDIR;+	if (S_ISDIR(a->src_inode->i_mode)) {+		au_fset_ren(a->flags, ISDIR);+		if (unlikely(a->dst_inode && !S_ISDIR(a->dst_inode->i_mode)))+			goto out_free;+		aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,+					  AuLock_DIR | AuLock_FLUSH);+	} else+		aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,+					  AuLock_FLUSH);++	au_fset_ren(a->flags, ISSAMEDIR); /* temporary */+	di_write_lock_parent(a->dst_parent);++	/* which branch we process */+	err = au_ren_wbr(a);+	if (unlikely(err < 0))+		goto out_unlock;+	a->br = au_sbr(a->dst_dentry->d_sb, a->btgt);+	a->h_path.mnt = a->br->br_mnt;++	/* are they available to be renamed */+	err = au_ren_may_dir(a);+	if (unlikely(err))+		goto out_unlock;++	/* prepare the writable parent dir on the same branch */+	if (a->dst_bstart == a->btgt) {+		au_fset_ren(a->flags, WHDST);+	} else {+		err = au_cpup_dirs(a->dst_dentry, a->btgt);+		if (unlikely(err))+			goto out_children;+	}++	if (a->src_dir != a->dst_dir) {+		/*+		 * this temporary unlock is safe,+		 * because both dir->i_mutex are locked.+		 */+		di_write_unlock(a->dst_parent);+		di_write_lock_parent(a->src_parent);+		err = au_wr_dir_need_wh(a->src_dentry,+					au_ftest_ren(a->flags, ISDIR),+					&a->btgt);+		di_write_unlock(a->src_parent);+		di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1);+		au_fclr_ren(a->flags, ISSAMEDIR);+	} else+		err = au_wr_dir_need_wh(a->src_dentry,+					au_ftest_ren(a->flags, ISDIR),+					&a->btgt);+	if (unlikely(err < 0))+		goto out_children;+	if (err)+		au_fset_ren(a->flags, WHSRC);++	/* lock them all */+	err = au_ren_lock(a);+	if (unlikely(err))+		goto out_children;++	if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) {+		err = au_may_ren(a);+		if (unlikely(err))+			goto out_hdir;+	}++	/* store timestamps to be revertible */+	au_ren_dt(a);++	/* here we go */+	err = do_rename(a);+	if (unlikely(err))+		goto out_dt;++	/* update dir attributes */+	au_ren_refresh_dir(a);++	/* dput/iput all lower dentries */+	au_ren_refresh(a);++	goto out_hdir; /* success */++ out_dt:+	au_ren_rev_dt(err, a);+ out_hdir:+	au_ren_unlock(a);+ out_children:+	au_nhash_wh_free(&a->whlist);+ out_unlock:+	if (unlikely(err && au_ftest_ren(a->flags, ISDIR))) {+		au_update_dbstart(a->dst_dentry);+		d_drop(a->dst_dentry);+	}+	if (!err)+		d_move(a->src_dentry, a->dst_dentry);+	if (au_ftest_ren(a->flags, ISSAMEDIR))+		di_write_unlock(a->dst_parent);+	else+		di_write_unlock2(a->src_parent, a->dst_parent);+	aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry);+ out_free:+	iput(a->dst_inode);+	if (a->thargs)+		au_whtmp_rmdir_free(a->thargs);+	kfree(a);+ out:+	return err;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/Kconfig linux-2.6.31.5/fs/aufs/Kconfig--- linux-2.6.31.5.orig/fs/aufs/Kconfig	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/Kconfig	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,132 @@+config AUFS_FS+	tristate "Aufs (Advanced multi layered unification filesystem) support"+	depends on EXPERIMENTAL+	help+	Aufs is a stackable unification filesystem such as Unionfs,+	which unifies several directories and provides a merged single+	directory.+	In the early days, aufs was entirely re-designed and+	re-implemented Unionfs Version 1.x series. Introducing many+	original ideas, approaches and improvements, it becomes totally+	different from Unionfs while keeping the basic features.++if AUFS_FS+choice+	prompt "Maximum number of branches"+	default AUFS_BRANCH_MAX_127+	help+	Specifies the maximum number of branches (or member directories)+	in a single aufs. The larger value consumes more system+	resources and has a minor impact to performance.+config AUFS_BRANCH_MAX_127+	bool "127"+	help+	Specifies the maximum number of branches (or member directories)+	in a single aufs. The larger value consumes more system+	resources and has a minor impact to performance.+config AUFS_BRANCH_MAX_511+	bool "511"+	help+	Specifies the maximum number of branches (or member directories)+	in a single aufs. The larger value consumes more system+	resources and has a minor impact to performance.+config AUFS_BRANCH_MAX_1023+	bool "1023"+	help+	Specifies the maximum number of branches (or member directories)+	in a single aufs. The larger value consumes more system+	resources and has a minor impact to performance.+config AUFS_BRANCH_MAX_32767+	bool "32767"+	help+	Specifies the maximum number of branches (or member directories)+	in a single aufs. The larger value consumes more system+	resources and has a minor impact to performance.+endchoice++config AUFS_HINOTIFY+	bool "Use inotify to detect actions on a branch"+	depends on INOTIFY+	help+	If you want to modify files on branches directly, eg. bypassing aufs,+	and want aufs to detect the changes of them fully, then enable this+	option and use 'udba=inotify' mount option.+	It will have a negative impact to the performance.+	See detail in aufs.5.++config AUFS_EXPORT+	bool "NFS-exportable aufs"+	depends on (AUFS_FS = y && EXPORTFS = y) || (AUFS_FS = m && EXPORTFS)+	help+	If you want to export your mounted aufs via NFS, then enable this+	option. There are several requirements for this configuration.+	See detail in aufs.5.++config AUFS_SHWH+	bool "Show whiteouts"+	help+	If you want to make the whiteouts in aufs visible, then enable+	this option and specify 'shwh' mount option. Although it may+	sounds like philosophy or something, but in technically it+	simply shows the name of whiteout with keeping its behaviour.++config AUFS_BR_RAMFS+	bool "Ramfs (initramfs/rootfs) as an aufs branch"+	help+	If you want to use ramfs as an aufs branch fs, then enable this+	option. Generally tmpfs is recommended.+	Aufs prohibited them to be a branch fs by default, because+	initramfs becomes unusable after switch_root or something+	generally. If you sets initramfs as an aufs branch and boot your+	system by switch_root, you will meet a problem easily since the+	files in initramfs may be inaccessible.+	Unless you are going to use ramfs as an aufs branch fs without+	switch_root or something, leave it N.++config AUFS_BR_FUSE+	bool "Fuse fs as an aufs branch"+	depends on FUSE_FS+	select AUFS_POLL+	help+	If you want to use fuse-based userspace filesystem as an aufs+	branch fs, then enable this option.+	It implements the internal poll(2) operation which is+	implemented by fuse only (curretnly).++config AUFS_DEBUG+	bool "Debug aufs"+	help+	Enable this to compile aufs internal debug code.+	It will have a negative impact to the performance.++config AUFS_MAGIC_SYSRQ+	bool+	depends on AUFS_DEBUG && MAGIC_SYSRQ+	default y+	help+	Automatic configuration for internal use.+	When aufs supports Magic SysRq, enabled automatically.++config AUFS_BDEV_LOOP+	bool+	depends on BLK_DEV_LOOP+	default y+	help+	Automatic configuration for internal use.+	Convert =[ym] into =y.++config AUFS_INO_T_64+	bool+	depends on AUFS_EXPORT+	depends on 64BIT && !(ALPHA || S390)+	default y+	help+	Automatic configuration for internal use.+	/* typedef unsigned long/int __kernel_ino_t */+	/* alpha and s390x are int */++config AUFS_POLL+	bool+	help+	Automatic configuration for internal use.+endifdiff -Nur linux-2.6.31.5.orig/fs/aufs/loop.c linux-2.6.31.5/fs/aufs/loop.c--- linux-2.6.31.5.orig/fs/aufs/loop.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/loop.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,55 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * support for loopback block device as a branch+ */++#include <linux/loop.h>+#include "aufs.h"++/*+ * test if two lower dentries have overlapping branches.+ */+int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_d1,+			     struct dentry *h_d2)+{+	struct inode *h_inode;+	struct loop_device *l;++	h_inode = h_d1->d_inode;+	if (MAJOR(h_inode->i_sb->s_dev) != LOOP_MAJOR)+		return 0;++	l = h_inode->i_sb->s_bdev->bd_disk->private_data;+	h_d1 = l->lo_backing_file->f_dentry;+	/* h_d1 can be local NFS. in this case aufs cannot detect the loop */+	if (unlikely(h_d1->d_sb == sb))+		return 1;+	return !!au_test_subdir(h_d1, h_d2);+}++/* true if a kernel thread named 'loop[0-9].*' accesses a file */+int au_test_loopback_kthread(void)+{+	const char c = current->comm[4];++	return current->mm == NULL+	       && '0' <= c && c <= '9'+	       && strncmp(current->comm, "loop", 4) == 0;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/loop.h linux-2.6.31.5/fs/aufs/loop.h--- linux-2.6.31.5.orig/fs/aufs/loop.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/loop.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,51 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * support for loopback mount as a branch+ */++#ifndef __AUFS_LOOP_H__+#define __AUFS_LOOP_H__++#ifdef __KERNEL__++struct dentry;+struct super_block;++#ifdef CONFIG_AUFS_BDEV_LOOP+/* loop.c */+int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_d1,+			     struct dentry *h_d2);+int au_test_loopback_kthread(void);+#else+static inline+int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_d1,+			     struct dentry *h_d2)+{+	return 0;+}++static inline int au_test_loopback_kthread(void)+{+	return 0;+}+#endif /* BLK_DEV_LOOP */++#endif /* __KERNEL__ */+#endif /* __AUFS_LOOP_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/magic.mk linux-2.6.31.5/fs/aufs/magic.mk--- linux-2.6.31.5.orig/fs/aufs/magic.mk	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/magic.mk	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,52 @@++# defined in ${srctree}/fs/fuse/inode.c+# tristate+ifdef CONFIG_FUSE_FS+ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546+endif++# defined in ${srctree}/fs/ocfs2/ocfs2_fs.h+# tristate+ifdef CONFIG_OCFS2_FS+ccflags-y += -DOCFS2_SUPER_MAGIC=0x7461636f+endif++# defined in ${srctree}/fs/ocfs2/dlm/userdlm.h+# tristate+ifdef CONFIG_OCFS2_FS_O2CB+ccflags-y += -DDLMFS_MAGIC=0x76a9f425+endif++# defined in ${srctree}/fs/ramfs/inode.c+# always true+ccflags-y += -DRAMFS_MAGIC=0x858458f6++# defined in ${srctree}/fs/cifs/cifsfs.c+# tristate+ifdef CONFIG_CIFS_FS+ccflags-y += -DCIFS_MAGIC_NUMBER=0xFF534D42+endif++# defined in ${srctree}/fs/xfs/xfs_sb.h+# tristate+ifdef CONFIG_XFS_FS+ccflags-y += -DXFS_SB_MAGIC=0x58465342+endif++# defined in ${srctree}/fs/configfs/mount.c+# tristate+ifdef CONFIG_CONFIGFS_FS+ccflags-y += -DCONFIGFS_MAGIC=0x62656570+endif++# defined in ${srctree}/fs/9p/v9fs.h+# tristate+ifdef CONFIG_9P_FS+ccflags-y += -DV9FS_MAGIC=0x01021997+endif++# defined in ${srctree}/fs/ubifs/ubifs.h+# tristate+ifdef CONFIG_UBIFS_FS+ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905+endifdiff -Nur linux-2.6.31.5.orig/fs/aufs/Makefile linux-2.6.31.5/fs/aufs/Makefile--- linux-2.6.31.5.orig/fs/aufs/Makefile	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/Makefile	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,23 @@++include ${src}/magic.mk+-include ${src}/priv_def.mk++obj-$(CONFIG_AUFS_FS) += aufs.o+aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \+	wkq.o vfsub.o dcsub.o \+	cpup.o whout.o plink.o wbr_policy.o \+	dinfo.o dentry.o \+	finfo.o file.o f_op.o \+	dir.o vdir.o \+	iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \+	ioctl.o++# all are boolean+aufs-$(CONFIG_SYSFS) += sysfs.o+aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o+aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o+aufs-$(CONFIG_AUFS_HINOTIFY) += hinotify.o+aufs-$(CONFIG_AUFS_EXPORT) += export.o+aufs-$(CONFIG_AUFS_POLL) += poll.o+aufs-$(CONFIG_AUFS_DEBUG) += debug.o+aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.odiff -Nur linux-2.6.31.5.orig/fs/aufs/module.c linux-2.6.31.5/fs/aufs/module.c--- linux-2.6.31.5.orig/fs/aufs/module.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/module.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,173 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * module global variables and operations+ */++#include <linux/module.h>+#include <linux/seq_file.h>+#include "aufs.h"++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp)+{+	if (new_sz <= nused)+		return p;++	p = krealloc(p, new_sz, gfp);+	if (p)+		memset(p + nused, 0, new_sz - nused);+	return p;+}++/* ---------------------------------------------------------------------- */++/*+ * aufs caches+ */+struct kmem_cache *au_cachep[AuCache_Last];+static int __init au_cache_init(void)+{+	au_cachep[AuCache_DINFO] = AuCache(au_dinfo);+	if (au_cachep[AuCache_DINFO])+		au_cachep[AuCache_ICNTNR] = AuCache(au_icntnr);+	if (au_cachep[AuCache_ICNTNR])+		au_cachep[AuCache_FINFO] = AuCache(au_finfo);+	if (au_cachep[AuCache_FINFO])+		au_cachep[AuCache_VDIR] = AuCache(au_vdir);+	if (au_cachep[AuCache_VDIR])+		au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr);+	if (au_cachep[AuCache_DEHSTR])+		return 0;++	return -ENOMEM;+}++static void au_cache_fin(void)+{+	int i;+	for (i = 0; i < AuCache_Last; i++)+		if (au_cachep[i]) {+			kmem_cache_destroy(au_cachep[i]);+			au_cachep[i] = NULL;+		}+}++/* ---------------------------------------------------------------------- */++int au_dir_roflags;++/*+ * functions for module interface.+ */+MODULE_LICENSE("GPL");+/* MODULE_LICENSE("GPL v2"); */+MODULE_AUTHOR("Junjiro R. Okajima <aufs-users@lists.sourceforge.net>");+MODULE_DESCRIPTION(AUFS_NAME+	" -- Advanced multi layered unification filesystem");+MODULE_VERSION(AUFS_VERSION);++/* it should be 'byte', but param_set_byte() prints it by "%c" */+short aufs_nwkq = AUFS_NWKQ_DEF;+MODULE_PARM_DESC(nwkq, "the number of workqueue thread, " AUFS_WKQ_NAME);+module_param_named(nwkq, aufs_nwkq, short, S_IRUGO);++/* this module parameter has no meaning when SYSFS is disabled */+int sysaufs_brs = 1;+MODULE_PARM_DESC(brs, "use <sysfs>/fs/aufs/si_*/brN");+module_param_named(brs, sysaufs_brs, int, S_IRUGO);++/* ---------------------------------------------------------------------- */++static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */++int au_seq_path(struct seq_file *seq, struct path *path)+{+	return seq_path(seq, path, au_esc_chars);+}++/* ---------------------------------------------------------------------- */++static int __init aufs_init(void)+{+	int err, i;+	char *p;++	p = au_esc_chars;+	for (i = 1; i <= ' '; i++)+		*p++ = i;+	*p++ = '\\';+	*p++ = '\x7f';+	*p = 0;++	au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE);++	sysaufs_brs_init();+	au_debug_init();++	err = -EINVAL;+	if (unlikely(aufs_nwkq <= 0))+		goto out;++	err = sysaufs_init();+	if (unlikely(err))+		goto out;+	err = au_wkq_init();+	if (unlikely(err))+		goto out_sysaufs;+	err = au_hinotify_init();+	if (unlikely(err))+		goto out_wkq;+	err = au_sysrq_init();+	if (unlikely(err))+		goto out_hin;+	err = au_cache_init();+	if (unlikely(err))+		goto out_sysrq;+	err = register_filesystem(&aufs_fs_type);+	if (unlikely(err))+		goto out_cache;+	pr_info(AUFS_NAME " " AUFS_VERSION "\n");+	goto out; /* success */++ out_cache:+	au_cache_fin();+ out_sysrq:+	au_sysrq_fin();+ out_hin:+	au_hinotify_fin();+ out_wkq:+	au_wkq_fin();+ out_sysaufs:+	sysaufs_fin();+ out:+	return err;+}++static void __exit aufs_exit(void)+{+	unregister_filesystem(&aufs_fs_type);+	au_cache_fin();+	au_sysrq_fin();+	au_hinotify_fin();+	au_wkq_fin();+	sysaufs_fin();+}++module_init(aufs_init);+module_exit(aufs_exit);diff -Nur linux-2.6.31.5.orig/fs/aufs/module.h linux-2.6.31.5/fs/aufs/module.h--- linux-2.6.31.5.orig/fs/aufs/module.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/module.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,78 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * module initialization and module-global+ */++#ifndef __AUFS_MODULE_H__+#define __AUFS_MODULE_H__++#ifdef __KERNEL__++#include <linux/slab.h>++struct path;+struct seq_file;++/* module parameters */+extern short aufs_nwkq;+extern int sysaufs_brs;++/* ---------------------------------------------------------------------- */++extern int au_dir_roflags;++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp);+int au_seq_path(struct seq_file *seq, struct path *path);++/* ---------------------------------------------------------------------- */++/* kmem cache */+enum {+	AuCache_DINFO,+	AuCache_ICNTNR,+	AuCache_FINFO,+	AuCache_VDIR,+	AuCache_DEHSTR,+#ifdef CONFIG_AUFS_HINOTIFY+	AuCache_HINOTIFY,+#endif+	AuCache_Last+};++#define AuCache(type)	KMEM_CACHE(type, SLAB_RECLAIM_ACCOUNT)++extern struct kmem_cache *au_cachep[];++#define AuCacheFuncs(name, index) \+static inline void *au_cache_alloc_##name(void) \+{ return kmem_cache_alloc(au_cachep[AuCache_##index], GFP_NOFS); } \+static inline void au_cache_free_##name(void *p) \+{ kmem_cache_free(au_cachep[AuCache_##index], p); }++AuCacheFuncs(dinfo, DINFO);+AuCacheFuncs(icntnr, ICNTNR);+AuCacheFuncs(finfo, FINFO);+AuCacheFuncs(vdir, VDIR);+AuCacheFuncs(dehstr, DEHSTR);++/*  ---------------------------------------------------------------------- */++#endif /* __KERNEL__ */+#endif /* __AUFS_MODULE_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/opts.c linux-2.6.31.5/fs/aufs/opts.c--- linux-2.6.31.5.orig/fs/aufs/opts.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/opts.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,1543 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * mount options/flags+ */++#include <linux/file.h>+#include <linux/namei.h>+#include <linux/types.h> /* a distribution requires */+#include <linux/parser.h>+#include "aufs.h"++/* ---------------------------------------------------------------------- */++enum {+	Opt_br,+	Opt_add, Opt_del, Opt_mod, Opt_reorder, Opt_append, Opt_prepend,+	Opt_idel, Opt_imod, Opt_ireorder,+	Opt_dirwh, Opt_rdcache, Opt_rdblk, Opt_rdhash, Opt_rendir,+	Opt_rdblk_def, Opt_rdhash_def,+	Opt_xino, Opt_zxino, Opt_noxino,+	Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino,+	Opt_trunc_xino_path, Opt_itrunc_xino,+	Opt_trunc_xib, Opt_notrunc_xib,+	Opt_shwh, Opt_noshwh,+	Opt_plink, Opt_noplink, Opt_list_plink,+	Opt_udba,+	/* Opt_lock, Opt_unlock, */+	Opt_cmd, Opt_cmd_args,+	Opt_diropq_a, Opt_diropq_w,+	Opt_warn_perm, Opt_nowarn_perm,+	Opt_wbr_copyup, Opt_wbr_create,+	Opt_refrof, Opt_norefrof,+	Opt_verbose, Opt_noverbose,+	Opt_sum, Opt_nosum, Opt_wsum,+	Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err+};++static match_table_t options = {+	{Opt_br, "br=%s"},+	{Opt_br, "br:%s"},++	{Opt_add, "add=%d:%s"},+	{Opt_add, "add:%d:%s"},+	{Opt_add, "ins=%d:%s"},+	{Opt_add, "ins:%d:%s"},+	{Opt_append, "append=%s"},+	{Opt_append, "append:%s"},+	{Opt_prepend, "prepend=%s"},+	{Opt_prepend, "prepend:%s"},++	{Opt_del, "del=%s"},+	{Opt_del, "del:%s"},+	/* {Opt_idel, "idel:%d"}, */+	{Opt_mod, "mod=%s"},+	{Opt_mod, "mod:%s"},+	/* {Opt_imod, "imod:%d:%s"}, */++	{Opt_dirwh, "dirwh=%d"},++	{Opt_xino, "xino=%s"},+	{Opt_noxino, "noxino"},+	{Opt_trunc_xino, "trunc_xino"},+	{Opt_trunc_xino_v, "trunc_xino_v=%d:%d"},+	{Opt_notrunc_xino, "notrunc_xino"},+	{Opt_trunc_xino_path, "trunc_xino=%s"},+	{Opt_itrunc_xino, "itrunc_xino=%d"},+	/* {Opt_zxino, "zxino=%s"}, */+	{Opt_trunc_xib, "trunc_xib"},+	{Opt_notrunc_xib, "notrunc_xib"},++	{Opt_plink, "plink"},+	{Opt_noplink, "noplink"},+#ifdef CONFIG_AUFS_DEBUG+	{Opt_list_plink, "list_plink"},+#endif++	{Opt_udba, "udba=%s"},++	{Opt_diropq_a, "diropq=always"},+	{Opt_diropq_a, "diropq=a"},+	{Opt_diropq_w, "diropq=whiteouted"},+	{Opt_diropq_w, "diropq=w"},++	{Opt_warn_perm, "warn_perm"},+	{Opt_nowarn_perm, "nowarn_perm"},++	/* keep them temporary */+	{Opt_ignore_silent, "coo=%s"},+	{Opt_ignore_silent, "nodlgt"},+	{Opt_ignore_silent, "nodirperm1"},+	{Opt_ignore_silent, "clean_plink"},++#ifdef CONFIG_AUFS_SHWH+	{Opt_shwh, "shwh"},+#endif+	{Opt_noshwh, "noshwh"},++	{Opt_rendir, "rendir=%d"},++	{Opt_refrof, "refrof"},+	{Opt_norefrof, "norefrof"},++	{Opt_verbose, "verbose"},+	{Opt_verbose, "v"},+	{Opt_noverbose, "noverbose"},+	{Opt_noverbose, "quiet"},+	{Opt_noverbose, "q"},+	{Opt_noverbose, "silent"},++	{Opt_sum, "sum"},+	{Opt_nosum, "nosum"},+	{Opt_wsum, "wsum"},++	{Opt_rdcache, "rdcache=%d"},+	{Opt_rdblk, "rdblk=%d"},+	{Opt_rdblk_def, "rdblk=def"},+	{Opt_rdhash, "rdhash=%d"},+	{Opt_rdhash_def, "rdhash=def"},++	{Opt_wbr_create, "create=%s"},+	{Opt_wbr_create, "create_policy=%s"},+	{Opt_wbr_copyup, "cpup=%s"},+	{Opt_wbr_copyup, "copyup=%s"},+	{Opt_wbr_copyup, "copyup_policy=%s"},++	/* internal use for the scripts */+	{Opt_ignore_silent, "si=%s"},++	{Opt_br, "dirs=%s"},+	{Opt_ignore, "debug=%d"},+	{Opt_ignore, "delete=whiteout"},+	{Opt_ignore, "delete=all"},+	{Opt_ignore, "imap=%s"},++	{Opt_err, NULL}+};++/* ---------------------------------------------------------------------- */++static const char *au_parser_pattern(int val, struct match_token *token)+{+	while (token->pattern) {+		if (token->token == val)+			return token->pattern;+		token++;+	}+	BUG();+	return "??";+}++/* ---------------------------------------------------------------------- */++static match_table_t brperms = {+	{AuBrPerm_RO, AUFS_BRPERM_RO},+	{AuBrPerm_RR, AUFS_BRPERM_RR},+	{AuBrPerm_RW, AUFS_BRPERM_RW},++	{AuBrPerm_ROWH, AUFS_BRPERM_ROWH},+	{AuBrPerm_RRWH, AUFS_BRPERM_RRWH},+	{AuBrPerm_RWNoLinkWH, AUFS_BRPERM_RWNLWH},++	{AuBrPerm_ROWH, "nfsro"},+	{AuBrPerm_RO, NULL}+};++static int br_perm_val(char *perm)+{+	int val;+	substring_t args[MAX_OPT_ARGS];++	val = match_token(perm, brperms, args);+	return val;+}++const char *au_optstr_br_perm(int brperm)+{+	return au_parser_pattern(brperm, (void *)brperms);+}++/* ---------------------------------------------------------------------- */++static match_table_t udbalevel = {+	{AuOpt_UDBA_REVAL, "reval"},+	{AuOpt_UDBA_NONE, "none"},+#ifdef CONFIG_AUFS_HINOTIFY+	{AuOpt_UDBA_HINOTIFY, "inotify"},+#endif+	{-1, NULL}+};++static int udba_val(char *str)+{+	substring_t args[MAX_OPT_ARGS];++	return match_token(str, udbalevel, args);+}++const char *au_optstr_udba(int udba)+{+	return au_parser_pattern(udba, (void *)udbalevel);+}++/* ---------------------------------------------------------------------- */++static match_table_t au_wbr_create_policy = {+	{AuWbrCreate_TDP, "tdp"},+	{AuWbrCreate_TDP, "top-down-parent"},+	{AuWbrCreate_RR, "rr"},+	{AuWbrCreate_RR, "round-robin"},+	{AuWbrCreate_MFS, "mfs"},+	{AuWbrCreate_MFS, "most-free-space"},+	{AuWbrCreate_MFSV, "mfs:%d"},+	{AuWbrCreate_MFSV, "most-free-space:%d"},++	{AuWbrCreate_MFSRR, "mfsrr:%d"},+	{AuWbrCreate_MFSRRV, "mfsrr:%d:%d"},+	{AuWbrCreate_PMFS, "pmfs"},+	{AuWbrCreate_PMFSV, "pmfs:%d"},++	{-1, NULL}+};++/*+ * cf. linux/lib/parser.c and cmdline.c+ * gave up calling memparse() since it uses simple_strtoull() instead of+ * strict_...().+ */+static int au_match_ull(substring_t *s, unsigned long long *result)+{+	int err;+	unsigned int len;+	char a[32];++	err = -ERANGE;+	len = s->to - s->from;+	if (len + 1 <= sizeof(a)) {+		memcpy(a, s->from, len);+		a[len] = '\0';+		err = strict_strtoull(a, 0, result);+	}+	return err;+}++static int au_wbr_mfs_wmark(substring_t *arg, char *str,+			    struct au_opt_wbr_create *create)+{+	int err;+	unsigned long long ull;++	err = 0;+	if (!au_match_ull(arg, &ull))+		create->mfsrr_watermark = ull;+	else {+		AuErr("bad integer in %s\n", str);+		err = -EINVAL;+	}++	return err;+}++static int au_wbr_mfs_sec(substring_t *arg, char *str,+			  struct au_opt_wbr_create *create)+{+	int n, err;++	err = 0;+	if (!match_int(arg, &n) && 0 <= n)+		create->mfs_second = n;+	else {+		AuErr("bad integer in %s\n", str);+		err = -EINVAL;+	}++	return err;+}++static int au_wbr_create_val(char *str, struct au_opt_wbr_create *create)+{+	int err, e;+	substring_t args[MAX_OPT_ARGS];++	err = match_token(str, au_wbr_create_policy, args);+	create->wbr_create = err;+	switch (err) {+	case AuWbrCreate_MFSRRV:+		e = au_wbr_mfs_wmark(&args[0], str, create);+		if (!e)+			e = au_wbr_mfs_sec(&args[1], str, create);+		if (unlikely(e))+			err = e;+		break;+	case AuWbrCreate_MFSRR:+		e = au_wbr_mfs_wmark(&args[0], str, create);+		if (unlikely(e)) {+			err = e;+			break;+		}+		/*FALLTHROUGH*/+	case AuWbrCreate_MFS:+	case AuWbrCreate_PMFS:+		create->mfs_second = AUFS_MFS_SECOND_DEF;+		break;+	case AuWbrCreate_MFSV:+	case AuWbrCreate_PMFSV:+		e = au_wbr_mfs_sec(&args[0], str, create);+		if (unlikely(e))+			err = e;+		break;+	}++	return err;+}++const char *au_optstr_wbr_create(int wbr_create)+{+	return au_parser_pattern(wbr_create, (void *)au_wbr_create_policy);+}++static match_table_t au_wbr_copyup_policy = {+	{AuWbrCopyup_TDP, "tdp"},+	{AuWbrCopyup_TDP, "top-down-parent"},+	{AuWbrCopyup_BUP, "bup"},+	{AuWbrCopyup_BUP, "bottom-up-parent"},+	{AuWbrCopyup_BU, "bu"},+	{AuWbrCopyup_BU, "bottom-up"},+	{-1, NULL}+};++static int au_wbr_copyup_val(char *str)+{+	substring_t args[MAX_OPT_ARGS];++	return match_token(str, au_wbr_copyup_policy, args);+}++const char *au_optstr_wbr_copyup(int wbr_copyup)+{+	return au_parser_pattern(wbr_copyup, (void *)au_wbr_copyup_policy);+}++/* ---------------------------------------------------------------------- */++static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;++static void dump_opts(struct au_opts *opts)+{+#ifdef CONFIG_AUFS_DEBUG+	/* reduce stack space */+	union {+		struct au_opt_add *add;+		struct au_opt_del *del;+		struct au_opt_mod *mod;+		struct au_opt_xino *xino;+		struct au_opt_xino_itrunc *xino_itrunc;+		struct au_opt_wbr_create *create;+	} u;+	struct au_opt *opt;++	opt = opts->opt;+	while (opt->type != Opt_tail) {+		switch (opt->type) {+		case Opt_add:+			u.add = &opt->add;+			AuDbg("add {b%d, %s, 0x%x, %p}\n",+				  u.add->bindex, u.add->pathname, u.add->perm,+				  u.add->path.dentry);+			break;+		case Opt_del:+		case Opt_idel:+			u.del = &opt->del;+			AuDbg("del {%s, %p}\n",+			      u.del->pathname, u.del->h_path.dentry);+			break;+		case Opt_mod:+		case Opt_imod:+			u.mod = &opt->mod;+			AuDbg("mod {%s, 0x%x, %p}\n",+				  u.mod->path, u.mod->perm, u.mod->h_root);+			break;+		case Opt_append:+			u.add = &opt->add;+			AuDbg("append {b%d, %s, 0x%x, %p}\n",+				  u.add->bindex, u.add->pathname, u.add->perm,+				  u.add->path.dentry);+			break;+		case Opt_prepend:+			u.add = &opt->add;+			AuDbg("prepend {b%d, %s, 0x%x, %p}\n",+				  u.add->bindex, u.add->pathname, u.add->perm,+				  u.add->path.dentry);+			break;+		case Opt_dirwh:+			AuDbg("dirwh %d\n", opt->dirwh);+			break;+		case Opt_rdcache:+			AuDbg("rdcache %d\n", opt->rdcache);+			break;+		case Opt_rdblk:+			AuDbg("rdblk %u\n", opt->rdblk);+			break;+		case Opt_rdblk_def:+			AuDbg("rdblk_def\n");+			break;+		case Opt_rdhash:+			AuDbg("rdhash %u\n", opt->rdhash);+			break;+		case Opt_rdhash_def:+			AuDbg("rdhash_def\n");+			break;+		case Opt_xino:+			u.xino = &opt->xino;+			AuDbg("xino {%s %.*s}\n",+				  u.xino->path,+				  AuDLNPair(u.xino->file->f_dentry));+			break;+		case Opt_trunc_xino:+			AuLabel(trunc_xino);+			break;+		case Opt_notrunc_xino:+			AuLabel(notrunc_xino);+			break;+		case Opt_trunc_xino_path:+		case Opt_itrunc_xino:+			u.xino_itrunc = &opt->xino_itrunc;+			AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex);+			break;++		case Opt_noxino:+			AuLabel(noxino);+			break;+		case Opt_trunc_xib:+			AuLabel(trunc_xib);+			break;+		case Opt_notrunc_xib:+			AuLabel(notrunc_xib);+			break;+		case Opt_shwh:+			AuLabel(shwh);+			break;+		case Opt_noshwh:+			AuLabel(noshwh);+			break;+		case Opt_plink:+			AuLabel(plink);+			break;+		case Opt_noplink:+			AuLabel(noplink);+			break;+		case Opt_list_plink:+			AuLabel(list_plink);+			break;+		case Opt_udba:+			AuDbg("udba %d, %s\n",+				  opt->udba, au_optstr_udba(opt->udba));+			break;+		case Opt_diropq_a:+			AuLabel(diropq_a);+			break;+		case Opt_diropq_w:+			AuLabel(diropq_w);+			break;+		case Opt_warn_perm:+			AuLabel(warn_perm);+			break;+		case Opt_nowarn_perm:+			AuLabel(nowarn_perm);+			break;+		case Opt_refrof:+			AuLabel(refrof);+			break;+		case Opt_norefrof:+			AuLabel(norefrof);+			break;+		case Opt_verbose:+			AuLabel(verbose);+			break;+		case Opt_noverbose:+			AuLabel(noverbose);+			break;+		case Opt_sum:+			AuLabel(sum);+			break;+		case Opt_nosum:+			AuLabel(nosum);+			break;+		case Opt_wsum:+			AuLabel(wsum);+			break;+		case Opt_wbr_create:+			u.create = &opt->wbr_create;+			AuDbg("create %d, %s\n", u.create->wbr_create,+				  au_optstr_wbr_create(u.create->wbr_create));+			switch (u.create->wbr_create) {+			case AuWbrCreate_MFSV:+			case AuWbrCreate_PMFSV:+				AuDbg("%d sec\n", u.create->mfs_second);+				break;+			case AuWbrCreate_MFSRR:+				AuDbg("%llu watermark\n",+					  u.create->mfsrr_watermark);+				break;+			case AuWbrCreate_MFSRRV:+				AuDbg("%llu watermark, %d sec\n",+					  u.create->mfsrr_watermark,+					  u.create->mfs_second);+				break;+			}+			break;+		case Opt_wbr_copyup:+			AuDbg("copyup %d, %s\n", opt->wbr_copyup,+				  au_optstr_wbr_copyup(opt->wbr_copyup));+			break;+		default:+			BUG();+		}+		opt++;+	}+#endif+}++void au_opts_free(struct au_opts *opts)+{+	struct au_opt *opt;++	opt = opts->opt;+	while (opt->type != Opt_tail) {+		switch (opt->type) {+		case Opt_add:+		case Opt_append:+		case Opt_prepend:+			path_put(&opt->add.path);+			break;+		case Opt_del:+		case Opt_idel:+			path_put(&opt->del.h_path);+			break;+		case Opt_mod:+		case Opt_imod:+			dput(opt->mod.h_root);+			break;+		case Opt_xino:+			fput(opt->xino.file);+			break;+		}+		opt++;+	}+}++static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags,+		   aufs_bindex_t bindex)+{+	int err;+	struct au_opt_add *add = &opt->add;+	char *p;++	add->bindex = bindex;+	add->perm = AuBrPerm_Last;+	add->pathname = opt_str;+	p = strchr(opt_str, '=');+	if (p) {+		*p++ = 0;+		if (*p)+			add->perm = br_perm_val(p);+	}++	err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path);+	if (!err) {+		if (!p) {+			add->perm = AuBrPerm_RO;+			if (au_test_fs_rr(add->path.dentry->d_sb))+				add->perm = AuBrPerm_RR;+			else if (!bindex && !(sb_flags & MS_RDONLY))+				add->perm = AuBrPerm_RW;+		}+		opt->type = Opt_add;+		goto out;+	}+	AuErr("lookup failed %s (%d)\n", add->pathname, err);+	err = -EINVAL;++ out:+	return err;+}++static int au_opts_parse_del(struct au_opt_del *del, substring_t args[])+{+	int err;++	del->pathname = args[0].from;+	AuDbg("del path %s\n", del->pathname);++	err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path);+	if (unlikely(err))+		AuErr("lookup failed %s (%d)\n", del->pathname, err);++	return err;+}++#if 0 /* reserved for future use */+static int au_opts_parse_idel(struct super_block *sb, aufs_bindex_t bindex,+			      struct au_opt_del *del, substring_t args[])+{+	int err;+	struct dentry *root;++	err = -EINVAL;+	root = sb->s_root;+	aufs_read_lock(root, AuLock_FLUSH);+	if (bindex < 0 || au_sbend(sb) < bindex) {+		AuErr("out of bounds, %d\n", bindex);+		goto out;+	}++	err = 0;+	del->h_path.dentry = dget(au_h_dptr(root, bindex));+	del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex));++ out:+	aufs_read_unlock(root, !AuLock_IR);+	return err;+}+#endif++static int au_opts_parse_mod(struct au_opt_mod *mod, substring_t args[])+{+	int err;+	struct path path;+	char *p;++	err = -EINVAL;+	mod->path = args[0].from;+	p = strchr(mod->path, '=');+	if (unlikely(!p)) {+		AuErr("no permssion %s\n", args[0].from);+		goto out;+	}++	*p++ = 0;+	err = vfsub_kern_path(mod->path, lkup_dirflags, &path);+	if (unlikely(err)) {+		AuErr("lookup failed %s (%d)\n", mod->path, err);+		goto out;+	}++	mod->perm = br_perm_val(p);+	AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, p);+	mod->h_root = dget(path.dentry);+	path_put(&path);++ out:+	return err;+}++#if 0 /* reserved for future use */+static int au_opts_parse_imod(struct super_block *sb, aufs_bindex_t bindex,+			      struct au_opt_mod *mod, substring_t args[])+{+	int err;+	struct dentry *root;++	err = -EINVAL;+	root = sb->s_root;+	aufs_read_lock(root, AuLock_FLUSH);+	if (bindex < 0 || au_sbend(sb) < bindex) {+		AuErr("out of bounds, %d\n", bindex);+		goto out;+	}++	err = 0;+	mod->perm = br_perm_val(args[1].from);+	AuDbg("mod path %s, perm 0x%x, %s\n",+	      mod->path, mod->perm, args[1].from);+	mod->h_root = dget(au_h_dptr(root, bindex));++ out:+	aufs_read_unlock(root, !AuLock_IR);+	return err;+}+#endif++static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino,+			      substring_t args[])+{+	int err;+	struct file *file;++	file = au_xino_create(sb, args[0].from, /*silent*/0);+	err = PTR_ERR(file);+	if (IS_ERR(file))+		goto out;++	err = -EINVAL;+	if (unlikely(file->f_dentry->d_sb == sb)) {+		fput(file);+		AuErr("%s must be outside\n", args[0].from);+		goto out;+	}++	err = 0;+	xino->file = file;+	xino->path = args[0].from;++ out:+	return err;+}++static+int au_opts_parse_xino_itrunc_path(struct super_block *sb,+				   struct au_opt_xino_itrunc *xino_itrunc,+				   substring_t args[])+{+	int err;+	aufs_bindex_t bend, bindex;+	struct path path;+	struct dentry *root;++	err = vfsub_kern_path(args[0].from, lkup_dirflags, &path);+	if (unlikely(err)) {+		AuErr("lookup failed %s (%d)\n", args[0].from, err);+		goto out;+	}++	xino_itrunc->bindex = -1;+	root = sb->s_root;+	aufs_read_lock(root, AuLock_FLUSH);+	bend = au_sbend(sb);+	for (bindex = 0; bindex <= bend; bindex++) {+		if (au_h_dptr(root, bindex) == path.dentry) {+			xino_itrunc->bindex = bindex;+			break;+		}+	}+	aufs_read_unlock(root, !AuLock_IR);+	path_put(&path);++	if (unlikely(xino_itrunc->bindex < 0)) {+		AuErr("no such branch %s\n", args[0].from);+		err = -EINVAL;+	}++ out:+	return err;+}++/* called without aufs lock */+int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts)+{+	int err, n, token;+	aufs_bindex_t bindex;+	unsigned char skipped;+	struct dentry *root;+	struct au_opt *opt, *opt_tail;+	char *opt_str;+	/* reduce the stack space */+	union {+		struct au_opt_xino_itrunc *xino_itrunc;+		struct au_opt_wbr_create *create;+	} u;+	struct {+		substring_t args[MAX_OPT_ARGS];+	} *a;++	err = -ENOMEM;+	a = kmalloc(sizeof(*a), GFP_NOFS);+	if (unlikely(!a))+		goto out;++	root = sb->s_root;+	err = 0;+	bindex = 0;+	opt = opts->opt;+	opt_tail = opt + opts->max_opt - 1;+	opt->type = Opt_tail;+	while (!err && (opt_str = strsep(&str, ",")) && *opt_str) {+		err = -EINVAL;+		skipped = 0;+		token = match_token(opt_str, options, a->args);+		switch (token) {+		case Opt_br:+			err = 0;+			while (!err && (opt_str = strsep(&a->args[0].from, ":"))+			       && *opt_str) {+				err = opt_add(opt, opt_str, opts->sb_flags,+					      bindex++);+				if (unlikely(!err && ++opt > opt_tail)) {+					err = -E2BIG;+					break;+				}+				opt->type = Opt_tail;+				skipped = 1;+			}+			break;+		case Opt_add:+			if (unlikely(match_int(&a->args[0], &n))) {+				AuErr("bad integer in %s\n", opt_str);+				break;+			}+			bindex = n;+			err = opt_add(opt, a->args[1].from, opts->sb_flags,+				      bindex);+			if (!err)+				opt->type = token;+			break;+		case Opt_append:+			err = opt_add(opt, a->args[0].from, opts->sb_flags,+				      /*dummy bindex*/1);+			if (!err)+				opt->type = token;+			break;+		case Opt_prepend:+			err = opt_add(opt, a->args[0].from, opts->sb_flags,+				      /*bindex*/0);+			if (!err)+				opt->type = token;+			break;+		case Opt_del:+			err = au_opts_parse_del(&opt->del, a->args);+			if (!err)+				opt->type = token;+			break;+#if 0 /* reserved for future use */+		case Opt_idel:+			del->pathname = "(indexed)";+			if (unlikely(match_int(&args[0], &n))) {+				AuErr("bad integer in %s\n", opt_str);+				break;+			}+			err = au_opts_parse_idel(sb, n, &opt->del, a->args);+			if (!err)+				opt->type = token;+			break;+#endif+		case Opt_mod:+			err = au_opts_parse_mod(&opt->mod, a->args);+			if (!err)+				opt->type = token;+			break;+#ifdef IMOD /* reserved for future use */+		case Opt_imod:+			u.mod->path = "(indexed)";+			if (unlikely(match_int(&a->args[0], &n))) {+				AuErr("bad integer in %s\n", opt_str);+				break;+			}+			err = au_opts_parse_imod(sb, n, &opt->mod, a->args);+			if (!err)+				opt->type = token;+			break;+#endif+		case Opt_xino:+			err = au_opts_parse_xino(sb, &opt->xino, a->args);+			if (!err)+				opt->type = token;+			break;++		case Opt_trunc_xino_path:+			err = au_opts_parse_xino_itrunc_path+				(sb, &opt->xino_itrunc, a->args);+			if (!err)+				opt->type = token;+			break;++		case Opt_itrunc_xino:+			u.xino_itrunc = &opt->xino_itrunc;+			if (unlikely(match_int(&a->args[0], &n))) {+				AuErr("bad integer in %s\n", opt_str);+				break;+			}+			u.xino_itrunc->bindex = n;+			aufs_read_lock(root, AuLock_FLUSH);+			if (n < 0 || au_sbend(sb) < n) {+				AuErr("out of bounds, %d\n", n);+				aufs_read_unlock(root, !AuLock_IR);+				break;+			}+			aufs_read_unlock(root, !AuLock_IR);+			err = 0;+			opt->type = token;+			break;++		case Opt_dirwh:+			if (unlikely(match_int(&a->args[0], &opt->dirwh)))+				break;+			err = 0;+			opt->type = token;+			break;++		case Opt_rdcache:+			if (unlikely(match_int(&a->args[0], &opt->rdcache)))+				break;+			err = 0;+			opt->type = token;+			break;+		case Opt_rdblk:+			if (unlikely(match_int(&a->args[0], &n)+				     || n <= 0+				     || n > KMALLOC_MAX_SIZE)) {+				AuErr("bad integer in %s\n", opt_str);+				break;+			}+			if (unlikely(n < NAME_MAX)) {+				AuErr("rdblk must be larger than %d\n",+				      NAME_MAX);+				break;+			}+			opt->rdblk = n;+			err = 0;+			opt->type = token;+			break;+		case Opt_rdhash:+			if (unlikely(match_int(&a->args[0], &n)+				     || n <= 0+				     || n * sizeof(struct hlist_head)+				     > KMALLOC_MAX_SIZE)) {+				AuErr("bad integer in %s\n", opt_str);+				break;+			}+			opt->rdhash = n;+			err = 0;+			opt->type = token;+			break;++		case Opt_trunc_xino:+		case Opt_notrunc_xino:+		case Opt_noxino:+		case Opt_trunc_xib:+		case Opt_notrunc_xib:+		case Opt_shwh:+		case Opt_noshwh:+		case Opt_plink:+		case Opt_noplink:+		case Opt_list_plink:+		case Opt_diropq_a:+		case Opt_diropq_w:+		case Opt_warn_perm:+		case Opt_nowarn_perm:+		case Opt_refrof:+		case Opt_norefrof:+		case Opt_verbose:+		case Opt_noverbose:+		case Opt_sum:+		case Opt_nosum:+		case Opt_wsum:+		case Opt_rdblk_def:+		case Opt_rdhash_def:+			err = 0;+			opt->type = token;+			break;++		case Opt_udba:+			opt->udba = udba_val(a->args[0].from);+			if (opt->udba >= 0) {+				err = 0;+				opt->type = token;+			} else+				AuErr("wrong value, %s\n", opt_str);+			break;++		case Opt_wbr_create:+			u.create = &opt->wbr_create;+			u.create->wbr_create+				= au_wbr_create_val(a->args[0].from, u.create);+			if (u.create->wbr_create >= 0) {+				err = 0;+				opt->type = token;+			} else+				AuErr("wrong value, %s\n", opt_str);+			break;+		case Opt_wbr_copyup:+			opt->wbr_copyup = au_wbr_copyup_val(a->args[0].from);+			if (opt->wbr_copyup >= 0) {+				err = 0;+				opt->type = token;+			} else+				AuErr("wrong value, %s\n", opt_str);+			break;++		case Opt_ignore:+			AuWarn("ignored %s\n", opt_str);+			/*FALLTHROUGH*/+		case Opt_ignore_silent:+			skipped = 1;+			err = 0;+			break;+		case Opt_err:+			AuErr("unknown option %s\n", opt_str);+			break;+		}++		if (!err && !skipped) {+			if (unlikely(++opt > opt_tail)) {+				err = -E2BIG;+				opt--;+				opt->type = Opt_tail;+				break;+			}+			opt->type = Opt_tail;+		}+	}++	kfree(a);+	dump_opts(opts);+	if (unlikely(err))+		au_opts_free(opts);++ out:+	return err;+}++static int au_opt_wbr_create(struct super_block *sb,+			     struct au_opt_wbr_create *create)+{+	int err;+	struct au_sbinfo *sbinfo;++	SiMustWriteLock(sb);++	err = 1; /* handled */+	sbinfo = au_sbi(sb);+	if (sbinfo->si_wbr_create_ops->fin) {+		err = sbinfo->si_wbr_create_ops->fin(sb);+		if (!err)+			err = 1;+	}++	sbinfo->si_wbr_create = create->wbr_create;+	sbinfo->si_wbr_create_ops = au_wbr_create_ops + create->wbr_create;+	switch (create->wbr_create) {+	case AuWbrCreate_MFSRRV:+	case AuWbrCreate_MFSRR:+		sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark;+		/*FALLTHROUGH*/+	case AuWbrCreate_MFS:+	case AuWbrCreate_MFSV:+	case AuWbrCreate_PMFS:+	case AuWbrCreate_PMFSV:+		sbinfo->si_wbr_mfs.mfs_expire = create->mfs_second * HZ;+		break;+	}++	if (sbinfo->si_wbr_create_ops->init)+		sbinfo->si_wbr_create_ops->init(sb); /* ignore */++	return err;+}++/*+ * returns,+ * plus: processed without an error+ * zero: unprocessed+ */+static int au_opt_simple(struct super_block *sb, struct au_opt *opt,+			 struct au_opts *opts)+{+	int err;+	struct au_sbinfo *sbinfo;++	SiMustWriteLock(sb);++	err = 1; /* handled */+	sbinfo = au_sbi(sb);+	switch (opt->type) {+	case Opt_udba:+		sbinfo->si_mntflags &= ~AuOptMask_UDBA;+		sbinfo->si_mntflags |= opt->udba;+		opts->given_udba |= opt->udba;+		break;++	case Opt_plink:+		au_opt_set(sbinfo->si_mntflags, PLINK);+		break;+	case Opt_noplink:+		if (au_opt_test(sbinfo->si_mntflags, PLINK))+			au_plink_put(sb);+		au_opt_clr(sbinfo->si_mntflags, PLINK);+		break;+	case Opt_list_plink:+		if (au_opt_test(sbinfo->si_mntflags, PLINK))+			au_plink_list(sb);+		break;++	case Opt_diropq_a:+		au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ);+		break;+	case Opt_diropq_w:+		au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ);+		break;++	case Opt_warn_perm:+		au_opt_set(sbinfo->si_mntflags, WARN_PERM);+		break;+	case Opt_nowarn_perm:+		au_opt_clr(sbinfo->si_mntflags, WARN_PERM);+		break;++	case Opt_refrof:+		au_opt_set(sbinfo->si_mntflags, REFROF);+		break;+	case Opt_norefrof:+		au_opt_clr(sbinfo->si_mntflags, REFROF);+		break;++	case Opt_verbose:+		au_opt_set(sbinfo->si_mntflags, VERBOSE);+		break;+	case Opt_noverbose:+		au_opt_clr(sbinfo->si_mntflags, VERBOSE);+		break;++	case Opt_sum:+		au_opt_set(sbinfo->si_mntflags, SUM);+		break;+	case Opt_wsum:+		au_opt_clr(sbinfo->si_mntflags, SUM);+		au_opt_set(sbinfo->si_mntflags, SUM_W);+	case Opt_nosum:+		au_opt_clr(sbinfo->si_mntflags, SUM);+		au_opt_clr(sbinfo->si_mntflags, SUM_W);+		break;++	case Opt_wbr_create:+		err = au_opt_wbr_create(sb, &opt->wbr_create);+		break;+	case Opt_wbr_copyup:+		sbinfo->si_wbr_copyup = opt->wbr_copyup;+		sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup;+		break;++	case Opt_dirwh:+		sbinfo->si_dirwh = opt->dirwh;+		break;++	case Opt_rdcache:+		sbinfo->si_rdcache = opt->rdcache * HZ;+		break;+	case Opt_rdblk:+		sbinfo->si_rdblk = opt->rdblk;+		break;+	case Opt_rdblk_def:+		sbinfo->si_rdblk = AUFS_RDBLK_DEF;+		break;+	case Opt_rdhash:+		sbinfo->si_rdhash = opt->rdhash;+		break;+	case Opt_rdhash_def:+		sbinfo->si_rdhash = AUFS_RDHASH_DEF;+		break;++	case Opt_shwh:+		au_opt_set(sbinfo->si_mntflags, SHWH);+		break;+	case Opt_noshwh:+		au_opt_clr(sbinfo->si_mntflags, SHWH);+		break;++	case Opt_trunc_xino:+		au_opt_set(sbinfo->si_mntflags, TRUNC_XINO);+		break;+	case Opt_notrunc_xino:+		au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO);+		break;++	case Opt_trunc_xino_path:+	case Opt_itrunc_xino:+		err = au_xino_trunc(sb, opt->xino_itrunc.bindex);+		if (!err)+			err = 1;+		break;++	case Opt_trunc_xib:+		au_fset_opts(opts->flags, TRUNC_XIB);+		break;+	case Opt_notrunc_xib:+		au_fclr_opts(opts->flags, TRUNC_XIB);+		break;++	default:+		err = 0;+		break;+	}++	return err;+}++/*+ * returns tri-state.+ * plus: processed without an error+ * zero: unprocessed+ * minus: error+ */+static int au_opt_br(struct super_block *sb, struct au_opt *opt,+		     struct au_opts *opts)+{+	int err, do_refresh;++	err = 0;+	switch (opt->type) {+	case Opt_append:+		opt->add.bindex = au_sbend(sb) + 1;+		if (opt->add.bindex < 0)+			opt->add.bindex = 0;+		goto add;+	case Opt_prepend:+		opt->add.bindex = 0;+	add:+	case Opt_add:+		err = au_br_add(sb, &opt->add,+				au_ftest_opts(opts->flags, REMOUNT));+		if (!err) {+			err = 1;+			au_fset_opts(opts->flags, REFRESH_DIR);+			if (au_br_whable(opt->add.perm))+				au_fset_opts(opts->flags, REFRESH_NONDIR);+		}+		break;++	case Opt_del:+	case Opt_idel:+		err = au_br_del(sb, &opt->del,+				au_ftest_opts(opts->flags, REMOUNT));+		if (!err) {+			err = 1;+			au_fset_opts(opts->flags, TRUNC_XIB);+			au_fset_opts(opts->flags, REFRESH_DIR);+			au_fset_opts(opts->flags, REFRESH_NONDIR);+		}+		break;++	case Opt_mod:+	case Opt_imod:+		err = au_br_mod(sb, &opt->mod,+				au_ftest_opts(opts->flags, REMOUNT),+				&do_refresh);+		if (!err) {+			err = 1;+			if (do_refresh) {+				au_fset_opts(opts->flags, REFRESH_DIR);+				au_fset_opts(opts->flags, REFRESH_NONDIR);+			}+		}+		break;+	}++	return err;+}++static int au_opt_xino(struct super_block *sb, struct au_opt *opt,+		       struct au_opt_xino **opt_xino,+		       struct au_opts *opts)+{+	int err;+	aufs_bindex_t bend, bindex;+	struct dentry *root, *parent, *h_root;++	err = 0;+	switch (opt->type) {+	case Opt_xino:+		err = au_xino_set(sb, &opt->xino,+				  !!au_ftest_opts(opts->flags, REMOUNT));+		if (unlikely(err))+			break;++		*opt_xino = &opt->xino;+		au_xino_brid_set(sb, -1);++		/* safe d_parent access */+		parent = opt->xino.file->f_dentry->d_parent;+		root = sb->s_root;+		bend = au_sbend(sb);+		for (bindex = 0; bindex <= bend; bindex++) {+			h_root = au_h_dptr(root, bindex);+			if (h_root == parent) {+				au_xino_brid_set(sb, au_sbr_id(sb, bindex));+				break;+			}+		}+		break;++	case Opt_noxino:+		au_xino_clr(sb);+		au_xino_brid_set(sb, -1);+		*opt_xino = (void *)-1;+		break;+	}++	return err;+}++int au_opts_verify(struct super_block *sb, unsigned long sb_flags,+		   unsigned int pending)+{+	int err;+	aufs_bindex_t bindex, bend;+	unsigned char do_plink, skip, do_free;+	struct au_branch *br;+	struct au_wbr *wbr;+	struct dentry *root;+	struct inode *dir, *h_dir;+	struct au_sbinfo *sbinfo;+	struct au_hinode *hdir;++	SiMustAnyLock(sb);++	sbinfo = au_sbi(sb);+	AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA));++	if (!(sb_flags & MS_RDONLY)) {+		if (unlikely(!au_br_writable(au_sbr_perm(sb, 0))))+			AuWarn("first branch should be rw\n");+		if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH)))+			AuWarn("shwh should be used with ro\n");+	}++	if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HINOTIFY)+	    && !au_opt_test(sbinfo->si_mntflags, XINO))+		AuWarn("udba=inotify requires xino\n");++	err = 0;+	root = sb->s_root;+	dir = sb->s_root->d_inode;+	do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK);+	bend = au_sbend(sb);+	for (bindex = 0; !err && bindex <= bend; bindex++) {+		skip = 0;+		h_dir = au_h_iptr(dir, bindex);+		br = au_sbr(sb, bindex);+		do_free = 0;++		wbr = br->br_wbr;+		if (wbr)+			wbr_wh_read_lock(wbr);++		switch (br->br_perm) {+		case AuBrPerm_RO:+		case AuBrPerm_ROWH:+		case AuBrPerm_RR:+		case AuBrPerm_RRWH:+			do_free = !!wbr;+			skip = (!wbr+				|| (!wbr->wbr_whbase+				    && !wbr->wbr_plink+				    && !wbr->wbr_orph));+			break;++		case AuBrPerm_RWNoLinkWH:+			/* skip = (!br->br_whbase && !br->br_orph); */+			skip = (!wbr || !wbr->wbr_whbase);+			if (skip && wbr) {+				if (do_plink)+					skip = !!wbr->wbr_plink;+				else+					skip = !wbr->wbr_plink;+			}+			break;++		case AuBrPerm_RW:+			/* skip = (br->br_whbase && br->br_ohph); */+			skip = (wbr && wbr->wbr_whbase);+			if (skip) {+				if (do_plink)+					skip = !!wbr->wbr_plink;+				else+					skip = !wbr->wbr_plink;+			}+			break;++		default:+			BUG();+		}+		if (wbr)+			wbr_wh_read_unlock(wbr);++		if (skip)+			continue;++		hdir = au_hi(dir, bindex);+		au_hin_imtx_lock_nested(hdir, AuLsc_I_PARENT);+		if (wbr)+			wbr_wh_write_lock(wbr);+		err = au_wh_init(au_h_dptr(root, bindex), br, sb);+		if (wbr)+			wbr_wh_write_unlock(wbr);+		au_hin_imtx_unlock(hdir);++		if (!err && do_free) {+			kfree(wbr);+			br->br_wbr = NULL;+		}+	}++	return err;+}++int au_opts_mount(struct super_block *sb, struct au_opts *opts)+{+	int err;+	unsigned int tmp;+	aufs_bindex_t bend;+	struct au_opt *opt;+	struct au_opt_xino *opt_xino, xino;+	struct au_sbinfo *sbinfo;++	SiMustWriteLock(sb);++	err = 0;+	opt_xino = NULL;+	opt = opts->opt;+	while (err >= 0 && opt->type != Opt_tail)+		err = au_opt_simple(sb, opt++, opts);+	if (err > 0)+		err = 0;+	else if (unlikely(err < 0))+		goto out;++	/* disable xino and udba temporary */+	sbinfo = au_sbi(sb);+	tmp = sbinfo->si_mntflags;+	au_opt_clr(sbinfo->si_mntflags, XINO);+	au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL);++	opt = opts->opt;+	while (err >= 0 && opt->type != Opt_tail)+		err = au_opt_br(sb, opt++, opts);+	if (err > 0)+		err = 0;+	else if (unlikely(err < 0))+		goto out;++	bend = au_sbend(sb);+	if (unlikely(bend < 0)) {+		err = -EINVAL;+		AuErr("no branches\n");+		goto out;+	}++	if (au_opt_test(tmp, XINO))+		au_opt_set(sbinfo->si_mntflags, XINO);+	opt = opts->opt;+	while (!err && opt->type != Opt_tail)+		err = au_opt_xino(sb, opt++, &opt_xino, opts);+	if (unlikely(err))+		goto out;++	err = au_opts_verify(sb, sb->s_flags, tmp);+	if (unlikely(err))+		goto out;++	/* restore xino */+	if (au_opt_test(tmp, XINO) && !opt_xino) {+		xino.file = au_xino_def(sb);+		err = PTR_ERR(xino.file);+		if (IS_ERR(xino.file))+			goto out;++		err = au_xino_set(sb, &xino, /*remount*/0);+		fput(xino.file);+		if (unlikely(err))+			goto out;+	}++	/* restore udba */+	sbinfo->si_mntflags &= ~AuOptMask_UDBA;+	sbinfo->si_mntflags |= (tmp & AuOptMask_UDBA);+	if (au_opt_test(tmp, UDBA_HINOTIFY)) {+		struct inode *dir = sb->s_root->d_inode;+		au_reset_hinotify(dir,+				  au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO);+	}++ out:+	return err;+}++int au_opts_remount(struct super_block *sb, struct au_opts *opts)+{+	int err, rerr;+	struct inode *dir;+	struct au_opt_xino *opt_xino;+	struct au_opt *opt;+	struct au_sbinfo *sbinfo;++	SiMustWriteLock(sb);++	dir = sb->s_root->d_inode;+	sbinfo = au_sbi(sb);+	err = 0;+	opt_xino = NULL;+	opt = opts->opt;+	while (err >= 0 && opt->type != Opt_tail) {+		err = au_opt_simple(sb, opt, opts);+		if (!err)+			err = au_opt_br(sb, opt, opts);+		if (!err)+			err = au_opt_xino(sb, opt, &opt_xino, opts);+		opt++;+	}+	if (err > 0)+		err = 0;+	AuTraceErr(err);+	/* go on even err */++	rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0);+	if (unlikely(rerr && !err))+		err = rerr;++	if (au_ftest_opts(opts->flags, TRUNC_XIB)) {+		rerr = au_xib_trunc(sb);+		if (unlikely(rerr && !err))+			err = rerr;+	}++	/* will be handled by the caller */+	if (!au_ftest_opts(opts->flags, REFRESH_DIR)+	    && (opts->given_udba || au_opt_test(sbinfo->si_mntflags, XINO)))+		au_fset_opts(opts->flags, REFRESH_DIR);++	AuDbg("status 0x%x\n", opts->flags);+	return err;+}++/* ---------------------------------------------------------------------- */++unsigned int au_opt_udba(struct super_block *sb)+{+	return au_mntflags(sb) & AuOptMask_UDBA;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/opts.h linux-2.6.31.5/fs/aufs/opts.h--- linux-2.6.31.5.orig/fs/aufs/opts.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/opts.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,196 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * mount options/flags+ */++#ifndef __AUFS_OPTS_H__+#define __AUFS_OPTS_H__++#ifdef __KERNEL__++#include <linux/path.h>+#include <linux/aufs_type.h>++struct file;+struct super_block;++/* ---------------------------------------------------------------------- */++/* mount flags */+#define AuOpt_XINO		1		/* external inode number bitmap+						   and translation table */+#define AuOpt_TRUNC_XINO	(1 << 1)	/* truncate xino files */+#define AuOpt_UDBA_NONE		(1 << 2)	/* users direct branch access */+#define AuOpt_UDBA_REVAL	(1 << 3)+#define AuOpt_UDBA_HINOTIFY	(1 << 4)+#define AuOpt_SHWH		(1 << 5)	/* show whiteout */+#define AuOpt_PLINK		(1 << 6)	/* pseudo-link */+#define AuOpt_DIRPERM1		(1 << 7)	/* unimplemented */+#define AuOpt_REFROF		(1 << 8)	/* unimplemented */+#define AuOpt_ALWAYS_DIROPQ	(1 << 9)	/* policy to creating diropq */+#define AuOpt_SUM		(1 << 10)	/* summation for statfs(2) */+#define AuOpt_SUM_W		(1 << 11)	/* unimplemented */+#define AuOpt_WARN_PERM		(1 << 12)	/* warn when add-branch */+#define AuOpt_VERBOSE		(1 << 13)	/* busy inode when del-branch */++#ifndef CONFIG_AUFS_HINOTIFY+#undef AuOpt_UDBA_HINOTIFY+#define AuOpt_UDBA_HINOTIFY	0+#endif+#ifndef CONFIG_AUFS_SHWH+#undef AuOpt_SHWH+#define AuOpt_SHWH		0+#endif++#define AuOpt_Def	(AuOpt_XINO \+			 | AuOpt_UDBA_REVAL \+			 | AuOpt_PLINK \+			 /* | AuOpt_DIRPERM1 */ \+			 | AuOpt_WARN_PERM)+#define AuOptMask_UDBA	(AuOpt_UDBA_NONE \+			 | AuOpt_UDBA_REVAL \+			 | AuOpt_UDBA_HINOTIFY)++#define au_opt_test(flags, name)	(flags & AuOpt_##name)+#define au_opt_set(flags, name) do { \+	BUILD_BUG_ON(AuOpt_##name & AuOptMask_UDBA); \+	((flags) |= AuOpt_##name); \+} while (0)+#define au_opt_set_udba(flags, name) do { \+	(flags) &= ~AuOptMask_UDBA; \+	((flags) |= AuOpt_##name); \+} while (0)+#define au_opt_clr(flags, name)		{ ((flags) &= ~AuOpt_##name); }++/* ---------------------------------------------------------------------- */++/* policies to select one among multiple writable branches */+enum {+	AuWbrCreate_TDP,	/* top down parent */+	AuWbrCreate_RR,		/* round robin */+	AuWbrCreate_MFS,	/* most free space */+	AuWbrCreate_MFSV,	/* mfs with seconds */+	AuWbrCreate_MFSRR,	/* mfs then rr */+	AuWbrCreate_MFSRRV,	/* mfs then rr with seconds */+	AuWbrCreate_PMFS,	/* parent and mfs */+	AuWbrCreate_PMFSV,	/* parent and mfs with seconds */++	AuWbrCreate_Def = AuWbrCreate_TDP+};++enum {+	AuWbrCopyup_TDP,	/* top down parent */+	AuWbrCopyup_BUP,	/* bottom up parent */+	AuWbrCopyup_BU,		/* bottom up */++	AuWbrCopyup_Def = AuWbrCopyup_TDP+};++/* ---------------------------------------------------------------------- */++struct au_opt_add {+	aufs_bindex_t	bindex;+	char		*pathname;+	int		perm;+	struct path	path;+};++struct au_opt_del {+	char		*pathname;+	struct path	h_path;+};++struct au_opt_mod {+	char		*path;+	int		perm;+	struct dentry	*h_root;+};++struct au_opt_xino {+	char		*path;+	struct file	*file;+};++struct au_opt_xino_itrunc {+	aufs_bindex_t	bindex;+};++struct au_opt_wbr_create {+	int			wbr_create;+	int			mfs_second;+	unsigned long long	mfsrr_watermark;+};++struct au_opt {+	int type;+	union {+		struct au_opt_xino	xino;+		struct au_opt_xino_itrunc xino_itrunc;+		struct au_opt_add	add;+		struct au_opt_del	del;+		struct au_opt_mod	mod;+		int			dirwh;+		int			rdcache;+		unsigned int		rdblk;+		unsigned int		rdhash;+		int			udba;+		struct au_opt_wbr_create wbr_create;+		int			wbr_copyup;+	};+};++/* opts flags */+#define AuOpts_REMOUNT		1+#define AuOpts_REFRESH_DIR	(1 << 1)+#define AuOpts_REFRESH_NONDIR	(1 << 2)+#define AuOpts_TRUNC_XIB	(1 << 3)+#define au_ftest_opts(flags, name)	((flags) & AuOpts_##name)+#define au_fset_opts(flags, name)	{ (flags) |= AuOpts_##name; }+#define au_fclr_opts(flags, name)	{ (flags) &= ~AuOpts_##name; }++struct au_opts {+	struct au_opt	*opt;+	int		max_opt;++	unsigned int	given_udba;+	unsigned int	flags;+	unsigned long	sb_flags;+};++/* ---------------------------------------------------------------------- */++const char *au_optstr_br_perm(int brperm);+const char *au_optstr_udba(int udba);+const char *au_optstr_wbr_copyup(int wbr_copyup);+const char *au_optstr_wbr_create(int wbr_create);++void au_opts_free(struct au_opts *opts);+int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts);+int au_opts_verify(struct super_block *sb, unsigned long sb_flags,+		   unsigned int pending);+int au_opts_mount(struct super_block *sb, struct au_opts *opts);+int au_opts_remount(struct super_block *sb, struct au_opts *opts);++unsigned int au_opt_udba(struct super_block *sb);++/* ---------------------------------------------------------------------- */++#endif /* __KERNEL__ */+#endif /* __AUFS_OPTS_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/plink.c linux-2.6.31.5/fs/aufs/plink.c--- linux-2.6.31.5.orig/fs/aufs/plink.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/plink.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,354 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * pseudo-link+ */++#include "aufs.h"++/*+ * during a user process maintains the pseudo-links,+ * prohibit adding a new plink and branch manipulation.+ */+void au_plink_block_maintain(struct super_block *sb)+{+	struct au_sbinfo *sbi = au_sbi(sb);++	SiMustAnyLock(sb);++	/* gave up wake_up_bit() */+	wait_event(sbi->si_plink_wq, !au_ftest_si(sbi, MAINTAIN_PLINK));+}++/* ---------------------------------------------------------------------- */++struct pseudo_link {+	struct list_head list;+	struct inode *inode;+};++#ifdef CONFIG_AUFS_DEBUG+void au_plink_list(struct super_block *sb)+{+	struct au_sbinfo *sbinfo;+	struct list_head *plink_list;+	struct pseudo_link *plink;++	SiMustAnyLock(sb);++	sbinfo = au_sbi(sb);+	AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));++	plink_list = &sbinfo->si_plink.head;+	spin_lock(&sbinfo->si_plink.spin);+	list_for_each_entry(plink, plink_list, list)+		AuDbg("%lu\n", plink->inode->i_ino);+	spin_unlock(&sbinfo->si_plink.spin);+}+#endif++/* is the inode pseudo-linked? */+int au_plink_test(struct inode *inode)+{+	int found;+	struct au_sbinfo *sbinfo;+	struct list_head *plink_list;+	struct pseudo_link *plink;++	sbinfo = au_sbi(inode->i_sb);+	AuRwMustAnyLock(&sbinfo->si_rwsem);+	AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK));++	found = 0;+	plink_list = &sbinfo->si_plink.head;+	spin_lock(&sbinfo->si_plink.spin);+	list_for_each_entry(plink, plink_list, list)+		if (plink->inode == inode) {+			found = 1;+			break;+		}+	spin_unlock(&sbinfo->si_plink.spin);+	return found;+}++/* ---------------------------------------------------------------------- */++/*+ * generate a name for plink.+ * the file will be stored under AUFS_WH_PLINKDIR.+ */+/* 20 is max digits length of ulong 64 */+#define PLINK_NAME_LEN	((20 + 1) * 2)++static int plink_name(char *name, int len, struct inode *inode,+		      aufs_bindex_t bindex)+{+	int rlen;+	struct inode *h_inode;++	h_inode = au_h_iptr(inode, bindex);+	rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino);+	return rlen;+}++/* lookup the plink-ed @inode under the branch at @bindex */+struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex)+{+	struct dentry *h_dentry, *h_parent;+	struct au_branch *br;+	struct inode *h_dir;+	char a[PLINK_NAME_LEN];+	struct qstr tgtname = {+		.name	= a+	};++	br = au_sbr(inode->i_sb, bindex);+	h_parent = br->br_wbr->wbr_plink;+	h_dir = h_parent->d_inode;+	tgtname.len = plink_name(a, sizeof(a), inode, bindex);++	/* always superio. */+	mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2);+	h_dentry = au_sio_lkup_one(&tgtname, h_parent, br);+	mutex_unlock(&h_dir->i_mutex);+	return h_dentry;+}++/* create a pseudo-link */+static int do_whplink(struct qstr *tgt, struct dentry *h_parent,+		      struct dentry *h_dentry, struct au_branch *br)+{+	int err;+	struct path h_path = {+		.mnt = br->br_mnt+	};+	struct inode *h_dir;++	h_dir = h_parent->d_inode;+ again:+	h_path.dentry = au_lkup_one(tgt, h_parent, br, /*nd*/NULL);+	err = PTR_ERR(h_path.dentry);+	if (IS_ERR(h_path.dentry))+		goto out;++	err = 0;+	/* wh.plink dir is not monitored */+	if (h_path.dentry->d_inode+	    && h_path.dentry->d_inode != h_dentry->d_inode) {+		err = vfsub_unlink(h_dir, &h_path, /*force*/0);+		dput(h_path.dentry);+		h_path.dentry = NULL;+		if (!err)+			goto again;+	}+	if (!err && !h_path.dentry->d_inode)+		err = vfsub_link(h_dentry, h_dir, &h_path);+	dput(h_path.dentry);++ out:+	return err;+}++struct do_whplink_args {+	int *errp;+	struct qstr *tgt;+	struct dentry *h_parent;+	struct dentry *h_dentry;+	struct au_branch *br;+};++static void call_do_whplink(void *args)+{+	struct do_whplink_args *a = args;+	*a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br);+}++static int whplink(struct dentry *h_dentry, struct inode *inode,+		   aufs_bindex_t bindex, struct au_branch *br)+{+	int err, wkq_err;+	struct au_wbr *wbr;+	struct dentry *h_parent;+	struct inode *h_dir;+	char a[PLINK_NAME_LEN];+	struct qstr tgtname = {+		.name = a+	};++	wbr = au_sbr(inode->i_sb, bindex)->br_wbr;+	h_parent = wbr->wbr_plink;+	h_dir = h_parent->d_inode;+	tgtname.len = plink_name(a, sizeof(a), inode, bindex);++	/* always superio. */+	mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2);+	if (!au_test_wkq(current)) {+		struct do_whplink_args args = {+			.errp		= &err,+			.tgt		= &tgtname,+			.h_parent	= h_parent,+			.h_dentry	= h_dentry,+			.br		= br+		};+		wkq_err = au_wkq_wait(call_do_whplink, &args);+		if (unlikely(wkq_err))+			err = wkq_err;+	} else+		err = do_whplink(&tgtname, h_parent, h_dentry, br);+	mutex_unlock(&h_dir->i_mutex);++	return err;+}++/* free a single plink */+static void do_put_plink(struct pseudo_link *plink, int do_del)+{+	iput(plink->inode);+	if (do_del)+		list_del(&plink->list);+	kfree(plink);+}++/*+ * create a new pseudo-link for @h_dentry on @bindex.+ * the linked inode is held in aufs @inode.+ */+void au_plink_append(struct inode *inode, aufs_bindex_t bindex,+		     struct dentry *h_dentry)+{+	struct super_block *sb;+	struct au_sbinfo *sbinfo;+	struct list_head *plink_list;+	struct pseudo_link *plink;+	int found, err, cnt;++	sb = inode->i_sb;+	sbinfo = au_sbi(sb);+	AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));++	err = 0;+	cnt = 0;+	found = 0;+	plink_list = &sbinfo->si_plink.head;+	spin_lock(&sbinfo->si_plink.spin);+	list_for_each_entry(plink, plink_list, list) {+		cnt++;+		if (plink->inode == inode) {+			found = 1;+			break;+		}+	}+	if (found) {+		spin_unlock(&sbinfo->si_plink.spin);+		return;+	}++	plink = NULL;+	if (!found) {+		plink = kmalloc(sizeof(*plink), GFP_ATOMIC);+		if (plink) {+			plink->inode = au_igrab(inode);+			list_add(&plink->list, plink_list);+			cnt++;+		} else+			err = -ENOMEM;+	}+	spin_unlock(&sbinfo->si_plink.spin);++	if (!err) {+		au_plink_block_maintain(sb);+		err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex));+	}++	if (unlikely(cnt > AUFS_PLINK_WARN))+		AuWarn1("unexpectedly many pseudo links, %d\n", cnt);+	if (unlikely(err)) {+		AuWarn("err %d, damaged pseudo link.\n", err);+		if (!found && plink)+			do_put_plink(plink, /*do_del*/1);+	}+}++/* free all plinks */+void au_plink_put(struct super_block *sb)+{+	struct au_sbinfo *sbinfo;+	struct list_head *plink_list;+	struct pseudo_link *plink, *tmp;++	SiMustWriteLock(sb);++	sbinfo = au_sbi(sb);+	AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));++	plink_list = &sbinfo->si_plink.head;+	/* no spin_lock since sbinfo is write-locked */+	list_for_each_entry_safe(plink, tmp, plink_list, list)+		do_put_plink(plink, 0);+	INIT_LIST_HEAD(plink_list);+}++/* free the plinks on a branch specified by @br_id */+void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id)+{+	struct au_sbinfo *sbinfo;+	struct list_head *plink_list;+	struct pseudo_link *plink, *tmp;+	struct inode *inode;+	aufs_bindex_t bstart, bend, bindex;+	unsigned char do_put;++	SiMustWriteLock(sb);++	sbinfo = au_sbi(sb);+	AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));++	plink_list = &sbinfo->si_plink.head;+	/* no spin_lock since sbinfo is write-locked */+	list_for_each_entry_safe(plink, tmp, plink_list, list) {+		do_put = 0;+		inode = au_igrab(plink->inode);+		ii_write_lock_child(inode);+		bstart = au_ibstart(inode);+		bend = au_ibend(inode);+		if (bstart >= 0) {+			for (bindex = bstart; bindex <= bend; bindex++) {+				if (!au_h_iptr(inode, bindex)+				    || au_ii_br_id(inode, bindex) != br_id)+					continue;+				au_set_h_iptr(inode, bindex, NULL, 0);+				do_put = 1;+				break;+			}+		} else+			do_put_plink(plink, 1);++		if (do_put) {+			for (bindex = bstart; bindex <= bend; bindex++)+				if (au_h_iptr(inode, bindex)) {+					do_put = 0;+					break;+				}+			if (do_put)+				do_put_plink(plink, 1);+		}+		ii_write_unlock(inode);+		iput(inode);+	}+}diff -Nur linux-2.6.31.5.orig/fs/aufs/poll.c linux-2.6.31.5/fs/aufs/poll.c--- linux-2.6.31.5.orig/fs/aufs/poll.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/poll.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,56 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * poll operation+ * There is only one filesystem which implements ->poll operation, currently.+ */++#include "aufs.h"++unsigned int aufs_poll(struct file *file, poll_table *wait)+{+	unsigned int mask;+	int err;+	struct file *h_file;+	struct dentry *dentry;+	struct super_block *sb;++	/* We should pretend an error happened. */+	mask = POLLERR /* | POLLIN | POLLOUT */;+	dentry = file->f_dentry;+	sb = dentry->d_sb;+	si_read_lock(sb, AuLock_FLUSH);+	err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);+	if (unlikely(err))+		goto out;++	/* it is not an error if h_file has no operation */+	mask = DEFAULT_POLLMASK;+	h_file = au_h_fptr(file, au_fbstart(file));+	if (h_file->f_op && h_file->f_op->poll)+		mask = h_file->f_op->poll(h_file, wait);++	di_read_unlock(dentry, AuLock_IR);+	fi_read_unlock(file);++ out:+	si_read_unlock(sb);+	AuTraceErr((int)mask);+	return mask;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/rwsem.h linux-2.6.31.5/fs/aufs/rwsem.h--- linux-2.6.31.5.orig/fs/aufs/rwsem.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/rwsem.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,186 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * simple read-write semaphore wrappers+ */++#ifndef __AUFS_RWSEM_H__+#define __AUFS_RWSEM_H__++#ifdef __KERNEL__++#include <linux/rwsem.h>++struct au_rwsem {+	struct rw_semaphore	rwsem;+#ifdef CONFIG_AUFS_DEBUG+	/* just for debugging, not almighty counter */+	atomic_t		rcnt, wcnt;+#endif+};++#ifdef CONFIG_AUFS_DEBUG+#define AuDbgCntInit(rw) do { \+	atomic_set(&(rw)->rcnt, 0); \+	atomic_set(&(rw)->wcnt, 0); \+	smp_mb(); /* atomic set */ \+} while (0)++#define AuDbgRcntInc(rw)	atomic_inc_return(&(rw)->rcnt)+#define AuDbgRcntDec(rw)	WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0)+#define AuDbgWcntInc(rw)	WARN_ON(atomic_inc_return(&(rw)->wcnt) > 1)+#define AuDbgWcntDec(rw)	WARN_ON(atomic_dec_return(&(rw)->wcnt) < 0)+#else+#define AuDbgCntInit(rw)	do {} while (0)+#define AuDbgRcntInc(rw)	do {} while (0)+#define AuDbgRcntDec(rw)	do {} while (0)+#define AuDbgWcntInc(rw)	do {} while (0)+#define AuDbgWcntDec(rw)	do {} while (0)+#endif /* CONFIG_AUFS_DEBUG */++/* to debug easier, do not make them inlined functions */+#define AuRwMustNoWaiters(rw)	AuDebugOn(!list_empty(&(rw)->rwsem.wait_list))+/* rwsem_is_locked() is unusable */+#define AuRwMustReadLock(rw)	AuDebugOn(atomic_read(&(rw)->rcnt) <= 0)+#define AuRwMustWriteLock(rw)	AuDebugOn(atomic_read(&(rw)->wcnt) <= 0)+#define AuRwMustAnyLock(rw)	AuDebugOn(atomic_read(&(rw)->rcnt) <= 0 \+					&& atomic_read(&(rw)->wcnt) <= 0)+#define AuRwDestroy(rw)		AuDebugOn(atomic_read(&(rw)->rcnt) \+					|| atomic_read(&(rw)->wcnt))++static inline void au_rw_init(struct au_rwsem *rw)+{+	AuDbgCntInit(rw);+	init_rwsem(&rw->rwsem);+}++static inline void au_rw_init_wlock(struct au_rwsem *rw)+{+	au_rw_init(rw);+	down_write(&rw->rwsem);+	AuDbgWcntInc(rw);+}++static inline void au_rw_init_wlock_nested(struct au_rwsem *rw,+					   unsigned int lsc)+{+	au_rw_init(rw);+	down_write_nested(&rw->rwsem, lsc);+	AuDbgWcntInc(rw);+}++static inline void au_rw_read_lock(struct au_rwsem *rw)+{+	down_read(&rw->rwsem);+	AuDbgRcntInc(rw);+}++static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc)+{+	down_read_nested(&rw->rwsem, lsc);+	AuDbgRcntInc(rw);+}++static inline void au_rw_read_unlock(struct au_rwsem *rw)+{+	AuRwMustReadLock(rw);+	AuDbgRcntDec(rw);+	up_read(&rw->rwsem);+}++static inline void au_rw_dgrade_lock(struct au_rwsem *rw)+{+	AuRwMustWriteLock(rw);+	AuDbgRcntInc(rw);+	AuDbgWcntDec(rw);+	downgrade_write(&rw->rwsem);+}++static inline void au_rw_write_lock(struct au_rwsem *rw)+{+	down_write(&rw->rwsem);+	AuDbgWcntInc(rw);+}++static inline void au_rw_write_lock_nested(struct au_rwsem *rw,+					   unsigned int lsc)+{+	down_write_nested(&rw->rwsem, lsc);+	AuDbgWcntInc(rw);+}++static inline void au_rw_write_unlock(struct au_rwsem *rw)+{+	AuRwMustWriteLock(rw);+	AuDbgWcntDec(rw);+	up_write(&rw->rwsem);+}++/* why is not _nested version defined */+static inline int au_rw_read_trylock(struct au_rwsem *rw)+{+	int ret = down_read_trylock(&rw->rwsem);+	if (ret)+		AuDbgRcntInc(rw);+	return ret;+}++static inline int au_rw_write_trylock(struct au_rwsem *rw)+{+	int ret = down_write_trylock(&rw->rwsem);+	if (ret)+		AuDbgWcntInc(rw);+	return ret;+}++#undef AuDbgCntInit+#undef AuDbgRcntInc+#undef AuDbgRcntDec+#undef AuDbgWcntInc+#undef AuDbgWcntDec++#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \+static inline void prefix##_read_lock(param) \+{ au_rw_read_lock(rwsem); } \+static inline void prefix##_write_lock(param) \+{ au_rw_write_lock(rwsem); } \+static inline int prefix##_read_trylock(param) \+{ return au_rw_read_trylock(rwsem); } \+static inline int prefix##_write_trylock(param) \+{ return au_rw_write_trylock(rwsem); }+/* why is not _nested version defined */+/* static inline void prefix##_read_trylock_nested(param, lsc)+{ au_rw_read_trylock_nested(rwsem, lsc)); }+static inline void prefix##_write_trylock_nestd(param, lsc)+{ au_rw_write_trylock_nested(rwsem, lsc); } */++#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \+static inline void prefix##_read_unlock(param) \+{ au_rw_read_unlock(rwsem); } \+static inline void prefix##_write_unlock(param) \+{ au_rw_write_unlock(rwsem); } \+static inline void prefix##_downgrade_lock(param) \+{ au_rw_dgrade_lock(rwsem); }++#define AuSimpleRwsemFuncs(prefix, param, rwsem) \+	AuSimpleLockRwsemFuncs(prefix, param, rwsem) \+	AuSimpleUnlockRwsemFuncs(prefix, param, rwsem)++#endif /* __KERNEL__ */+#endif /* __AUFS_RWSEM_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/sbinfo.c linux-2.6.31.5/fs/aufs/sbinfo.c--- linux-2.6.31.5.orig/fs/aufs/sbinfo.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/sbinfo.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,208 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * superblock private data+ */++#include "aufs.h"++/*+ * they are necessary regardless sysfs is disabled.+ */+void au_si_free(struct kobject *kobj)+{+	struct au_sbinfo *sbinfo;+	struct super_block *sb;++	sbinfo = container_of(kobj, struct au_sbinfo, si_kobj);+	AuDebugOn(!list_empty(&sbinfo->si_plink.head));++	sb = sbinfo->si_sb;+	si_write_lock(sb);+	au_xino_clr(sb);+	au_br_free(sbinfo);+	kfree(sbinfo->si_branch);+	mutex_destroy(&sbinfo->si_xib_mtx);+	si_write_unlock(sb);+	AuRwDestroy(&sbinfo->si_rwsem);++	kfree(sbinfo);+}++int au_si_alloc(struct super_block *sb)+{+	int err;+	struct au_sbinfo *sbinfo;++	err = -ENOMEM;+	sbinfo = kmalloc(sizeof(*sbinfo), GFP_NOFS);+	if (unlikely(!sbinfo))+		goto out;++	/* will be reallocated separately */+	sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS);+	if (unlikely(!sbinfo->si_branch))+		goto out_sbinfo;++	memset(&sbinfo->si_kobj, 0, sizeof(sbinfo->si_kobj));+	err = sysaufs_si_init(sbinfo);+	if (unlikely(err))+		goto out_br;++	au_nwt_init(&sbinfo->si_nowait);+	au_rw_init_wlock(&sbinfo->si_rwsem);+	sbinfo->si_generation = 0;+	sbinfo->au_si_status = 0;+	sbinfo->si_bend = -1;+	sbinfo->si_last_br_id = 0;++	sbinfo->si_wbr_copyup = AuWbrCopyup_Def;+	sbinfo->si_wbr_create = AuWbrCreate_Def;+	sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + AuWbrCopyup_Def;+	sbinfo->si_wbr_create_ops = au_wbr_create_ops + AuWbrCreate_Def;++	sbinfo->si_mntflags = AuOpt_Def;++	sbinfo->si_xread = NULL;+	sbinfo->si_xwrite = NULL;+	sbinfo->si_xib = NULL;+	mutex_init(&sbinfo->si_xib_mtx);+	sbinfo->si_xib_buf = NULL;+	sbinfo->si_xino_brid = -1;+	/* leave si_xib_last_pindex and si_xib_next_bit */++	sbinfo->si_rdcache = AUFS_RDCACHE_DEF * HZ;+	sbinfo->si_rdblk = AUFS_RDBLK_DEF;+	sbinfo->si_rdhash = AUFS_RDHASH_DEF;+	sbinfo->si_dirwh = AUFS_DIRWH_DEF;++	au_spl_init(&sbinfo->si_plink);+	init_waitqueue_head(&sbinfo->si_plink_wq);++	/* leave other members for sysaufs and si_mnt. */+	sbinfo->si_sb = sb;+	sb->s_fs_info = sbinfo;+	au_debug_sbinfo_init(sbinfo);+	return 0; /* success */++ out_br:+	kfree(sbinfo->si_branch);+ out_sbinfo:+	kfree(sbinfo);+ out:+	return err;+}++int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr)+{+	int err, sz;+	struct au_branch **brp;++	AuRwMustWriteLock(&sbinfo->si_rwsem);++	err = -ENOMEM;+	sz = sizeof(*brp) * (sbinfo->si_bend + 1);+	if (unlikely(!sz))+		sz = sizeof(*brp);+	brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS);+	if (brp) {+		sbinfo->si_branch = brp;+		err = 0;+	}++	return err;+}++/* ---------------------------------------------------------------------- */++unsigned int au_sigen_inc(struct super_block *sb)+{+	unsigned int gen;++	SiMustWriteLock(sb);++	gen = ++au_sbi(sb)->si_generation;+	au_update_digen(sb->s_root);+	au_update_iigen(sb->s_root->d_inode);+	sb->s_root->d_inode->i_version++;+	return gen;+}++aufs_bindex_t au_new_br_id(struct super_block *sb)+{+	aufs_bindex_t br_id;+	int i;+	struct au_sbinfo *sbinfo;++	SiMustWriteLock(sb);++	sbinfo = au_sbi(sb);+	for (i = 0; i <= AUFS_BRANCH_MAX; i++) {+		br_id = ++sbinfo->si_last_br_id;+		if (br_id && au_br_index(sb, br_id) < 0)+			return br_id;+	}++	return -1;+}++/* ---------------------------------------------------------------------- */++/* dentry and super_block lock. call at entry point */+void aufs_read_lock(struct dentry *dentry, int flags)+{+	si_read_lock(dentry->d_sb, flags);+	if (au_ftest_lock(flags, DW))+		di_write_lock_child(dentry);+	else+		di_read_lock_child(dentry, flags);+}++void aufs_read_unlock(struct dentry *dentry, int flags)+{+	if (au_ftest_lock(flags, DW))+		di_write_unlock(dentry);+	else+		di_read_unlock(dentry, flags);+	si_read_unlock(dentry->d_sb);+}++void aufs_write_lock(struct dentry *dentry)+{+	si_write_lock(dentry->d_sb);+	di_write_lock_child(dentry);+}++void aufs_write_unlock(struct dentry *dentry)+{+	di_write_unlock(dentry);+	si_write_unlock(dentry->d_sb);+}++void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags)+{+	si_read_lock(d1->d_sb, flags);+	di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR));+}++void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2)+{+	di_write_unlock2(d1, d2);+	si_read_unlock(d1->d_sb);+}diff -Nur linux-2.6.31.5.orig/fs/aufs/spl.h linux-2.6.31.5/fs/aufs/spl.h--- linux-2.6.31.5.orig/fs/aufs/spl.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/spl.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,57 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * simple list protected by a spinlock+ */++#ifndef __AUFS_SPL_H__+#define __AUFS_SPL_H__++#ifdef __KERNEL__++#include <linux/spinlock.h>+#include <linux/list.h>++struct au_splhead {+	spinlock_t		spin;+	struct list_head	head;+};++static inline void au_spl_init(struct au_splhead *spl)+{+	spin_lock_init(&spl->spin);+	INIT_LIST_HEAD(&spl->head);+}++static inline void au_spl_add(struct list_head *list, struct au_splhead *spl)+{+	spin_lock(&spl->spin);+	list_add(list, &spl->head);+	spin_unlock(&spl->spin);+}++static inline void au_spl_del(struct list_head *list, struct au_splhead *spl)+{+	spin_lock(&spl->spin);+	list_del(list);+	spin_unlock(&spl->spin);+}++#endif /* __KERNEL__ */+#endif /* __AUFS_SPL_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/super.c linux-2.6.31.5/fs/aufs/super.c--- linux-2.6.31.5.orig/fs/aufs/super.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/super.c	2009-11-15 22:20:26.000000000 +0100@@ -0,0 +1,874 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * mount and super_block operations+ */++#include <linux/buffer_head.h>+#include <linux/module.h>+#include <linux/seq_file.h>+#include <linux/statfs.h>+#include "aufs.h"++/*+ * super_operations+ */+static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused)+{+	struct au_icntnr *c;++	c = au_cache_alloc_icntnr();+	if (c) {+		inode_init_once(&c->vfs_inode);+		c->vfs_inode.i_version = 1; /* sigen(sb); */+		c->iinfo.ii_hinode = NULL;+		return &c->vfs_inode;+	}+	return NULL;+}++static void aufs_destroy_inode(struct inode *inode)+{+	au_iinfo_fin(inode);+	au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode));+}++struct inode *au_iget_locked(struct super_block *sb, ino_t ino)+{+	struct inode *inode;+	int err;++	inode = iget_locked(sb, ino);+	if (unlikely(!inode)) {+		inode = ERR_PTR(-ENOMEM);+		goto out;+	}+	if (!(inode->i_state & I_NEW))+		goto out;++	err = au_xigen_new(inode);+	if (!err)+		err = au_iinfo_init(inode);+	if (!err)+		inode->i_version++;+	else {+		iget_failed(inode);+		inode = ERR_PTR(err);+	}++ out:+	/* never return NULL */+	AuDebugOn(!inode);+	AuTraceErrPtr(inode);+	return inode;+}++/* lock free root dinfo */+static int au_show_brs(struct seq_file *seq, struct super_block *sb)+{+	int err;+	aufs_bindex_t bindex, bend;+	struct path path;+	struct au_hdentry *hd;+	struct au_branch *br;++	err = 0;+	bend = au_sbend(sb);+	hd = au_di(sb->s_root)->di_hdentry;+	for (bindex = 0; !err && bindex <= bend; bindex++) {+		br = au_sbr(sb, bindex);+		path.mnt = br->br_mnt;+		path.dentry = hd[bindex].hd_dentry;+		err = au_seq_path(seq, &path);+		if (err > 0)+			err = seq_printf(seq, "=%s",+					 au_optstr_br_perm(br->br_perm));+		if (!err && bindex != bend)+			err = seq_putc(seq, ':');+	}++	return err;+}++static void au_show_wbr_create(struct seq_file *m, int v,+			       struct au_sbinfo *sbinfo)+{+	const char *pat;++	AuRwMustAnyLock(&sbinfo->si_rwsem);++	seq_printf(m, ",create=");+	pat = au_optstr_wbr_create(v);+	switch (v) {+	case AuWbrCreate_TDP:+	case AuWbrCreate_RR:+	case AuWbrCreate_MFS:+	case AuWbrCreate_PMFS:+		seq_printf(m, pat);+		break;+	case AuWbrCreate_MFSV:+		seq_printf(m, /*pat*/"mfs:%lu",+			   sbinfo->si_wbr_mfs.mfs_expire / HZ);+		break;+	case AuWbrCreate_PMFSV:+		seq_printf(m, /*pat*/"pmfs:%lu",+			   sbinfo->si_wbr_mfs.mfs_expire / HZ);+		break;+	case AuWbrCreate_MFSRR:+		seq_printf(m, /*pat*/"mfsrr:%llu",+			   sbinfo->si_wbr_mfs.mfsrr_watermark);+		break;+	case AuWbrCreate_MFSRRV:+		seq_printf(m, /*pat*/"mfsrr:%llu:%lu",+			   sbinfo->si_wbr_mfs.mfsrr_watermark,+			   sbinfo->si_wbr_mfs.mfs_expire / HZ);+		break;+	}+}++static int au_show_xino(struct seq_file *seq, struct vfsmount *mnt)+{+#ifdef CONFIG_SYSFS+	return 0;+#else+	int err;+	const int len = sizeof(AUFS_XINO_FNAME) - 1;+	aufs_bindex_t bindex, brid;+	struct super_block *sb;+	struct qstr *name;+	struct file *f;+	struct dentry *d, *h_root;++	AuRwMustAnyLock(&sbinfo->si_rwsem);++	err = 0;+	sb = mnt->mnt_sb;+	f = au_sbi(sb)->si_xib;+	if (!f)+		goto out;++	/* stop printing the default xino path on the first writable branch */+	h_root = NULL;+	brid = au_xino_brid(sb);+	if (brid >= 0) {+		bindex = au_br_index(sb, brid);+		h_root = au_di(sb->s_root)->di_hdentry[0 + bindex].hd_dentry;+	}+	d = f->f_dentry;+	name = &d->d_name;+	/* safe ->d_parent because the file is unlinked */+	if (d->d_parent == h_root+	    && name->len == len+	    && !memcmp(name->name, AUFS_XINO_FNAME, len))+		goto out;++	seq_puts(seq, ",xino=");+	err = au_xino_path(seq, f);++ out:+	return err;+#endif+}++/* seq_file will re-call me in case of too long string */+static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt)+{+	int err, n;+	unsigned int mnt_flags, v;+	struct super_block *sb;+	struct au_sbinfo *sbinfo;++#define AuBool(name, str) do { \+	v = au_opt_test(mnt_flags, name); \+	if (v != au_opt_test(AuOpt_Def, name)) \+		seq_printf(m, ",%s" #str, v ? "" : "no"); \+} while (0)++#define AuStr(name, str) do { \+	v = mnt_flags & AuOptMask_##name; \+	if (v != (AuOpt_Def & AuOptMask_##name)) \+		seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \+} while (0)++#define AuUInt(name, str, val) do { \+	if (val != AUFS_##name##_DEF) \+		seq_printf(m, "," #str "=%u", val); \+} while (0)++	/* lock free root dinfo */+	sb = mnt->mnt_sb;+	si_noflush_read_lock(sb);+	sbinfo = au_sbi(sb);+	seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo));++	mnt_flags = au_mntflags(sb);+	if (au_opt_test(mnt_flags, XINO)) {+		err = au_show_xino(m, mnt);+		if (unlikely(err))+			goto out;+	} else+		seq_puts(m, ",noxino");++	AuBool(TRUNC_XINO, trunc_xino);+	AuStr(UDBA, udba);+	AuBool(SHWH, shwh);+	AuBool(PLINK, plink);+	/* AuBool(DIRPERM1, dirperm1); */+	/* AuBool(REFROF, refrof); */++	v = sbinfo->si_wbr_create;+	if (v != AuWbrCreate_Def)+		au_show_wbr_create(m, v, sbinfo);++	v = sbinfo->si_wbr_copyup;+	if (v != AuWbrCopyup_Def)+		seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v));++	v = au_opt_test(mnt_flags, ALWAYS_DIROPQ);+	if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ))+		seq_printf(m, ",diropq=%c", v ? 'a' : 'w');++	AuUInt(DIRWH, dirwh, sbinfo->si_dirwh);++	n = sbinfo->si_rdcache / HZ;+	AuUInt(RDCACHE, rdcache, n);++	AuUInt(RDBLK, rdblk, sbinfo->si_rdblk);+	AuUInt(RDHASH, rdhash, sbinfo->si_rdhash);++	AuBool(SUM, sum);+	/* AuBool(SUM_W, wsum); */+	AuBool(WARN_PERM, warn_perm);+	AuBool(VERBOSE, verbose);++ out:+	/* be sure to print "br:" last */+	if (!sysaufs_brs) {+		seq_puts(m, ",br:");+		au_show_brs(m, sb);+	}+	si_read_unlock(sb);+	return 0;++#undef Deleted+#undef AuBool+#undef AuStr+}++/* ---------------------------------------------------------------------- */++/* sum mode which returns the summation for statfs(2) */++static u64 au_add_till_max(u64 a, u64 b)+{+	u64 old;++	old = a;+	a += b;+	if (old < a)+		return a;+	return ULLONG_MAX;+}++static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf)+{+	int err;+	u64 blocks, bfree, bavail, files, ffree;+	aufs_bindex_t bend, bindex, i;+	unsigned char shared;+	struct vfsmount *h_mnt;+	struct super_block *h_sb;++	blocks = 0;+	bfree = 0;+	bavail = 0;+	files = 0;+	ffree = 0;++	err = 0;+	bend = au_sbend(sb);+	for (bindex = bend; bindex >= 0; bindex--) {+		h_mnt = au_sbr_mnt(sb, bindex);+		h_sb = h_mnt->mnt_sb;+		shared = 0;+		for (i = bindex + 1; !shared && i <= bend; i++)+			shared = (au_sbr_sb(sb, i) == h_sb);+		if (shared)+			continue;++		/* sb->s_root for NFS is unreliable */+		err = vfs_statfs(h_mnt->mnt_root, buf);+		if (unlikely(err))+			goto out;++		blocks = au_add_till_max(blocks, buf->f_blocks);+		bfree = au_add_till_max(bfree, buf->f_bfree);+		bavail = au_add_till_max(bavail, buf->f_bavail);+		files = au_add_till_max(files, buf->f_files);+		ffree = au_add_till_max(ffree, buf->f_ffree);+	}++	buf->f_blocks = blocks;+	buf->f_bfree = bfree;+	buf->f_bavail = bavail;+	buf->f_files = files;+	buf->f_ffree = ffree;++ out:+	return err;+}++static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf)+{+	int err;+	struct super_block *sb;++	/* lock free root dinfo */+	sb = dentry->d_sb;+	si_noflush_read_lock(sb);+	if (!au_opt_test(au_mntflags(sb), SUM))+		/* sb->s_root for NFS is unreliable */+		err = vfs_statfs(au_sbr_mnt(sb, 0)->mnt_root, buf);+	else+		err = au_statfs_sum(sb, buf);+	si_read_unlock(sb);++	if (!err) {+		buf->f_type = AUFS_SUPER_MAGIC;+		buf->f_namelen -= AUFS_WH_PFX_LEN;+		memset(&buf->f_fsid, 0, sizeof(buf->f_fsid));+	}+	/* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */++	return err;+}++/* ---------------------------------------------------------------------- */++/* try flushing the lower fs at aufs remount/unmount time */++static void au_fsync_br(struct super_block *sb)+{+	aufs_bindex_t bend, bindex;+	int brperm;+	struct au_branch *br;+	struct super_block *h_sb;++	bend = au_sbend(sb);+	for (bindex = 0; bindex < bend; bindex++) {+		br = au_sbr(sb, bindex);+		brperm = br->br_perm;+		if (brperm == AuBrPerm_RR || brperm == AuBrPerm_RRWH)+			continue;+		h_sb = br->br_mnt->mnt_sb;+		if (bdev_read_only(h_sb->s_bdev))+			continue;++		lockdep_off();+		down_write(&h_sb->s_umount);+		shrink_dcache_sb(h_sb);+		sync_filesystem(h_sb);+		up_write(&h_sb->s_umount);+		lockdep_on();+	}+}++/*+ * this IS NOT for super_operations.+ * I guess it will be reverted someday.+ */+static void aufs_umount_begin(struct super_block *sb)+{+	struct au_sbinfo *sbinfo;++	sbinfo = au_sbi(sb);+	if (!sbinfo)+		return;++	si_write_lock(sb);+	au_fsync_br(sb);+	if (au_opt_test(au_mntflags(sb), PLINK))+		au_plink_put(sb);+	if (sbinfo->si_wbr_create_ops->fin)+		sbinfo->si_wbr_create_ops->fin(sb);+	si_write_unlock(sb);+}++/* final actions when unmounting a file system */+static void aufs_put_super(struct super_block *sb)+{+	struct au_sbinfo *sbinfo;++	sbinfo = au_sbi(sb);+	if (!sbinfo)+		return;++	aufs_umount_begin(sb);+	dbgaufs_si_fin(sbinfo);+	kobject_put(&sbinfo->si_kobj);+}++/* ---------------------------------------------------------------------- */++/*+ * refresh dentry and inode at remount time.+ */+static int do_refresh(struct dentry *dentry, mode_t type,+		      unsigned int dir_flags)+{+	int err;+	struct dentry *parent;++	di_write_lock_child(dentry);+	parent = dget_parent(dentry);+	di_read_lock_parent(parent, AuLock_IR);++	/* returns the number of positive dentries */+	err = au_refresh_hdentry(dentry, type);+	if (err >= 0) {+		struct inode *inode = dentry->d_inode;+		err = au_refresh_hinode(inode, dentry);+		if (!err && type == S_IFDIR)+			au_reset_hinotify(inode, dir_flags);+	}+	if (unlikely(err))+		AuErr("unrecoverable error %d, %.*s\n", err, AuDLNPair(dentry));++	di_read_unlock(parent, AuLock_IR);+	dput(parent);+	di_write_unlock(dentry);++	return err;+}++static int test_dir(struct dentry *dentry, void *arg __maybe_unused)+{+	return S_ISDIR(dentry->d_inode->i_mode);+}++/* gave up consolidating with refresh_nondir() */+static int refresh_dir(struct dentry *root, unsigned int sigen)+{+	int err, i, j, ndentry, e;+	struct au_dcsub_pages dpages;+	struct au_dpage *dpage;+	struct dentry **dentries;+	struct inode *inode;+	const unsigned int flags = au_hi_flags(root->d_inode, /*isdir*/1);++	err = 0;+	list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list)+		if (S_ISDIR(inode->i_mode) && au_iigen(inode) != sigen) {+			ii_write_lock_child(inode);+			e = au_refresh_hinode_self(inode, /*do_attr*/1);+			ii_write_unlock(inode);+			if (unlikely(e)) {+				AuDbg("e %d, i%lu\n", e, inode->i_ino);+				if (!err)+					err = e;+				/* go on even if err */+			}+		}++	e = au_dpages_init(&dpages, GFP_NOFS);+	if (unlikely(e)) {+		if (!err)+			err = e;+		goto out;+	}+	e = au_dcsub_pages(&dpages, root, test_dir, NULL);+	if (unlikely(e)) {+		if (!err)+			err = e;+		goto out_dpages;+	}++	for (i = 0; !e && i < dpages.ndpage; i++) {+		dpage = dpages.dpages + i;+		dentries = dpage->dentries;+		ndentry = dpage->ndentry;+		for (j = 0; !e && j < ndentry; j++) {+			struct dentry *d;++			d = dentries[j];+			au_dbg_verify_dir_parent(d, sigen);+			if (au_digen(d) != sigen) {+				e = do_refresh(d, S_IFDIR, flags);+				if (unlikely(e && !err))+					err = e;+				/* break on err */+			}+		}+	}++ out_dpages:+	au_dpages_free(&dpages);+ out:+	return err;+}++static int test_nondir(struct dentry *dentry, void *arg __maybe_unused)+{+	return !S_ISDIR(dentry->d_inode->i_mode);+}++static int refresh_nondir(struct dentry *root, unsigned int sigen,+			  int do_dentry)+{+	int err, i, j, ndentry, e;+	struct au_dcsub_pages dpages;+	struct au_dpage *dpage;+	struct dentry **dentries;+	struct inode *inode;++	err = 0;+	list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list)+		if (!S_ISDIR(inode->i_mode) && au_iigen(inode) != sigen) {+			ii_write_lock_child(inode);+			e = au_refresh_hinode_self(inode, /*do_attr*/1);+			ii_write_unlock(inode);+			if (unlikely(e)) {+				AuDbg("e %d, i%lu\n", e, inode->i_ino);+				if (!err)+					err = e;+				/* go on even if err */+			}+		}++	if (!do_dentry)+		goto out;++	e = au_dpages_init(&dpages, GFP_NOFS);+	if (unlikely(e)) {+		if (!err)+			err = e;+		goto out;+	}+	e = au_dcsub_pages(&dpages, root, test_nondir, NULL);+	if (unlikely(e)) {+		if (!err)+			err = e;+		goto out_dpages;+	}++	for (i = 0; i < dpages.ndpage; i++) {+		dpage = dpages.dpages + i;+		dentries = dpage->dentries;+		ndentry = dpage->ndentry;+		for (j = 0; j < ndentry; j++) {+			struct dentry *d;++			d = dentries[j];+			au_dbg_verify_nondir_parent(d, sigen);+			inode = d->d_inode;+			if (inode && au_digen(d) != sigen) {+				e = do_refresh(d, inode->i_mode & S_IFMT,+					       /*dir_flags*/0);+				if (unlikely(e && !err))+					err = e;+				/* go on even err */+			}+		}+	}++ out_dpages:+	au_dpages_free(&dpages);+ out:+	return err;+}++static void au_remount_refresh(struct super_block *sb, unsigned int flags)+{+	int err;+	unsigned int sigen;+	struct au_sbinfo *sbinfo;+	struct dentry *root;+	struct inode *inode;++	au_sigen_inc(sb);+	sigen = au_sigen(sb);+	sbinfo = au_sbi(sb);+	au_fclr_si(sbinfo, FAILED_REFRESH_DIRS);++	root = sb->s_root;+	DiMustNoWaiters(root);+	inode = root->d_inode;+	IiMustNoWaiters(inode);+	au_reset_hinotify(inode, au_hi_flags(inode, /*isdir*/1));+	di_write_unlock(root);++	err = refresh_dir(root, sigen);+	if (unlikely(err)) {+		au_fset_si(sbinfo, FAILED_REFRESH_DIRS);+		AuWarn("Refreshing directories failed, ignored (%d)\n", err);+	}++	if (au_ftest_opts(flags, REFRESH_NONDIR)) {+		err = refresh_nondir(root, sigen, !err);+		if (unlikely(err))+			AuWarn("Refreshing non-directories failed, ignored"+			       "(%d)\n", err);+	}++	/* aufs_write_lock() calls ..._child() */+	di_write_lock_child(root);+	au_cpup_attr_all(root->d_inode, /*force*/1);+}++/* stop extra interpretation of errno in mount(8), and strange error messages */+static int cvt_err(int err)+{+	AuTraceErr(err);++	switch (err) {+	case -ENOENT:+	case -ENOTDIR:+	case -EEXIST:+	case -EIO:+		err = -EINVAL;+	}+	return err;+}++static int aufs_remount_fs(struct super_block *sb, int *flags, char *data)+{+	int err;+	struct au_opts opts;+	struct dentry *root;+	struct inode *inode;+	struct au_sbinfo *sbinfo;++	err = 0;+	root = sb->s_root;+	if (!data || !*data) {+		aufs_write_lock(root);+		err = au_opts_verify(sb, *flags, /*pending*/0);+		if (!err)+			au_fsync_br(sb);+		aufs_write_unlock(root);+		goto out;+	}++	err = -ENOMEM;+	memset(&opts, 0, sizeof(opts));+	opts.opt = (void *)__get_free_page(GFP_NOFS);+	if (unlikely(!opts.opt))+		goto out;+	opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);+	opts.flags = AuOpts_REMOUNT;+	opts.sb_flags = *flags;++	/* parse it before aufs lock */+	err = au_opts_parse(sb, data, &opts);+	if (unlikely(err))+		goto out_opts;++	sbinfo = au_sbi(sb);+	inode = root->d_inode;+	mutex_lock(&inode->i_mutex);+	aufs_write_lock(root);+	au_fsync_br(sb);++	/* au_opts_remount() may return an error */+	err = au_opts_remount(sb, &opts);+	au_opts_free(&opts);++	if (au_ftest_opts(opts.flags, REFRESH_DIR)+	    || au_ftest_opts(opts.flags, REFRESH_NONDIR))+		au_remount_refresh(sb, opts.flags);++	aufs_write_unlock(root);+	mutex_unlock(&inode->i_mutex);++ out_opts:+	free_page((unsigned long)opts.opt);+ out:+	err = cvt_err(err);+	AuTraceErr(err);+	return err;+}++static struct super_operations aufs_sop = {+	.alloc_inode	= aufs_alloc_inode,+	.destroy_inode	= aufs_destroy_inode,+	.drop_inode	= generic_delete_inode,+	.show_options	= aufs_show_options,+	.statfs		= aufs_statfs,+	.put_super	= aufs_put_super,+	.remount_fs	= aufs_remount_fs+};++/* ---------------------------------------------------------------------- */++static int alloc_root(struct super_block *sb)+{+	int err;+	struct inode *inode;+	struct dentry *root;++	err = -ENOMEM;+	inode = au_iget_locked(sb, AUFS_ROOT_INO);+	err = PTR_ERR(inode);+	if (IS_ERR(inode))+		goto out;++	inode->i_op = &aufs_dir_iop;+	inode->i_fop = &aufs_dir_fop;+	inode->i_mode = S_IFDIR;+	inode->i_nlink = 2;+	unlock_new_inode(inode);++	root = d_alloc_root(inode);+	if (unlikely(!root))+		goto out_iput;+	err = PTR_ERR(root);+	if (IS_ERR(root))+		goto out_iput;++	err = au_alloc_dinfo(root);+	if (!err) {+		sb->s_root = root;+		return 0; /* success */+	}+	dput(root);+	goto out; /* do not iput */++ out_iput:+	iget_failed(inode);+	iput(inode);+ out:+	return err;++}++static int aufs_fill_super(struct super_block *sb, void *raw_data,+			   int silent __maybe_unused)+{+	int err;+	struct au_opts opts;+	struct dentry *root;+	struct inode *inode;+	char *arg = raw_data;++	if (unlikely(!arg || !*arg)) {+		err = -EINVAL;+		AuErr("no arg\n");+		goto out;+	}++	err = -ENOMEM;+	memset(&opts, 0, sizeof(opts));+	opts.opt = (void *)__get_free_page(GFP_NOFS);+	if (unlikely(!opts.opt))+		goto out;+	opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);+	opts.sb_flags = sb->s_flags;++	err = au_si_alloc(sb);+	if (unlikely(err))+		goto out_opts;++	/* all timestamps always follow the ones on the branch */+	sb->s_flags |= MS_NOATIME | MS_NODIRATIME;+	sb->s_op = &aufs_sop;+	sb->s_magic = AUFS_SUPER_MAGIC;+	sb->s_maxbytes = 0;+	au_export_init(sb);++	err = alloc_root(sb);+	if (unlikely(err)) {+		si_write_unlock(sb);+		goto out_info;+	}+	root = sb->s_root;+	inode = root->d_inode;++	/*+	 * actually we can parse options regardless aufs lock here.+	 * but at remount time, parsing must be done before aufs lock.+	 * so we follow the same rule.+	 */+	ii_write_lock_parent(inode);+	aufs_write_unlock(root);+	err = au_opts_parse(sb, arg, &opts);+	if (unlikely(err))+		goto out_root;++	/* lock vfs_inode first, then aufs. */+	mutex_lock(&inode->i_mutex);+	inode->i_op = &aufs_dir_iop;+	inode->i_fop = &aufs_dir_fop;+	aufs_write_lock(root);+	err = au_opts_mount(sb, &opts);+	au_opts_free(&opts);+	if (unlikely(err))+		goto out_unlock;+	aufs_write_unlock(root);+	mutex_unlock(&inode->i_mutex);+	goto out_opts; /* success */++ out_unlock:+	aufs_write_unlock(root);+	mutex_unlock(&inode->i_mutex);+ out_root:+	dput(root);+	sb->s_root = NULL;+ out_info:+	kobject_put(&au_sbi(sb)->si_kobj);+	sb->s_fs_info = NULL;+ out_opts:+	free_page((unsigned long)opts.opt);+ out:+	AuTraceErr(err);+	err = cvt_err(err);+	AuTraceErr(err);+	return err;+}++/* ---------------------------------------------------------------------- */++static int aufs_get_sb(struct file_system_type *fs_type, int flags,+		       const char *dev_name __maybe_unused, void *raw_data,+		       struct vfsmount *mnt)+{+	int err;+	struct super_block *sb;++	/* all timestamps always follow the ones on the branch */+	/* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */+	err = get_sb_nodev(fs_type, flags, raw_data, aufs_fill_super, mnt);+	if (!err) {+		sb = mnt->mnt_sb;+		si_write_lock(sb);+		sysaufs_brs_add(sb, 0);+		si_write_unlock(sb);+	}+	return err;+}++struct file_system_type aufs_fs_type = {+	.name		= AUFS_FSTYPE,+	.fs_flags	=+		FS_RENAME_DOES_D_MOVE	/* a race between rename and others */+		| FS_REVAL_DOT,		/* for NFS branch and udba */+	.get_sb		= aufs_get_sb,+	.kill_sb	= generic_shutdown_super,+	/* no need to __module_get() and module_put(). */+	.owner		= THIS_MODULE,+};diff -Nur linux-2.6.31.5.orig/fs/aufs/super.h linux-2.6.31.5/fs/aufs/super.h--- linux-2.6.31.5.orig/fs/aufs/super.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/super.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,384 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * super_block operations+ */++#ifndef __AUFS_SUPER_H__+#define __AUFS_SUPER_H__++#ifdef __KERNEL__++#include <linux/fs.h>+#include <linux/aufs_type.h>+#include "rwsem.h"+#include "spl.h"+#include "wkq.h"++typedef ssize_t (*au_readf_t)(struct file *, char __user *, size_t, loff_t *);+typedef ssize_t (*au_writef_t)(struct file *, const char __user *, size_t,+			       loff_t *);++/* policies to select one among multiple writable branches */+struct au_wbr_copyup_operations {+	int (*copyup)(struct dentry *dentry);+};++struct au_wbr_create_operations {+	int (*create)(struct dentry *dentry, int isdir);+	int (*init)(struct super_block *sb);+	int (*fin)(struct super_block *sb);+};++struct au_wbr_mfs {+	struct mutex	mfs_lock; /* protect this structure */+	unsigned long	mfs_jiffy;+	unsigned long	mfs_expire;+	aufs_bindex_t	mfs_bindex;++	unsigned long long	mfsrr_bytes;+	unsigned long long	mfsrr_watermark;+};++struct au_branch;+struct au_sbinfo {+	/* nowait tasks in the system-wide workqueue */+	struct au_nowait_tasks	si_nowait;++	struct au_rwsem		si_rwsem;++	/* branch management */+	unsigned int		si_generation;++	/* see above flags */+	unsigned char		au_si_status;++	aufs_bindex_t		si_bend;+	aufs_bindex_t		si_last_br_id;+	struct au_branch	**si_branch;++	/* policy to select a writable branch */+	unsigned char		si_wbr_copyup;+	unsigned char		si_wbr_create;+	struct au_wbr_copyup_operations *si_wbr_copyup_ops;+	struct au_wbr_create_operations *si_wbr_create_ops;++	/* round robin */+	atomic_t		si_wbr_rr_next;++	/* most free space */+	struct au_wbr_mfs	si_wbr_mfs;++	/* mount flags */+	/* include/asm-ia64/siginfo.h defines a macro named si_flags */+	unsigned int		si_mntflags;++	/* external inode number (bitmap and translation table) */+	au_readf_t		si_xread;+	au_writef_t		si_xwrite;+	struct file		*si_xib;+	struct mutex		si_xib_mtx; /* protect xib members */+	unsigned long		*si_xib_buf;+	unsigned long		si_xib_last_pindex;+	int			si_xib_next_bit;+	aufs_bindex_t		si_xino_brid;+	/* reserved for future use */+	/* unsigned long long	si_xib_limit; */	/* Max xib file size */++#ifdef CONFIG_AUFS_EXPORT+	/* i_generation */+	struct file		*si_xigen;+	atomic_t		si_xigen_next;+#endif++	/* vdir parameters */+	unsigned long		si_rdcache;	/* max cache time in HZ */+	unsigned int		si_rdblk;	/* deblk size */+	unsigned int		si_rdhash;	/* hash size */++	/*+	 * If the number of whiteouts are larger than si_dirwh, leave all of+	 * them after au_whtmp_ren to reduce the cost of rmdir(2).+	 * future fsck.aufs or kernel thread will remove them later.+	 * Otherwise, remove all whiteouts and the dir in rmdir(2).+	 */+	unsigned int		si_dirwh;++	/*+	 * rename(2) a directory with all children.+	 */+	/* reserved for future use */+	/* int			si_rendir; */++	/* pseudo_link list */+	struct au_splhead	si_plink;+	wait_queue_head_t	si_plink_wq;++	/*+	 * sysfs and lifetime management.+	 * this is not a small structure and it may be a waste of memory in case+	 * of sysfs is disabled, particulary when many aufs-es are mounted.+	 * but using sysfs is majority.+	 */+	struct kobject		si_kobj;+#ifdef CONFIG_DEBUG_FS+	struct dentry		 *si_dbgaufs, *si_dbgaufs_xib;+#ifdef CONFIG_AUFS_EXPORT+	struct dentry		 *si_dbgaufs_xigen;+#endif+#endif++	/* dirty, necessary for unmounting, sysfs and sysrq */+	struct super_block	*si_sb;+};++/* sbinfo status flags */+/*+ * set true when refresh_dirs() failed at remount time.+ * then try refreshing dirs at access time again.+ * if it is false, refreshing dirs at access time is unnecesary+ */+#define AuSi_FAILED_REFRESH_DIRS	1+#define AuSi_MAINTAIN_PLINK		(1 << 1)	/* ioctl */+static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi,+					   unsigned int flag)+{+	AuRwMustAnyLock(&sbi->si_rwsem);+	return sbi->au_si_status & flag;+}+#define au_ftest_si(sbinfo, name)	au_do_ftest_si(sbinfo, AuSi_##name)+#define au_fset_si(sbinfo, name) do { \+	AuRwMustWriteLock(&(sbinfo)->si_rwsem); \+	(sbinfo)->au_si_status |= AuSi_##name; \+} while (0)+#define au_fclr_si(sbinfo, name) do { \+	AuRwMustWriteLock(&(sbinfo)->si_rwsem); \+	(sbinfo)->au_si_status &= ~AuSi_##name; \+} while (0)++/* ---------------------------------------------------------------------- */++/* policy to select one among writable branches */+#define AuWbrCopyup(sbinfo, args...) \+	((sbinfo)->si_wbr_copyup_ops->copyup(args))+#define AuWbrCreate(sbinfo, args...) \+	((sbinfo)->si_wbr_create_ops->create(args))++/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */+#define AuLock_DW		1		/* write-lock dentry */+#define AuLock_IR		(1 << 1)	/* read-lock inode */+#define AuLock_IW		(1 << 2)	/* write-lock inode */+#define AuLock_FLUSH		(1 << 3)	/* wait for 'nowait' tasks */+#define AuLock_DIR		(1 << 4)	/* target is a dir */+#define au_ftest_lock(flags, name)	((flags) & AuLock_##name)+#define au_fset_lock(flags, name)	{ (flags) |= AuLock_##name; }+#define au_fclr_lock(flags, name)	{ (flags) &= ~AuLock_##name; }++/* ---------------------------------------------------------------------- */++/* super.c */+extern struct file_system_type aufs_fs_type;+struct inode *au_iget_locked(struct super_block *sb, ino_t ino);++/* sbinfo.c */+void au_si_free(struct kobject *kobj);+int au_si_alloc(struct super_block *sb);+int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr);++unsigned int au_sigen_inc(struct super_block *sb);+aufs_bindex_t au_new_br_id(struct super_block *sb);++void aufs_read_lock(struct dentry *dentry, int flags);+void aufs_read_unlock(struct dentry *dentry, int flags);+void aufs_write_lock(struct dentry *dentry);+void aufs_write_unlock(struct dentry *dentry);+void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int isdir);+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2);++/* wbr_policy.c */+extern struct au_wbr_copyup_operations au_wbr_copyup_ops[];+extern struct au_wbr_create_operations au_wbr_create_ops[];+int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst);++/* ---------------------------------------------------------------------- */++static inline struct au_sbinfo *au_sbi(struct super_block *sb)+{+	return sb->s_fs_info;+}++/* ---------------------------------------------------------------------- */++#ifdef CONFIG_AUFS_EXPORT+void au_export_init(struct super_block *sb);++static inline int au_test_nfsd(struct task_struct *tsk)+{+	return !tsk->mm && !strcmp(tsk->comm, "nfsd");+}++int au_xigen_inc(struct inode *inode);+int au_xigen_new(struct inode *inode);+int au_xigen_set(struct super_block *sb, struct file *base);+void au_xigen_clr(struct super_block *sb);++static inline int au_busy_or_stale(void)+{+	if (!au_test_nfsd(current))+		return -EBUSY;+	return -ESTALE;+}+#else+static inline void au_export_init(struct super_block *sb)+{+	/* nothing */+}++static inline int au_test_nfsd(struct task_struct *tsk)+{+	return 0;+}++static inline int au_xigen_inc(struct inode *inode)+{+	return 0;+}++static inline int au_xigen_new(struct inode *inode)+{+	return 0;+}++static inline int au_xigen_set(struct super_block *sb, struct file *base)+{+	return 0;+}++static inline void au_xigen_clr(struct super_block *sb)+{+	/* empty */+}++static inline int au_busy_or_stale(void)+{+	return -EBUSY;+}+#endif /* CONFIG_AUFS_EXPORT */++/* ---------------------------------------------------------------------- */++static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo)+{+	/*+	 * This function is a dynamic '__init' fucntion actually,+	 * so the tiny check for si_rwsem is unnecessary.+	 */+	/* AuRwMustWriteLock(&sbinfo->si_rwsem); */+#ifdef CONFIG_DEBUG_FS+	sbinfo->si_dbgaufs = NULL;+	sbinfo->si_dbgaufs_xib = NULL;+#ifdef CONFIG_AUFS_EXPORT+	sbinfo->si_dbgaufs_xigen = NULL;+#endif+#endif+}++/* ---------------------------------------------------------------------- */++/* lock superblock. mainly for entry point functions */+/*+ * si_noflush_read_lock, si_noflush_write_lock,+ * si_read_unlock, si_write_unlock, si_downgrade_lock+ */+AuSimpleLockRwsemFuncs(si_noflush, struct super_block *sb,+		       &au_sbi(sb)->si_rwsem);+AuSimpleUnlockRwsemFuncs(si, struct super_block *sb, &au_sbi(sb)->si_rwsem);++#define SiMustNoWaiters(sb)	AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem)+#define SiMustAnyLock(sb)	AuRwMustAnyLock(&au_sbi(sb)->si_rwsem)+#define SiMustWriteLock(sb)	AuRwMustWriteLock(&au_sbi(sb)->si_rwsem)++static inline void si_read_lock(struct super_block *sb, int flags)+{+	if (au_ftest_lock(flags, FLUSH))+		au_nwt_flush(&au_sbi(sb)->si_nowait);+	si_noflush_read_lock(sb);+}++static inline void si_write_lock(struct super_block *sb)+{+	au_nwt_flush(&au_sbi(sb)->si_nowait);+	si_noflush_write_lock(sb);+}++static inline int si_read_trylock(struct super_block *sb, int flags)+{+	if (au_ftest_lock(flags, FLUSH))+		au_nwt_flush(&au_sbi(sb)->si_nowait);+	return si_noflush_read_trylock(sb);+}++static inline int si_write_trylock(struct super_block *sb, int flags)+{+	if (au_ftest_lock(flags, FLUSH))+		au_nwt_flush(&au_sbi(sb)->si_nowait);+	return si_noflush_write_trylock(sb);+}++/* ---------------------------------------------------------------------- */++static inline aufs_bindex_t au_sbend(struct super_block *sb)+{+	SiMustAnyLock(sb);+	return au_sbi(sb)->si_bend;+}++static inline unsigned int au_mntflags(struct super_block *sb)+{+	SiMustAnyLock(sb);+	return au_sbi(sb)->si_mntflags;+}++static inline unsigned int au_sigen(struct super_block *sb)+{+	SiMustAnyLock(sb);+	return au_sbi(sb)->si_generation;+}++static inline struct au_branch *au_sbr(struct super_block *sb,+				       aufs_bindex_t bindex)+{+	SiMustAnyLock(sb);+	return au_sbi(sb)->si_branch[0 + bindex];+}++static inline void au_xino_brid_set(struct super_block *sb, aufs_bindex_t brid)+{+	SiMustWriteLock(sb);+	au_sbi(sb)->si_xino_brid = brid;+}++static inline aufs_bindex_t au_xino_brid(struct super_block *sb)+{+	SiMustAnyLock(sb);+	return au_sbi(sb)->si_xino_brid;+}++#endif /* __KERNEL__ */+#endif /* __AUFS_SUPER_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/sysaufs.c linux-2.6.31.5/fs/aufs/sysaufs.c--- linux-2.6.31.5.orig/fs/aufs/sysaufs.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/sysaufs.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,104 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * sysfs interface and lifetime management+ * they are necessary regardless sysfs is disabled.+ */++#include <linux/fs.h>+#include <linux/random.h>+#include <linux/sysfs.h>+#include "aufs.h"++unsigned long sysaufs_si_mask;+struct kset *sysaufs_ket;++#define AuSiAttr(_name) { \+	.attr   = { .name = __stringify(_name), .mode = 0444 },	\+	.show   = sysaufs_si_##_name,				\+}++static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path);+struct attribute *sysaufs_si_attrs[] = {+	&sysaufs_si_attr_xi_path.attr,+	NULL,+};++static struct sysfs_ops au_sbi_ops = {+	.show   = sysaufs_si_show+};++static struct kobj_type au_sbi_ktype = {+	.release	= au_si_free,+	.sysfs_ops	= &au_sbi_ops,+	.default_attrs	= sysaufs_si_attrs+};++/* ---------------------------------------------------------------------- */++int sysaufs_si_init(struct au_sbinfo *sbinfo)+{+	int err;++	sbinfo->si_kobj.kset = sysaufs_ket;+	/* cf. sysaufs_name() */+	err = kobject_init_and_add+		(&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_ket->kobj*/NULL,+		 SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo));++	dbgaufs_si_null(sbinfo);+	if (!err) {+		err = dbgaufs_si_init(sbinfo);+		if (unlikely(err))+			kobject_put(&sbinfo->si_kobj);+	}+	return err;+}++void sysaufs_fin(void)+{+	dbgaufs_fin();+	sysfs_remove_group(&sysaufs_ket->kobj, sysaufs_attr_group);+	kset_unregister(sysaufs_ket);+}++int __init sysaufs_init(void)+{+	int err;++	do {+		get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask));+	} while (!sysaufs_si_mask);++	sysaufs_ket = kset_create_and_add(AUFS_NAME, NULL, fs_kobj);+	err = PTR_ERR(sysaufs_ket);+	if (IS_ERR(sysaufs_ket))+		goto out;+	err = sysfs_create_group(&sysaufs_ket->kobj, sysaufs_attr_group);+	if (unlikely(err)) {+		kset_unregister(sysaufs_ket);+		goto out;+	}++	err = dbgaufs_init();+	if (unlikely(err))+		sysaufs_fin();+ out:+	return err;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/sysaufs.h linux-2.6.31.5/fs/aufs/sysaufs.h--- linux-2.6.31.5.orig/fs/aufs/sysaufs.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/sysaufs.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,120 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * sysfs interface and mount lifetime management+ */++#ifndef __SYSAUFS_H__+#define __SYSAUFS_H__++#ifdef __KERNEL__++#include <linux/sysfs.h>+#include <linux/aufs_type.h>+#include "module.h"++struct super_block;+struct au_sbinfo;++struct sysaufs_si_attr {+	struct attribute attr;+	int (*show)(struct seq_file *seq, struct super_block *sb);+};++/* ---------------------------------------------------------------------- */++/* sysaufs.c */+extern unsigned long sysaufs_si_mask;+extern struct kset *sysaufs_ket;+extern struct attribute *sysaufs_si_attrs[];+int sysaufs_si_init(struct au_sbinfo *sbinfo);+int __init sysaufs_init(void);+void sysaufs_fin(void);++/* ---------------------------------------------------------------------- */++/* some people doesn't like to show a pointer in kernel */+static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo)+{+	return sysaufs_si_mask ^ (unsigned long)sbinfo;+}++#define SysaufsSiNamePrefix	"si_"+#define SysaufsSiNameLen	(sizeof(SysaufsSiNamePrefix) + 16)+static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name)+{+	snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx",+		 sysaufs_si_id(sbinfo));+}++struct au_branch;+#ifdef CONFIG_SYSFS+/* sysfs.c */+extern struct attribute_group *sysaufs_attr_group;++int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb);+ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,+			 char *buf);++void sysaufs_br_init(struct au_branch *br);+void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex);+void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex);++#define sysaufs_brs_init()	do {} while (0)++#else+#define sysaufs_attr_group	NULL++static inline+int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb)+{+	return 0;+}++static inline+ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,+			 char *buf)+{+	return 0;+}++static inline void sysaufs_br_init(struct au_branch *br)+{+	/* empty */+}++static inline void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)+{+	/* nothing */+}++static inline void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)+{+	/* nothing */+}++static inline void sysaufs_brs_init(void)+{+	sysaufs_brs = 0;+}++#endif /* CONFIG_SYSFS */++#endif /* __KERNEL__ */+#endif /* __SYSAUFS_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/sysfs.c linux-2.6.31.5/fs/aufs/sysfs.c--- linux-2.6.31.5.orig/fs/aufs/sysfs.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/sysfs.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,210 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * sysfs interface+ */++#include <linux/fs.h>+#include <linux/module.h>+#include <linux/seq_file.h>+#include <linux/sysfs.h>+#include "aufs.h"++static struct attribute *au_attr[] = {+	NULL,	/* need to NULL terminate the list of attributes */+};++static struct attribute_group sysaufs_attr_group_body = {+	.attrs = au_attr+};++struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body;++/* ---------------------------------------------------------------------- */++int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb)+{+	int err;++	SiMustAnyLock(sb);++	err = 0;+	if (au_opt_test(au_mntflags(sb), XINO)) {+		err = au_xino_path(seq, au_sbi(sb)->si_xib);+		seq_putc(seq, '\n');+	}+	return err;+}++/*+ * the lifetime of branch is independent from the entry under sysfs.+ * sysfs handles the lifetime of the entry, and never call ->show() after it is+ * unlinked.+ */+static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb,+			 aufs_bindex_t bindex)+{+	struct path path;+	struct dentry *root;+	struct au_branch *br;++	AuDbg("b%d\n", bindex);++	root = sb->s_root;+	di_read_lock_parent(root, !AuLock_IR);+	br = au_sbr(sb, bindex);+	path.mnt = br->br_mnt;+	path.dentry = au_h_dptr(root, bindex);+	au_seq_path(seq, &path);+	di_read_unlock(root, !AuLock_IR);+	seq_printf(seq, "=%s\n", au_optstr_br_perm(br->br_perm));+	return 0;+}++/* ---------------------------------------------------------------------- */++static struct seq_file *au_seq(char *p, ssize_t len)+{+	struct seq_file *seq;++	seq = kzalloc(sizeof(*seq), GFP_NOFS);+	if (seq) {+		/* mutex_init(&seq.lock); */+		seq->buf = p;+		seq->size = len;+		return seq; /* success */+	}++	seq = ERR_PTR(-ENOMEM);+	return seq;+}++#define SysaufsBr_PREFIX "br"++/* todo: file size may exceed PAGE_SIZE */+ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,+			 char *buf)+{+	ssize_t err;+	long l;+	aufs_bindex_t bend;+	struct au_sbinfo *sbinfo;+	struct super_block *sb;+	struct seq_file *seq;+	char *name;+	struct attribute **cattr;++	sbinfo = container_of(kobj, struct au_sbinfo, si_kobj);+	sb = sbinfo->si_sb;+	si_noflush_read_lock(sb);++	seq = au_seq(buf, PAGE_SIZE);+	err = PTR_ERR(seq);+	if (IS_ERR(seq))+		goto out;++	name = (void *)attr->name;+	cattr = sysaufs_si_attrs;+	while (*cattr) {+		if (!strcmp(name, (*cattr)->name)) {+			err = container_of(*cattr, struct sysaufs_si_attr, attr)+				->show(seq, sb);+			goto out_seq;+		}+		cattr++;+	}++	bend = au_sbend(sb);+	if (!strncmp(name, SysaufsBr_PREFIX, sizeof(SysaufsBr_PREFIX) - 1)) {+		name += sizeof(SysaufsBr_PREFIX) - 1;+		err = strict_strtol(name, 10, &l);+		if (!err) {+			if (l <= bend)+				err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l);+			else+				err = -ENOENT;+		}+		goto out_seq;+	}+	BUG();++ out_seq:+	if (!err) {+		err = seq->count;+		/* sysfs limit */+		if (unlikely(err == PAGE_SIZE))+			err = -EFBIG;+	}+	kfree(seq);+ out:+	si_read_unlock(sb);+	return err;+}++/* ---------------------------------------------------------------------- */++void sysaufs_br_init(struct au_branch *br)+{+	br->br_attr.name = br->br_name;+	br->br_attr.mode = S_IRUGO;+	br->br_attr.owner = THIS_MODULE;+}++void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)+{+	struct au_branch *br;+	struct kobject *kobj;+	aufs_bindex_t bend;++	dbgaufs_brs_del(sb, bindex);++	if (!sysaufs_brs)+		return;++	kobj = &au_sbi(sb)->si_kobj;+	bend = au_sbend(sb);+	for (; bindex <= bend; bindex++) {+		br = au_sbr(sb, bindex);+		sysfs_remove_file(kobj, &br->br_attr);+	}+}++void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)+{+	int err;+	aufs_bindex_t bend;+	struct kobject *kobj;+	struct au_branch *br;++	dbgaufs_brs_add(sb, bindex);++	if (!sysaufs_brs)+		return;++	kobj = &au_sbi(sb)->si_kobj;+	bend = au_sbend(sb);+	for (; bindex <= bend; bindex++) {+		br = au_sbr(sb, bindex);+		snprintf(br->br_name, sizeof(br->br_name), SysaufsBr_PREFIX+			 "%d", bindex);+		err = sysfs_create_file(kobj, &br->br_attr);+		if (unlikely(err))+			AuWarn("failed %s under sysfs(%d)\n", br->br_name, err);+	}+}diff -Nur linux-2.6.31.5.orig/fs/aufs/sysrq.c linux-2.6.31.5/fs/aufs/sysrq.c--- linux-2.6.31.5.orig/fs/aufs/sysrq.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/sysrq.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,115 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * magic sysrq hanlder+ */++#include <linux/fs.h>+#include <linux/module.h>+#include <linux/moduleparam.h>+/* #include <linux/sysrq.h> */+#include "aufs.h"++/* ---------------------------------------------------------------------- */++static void sysrq_sb(struct super_block *sb)+{+	char *plevel;+	struct au_sbinfo *sbinfo;+	struct file *file;++	plevel = au_plevel;+	au_plevel = KERN_WARNING;+	au_debug(1);++	sbinfo = au_sbi(sb);+	pr_warning("si=%lx\n", sysaufs_si_id(sbinfo));+	pr_warning(AUFS_NAME ": superblock\n");+	au_dpri_sb(sb);+	pr_warning(AUFS_NAME ": root dentry\n");+	au_dpri_dentry(sb->s_root);+	pr_warning(AUFS_NAME ": root inode\n");+	au_dpri_inode(sb->s_root->d_inode);+#if 0+	struct inode *i;+	pr_warning(AUFS_NAME ": isolated inode\n");+	list_for_each_entry(i, &sb->s_inodes, i_sb_list)+		if (list_empty(&i->i_dentry))+			au_dpri_inode(i);+#endif+	pr_warning(AUFS_NAME ": files\n");+	list_for_each_entry(file, &sb->s_files, f_u.fu_list)+		if (!special_file(file->f_dentry->d_inode->i_mode))+			au_dpri_file(file);++	au_plevel = plevel;+	au_debug(0);+}++/* ---------------------------------------------------------------------- */++/* module parameter */+static char *aufs_sysrq_key = "a";+module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO);+MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME);++static void au_sysrq(int key __maybe_unused,+		     struct tty_struct *tty __maybe_unused)+{+	struct kobject *kobj;+	struct au_sbinfo *sbinfo;++	/* spin_lock(&sysaufs_ket->list_lock); */+	list_for_each_entry(kobj, &sysaufs_ket->list, entry) {+		sbinfo = container_of(kobj, struct au_sbinfo, si_kobj);+		sysrq_sb(sbinfo->si_sb);+	}+	/* spin_unlock(&sysaufs_ket->list_lock); */+}++static struct sysrq_key_op au_sysrq_op = {+	.handler	= au_sysrq,+	.help_msg	= "Aufs",+	.action_msg	= "Aufs",+	.enable_mask	= SYSRQ_ENABLE_DUMP+};++/* ---------------------------------------------------------------------- */++int __init au_sysrq_init(void)+{+	int err;+	char key;++	err = -1;+	key = *aufs_sysrq_key;+	if ('a' <= key && key <= 'z')+		err = register_sysrq_key(key, &au_sysrq_op);+	if (unlikely(err))+		AuErr("err %d, sysrq=%c\n", err, key);+	return err;+}++void au_sysrq_fin(void)+{+	int err;+	err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op);+	if (unlikely(err))+		AuErr("err %d (ignored)\n", err);+}diff -Nur linux-2.6.31.5.orig/fs/aufs/vdir.c linux-2.6.31.5/fs/aufs/vdir.c--- linux-2.6.31.5.orig/fs/aufs/vdir.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/vdir.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,882 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * virtual or vertical directory+ */++#include <linux/hash.h>+#include "aufs.h"++static unsigned int calc_size(int nlen)+{+	BUILD_BUG_ON(sizeof(ino_t) != sizeof(long));+	return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t));+}++static int set_deblk_end(union au_vdir_deblk_p *p,+			 union au_vdir_deblk_p *deblk_end)+{+	if (calc_size(0) <= deblk_end->deblk - p->deblk) {+		p->de->de_str.len = 0;+		/* smp_mb(); */+		return 0;+	}+	return -1; /* error */+}++/* returns true or false */+static int is_deblk_end(union au_vdir_deblk_p *p,+			union au_vdir_deblk_p *deblk_end)+{+	if (calc_size(0) <= deblk_end->deblk - p->deblk)+		return !p->de->de_str.len;+	return 1;+}++static unsigned char *last_deblk(struct au_vdir *vdir)+{+	return vdir->vd_deblk[vdir->vd_nblk - 1];+}++/* ---------------------------------------------------------------------- */++/*+ * the allocated memory has to be freed by+ * au_nhash_wh_free() or au_nhash_de_free().+ */+int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp)+{+	struct hlist_head *head;+	unsigned int u;++	head = kmalloc(sizeof(*nhash->nh_head) * num_hash, gfp);+	if (head) {+		nhash->nh_num = num_hash;+		nhash->nh_head = head;+		for (u = 0; u < num_hash; u++)+			INIT_HLIST_HEAD(head++);+		return 0; /* success */+	}++	return -ENOMEM;+}++static void nhash_count(struct hlist_head *head)+{+#if 0+	unsigned long n;+	struct hlist_node *pos;++	n = 0;+	hlist_for_each(pos, head)+		n++;+	AuInfo("%lu\n", n);+#endif+}++static void au_nhash_wh_do_free(struct hlist_head *head)+{+	struct au_vdir_wh *tpos;+	struct hlist_node *pos, *node;++	hlist_for_each_entry_safe(tpos, pos, node, head, wh_hash) {+		/* hlist_del(pos); */+		kfree(tpos);+	}+}++static void au_nhash_de_do_free(struct hlist_head *head)+{+	struct au_vdir_dehstr *tpos;+	struct hlist_node *pos, *node;++	hlist_for_each_entry_safe(tpos, pos, node, head, hash) {+		/* hlist_del(pos); */+		au_cache_free_dehstr(tpos);+	}+}++static void au_nhash_do_free(struct au_nhash *nhash,+			     void (*free)(struct hlist_head *head))+{+	unsigned int u, n;+	struct hlist_head *head;++	n = nhash->nh_num;+	head = nhash->nh_head;+	for (u = 0; u < n; u++) {+		nhash_count(head);+		free(head++);+	}+	kfree(nhash->nh_head);+}++void au_nhash_wh_free(struct au_nhash *whlist)+{+	au_nhash_do_free(whlist, au_nhash_wh_do_free);+}++static void au_nhash_de_free(struct au_nhash *delist)+{+	au_nhash_do_free(delist, au_nhash_de_do_free);+}++/* ---------------------------------------------------------------------- */++int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt,+			    int limit)+{+	int num;+	unsigned int u, n;+	struct hlist_head *head;+	struct au_vdir_wh *tpos;+	struct hlist_node *pos;++	num = 0;+	n = whlist->nh_num;+	head = whlist->nh_head;+	for (u = 0; u < n; u++) {+		hlist_for_each_entry(tpos, pos, head, wh_hash)+			if (tpos->wh_bindex == btgt && ++num > limit)+				return 1;+		head++;+	}+	return 0;+}++static struct hlist_head *au_name_hash(struct au_nhash *nhash,+				       unsigned char *name,+				       unsigned int len)+{+	unsigned int v;+	/* const unsigned int magic_bit = 12; */++	v = 0;+	while (len--)+		v += *name++;+	/* v = hash_long(v, magic_bit); */+	v %= nhash->nh_num;+	return nhash->nh_head + v;+}++static int au_nhash_test_name(struct au_vdir_destr *str, const char *name,+			      int nlen)+{+	return str->len == nlen && !memcmp(str->name, name, nlen);+}++/* returns found or not */+int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen)+{+	struct hlist_head *head;+	struct au_vdir_wh *tpos;+	struct hlist_node *pos;+	struct au_vdir_destr *str;++	head = au_name_hash(whlist, name, nlen);+	hlist_for_each_entry(tpos, pos, head, wh_hash) {+		str = &tpos->wh_str;+		AuDbg("%.*s\n", str->len, str->name);+		if (au_nhash_test_name(str, name, nlen))+			return 1;+	}+	return 0;+}++/* returns found(true) or not */+static int test_known(struct au_nhash *delist, char *name, int nlen)+{+	struct hlist_head *head;+	struct au_vdir_dehstr *tpos;+	struct hlist_node *pos;+	struct au_vdir_destr *str;++	head = au_name_hash(delist, name, nlen);+	hlist_for_each_entry(tpos, pos, head, hash) {+		str = tpos->str;+		AuDbg("%.*s\n", str->len, str->name);+		if (au_nhash_test_name(str, name, nlen))+			return 1;+	}+	return 0;+}++static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino,+			    unsigned char d_type)+{+#ifdef CONFIG_AUFS_SHWH+	wh->wh_ino = ino;+	wh->wh_type = d_type;+#endif+}++/* ---------------------------------------------------------------------- */++int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino,+		       unsigned int d_type, aufs_bindex_t bindex,+		       unsigned char shwh)+{+	int err;+	struct au_vdir_destr *str;+	struct au_vdir_wh *wh;++	AuDbg("%.*s\n", nlen, name);+	err = -ENOMEM;+	wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS);+	if (unlikely(!wh))+		goto out;++	err = 0;+	wh->wh_bindex = bindex;+	if (shwh)+		au_shwh_init_wh(wh, ino, d_type);+	str = &wh->wh_str;+	str->len = nlen;+	memcpy(str->name, name, nlen);+	hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen));+	/* smp_mb(); */++ out:+	return err;+}++static int append_deblk(struct au_vdir *vdir)+{+	int err;+	unsigned long ul;+	const unsigned int deblk_sz = vdir->vd_deblk_sz;+	union au_vdir_deblk_p p, deblk_end;+	unsigned char **o;++	err = -ENOMEM;+	o = krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1),+		     GFP_NOFS);+	if (unlikely(!o))+		goto out;++	vdir->vd_deblk = o;+	p.deblk = kmalloc(deblk_sz, GFP_NOFS);+	if (p.deblk) {+		ul = vdir->vd_nblk++;+		vdir->vd_deblk[ul] = p.deblk;+		vdir->vd_last.ul = ul;+		vdir->vd_last.p.deblk = p.deblk;+		deblk_end.deblk = p.deblk + deblk_sz;+		err = set_deblk_end(&p, &deblk_end);+	}++ out:+	return err;+}++static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino,+		     unsigned int d_type, struct au_nhash *delist)+{+	int err;+	unsigned int sz;+	const unsigned int deblk_sz = vdir->vd_deblk_sz;+	union au_vdir_deblk_p p, *room, deblk_end;+	struct au_vdir_dehstr *dehstr;++	p.deblk = last_deblk(vdir);+	deblk_end.deblk = p.deblk + deblk_sz;+	room = &vdir->vd_last.p;+	AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk+		  || !is_deblk_end(room, &deblk_end));++	sz = calc_size(nlen);+	if (unlikely(sz > deblk_end.deblk - room->deblk)) {+		err = append_deblk(vdir);+		if (unlikely(err))+			goto out;++		p.deblk = last_deblk(vdir);+		deblk_end.deblk = p.deblk + deblk_sz;+		/* smp_mb(); */+		AuDebugOn(room->deblk != p.deblk);+	}++	err = -ENOMEM;+	dehstr = au_cache_alloc_dehstr();+	if (unlikely(!dehstr))+		goto out;++	dehstr->str = &room->de->de_str;+	hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen));+	room->de->de_ino = ino;+	room->de->de_type = d_type;+	room->de->de_str.len = nlen;+	memcpy(room->de->de_str.name, name, nlen);++	err = 0;+	room->deblk += sz;+	if (unlikely(set_deblk_end(room, &deblk_end)))+		err = append_deblk(vdir);+	/* smp_mb(); */++ out:+	return err;+}++/* ---------------------------------------------------------------------- */++void au_vdir_free(struct au_vdir *vdir)+{+	unsigned char **deblk;++	deblk = vdir->vd_deblk;+	while (vdir->vd_nblk--)+		kfree(*deblk++);+	kfree(vdir->vd_deblk);+	au_cache_free_vdir(vdir);+}++static struct au_vdir *alloc_vdir(struct super_block *sb)+{+	struct au_vdir *vdir;+	int err;++	SiMustAnyLock(sb);++	err = -ENOMEM;+	vdir = au_cache_alloc_vdir();+	if (unlikely(!vdir))+		goto out;++	vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS);+	if (unlikely(!vdir->vd_deblk))+		goto out_free;++	vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk;+	vdir->vd_nblk = 0;+	vdir->vd_version = 0;+	vdir->vd_jiffy = 0;+	err = append_deblk(vdir);+	if (!err)+		return vdir; /* success */++	kfree(vdir->vd_deblk);++ out_free:+	au_cache_free_vdir(vdir);+ out:+	vdir = ERR_PTR(err);+	return vdir;+}++static int reinit_vdir(struct au_vdir *vdir)+{+	int err;+	union au_vdir_deblk_p p, deblk_end;++	while (vdir->vd_nblk > 1) {+		kfree(vdir->vd_deblk[vdir->vd_nblk - 1]);+		/* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */+		vdir->vd_nblk--;+	}+	p.deblk = vdir->vd_deblk[0];+	deblk_end.deblk = p.deblk + vdir->vd_deblk_sz;+	err = set_deblk_end(&p, &deblk_end);+	/* keep vd_dblk_sz */+	vdir->vd_last.ul = 0;+	vdir->vd_last.p.deblk = vdir->vd_deblk[0];+	vdir->vd_version = 0;+	vdir->vd_jiffy = 0;+	/* smp_mb(); */+	return err;+}++/* ---------------------------------------------------------------------- */++static int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,+		  unsigned int d_type, ino_t *ino)+{+	int err;+	struct mutex *mtx;+	const int isdir = (d_type == DT_DIR);++	/* prevent hardlinks from race condition */+	mtx = NULL;+	if (!isdir) {+		mtx = &au_sbr(sb, bindex)->br_xino.xi_nondir_mtx;+		mutex_lock(mtx);+	}+	err = au_xino_read(sb, bindex, h_ino, ino);+	if (unlikely(err))+		goto out;++	if (!*ino) {+		err = -EIO;+		*ino = au_xino_new_ino(sb);+		if (unlikely(!*ino))+			goto out;+		err = au_xino_write(sb, bindex, h_ino, *ino);+		if (unlikely(err))+			goto out;+	}++ out:+	if (!isdir)+		mutex_unlock(mtx);+	return err;+}++static int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,+		     unsigned int d_type, ino_t *ino)+{+#ifdef CONFIG_AUFS_SHWH+	return au_ino(sb, bindex, h_ino, d_type, ino);+#else+	return 0;+#endif+}++#define AuFillVdir_CALLED	1+#define AuFillVdir_WHABLE	(1 << 1)+#define AuFillVdir_SHWH		(1 << 2)+#define au_ftest_fillvdir(flags, name)	((flags) & AuFillVdir_##name)+#define au_fset_fillvdir(flags, name)	{ (flags) |= AuFillVdir_##name; }+#define au_fclr_fillvdir(flags, name)	{ (flags) &= ~AuFillVdir_##name; }++#ifndef CONFIG_AUFS_SHWH+#undef AuFillVdir_SHWH+#define AuFillVdir_SHWH		0+#endif++struct fillvdir_arg {+	struct file		*file;+	struct au_vdir		*vdir;+	struct au_nhash		delist;+	struct au_nhash		whlist;+	aufs_bindex_t		bindex;+	unsigned int		flags;+	int			err;+};++static int fillvdir(void *__arg, const char *__name, int nlen,+		    loff_t offset __maybe_unused, u64 h_ino,+		    unsigned int d_type)+{+	struct fillvdir_arg *arg = __arg;+	char *name = (void *)__name;+	struct super_block *sb;+	ino_t ino;+	const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH);++	arg->err = 0;+	sb = arg->file->f_dentry->d_sb;+	au_fset_fillvdir(arg->flags, CALLED);+	/* smp_mb(); */+	if (nlen <= AUFS_WH_PFX_LEN+	    || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {+		if (test_known(&arg->delist, name, nlen)+		    || au_nhash_test_known_wh(&arg->whlist, name, nlen))+			goto out; /* already exists or whiteouted */++		sb = arg->file->f_dentry->d_sb;+		arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino);+		if (!arg->err)+			arg->err = append_de(arg->vdir, name, nlen, ino,+					     d_type, &arg->delist);+	} else if (au_ftest_fillvdir(arg->flags, WHABLE)) {+		name += AUFS_WH_PFX_LEN;+		nlen -= AUFS_WH_PFX_LEN;+		if (au_nhash_test_known_wh(&arg->whlist, name, nlen))+			goto out; /* already whiteouted */++		if (shwh)+			arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type,+					     &ino);+		if (!arg->err)+			arg->err = au_nhash_append_wh+				(&arg->whlist, name, nlen, ino, d_type,+				 arg->bindex, shwh);+	}++ out:+	if (!arg->err)+		arg->vdir->vd_jiffy = jiffies;+	/* smp_mb(); */+	AuTraceErr(arg->err);+	return arg->err;+}++static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir,+			  struct au_nhash *whlist, struct au_nhash *delist)+{+#ifdef CONFIG_AUFS_SHWH+	int err;+	unsigned int nh, u;+	struct hlist_head *head;+	struct au_vdir_wh *tpos;+	struct hlist_node *pos, *n;+	char *p, *o;+	struct au_vdir_destr *destr;++	AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH));++	err = -ENOMEM;+	o = p = __getname();+	if (unlikely(!p))+		goto out;++	err = 0;+	nh = whlist->nh_num;+	memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN);+	p += AUFS_WH_PFX_LEN;+	for (u = 0; u < nh; u++) {+		head = whlist->nh_head + u;+		hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) {+			destr = &tpos->wh_str;+			memcpy(p, destr->name, destr->len);+			err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN,+					tpos->wh_ino, tpos->wh_type, delist);+			if (unlikely(err))+				break;+		}+	}++	__putname(o);++ out:+	AuTraceErr(err);+	return err;+#else+	return 0;+#endif+}++static int au_do_read_vdir(struct fillvdir_arg *arg)+{+	int err;+	unsigned int rdhash;+	loff_t offset;+	aufs_bindex_t bend, bindex, bstart;+	unsigned char shwh;+	struct file *hf, *file;+	struct super_block *sb;++	file = arg->file;+	sb = file->f_dentry->d_sb;+	SiMustAnyLock(sb);++	rdhash = au_sbi(sb)->si_rdhash;+	err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS);+	if (unlikely(err))+		goto out;+	err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS);+	if (unlikely(err))+		goto out_delist;++	err = 0;+	arg->flags = 0;+	shwh = 0;+	if (au_opt_test(au_mntflags(sb), SHWH)) {+		shwh = 1;+		au_fset_fillvdir(arg->flags, SHWH);+	}+	bstart = au_fbstart(file);+	bend = au_fbend(file);+	for (bindex = bstart; !err && bindex <= bend; bindex++) {+		hf = au_h_fptr(file, bindex);+		if (!hf)+			continue;++		offset = vfsub_llseek(hf, 0, SEEK_SET);+		err = offset;+		if (unlikely(offset))+			break;++		arg->bindex = bindex;+		au_fclr_fillvdir(arg->flags, WHABLE);+		if (shwh+		    || (bindex != bend+			&& au_br_whable(au_sbr_perm(sb, bindex))))+			au_fset_fillvdir(arg->flags, WHABLE);+		do {+			arg->err = 0;+			au_fclr_fillvdir(arg->flags, CALLED);+			/* smp_mb(); */+			err = vfsub_readdir(hf, fillvdir, arg);+			if (err >= 0)+				err = arg->err;+		} while (!err && au_ftest_fillvdir(arg->flags, CALLED));+	}++	if (!err && shwh)+		err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist);++	au_nhash_wh_free(&arg->whlist);++ out_delist:+	au_nhash_de_free(&arg->delist);+ out:+	return err;+}++static int read_vdir(struct file *file, int may_read)+{+	int err;+	unsigned long expire;+	unsigned char do_read;+	struct fillvdir_arg arg;+	struct inode *inode;+	struct au_vdir *vdir, *allocated;++	err = 0;+	inode = file->f_dentry->d_inode;+	IMustLock(inode);+	SiMustAnyLock(inode->i_sb);++	allocated = NULL;+	do_read = 0;+	expire = au_sbi(inode->i_sb)->si_rdcache;+	vdir = au_ivdir(inode);+	if (!vdir) {+		do_read = 1;+		vdir = alloc_vdir(inode->i_sb);+		err = PTR_ERR(vdir);+		if (IS_ERR(vdir))+			goto out;+		err = 0;+		allocated = vdir;+	} else if (may_read+		   && (inode->i_version != vdir->vd_version+		       || time_after(jiffies, vdir->vd_jiffy + expire))) {+		do_read = 1;+		err = reinit_vdir(vdir);+		if (unlikely(err))+			goto out;+	}++	if (!do_read)+		return 0; /* success */++	arg.file = file;+	arg.vdir = vdir;+	err = au_do_read_vdir(&arg);+	if (!err) {+		/* file->f_pos = 0; */+		vdir->vd_version = inode->i_version;+		vdir->vd_last.ul = 0;+		vdir->vd_last.p.deblk = vdir->vd_deblk[0];+		if (allocated)+			au_set_ivdir(inode, allocated);+	} else if (allocated)+		au_vdir_free(allocated);++ out:+	return err;+}++static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src)+{+	int err, rerr;+	unsigned long ul, n;+	const unsigned int deblk_sz = src->vd_deblk_sz;++	AuDebugOn(tgt->vd_nblk != 1);++	err = -ENOMEM;+	if (tgt->vd_nblk < src->vd_nblk) {+		unsigned char **p;++		p = krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk,+			     GFP_NOFS);+		if (unlikely(!p))+			goto out;+		tgt->vd_deblk = p;+	}++	tgt->vd_nblk = src->vd_nblk;+	tgt->vd_deblk_sz = deblk_sz;+	memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz);+	/* tgt->vd_last.i = 0; */+	/* tgt->vd_last.p.deblk = tgt->vd_deblk[0]; */+	tgt->vd_version = src->vd_version;+	tgt->vd_jiffy = src->vd_jiffy;++	n = src->vd_nblk;+	for (ul = 1; ul < n; ul++) {+		tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz,+					    GFP_NOFS);+		if (unlikely(!tgt->vd_deblk[ul]))+			goto out;+	}+	/* smp_mb(); */+	return 0; /* success */++ out:+	rerr = reinit_vdir(tgt);+	BUG_ON(rerr);+	return err;+}++int au_vdir_init(struct file *file)+{+	int err;+	struct inode *inode;+	struct au_vdir *vdir_cache, *allocated;++	err = read_vdir(file, !file->f_pos);+	if (unlikely(err))+		goto out;++	allocated = NULL;+	vdir_cache = au_fvdir_cache(file);+	if (!vdir_cache) {+		vdir_cache = alloc_vdir(file->f_dentry->d_sb);+		err = PTR_ERR(vdir_cache);+		if (IS_ERR(vdir_cache))+			goto out;+		allocated = vdir_cache;+	} else if (!file->f_pos && vdir_cache->vd_version != file->f_version) {+		err = reinit_vdir(vdir_cache);+		if (unlikely(err))+			goto out;+	} else+		return 0; /* success */++	inode = file->f_dentry->d_inode;+	err = copy_vdir(vdir_cache, au_ivdir(inode));+	if (!err) {+		file->f_version = inode->i_version;+		if (allocated)+			au_set_fvdir_cache(file, allocated);+	} else if (allocated)+		au_vdir_free(allocated);++ out:+	return err;+}++static loff_t calc_offset(struct au_vdir *vdir)+{+	loff_t offset;+	union au_vdir_deblk_p p;++	p.deblk = vdir->vd_deblk[vdir->vd_last.ul];+	offset = vdir->vd_last.p.deblk - p.deblk;+	offset += vdir->vd_deblk_sz * vdir->vd_last.ul;+	return offset;+}++/* returns true or false */+static int seek_vdir(struct file *file)+{+	int valid;+	unsigned int deblk_sz;+	unsigned long ul, n;+	loff_t offset;+	union au_vdir_deblk_p p, deblk_end;+	struct au_vdir *vdir_cache;++	valid = 1;+	vdir_cache = au_fvdir_cache(file);+	offset = calc_offset(vdir_cache);+	AuDbg("offset %lld\n", offset);+	if (file->f_pos == offset)+		goto out;++	vdir_cache->vd_last.ul = 0;+	vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0];+	if (!file->f_pos)+		goto out;++	valid = 0;+	deblk_sz = vdir_cache->vd_deblk_sz;+	ul = div64_u64(file->f_pos, deblk_sz);+	AuDbg("ul %lu\n", ul);+	if (ul >= vdir_cache->vd_nblk)+		goto out;++	n = vdir_cache->vd_nblk;+	for (; ul < n; ul++) {+		p.deblk = vdir_cache->vd_deblk[ul];+		deblk_end.deblk = p.deblk + deblk_sz;+		offset = ul;+		offset *= deblk_sz;+		while (!is_deblk_end(&p, &deblk_end) && offset < file->f_pos) {+			unsigned int l;++			l = calc_size(p.de->de_str.len);+			offset += l;+			p.deblk += l;+		}+		if (!is_deblk_end(&p, &deblk_end)) {+			valid = 1;+			vdir_cache->vd_last.ul = ul;+			vdir_cache->vd_last.p = p;+			break;+		}+	}++ out:+	/* smp_mb(); */+	AuTraceErr(!valid);+	return valid;+}++int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir)+{+	int err;+	unsigned int l, deblk_sz;+	union au_vdir_deblk_p deblk_end;+	struct au_vdir *vdir_cache;+	struct au_vdir_de *de;++	vdir_cache = au_fvdir_cache(file);+	if (!seek_vdir(file))+		return 0;++	deblk_sz = vdir_cache->vd_deblk_sz;+	while (1) {+		deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul];+		deblk_end.deblk += deblk_sz;+		while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) {+			de = vdir_cache->vd_last.p.de;+			AuDbg("%.*s, off%lld, i%lu, dt%d\n",+			      de->de_str.len, de->de_str.name, file->f_pos,+			      (unsigned long)de->de_ino, de->de_type);+			err = filldir(dirent, de->de_str.name, de->de_str.len,+				      file->f_pos, de->de_ino, de->de_type);+			if (unlikely(err)) {+				AuTraceErr(err);+				/* todo: ignore the error caused by udba? */+				/* return err; */+				return 0;+			}++			l = calc_size(de->de_str.len);+			vdir_cache->vd_last.p.deblk += l;+			file->f_pos += l;+		}+		if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) {+			vdir_cache->vd_last.ul++;+			vdir_cache->vd_last.p.deblk+				= vdir_cache->vd_deblk[vdir_cache->vd_last.ul];+			file->f_pos = deblk_sz * vdir_cache->vd_last.ul;+			continue;+		}+		break;+	}++	/* smp_mb(); */+	return 0;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/vfsub.c linux-2.6.31.5/fs/aufs/vfsub.c--- linux-2.6.31.5.orig/fs/aufs/vfsub.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/vfsub.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,740 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * sub-routines for VFS+ */++#include <linux/namei.h>+#include <linux/security.h>+#include <linux/splice.h>+#include <linux/uaccess.h>+#include "aufs.h"++int vfsub_update_h_iattr(struct path *h_path, int *did)+{+	int err;+	struct kstat st;+	struct super_block *h_sb;++	/* for remote fs, leave work for its getattr or d_revalidate */+	/* for bad i_attr fs, handle them in aufs_getattr() */+	/* still some fs may acquire i_mutex. we need to skip them */+	err = 0;+	if (!did)+		did = &err;+	h_sb = h_path->dentry->d_sb;+	*did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb));+	if (*did)+		err = vfs_getattr(h_path->mnt, h_path->dentry, &st);++	return err;+}++/* ---------------------------------------------------------------------- */++#ifdef CONFIG_IMA+#error IMA is not supported since it does not work well. Wait for their fixing.+#endif++struct file *vfsub_filp_open(const char *path, int oflags, int mode)+{+	struct file *file;++	lockdep_off();+	file = filp_open(path, oflags, mode);+	lockdep_on();+	if (IS_ERR(file))+		goto out;+	vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/++ out:+	return file;+}++int vfsub_kern_path(const char *name, unsigned int flags, struct path *path)+{+	int err;++	/* lockdep_off(); */+	err = kern_path(name, flags, path);+	/* lockdep_on(); */+	if (!err && path->dentry->d_inode)+		vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/+	return err;+}++struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent,+				    int len)+{+	struct path path = {+		.mnt = NULL+	};++	IMustLock(parent->d_inode);++	path.dentry = lookup_one_len(name, parent, len);+	if (IS_ERR(path.dentry))+		goto out;+	if (path.dentry->d_inode)+		vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/++ out:+	return path.dentry;+}++struct dentry *vfsub_lookup_hash(struct nameidata *nd)+{+	struct path path = {+		.mnt = nd->path.mnt+	};++	IMustLock(nd->path.dentry->d_inode);++	path.dentry = lookup_hash(nd);+	if (!IS_ERR(path.dentry) && path.dentry->d_inode)+		vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/++	return path.dentry;+}++/* ---------------------------------------------------------------------- */++struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1,+				 struct dentry *d2, struct au_hinode *hdir2)+{+	struct dentry *d;++	lockdep_off();+	d = lock_rename(d1, d2);+	lockdep_on();+	au_hin_suspend(hdir1);+	if (hdir1 != hdir2)+		au_hin_suspend(hdir2);++	return d;+}++void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1,+			 struct dentry *d2, struct au_hinode *hdir2)+{+	au_hin_resume(hdir1);+	if (hdir1 != hdir2)+		au_hin_resume(hdir2);+	lockdep_off();+	unlock_rename(d1, d2);+	lockdep_on();+}++/* ---------------------------------------------------------------------- */++int vfsub_create(struct inode *dir, struct path *path, int mode)+{+	int err;+	struct dentry *d;++	IMustLock(dir);++	d = path->dentry;+	path->dentry = d->d_parent;+	err = security_path_mknod(path, path->dentry, mode, 0);+	path->dentry = d;+	if (unlikely(err))+		goto out;++	if (au_test_fs_null_nd(dir->i_sb))+		err = vfs_create(dir, path->dentry, mode, NULL);+	else {+		struct nameidata h_nd;++		memset(&h_nd, 0, sizeof(h_nd));+		h_nd.flags = LOOKUP_CREATE;+		h_nd.intent.open.flags = O_CREAT+			| vfsub_fmode_to_uint(FMODE_READ);+		h_nd.intent.open.create_mode = mode;+		h_nd.path.dentry = path->dentry->d_parent;+		h_nd.path.mnt = path->mnt;+		path_get(&h_nd.path);+		err = vfs_create(dir, path->dentry, mode, &h_nd);+		path_put(&h_nd.path);+	}++	if (!err) {+		struct path tmp = *path;+		int did;++		vfsub_update_h_iattr(&tmp, &did);+		if (did) {+			tmp.dentry = path->dentry->d_parent;+			vfsub_update_h_iattr(&tmp, /*did*/NULL);+		}+		/*ignore*/+	}++ out:+	return err;+}++int vfsub_symlink(struct inode *dir, struct path *path, const char *symname)+{+	int err;+	struct dentry *d;++	IMustLock(dir);++	d = path->dentry;+	path->dentry = d->d_parent;+	err = security_path_symlink(path, path->dentry, symname);+	path->dentry = d;+	if (unlikely(err))+		goto out;++	err = vfs_symlink(dir, path->dentry, symname);+	if (!err) {+		struct path tmp = *path;+		int did;++		vfsub_update_h_iattr(&tmp, &did);+		if (did) {+			tmp.dentry = path->dentry->d_parent;+			vfsub_update_h_iattr(&tmp, /*did*/NULL);+		}+		/*ignore*/+	}++ out:+	return err;+}++int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev)+{+	int err;+	struct dentry *d;++	IMustLock(dir);++	d = path->dentry;+	path->dentry = d->d_parent;+	err = security_path_mknod(path, path->dentry, mode, dev);+	path->dentry = d;+	if (unlikely(err))+		goto out;++	err = vfs_mknod(dir, path->dentry, mode, dev);+	if (!err) {+		struct path tmp = *path;+		int did;++		vfsub_update_h_iattr(&tmp, &did);+		if (did) {+			tmp.dentry = path->dentry->d_parent;+			vfsub_update_h_iattr(&tmp, /*did*/NULL);+		}+		/*ignore*/+	}++ out:+	return err;+}++static int au_test_nlink(struct inode *inode)+{+	const unsigned int link_max = UINT_MAX >> 1; /* rough margin */++	if (!au_test_fs_no_limit_nlink(inode->i_sb)+	    || inode->i_nlink < link_max)+		return 0;+	return -EMLINK;+}++int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path)+{+	int err;+	struct dentry *d;++	IMustLock(dir);++	err = au_test_nlink(src_dentry->d_inode);+	if (unlikely(err))+		return err;++	d = path->dentry;+	path->dentry = d->d_parent;+	err = security_path_link(src_dentry, path, path->dentry);+	path->dentry = d;+	if (unlikely(err))+		goto out;++	lockdep_off();+	err = vfs_link(src_dentry, dir, path->dentry);+	lockdep_on();+	if (!err) {+		struct path tmp = *path;+		int did;++		/* fuse has different memory inode for the same inumber */+		vfsub_update_h_iattr(&tmp, &did);+		if (did) {+			tmp.dentry = path->dentry->d_parent;+			vfsub_update_h_iattr(&tmp, /*did*/NULL);+			tmp.dentry = src_dentry;+			vfsub_update_h_iattr(&tmp, /*did*/NULL);+		}+		/*ignore*/+	}++ out:+	return err;+}++int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,+		 struct inode *dir, struct path *path)+{+	int err;+	struct path tmp = {+		.mnt	= path->mnt+	};+	struct dentry *d;++	IMustLock(dir);+	IMustLock(src_dir);++	d = path->dentry;+	path->dentry = d->d_parent;+	tmp.dentry = src_dentry->d_parent;+	err = security_path_rename(&tmp, src_dentry, path, path->dentry);+	path->dentry = d;+	if (unlikely(err))+		goto out;++	lockdep_off();+	err = vfs_rename(src_dir, src_dentry, dir, path->dentry);+	lockdep_on();+	if (!err) {+		int did;++		tmp.dentry = d->d_parent;+		vfsub_update_h_iattr(&tmp, &did);+		if (did) {+			tmp.dentry = src_dentry;+			vfsub_update_h_iattr(&tmp, /*did*/NULL);+			tmp.dentry = src_dentry->d_parent;+			vfsub_update_h_iattr(&tmp, /*did*/NULL);+		}+		/*ignore*/+	}++ out:+	return err;+}++int vfsub_mkdir(struct inode *dir, struct path *path, int mode)+{+	int err;+	struct dentry *d;++	IMustLock(dir);++	d = path->dentry;+	path->dentry = d->d_parent;+	err = security_path_mkdir(path, path->dentry, mode);+	path->dentry = d;+	if (unlikely(err))+		goto out;++	err = vfs_mkdir(dir, path->dentry, mode);+	if (!err) {+		struct path tmp = *path;+		int did;++		vfsub_update_h_iattr(&tmp, &did);+		if (did) {+			tmp.dentry = path->dentry->d_parent;+			vfsub_update_h_iattr(&tmp, /*did*/NULL);+		}+		/*ignore*/+	}++ out:+	return err;+}++int vfsub_rmdir(struct inode *dir, struct path *path)+{+	int err;+	struct dentry *d;++	IMustLock(dir);++	d = path->dentry;+	path->dentry = d->d_parent;+	err = security_path_rmdir(path, path->dentry);+	path->dentry = d;+	if (unlikely(err))+		goto out;++	lockdep_off();+	err = vfs_rmdir(dir, path->dentry);+	lockdep_on();+	if (!err) {+		struct path tmp = {+			.dentry	= path->dentry->d_parent,+			.mnt	= path->mnt+		};++		vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/+	}++ out:+	return err;+}++/* ---------------------------------------------------------------------- */++ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,+		     loff_t *ppos)+{+	ssize_t err;++	err = vfs_read(file, ubuf, count, ppos);+	if (err >= 0)+		vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/+	return err;+}++/* todo: kernel_read()? */+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count,+		     loff_t *ppos)+{+	ssize_t err;+	mm_segment_t oldfs;++	oldfs = get_fs();+	set_fs(KERNEL_DS);+	err = vfsub_read_u(file, (char __user *)kbuf, count, ppos);+	set_fs(oldfs);+	return err;+}++ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,+		      loff_t *ppos)+{+	ssize_t err;++	lockdep_off();+	err = vfs_write(file, ubuf, count, ppos);+	lockdep_on();+	if (err >= 0)+		vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/+	return err;+}++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos)+{+	ssize_t err;+	mm_segment_t oldfs;++	oldfs = get_fs();+	set_fs(KERNEL_DS);+	err = vfsub_write_u(file, (const char __user *)kbuf, count, ppos);+	set_fs(oldfs);+	return err;+}++int vfsub_readdir(struct file *file, filldir_t filldir, void *arg)+{+	int err;++	lockdep_off();+	err = vfs_readdir(file, filldir, arg);+	lockdep_on();+	if (err >= 0)+		vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/+	return err;+}++long vfsub_splice_to(struct file *in, loff_t *ppos,+		     struct pipe_inode_info *pipe, size_t len,+		     unsigned int flags)+{+	long err;++	lockdep_off();+	err = do_splice_to(in, ppos, pipe, len, flags);+	lockdep_on();+	if (err >= 0)+		vfsub_update_h_iattr(&in->f_path, /*did*/NULL); /*ignore*/+	return err;+}++long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out,+		       loff_t *ppos, size_t len, unsigned int flags)+{+	long err;++	lockdep_off();+	err = do_splice_from(pipe, out, ppos, len, flags);+	lockdep_on();+	if (err >= 0)+		vfsub_update_h_iattr(&out->f_path, /*did*/NULL); /*ignore*/+	return err;+}++/* cf. open.c:do_sys_truncate() and do_sys_ftruncate() */+int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr,+		struct file *h_file)+{+	int err;+	struct inode *h_inode;++	h_inode = h_path->dentry->d_inode;+	if (!h_file) {+		err = mnt_want_write(h_path->mnt);+		if (err)+			goto out;+		err = inode_permission(h_inode, MAY_WRITE);+		if (err)+			goto out_mnt;+		err = get_write_access(h_inode);+		if (err)+			goto out_mnt;+		err = break_lease(h_inode, vfsub_fmode_to_uint(FMODE_WRITE));+		if (err)+			goto out_inode;+	}++	err = locks_verify_truncate(h_inode, h_file, length);+	if (!err)+		err = security_path_truncate(h_path, length, attr);+	if (!err) {+		lockdep_off();+		err = do_truncate(h_path->dentry, length, attr, h_file);+		lockdep_on();+	}++ out_inode:+	if (!h_file)+		put_write_access(h_inode);+ out_mnt:+	if (!h_file)+		mnt_drop_write(h_path->mnt);+ out:+	return err;+}++/* ---------------------------------------------------------------------- */++struct au_vfsub_mkdir_args {+	int *errp;+	struct inode *dir;+	struct path *path;+	int mode;+};++static void au_call_vfsub_mkdir(void *args)+{+	struct au_vfsub_mkdir_args *a = args;+	*a->errp = vfsub_mkdir(a->dir, a->path, a->mode);+}++int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode)+{+	int err, do_sio, wkq_err;++	do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE);+	if (!do_sio)+		err = vfsub_mkdir(dir, path, mode);+	else {+		struct au_vfsub_mkdir_args args = {+			.errp	= &err,+			.dir	= dir,+			.path	= path,+			.mode	= mode+		};+		wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args);+		if (unlikely(wkq_err))+			err = wkq_err;+	}++	return err;+}++struct au_vfsub_rmdir_args {+	int *errp;+	struct inode *dir;+	struct path *path;+};++static void au_call_vfsub_rmdir(void *args)+{+	struct au_vfsub_rmdir_args *a = args;+	*a->errp = vfsub_rmdir(a->dir, a->path);+}++int vfsub_sio_rmdir(struct inode *dir, struct path *path)+{+	int err, do_sio, wkq_err;++	do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE);+	if (!do_sio)+		err = vfsub_rmdir(dir, path);+	else {+		struct au_vfsub_rmdir_args args = {+			.errp	= &err,+			.dir	= dir,+			.path	= path+		};+		wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args);+		if (unlikely(wkq_err))+			err = wkq_err;+	}++	return err;+}++/* ---------------------------------------------------------------------- */++struct notify_change_args {+	int *errp;+	struct path *path;+	struct iattr *ia;+};++static void call_notify_change(void *args)+{+	struct notify_change_args *a = args;+	struct inode *h_inode;++	h_inode = a->path->dentry->d_inode;+	IMustLock(h_inode);++	*a->errp = -EPERM;+	if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) {+		lockdep_off();+		*a->errp = notify_change(a->path->dentry, a->ia);+		lockdep_on();+		if (!*a->errp)+			vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/+	}+	AuTraceErr(*a->errp);+}++int vfsub_notify_change(struct path *path, struct iattr *ia)+{+	int err;+	struct notify_change_args args = {+		.errp	= &err,+		.path	= path,+		.ia	= ia+	};++	call_notify_change(&args);++	return err;+}++int vfsub_sio_notify_change(struct path *path, struct iattr *ia)+{+	int err, wkq_err;+	struct notify_change_args args = {+		.errp	= &err,+		.path	= path,+		.ia	= ia+	};++	wkq_err = au_wkq_wait(call_notify_change, &args);+	if (unlikely(wkq_err))+		err = wkq_err;++	return err;+}++/* ---------------------------------------------------------------------- */++struct unlink_args {+	int *errp;+	struct inode *dir;+	struct path *path;+};++static void call_unlink(void *args)+{+	struct unlink_args *a = args;+	struct dentry *d = a->path->dentry;+	struct inode *h_inode;+	const int stop_sillyrename = (au_test_nfs(d->d_sb)+				      && atomic_read(&d->d_count) == 1);++	IMustLock(a->dir);++	a->path->dentry = d->d_parent;+	*a->errp = security_path_unlink(a->path, d);+	a->path->dentry = d;+	if (unlikely(*a->errp))+		return;++	if (!stop_sillyrename)+		dget(d);+	h_inode = d->d_inode;+	if (h_inode)+		atomic_inc(&h_inode->i_count);++	lockdep_off();+	*a->errp = vfs_unlink(a->dir, d);+	lockdep_on();+	if (!*a->errp) {+		struct path tmp = {+			.dentry = d->d_parent,+			.mnt	= a->path->mnt+		};+		vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/+	}++	if (!stop_sillyrename)+		dput(d);+	if (h_inode)+		iput(h_inode);++	AuTraceErr(*a->errp);+}++/*+ * @dir: must be locked.+ * @dentry: target dentry.+ */+int vfsub_unlink(struct inode *dir, struct path *path, int force)+{+	int err;+	struct unlink_args args = {+		.errp	= &err,+		.dir	= dir,+		.path	= path+	};++	if (!force)+		call_unlink(&args);+	else {+		int wkq_err;++		wkq_err = au_wkq_wait(call_unlink, &args);+		if (unlikely(wkq_err))+			err = wkq_err;+	}++	return err;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/vfsub.h linux-2.6.31.5/fs/aufs/vfsub.h--- linux-2.6.31.5.orig/fs/aufs/vfsub.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/vfsub.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,172 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * sub-routines for VFS+ */++#ifndef __AUFS_VFSUB_H__+#define __AUFS_VFSUB_H__++#ifdef __KERNEL__++#include <linux/fs.h>+#include <linux/fs_stack.h>++/* ---------------------------------------------------------------------- */++/* lock subclass for lower inode */+/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */+/* reduce? gave up. */+enum {+	AuLsc_I_Begin = I_MUTEX_QUOTA, /* 4 */+	AuLsc_I_PARENT,		/* lower inode, parent first */+	AuLsc_I_PARENT2,	/* copyup dirs */+	AuLsc_I_PARENT3,	/* copyup wh */+	AuLsc_I_CHILD,+	AuLsc_I_CHILD2,+	AuLsc_I_End+};++/* to debug easier, do not make them inlined functions */+#define MtxMustLock(mtx)	AuDebugOn(!mutex_is_locked(mtx))+#define IMustLock(i)		MtxMustLock(&(i)->i_mutex)++/* ---------------------------------------------------------------------- */++static inline void vfsub_copy_inode_size(struct inode *inode,+					 struct inode *h_inode)+{+	spin_lock(&inode->i_lock);+	fsstack_copy_inode_size(inode, h_inode);+	spin_unlock(&inode->i_lock);+}++int vfsub_update_h_iattr(struct path *h_path, int *did);+struct file *vfsub_filp_open(const char *path, int oflags, int mode);+int vfsub_kern_path(const char *name, unsigned int flags, struct path *path);+struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent,+				    int len);+struct dentry *vfsub_lookup_hash(struct nameidata *nd);++/* ---------------------------------------------------------------------- */++struct au_hinode;+struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1,+				 struct dentry *d2, struct au_hinode *hdir2);+void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1,+			 struct dentry *d2, struct au_hinode *hdir2);++int vfsub_create(struct inode *dir, struct path *path, int mode);+int vfsub_symlink(struct inode *dir, struct path *path,+		  const char *symname);+int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev);+int vfsub_link(struct dentry *src_dentry, struct inode *dir,+	       struct path *path);+int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry,+		 struct inode *hdir, struct path *path);+int vfsub_mkdir(struct inode *dir, struct path *path, int mode);+int vfsub_rmdir(struct inode *dir, struct path *path);++/* ---------------------------------------------------------------------- */++ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,+		     loff_t *ppos);+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count,+			loff_t *ppos);+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,+		      loff_t *ppos);+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count,+		      loff_t *ppos);+int vfsub_readdir(struct file *file, filldir_t filldir, void *arg);++static inline void vfsub_file_accessed(struct file *h_file)+{+	file_accessed(h_file);+	vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); /*ignore*/+}++static inline void vfsub_touch_atime(struct vfsmount *h_mnt,+				     struct dentry *h_dentry)+{+	struct path h_path = {+		.dentry	= h_dentry,+		.mnt	= h_mnt+	};+	touch_atime(h_mnt, h_dentry);+	vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/+}++long vfsub_splice_to(struct file *in, loff_t *ppos,+		     struct pipe_inode_info *pipe, size_t len,+		     unsigned int flags);+long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out,+		       loff_t *ppos, size_t len, unsigned int flags);+int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr,+		struct file *h_file);++/* ---------------------------------------------------------------------- */++static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin)+{+	loff_t err;++	lockdep_off();+	err = vfs_llseek(file, offset, origin);+	lockdep_on();+	return err;+}++/* ---------------------------------------------------------------------- */++/* dirty workaround for strict type of fmode_t */+union vfsub_fmu {+	fmode_t fm;+	unsigned int ui;+};++static inline unsigned int vfsub_fmode_to_uint(fmode_t fm)+{+	union vfsub_fmu u = {+		.fm = fm+	};++	BUILD_BUG_ON(sizeof(u.fm) != sizeof(u.ui));++	return u.ui;+}++static inline fmode_t vfsub_uint_to_fmode(unsigned int ui)+{+	union vfsub_fmu u = {+		.ui = ui+	};++	return u.fm;+}++/* ---------------------------------------------------------------------- */++int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode);+int vfsub_sio_rmdir(struct inode *dir, struct path *path);+int vfsub_sio_notify_change(struct path *path, struct iattr *ia);+int vfsub_notify_change(struct path *path, struct iattr *ia);+int vfsub_unlink(struct inode *dir, struct path *path, int force);++#endif /* __KERNEL__ */+#endif /* __AUFS_VFSUB_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/wbr_policy.c linux-2.6.31.5/fs/aufs/wbr_policy.c--- linux-2.6.31.5.orig/fs/aufs/wbr_policy.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/wbr_policy.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,641 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * policies for selecting one among multiple writable branches+ */++#include <linux/statfs.h>+#include "aufs.h"++/* subset of cpup_attr() */+static noinline_for_stack+int au_cpdown_attr(struct path *h_path, struct dentry *h_src)+{+	int err, sbits;+	struct iattr ia;+	struct inode *h_isrc;++	h_isrc = h_src->d_inode;+	ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID;+	ia.ia_mode = h_isrc->i_mode;+	ia.ia_uid = h_isrc->i_uid;+	ia.ia_gid = h_isrc->i_gid;+	sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID));+	au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc);+	err = vfsub_sio_notify_change(h_path, &ia);++	/* is this nfs only? */+	if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) {+		ia.ia_valid = ATTR_FORCE | ATTR_MODE;+		ia.ia_mode = h_isrc->i_mode;+		err = vfsub_sio_notify_change(h_path, &ia);+	}++	return err;+}++#define AuCpdown_PARENT_OPQ	1+#define AuCpdown_WHED		(1 << 1)+#define AuCpdown_MADE_DIR	(1 << 2)+#define AuCpdown_DIROPQ		(1 << 3)+#define au_ftest_cpdown(flags, name)	((flags) & AuCpdown_##name)+#define au_fset_cpdown(flags, name)	{ (flags) |= AuCpdown_##name; }+#define au_fclr_cpdown(flags, name)	{ (flags) &= ~AuCpdown_##name; }++struct au_cpdown_dir_args {+	struct dentry *parent;+	unsigned int flags;+};++static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst,+			     struct au_cpdown_dir_args *a)+{+	int err;+	struct dentry *opq_dentry;++	opq_dentry = au_diropq_create(dentry, bdst);+	err = PTR_ERR(opq_dentry);+	if (IS_ERR(opq_dentry))+		goto out;+	dput(opq_dentry);+	au_fset_cpdown(a->flags, DIROPQ);++ out:+	return err;+}++static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent,+			    struct inode *dir, aufs_bindex_t bdst)+{+	int err;+	struct path h_path;+	struct au_branch *br;++	br = au_sbr(dentry->d_sb, bdst);+	h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br);+	err = PTR_ERR(h_path.dentry);+	if (IS_ERR(h_path.dentry))+		goto out;++	err = 0;+	if (h_path.dentry->d_inode) {+		h_path.mnt = br->br_mnt;+		err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path,+					  dentry);+	}+	dput(h_path.dentry);++ out:+	return err;+}++static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,+			 struct dentry *h_parent, void *arg)+{+	int err, rerr;+	aufs_bindex_t bend, bopq, bstart;+	unsigned char parent_opq;+	struct path h_path;+	struct dentry *parent;+	struct inode *h_dir, *h_inode, *inode, *dir;+	struct au_cpdown_dir_args *args = arg;++	bstart = au_dbstart(dentry);+	/* dentry is di-locked */+	parent = dget_parent(dentry);+	dir = parent->d_inode;+	h_dir = h_parent->d_inode;+	AuDebugOn(h_dir != au_h_iptr(dir, bdst));+	IMustLock(h_dir);++	err = au_lkup_neg(dentry, bdst);+	if (unlikely(err < 0))+		goto out;+	h_path.dentry = au_h_dptr(dentry, bdst);+	h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst);+	err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path,+			      S_IRWXU | S_IRUGO | S_IXUGO);+	if (unlikely(err))+		goto out_put;+	au_fset_cpdown(args->flags, MADE_DIR);++	bend = au_dbend(dentry);+	bopq = au_dbdiropq(dentry);+	au_fclr_cpdown(args->flags, WHED);+	au_fclr_cpdown(args->flags, DIROPQ);+	if (au_dbwh(dentry) == bdst)+		au_fset_cpdown(args->flags, WHED);+	if (!au_ftest_cpdown(args->flags, PARENT_OPQ) && bopq <= bdst)+		au_fset_cpdown(args->flags, PARENT_OPQ);+	parent_opq = (au_ftest_cpdown(args->flags, PARENT_OPQ)+		      && args->parent == dentry);+	h_inode = h_path.dentry->d_inode;+	mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);+	if (au_ftest_cpdown(args->flags, WHED)) {+		err = au_cpdown_dir_opq(dentry, bdst, args);+		if (unlikely(err)) {+			mutex_unlock(&h_inode->i_mutex);+			goto out_dir;+		}+	}++	err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart));+	mutex_unlock(&h_inode->i_mutex);+	if (unlikely(err))+		goto out_opq;++	if (au_ftest_cpdown(args->flags, WHED)) {+		err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst);+		if (unlikely(err))+			goto out_opq;+	}++	inode = dentry->d_inode;+	if (au_ibend(inode) < bdst)+		au_set_ibend(inode, bdst);+	au_set_h_iptr(inode, bdst, au_igrab(h_inode),+		      au_hi_flags(inode, /*isdir*/1));+	goto out; /* success */++	/* revert */+ out_opq:+	if (au_ftest_cpdown(args->flags, DIROPQ)) {+		mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);+		rerr = au_diropq_remove(dentry, bdst);+		mutex_unlock(&h_inode->i_mutex);+		if (unlikely(rerr)) {+			AuIOErr("failed removing diropq for %.*s b%d (%d)\n",+				AuDLNPair(dentry), bdst, rerr);+			err = -EIO;+			goto out;+		}+	}+ out_dir:+	if (au_ftest_cpdown(args->flags, MADE_DIR)) {+		rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path);+		if (unlikely(rerr)) {+			AuIOErr("failed removing %.*s b%d (%d)\n",+				AuDLNPair(dentry), bdst, rerr);+			err = -EIO;+		}+	}+ out_put:+	au_set_h_dptr(dentry, bdst, NULL);+	if (au_dbend(dentry) == bdst)+		au_update_dbend(dentry);+ out:+	dput(parent);+	return err;+}++int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst)+{+	int err;+	struct au_cpdown_dir_args args = {+		.parent	= dget_parent(dentry),+		.flags	= 0+	};++	err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &args);+	dput(args.parent);++	return err;+}++/* ---------------------------------------------------------------------- */++/* policies for create */++static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex)+{+	for (; bindex >= 0; bindex--)+		if (!au_br_rdonly(au_sbr(sb, bindex)))+			return bindex;+	return -EROFS;+}++/* top down parent */+static int au_wbr_create_tdp(struct dentry *dentry, int isdir __maybe_unused)+{+	int err;+	aufs_bindex_t bstart, bindex;+	struct super_block *sb;+	struct dentry *parent, *h_parent;++	sb = dentry->d_sb;+	bstart = au_dbstart(dentry);+	err = bstart;+	if (!au_br_rdonly(au_sbr(sb, bstart)))+		goto out;++	err = -EROFS;+	parent = dget_parent(dentry);+	for (bindex = au_dbstart(parent); bindex < bstart; bindex++) {+		h_parent = au_h_dptr(parent, bindex);+		if (!h_parent || !h_parent->d_inode)+			continue;++		if (!au_br_rdonly(au_sbr(sb, bindex))) {+			err = bindex;+			break;+		}+	}+	dput(parent);++	/* bottom up here */+	if (unlikely(err < 0))+		err = au_wbr_bu(sb, bstart - 1);++ out:+	AuDbg("b%d\n", err);+	return err;+}++/* ---------------------------------------------------------------------- */++/* an exception for the policy other than tdp */+static int au_wbr_create_exp(struct dentry *dentry)+{+	int err;+	aufs_bindex_t bwh, bdiropq;+	struct dentry *parent;++	err = -1;+	bwh = au_dbwh(dentry);+	parent = dget_parent(dentry);+	bdiropq = au_dbdiropq(parent);+	if (bwh >= 0) {+		if (bdiropq >= 0)+			err = min(bdiropq, bwh);+		else+			err = bwh;+		AuDbg("%d\n", err);+	} else if (bdiropq >= 0) {+		err = bdiropq;+		AuDbg("%d\n", err);+	}+	dput(parent);++	if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err)))+		err = -1;++	AuDbg("%d\n", err);+	return err;+}++/* ---------------------------------------------------------------------- */++/* round robin */+static int au_wbr_create_init_rr(struct super_block *sb)+{+	int err;++	err = au_wbr_bu(sb, au_sbend(sb));+	atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */+	/* smp_mb(); */++	AuDbg("b%d\n", err);+	return err;+}++static int au_wbr_create_rr(struct dentry *dentry, int isdir)+{+	int err, nbr;+	unsigned int u;+	aufs_bindex_t bindex, bend;+	struct super_block *sb;+	atomic_t *next;++	err = au_wbr_create_exp(dentry);+	if (err >= 0)+		goto out;++	sb = dentry->d_sb;+	next = &au_sbi(sb)->si_wbr_rr_next;+	bend = au_sbend(sb);+	nbr = bend + 1;+	for (bindex = 0; bindex <= bend; bindex++) {+		if (!isdir) {+			err = atomic_dec_return(next) + 1;+			/* modulo for 0 is meaningless */+			if (unlikely(!err))+				err = atomic_dec_return(next) + 1;+		} else+			err = atomic_read(next);+		AuDbg("%d\n", err);+		u = err;+		err = u % nbr;+		AuDbg("%d\n", err);+		if (!au_br_rdonly(au_sbr(sb, err)))+			break;+		err = -EROFS;+	}++ out:+	AuDbg("%d\n", err);+	return err;+}++/* ---------------------------------------------------------------------- */++/* most free space */+static void au_mfs(struct dentry *dentry)+{+	struct super_block *sb;+	struct au_branch *br;+	struct au_wbr_mfs *mfs;+	aufs_bindex_t bindex, bend;+	int err;+	unsigned long long b, bavail;+	/* reduce the stack usage */+	struct kstatfs *st;++	st = kmalloc(sizeof(*st), GFP_NOFS);+	if (unlikely(!st)) {+		AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM);+		return;+	}++	bavail = 0;+	sb = dentry->d_sb;+	mfs = &au_sbi(sb)->si_wbr_mfs;+	MtxMustLock(&mfs->mfs_lock);+	mfs->mfs_bindex = -EROFS;+	mfs->mfsrr_bytes = 0;+	bend = au_sbend(sb);+	for (bindex = 0; bindex <= bend; bindex++) {+		br = au_sbr(sb, bindex);+		if (au_br_rdonly(br))+			continue;++		/* sb->s_root for NFS is unreliable */+		err = vfs_statfs(br->br_mnt->mnt_root, st);+		if (unlikely(err)) {+			AuWarn1("failed statfs, b%d, %d\n", bindex, err);+			continue;+		}++		/* when the available size is equal, select the lower one */+		BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail)+			     || sizeof(b) < sizeof(st->f_bsize));+		b = st->f_bavail * st->f_bsize;+		br->br_wbr->wbr_bytes = b;+		if (b >= bavail) {+			bavail = b;+			mfs->mfs_bindex = bindex;+			mfs->mfs_jiffy = jiffies;+		}+	}++	mfs->mfsrr_bytes = bavail;+	AuDbg("b%d\n", mfs->mfs_bindex);+	kfree(st);+}++static int au_wbr_create_mfs(struct dentry *dentry, int isdir __maybe_unused)+{+	int err;+	struct super_block *sb;+	struct au_wbr_mfs *mfs;++	err = au_wbr_create_exp(dentry);+	if (err >= 0)+		goto out;++	sb = dentry->d_sb;+	mfs = &au_sbi(sb)->si_wbr_mfs;+	mutex_lock(&mfs->mfs_lock);+	if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire)+	    || mfs->mfs_bindex < 0+	    || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex)))+		au_mfs(dentry);+	mutex_unlock(&mfs->mfs_lock);+	err = mfs->mfs_bindex;++ out:+	AuDbg("b%d\n", err);+	return err;+}++static int au_wbr_create_init_mfs(struct super_block *sb)+{+	struct au_wbr_mfs *mfs;++	mfs = &au_sbi(sb)->si_wbr_mfs;+	mutex_init(&mfs->mfs_lock);+	mfs->mfs_jiffy = 0;+	mfs->mfs_bindex = -EROFS;++	return 0;+}++static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused)+{+	mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock);+	return 0;+}++/* ---------------------------------------------------------------------- */++/* most free space and then round robin */+static int au_wbr_create_mfsrr(struct dentry *dentry, int isdir)+{+	int err;+	struct au_wbr_mfs *mfs;++	err = au_wbr_create_mfs(dentry, isdir);+	if (err >= 0) {+		mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs;+		mutex_lock(&mfs->mfs_lock);+		if (mfs->mfsrr_bytes < mfs->mfsrr_watermark)+			err = au_wbr_create_rr(dentry, isdir);+		mutex_unlock(&mfs->mfs_lock);+	}++	AuDbg("b%d\n", err);+	return err;+}++static int au_wbr_create_init_mfsrr(struct super_block *sb)+{+	int err;++	au_wbr_create_init_mfs(sb); /* ignore */+	err = au_wbr_create_init_rr(sb);++	return err;+}++/* ---------------------------------------------------------------------- */++/* top down parent and most free space */+static int au_wbr_create_pmfs(struct dentry *dentry, int isdir)+{+	int err, e2;+	unsigned long long b;+	aufs_bindex_t bindex, bstart, bend;+	struct super_block *sb;+	struct dentry *parent, *h_parent;+	struct au_branch *br;++	err = au_wbr_create_tdp(dentry, isdir);+	if (unlikely(err < 0))+		goto out;+	parent = dget_parent(dentry);+	bstart = au_dbstart(parent);+	bend = au_dbtaildir(parent);+	if (bstart == bend)+		goto out_parent; /* success */++	e2 = au_wbr_create_mfs(dentry, isdir);+	if (e2 < 0)+		goto out_parent; /* success */++	/* when the available size is equal, select upper one */+	sb = dentry->d_sb;+	br = au_sbr(sb, err);+	b = br->br_wbr->wbr_bytes;+	AuDbg("b%d, %llu\n", err, b);++	for (bindex = bstart; bindex <= bend; bindex++) {+		h_parent = au_h_dptr(parent, bindex);+		if (!h_parent || !h_parent->d_inode)+			continue;++		br = au_sbr(sb, bindex);+		if (!au_br_rdonly(br) && br->br_wbr->wbr_bytes > b) {+			b = br->br_wbr->wbr_bytes;+			err = bindex;+			AuDbg("b%d, %llu\n", err, b);+		}+	}++ out_parent:+	dput(parent);+ out:+	AuDbg("b%d\n", err);+	return err;+}++/* ---------------------------------------------------------------------- */++/* policies for copyup */++/* top down parent */+static int au_wbr_copyup_tdp(struct dentry *dentry)+{+	return au_wbr_create_tdp(dentry, /*isdir, anything is ok*/0);+}++/* bottom up parent */+static int au_wbr_copyup_bup(struct dentry *dentry)+{+	int err;+	aufs_bindex_t bindex, bstart;+	struct dentry *parent, *h_parent;+	struct super_block *sb;++	err = -EROFS;+	sb = dentry->d_sb;+	parent = dget_parent(dentry);+	bstart = au_dbstart(parent);+	for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) {+		h_parent = au_h_dptr(parent, bindex);+		if (!h_parent || !h_parent->d_inode)+			continue;++		if (!au_br_rdonly(au_sbr(sb, bindex))) {+			err = bindex;+			break;+		}+	}+	dput(parent);++	/* bottom up here */+	if (unlikely(err < 0))+		err = au_wbr_bu(sb, bstart - 1);++	AuDbg("b%d\n", err);+	return err;+}++/* bottom up */+static int au_wbr_copyup_bu(struct dentry *dentry)+{+	int err;++	err = au_wbr_bu(dentry->d_sb, au_dbstart(dentry));++	AuDbg("b%d\n", err);+	return err;+}++/* ---------------------------------------------------------------------- */++struct au_wbr_copyup_operations au_wbr_copyup_ops[] = {+	[AuWbrCopyup_TDP] = {+		.copyup	= au_wbr_copyup_tdp+	},+	[AuWbrCopyup_BUP] = {+		.copyup	= au_wbr_copyup_bup+	},+	[AuWbrCopyup_BU] = {+		.copyup	= au_wbr_copyup_bu+	}+};++struct au_wbr_create_operations au_wbr_create_ops[] = {+	[AuWbrCreate_TDP] = {+		.create	= au_wbr_create_tdp+	},+	[AuWbrCreate_RR] = {+		.create	= au_wbr_create_rr,+		.init	= au_wbr_create_init_rr+	},+	[AuWbrCreate_MFS] = {+		.create	= au_wbr_create_mfs,+		.init	= au_wbr_create_init_mfs,+		.fin	= au_wbr_create_fin_mfs+	},+	[AuWbrCreate_MFSV] = {+		.create	= au_wbr_create_mfs,+		.init	= au_wbr_create_init_mfs,+		.fin	= au_wbr_create_fin_mfs+	},+	[AuWbrCreate_MFSRR] = {+		.create	= au_wbr_create_mfsrr,+		.init	= au_wbr_create_init_mfsrr,+		.fin	= au_wbr_create_fin_mfs+	},+	[AuWbrCreate_MFSRRV] = {+		.create	= au_wbr_create_mfsrr,+		.init	= au_wbr_create_init_mfsrr,+		.fin	= au_wbr_create_fin_mfs+	},+	[AuWbrCreate_PMFS] = {+		.create	= au_wbr_create_pmfs,+		.init	= au_wbr_create_init_mfs,+		.fin	= au_wbr_create_fin_mfs+	},+	[AuWbrCreate_PMFSV] = {+		.create	= au_wbr_create_pmfs,+		.init	= au_wbr_create_init_mfs,+		.fin	= au_wbr_create_fin_mfs+	}+};diff -Nur linux-2.6.31.5.orig/fs/aufs/whout.c linux-2.6.31.5/fs/aufs/whout.c--- linux-2.6.31.5.orig/fs/aufs/whout.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/whout.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,1048 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * whiteout for logical deletion and opaque directory+ */++#include <linux/fs.h>+#include "aufs.h"++#define WH_MASK			S_IRUGO++/*+ * If a directory contains this file, then it is opaque.  We start with the+ * .wh. flag so that it is blocked by lookup.+ */+static struct qstr diropq_name = {+	.name = AUFS_WH_DIROPQ,+	.len = sizeof(AUFS_WH_DIROPQ) - 1+};++/*+ * generate whiteout name, which is NOT terminated by NULL.+ * @name: original d_name.name+ * @len: original d_name.len+ * @wh: whiteout qstr+ * returns zero when succeeds, otherwise error.+ * succeeded value as wh->name should be freed by kfree().+ */+int au_wh_name_alloc(struct qstr *wh, const struct qstr *name)+{+	char *p;++	if (unlikely(name->len > PATH_MAX - AUFS_WH_PFX_LEN))+		return -ENAMETOOLONG;++	wh->len = name->len + AUFS_WH_PFX_LEN;+	p = kmalloc(wh->len, GFP_NOFS);+	wh->name = p;+	if (p) {+		memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN);+		memcpy(p + AUFS_WH_PFX_LEN, name->name, name->len);+		/* smp_mb(); */+		return 0;+	}+	return -ENOMEM;+}++/* ---------------------------------------------------------------------- */++/*+ * test if the @wh_name exists under @h_parent.+ * @try_sio specifies the necessary of super-io.+ */+int au_wh_test(struct dentry *h_parent, struct qstr *wh_name,+	       struct au_branch *br, int try_sio)+{+	int err;+	struct dentry *wh_dentry;+	struct inode *h_dir;++	h_dir = h_parent->d_inode;+	if (!try_sio)+		wh_dentry = au_lkup_one(wh_name, h_parent, br, /*nd*/NULL);+	else+		wh_dentry = au_sio_lkup_one(wh_name, h_parent, br);+	err = PTR_ERR(wh_dentry);+	if (IS_ERR(wh_dentry))+		goto out;++	err = 0;+	if (!wh_dentry->d_inode)+		goto out_wh; /* success */++	err = 1;+	if (S_ISREG(wh_dentry->d_inode->i_mode))+		goto out_wh; /* success */++	err = -EIO;+	AuIOErr("%.*s Invalid whiteout entry type 0%o.\n",+		AuDLNPair(wh_dentry), wh_dentry->d_inode->i_mode);++ out_wh:+	dput(wh_dentry);+ out:+	return err;+}++/*+ * test if the @h_dentry sets opaque or not.+ */+int au_diropq_test(struct dentry *h_dentry, struct au_branch *br)+{+	int err;+	struct inode *h_dir;++	h_dir = h_dentry->d_inode;+	err = au_wh_test(h_dentry, &diropq_name, br,+			 au_test_h_perm_sio(h_dir, MAY_EXEC));+	return err;+}++/*+ * returns a negative dentry whose name is unique and temporary.+ */+struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br,+			     struct qstr *prefix)+{+#define HEX_LEN	4+	struct dentry *dentry;+	int i;+	char defname[AUFS_WH_PFX_LEN * 2 + DNAME_INLINE_LEN_MIN + 1+		     + HEX_LEN + 1], *name, *p;+	static unsigned short cnt;+	struct qstr qs;++	name = defname;+	qs.len = sizeof(defname) - DNAME_INLINE_LEN_MIN + prefix->len - 1;+	if (unlikely(prefix->len > DNAME_INLINE_LEN_MIN)) {+		dentry = ERR_PTR(-ENAMETOOLONG);+		if (unlikely(qs.len >= PATH_MAX))+			goto out;+		dentry = ERR_PTR(-ENOMEM);+		name = kmalloc(qs.len + 1, GFP_NOFS);+		if (unlikely(!name))+			goto out;+	}++	/* doubly whiteout-ed */+	memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2);+	p = name + AUFS_WH_PFX_LEN * 2;+	memcpy(p, prefix->name, prefix->len);+	p += prefix->len;+	*p++ = '.';+	AuDebugOn(name + qs.len + 1 - p <= HEX_LEN);++	qs.name = name;+	for (i = 0; i < 3; i++) {+		sprintf(p, "%.*d", HEX_LEN, cnt++);+		dentry = au_sio_lkup_one(&qs, h_parent, br);+		if (IS_ERR(dentry) || !dentry->d_inode)+			goto out_name;+		dput(dentry);+	}+	/* AuWarn("could not get random name\n"); */+	dentry = ERR_PTR(-EEXIST);+	AuDbg("%.*s\n", AuLNPair(&qs));+	BUG();++ out_name:+	if (name != defname)+		kfree(name);+ out:+	return dentry;+#undef HEX_LEN+}++/*+ * rename the @h_dentry on @br to the whiteouted temporary name.+ */+int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br)+{+	int err;+	struct path h_path = {+		.mnt = br->br_mnt+	};+	struct inode *h_dir;+	struct dentry *h_parent;++	h_parent = h_dentry->d_parent; /* dir inode is locked */+	h_dir = h_parent->d_inode;+	IMustLock(h_dir);++	h_path.dentry = au_whtmp_lkup(h_parent, br, &h_dentry->d_name);+	err = PTR_ERR(h_path.dentry);+	if (IS_ERR(h_path.dentry))+		goto out;++	/* under the same dir, no need to lock_rename() */+	err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path);+	AuTraceErr(err);+	dput(h_path.dentry);++ out:+	return err;+}++/* ---------------------------------------------------------------------- */+/*+ * functions for removing a whiteout+ */++static int do_unlink_wh(struct inode *h_dir, struct path *h_path)+{+	int force;++	/*+	 * forces superio when the dir has a sticky bit.+	 * this may be a violation of unix fs semantics.+	 */+	force = (h_dir->i_mode & S_ISVTX)+		&& h_path->dentry->d_inode->i_uid != current_fsuid();+	return vfsub_unlink(h_dir, h_path, force);+}++int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path,+			struct dentry *dentry)+{+	int err;++	err = do_unlink_wh(h_dir, h_path);+	if (!err && dentry)+		au_set_dbwh(dentry, -1);++	return err;+}++static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh,+			  struct au_branch *br)+{+	int err;+	struct path h_path = {+		.mnt = br->br_mnt+	};++	err = 0;+	h_path.dentry = au_lkup_one(wh, h_parent, br, /*nd*/NULL);+	if (IS_ERR(h_path.dentry))+		err = PTR_ERR(h_path.dentry);+	else {+		if (h_path.dentry->d_inode+		    && S_ISREG(h_path.dentry->d_inode->i_mode))+			err = do_unlink_wh(h_parent->d_inode, &h_path);+		dput(h_path.dentry);+	}++	return err;+}++/* ---------------------------------------------------------------------- */+/*+ * initialize/clean whiteout for a branch+ */++static void au_wh_clean(struct inode *h_dir, struct path *whpath,+			const int isdir)+{+	int err;++	if (!whpath->dentry->d_inode)+		return;++	err = mnt_want_write(whpath->mnt);+	if (!err) {+		if (isdir)+			err = vfsub_rmdir(h_dir, whpath);+		else+			err = vfsub_unlink(h_dir, whpath, /*force*/0);+		mnt_drop_write(whpath->mnt);+	}+	if (unlikely(err))+		AuWarn("failed removing %.*s (%d), ignored.\n",+		       AuDLNPair(whpath->dentry), err);+}++static int test_linkable(struct dentry *h_root)+{+	struct inode *h_dir = h_root->d_inode;++	if (h_dir->i_op->link)+		return 0;++	AuErr("%.*s (%s) doesn't support link(2), use noplink and rw+nolwh\n",+	      AuDLNPair(h_root), au_sbtype(h_root->d_sb));+	return -ENOSYS;+}++/* todo: should this mkdir be done in /sbin/mount.aufs helper? */+static int au_whdir(struct inode *h_dir, struct path *path)+{+	int err;++	err = -EEXIST;+	if (!path->dentry->d_inode) {+		int mode = S_IRWXU;++		if (au_test_nfs(path->dentry->d_sb))+			mode |= S_IXUGO;+		err = mnt_want_write(path->mnt);+		if (!err) {+			err = vfsub_mkdir(h_dir, path, mode);+			mnt_drop_write(path->mnt);+		}+	} else if (S_ISDIR(path->dentry->d_inode->i_mode))+		err = 0;+	else+		AuErr("unknown %.*s exists\n", AuDLNPair(path->dentry));++	return err;+}++struct au_wh_base {+	const struct qstr *name;+	struct dentry *dentry;+};++static void au_wh_init_ro(struct inode *h_dir, struct au_wh_base base[],+			  struct path *h_path)+{+	h_path->dentry = base[AuBrWh_BASE].dentry;+	au_wh_clean(h_dir, h_path, /*isdir*/0);+	h_path->dentry = base[AuBrWh_PLINK].dentry;+	au_wh_clean(h_dir, h_path, /*isdir*/1);+	h_path->dentry = base[AuBrWh_ORPH].dentry;+	au_wh_clean(h_dir, h_path, /*isdir*/1);+}++/*+ * returns tri-state,+ * minus: error, caller should print the mesage+ * zero: succuess+ * plus: error, caller should NOT print the mesage+ */+static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr,+				int do_plink, struct au_wh_base base[],+				struct path *h_path)+{+	int err;+	struct inode *h_dir;++	h_dir = h_root->d_inode;+	h_path->dentry = base[AuBrWh_BASE].dentry;+	au_wh_clean(h_dir, h_path, /*isdir*/0);+	h_path->dentry = base[AuBrWh_PLINK].dentry;+	if (do_plink) {+		err = test_linkable(h_root);+		if (unlikely(err)) {+			err = 1;+			goto out;+		}++		err = au_whdir(h_dir, h_path);+		if (unlikely(err))+			goto out;+		wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry);+	} else+		au_wh_clean(h_dir, h_path, /*isdir*/1);+	h_path->dentry = base[AuBrWh_ORPH].dentry;+	err = au_whdir(h_dir, h_path);+	if (unlikely(err))+		goto out;+	wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry);++ out:+	return err;+}++/*+ * for the moment, aufs supports the branch filesystem which does not support+ * link(2). testing on FAT which does not support i_op->setattr() fully either,+ * copyup failed. finally, such filesystem will not be used as the writable+ * branch.+ *+ * returns tri-state, see above.+ */+static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr,+			 int do_plink, struct au_wh_base base[],+			 struct path *h_path)+{+	int err;+	struct inode *h_dir;++	WbrWhMustWriteLock(wbr);++	err = test_linkable(h_root);+	if (unlikely(err)) {+		err = 1;+		goto out;+	}++	/*+	 * todo: should this create be done in /sbin/mount.aufs helper?+	 */+	err = -EEXIST;+	h_dir = h_root->d_inode;+	if (!base[AuBrWh_BASE].dentry->d_inode) {+		err = mnt_want_write(h_path->mnt);+		if (!err) {+			h_path->dentry = base[AuBrWh_BASE].dentry;+			err = vfsub_create(h_dir, h_path, WH_MASK);+			mnt_drop_write(h_path->mnt);+		}+	} else if (S_ISREG(base[AuBrWh_BASE].dentry->d_inode->i_mode))+		err = 0;+	else+		AuErr("unknown %.*s/%.*s exists\n",+		      AuDLNPair(h_root), AuDLNPair(base[AuBrWh_BASE].dentry));+	if (unlikely(err))+		goto out;++	h_path->dentry = base[AuBrWh_PLINK].dentry;+	if (do_plink) {+		err = au_whdir(h_dir, h_path);+		if (unlikely(err))+			goto out;+		wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry);+	} else+		au_wh_clean(h_dir, h_path, /*isdir*/1);+	wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry);++	h_path->dentry = base[AuBrWh_ORPH].dentry;+	err = au_whdir(h_dir, h_path);+	if (unlikely(err))+		goto out;+	wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry);++ out:+	return err;+}++/*+ * initialize the whiteout base file/dir for @br.+ */+int au_wh_init(struct dentry *h_root, struct au_branch *br,+	       struct super_block *sb)+{+	int err, i;+	const unsigned char do_plink+		= !!au_opt_test(au_mntflags(sb), PLINK);+	struct path path = {+		.mnt = br->br_mnt+	};+	struct inode *h_dir;+	struct au_wbr *wbr = br->br_wbr;+	static const struct qstr base_name[] = {+		[AuBrWh_BASE] = {+			.name	= AUFS_BASE_NAME,+			.len	= sizeof(AUFS_BASE_NAME) - 1+		},+		[AuBrWh_PLINK] = {+			.name	= AUFS_PLINKDIR_NAME,+			.len	= sizeof(AUFS_PLINKDIR_NAME) - 1+		},+		[AuBrWh_ORPH] = {+			.name	= AUFS_ORPHDIR_NAME,+			.len	= sizeof(AUFS_ORPHDIR_NAME) - 1+		}+	};+	struct au_wh_base base[] = {+		[AuBrWh_BASE] = {+			.name	= base_name + AuBrWh_BASE,+			.dentry	= NULL+		},+		[AuBrWh_PLINK] = {+			.name	= base_name + AuBrWh_PLINK,+			.dentry	= NULL+		},+		[AuBrWh_ORPH] = {+			.name	= base_name + AuBrWh_ORPH,+			.dentry	= NULL+		}+	};++	if (wbr)+		WbrWhMustWriteLock(wbr);++	h_dir = h_root->d_inode;+	for (i = 0; i < AuBrWh_Last; i++) {+		/* doubly whiteouted */+		struct dentry *d;++		d = au_wh_lkup(h_root, (void *)base[i].name, br);+		err = PTR_ERR(d);+		if (IS_ERR(d))+			goto out;++		base[i].dentry = d;+		AuDebugOn(wbr+			  && wbr->wbr_wh[i]+			  && wbr->wbr_wh[i] != base[i].dentry);+	}++	if (wbr)+		for (i = 0; i < AuBrWh_Last; i++) {+			dput(wbr->wbr_wh[i]);+			wbr->wbr_wh[i] = NULL;+		}++	err = 0;++	switch (br->br_perm) {+	case AuBrPerm_RO:+	case AuBrPerm_ROWH:+	case AuBrPerm_RR:+	case AuBrPerm_RRWH:+		au_wh_init_ro(h_dir, base, &path);+		break;++	case AuBrPerm_RWNoLinkWH:+		err = au_wh_init_rw_nolink(h_root, wbr, do_plink, base, &path);+		if (err > 0)+			goto out;+		else if (err)+			goto out_err;+		break;++	case AuBrPerm_RW:+		err = au_wh_init_rw(h_root, wbr, do_plink, base, &path);+		if (err > 0)+			goto out;+		else if (err)+			goto out_err;+		break;++	default:+		BUG();+	}+	goto out; /* success */++ out_err:+	AuErr("an error(%d) on the writable branch %.*s(%s)\n",+	      err, AuDLNPair(h_root), au_sbtype(h_root->d_sb));+ out:+	for (i = 0; i < AuBrWh_Last; i++)+		dput(base[i].dentry);+	return err;+}++/* ---------------------------------------------------------------------- */+/*+ * whiteouts are all hard-linked usually.+ * when its link count reaches a ceiling, we create a new whiteout base+ * asynchronously.+ */++struct reinit_br_wh {+	struct super_block *sb;+	struct au_branch *br;+};++static void reinit_br_wh(void *arg)+{+	int err;+	aufs_bindex_t bindex;+	struct path h_path;+	struct reinit_br_wh *a = arg;+	struct au_wbr *wbr;+	struct inode *dir;+	struct dentry *h_root;+	struct au_hinode *hdir;++	err = 0;+	wbr = a->br->br_wbr;+	/* big aufs lock */+	si_noflush_write_lock(a->sb);+	if (!au_br_writable(a->br->br_perm))+		goto out;+	bindex = au_br_index(a->sb, a->br->br_id);+	if (unlikely(bindex < 0))+		goto out;++	di_read_lock_parent(a->sb->s_root, AuLock_IR);+	dir = a->sb->s_root->d_inode;+	hdir = au_hi(dir, bindex);+	h_root = au_h_dptr(a->sb->s_root, bindex);++	au_hin_imtx_lock_nested(hdir, AuLsc_I_PARENT);+	wbr_wh_write_lock(wbr);+	err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode,+			  h_root, a->br);+	if (!err) {+		err = mnt_want_write(a->br->br_mnt);+		if (!err) {+			h_path.dentry = wbr->wbr_whbase;+			h_path.mnt = a->br->br_mnt;+			err = vfsub_unlink(hdir->hi_inode, &h_path, /*force*/0);+			mnt_drop_write(a->br->br_mnt);+		}+	} else {+		AuWarn("%.*s is moved, ignored\n", AuDLNPair(wbr->wbr_whbase));+		err = 0;+	}+	dput(wbr->wbr_whbase);+	wbr->wbr_whbase = NULL;+	if (!err)+		err = au_wh_init(h_root, a->br, a->sb);+	wbr_wh_write_unlock(wbr);+	au_hin_imtx_unlock(hdir);+	di_read_unlock(a->sb->s_root, AuLock_IR);++ out:+	if (wbr)+		atomic_dec(&wbr->wbr_wh_running);+	atomic_dec(&a->br->br_count);+	au_nwt_done(&au_sbi(a->sb)->si_nowait);+	si_write_unlock(a->sb);+	kfree(arg);+	if (unlikely(err))+		AuIOErr("err %d\n", err);+}++static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br)+{+	int do_dec, wkq_err;+	struct reinit_br_wh *arg;++	do_dec = 1;+	if (atomic_inc_return(&br->br_wbr->wbr_wh_running) != 1)+		goto out;++	/* ignore ENOMEM */+	arg = kmalloc(sizeof(*arg), GFP_NOFS);+	if (arg) {+		/*+		 * dec(wh_running), kfree(arg) and dec(br_count)+		 * in reinit function+		 */+		arg->sb = sb;+		arg->br = br;+		atomic_inc(&br->br_count);+		wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb);+		if (unlikely(wkq_err)) {+			atomic_dec(&br->br_wbr->wbr_wh_running);+			atomic_dec(&br->br_count);+			kfree(arg);+		}+		do_dec = 0;+	}++ out:+	if (do_dec)+		atomic_dec(&br->br_wbr->wbr_wh_running);+}++/* ---------------------------------------------------------------------- */++/*+ * create the whiteout @wh.+ */+static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex,+			     struct dentry *wh)+{+	int err;+	struct path h_path = {+		.dentry = wh+	};+	struct au_branch *br;+	struct au_wbr *wbr;+	struct dentry *h_parent;+	struct inode *h_dir;++	h_parent = wh->d_parent; /* dir inode is locked */+	h_dir = h_parent->d_inode;+	IMustLock(h_dir);++	br = au_sbr(sb, bindex);+	h_path.mnt = br->br_mnt;+	wbr = br->br_wbr;+	wbr_wh_read_lock(wbr);+	if (wbr->wbr_whbase) {+		err = vfsub_link(wbr->wbr_whbase, h_dir, &h_path);+		if (!err || err != -EMLINK)+			goto out;++		/* link count full. re-initialize br_whbase. */+		kick_reinit_br_wh(sb, br);+	}++	/* return this error in this context */+	err = vfsub_create(h_dir, &h_path, WH_MASK);++ out:+	wbr_wh_read_unlock(wbr);+	return err;+}++/* ---------------------------------------------------------------------- */++/*+ * create or remove the diropq.+ */+static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex,+				unsigned int flags)+{+	struct dentry *opq_dentry, *h_dentry;+	struct super_block *sb;+	struct au_branch *br;+	int err;++	sb = dentry->d_sb;+	br = au_sbr(sb, bindex);+	h_dentry = au_h_dptr(dentry, bindex);+	opq_dentry = au_lkup_one(&diropq_name, h_dentry, br, /*nd*/NULL);+	if (IS_ERR(opq_dentry))+		goto out;++	if (au_ftest_diropq(flags, CREATE)) {+		err = link_or_create_wh(sb, bindex, opq_dentry);+		if (!err) {+			au_set_dbdiropq(dentry, bindex);+			goto out; /* success */+		}+	} else {+		struct path tmp = {+			.dentry = opq_dentry,+			.mnt	= br->br_mnt+		};+		err = do_unlink_wh(au_h_iptr(dentry->d_inode, bindex), &tmp);+		if (!err)+			au_set_dbdiropq(dentry, -1);+	}+	dput(opq_dentry);+	opq_dentry = ERR_PTR(err);++ out:+	return opq_dentry;+}++struct do_diropq_args {+	struct dentry **errp;+	struct dentry *dentry;+	aufs_bindex_t bindex;+	unsigned int flags;+};++static void call_do_diropq(void *args)+{+	struct do_diropq_args *a = args;+	*a->errp = do_diropq(a->dentry, a->bindex, a->flags);+}++struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex,+			     unsigned int flags)+{+	struct dentry *diropq, *h_dentry;++	h_dentry = au_h_dptr(dentry, bindex);+	if (!au_test_h_perm_sio(h_dentry->d_inode, MAY_EXEC | MAY_WRITE))+		diropq = do_diropq(dentry, bindex, flags);+	else {+		int wkq_err;+		struct do_diropq_args args = {+			.errp		= &diropq,+			.dentry		= dentry,+			.bindex		= bindex,+			.flags		= flags+		};++		wkq_err = au_wkq_wait(call_do_diropq, &args);+		if (unlikely(wkq_err))+			diropq = ERR_PTR(wkq_err);+	}++	return diropq;+}++/* ---------------------------------------------------------------------- */++/*+ * lookup whiteout dentry.+ * @h_parent: lower parent dentry which must exist and be locked+ * @base_name: name of dentry which will be whiteouted+ * returns dentry for whiteout.+ */+struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name,+			  struct au_branch *br)+{+	int err;+	struct qstr wh_name;+	struct dentry *wh_dentry;++	err = au_wh_name_alloc(&wh_name, base_name);+	wh_dentry = ERR_PTR(err);+	if (!err) {+		wh_dentry = au_lkup_one(&wh_name, h_parent, br, /*nd*/NULL);+		kfree(wh_name.name);+	}+	return wh_dentry;+}++/*+ * link/create a whiteout for @dentry on @bindex.+ */+struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex,+			    struct dentry *h_parent)+{+	struct dentry *wh_dentry;+	struct super_block *sb;+	int err;++	sb = dentry->d_sb;+	wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex));+	if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) {+		err = link_or_create_wh(sb, bindex, wh_dentry);+		if (!err)+			au_set_dbwh(dentry, bindex);+		else {+			dput(wh_dentry);+			wh_dentry = ERR_PTR(err);+		}+	}++	return wh_dentry;+}++/* ---------------------------------------------------------------------- */++/* Delete all whiteouts in this directory on branch bindex. */+static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist,+			   aufs_bindex_t bindex, struct au_branch *br)+{+	int err;+	unsigned long ul, n;+	struct qstr wh_name;+	char *p;+	struct hlist_head *head;+	struct au_vdir_wh *tpos;+	struct hlist_node *pos;+	struct au_vdir_destr *str;++	err = -ENOMEM;+	p = __getname();+	wh_name.name = p;+	if (unlikely(!wh_name.name))+		goto out;++	err = 0;+	memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN);+	p += AUFS_WH_PFX_LEN;+	n = whlist->nh_num;+	head = whlist->nh_head;+	for (ul = 0; !err && ul < n; ul++, head++) {+		hlist_for_each_entry(tpos, pos, head, wh_hash) {+			if (tpos->wh_bindex != bindex)+				continue;++			str = &tpos->wh_str;+			if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) {+				memcpy(p, str->name, str->len);+				wh_name.len = AUFS_WH_PFX_LEN + str->len;+				err = unlink_wh_name(h_dentry, &wh_name, br);+				if (!err)+					continue;+				break;+			}+			AuIOErr("whiteout name too long %.*s\n",+				str->len, str->name);+			err = -EIO;+			break;+		}+	}+	__putname(wh_name.name);++ out:+	return err;+}++struct del_wh_children_args {+	int *errp;+	struct dentry *h_dentry;+	struct au_nhash whlist;+	aufs_bindex_t bindex;+	struct au_branch *br;+};++static void call_del_wh_children(void *args)+{+	struct del_wh_children_args *a = args;+	*a->errp = del_wh_children(a->h_dentry, &a->whlist, a->bindex, a->br);+}++/* ---------------------------------------------------------------------- */++struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp)+{+	struct au_whtmp_rmdir *whtmp;+	int err;++	SiMustAnyLock(sb);++	whtmp = kmalloc(sizeof(*whtmp), gfp);+	if (unlikely(!whtmp)) {+		whtmp = ERR_PTR(-ENOMEM);+		goto out;+	}++	whtmp->dir = NULL;+	whtmp->wh_dentry = NULL;+	err = au_nhash_alloc(&whtmp->whlist, au_sbi(sb)->si_rdhash, gfp);+	if (!err)+		return whtmp; /* success */++	kfree(whtmp);+	whtmp = ERR_PTR(err);++ out:+	return whtmp;+}++void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp)+{+	dput(whtmp->wh_dentry);+	iput(whtmp->dir);+	au_nhash_wh_free(&whtmp->whlist);+	kfree(whtmp);+}++/*+ * rmdir the whiteouted temporary named dir @h_dentry.+ * @whlist: whiteouted children.+ */+int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex,+		   struct dentry *wh_dentry, struct au_nhash *whlist)+{+	int err;+	struct path h_tmp;+	struct inode *wh_inode, *h_dir;+	struct au_branch *br;++	h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */+	IMustLock(h_dir);++	br = au_sbr(dir->i_sb, bindex);+	wh_inode = wh_dentry->d_inode;+	mutex_lock_nested(&wh_inode->i_mutex, AuLsc_I_CHILD);++	/*+	 * someone else might change some whiteouts while we were sleeping.+	 * it means this whlist may have an obsoleted entry.+	 */+	if (!au_test_h_perm_sio(wh_inode, MAY_EXEC | MAY_WRITE))+		err = del_wh_children(wh_dentry, whlist, bindex, br);+	else {+		int wkq_err;+		struct del_wh_children_args args = {+			.errp		= &err,+			.h_dentry	= wh_dentry,+			.whlist		= *whlist,+			.bindex		= bindex,+			.br		= br+		};++		wkq_err = au_wkq_wait(call_del_wh_children, &args);+		if (unlikely(wkq_err))+			err = wkq_err;+	}+	mutex_unlock(&wh_inode->i_mutex);++	if (!err) {+		h_tmp.dentry = wh_dentry;+		h_tmp.mnt = br->br_mnt;+		err = vfsub_rmdir(h_dir, &h_tmp);+		/* d_drop(h_dentry); */+	}++	if (!err) {+		if (au_ibstart(dir) == bindex) {+			au_cpup_attr_timesizes(dir);+			drop_nlink(dir);+		}+		return 0; /* success */+	}++	AuWarn("failed removing %.*s(%d), ignored\n",+	       AuDLNPair(wh_dentry), err);+	return err;+}++static void call_rmdir_whtmp(void *args)+{+	int err;+	struct au_whtmp_rmdir *a = args;+	struct super_block *sb;+	struct dentry *h_parent;+	struct inode *h_dir;+	struct au_branch *br;+	struct au_hinode *hdir;++	/* rmdir by nfsd may cause deadlock with this i_mutex */+	/* mutex_lock(&a->dir->i_mutex); */+	sb = a->dir->i_sb;+	si_noflush_read_lock(sb);+	err = au_test_ro(sb, a->bindex, NULL);+	if (unlikely(err))+		goto out;++	err = -EIO;+	br = au_sbr(sb, a->bindex);+	ii_write_lock_parent(a->dir);+	h_parent = dget_parent(a->wh_dentry);+	h_dir = h_parent->d_inode;+	hdir = au_hi(a->dir, a->bindex);+	au_hin_imtx_lock_nested(hdir, AuLsc_I_PARENT);+	err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, br);+	if (!err) {+		err = mnt_want_write(br->br_mnt);+		if (!err) {+			err = au_whtmp_rmdir(a->dir, a->bindex, a->wh_dentry,+					     &a->whlist);+			mnt_drop_write(br->br_mnt);+		}+	}+	au_hin_imtx_unlock(hdir);+	dput(h_parent);+	ii_write_unlock(a->dir);++ out:+	/* mutex_unlock(&a->dir->i_mutex); */+	au_nwt_done(&au_sbi(sb)->si_nowait);+	si_read_unlock(sb);+	au_whtmp_rmdir_free(a);+	if (unlikely(err))+		AuIOErr("err %d\n", err);+}++void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex,+			 struct dentry *wh_dentry, struct au_whtmp_rmdir *args)+{+	int wkq_err;++	IMustLock(dir);++	/* all post-process will be done in do_rmdir_whtmp(). */+	args->dir = au_igrab(dir);+	args->bindex = bindex;+	args->wh_dentry = dget(wh_dentry);+	wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, dir->i_sb);+	if (unlikely(wkq_err)) {+		AuWarn("rmdir error %.*s (%d), ignored\n",+		       AuDLNPair(wh_dentry), wkq_err);+		au_whtmp_rmdir_free(args);+	}+}diff -Nur linux-2.6.31.5.orig/fs/aufs/whout.h linux-2.6.31.5/fs/aufs/whout.h--- linux-2.6.31.5.orig/fs/aufs/whout.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/whout.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,87 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * whiteout for logical deletion and opaque directory+ */++#ifndef __AUFS_WHOUT_H__+#define __AUFS_WHOUT_H__++#ifdef __KERNEL__++#include <linux/aufs_type.h>+#include "dir.h"++/* whout.c */+int au_wh_name_alloc(struct qstr *wh, const struct qstr *name);+struct au_branch;+int au_wh_test(struct dentry *h_parent, struct qstr *wh_name,+	       struct au_branch *br, int try_sio);+int au_diropq_test(struct dentry *h_dentry, struct au_branch *br);+struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br,+			     struct qstr *prefix);+int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br);+int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path,+			struct dentry *dentry);+int au_wh_init(struct dentry *h_parent, struct au_branch *br,+	       struct super_block *sb);++/* diropq flags */+#define AuDiropq_CREATE	1+#define au_ftest_diropq(flags, name)	((flags) & AuDiropq_##name)+#define au_fset_diropq(flags, name)	{ (flags) |= AuDiropq_##name; }+#define au_fclr_diropq(flags, name)	{ (flags) &= ~AuDiropq_##name; }++struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex,+			     unsigned int flags);+struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name,+			  struct au_branch *br);+struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex,+			    struct dentry *h_parent);++/* real rmdir for the whiteout-ed dir */+struct au_whtmp_rmdir {+	struct inode *dir;+	aufs_bindex_t bindex;+	struct dentry *wh_dentry;+	struct au_nhash whlist;+};++struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp);+void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp);+int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex,+		   struct dentry *wh_dentry, struct au_nhash *whlist);+void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex,+			 struct dentry *wh_dentry, struct au_whtmp_rmdir *args);++/* ---------------------------------------------------------------------- */++static inline struct dentry *au_diropq_create(struct dentry *dentry,+					      aufs_bindex_t bindex)+{+	return au_diropq_sio(dentry, bindex, AuDiropq_CREATE);+}++static inline int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex)+{+	return PTR_ERR(au_diropq_sio(dentry, bindex, !AuDiropq_CREATE));+}++#endif /* __KERNEL__ */+#endif /* __AUFS_WHOUT_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/wkq.c linux-2.6.31.5/fs/aufs/wkq.c--- linux-2.6.31.5.orig/fs/aufs/wkq.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/wkq.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,259 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * workqueue for asynchronous/super-io operations+ * todo: try new dredential scheme+ */++#include <linux/module.h>+#include "aufs.h"++/* internal workqueue named AUFS_WKQ_NAME */+static struct au_wkq {+	struct workqueue_struct	*q;++	/* balancing */+	atomic_t		busy;+} *au_wkq;++struct au_wkinfo {+	struct work_struct wk;+	struct super_block *sb;++	unsigned int flags; /* see wkq.h */++	au_wkq_func_t func;+	void *args;++	atomic_t *busyp;+	struct completion *comp;+};++/* ---------------------------------------------------------------------- */++static int enqueue(struct au_wkq *wkq, struct au_wkinfo *wkinfo)+{+	wkinfo->busyp = &wkq->busy;+	if (au_ftest_wkq(wkinfo->flags, WAIT))+		return !queue_work(wkq->q, &wkinfo->wk);+	else+		return !schedule_work(&wkinfo->wk);+}++static void do_wkq(struct au_wkinfo *wkinfo)+{+	unsigned int idle, n;+	int i, idle_idx;++	while (1) {+		if (au_ftest_wkq(wkinfo->flags, WAIT)) {+			idle_idx = 0;+			idle = UINT_MAX;+			for (i = 0; i < aufs_nwkq; i++) {+				n = atomic_inc_return(&au_wkq[i].busy);+				if (n == 1 && !enqueue(au_wkq + i, wkinfo))+					return; /* success */++				if (n < idle) {+					idle_idx = i;+					idle = n;+				}+				atomic_dec(&au_wkq[i].busy);+			}+		} else+			idle_idx = aufs_nwkq;++		atomic_inc(&au_wkq[idle_idx].busy);+		if (!enqueue(au_wkq + idle_idx, wkinfo))+			return; /* success */++		/* impossible? */+		AuWarn1("failed to queue_work()\n");+		yield();+	}+}++static void wkq_func(struct work_struct *wk)+{+	struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk);++	wkinfo->func(wkinfo->args);+	atomic_dec_return(wkinfo->busyp);+	if (au_ftest_wkq(wkinfo->flags, WAIT))+		complete(wkinfo->comp);+	else {+		kobject_put(&au_sbi(wkinfo->sb)->si_kobj);+		module_put(THIS_MODULE);+		kfree(wkinfo);+	}+}++/*+ * Since struct completion is large, try allocating it dynamically.+ */+#if defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS)+#define AuWkqCompDeclare(name)	struct completion *comp = NULL++static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp)+{+	*comp = kmalloc(sizeof(**comp), GFP_NOFS);+	if (*comp) {+		init_completion(*comp);+		wkinfo->comp = *comp;+		return 0;+	}+	return -ENOMEM;+}++static void au_wkq_comp_free(struct completion *comp)+{+	kfree(comp);+}++#else++/* no braces */+#define AuWkqCompDeclare(name) \+	DECLARE_COMPLETION_ONSTACK(_ ## name); \+	struct completion *comp = &_ ## name++static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp)+{+	wkinfo->comp = *comp;+	return 0;+}++static void au_wkq_comp_free(struct completion *comp __maybe_unused)+{+	/* empty */+}+#endif /* 4KSTACKS */++static void au_wkq_run(struct au_wkinfo *wkinfo)+{+	au_dbg_verify_kthread();+	INIT_WORK(&wkinfo->wk, wkq_func);+	do_wkq(wkinfo);+}++int au_wkq_wait(au_wkq_func_t func, void *args)+{+	int err;+	AuWkqCompDeclare(comp);+	struct au_wkinfo wkinfo = {+		.flags	= AuWkq_WAIT,+		.func	= func,+		.args	= args+	};++	err = au_wkq_comp_alloc(&wkinfo, &comp);+	if (!err) {+		au_wkq_run(&wkinfo);+		/* no timeout, no interrupt */+		wait_for_completion(wkinfo.comp);+		au_wkq_comp_free(comp);+	}++	return err;++}++int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb)+{+	int err;+	struct au_wkinfo *wkinfo;++	atomic_inc(&au_sbi(sb)->si_nowait.nw_len);++	/*+	 * wkq_func() must free this wkinfo.+	 * it highly depends upon the implementation of workqueue.+	 */+	err = 0;+	wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS);+	if (wkinfo) {+		wkinfo->sb = sb;+		wkinfo->flags = !AuWkq_WAIT;+		wkinfo->func = func;+		wkinfo->args = args;+		wkinfo->comp = NULL;+		kobject_get(&au_sbi(sb)->si_kobj);+		__module_get(THIS_MODULE);++		au_wkq_run(wkinfo);+	} else {+		err = -ENOMEM;+		atomic_dec(&au_sbi(sb)->si_nowait.nw_len);+	}++	return err;+}++/* ---------------------------------------------------------------------- */++void au_nwt_init(struct au_nowait_tasks *nwt)+{+	atomic_set(&nwt->nw_len, 0);+	/* smp_mb();*/ /* atomic_set */+	init_waitqueue_head(&nwt->nw_wq);+}++void au_wkq_fin(void)+{+	int i;++	for (i = 0; i < aufs_nwkq; i++)+		if (au_wkq[i].q && !IS_ERR(au_wkq[i].q))+			destroy_workqueue(au_wkq[i].q);+	kfree(au_wkq);+}++int __init au_wkq_init(void)+{+	int err, i;+	struct au_wkq *nowaitq;++	/* '+1' is for accounting of nowait queue */+	err = -ENOMEM;+	au_wkq = kcalloc(aufs_nwkq + 1, sizeof(*au_wkq), GFP_NOFS);+	if (unlikely(!au_wkq))+		goto out;++	err = 0;+	for (i = 0; i < aufs_nwkq; i++) {+		au_wkq[i].q = create_singlethread_workqueue(AUFS_WKQ_NAME);+		if (au_wkq[i].q && !IS_ERR(au_wkq[i].q)) {+			atomic_set(&au_wkq[i].busy, 0);+			continue;+		}++		err = PTR_ERR(au_wkq[i].q);+		au_wkq_fin();+		goto out;+	}++	/* nowait accounting */+	nowaitq = au_wkq + aufs_nwkq;+	atomic_set(&nowaitq->busy, 0);+	nowaitq->q = NULL;+	/* smp_mb(); */ /* atomic_set */++ out:+	return err;+}diff -Nur linux-2.6.31.5.orig/fs/aufs/wkq.h linux-2.6.31.5/fs/aufs/wkq.h--- linux-2.6.31.5.orig/fs/aufs/wkq.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/wkq.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,82 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * workqueue for asynchronous/super-io operations+ * todo: try new credentials management scheme+ */++#ifndef __AUFS_WKQ_H__+#define __AUFS_WKQ_H__++#ifdef __KERNEL__++#include <linux/sched.h>+#include <linux/wait.h>+#include <linux/aufs_type.h>++struct super_block;++/* ---------------------------------------------------------------------- */++/*+ * in the next operation, wait for the 'nowait' tasks in system-wide workqueue+ */+struct au_nowait_tasks {+	atomic_t		nw_len;+	wait_queue_head_t	nw_wq;+};++/* ---------------------------------------------------------------------- */++typedef void (*au_wkq_func_t)(void *args);++/* wkq flags */+#define AuWkq_WAIT	1+#define au_ftest_wkq(flags, name)	((flags) & AuWkq_##name)+#define au_fset_wkq(flags, name)	{ (flags) |= AuWkq_##name; }+#define au_fclr_wkq(flags, name)	{ (flags) &= ~AuWkq_##name; }++/* wkq.c */+int au_wkq_wait(au_wkq_func_t func, void *args);+int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb);+void au_nwt_init(struct au_nowait_tasks *nwt);+int __init au_wkq_init(void);+void au_wkq_fin(void);++/* ---------------------------------------------------------------------- */++static inline int au_test_wkq(struct task_struct *tsk)+{+	return !tsk->mm && !strcmp(tsk->comm, AUFS_WKQ_NAME);+}++static inline void au_nwt_done(struct au_nowait_tasks *nwt)+{+	if (!atomic_dec_return(&nwt->nw_len))+		wake_up_all(&nwt->nw_wq);+}++static inline int au_nwt_flush(struct au_nowait_tasks *nwt)+{+	wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len));+	return 0;+}++#endif /* __KERNEL__ */+#endif /* __AUFS_WKQ_H__ */diff -Nur linux-2.6.31.5.orig/fs/aufs/xino.c linux-2.6.31.5/fs/aufs/xino.c--- linux-2.6.31.5.orig/fs/aufs/xino.c	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/fs/aufs/xino.c	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,1200 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++/*+ * external inode number translation table and bitmap+ */++#include <linux/file.h>+#include <linux/seq_file.h>+#include <linux/uaccess.h>+#include "aufs.h"++ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, size_t size,+		   loff_t *pos)+{+	ssize_t err;+	mm_segment_t oldfs;++	oldfs = get_fs();+	set_fs(KERNEL_DS);+	do {+		/* todo: signal_pending? */+		err = func(file, (char __user *)buf, size, pos);+	} while (err == -EAGAIN || err == -EINTR);+	set_fs(oldfs);++#if 0 /* reserved for future use */+	if (err > 0)+		fsnotify_access(file->f_dentry);+#endif++	return err;+}++/* ---------------------------------------------------------------------- */++static ssize_t do_xino_fwrite(au_writef_t func, struct file *file, void *buf,+			      size_t size, loff_t *pos)+{+	ssize_t err;+	mm_segment_t oldfs;++	oldfs = get_fs();+	set_fs(KERNEL_DS);+	lockdep_off();+	do {+		/* todo: signal_pending? */+		err = func(file, (const char __user *)buf, size, pos);+	} while (err == -EAGAIN || err == -EINTR);+	lockdep_on();+	set_fs(oldfs);++#if 0 /* reserved for future use */+	if (err > 0)+		fsnotify_modify(file->f_dentry);+#endif++	return err;+}++struct do_xino_fwrite_args {+	ssize_t *errp;+	au_writef_t func;+	struct file *file;+	void *buf;+	size_t size;+	loff_t *pos;+};++static void call_do_xino_fwrite(void *args)+{+	struct do_xino_fwrite_args *a = args;+	*a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos);+}++ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size,+		    loff_t *pos)+{+	ssize_t err;++	/* todo: signal block and no wkq? */+	/* todo: new credential scheme */+	/*+	 * it breaks RLIMIT_FSIZE and normal user's limit,+	 * users should care about quota and real 'filesystem full.'+	 */+	if (!au_test_wkq(current)) {+		int wkq_err;+		struct do_xino_fwrite_args args = {+			.errp	= &err,+			.func	= func,+			.file	= file,+			.buf	= buf,+			.size	= size,+			.pos	= pos+		};++		wkq_err = au_wkq_wait(call_do_xino_fwrite, &args);+		if (unlikely(wkq_err))+			err = wkq_err;+	} else+		err = do_xino_fwrite(func, file, buf, size, pos);++	return err;+}++/* ---------------------------------------------------------------------- */++/*+ * create a new xinofile at the same place/path as @base_file.+ */+struct file *au_xino_create2(struct file *base_file, struct file *copy_src)+{+	struct file *file;+	struct dentry *base, *dentry, *parent;+	struct inode *dir;+	struct qstr *name;+	int err;++	base = base_file->f_dentry;+	parent = base->d_parent; /* dir inode is locked */+	dir = parent->d_inode;+	IMustLock(dir);++	file = ERR_PTR(-EINVAL);+	name = &base->d_name;+	dentry = vfsub_lookup_one_len(name->name, parent, name->len);+	if (IS_ERR(dentry)) {+		file = (void *)dentry;+		AuErr("%.*s lookup err %ld\n", AuLNPair(name), PTR_ERR(dentry));+		goto out;+	}++	/* no need to mnt_want_write() since we call dentry_open() later */+	err = vfs_create(dir, dentry, S_IRUGO | S_IWUGO, NULL);+	if (unlikely(err)) {+		file = ERR_PTR(err);+		AuErr("%.*s create err %d\n", AuLNPair(name), err);+		goto out_dput;+	}++	file = dentry_open(dget(dentry), mntget(base_file->f_vfsmnt),+			   O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE,+			   current_cred());+	if (IS_ERR(file)) {+		AuErr("%.*s open err %ld\n", AuLNPair(name), PTR_ERR(file));+		goto out_dput;+	}++	err = vfsub_unlink(dir, &file->f_path, /*force*/0);+	if (unlikely(err)) {+		AuErr("%.*s unlink err %d\n", AuLNPair(name), err);+		goto out_fput;+	}++	if (copy_src) {+		/* no one can touch copy_src xino */+		err = au_copy_file(file, copy_src,+				   i_size_read(copy_src->f_dentry->d_inode));+		if (unlikely(err)) {+			AuErr("%.*s copy err %d\n", AuLNPair(name), err);+			goto out_fput;+		}+	}+	goto out_dput; /* success */++ out_fput:+	fput(file);+	file = ERR_PTR(err);+ out_dput:+	dput(dentry);+ out:+	return file;+}++struct au_xino_lock_dir {+	struct au_hinode *hdir;+	struct dentry *parent;+	struct mutex *mtx;+};++static void au_xino_lock_dir(struct super_block *sb, struct file *xino,+			     struct au_xino_lock_dir *ldir)+{+	aufs_bindex_t brid, bindex;++	ldir->hdir = NULL;+	bindex = -1;+	brid = au_xino_brid(sb);+	if (brid >= 0)+		bindex = au_br_index(sb, brid);+	if (bindex >= 0) {+		ldir->hdir = au_hi(sb->s_root->d_inode, bindex);+		au_hin_imtx_lock_nested(ldir->hdir, AuLsc_I_PARENT);+	} else {+		ldir->parent = dget_parent(xino->f_dentry);+		ldir->mtx = &ldir->parent->d_inode->i_mutex;+		mutex_lock_nested(ldir->mtx, AuLsc_I_PARENT);+	}+}++static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir)+{+	if (ldir->hdir)+		au_hin_imtx_unlock(ldir->hdir);+	else {+		mutex_unlock(ldir->mtx);+		dput(ldir->parent);+	}+}++/* ---------------------------------------------------------------------- */++/* trucate xino files asynchronously */++int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex)+{+	int err;+	aufs_bindex_t bi, bend;+	struct au_branch *br;+	struct file *new_xino, *file;+	struct super_block *h_sb;+	struct au_xino_lock_dir ldir;++	err = -EINVAL;+	bend = au_sbend(sb);+	if (unlikely(bindex < 0 || bend < bindex))+		goto out;+	br = au_sbr(sb, bindex);+	file = br->br_xino.xi_file;+	if (!file)+		goto out;++	au_xino_lock_dir(sb, file, &ldir);+	/* mnt_want_write() is unnecessary here */+	new_xino = au_xino_create2(file, file);+	au_xino_unlock_dir(&ldir);+	err = PTR_ERR(new_xino);+	if (IS_ERR(new_xino))+		goto out;+	err = 0;+	fput(file);+	br->br_xino.xi_file = new_xino;++	h_sb = br->br_mnt->mnt_sb;+	for (bi = 0; bi <= bend; bi++) {+		if (unlikely(bi == bindex))+			continue;+		br = au_sbr(sb, bi);+		if (br->br_mnt->mnt_sb != h_sb)+			continue;++		fput(br->br_xino.xi_file);+		br->br_xino.xi_file = new_xino;+		get_file(new_xino);+	}++ out:+	return err;+}++struct xino_do_trunc_args {+	struct super_block *sb;+	struct au_branch *br;+};++static void xino_do_trunc(void *_args)+{+	struct xino_do_trunc_args *args = _args;+	struct super_block *sb;+	struct au_branch *br;+	struct inode *dir;+	int err;+	aufs_bindex_t bindex;++	err = 0;+	sb = args->sb;+	dir = sb->s_root->d_inode;+	br = args->br;++	si_noflush_write_lock(sb);+	ii_read_lock_parent(dir);+	bindex = au_br_index(sb, br->br_id);+	err = au_xino_trunc(sb, bindex);+	if (!err+	    && br->br_xino.xi_file->f_dentry->d_inode->i_blocks+	    >= br->br_xino_upper)+		br->br_xino_upper += AUFS_XINO_TRUNC_STEP;++	ii_read_unlock(dir);+	if (unlikely(err))+		AuWarn("err b%d, (%d)\n", bindex, err);+	atomic_dec(&br->br_xino_running);+	atomic_dec(&br->br_count);+	au_nwt_done(&au_sbi(sb)->si_nowait);+	si_write_unlock(sb);+	kfree(args);+}++static void xino_try_trunc(struct super_block *sb, struct au_branch *br)+{+	struct xino_do_trunc_args *args;+	int wkq_err;++	if (br->br_xino.xi_file->f_dentry->d_inode->i_blocks+	    < br->br_xino_upper)+		return;++	if (atomic_inc_return(&br->br_xino_running) > 1)+		goto out;++	/* lock and kfree() will be called in trunc_xino() */+	args = kmalloc(sizeof(*args), GFP_NOFS);+	if (unlikely(!args)) {+		AuErr1("no memory\n");+		goto out_args;+	}++	atomic_inc_return(&br->br_count);+	args->sb = sb;+	args->br = br;+	wkq_err = au_wkq_nowait(xino_do_trunc, args, sb);+	if (!wkq_err)+		return; /* success */++	AuErr("wkq %d\n", wkq_err);+	atomic_dec_return(&br->br_count);++ out_args:+	kfree(args);+ out:+	atomic_dec_return(&br->br_xino_running);+}++/* ---------------------------------------------------------------------- */++static int au_xino_do_write(au_writef_t write, struct file *file,+			    ino_t h_ino, ino_t ino)+{+	loff_t pos;+	ssize_t sz;++	pos = h_ino;+	if (unlikely(au_loff_max / sizeof(ino) - 1 < pos)) {+		AuIOErr1("too large hi%lu\n", (unsigned long)h_ino);+		return -EFBIG;+	}+	pos *= sizeof(ino);+	sz = xino_fwrite(write, file, &ino, sizeof(ino), &pos);+	if (sz == sizeof(ino))+		return 0; /* success */++	AuIOErr("write failed (%zd)\n", sz);+	return -EIO;+}++/*+ * write @ino to the xinofile for the specified branch{@sb, @bindex}+ * at the position of @h_ino.+ * even if @ino is zero, it is written to the xinofile and means no entry.+ * if the size of the xino file on a specific filesystem exceeds the watermark,+ * try truncating it.+ */+int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,+		  ino_t ino)+{+	int err;+	unsigned int mnt_flags;+	struct au_branch *br;++	BUILD_BUG_ON(sizeof(long long) != sizeof(au_loff_max)+		     || ((loff_t)-1) > 0);+	SiMustAnyLock(sb);++	mnt_flags = au_mntflags(sb);+	if (!au_opt_test(mnt_flags, XINO))+		return 0;++	br = au_sbr(sb, bindex);+	err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file,+			       h_ino, ino);+	if (!err) {+		if (au_opt_test(mnt_flags, TRUNC_XINO)+		    && au_test_fs_trunc_xino(br->br_mnt->mnt_sb))+			xino_try_trunc(sb, br);+		return 0; /* success */+	}++	AuIOErr("write failed (%d)\n", err);+	return -EIO;+}++/* ---------------------------------------------------------------------- */++/* aufs inode number bitmap */++static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE;+static ino_t xib_calc_ino(unsigned long pindex, int bit)+{+	ino_t ino;++	AuDebugOn(bit < 0 || page_bits <= bit);+	ino = AUFS_FIRST_INO + pindex * page_bits + bit;+	return ino;+}++static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit)+{+	AuDebugOn(ino < AUFS_FIRST_INO);+	ino -= AUFS_FIRST_INO;+	*pindex = ino / page_bits;+	*bit = ino % page_bits;+}++static int xib_pindex(struct super_block *sb, unsigned long pindex)+{+	int err;+	loff_t pos;+	ssize_t sz;+	struct au_sbinfo *sbinfo;+	struct file *xib;+	unsigned long *p;++	sbinfo = au_sbi(sb);+	MtxMustLock(&sbinfo->si_xib_mtx);+	AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE+		  || !au_opt_test(sbinfo->si_mntflags, XINO));++	if (pindex == sbinfo->si_xib_last_pindex)+		return 0;++	xib = sbinfo->si_xib;+	p = sbinfo->si_xib_buf;+	pos = sbinfo->si_xib_last_pindex;+	pos *= PAGE_SIZE;+	sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos);+	if (unlikely(sz != PAGE_SIZE))+		goto out;++	pos = pindex;+	pos *= PAGE_SIZE;+	if (i_size_read(xib->f_dentry->d_inode) >= pos + PAGE_SIZE)+		sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos);+	else {+		memset(p, 0, PAGE_SIZE);+		sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos);+	}+	if (sz == PAGE_SIZE) {+		sbinfo->si_xib_last_pindex = pindex;+		return 0; /* success */+	}++ out:+	AuIOErr1("write failed (%zd)\n", sz);+	err = sz;+	if (sz >= 0)+		err = -EIO;+	return err;+}++/* ---------------------------------------------------------------------- */++int au_xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,+		   ino_t ino)+{+	int err, bit;+	unsigned long pindex;+	struct au_sbinfo *sbinfo;++	if (!au_opt_test(au_mntflags(sb), XINO))+		return 0;++	err = 0;+	if (ino) {+		sbinfo = au_sbi(sb);+		xib_calc_bit(ino, &pindex, &bit);+		AuDebugOn(page_bits <= bit);+		mutex_lock(&sbinfo->si_xib_mtx);+		err = xib_pindex(sb, pindex);+		if (!err) {+			clear_bit(bit, sbinfo->si_xib_buf);+			sbinfo->si_xib_next_bit = bit;+		}+		mutex_unlock(&sbinfo->si_xib_mtx);+	}++	if (!err)+		err = au_xino_write(sb, bindex, h_ino, 0);+	return err;+}++/* get an unused inode number from bitmap */+ino_t au_xino_new_ino(struct super_block *sb)+{+	ino_t ino;+	unsigned long *p, pindex, ul, pend;+	struct au_sbinfo *sbinfo;+	struct file *file;+	int free_bit, err;++	if (!au_opt_test(au_mntflags(sb), XINO))+		return iunique(sb, AUFS_FIRST_INO);++	sbinfo = au_sbi(sb);+	mutex_lock(&sbinfo->si_xib_mtx);+	p = sbinfo->si_xib_buf;+	free_bit = sbinfo->si_xib_next_bit;+	if (free_bit < page_bits && !test_bit(free_bit, p))+		goto out; /* success */+	free_bit = find_first_zero_bit(p, page_bits);+	if (free_bit < page_bits)+		goto out; /* success */++	pindex = sbinfo->si_xib_last_pindex;+	for (ul = pindex - 1; ul < ULONG_MAX; ul--) {+		err = xib_pindex(sb, ul);+		if (unlikely(err))+			goto out_err;+		free_bit = find_first_zero_bit(p, page_bits);+		if (free_bit < page_bits)+			goto out; /* success */+	}++	file = sbinfo->si_xib;+	pend = i_size_read(file->f_dentry->d_inode) / PAGE_SIZE;+	for (ul = pindex + 1; ul <= pend; ul++) {+		err = xib_pindex(sb, ul);+		if (unlikely(err))+			goto out_err;+		free_bit = find_first_zero_bit(p, page_bits);+		if (free_bit < page_bits)+			goto out; /* success */+	}+	BUG();++ out:+	set_bit(free_bit, p);+	sbinfo->si_xib_next_bit++;+	pindex = sbinfo->si_xib_last_pindex;+	mutex_unlock(&sbinfo->si_xib_mtx);+	ino = xib_calc_ino(pindex, free_bit);+	AuDbg("i%lu\n", (unsigned long)ino);+	return ino;+ out_err:+	mutex_unlock(&sbinfo->si_xib_mtx);+	AuDbg("i0\n");+	return 0;+}++/*+ * read @ino from xinofile for the specified branch{@sb, @bindex}+ * at the position of @h_ino.+ * if @ino does not exist and @do_new is true, get new one.+ */+int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,+		 ino_t *ino)+{+	int err;+	ssize_t sz;+	loff_t pos;+	struct file *file;+	struct au_sbinfo *sbinfo;++	*ino = 0;+	if (!au_opt_test(au_mntflags(sb), XINO))+		return 0; /* no xino */++	err = 0;+	sbinfo = au_sbi(sb);+	pos = h_ino;+	if (unlikely(au_loff_max / sizeof(*ino) - 1 < pos)) {+		AuIOErr1("too large hi%lu\n", (unsigned long)h_ino);+		return -EFBIG;+	}+	pos *= sizeof(*ino);++	file = au_sbr(sb, bindex)->br_xino.xi_file;+	if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(*ino))+		return 0; /* no ino */++	sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &pos);+	if (sz == sizeof(*ino))+		return 0; /* success */++	err = sz;+	if (unlikely(sz >= 0)) {+		err = -EIO;+		AuIOErr("xino read error (%zd)\n", sz);+	}++	return err;+}++/* ---------------------------------------------------------------------- */++/* create and set a new xino file */++struct file *au_xino_create(struct super_block *sb, char *fname, int silent)+{+	struct file *file;+	struct dentry *h_parent, *d;+	struct inode *h_dir;+	int err;++	/*+	 * at mount-time, and the xino file is the default path,+	 * hinotify is disabled so we have no inotify events to ignore.+	 * when a user specified the xino, we cannot get au_hdir to be ignored.+	 */+	file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE,+			       S_IRUGO | S_IWUGO);+	if (IS_ERR(file)) {+		if (!silent)+			AuErr("open %s(%ld)\n", fname, PTR_ERR(file));+		return file;+	}++	/* keep file count */+	h_parent = dget_parent(file->f_dentry);+	h_dir = h_parent->d_inode;+	mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);+	/* mnt_want_write() is unnecessary here */+	err = vfsub_unlink(h_dir, &file->f_path, /*force*/0);+	mutex_unlock(&h_dir->i_mutex);+	dput(h_parent);+	if (unlikely(err)) {+		if (!silent)+			AuErr("unlink %s(%d)\n", fname, err);+		goto out;+	}++	err = -EINVAL;+	d = file->f_dentry;+	if (unlikely(sb == d->d_sb)) {+		if (!silent)+			AuErr("%s must be outside\n", fname);+		goto out;+	}+	if (unlikely(au_test_fs_bad_xino(d->d_sb))) {+		if (!silent)+			AuErr("xino doesn't support %s(%s)\n",+			      fname, au_sbtype(d->d_sb));+		goto out;+	}+	return file; /* success */++ out:+	fput(file);+	file = ERR_PTR(err);+	return file;+}++/*+ * find another branch who is on the same filesystem of the specified+ * branch{@btgt}. search until @bend.+ */+static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt,+			aufs_bindex_t bend)+{+	aufs_bindex_t bindex;+	struct super_block *tgt_sb = au_sbr_sb(sb, btgt);++	for (bindex = 0; bindex < btgt; bindex++)+		if (unlikely(tgt_sb == au_sbr_sb(sb, bindex)))+			return bindex;+	for (bindex++; bindex <= bend; bindex++)+		if (unlikely(tgt_sb == au_sbr_sb(sb, bindex)))+			return bindex;+	return -1;+}++/* ---------------------------------------------------------------------- */++/*+ * initialize the xinofile for the specified branch @br+ * at the place/path where @base_file indicates.+ * test whether another branch is on the same filesystem or not,+ * if @do_test is true.+ */+int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino,+	       struct file *base_file, int do_test)+{+	int err;+	ino_t ino;+	aufs_bindex_t bend, bindex;+	struct au_branch *shared_br, *b;+	struct file *file;+	struct super_block *tgt_sb;++	shared_br = NULL;+	bend = au_sbend(sb);+	if (do_test) {+		tgt_sb = br->br_mnt->mnt_sb;+		for (bindex = 0; bindex <= bend; bindex++) {+			b = au_sbr(sb, bindex);+			if (tgt_sb == b->br_mnt->mnt_sb) {+				shared_br = b;+				break;+			}+		}+	}++	if (!shared_br || !shared_br->br_xino.xi_file) {+		struct au_xino_lock_dir ldir;++		au_xino_lock_dir(sb, base_file, &ldir);+		/* mnt_want_write() is unnecessary here */+		file = au_xino_create2(base_file, NULL);+		au_xino_unlock_dir(&ldir);+		err = PTR_ERR(file);+		if (IS_ERR(file))+			goto out;+		br->br_xino.xi_file = file;+	} else {+		br->br_xino.xi_file = shared_br->br_xino.xi_file;+		get_file(br->br_xino.xi_file);+	}++	ino = AUFS_ROOT_INO;+	err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file,+			       h_ino, ino);+	if (!err)+		return 0; /* success */+++ out:+	return err;+}++/* ---------------------------------------------------------------------- */++/* trucate a xino bitmap file */++/* todo: slow */+static int do_xib_restore(struct super_block *sb, struct file *file, void *page)+{+	int err, bit;+	ssize_t sz;+	unsigned long pindex;+	loff_t pos, pend;+	struct au_sbinfo *sbinfo;+	au_readf_t func;+	ino_t *ino;+	unsigned long *p;++	err = 0;+	sbinfo = au_sbi(sb);+	MtxMustLock(&sbinfo->si_xib_mtx);+	p = sbinfo->si_xib_buf;+	func = sbinfo->si_xread;+	pend = i_size_read(file->f_dentry->d_inode);+	pos = 0;+	while (pos < pend) {+		sz = xino_fread(func, file, page, PAGE_SIZE, &pos);+		err = sz;+		if (unlikely(sz <= 0))+			goto out;++		err = 0;+		for (ino = page; sz > 0; ino++, sz -= sizeof(ino)) {+			if (unlikely(*ino < AUFS_FIRST_INO))+				continue;++			xib_calc_bit(*ino, &pindex, &bit);+			AuDebugOn(page_bits <= bit);+			err = xib_pindex(sb, pindex);+			if (!err)+				set_bit(bit, p);+			else+				goto out;+		}+	}++ out:+	return err;+}++static int xib_restore(struct super_block *sb)+{+	int err;+	aufs_bindex_t bindex, bend;+	void *page;++	err = -ENOMEM;+	page = (void *)__get_free_page(GFP_NOFS);+	if (unlikely(!page))+		goto out;++	err = 0;+	bend = au_sbend(sb);+	for (bindex = 0; !err && bindex <= bend; bindex++)+		if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0)+			err = do_xib_restore+				(sb, au_sbr(sb, bindex)->br_xino.xi_file, page);+		else+			AuDbg("b%d\n", bindex);+	free_page((unsigned long)page);++ out:+	return err;+}++int au_xib_trunc(struct super_block *sb)+{+	int err;+	ssize_t sz;+	loff_t pos;+	struct au_xino_lock_dir ldir;+	struct au_sbinfo *sbinfo;+	unsigned long *p;+	struct file *file;++	SiMustWriteLock(sb);++	err = 0;+	sbinfo = au_sbi(sb);+	if (!au_opt_test(sbinfo->si_mntflags, XINO))+		goto out;++	file = sbinfo->si_xib;+	if (i_size_read(file->f_dentry->d_inode) <= PAGE_SIZE)+		goto out;++	au_xino_lock_dir(sb, file, &ldir);+	/* mnt_want_write() is unnecessary here */+	file = au_xino_create2(sbinfo->si_xib, NULL);+	au_xino_unlock_dir(&ldir);+	err = PTR_ERR(file);+	if (IS_ERR(file))+		goto out;+	fput(sbinfo->si_xib);+	sbinfo->si_xib = file;++	p = sbinfo->si_xib_buf;+	memset(p, 0, PAGE_SIZE);+	pos = 0;+	sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xib, p, PAGE_SIZE, &pos);+	if (unlikely(sz != PAGE_SIZE)) {+		err = sz;+		AuIOErr("err %d\n", err);+		if (sz >= 0)+			err = -EIO;+		goto out;+	}++	mutex_lock(&sbinfo->si_xib_mtx);+	/* mnt_want_write() is unnecessary here */+	err = xib_restore(sb);+	mutex_unlock(&sbinfo->si_xib_mtx);++out:+	return err;+}++/* ---------------------------------------------------------------------- */++/*+ * xino mount option handlers+ */+static au_readf_t find_readf(struct file *h_file)+{+	const struct file_operations *fop = h_file->f_op;++	if (fop) {+		if (fop->read)+			return fop->read;+		if (fop->aio_read)+			return do_sync_read;+	}+	return ERR_PTR(-ENOSYS);+}++static au_writef_t find_writef(struct file *h_file)+{+	const struct file_operations *fop = h_file->f_op;++	if (fop) {+		if (fop->write)+			return fop->write;+		if (fop->aio_write)+			return do_sync_write;+	}+	return ERR_PTR(-ENOSYS);+}++/* xino bitmap */+static void xino_clear_xib(struct super_block *sb)+{+	struct au_sbinfo *sbinfo;++	SiMustWriteLock(sb);++	sbinfo = au_sbi(sb);+	sbinfo->si_xread = NULL;+	sbinfo->si_xwrite = NULL;+	if (sbinfo->si_xib)+		fput(sbinfo->si_xib);+	sbinfo->si_xib = NULL;+	free_page((unsigned long)sbinfo->si_xib_buf);+	sbinfo->si_xib_buf = NULL;+}++static int au_xino_set_xib(struct super_block *sb, struct file *base)+{+	int err;+	loff_t pos;+	struct au_sbinfo *sbinfo;+	struct file *file;++	SiMustWriteLock(sb);++	sbinfo = au_sbi(sb);+	file = au_xino_create2(base, sbinfo->si_xib);+	err = PTR_ERR(file);+	if (IS_ERR(file))+		goto out;+	if (sbinfo->si_xib)+		fput(sbinfo->si_xib);+	sbinfo->si_xib = file;+	sbinfo->si_xread = find_readf(file);+	sbinfo->si_xwrite = find_writef(file);++	err = -ENOMEM;+	if (!sbinfo->si_xib_buf)+		sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS);+	if (unlikely(!sbinfo->si_xib_buf))+		goto out_unset;++	sbinfo->si_xib_last_pindex = 0;+	sbinfo->si_xib_next_bit = 0;+	if (i_size_read(file->f_dentry->d_inode) < PAGE_SIZE) {+		pos = 0;+		err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf,+				  PAGE_SIZE, &pos);+		if (unlikely(err != PAGE_SIZE))+			goto out_free;+	}+	err = 0;+	goto out; /* success */++ out_free:+	free_page((unsigned long)sbinfo->si_xib_buf);+	sbinfo->si_xib_buf = NULL;+	if (err >= 0)+		err = -EIO;+ out_unset:+	fput(sbinfo->si_xib);+	sbinfo->si_xib = NULL;+	sbinfo->si_xread = NULL;+	sbinfo->si_xwrite = NULL;+ out:+	return err;+}++/* xino for each branch */+static void xino_clear_br(struct super_block *sb)+{+	aufs_bindex_t bindex, bend;+	struct au_branch *br;++	bend = au_sbend(sb);+	for (bindex = 0; bindex <= bend; bindex++) {+		br = au_sbr(sb, bindex);+		if (!br || !br->br_xino.xi_file)+			continue;++		fput(br->br_xino.xi_file);+		br->br_xino.xi_file = NULL;+	}+}++static int au_xino_set_br(struct super_block *sb, struct file *base)+{+	int err;+	ino_t ino;+	aufs_bindex_t bindex, bend, bshared;+	struct {+		struct file *old, *new;+	} *fpair, *p;+	struct au_branch *br;+	struct inode *inode;+	au_writef_t writef;++	SiMustWriteLock(sb);++	err = -ENOMEM;+	bend = au_sbend(sb);+	fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_NOFS);+	if (unlikely(!fpair))+		goto out;++	inode = sb->s_root->d_inode;+	ino = AUFS_ROOT_INO;+	writef = au_sbi(sb)->si_xwrite;+	for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) {+		br = au_sbr(sb, bindex);+		bshared = is_sb_shared(sb, bindex, bindex - 1);+		if (bshared >= 0) {+			/* shared xino */+			*p = fpair[bshared];+			get_file(p->new);+		}++		if (!p->new) {+			/* new xino */+			p->old = br->br_xino.xi_file;+			p->new = au_xino_create2(base, br->br_xino.xi_file);+			err = PTR_ERR(p->new);+			if (IS_ERR(p->new)) {+				p->new = NULL;+				goto out_pair;+			}+		}++		err = au_xino_do_write(writef, p->new,+				       au_h_iptr(inode, bindex)->i_ino, ino);+		if (unlikely(err))+			goto out_pair;+	}++	for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) {+		br = au_sbr(sb, bindex);+		if (br->br_xino.xi_file)+			fput(br->br_xino.xi_file);+		get_file(p->new);+		br->br_xino.xi_file = p->new;+	}++ out_pair:+	for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++)+		if (p->new)+			fput(p->new);+		else+			break;+	kfree(fpair);+ out:+	return err;+}++void au_xino_clr(struct super_block *sb)+{+	struct au_sbinfo *sbinfo;++	au_xigen_clr(sb);+	xino_clear_xib(sb);+	xino_clear_br(sb);+	sbinfo = au_sbi(sb);+	/* lvalue, do not call au_mntflags() */+	au_opt_clr(sbinfo->si_mntflags, XINO);+}++int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount)+{+	int err, skip;+	struct dentry *parent, *cur_parent;+	struct qstr *dname, *cur_name;+	struct file *cur_xino;+	struct inode *dir;+	struct au_sbinfo *sbinfo;++	SiMustWriteLock(sb);++	err = 0;+	sbinfo = au_sbi(sb);+	parent = dget_parent(xino->file->f_dentry);+	if (remount) {+		skip = 0;+		dname = &xino->file->f_dentry->d_name;+		cur_xino = sbinfo->si_xib;+		if (cur_xino) {+			cur_parent = dget_parent(cur_xino->f_dentry);+			cur_name = &cur_xino->f_dentry->d_name;+			skip = (cur_parent == parent+				&& dname->len == cur_name->len+				&& !memcmp(dname->name, cur_name->name,+					   dname->len));+			dput(cur_parent);+		}+		if (skip)+			goto out;+	}++	au_opt_set(sbinfo->si_mntflags, XINO);+	dir = parent->d_inode;+	mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT);+	/* mnt_want_write() is unnecessary here */+	err = au_xino_set_xib(sb, xino->file);+	if (!err)+		err = au_xigen_set(sb, xino->file);+	if (!err)+		err = au_xino_set_br(sb, xino->file);+	mutex_unlock(&dir->i_mutex);+	if (!err)+		goto out; /* success */++	/* reset all */+	AuIOErr("failed creating xino(%d).\n", err);++ out:+	dput(parent);+	return err;+}++/* ---------------------------------------------------------------------- */++/*+ * create a xinofile at the default place/path.+ */+struct file *au_xino_def(struct super_block *sb)+{+	struct file *file;+	char *page, *p;+	struct au_branch *br;+	struct super_block *h_sb;+	struct path path;+	aufs_bindex_t bend, bindex, bwr;++	br = NULL;+	bend = au_sbend(sb);+	bwr = -1;+	for (bindex = 0; bindex <= bend; bindex++) {+		br = au_sbr(sb, bindex);+		if (au_br_writable(br->br_perm)+		    && !au_test_fs_bad_xino(br->br_mnt->mnt_sb)) {+			bwr = bindex;+			break;+		}+	}++	if (bwr >= 0) {+		file = ERR_PTR(-ENOMEM);+		page = __getname();+		if (unlikely(!page))+			goto out;+		path.mnt = br->br_mnt;+		path.dentry = au_h_dptr(sb->s_root, bwr);+		p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME));+		file = (void *)p;+		if (!IS_ERR(p)) {+			strcat(p, "/" AUFS_XINO_FNAME);+			AuDbg("%s\n", p);+			file = au_xino_create(sb, p, /*silent*/0);+			if (!IS_ERR(file))+				au_xino_brid_set(sb, br->br_id);+		}+		__putname(page);+	} else {+		file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0);+		if (IS_ERR(file))+			goto out;+		h_sb = file->f_dentry->d_sb;+		if (unlikely(au_test_fs_bad_xino(h_sb))) {+			AuErr("xino doesn't support %s(%s)\n",+			      AUFS_XINO_DEFPATH, au_sbtype(h_sb));+			fput(file);+			file = ERR_PTR(-EINVAL);+		}+		if (!IS_ERR(file))+			au_xino_brid_set(sb, -1);+	}++ out:+	return file;+}++/* ---------------------------------------------------------------------- */++int au_xino_path(struct seq_file *seq, struct file *file)+{+	int err;++	err = au_seq_path(seq, &file->f_path);+	if (unlikely(err < 0))+		goto out;++	err = 0;+#define Deleted "\\040(deleted)"+	seq->count -= sizeof(Deleted) - 1;+	AuDebugOn(memcmp(seq->buf + seq->count, Deleted,+			 sizeof(Deleted) - 1));+#undef Deleted++ out:+	return err;+}diff -Nur linux-2.6.31.5.orig/fs/Kconfig linux-2.6.31.5/fs/Kconfig--- linux-2.6.31.5.orig/fs/Kconfig	2009-10-23 00:57:56.000000000 +0200+++ linux-2.6.31.5/fs/Kconfig	2009-11-15 22:02:37.000000000 +0100@@ -187,6 +187,7 @@ source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" source "fs/nilfs2/Kconfig"+source "fs/aufs/Kconfig"  endif # MISC_FILESYSTEMS diff -Nur linux-2.6.31.5.orig/fs/Makefile linux-2.6.31.5/fs/Makefile--- linux-2.6.31.5.orig/fs/Makefile	2009-10-23 00:57:56.000000000 +0200+++ linux-2.6.31.5/fs/Makefile	2009-11-15 22:02:37.000000000 +0100@@ -85,6 +85,7 @@ obj-$(CONFIG_HFS_FS)		+= hfs/ obj-$(CONFIG_ECRYPT_FS)		+= ecryptfs/ obj-$(CONFIG_VXFS_FS)		+= freevxfs/+obj-$(CONFIG_AUFS_FS)           += aufs/ obj-$(CONFIG_NFS_FS)		+= nfs/ obj-$(CONFIG_EXPORTFS)		+= exportfs/ obj-$(CONFIG_NFSD)		+= nfsd/diff -Nur linux-2.6.31.5.orig/fs/namei.c linux-2.6.31.5/fs/namei.c--- linux-2.6.31.5.orig/fs/namei.c	2009-10-23 00:57:56.000000000 +0200+++ linux-2.6.31.5/fs/namei.c	2009-11-15 22:02:37.000000000 +0100@@ -337,6 +337,7 @@  	return 0; }+EXPORT_SYMBOL(deny_write_access);  /**  * path_get - get a reference to a path@@ -1219,7 +1220,7 @@  * needs parent already locked. Doesn't follow mounts.  * SMP-safe.  */-static struct dentry *lookup_hash(struct nameidata *nd)+struct dentry *lookup_hash(struct nameidata *nd) { 	int err; @@ -1228,8 +1229,9 @@ 		return ERR_PTR(err); 	return __lookup_hash(&nd->last, nd->path.dentry, nd); }+EXPORT_SYMBOL(lookup_hash); -static int __lookup_one_len(const char *name, struct qstr *this,+int __lookup_one_len(const char *name, struct qstr *this, 		struct dentry *base, int len) { 	unsigned long hash;@@ -1250,6 +1252,7 @@ 	this->hash = end_name_hash(hash); 	return 0; }+EXPORT_SYMBOL(__lookup_one_len);  /**  * lookup_one_len - filesystem helper to lookup single pathname componentdiff -Nur linux-2.6.31.5.orig/fs/namespace.c linux-2.6.31.5/fs/namespace.c--- linux-2.6.31.5.orig/fs/namespace.c	2009-10-23 00:57:56.000000000 +0200+++ linux-2.6.31.5/fs/namespace.c	2009-11-15 22:02:37.000000000 +0100@@ -39,6 +39,7 @@  /* spinlock for vfsmount related operations, inplace of dcache_lock */ __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);+EXPORT_SYMBOL(vfsmount_lock);  static int event; static DEFINE_IDA(mnt_id_ida);diff -Nur linux-2.6.31.5.orig/fs/open.c linux-2.6.31.5/fs/open.c--- linux-2.6.31.5.orig/fs/open.c	2009-10-23 00:57:56.000000000 +0200+++ linux-2.6.31.5/fs/open.c	2009-11-15 22:02:37.000000000 +0100@@ -221,6 +221,7 @@ 	mutex_unlock(&dentry->d_inode->i_mutex); 	return err; }+EXPORT_SYMBOL(do_truncate);  static long do_sys_truncate(const char __user *pathname, loff_t length) {diff -Nur linux-2.6.31.5.orig/fs/splice.c linux-2.6.31.5/fs/splice.c--- linux-2.6.31.5.orig/fs/splice.c	2009-10-23 00:57:56.000000000 +0200+++ linux-2.6.31.5/fs/splice.c	2009-11-15 22:02:37.000000000 +0100@@ -1057,8 +1057,8 @@ /*  * Attempt to initiate a splice from pipe to file.  */-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,-			   loff_t *ppos, size_t len, unsigned int flags)+long do_splice_from(struct pipe_inode_info *pipe, struct file *out,+		    loff_t *ppos, size_t len, unsigned int flags) { 	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, 				loff_t *, size_t, unsigned int);@@ -1080,13 +1080,14 @@  	return splice_write(pipe, out, ppos, len, flags); }+EXPORT_SYMBOL(do_splice_from);  /*  * Attempt to initiate a splice from a file to a pipe.  */-static long do_splice_to(struct file *in, loff_t *ppos,-			 struct pipe_inode_info *pipe, size_t len,-			 unsigned int flags)+long do_splice_to(struct file *in, loff_t *ppos,+		  struct pipe_inode_info *pipe, size_t len,+		  unsigned int flags) { 	ssize_t (*splice_read)(struct file *, loff_t *, 			       struct pipe_inode_info *, size_t, unsigned int);@@ -1105,6 +1106,7 @@  	return splice_read(in, ppos, pipe, len, flags); }+EXPORT_SYMBOL(do_splice_to);  /**  * splice_direct_to_actor - splices data directly between two non-pipesdiff -Nur linux-2.6.31.5.orig/include/linux/aufs_type.h linux-2.6.31.5/include/linux/aufs_type.h--- linux-2.6.31.5.orig/include/linux/aufs_type.h	1970-01-01 01:00:00.000000000 +0100+++ linux-2.6.31.5/include/linux/aufs_type.h	2009-11-15 22:02:37.000000000 +0100@@ -0,0 +1,109 @@+/*+ * Copyright (C) 2005-2009 Junjiro R. Okajima+ *+ * This program, aufs is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+ */++#ifndef __AUFS_TYPE_H__+#define __AUFS_TYPE_H__++#include <linux/ioctl.h>++#define AUFS_VERSION	"2-standalone.tree-30-20090803"++/* todo? move this to linux-2.6.19/include/magic.h */+#define AUFS_SUPER_MAGIC	('a' << 24 | 'u' << 16 | 'f' << 8 | 's')++/* ---------------------------------------------------------------------- */++#ifdef CONFIG_AUFS_BRANCH_MAX_127+/* some environments treat 'char' as 'unsigned char' by default */+typedef signed char aufs_bindex_t;+#define AUFS_BRANCH_MAX 127+#else+typedef short aufs_bindex_t;+#ifdef CONFIG_AUFS_BRANCH_MAX_511+#define AUFS_BRANCH_MAX 511+#elif defined(CONFIG_AUFS_BRANCH_MAX_1023)+#define AUFS_BRANCH_MAX 1023+#elif defined(CONFIG_AUFS_BRANCH_MAX_32767)+#define AUFS_BRANCH_MAX 32767+#endif+#endif++#ifdef __KERNEL__+#ifndef AUFS_BRANCH_MAX+#error unknown CONFIG_AUFS_BRANCH_MAX value+#endif+#endif /* __KERNEL__ */++/* ---------------------------------------------------------------------- */++#define AUFS_NAME		"aufs"+#define AUFS_FSTYPE		AUFS_NAME++#define AUFS_ROOT_INO		2+#define AUFS_FIRST_INO		11++#define AUFS_WH_PFX		".wh."+#define AUFS_WH_PFX_LEN		((int)sizeof(AUFS_WH_PFX) - 1)+#define AUFS_XINO_FNAME		"." AUFS_NAME ".xino"+#define AUFS_XINO_DEFPATH	"/tmp/" AUFS_XINO_FNAME+#define AUFS_XINO_TRUNC_INIT	64 /* blocks */+#define AUFS_XINO_TRUNC_STEP	4  /* blocks */+#define AUFS_DIRWH_DEF		3+#define AUFS_RDCACHE_DEF	10 /* seconds */+#define AUFS_RDBLK_DEF		512 /* bytes */+#define AUFS_RDHASH_DEF		32+#define AUFS_WKQ_NAME		AUFS_NAME "d"+#define AUFS_NWKQ_DEF		4+#define AUFS_MFS_SECOND_DEF	30 /* seconds */+#define AUFS_PLINK_WARN		100 /* number of plinks */++#define AUFS_DIROPQ_NAME	AUFS_WH_PFX ".opq" /* whiteouted doubly */+#define AUFS_WH_DIROPQ		AUFS_WH_PFX AUFS_DIROPQ_NAME++#define AUFS_BASE_NAME		AUFS_WH_PFX AUFS_NAME+#define AUFS_PLINKDIR_NAME	AUFS_WH_PFX "plnk"+#define AUFS_ORPHDIR_NAME	AUFS_WH_PFX "orph"++/* doubly whiteouted */+#define AUFS_WH_BASE		AUFS_WH_PFX AUFS_BASE_NAME+#define AUFS_WH_PLINKDIR	AUFS_WH_PFX AUFS_PLINKDIR_NAME+#define AUFS_WH_ORPHDIR		AUFS_WH_PFX AUFS_ORPHDIR_NAME++/* branch permission */+#define AUFS_BRPERM_RW		"rw"+#define AUFS_BRPERM_RO		"ro"+#define AUFS_BRPERM_RR		"rr"+#define AUFS_BRPERM_WH		"wh"+#define AUFS_BRPERM_NLWH	"nolwh"+#define AUFS_BRPERM_ROWH	AUFS_BRPERM_RO "+" AUFS_BRPERM_WH+#define AUFS_BRPERM_RRWH	AUFS_BRPERM_RR "+" AUFS_BRPERM_WH+#define AUFS_BRPERM_RWNLWH	AUFS_BRPERM_RW "+" AUFS_BRPERM_NLWH++/* ---------------------------------------------------------------------- */++/* ioctl */+enum {+	AuCtl_PLINK_MAINT,+	AuCtl_PLINK_CLEAN+};++#define AuCtlType		'A'+#define AUFS_CTL_PLINK_MAINT	_IO(AuCtlType, AuCtl_PLINK_MAINT)+#define AUFS_CTL_PLINK_CLEAN	_IO(AuCtlType, AuCtl_PLINK_CLEAN)++#endif /* __AUFS_TYPE_H__ */diff -Nur linux-2.6.31.5.orig/include/linux/Kbuild linux-2.6.31.5/include/linux/Kbuild--- linux-2.6.31.5.orig/include/linux/Kbuild	2009-10-23 00:57:56.000000000 +0200+++ linux-2.6.31.5/include/linux/Kbuild	2009-11-15 22:02:38.000000000 +0100@@ -34,6 +34,7 @@ header-y += atmsap.h header-y += atmsvc.h header-y += atm_zatm.h+header-y += aufs_type.h header-y += auto_fs4.h header-y += ax25.h header-y += b1lli.hdiff -Nur linux-2.6.31.5.orig/include/linux/namei.h linux-2.6.31.5/include/linux/namei.h--- linux-2.6.31.5.orig/include/linux/namei.h	2009-10-23 00:57:56.000000000 +0200+++ linux-2.6.31.5/include/linux/namei.h	2009-11-15 22:02:38.000000000 +0100@@ -75,6 +75,9 @@ extern struct file *nameidata_to_filp(struct nameidata *nd, int flags); extern void release_open_intent(struct nameidata *); +extern struct dentry *lookup_hash(struct nameidata *nd);+extern int __lookup_one_len(const char *name, struct qstr *this,+			    struct dentry *base, int len); extern struct dentry *lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_noperm(const char *, struct dentry *); diff -Nur linux-2.6.31.5.orig/include/linux/splice.h linux-2.6.31.5/include/linux/splice.h--- linux-2.6.31.5.orig/include/linux/splice.h	2009-10-23 00:57:56.000000000 +0200+++ linux-2.6.31.5/include/linux/splice.h	2009-11-15 22:02:38.000000000 +0100@@ -82,4 +82,10 @@ extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *, 				      splice_direct_actor *); +extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out,+			   loff_t *ppos, size_t len, unsigned int flags);+extern long do_splice_to(struct file *in, loff_t *ppos,+			 struct pipe_inode_info *pipe, size_t len,+			 unsigned int flags);+ #endifdiff -Nur linux-2.6.31.5.orig/security/device_cgroup.c linux-2.6.31.5/security/device_cgroup.c--- linux-2.6.31.5.orig/security/device_cgroup.c	2009-10-23 00:57:56.000000000 +0200+++ linux-2.6.31.5/security/device_cgroup.c	2009-11-15 22:02:38.000000000 +0100@@ -513,6 +513,7 @@  	return -EPERM; }+EXPORT_SYMBOL(devcgroup_inode_permission);  int devcgroup_inode_mknod(int mode, dev_t dev) {diff -Nur linux-2.6.31.5.orig/security/security.c linux-2.6.31.5/security/security.c--- linux-2.6.31.5.orig/security/security.c	2009-10-23 00:57:56.000000000 +0200+++ linux-2.6.31.5/security/security.c	2009-11-15 22:02:38.000000000 +0100@@ -386,6 +386,7 @@ 		return 0; 	return security_ops->path_mkdir(path, dentry, mode); }+EXPORT_SYMBOL(security_path_mkdir);  int security_path_rmdir(struct path *path, struct dentry *dentry) {@@ -393,6 +394,7 @@ 		return 0; 	return security_ops->path_rmdir(path, dentry); }+EXPORT_SYMBOL(security_path_rmdir);  int security_path_unlink(struct path *path, struct dentry *dentry) {@@ -400,6 +402,7 @@ 		return 0; 	return security_ops->path_unlink(path, dentry); }+EXPORT_SYMBOL(security_path_unlink);  int security_path_symlink(struct path *path, struct dentry *dentry, 			  const char *old_name)@@ -408,6 +411,7 @@ 		return 0; 	return security_ops->path_symlink(path, dentry, old_name); }+EXPORT_SYMBOL(security_path_symlink);  int security_path_link(struct dentry *old_dentry, struct path *new_dir, 		       struct dentry *new_dentry)@@ -416,6 +420,7 @@ 		return 0; 	return security_ops->path_link(old_dentry, new_dir, new_dentry); }+EXPORT_SYMBOL(security_path_link);  int security_path_rename(struct path *old_dir, struct dentry *old_dentry, 			 struct path *new_dir, struct dentry *new_dentry)@@ -426,6 +431,7 @@ 	return security_ops->path_rename(old_dir, old_dentry, new_dir, 					 new_dentry); }+EXPORT_SYMBOL(security_path_rename);  int security_path_truncate(struct path *path, loff_t length, 			   unsigned int time_attrs)@@ -434,6 +440,7 @@ 		return 0; 	return security_ops->path_truncate(path, length, time_attrs); }+EXPORT_SYMBOL(security_path_truncate); #endif  int security_inode_create(struct inode *dir, struct dentry *dentry, int mode)@@ -505,6 +512,7 @@ 		return 0; 	return security_ops->inode_readlink(dentry); }+EXPORT_SYMBOL(security_inode_readlink);  int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd) {@@ -519,6 +527,7 @@ 		return 0; 	return security_ops->inode_permission(inode, mask); }+EXPORT_SYMBOL(security_inode_permission);  int security_inode_setattr(struct dentry *dentry, struct iattr *attr) {@@ -619,6 +628,7 @@ { 	return security_ops->file_permission(file, mask); }+EXPORT_SYMBOL(security_file_permission);  int security_file_alloc(struct file *file) {
 |