| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550 | diff -Nur linux-3.4.90.orig/fs/Kconfig linux-3.4.90/fs/Kconfig--- linux-3.4.90.orig/fs/Kconfig	2014-05-13 14:11:45.000000000 +0200+++ linux-3.4.90/fs/Kconfig	2014-05-17 15:08:09.000000000 +0200@@ -203,6 +203,7 @@ source "fs/befs/Kconfig" source "fs/bfs/Kconfig" source "fs/efs/Kconfig"+source "fs/yaffs2/Kconfig" source "fs/jffs2/Kconfig" # UBIFS File system configuration source "fs/ubifs/Kconfig"diff -Nur linux-3.4.90.orig/fs/Makefile linux-3.4.90/fs/Makefile--- linux-3.4.90.orig/fs/Makefile	2014-05-13 14:11:45.000000000 +0200+++ linux-3.4.90/fs/Makefile	2014-05-17 15:09:05.000000000 +0200@@ -97,6 +97,7 @@ obj-$(CONFIG_UFS_FS)		+= ufs/ obj-$(CONFIG_EFS_FS)		+= efs/ obj-$(CONFIG_JFFS2_FS)		+= jffs2/+obj-$(CONFIG_YAFFS_FS)		+= yaffs2/ obj-$(CONFIG_LOGFS)		+= logfs/ obj-$(CONFIG_UBIFS_FS)		+= ubifs/ obj-$(CONFIG_AFFS_FS)		+= affs/diff -Nur linux-3.4.90.orig/fs/yaffs2/Kconfig linux-3.4.90/fs/yaffs2/Kconfig--- linux-3.4.90.orig/fs/yaffs2/Kconfig	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/Kconfig	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,171 @@+#+# yaffs file system configurations+#++config YAFFS_FS+	tristate "yaffs2 file system support"+	default n+	depends on MTD_BLOCK+	select YAFFS_YAFFS1+	select YAFFS_YAFFS2+	help+	  yaffs2, or Yet Another Flash File System, is a file system+	  optimised for NAND Flash chips.++	  To compile the yaffs2 file system support as a module, choose M+	  here: the module will be called yaffs2.++	  If unsure, say N.++	  Further information on yaffs2 is available at+	  <http://www.aleph1.co.uk/yaffs/>.++config YAFFS_YAFFS1+	bool "512 byte / page devices"+	depends on YAFFS_FS+	default y+	help+	  Enable yaffs1 support -- yaffs for 512 byte / page devices++	  Not needed for 2K-page devices.++	  If unsure, say Y.++config YAFFS_9BYTE_TAGS+	bool "Use older-style on-NAND data format with pageStatus byte"+	depends on YAFFS_YAFFS1+	default n+	help++	  Older-style on-NAND data format has a "pageStatus" byte to record+	  chunk/page state.  This byte is zero when the page is discarded.+	  Choose this option if you have existing on-NAND data using this+	  format that you need to continue to support.  New data written+	  also uses the older-style format.  Note: Use of this option+	  generally requires that MTD's oob layout be adjusted to use the+	  older-style format.  See notes on tags formats and MTD versions+	  in yaffs_mtdif1.c.++	  If unsure, say N.++config YAFFS_DOES_ECC+	bool "Lets yaffs do its own ECC"+	depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS+	default n+	help+	  This enables yaffs to use its own ECC functions instead of using+	  the ones from the generic MTD-NAND driver.++	  If unsure, say N.++config YAFFS_ECC_WRONG_ORDER+	bool "Use the same ecc byte order as Steven Hill's nand_ecc.c"+	depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS+	default n+	help+	  This makes yaffs_ecc.c use the same ecc byte order as Steven+	  Hill's nand_ecc.c. If not set, then you get the same ecc byte+	  order as SmartMedia.++	  If unsure, say N.++config YAFFS_YAFFS2+	bool "2048 byte (or larger) / page devices"+	depends on YAFFS_FS+	default y+	help+	  Enable yaffs2 support -- yaffs for >= 2K bytes per page devices++	  If unsure, say Y.++config YAFFS_AUTO_YAFFS2+	bool "Autoselect yaffs2 format"+	depends on YAFFS_YAFFS2+	default y+	help+	  Without this, you need to explicitely use yaffs2 as the file+	  system type. With this, you can say "yaffs" and yaffs or yaffs2+	  will be used depending on the device page size (yaffs on+	  512-byte page devices, yaffs2 on 2K page devices).++	  If unsure, say Y.++config YAFFS_DISABLE_TAGS_ECC+	bool "Disable yaffs from doing ECC on tags by default"+	depends on YAFFS_FS && YAFFS_YAFFS2+	default n+	help+	  This defaults yaffs to using its own ECC calculations on tags instead of+	  just relying on the MTD.+	  This behavior can also be overridden with tags_ecc_on and+	  tags_ecc_off mount options.++	  If unsure, say N.++config YAFFS_ALWAYS_CHECK_CHUNK_ERASED+	bool "Force chunk erase check"+	depends on YAFFS_FS+	default n+	help+          Normally yaffs only checks chunks before writing until an erased+	  chunk is found. This helps to detect any partially written+	  chunks that might have happened due to power loss.++	  Enabling this forces on the test that chunks are erased in flash+	  before writing to them. This takes more time but is potentially+	  a bit more secure.++	  Suggest setting Y during development and ironing out driver+	  issues etc. Suggest setting to N if you want faster writing.++	  If unsure, say Y.++config YAFFS_EMPTY_LOST_AND_FOUND+	bool "Empty lost and found on boot"+	depends on YAFFS_FS+	default n+	help+	  If this is enabled then the contents of lost and found is+	  automatically dumped at mount.++	  If unsure, say N.++config YAFFS_DISABLE_BLOCK_REFRESHING+	bool "Disable yaffs2 block refreshing"+	depends on YAFFS_FS+	default n+	help+	 If this is set, then block refreshing is disabled.+	 Block refreshing infrequently refreshes the oldest block in+	 a yaffs2 file system. This mechanism helps to refresh flash to+	 mitigate against data loss. This is particularly useful for MLC.++	  If unsure, say N.++config YAFFS_DISABLE_BACKGROUND+	bool "Disable yaffs2 background processing"+	depends on YAFFS_FS+	default n+	help+	 If this is set, then background processing is disabled.+	 Background processing makes many foreground activities faster.++	 If unsure, say N.++config YAFFS_DISABLE_BAD_BLOCK_MARKING+	bool "Disable yaffs2 bad block marking"+	depends on YAFFS_FS+	default n+	help+	 Useful during early flash bring up to prevent problems causing+	 lots of bad block marking.++	 If unsure, say N.++config YAFFS_XATTR+	bool "Enable yaffs2 xattr support"+	depends on YAFFS_FS+	default y+	help+	 If this is set then yaffs2 will provide xattr support.+	 If unsure, say Y.diff -Nur linux-3.4.90.orig/fs/yaffs2/Makefile linux-3.4.90/fs/yaffs2/Makefile--- linux-3.4.90.orig/fs/yaffs2/Makefile	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/Makefile	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,18 @@+#+# Makefile for the linux YAFFS filesystem routines.+#++obj-$(CONFIG_YAFFS_FS) += yaffs.o++yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o+yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o+yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o+yaffs-y += yaffs_mtdif.o+yaffs-y += yaffs_nameval.o yaffs_attribs.o+yaffs-y += yaffs_allocator.o+yaffs-y += yaffs_yaffs1.o+yaffs-y += yaffs_yaffs2.o+yaffs-y += yaffs_bitmap.o+yaffs-y += yaffs_summary.o+yaffs-y += yaffs_verify.o+diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_allocator.c linux-3.4.90/fs/yaffs2/yaffs_allocator.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_allocator.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_allocator.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,357 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yaffs_allocator.h"+#include "yaffs_guts.h"+#include "yaffs_trace.h"+#include "yportenv.h"++/*+ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks+ * of approx 100 objects that are themn allocated singly.+ * This is basically a simplified slab allocator.+ *+ * We don't use the Linux slab allocator because slab does not allow+ * us to dump all the objects in one hit when we do a umount and tear+ * down  all the tnodes and objects. slab requires that we first free+ * the individual objects.+ *+ * Once yaffs has been mainlined I shall try to motivate for a change+ * to slab to provide the extra features we need here.+ */++struct yaffs_tnode_list {+	struct yaffs_tnode_list *next;+	struct yaffs_tnode *tnodes;+};++struct yaffs_obj_list {+	struct yaffs_obj_list *next;+	struct yaffs_obj *objects;+};++struct yaffs_allocator {+	int n_tnodes_created;+	struct yaffs_tnode *free_tnodes;+	int n_free_tnodes;+	struct yaffs_tnode_list *alloc_tnode_list;++	int n_obj_created;+	struct list_head free_objs;+	int n_free_objects;++	struct yaffs_obj_list *allocated_obj_list;+};++static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev)+{+	struct yaffs_allocator *allocator =+	    (struct yaffs_allocator *)dev->allocator;+	struct yaffs_tnode_list *tmp;++	if (!allocator) {+		BUG();+		return;+	}++	while (allocator->alloc_tnode_list) {+		tmp = allocator->alloc_tnode_list->next;++		kfree(allocator->alloc_tnode_list->tnodes);+		kfree(allocator->alloc_tnode_list);+		allocator->alloc_tnode_list = tmp;+	}++	allocator->free_tnodes = NULL;+	allocator->n_free_tnodes = 0;+	allocator->n_tnodes_created = 0;+}++static void yaffs_init_raw_tnodes(struct yaffs_dev *dev)+{+	struct yaffs_allocator *allocator = dev->allocator;++	if (!allocator) {+		BUG();+		return;+	}++	allocator->alloc_tnode_list = NULL;+	allocator->free_tnodes = NULL;+	allocator->n_free_tnodes = 0;+	allocator->n_tnodes_created = 0;+}++static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes)+{+	struct yaffs_allocator *allocator =+	    (struct yaffs_allocator *)dev->allocator;+	int i;+	struct yaffs_tnode *new_tnodes;+	u8 *mem;+	struct yaffs_tnode *curr;+	struct yaffs_tnode *next;+	struct yaffs_tnode_list *tnl;++	if (!allocator) {+		BUG();+		return YAFFS_FAIL;+	}++	if (n_tnodes < 1)+		return YAFFS_OK;++	/* make these things */+	new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS);+	mem = (u8 *) new_tnodes;++	if (!new_tnodes) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"yaffs: Could not allocate Tnodes");+		return YAFFS_FAIL;+	}++	/* New hookup for wide tnodes */+	for (i = 0; i < n_tnodes - 1; i++) {+		curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size];+		next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size];+		curr->internal[0] = next;+	}++	curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size];+	curr->internal[0] = allocator->free_tnodes;+	allocator->free_tnodes = (struct yaffs_tnode *)mem;++	allocator->n_free_tnodes += n_tnodes;+	allocator->n_tnodes_created += n_tnodes;++	/* Now add this bunch of tnodes to a list for freeing up.+	 * NB If we can't add this to the management list it isn't fatal+	 * but it just means we can't free this bunch of tnodes later.+	 */+	tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS);+	if (!tnl) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"Could not add tnodes to management list");+		return YAFFS_FAIL;+	} else {+		tnl->tnodes = new_tnodes;+		tnl->next = allocator->alloc_tnode_list;+		allocator->alloc_tnode_list = tnl;+	}++	yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added");++	return YAFFS_OK;+}++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev)+{+	struct yaffs_allocator *allocator =+	    (struct yaffs_allocator *)dev->allocator;+	struct yaffs_tnode *tn = NULL;++	if (!allocator) {+		BUG();+		return NULL;+	}++	/* If there are none left make more */+	if (!allocator->free_tnodes)+		yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES);++	if (allocator->free_tnodes) {+		tn = allocator->free_tnodes;+		allocator->free_tnodes = allocator->free_tnodes->internal[0];+		allocator->n_free_tnodes--;+	}++	return tn;+}++/* FreeTnode frees up a tnode and puts it back on the free list */+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn)+{+	struct yaffs_allocator *allocator = dev->allocator;++	if (!allocator) {+		BUG();+		return;+	}++	if (tn) {+		tn->internal[0] = allocator->free_tnodes;+		allocator->free_tnodes = tn;+		allocator->n_free_tnodes++;+	}+	dev->checkpoint_blocks_required = 0;	/* force recalculation */+}++/*--------------- yaffs_obj alloaction ------------------------+ *+ * Free yaffs_objs are stored in a list using obj->siblings.+ * The blocks of allocated objects are stored in a linked list.+ */++static void yaffs_init_raw_objs(struct yaffs_dev *dev)+{+	struct yaffs_allocator *allocator = dev->allocator;++	if (!allocator) {+		BUG();+		return;+	}++	allocator->allocated_obj_list = NULL;+	INIT_LIST_HEAD(&allocator->free_objs);+	allocator->n_free_objects = 0;+}++static void yaffs_deinit_raw_objs(struct yaffs_dev *dev)+{+	struct yaffs_allocator *allocator = dev->allocator;+	struct yaffs_obj_list *tmp;++	if (!allocator) {+		BUG();+		return;+	}++	while (allocator->allocated_obj_list) {+		tmp = allocator->allocated_obj_list->next;+		kfree(allocator->allocated_obj_list->objects);+		kfree(allocator->allocated_obj_list);+		allocator->allocated_obj_list = tmp;+	}++	INIT_LIST_HEAD(&allocator->free_objs);+	allocator->n_free_objects = 0;+	allocator->n_obj_created = 0;+}++static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj)+{+	struct yaffs_allocator *allocator = dev->allocator;+	int i;+	struct yaffs_obj *new_objs;+	struct yaffs_obj_list *list;++	if (!allocator) {+		BUG();+		return YAFFS_FAIL;+	}++	if (n_obj < 1)+		return YAFFS_OK;++	/* make these things */+	new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS);+	list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS);++	if (!new_objs || !list) {+		kfree(new_objs);+		new_objs = NULL;+		kfree(list);+		list = NULL;+		yaffs_trace(YAFFS_TRACE_ALLOCATE,+			"Could not allocate more objects");+		return YAFFS_FAIL;+	}++	/* Hook them into the free list */+	for (i = 0; i < n_obj; i++)+		list_add(&new_objs[i].siblings, &allocator->free_objs);++	allocator->n_free_objects += n_obj;+	allocator->n_obj_created += n_obj;++	/* Now add this bunch of Objects to a list for freeing up. */++	list->objects = new_objs;+	list->next = allocator->allocated_obj_list;+	allocator->allocated_obj_list = list;++	return YAFFS_OK;+}++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev)+{+	struct yaffs_obj *obj = NULL;+	struct list_head *lh;+	struct yaffs_allocator *allocator = dev->allocator;++	if (!allocator) {+		BUG();+		return obj;+	}++	/* If there are none left make more */+	if (list_empty(&allocator->free_objs))+		yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS);++	if (!list_empty(&allocator->free_objs)) {+		lh = allocator->free_objs.next;+		obj = list_entry(lh, struct yaffs_obj, siblings);+		list_del_init(lh);+		allocator->n_free_objects--;+	}++	return obj;+}++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj)+{++	struct yaffs_allocator *allocator = dev->allocator;++	if (!allocator) {+		BUG();+		return;+	}++	/* Link into the free list. */+	list_add(&obj->siblings, &allocator->free_objs);+	allocator->n_free_objects++;+}++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev)+{++	if (!dev->allocator) {+		BUG();+		return;+	}++	yaffs_deinit_raw_tnodes(dev);+	yaffs_deinit_raw_objs(dev);+	kfree(dev->allocator);+	dev->allocator = NULL;+}++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev)+{+	struct yaffs_allocator *allocator;++	if (dev->allocator) {+		BUG();+		return;+	}++	allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS);+	if (allocator) {+		dev->allocator = allocator;+		yaffs_init_raw_tnodes(dev);+		yaffs_init_raw_objs(dev);+	}+}+diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_allocator.h linux-3.4.90/fs/yaffs2/yaffs_allocator.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_allocator.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_allocator.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,30 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_ALLOCATOR_H__+#define __YAFFS_ALLOCATOR_H__++#include "yaffs_guts.h"++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev);+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev);++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev);+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn);++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev);+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj);++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_attribs.c linux-3.4.90/fs/yaffs2/yaffs_attribs.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_attribs.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_attribs.c	2014-05-17 16:37:59.000000000 +0200@@ -0,0 +1,166 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yaffs_guts.h"+#include "yaffs_attribs.h"++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))+static inline uid_t ia_uid_read(const struct iattr *iattr)+{+	return from_kuid(&init_user_ns, iattr->ia_uid);+}++static inline gid_t ia_gid_read(const struct iattr *iattr)+{+	return from_kgid(&init_user_ns, iattr->ia_gid);+}++static inline void ia_uid_write(struct iattr *iattr, uid_t uid)+{+	iattr->ia_uid = make_kuid(&init_user_ns, uid);+}++static inline void ia_gid_write(struct iattr *iattr, gid_t gid)+{+	iattr->ia_gid = make_kgid(&init_user_ns, gid);+}+#else+static inline uid_t ia_uid_read(const struct iattr *iattr)+{+	return iattr->ia_uid;+}++static inline gid_t ia_gid_read(const struct iattr *iattr)+{+	return iattr->ia_gid;+}++static inline void ia_uid_write(struct iattr *iattr, uid_t uid)+{+	iattr->ia_uid = uid;+}++static inline void ia_gid_write(struct iattr *iattr, gid_t gid)+{+	iattr->ia_gid = gid;+}+#endif++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh)+{+	obj->yst_uid = oh->yst_uid;+	obj->yst_gid = oh->yst_gid;+	obj->yst_atime = oh->yst_atime;+	obj->yst_mtime = oh->yst_mtime;+	obj->yst_ctime = oh->yst_ctime;+	obj->yst_rdev = oh->yst_rdev;+}++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj)+{+	oh->yst_uid = obj->yst_uid;+	oh->yst_gid = obj->yst_gid;+	oh->yst_atime = obj->yst_atime;+	oh->yst_mtime = obj->yst_mtime;+	oh->yst_ctime = obj->yst_ctime;+	oh->yst_rdev = obj->yst_rdev;++}++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c)+{+	obj->yst_mtime = Y_CURRENT_TIME;+	if (do_a)+		obj->yst_atime = obj->yst_mtime;+	if (do_c)+		obj->yst_ctime = obj->yst_mtime;+}++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev)+{+	yaffs_load_current_time(obj, 1, 1);+	obj->yst_rdev = rdev;+	obj->yst_uid = uid;+	obj->yst_gid = gid;+}++static loff_t yaffs_get_file_size(struct yaffs_obj *obj)+{+	YCHAR *alias = NULL;+	obj = yaffs_get_equivalent_obj(obj);++	switch (obj->variant_type) {+	case YAFFS_OBJECT_TYPE_FILE:+		return obj->variant.file_variant.file_size;+	case YAFFS_OBJECT_TYPE_SYMLINK:+		alias = obj->variant.symlink_variant.alias;+		if (!alias)+			return 0;+		return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH);+	default:+		return 0;+	}+}++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr)+{+	unsigned int valid = attr->ia_valid;++	if (valid & ATTR_MODE)+		obj->yst_mode = attr->ia_mode;+	if (valid & ATTR_UID)+		obj->yst_uid =  ia_uid_read(attr);+	if (valid & ATTR_GID)+		obj->yst_gid =  ia_gid_read(attr);++	if (valid & ATTR_ATIME)+		obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime);+	if (valid & ATTR_CTIME)+		obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime);+	if (valid & ATTR_MTIME)+		obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime);++	if (valid & ATTR_SIZE)+		yaffs_resize_file(obj, attr->ia_size);++	yaffs_update_oh(obj, NULL, 1, 0, 0, NULL);++	return YAFFS_OK;++}++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr)+{+	unsigned int valid = 0;++	attr->ia_mode = obj->yst_mode;+	valid |= ATTR_MODE;+	ia_uid_write(attr, obj->yst_uid);+	valid |= ATTR_UID;+	ia_gid_write(attr, obj->yst_gid);+	valid |= ATTR_GID;++	Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime;+	valid |= ATTR_ATIME;+	Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime;+	valid |= ATTR_CTIME;+	Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime;+	valid |= ATTR_MTIME;++	attr->ia_size = yaffs_get_file_size(obj);+	valid |= ATTR_SIZE;++	attr->ia_valid = valid;++	return YAFFS_OK;+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_attribs.h linux-3.4.90/fs/yaffs2/yaffs_attribs.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_attribs.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_attribs.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,28 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_ATTRIBS_H__+#define __YAFFS_ATTRIBS_H__++#include "yaffs_guts.h"++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh);+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj);+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev);+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c);+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr);+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr);++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_bitmap.c linux-3.4.90/fs/yaffs2/yaffs_bitmap.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_bitmap.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_bitmap.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,97 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yaffs_bitmap.h"+#include "yaffs_trace.h"+/*+ * Chunk bitmap manipulations+ */++static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk)+{+	if (blk < dev->internal_start_block || blk > dev->internal_end_block) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"BlockBits block %d is not valid",+			blk);+		BUG();+	}+	return dev->chunk_bits ++	    (dev->chunk_bit_stride * (blk - dev->internal_start_block));+}++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk)+{+	if (blk < dev->internal_start_block || blk > dev->internal_end_block ||+	    chunk < 0 || chunk >= dev->param.chunks_per_block) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"Chunk Id (%d:%d) invalid",+			blk, chunk);+		BUG();+	}+}++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk)+{+	u8 *blk_bits = yaffs_block_bits(dev, blk);++	memset(blk_bits, 0, dev->chunk_bit_stride);+}++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)+{+	u8 *blk_bits = yaffs_block_bits(dev, blk);++	yaffs_verify_chunk_bit_id(dev, blk, chunk);+	blk_bits[chunk / 8] &= ~(1 << (chunk & 7));+}++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)+{+	u8 *blk_bits = yaffs_block_bits(dev, blk);++	yaffs_verify_chunk_bit_id(dev, blk, chunk);+	blk_bits[chunk / 8] |= (1 << (chunk & 7));+}++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)+{+	u8 *blk_bits = yaffs_block_bits(dev, blk);++	yaffs_verify_chunk_bit_id(dev, blk, chunk);+	return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0;+}++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk)+{+	u8 *blk_bits = yaffs_block_bits(dev, blk);+	int i;++	for (i = 0; i < dev->chunk_bit_stride; i++) {+		if (*blk_bits)+			return 1;+		blk_bits++;+	}+	return 0;+}++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk)+{+	u8 *blk_bits = yaffs_block_bits(dev, blk);+	int i;+	int n = 0;++	for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++)+		n += hweight8(*blk_bits);++	return n;+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_bitmap.h linux-3.4.90/fs/yaffs2/yaffs_bitmap.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_bitmap.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_bitmap.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,33 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++/*+ * Chunk bitmap manipulations+ */++#ifndef __YAFFS_BITMAP_H__+#define __YAFFS_BITMAP_H__++#include "yaffs_guts.h"++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk);+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk);+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk);+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk);++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.4.90/fs/yaffs2/yaffs_checkptrw.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_checkptrw.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_checkptrw.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,474 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yaffs_checkptrw.h"+#include "yaffs_getblockinfo.h"++struct yaffs_checkpt_chunk_hdr {+	int version;+	int seq;+	u32 sum;+	u32 xor;+} ;+++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk)+{+	return chunk - dev->chunk_offset;+}++static int apply_block_offset(struct yaffs_dev *dev, int block)+{+	return block - dev->block_offset;+}++static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev)+{+	struct yaffs_checkpt_chunk_hdr hdr;++	hdr.version = YAFFS_CHECKPOINT_VERSION;+	hdr.seq = dev->checkpt_page_seq;+	hdr.sum = dev->checkpt_sum;+	hdr.xor = dev->checkpt_xor;++	dev->checkpt_byte_offs = sizeof(hdr);++	memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr));+}++static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev)+{+	struct yaffs_checkpt_chunk_hdr hdr;++	memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr));++	dev->checkpt_byte_offs = sizeof(hdr);++	return hdr.version == YAFFS_CHECKPOINT_VERSION &&+		hdr.seq == dev->checkpt_page_seq &&+		hdr.sum == dev->checkpt_sum &&+		hdr.xor == dev->checkpt_xor;+}++static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev)+{+	int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;++	yaffs_trace(YAFFS_TRACE_CHECKPOINT,+		"checkpt blocks_avail = %d", blocks_avail);++	return (blocks_avail <= 0) ? 0 : 1;+}++static int yaffs_checkpt_erase(struct yaffs_dev *dev)+{+	int i;++	if (!dev->drv.drv_erase_fn)+		return 0;+	yaffs_trace(YAFFS_TRACE_CHECKPOINT,+		"checking blocks %d to %d",+		dev->internal_start_block, dev->internal_end_block);++	for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {+		struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);+		int offset_i = apply_block_offset(dev, i);+		int result;++		if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {+			yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"erasing checkpt block %d", i);++			dev->n_erasures++;++			result = dev->drv.drv_erase_fn(dev, offset_i);+			if(result) {+				bi->block_state = YAFFS_BLOCK_STATE_EMPTY;+				dev->n_erased_blocks++;+				dev->n_free_chunks +=+				    dev->param.chunks_per_block;+			} else {+				dev->drv.drv_mark_bad_fn(dev, offset_i);+				bi->block_state = YAFFS_BLOCK_STATE_DEAD;+			}+		}+	}++	dev->blocks_in_checkpt = 0;++	return 1;+}++static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev)+{+	int i;+	int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;++	yaffs_trace(YAFFS_TRACE_CHECKPOINT,+		"allocating checkpt block: erased %d reserved %d avail %d next %d ",+		dev->n_erased_blocks, dev->param.n_reserved_blocks,+		blocks_avail, dev->checkpt_next_block);++	if (dev->checkpt_next_block >= 0 &&+	    dev->checkpt_next_block <= dev->internal_end_block &&+	    blocks_avail > 0) {++		for (i = dev->checkpt_next_block; i <= dev->internal_end_block;+		     i++) {+			struct yaffs_block_info *bi;++			bi = yaffs_get_block_info(dev, i);+			if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {+				dev->checkpt_next_block = i + 1;+				dev->checkpt_cur_block = i;+				yaffs_trace(YAFFS_TRACE_CHECKPOINT,+					"allocating checkpt block %d", i);+				return;+			}+		}+	}+	yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks");++	dev->checkpt_next_block = -1;+	dev->checkpt_cur_block = -1;+}++static void yaffs2_checkpt_find_block(struct yaffs_dev *dev)+{+	int i;+	struct yaffs_ext_tags tags;++	yaffs_trace(YAFFS_TRACE_CHECKPOINT,+		"find next checkpt block: start:  blocks %d next %d",+		dev->blocks_in_checkpt, dev->checkpt_next_block);++	if (dev->blocks_in_checkpt < dev->checkpt_max_blocks)+		for (i = dev->checkpt_next_block; i <= dev->internal_end_block;+		     i++) {+			int chunk = i * dev->param.chunks_per_block;+			enum yaffs_block_state state;+			u32 seq;++			dev->tagger.read_chunk_tags_fn(dev,+					apply_chunk_offset(dev, chunk),+					NULL, &tags);+			yaffs_trace(YAFFS_TRACE_CHECKPOINT,+				"find next checkpt block: search: block %d state %d oid %d seq %d eccr %d",+				i, (int) state,+				tags.obj_id, tags.seq_number,+				tags.ecc_result);++			if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA)+				continue;++			dev->tagger.query_block_fn(dev,+						apply_block_offset(dev, i),+						&state, &seq);+			if (state == YAFFS_BLOCK_STATE_DEAD)+				continue;++			/* Right kind of block */+			dev->checkpt_next_block = tags.obj_id;+			dev->checkpt_cur_block = i;+			dev->checkpt_block_list[dev->blocks_in_checkpt] = i;+			dev->blocks_in_checkpt++;+			yaffs_trace(YAFFS_TRACE_CHECKPOINT,+				"found checkpt block %d", i);+			return;+		}++	yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks");++	dev->checkpt_next_block = -1;+	dev->checkpt_cur_block = -1;+}++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing)+{+	int i;++	dev->checkpt_open_write = writing;++	/* Got the functions we need? */+	if (!dev->tagger.write_chunk_tags_fn ||+	    !dev->tagger.read_chunk_tags_fn ||+	    !dev->drv.drv_erase_fn ||+	    !dev->drv.drv_mark_bad_fn)+		return 0;++	if (writing && !yaffs2_checkpt_space_ok(dev))+		return 0;++	if (!dev->checkpt_buffer)+		dev->checkpt_buffer =+		    kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);+	if (!dev->checkpt_buffer)+		return 0;++	dev->checkpt_page_seq = 0;+	dev->checkpt_byte_count = 0;+	dev->checkpt_sum = 0;+	dev->checkpt_xor = 0;+	dev->checkpt_cur_block = -1;+	dev->checkpt_cur_chunk = -1;+	dev->checkpt_next_block = dev->internal_start_block;++	if (writing) {+		memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);+		yaffs2_checkpt_init_chunk_hdr(dev);+		return yaffs_checkpt_erase(dev);+	}++	/* Opening for a read */+	/* Set to a value that will kick off a read */+	dev->checkpt_byte_offs = dev->data_bytes_per_chunk;+	/* A checkpoint block list of 1 checkpoint block per 16 block is+	 * (hopefully) going to be way more than we need */+	dev->blocks_in_checkpt = 0;+	dev->checkpt_max_blocks =+	    (dev->internal_end_block - dev->internal_start_block) / 16 + 2;+	dev->checkpt_block_list =+	    kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS);++	if (!dev->checkpt_block_list)+		return 0;++	for (i = 0; i < dev->checkpt_max_blocks; i++)+		dev->checkpt_block_list[i] = -1;++	return 1;+}++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum)+{+	u32 composite_sum;++	composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff);+	*sum = composite_sum;+	return 1;+}++static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev)+{+	int chunk;+	int offset_chunk;+	struct yaffs_ext_tags tags;++	if (dev->checkpt_cur_block < 0) {+		yaffs2_checkpt_find_erased_block(dev);+		dev->checkpt_cur_chunk = 0;+	}++	if (dev->checkpt_cur_block < 0)+		return 0;++	tags.is_deleted = 0;+	tags.obj_id = dev->checkpt_next_block;	/* Hint to next place to look */+	tags.chunk_id = dev->checkpt_page_seq + 1;+	tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA;+	tags.n_bytes = dev->data_bytes_per_chunk;+	if (dev->checkpt_cur_chunk == 0) {+		/* First chunk we write for the block? Set block state to+		   checkpoint */+		struct yaffs_block_info *bi =+		    yaffs_get_block_info(dev, dev->checkpt_cur_block);+		bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;+		dev->blocks_in_checkpt++;+	}++	chunk =+	    dev->checkpt_cur_block * dev->param.chunks_per_block ++	    dev->checkpt_cur_chunk;++	yaffs_trace(YAFFS_TRACE_CHECKPOINT,+		"checkpoint wite buffer nand %d(%d:%d) objid %d chId %d",+		chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk,+		tags.obj_id, tags.chunk_id);++	offset_chunk = apply_chunk_offset(dev, chunk);++	dev->n_page_writes++;++	dev->tagger.write_chunk_tags_fn(dev, offset_chunk,+				       dev->checkpt_buffer, &tags);+	dev->checkpt_page_seq++;+	dev->checkpt_cur_chunk++;+	if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) {+		dev->checkpt_cur_chunk = 0;+		dev->checkpt_cur_block = -1;+	}+	memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);++	yaffs2_checkpt_init_chunk_hdr(dev);+++	return 1;+}++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes)+{+	int i = 0;+	int ok = 1;+	u8 *data_bytes = (u8 *) data;++	if (!dev->checkpt_buffer)+		return 0;++	if (!dev->checkpt_open_write)+		return -1;++	while (i < n_bytes && ok) {+		dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes;+		dev->checkpt_sum += *data_bytes;+		dev->checkpt_xor ^= *data_bytes;++		dev->checkpt_byte_offs++;+		i++;+		data_bytes++;+		dev->checkpt_byte_count++;++		if (dev->checkpt_byte_offs < 0 ||+		    dev->checkpt_byte_offs >= dev->data_bytes_per_chunk)+			ok = yaffs2_checkpt_flush_buffer(dev);+	}++	return i;+}++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes)+{+	int i = 0;+	int ok = 1;+	struct yaffs_ext_tags tags;+	int chunk;+	int offset_chunk;+	u8 *data_bytes = (u8 *) data;++	if (!dev->checkpt_buffer)+		return 0;++	if (dev->checkpt_open_write)+		return -1;++	while (i < n_bytes && ok) {++		if (dev->checkpt_byte_offs < 0 ||+		    dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) {++			if (dev->checkpt_cur_block < 0) {+				yaffs2_checkpt_find_block(dev);+				dev->checkpt_cur_chunk = 0;+			}++			if (dev->checkpt_cur_block < 0) {+				ok = 0;+				break;+			}++			chunk = dev->checkpt_cur_block *+			    dev->param.chunks_per_block ++			    dev->checkpt_cur_chunk;++			offset_chunk = apply_chunk_offset(dev, chunk);+			dev->n_page_reads++;++			/* read in the next chunk */+			dev->tagger.read_chunk_tags_fn(dev,+						offset_chunk,+						dev->checkpt_buffer,+						&tags);++			if (tags.chunk_id != (dev->checkpt_page_seq + 1) ||+			    tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||+			    tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) {+				ok = 0;+				break;+			}+			if(!yaffs2_checkpt_check_chunk_hdr(dev)) {+				ok = 0;+				break;+			}++			dev->checkpt_page_seq++;+			dev->checkpt_cur_chunk++;++			if (dev->checkpt_cur_chunk >=+					dev->param.chunks_per_block)+				dev->checkpt_cur_block = -1;++		}++		*data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs];+		dev->checkpt_sum += *data_bytes;+		dev->checkpt_xor ^= *data_bytes;+		dev->checkpt_byte_offs++;+		i++;+		data_bytes++;+		dev->checkpt_byte_count++;+	}++	return i;+}++int yaffs_checkpt_close(struct yaffs_dev *dev)+{+	int i;++	if (dev->checkpt_open_write) {+		if (dev->checkpt_byte_offs !=+			sizeof(sizeof(struct yaffs_checkpt_chunk_hdr)))+			yaffs2_checkpt_flush_buffer(dev);+	} else if (dev->checkpt_block_list) {+		for (i = 0;+		     i < dev->blocks_in_checkpt &&+		     dev->checkpt_block_list[i] >= 0; i++) {+			int blk = dev->checkpt_block_list[i];+			struct yaffs_block_info *bi = NULL;++			if (dev->internal_start_block <= blk &&+			    blk <= dev->internal_end_block)+				bi = yaffs_get_block_info(dev, blk);+			if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY)+				bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;+		}+		kfree(dev->checkpt_block_list);+		dev->checkpt_block_list = NULL;+	}++	dev->n_free_chunks -=+		dev->blocks_in_checkpt * dev->param.chunks_per_block;+	dev->n_erased_blocks -= dev->blocks_in_checkpt;++	yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d",+		dev->checkpt_byte_count);++	if (dev->checkpt_buffer) {+		/* free the buffer */+		kfree(dev->checkpt_buffer);+		dev->checkpt_buffer = NULL;+		return 1;+	} else {+		return 0;+	}+}++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev)+{+	/* Erase the checkpoint data */++	yaffs_trace(YAFFS_TRACE_CHECKPOINT,+		"checkpoint invalidate of %d blocks",+		dev->blocks_in_checkpt);++	return yaffs_checkpt_erase(dev);+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.4.90/fs/yaffs2/yaffs_checkptrw.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_checkptrw.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_checkptrw.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,33 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_CHECKPTRW_H__+#define __YAFFS_CHECKPTRW_H__++#include "yaffs_guts.h"++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing);++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes);++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes);++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum);++int yaffs_checkpt_close(struct yaffs_dev *dev);++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev);++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_ecc.c linux-3.4.90/fs/yaffs2/yaffs_ecc.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_ecc.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_ecc.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,281 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++/*+ * This code implements the ECC algorithm used in SmartMedia.+ *+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.+ * The two unused bit are set to 1.+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two+ * such ECC blocks are used on a 512-byte NAND page.+ *+ */++#include "yportenv.h"++#include "yaffs_ecc.h"++/* Table generated by gen-ecc.c+ * Using a table means we do not have to calculate p1..p4 and p1'..p4'+ * for each byte of data. These are instead provided in a table in bits7..2.+ * Bit 0 of each entry indicates whether the entry has an odd or even parity,+ * and therefore this bytes influence on the line parity.+ */++static const unsigned char column_parity_table[] = {+	0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,+	0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,+	0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,+	0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,+	0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,+	0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,+	0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,+	0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,+	0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,+	0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,+	0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,+	0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,+	0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,+	0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,+	0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,+	0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,+	0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,+	0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,+	0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,+	0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,+	0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,+	0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,+	0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,+	0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,+	0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,+	0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,+	0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,+	0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,+	0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,+	0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,+	0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,+	0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,+};+++/* Calculate the ECC for a 256-byte block of data */+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc)+{+	unsigned int i;+	unsigned char col_parity = 0;+	unsigned char line_parity = 0;+	unsigned char line_parity_prime = 0;+	unsigned char t;+	unsigned char b;++	for (i = 0; i < 256; i++) {+		b = column_parity_table[*data++];+		col_parity ^= b;++		if (b & 0x01) {	/* odd number of bits in the byte */+			line_parity ^= i;+			line_parity_prime ^= ~i;+		}+	}++	ecc[2] = (~col_parity) | 0x03;++	t = 0;+	if (line_parity & 0x80)+		t |= 0x80;+	if (line_parity_prime & 0x80)+		t |= 0x40;+	if (line_parity & 0x40)+		t |= 0x20;+	if (line_parity_prime & 0x40)+		t |= 0x10;+	if (line_parity & 0x20)+		t |= 0x08;+	if (line_parity_prime & 0x20)+		t |= 0x04;+	if (line_parity & 0x10)+		t |= 0x02;+	if (line_parity_prime & 0x10)+		t |= 0x01;+	ecc[1] = ~t;++	t = 0;+	if (line_parity & 0x08)+		t |= 0x80;+	if (line_parity_prime & 0x08)+		t |= 0x40;+	if (line_parity & 0x04)+		t |= 0x20;+	if (line_parity_prime & 0x04)+		t |= 0x10;+	if (line_parity & 0x02)+		t |= 0x08;+	if (line_parity_prime & 0x02)+		t |= 0x04;+	if (line_parity & 0x01)+		t |= 0x02;+	if (line_parity_prime & 0x01)+		t |= 0x01;+	ecc[0] = ~t;++}++/* Correct the ECC on a 256 byte block of data */++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc,+		      const unsigned char *test_ecc)+{+	unsigned char d0, d1, d2;	/* deltas */++	d0 = read_ecc[0] ^ test_ecc[0];+	d1 = read_ecc[1] ^ test_ecc[1];+	d2 = read_ecc[2] ^ test_ecc[2];++	if ((d0 | d1 | d2) == 0)+		return 0;	/* no error */++	if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&+	    ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&+	    ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) {+		/* Single bit (recoverable) error in data */++		unsigned byte;+		unsigned bit;++		bit = byte = 0;++		if (d1 & 0x80)+			byte |= 0x80;+		if (d1 & 0x20)+			byte |= 0x40;+		if (d1 & 0x08)+			byte |= 0x20;+		if (d1 & 0x02)+			byte |= 0x10;+		if (d0 & 0x80)+			byte |= 0x08;+		if (d0 & 0x20)+			byte |= 0x04;+		if (d0 & 0x08)+			byte |= 0x02;+		if (d0 & 0x02)+			byte |= 0x01;++		if (d2 & 0x80)+			bit |= 0x04;+		if (d2 & 0x20)+			bit |= 0x02;+		if (d2 & 0x08)+			bit |= 0x01;++		data[byte] ^= (1 << bit);++		return 1;	/* Corrected the error */+	}++	if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) {+		/* Reccoverable error in ecc */++		read_ecc[0] = test_ecc[0];+		read_ecc[1] = test_ecc[1];+		read_ecc[2] = test_ecc[2];++		return 1;	/* Corrected the error */+	}++	/* Unrecoverable error */++	return -1;++}++/*+ * ECCxxxOther does ECC calcs on arbitrary n bytes of data+ */+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes,+			  struct yaffs_ecc_other *ecc_other)+{+	unsigned int i;+	unsigned char col_parity = 0;+	unsigned line_parity = 0;+	unsigned line_parity_prime = 0;+	unsigned char b;++	for (i = 0; i < n_bytes; i++) {+		b = column_parity_table[*data++];+		col_parity ^= b;++		if (b & 0x01) {+			/* odd number of bits in the byte */+			line_parity ^= i;+			line_parity_prime ^= ~i;+		}++	}++	ecc_other->col_parity = (col_parity >> 2) & 0x3f;+	ecc_other->line_parity = line_parity;+	ecc_other->line_parity_prime = line_parity_prime;+}++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes,+			    struct yaffs_ecc_other *read_ecc,+			    const struct yaffs_ecc_other *test_ecc)+{+	unsigned char delta_col;	/* column parity delta */+	unsigned delta_line;	/* line parity delta */+	unsigned delta_line_prime;	/* line parity delta */+	unsigned bit;++	delta_col = read_ecc->col_parity ^ test_ecc->col_parity;+	delta_line = read_ecc->line_parity ^ test_ecc->line_parity;+	delta_line_prime =+	    read_ecc->line_parity_prime ^ test_ecc->line_parity_prime;++	if ((delta_col | delta_line | delta_line_prime) == 0)+		return 0;	/* no error */++	if (delta_line == ~delta_line_prime &&+	    (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) {+		/* Single bit (recoverable) error in data */++		bit = 0;++		if (delta_col & 0x20)+			bit |= 0x04;+		if (delta_col & 0x08)+			bit |= 0x02;+		if (delta_col & 0x02)+			bit |= 0x01;++		if (delta_line >= n_bytes)+			return -1;++		data[delta_line] ^= (1 << bit);++		return 1;	/* corrected */+	}++	if ((hweight32(delta_line) ++	     hweight32(delta_line_prime) ++	     hweight8(delta_col)) == 1) {+		/* Reccoverable error in ecc */++		*read_ecc = *test_ecc;+		return 1;	/* corrected */+	}++	/* Unrecoverable error */++	return -1;+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_ecc.h linux-3.4.90/fs/yaffs2/yaffs_ecc.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_ecc.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_ecc.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,44 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++/*+ * This code implements the ECC algorithm used in SmartMedia.+ *+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.+ * The two unused bit are set to 1.+ * The ECC can correct single bit errors in a 256-byte page of data.+ * Thus, two such ECC blocks are used on a 512-byte NAND page.+ *+ */++#ifndef __YAFFS_ECC_H__+#define __YAFFS_ECC_H__++struct yaffs_ecc_other {+	unsigned char col_parity;+	unsigned line_parity;+	unsigned line_parity_prime;+};++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc);+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc,+		      const unsigned char *test_ecc);++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes,+			  struct yaffs_ecc_other *ecc);+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes,+			    struct yaffs_ecc_other *read_ecc,+			    const struct yaffs_ecc_other *test_ecc);+#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.4.90/fs/yaffs2/yaffs_getblockinfo.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_getblockinfo.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_getblockinfo.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,35 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_GETBLOCKINFO_H__+#define __YAFFS_GETBLOCKINFO_H__++#include "yaffs_guts.h"+#include "yaffs_trace.h"++/* Function to manipulate block info */+static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev+							      *dev, int blk)+{+	if (blk < dev->internal_start_block || blk > dev->internal_end_block) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"**>> yaffs: get_block_info block %d is not valid",+			blk);+		BUG();+	}+	return &dev->block_info[blk - dev->internal_start_block];+}++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_guts.c linux-3.4.90/fs/yaffs2/yaffs_guts.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_guts.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_guts.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,5146 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yportenv.h"+#include "yaffs_trace.h"++#include "yaffs_guts.h"+#include "yaffs_getblockinfo.h"+#include "yaffs_tagscompat.h"+#include "yaffs_tagsmarshall.h"+#include "yaffs_nand.h"+#include "yaffs_yaffs1.h"+#include "yaffs_yaffs2.h"+#include "yaffs_bitmap.h"+#include "yaffs_verify.h"+#include "yaffs_nand.h"+#include "yaffs_packedtags2.h"+#include "yaffs_nameval.h"+#include "yaffs_allocator.h"+#include "yaffs_attribs.h"+#include "yaffs_summary.h"++/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */+#define YAFFS_GC_GOOD_ENOUGH 2+#define YAFFS_GC_PASSIVE_THRESHOLD 4++#include "yaffs_ecc.h"++/* Forward declarations */++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk,+			     const u8 *buffer, int n_bytes, int use_reserve);++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name,+				int buffer_size);++/* Function to calculate chunk and offset */++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr,+				int *chunk_out, u32 *offset_out)+{+	int chunk;+	u32 offset;++	chunk = (u32) (addr >> dev->chunk_shift);++	if (dev->chunk_div == 1) {+		/* easy power of 2 case */+		offset = (u32) (addr & dev->chunk_mask);+	} else {+		/* Non power-of-2 case */++		loff_t chunk_base;++		chunk /= dev->chunk_div;++		chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk;+		offset = (u32) (addr - chunk_base);+	}++	*chunk_out = chunk;+	*offset_out = offset;+}++/* Function to return the number of shifts for a power of 2 greater than or+ * equal to the given number+ * Note we don't try to cater for all possible numbers and this does not have to+ * be hellishly efficient.+ */++static inline u32 calc_shifts_ceiling(u32 x)+{+	int extra_bits;+	int shifts;++	shifts = extra_bits = 0;++	while (x > 1) {+		if (x & 1)+			extra_bits++;+		x >>= 1;+		shifts++;+	}++	if (extra_bits)+		shifts++;++	return shifts;+}++/* Function to return the number of shifts to get a 1 in bit 0+ */++static inline u32 calc_shifts(u32 x)+{+	u32 shifts;++	shifts = 0;++	if (!x)+		return 0;++	while (!(x & 1)) {+		x >>= 1;+		shifts++;+	}++	return shifts;+}++/*+ * Temporary buffer manipulations.+ */++static int yaffs_init_tmp_buffers(struct yaffs_dev *dev)+{+	int i;+	u8 *buf = (u8 *) 1;++	memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer));++	for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) {+		dev->temp_buffer[i].in_use = 0;+		buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);+		dev->temp_buffer[i].buffer = buf;+	}++	return buf ? YAFFS_OK : YAFFS_FAIL;+}++u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev)+{+	int i;++	dev->temp_in_use++;+	if (dev->temp_in_use > dev->max_temp)+		dev->max_temp = dev->temp_in_use;++	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {+		if (dev->temp_buffer[i].in_use == 0) {+			dev->temp_buffer[i].in_use = 1;+			return dev->temp_buffer[i].buffer;+		}+	}++	yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers");+	/*+	 * If we got here then we have to allocate an unmanaged one+	 * This is not good.+	 */++	dev->unmanaged_buffer_allocs++;+	return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS);++}++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer)+{+	int i;++	dev->temp_in_use--;++	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {+		if (dev->temp_buffer[i].buffer == buffer) {+			dev->temp_buffer[i].in_use = 0;+			return;+		}+	}++	if (buffer) {+		/* assume it is an unmanaged one. */+		yaffs_trace(YAFFS_TRACE_BUFFERS,+			"Releasing unmanaged temp buffer");+		kfree(buffer);+		dev->unmanaged_buffer_deallocs++;+	}++}++/*+ * Functions for robustisizing TODO+ *+ */++static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk,+				     const u8 *data,+				     const struct yaffs_ext_tags *tags)+{+	(void) dev;+	(void) nand_chunk;+	(void) data;+	(void) tags;+}++static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk,+				      const struct yaffs_ext_tags *tags)+{+	(void) dev;+	(void) nand_chunk;+	(void) tags;+}++void yaffs_handle_chunk_error(struct yaffs_dev *dev,+			      struct yaffs_block_info *bi)+{+	if (!bi->gc_prioritise) {+		bi->gc_prioritise = 1;+		dev->has_pending_prioritised_gc = 1;+		bi->chunk_error_strikes++;++		if (bi->chunk_error_strikes > 3) {+			bi->needs_retiring = 1;	/* Too many stikes, so retire */+			yaffs_trace(YAFFS_TRACE_ALWAYS,+				"yaffs: Block struck out");++		}+	}+}++static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk,+					int erased_ok)+{+	int flash_block = nand_chunk / dev->param.chunks_per_block;+	struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block);++	yaffs_handle_chunk_error(dev, bi);++	if (erased_ok) {+		/* Was an actual write failure,+		 * so mark the block for retirement.*/+		bi->needs_retiring = 1;+		yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,+		  "**>> Block %d needs retiring", flash_block);+	}++	/* Delete the chunk */+	yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);+	yaffs_skip_rest_of_block(dev);+}++/*+ * Verification code+ */++/*+ *  Simple hash function. Needs to have a reasonable spread+ */++static inline int yaffs_hash_fn(int n)+{+	if (n < 0)+		n = -n;+	return n % YAFFS_NOBJECT_BUCKETS;+}++/*+ * Access functions to useful fake objects.+ * Note that root might have a presence in NAND if permissions are set.+ */++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev)+{+	return dev->root_dir;+}++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev)+{+	return dev->lost_n_found;+}++/*+ *  Erased NAND checking functions+ */++int yaffs_check_ff(u8 *buffer, int n_bytes)+{+	/* Horrible, slow implementation */+	while (n_bytes--) {+		if (*buffer != 0xff)+			return 0;+		buffer++;+	}+	return 1;+}++static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk)+{+	int retval = YAFFS_OK;+	u8 *data = yaffs_get_temp_buffer(dev);+	struct yaffs_ext_tags tags;+	int result;++	result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags);++	if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR)+		retval = YAFFS_FAIL;++	if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) ||+		tags.chunk_used) {+		yaffs_trace(YAFFS_TRACE_NANDACCESS,+			"Chunk %d not erased", nand_chunk);+		retval = YAFFS_FAIL;+	}++	yaffs_release_temp_buffer(dev, data);++	return retval;++}++static int yaffs_verify_chunk_written(struct yaffs_dev *dev,+				      int nand_chunk,+				      const u8 *data,+				      struct yaffs_ext_tags *tags)+{+	int retval = YAFFS_OK;+	struct yaffs_ext_tags temp_tags;+	u8 *buffer = yaffs_get_temp_buffer(dev);+	int result;++	result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags);+	if (memcmp(buffer, data, dev->data_bytes_per_chunk) ||+	    temp_tags.obj_id != tags->obj_id ||+	    temp_tags.chunk_id != tags->chunk_id ||+	    temp_tags.n_bytes != tags->n_bytes)+		retval = YAFFS_FAIL;++	yaffs_release_temp_buffer(dev, buffer);++	return retval;+}+++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks)+{+	int reserved_chunks;+	int reserved_blocks = dev->param.n_reserved_blocks;+	int checkpt_blocks;++	checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev);++	reserved_chunks =+	    (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block;++	return (dev->n_free_chunks > (reserved_chunks + n_chunks));+}++static int yaffs_find_alloc_block(struct yaffs_dev *dev)+{+	int i;+	struct yaffs_block_info *bi;++	if (dev->n_erased_blocks < 1) {+		/* Hoosterman we've got a problem.+		 * Can't get space to gc+		 */+		yaffs_trace(YAFFS_TRACE_ERROR,+		  "yaffs tragedy: no more erased blocks");++		return -1;+	}++	/* Find an empty block. */++	for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {+		dev->alloc_block_finder++;+		if (dev->alloc_block_finder < dev->internal_start_block+		    || dev->alloc_block_finder > dev->internal_end_block) {+			dev->alloc_block_finder = dev->internal_start_block;+		}++		bi = yaffs_get_block_info(dev, dev->alloc_block_finder);++		if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {+			bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING;+			dev->seq_number++;+			bi->seq_number = dev->seq_number;+			dev->n_erased_blocks--;+			yaffs_trace(YAFFS_TRACE_ALLOCATE,+			  "Allocated block %d, seq  %d, %d left" ,+			   dev->alloc_block_finder, dev->seq_number,+			   dev->n_erased_blocks);+			return dev->alloc_block_finder;+		}+	}++	yaffs_trace(YAFFS_TRACE_ALWAYS,+		"yaffs tragedy: no more erased blocks, but there should have been %d",+		dev->n_erased_blocks);++	return -1;+}++static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver,+			     struct yaffs_block_info **block_ptr)+{+	int ret_val;+	struct yaffs_block_info *bi;++	if (dev->alloc_block < 0) {+		/* Get next block to allocate off */+		dev->alloc_block = yaffs_find_alloc_block(dev);+		dev->alloc_page = 0;+	}++	if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) {+		/* No space unless we're allowed to use the reserve. */+		return -1;+	}++	if (dev->n_erased_blocks < dev->param.n_reserved_blocks+	    && dev->alloc_page == 0)+		yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve");++	/* Next page please.... */+	if (dev->alloc_block >= 0) {+		bi = yaffs_get_block_info(dev, dev->alloc_block);++		ret_val = (dev->alloc_block * dev->param.chunks_per_block) ++		    dev->alloc_page;+		bi->pages_in_use++;+		yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page);++		dev->alloc_page++;++		dev->n_free_chunks--;++		/* If the block is full set the state to full */+		if (dev->alloc_page >= dev->param.chunks_per_block) {+			bi->block_state = YAFFS_BLOCK_STATE_FULL;+			dev->alloc_block = -1;+		}++		if (block_ptr)+			*block_ptr = bi;++		return ret_val;+	}++	yaffs_trace(YAFFS_TRACE_ERROR,+		"!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!");++	return -1;+}++static int yaffs_get_erased_chunks(struct yaffs_dev *dev)+{+	int n;++	n = dev->n_erased_blocks * dev->param.chunks_per_block;++	if (dev->alloc_block > 0)+		n += (dev->param.chunks_per_block - dev->alloc_page);++	return n;++}++/*+ * yaffs_skip_rest_of_block() skips over the rest of the allocation block+ * if we don't want to write to it.+ */+void yaffs_skip_rest_of_block(struct yaffs_dev *dev)+{+	struct yaffs_block_info *bi;++	if (dev->alloc_block > 0) {+		bi = yaffs_get_block_info(dev, dev->alloc_block);+		if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) {+			bi->block_state = YAFFS_BLOCK_STATE_FULL;+			dev->alloc_block = -1;+		}+	}+}++static int yaffs_write_new_chunk(struct yaffs_dev *dev,+				 const u8 *data,+				 struct yaffs_ext_tags *tags, int use_reserver)+{+	int attempts = 0;+	int write_ok = 0;+	int chunk;++	yaffs2_checkpt_invalidate(dev);++	do {+		struct yaffs_block_info *bi = 0;+		int erased_ok = 0;++		chunk = yaffs_alloc_chunk(dev, use_reserver, &bi);+		if (chunk < 0) {+			/* no space */+			break;+		}++		/* First check this chunk is erased, if it needs+		 * checking.  The checking policy (unless forced+		 * always on) is as follows:+		 *+		 * Check the first page we try to write in a block.+		 * If the check passes then we don't need to check any+		 * more.        If the check fails, we check again...+		 * If the block has been erased, we don't need to check.+		 *+		 * However, if the block has been prioritised for gc,+		 * then we think there might be something odd about+		 * this block and stop using it.+		 *+		 * Rationale: We should only ever see chunks that have+		 * not been erased if there was a partially written+		 * chunk due to power loss.  This checking policy should+		 * catch that case with very few checks and thus save a+		 * lot of checks that are most likely not needed.+		 *+		 * Mods to the above+		 * If an erase check fails or the write fails we skip the+		 * rest of the block.+		 */++		/* let's give it a try */+		attempts++;++		if (dev->param.always_check_erased)+			bi->skip_erased_check = 0;++		if (!bi->skip_erased_check) {+			erased_ok = yaffs_check_chunk_erased(dev, chunk);+			if (erased_ok != YAFFS_OK) {+				yaffs_trace(YAFFS_TRACE_ERROR,+				  "**>> yaffs chunk %d was not erased",+				  chunk);++				/* If not erased, delete this one,+				 * skip rest of block and+				 * try another chunk */+				yaffs_chunk_del(dev, chunk, 1, __LINE__);+				yaffs_skip_rest_of_block(dev);+				continue;+			}+		}++		write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags);++		if (!bi->skip_erased_check)+			write_ok =+			    yaffs_verify_chunk_written(dev, chunk, data, tags);++		if (write_ok != YAFFS_OK) {+			/* Clean up aborted write, skip to next block and+			 * try another chunk */+			yaffs_handle_chunk_wr_error(dev, chunk, erased_ok);+			continue;+		}++		bi->skip_erased_check = 1;++		/* Copy the data into the robustification buffer */+		yaffs_handle_chunk_wr_ok(dev, chunk, data, tags);++	} while (write_ok != YAFFS_OK &&+		 (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts));++	if (!write_ok)+		chunk = -1;++	if (attempts > 1) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"**>> yaffs write required %d attempts",+			attempts);+		dev->n_retried_writes += (attempts - 1);+	}++	return chunk;+}++/*+ * Block retiring for handling a broken block.+ */++static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block)+{+	struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block);++	yaffs2_checkpt_invalidate(dev);++	yaffs2_clear_oldest_dirty_seq(dev, bi);++	if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) {+		if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) {+			yaffs_trace(YAFFS_TRACE_ALWAYS,+				"yaffs: Failed to mark bad and erase block %d",+				flash_block);+		} else {+			struct yaffs_ext_tags tags;+			int chunk_id =+			    flash_block * dev->param.chunks_per_block;++			u8 *buffer = yaffs_get_temp_buffer(dev);++			memset(buffer, 0xff, dev->data_bytes_per_chunk);+			memset(&tags, 0, sizeof(tags));+			tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK;+			if (dev->tagger.write_chunk_tags_fn(dev, chunk_id -+							dev->chunk_offset,+							buffer,+							&tags) != YAFFS_OK)+				yaffs_trace(YAFFS_TRACE_ALWAYS,+					"yaffs: Failed to write bad block marker to block %d",+					flash_block);++			yaffs_release_temp_buffer(dev, buffer);+		}+	}++	bi->block_state = YAFFS_BLOCK_STATE_DEAD;+	bi->gc_prioritise = 0;+	bi->needs_retiring = 0;++	dev->n_retired_blocks++;+}++/*---------------- Name handling functions ------------*/++static u16 yaffs_calc_name_sum(const YCHAR *name)+{+	u16 sum = 0;+	u16 i = 1;++	if (!name)+		return 0;++	while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) {++		/* 0x1f mask is case insensitive */+		sum += ((*name) & 0x1f) * i;+		i++;+		name++;+	}+	return sum;+}+++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name)+{+	memset(obj->short_name, 0, sizeof(obj->short_name));++	if (name && !name[0]) {+		yaffs_fix_null_name(obj, obj->short_name,+				YAFFS_SHORT_NAME_LENGTH);+		name = obj->short_name;+	} else if (name &&+		strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <=+		YAFFS_SHORT_NAME_LENGTH)  {+		strcpy(obj->short_name, name);+	}++	obj->sum = yaffs_calc_name_sum(name);+}++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj,+				const struct yaffs_obj_hdr *oh)+{+#ifdef CONFIG_YAFFS_AUTO_UNICODE+	YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1];+	memset(tmp_name, 0, sizeof(tmp_name));+	yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name,+				YAFFS_MAX_NAME_LENGTH + 1);+	yaffs_set_obj_name(obj, tmp_name);+#else+	yaffs_set_obj_name(obj, oh->name);+#endif+}++loff_t yaffs_max_file_size(struct yaffs_dev *dev)+{+	if(sizeof(loff_t) < 8)+		return YAFFS_MAX_FILE_SIZE_32;+	else+		return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk;+}++/*-------------------- TNODES -------------------++ * List of spare tnodes+ * The list is hooked together using the first pointer+ * in the tnode.+ */++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev)+{+	struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev);++	if (tn) {+		memset(tn, 0, dev->tnode_size);+		dev->n_tnodes++;+	}++	dev->checkpoint_blocks_required = 0;	/* force recalculation */++	return tn;+}++/* FreeTnode frees up a tnode and puts it back on the free list */+static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn)+{+	yaffs_free_raw_tnode(dev, tn);+	dev->n_tnodes--;+	dev->checkpoint_blocks_required = 0;	/* force recalculation */+}++static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev)+{+	yaffs_deinit_raw_tnodes_and_objs(dev);+	dev->n_obj = 0;+	dev->n_tnodes = 0;+}++static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn,+			unsigned pos, unsigned val)+{+	u32 *map = (u32 *) tn;+	u32 bit_in_map;+	u32 bit_in_word;+	u32 word_in_map;+	u32 mask;++	pos &= YAFFS_TNODES_LEVEL0_MASK;+	val >>= dev->chunk_grp_bits;++	bit_in_map = pos * dev->tnode_width;+	word_in_map = bit_in_map / 32;+	bit_in_word = bit_in_map & (32 - 1);++	mask = dev->tnode_mask << bit_in_word;++	map[word_in_map] &= ~mask;+	map[word_in_map] |= (mask & (val << bit_in_word));++	if (dev->tnode_width > (32 - bit_in_word)) {+		bit_in_word = (32 - bit_in_word);+		word_in_map++;+		mask =+		    dev->tnode_mask >> bit_in_word;+		map[word_in_map] &= ~mask;+		map[word_in_map] |= (mask & (val >> bit_in_word));+	}+}++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn,+			 unsigned pos)+{+	u32 *map = (u32 *) tn;+	u32 bit_in_map;+	u32 bit_in_word;+	u32 word_in_map;+	u32 val;++	pos &= YAFFS_TNODES_LEVEL0_MASK;++	bit_in_map = pos * dev->tnode_width;+	word_in_map = bit_in_map / 32;+	bit_in_word = bit_in_map & (32 - 1);++	val = map[word_in_map] >> bit_in_word;++	if (dev->tnode_width > (32 - bit_in_word)) {+		bit_in_word = (32 - bit_in_word);+		word_in_map++;+		val |= (map[word_in_map] << bit_in_word);+	}++	val &= dev->tnode_mask;+	val <<= dev->chunk_grp_bits;++	return val;+}++/* ------------------- End of individual tnode manipulation -----------------*/++/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------+ * The look up tree is represented by the top tnode and the number of top_level+ * in the tree. 0 means only the level 0 tnode is in the tree.+ */++/* FindLevel0Tnode finds the level 0 tnode, if one exists. */+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev,+				       struct yaffs_file_var *file_struct,+				       u32 chunk_id)+{+	struct yaffs_tnode *tn = file_struct->top;+	u32 i;+	int required_depth;+	int level = file_struct->top_level;++	(void) dev;++	/* Check sane level and chunk Id */+	if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL)+		return NULL;++	if (chunk_id > YAFFS_MAX_CHUNK_ID)+		return NULL;++	/* First check we're tall enough (ie enough top_level) */++	i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS;+	required_depth = 0;+	while (i) {+		i >>= YAFFS_TNODES_INTERNAL_BITS;+		required_depth++;+	}++	if (required_depth > file_struct->top_level)+		return NULL;	/* Not tall enough, so we can't find it */++	/* Traverse down to level 0 */+	while (level > 0 && tn) {+		tn = tn->internal[(chunk_id >>+				   (YAFFS_TNODES_LEVEL0_BITS ++				    (level - 1) *+				    YAFFS_TNODES_INTERNAL_BITS)) &+				  YAFFS_TNODES_INTERNAL_MASK];+		level--;+	}++	return tn;+}++/* add_find_tnode_0 finds the level 0 tnode if it exists,+ * otherwise first expands the tree.+ * This happens in two steps:+ *  1. If the tree isn't tall enough, then make it taller.+ *  2. Scan down the tree towards the level 0 tnode adding tnodes if required.+ *+ * Used when modifying the tree.+ *+ *  If the tn argument is NULL, then a fresh tnode will be added otherwise the+ *  specified tn will be plugged into the ttree.+ */++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev,+					   struct yaffs_file_var *file_struct,+					   u32 chunk_id,+					   struct yaffs_tnode *passed_tn)+{+	int required_depth;+	int i;+	int l;+	struct yaffs_tnode *tn;+	u32 x;++	/* Check sane level and page Id */+	if (file_struct->top_level < 0 ||+	    file_struct->top_level > YAFFS_TNODES_MAX_LEVEL)+		return NULL;++	if (chunk_id > YAFFS_MAX_CHUNK_ID)+		return NULL;++	/* First check we're tall enough (ie enough top_level) */++	x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS;+	required_depth = 0;+	while (x) {+		x >>= YAFFS_TNODES_INTERNAL_BITS;+		required_depth++;+	}++	if (required_depth > file_struct->top_level) {+		/* Not tall enough, gotta make the tree taller */+		for (i = file_struct->top_level; i < required_depth; i++) {++			tn = yaffs_get_tnode(dev);++			if (tn) {+				tn->internal[0] = file_struct->top;+				file_struct->top = tn;+				file_struct->top_level++;+			} else {+				yaffs_trace(YAFFS_TRACE_ERROR,+					"yaffs: no more tnodes");+				return NULL;+			}+		}+	}++	/* Traverse down to level 0, adding anything we need */++	l = file_struct->top_level;+	tn = file_struct->top;++	if (l > 0) {+		while (l > 0 && tn) {+			x = (chunk_id >>+			     (YAFFS_TNODES_LEVEL0_BITS ++			      (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) &+			    YAFFS_TNODES_INTERNAL_MASK;++			if ((l > 1) && !tn->internal[x]) {+				/* Add missing non-level-zero tnode */+				tn->internal[x] = yaffs_get_tnode(dev);+				if (!tn->internal[x])+					return NULL;+			} else if (l == 1) {+				/* Looking from level 1 at level 0 */+				if (passed_tn) {+					/* If we already have one, release it */+					if (tn->internal[x])+						yaffs_free_tnode(dev,+							tn->internal[x]);+					tn->internal[x] = passed_tn;++				} else if (!tn->internal[x]) {+					/* Don't have one, none passed in */+					tn->internal[x] = yaffs_get_tnode(dev);+					if (!tn->internal[x])+						return NULL;+				}+			}++			tn = tn->internal[x];+			l--;+		}+	} else {+		/* top is level 0 */+		if (passed_tn) {+			memcpy(tn, passed_tn,+			       (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8);+			yaffs_free_tnode(dev, passed_tn);+		}+	}++	return tn;+}++static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id,+			    int chunk_obj)+{+	return (tags->chunk_id == chunk_obj &&+		tags->obj_id == obj_id &&+		!tags->is_deleted) ? 1 : 0;++}++static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk,+					struct yaffs_ext_tags *tags, int obj_id,+					int inode_chunk)+{+	int j;++	for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) {+		if (yaffs_check_chunk_bit+		    (dev, the_chunk / dev->param.chunks_per_block,+		     the_chunk % dev->param.chunks_per_block)) {++			if (dev->chunk_grp_size == 1)+				return the_chunk;+			else {+				yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL,+							 tags);+				if (yaffs_tags_match(tags,+							obj_id, inode_chunk)) {+					/* found it; */+					return the_chunk;+				}+			}+		}+		the_chunk++;+	}+	return -1;+}++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk,+				    struct yaffs_ext_tags *tags)+{+	/*Get the Tnode, then get the level 0 offset chunk offset */+	struct yaffs_tnode *tn;+	int the_chunk = -1;+	struct yaffs_ext_tags local_tags;+	int ret_val = -1;+	struct yaffs_dev *dev = in->my_dev;++	if (!tags) {+		/* Passed a NULL, so use our own tags space */+		tags = &local_tags;+	}++	tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk);++	if (!tn)+		return ret_val;++	the_chunk = yaffs_get_group_base(dev, tn, inode_chunk);++	ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id,+					      inode_chunk);+	return ret_val;+}++static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk,+				     struct yaffs_ext_tags *tags)+{+	/* Get the Tnode, then get the level 0 offset chunk offset */+	struct yaffs_tnode *tn;+	int the_chunk = -1;+	struct yaffs_ext_tags local_tags;+	struct yaffs_dev *dev = in->my_dev;+	int ret_val = -1;++	if (!tags) {+		/* Passed a NULL, so use our own tags space */+		tags = &local_tags;+	}++	tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk);++	if (!tn)+		return ret_val;++	the_chunk = yaffs_get_group_base(dev, tn, inode_chunk);++	ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id,+					      inode_chunk);++	/* Delete the entry in the filestructure (if found) */+	if (ret_val != -1)+		yaffs_load_tnode_0(dev, tn, inode_chunk, 0);++	return ret_val;+}++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk,+			    int nand_chunk, int in_scan)+{+	/* NB in_scan is zero unless scanning.+	 * For forward scanning, in_scan is > 0;+	 * for backward scanning in_scan is < 0+	 *+	 * nand_chunk = 0 is a dummy insert to make sure the tnodes are there.+	 */++	struct yaffs_tnode *tn;+	struct yaffs_dev *dev = in->my_dev;+	int existing_cunk;+	struct yaffs_ext_tags existing_tags;+	struct yaffs_ext_tags new_tags;+	unsigned existing_serial, new_serial;++	if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) {+		/* Just ignore an attempt at putting a chunk into a non-file+		 * during scanning.+		 * If it is not during Scanning then something went wrong!+		 */+		if (!in_scan) {+			yaffs_trace(YAFFS_TRACE_ERROR,+				"yaffs tragedy:attempt to put data chunk into a non-file"+				);+			BUG();+		}++		yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);+		return YAFFS_OK;+	}++	tn = yaffs_add_find_tnode_0(dev,+				    &in->variant.file_variant,+				    inode_chunk, NULL);+	if (!tn)+		return YAFFS_FAIL;++	if (!nand_chunk)+		/* Dummy insert, bail now */+		return YAFFS_OK;++	existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk);++	if (in_scan != 0) {+		/* If we're scanning then we need to test for duplicates+		 * NB This does not need to be efficient since it should only+		 * happen when the power fails during a write, then only one+		 * chunk should ever be affected.+		 *+		 * Correction for YAFFS2: This could happen quite a lot and we+		 * need to think about efficiency! TODO+		 * Update: For backward scanning we don't need to re-read tags+		 * so this is quite cheap.+		 */++		if (existing_cunk > 0) {+			/* NB Right now existing chunk will not be real+			 * chunk_id if the chunk group size > 1+			 * thus we have to do a FindChunkInFile to get the+			 * real chunk id.+			 *+			 * We have a duplicate now we need to decide which+			 * one to use:+			 *+			 * Backwards scanning YAFFS2: The old one is what+			 * we use, dump the new one.+			 * YAFFS1: Get both sets of tags and compare serial+			 * numbers.+			 */++			if (in_scan > 0) {+				/* Only do this for forward scanning */+				yaffs_rd_chunk_tags_nand(dev,+							 nand_chunk,+							 NULL, &new_tags);++				/* Do a proper find */+				existing_cunk =+				    yaffs_find_chunk_in_file(in, inode_chunk,+							     &existing_tags);+			}++			if (existing_cunk <= 0) {+				/*Hoosterman - how did this happen? */++				yaffs_trace(YAFFS_TRACE_ERROR,+					"yaffs tragedy: existing chunk < 0 in scan"+					);++			}++			/* NB The deleted flags should be false, otherwise+			 * the chunks will not be loaded during a scan+			 */++			if (in_scan > 0) {+				new_serial = new_tags.serial_number;+				existing_serial = existing_tags.serial_number;+			}++			if ((in_scan > 0) &&+			    (existing_cunk <= 0 ||+			     ((existing_serial + 1) & 3) == new_serial)) {+				/* Forward scanning.+				 * Use new+				 * Delete the old one and drop through to+				 * update the tnode+				 */+				yaffs_chunk_del(dev, existing_cunk, 1,+						__LINE__);+			} else {+				/* Backward scanning or we want to use the+				 * existing one+				 * Delete the new one and return early so that+				 * the tnode isn't changed+				 */+				yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);+				return YAFFS_OK;+			}+		}++	}++	if (existing_cunk == 0)+		in->n_data_chunks++;++	yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk);++	return YAFFS_OK;+}++static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk)+{+	struct yaffs_block_info *the_block;+	unsigned block_no;++	yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk);++	block_no = chunk / dev->param.chunks_per_block;+	the_block = yaffs_get_block_info(dev, block_no);+	if (the_block) {+		the_block->soft_del_pages++;+		dev->n_free_chunks++;+		yaffs2_update_oldest_dirty_seq(dev, block_no, the_block);+	}+}++/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all+ * the chunks in the file.+ * All soft deleting does is increment the block's softdelete count and pulls+ * the chunk out of the tnode.+ * Thus, essentially this is the same as DeleteWorker except that the chunks+ * are soft deleted.+ */++static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn,+				 u32 level, int chunk_offset)+{+	int i;+	int the_chunk;+	int all_done = 1;+	struct yaffs_dev *dev = in->my_dev;++	if (!tn)+		return 1;++	if (level > 0) {+		for (i = YAFFS_NTNODES_INTERNAL - 1;+			all_done && i >= 0;+			i--) {+			if (tn->internal[i]) {+				all_done =+				    yaffs_soft_del_worker(in,+					tn->internal[i],+					level - 1,+					(chunk_offset <<+					YAFFS_TNODES_INTERNAL_BITS)+					+ i);+				if (all_done) {+					yaffs_free_tnode(dev,+						tn->internal[i]);+					tn->internal[i] = NULL;+				} else {+					/* Can this happen? */+				}+			}+		}+		return (all_done) ? 1 : 0;+	}++	/* level 0 */+	 for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) {+		the_chunk = yaffs_get_group_base(dev, tn, i);+		if (the_chunk) {+			yaffs_soft_del_chunk(dev, the_chunk);+			yaffs_load_tnode_0(dev, tn, i, 0);+		}+	}+	return 1;+}++static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj)+{+	struct yaffs_dev *dev = obj->my_dev;+	struct yaffs_obj *parent;++	yaffs_verify_obj_in_dir(obj);+	parent = obj->parent;++	yaffs_verify_dir(parent);++	if (dev && dev->param.remove_obj_fn)+		dev->param.remove_obj_fn(obj);++	list_del_init(&obj->siblings);+	obj->parent = NULL;++	yaffs_verify_dir(parent);+}++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj)+{+	if (!directory) {+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"tragedy: Trying to add an object to a null pointer directory"+			);+		BUG();+		return;+	}+	if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"tragedy: Trying to add an object to a non-directory"+			);+		BUG();+	}++	if (obj->siblings.prev == NULL) {+		/* Not initialised */+		BUG();+	}++	yaffs_verify_dir(directory);++	yaffs_remove_obj_from_dir(obj);++	/* Now add it */+	list_add(&obj->siblings, &directory->variant.dir_variant.children);+	obj->parent = directory;++	if (directory == obj->my_dev->unlinked_dir+	    || directory == obj->my_dev->del_dir) {+		obj->unlinked = 1;+		obj->my_dev->n_unlinked_files++;+		obj->rename_allowed = 0;+	}++	yaffs_verify_dir(directory);+	yaffs_verify_obj_in_dir(obj);+}++static int yaffs_change_obj_name(struct yaffs_obj *obj,+				 struct yaffs_obj *new_dir,+				 const YCHAR *new_name, int force, int shadows)+{+	int unlink_op;+	int del_op;+	struct yaffs_obj *existing_target;++	if (new_dir == NULL)+		new_dir = obj->parent;	/* use the old directory */++	if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"tragedy: yaffs_change_obj_name: new_dir is not a directory"+			);+		BUG();+	}++	unlink_op = (new_dir == obj->my_dev->unlinked_dir);+	del_op = (new_dir == obj->my_dev->del_dir);++	existing_target = yaffs_find_by_name(new_dir, new_name);++	/* If the object is a file going into the unlinked directory,+	 *   then it is OK to just stuff it in since duplicate names are OK.+	 *   else only proceed if the new name does not exist and we're putting+	 *   it into a directory.+	 */+	if (!(unlink_op || del_op || force ||+	      shadows > 0 || !existing_target) ||+	      new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)+		return YAFFS_FAIL;++	yaffs_set_obj_name(obj, new_name);+	obj->dirty = 1;+	yaffs_add_obj_to_dir(new_dir, obj);++	if (unlink_op)+		obj->unlinked = 1;++	/* If it is a deletion then we mark it as a shrink for gc  */+	if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0)+		return YAFFS_OK;++	return YAFFS_FAIL;+}++/*------------------------ Short Operations Cache ------------------------------+ *   In many situations where there is no high level buffering  a lot of+ *   reads might be short sequential reads, and a lot of writes may be short+ *   sequential writes. eg. scanning/writing a jpeg file.+ *   In these cases, a short read/write cache can provide a huge perfomance+ *   benefit with dumb-as-a-rock code.+ *   In Linux, the page cache provides read buffering and the short op cache+ *   provides write buffering.+ *+ *   There are a small number (~10) of cache chunks per device so that we don't+ *   need a very intelligent search.+ */++static int yaffs_obj_cache_dirty(struct yaffs_obj *obj)+{+	struct yaffs_dev *dev = obj->my_dev;+	int i;+	struct yaffs_cache *cache;+	int n_caches = obj->my_dev->param.n_caches;++	for (i = 0; i < n_caches; i++) {+		cache = &dev->cache[i];+		if (cache->object == obj && cache->dirty)+			return 1;+	}++	return 0;+}++static void yaffs_flush_file_cache(struct yaffs_obj *obj)+{+	struct yaffs_dev *dev = obj->my_dev;+	int lowest = -99;	/* Stop compiler whining. */+	int i;+	struct yaffs_cache *cache;+	int chunk_written = 0;+	int n_caches = obj->my_dev->param.n_caches;++	if (n_caches < 1)+		return;+	do {+		cache = NULL;++		/* Find the lowest dirty chunk for this object */+		for (i = 0; i < n_caches; i++) {+			if (dev->cache[i].object == obj &&+			    dev->cache[i].dirty) {+				if (!cache ||+				    dev->cache[i].chunk_id < lowest) {+					cache = &dev->cache[i];+					lowest = cache->chunk_id;+				}+			}+		}++		if (cache && !cache->locked) {+			/* Write it out and free it up */+			chunk_written =+			    yaffs_wr_data_obj(cache->object,+					      cache->chunk_id,+					      cache->data,+					      cache->n_bytes, 1);+			cache->dirty = 0;+			cache->object = NULL;+		}+	} while (cache && chunk_written > 0);++	if (cache)+		/* Hoosterman, disk full while writing cache out. */+		yaffs_trace(YAFFS_TRACE_ERROR,+			"yaffs tragedy: no space during cache write");+}++/*yaffs_flush_whole_cache(dev)+ *+ *+ */++void yaffs_flush_whole_cache(struct yaffs_dev *dev)+{+	struct yaffs_obj *obj;+	int n_caches = dev->param.n_caches;+	int i;++	/* Find a dirty object in the cache and flush it...+	 * until there are no further dirty objects.+	 */+	do {+		obj = NULL;+		for (i = 0; i < n_caches && !obj; i++) {+			if (dev->cache[i].object && dev->cache[i].dirty)+				obj = dev->cache[i].object;+		}+		if (obj)+			yaffs_flush_file_cache(obj);+	} while (obj);++}++/* Grab us a cache chunk for use.+ * First look for an empty one.+ * Then look for the least recently used non-dirty one.+ * Then look for the least recently used dirty one...., flush and look again.+ */+static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev)+{+	int i;++	if (dev->param.n_caches > 0) {+		for (i = 0; i < dev->param.n_caches; i++) {+			if (!dev->cache[i].object)+				return &dev->cache[i];+		}+	}+	return NULL;+}++static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev)+{+	struct yaffs_cache *cache;+	struct yaffs_obj *the_obj;+	int usage;+	int i;+	int pushout;++	if (dev->param.n_caches < 1)+		return NULL;++	/* Try find a non-dirty one... */++	cache = yaffs_grab_chunk_worker(dev);++	if (!cache) {+		/* They were all dirty, find the LRU object and flush+		 * its cache, then  find again.+		 * NB what's here is not very accurate,+		 * we actually flush the object with the LRU chunk.+		 */++		/* With locking we can't assume we can use entry zero,+		 * Set the_obj to a valid pointer for Coverity. */+		the_obj = dev->cache[0].object;+		usage = -1;+		cache = NULL;+		pushout = -1;++		for (i = 0; i < dev->param.n_caches; i++) {+			if (dev->cache[i].object &&+			    !dev->cache[i].locked &&+			    (dev->cache[i].last_use < usage ||+			    !cache)) {+				usage = dev->cache[i].last_use;+				the_obj = dev->cache[i].object;+				cache = &dev->cache[i];+				pushout = i;+			}+		}++		if (!cache || cache->dirty) {+			/* Flush and try again */+			yaffs_flush_file_cache(the_obj);+			cache = yaffs_grab_chunk_worker(dev);+		}+	}+	return cache;+}++/* Find a cached chunk */+static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj,+						  int chunk_id)+{+	struct yaffs_dev *dev = obj->my_dev;+	int i;++	if (dev->param.n_caches < 1)+		return NULL;++	for (i = 0; i < dev->param.n_caches; i++) {+		if (dev->cache[i].object == obj &&+		    dev->cache[i].chunk_id == chunk_id) {+			dev->cache_hits++;++			return &dev->cache[i];+		}+	}+	return NULL;+}++/* Mark the chunk for the least recently used algorithym */+static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache,+			    int is_write)+{+	int i;++	if (dev->param.n_caches < 1)+		return;++	if (dev->cache_last_use < 0 ||+		dev->cache_last_use > 100000000) {+		/* Reset the cache usages */+		for (i = 1; i < dev->param.n_caches; i++)+			dev->cache[i].last_use = 0;++		dev->cache_last_use = 0;+	}+	dev->cache_last_use++;+	cache->last_use = dev->cache_last_use;++	if (is_write)+		cache->dirty = 1;+}++/* Invalidate a single cache page.+ * Do this when a whole page gets written,+ * ie the short cache for this page is no longer valid.+ */+static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id)+{+	struct yaffs_cache *cache;++	if (object->my_dev->param.n_caches > 0) {+		cache = yaffs_find_chunk_cache(object, chunk_id);++		if (cache)+			cache->object = NULL;+	}+}++/* Invalidate all the cache pages associated with this object+ * Do this whenever ther file is deleted or resized.+ */+static void yaffs_invalidate_whole_cache(struct yaffs_obj *in)+{+	int i;+	struct yaffs_dev *dev = in->my_dev;++	if (dev->param.n_caches > 0) {+		/* Invalidate it. */+		for (i = 0; i < dev->param.n_caches; i++) {+			if (dev->cache[i].object == in)+				dev->cache[i].object = NULL;+		}+	}+}++static void yaffs_unhash_obj(struct yaffs_obj *obj)+{+	int bucket;+	struct yaffs_dev *dev = obj->my_dev;++	/* If it is still linked into the bucket list, free from the list */+	if (!list_empty(&obj->hash_link)) {+		list_del_init(&obj->hash_link);+		bucket = yaffs_hash_fn(obj->obj_id);+		dev->obj_bucket[bucket].count--;+	}+}++/*  FreeObject frees up a Object and puts it back on the free list */+static void yaffs_free_obj(struct yaffs_obj *obj)+{+	struct yaffs_dev *dev;++	if (!obj) {+		BUG();+		return;+	}+	dev = obj->my_dev;+	yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p",+		obj, obj->my_inode);+	if (obj->parent)+		BUG();+	if (!list_empty(&obj->siblings))+		BUG();++	if (obj->my_inode) {+		/* We're still hooked up to a cached inode.+		 * Don't delete now, but mark for later deletion+		 */+		obj->defered_free = 1;+		return;+	}++	yaffs_unhash_obj(obj);++	yaffs_free_raw_obj(dev, obj);+	dev->n_obj--;+	dev->checkpoint_blocks_required = 0;	/* force recalculation */+}++void yaffs_handle_defered_free(struct yaffs_obj *obj)+{+	if (obj->defered_free)+		yaffs_free_obj(obj);+}++static int yaffs_generic_obj_del(struct yaffs_obj *in)+{+	/* Iinvalidate the file's data in the cache, without flushing. */+	yaffs_invalidate_whole_cache(in);++	if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) {+		/* Move to unlinked directory so we have a deletion record */+		yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0,+				      0);+	}++	yaffs_remove_obj_from_dir(in);+	yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__);+	in->hdr_chunk = 0;++	yaffs_free_obj(in);+	return YAFFS_OK;++}++static void yaffs_soft_del_file(struct yaffs_obj *obj)+{+	if (!obj->deleted ||+	    obj->variant_type != YAFFS_OBJECT_TYPE_FILE ||+	    obj->soft_del)+		return;++	if (obj->n_data_chunks <= 0) {+		/* Empty file with no duplicate object headers,+		 * just delete it immediately */+		yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top);+		obj->variant.file_variant.top = NULL;+		yaffs_trace(YAFFS_TRACE_TRACING,+			"yaffs: Deleting empty file %d",+			obj->obj_id);+		yaffs_generic_obj_del(obj);+	} else {+		yaffs_soft_del_worker(obj,+				      obj->variant.file_variant.top,+				      obj->variant.+				      file_variant.top_level, 0);+		obj->soft_del = 1;+	}+}++/* Pruning removes any part of the file structure tree that is beyond the+ * bounds of the file (ie that does not point to chunks).+ *+ * A file should only get pruned when its size is reduced.+ *+ * Before pruning, the chunks must be pulled from the tree and the+ * level 0 tnode entries must be zeroed out.+ * Could also use this for file deletion, but that's probably better handled+ * by a special case.+ *+ * This function is recursive. For levels > 0 the function is called again on+ * any sub-tree. For level == 0 we just check if the sub-tree has data.+ * If there is no data in a subtree then it is pruned.+ */++static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev,+					      struct yaffs_tnode *tn, u32 level,+					      int del0)+{+	int i;+	int has_data;++	if (!tn)+		return tn;++	has_data = 0;++	if (level > 0) {+		for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) {+			if (tn->internal[i]) {+				tn->internal[i] =+				    yaffs_prune_worker(dev,+						tn->internal[i],+						level - 1,+						(i == 0) ? del0 : 1);+			}++			if (tn->internal[i])+				has_data++;+		}+	} else {+		int tnode_size_u32 = dev->tnode_size / sizeof(u32);+		u32 *map = (u32 *) tn;++		for (i = 0; !has_data && i < tnode_size_u32; i++) {+			if (map[i])+				has_data++;+		}+	}++	if (has_data == 0 && del0) {+		/* Free and return NULL */+		yaffs_free_tnode(dev, tn);+		tn = NULL;+	}+	return tn;+}++static int yaffs_prune_tree(struct yaffs_dev *dev,+			    struct yaffs_file_var *file_struct)+{+	int i;+	int has_data;+	int done = 0;+	struct yaffs_tnode *tn;++	if (file_struct->top_level < 1)+		return YAFFS_OK;++	file_struct->top =+	   yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0);++	/* Now we have a tree with all the non-zero branches NULL but+	 * the height is the same as it was.+	 * Let's see if we can trim internal tnodes to shorten the tree.+	 * We can do this if only the 0th element in the tnode is in use+	 * (ie all the non-zero are NULL)+	 */++	while (file_struct->top_level && !done) {+		tn = file_struct->top;++		has_data = 0;+		for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) {+			if (tn->internal[i])+				has_data++;+		}++		if (!has_data) {+			file_struct->top = tn->internal[0];+			file_struct->top_level--;+			yaffs_free_tnode(dev, tn);+		} else {+			done = 1;+		}+	}++	return YAFFS_OK;+}++/*-------------------- End of File Structure functions.-------------------*/++/* alloc_empty_obj gets us a clean Object.*/+static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev)+{+	struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev);++	if (!obj)+		return obj;++	dev->n_obj++;++	/* Now sweeten it up... */++	memset(obj, 0, sizeof(struct yaffs_obj));+	obj->being_created = 1;++	obj->my_dev = dev;+	obj->hdr_chunk = 0;+	obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN;+	INIT_LIST_HEAD(&(obj->hard_links));+	INIT_LIST_HEAD(&(obj->hash_link));+	INIT_LIST_HEAD(&obj->siblings);++	/* Now make the directory sane */+	if (dev->root_dir) {+		obj->parent = dev->root_dir;+		list_add(&(obj->siblings),+			 &dev->root_dir->variant.dir_variant.children);+	}++	/* Add it to the lost and found directory.+	 * NB Can't put root or lost-n-found in lost-n-found so+	 * check if lost-n-found exists first+	 */+	if (dev->lost_n_found)+		yaffs_add_obj_to_dir(dev->lost_n_found, obj);++	obj->being_created = 0;++	dev->checkpoint_blocks_required = 0;	/* force recalculation */++	return obj;+}++static int yaffs_find_nice_bucket(struct yaffs_dev *dev)+{+	int i;+	int l = 999;+	int lowest = 999999;++	/* Search for the shortest list or one that+	 * isn't too long.+	 */++	for (i = 0; i < 10 && lowest > 4; i++) {+		dev->bucket_finder++;+		dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS;+		if (dev->obj_bucket[dev->bucket_finder].count < lowest) {+			lowest = dev->obj_bucket[dev->bucket_finder].count;+			l = dev->bucket_finder;+		}+	}++	return l;+}++static int yaffs_new_obj_id(struct yaffs_dev *dev)+{+	int bucket = yaffs_find_nice_bucket(dev);+	int found = 0;+	struct list_head *i;+	u32 n = (u32) bucket;++	/* Now find an object value that has not already been taken+	 * by scanning the list.+	 */++	while (!found) {+		found = 1;+		n += YAFFS_NOBJECT_BUCKETS;+		if (1 || dev->obj_bucket[bucket].count > 0) {+			list_for_each(i, &dev->obj_bucket[bucket].list) {+				/* If there is already one in the list */+				if (i && list_entry(i, struct yaffs_obj,+						    hash_link)->obj_id == n) {+					found = 0;+				}+			}+		}+	}+	return n;+}++static void yaffs_hash_obj(struct yaffs_obj *in)+{+	int bucket = yaffs_hash_fn(in->obj_id);+	struct yaffs_dev *dev = in->my_dev;++	list_add(&in->hash_link, &dev->obj_bucket[bucket].list);+	dev->obj_bucket[bucket].count++;+}++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number)+{+	int bucket = yaffs_hash_fn(number);+	struct list_head *i;+	struct yaffs_obj *in;++	list_for_each(i, &dev->obj_bucket[bucket].list) {+		/* Look if it is in the list */+		in = list_entry(i, struct yaffs_obj, hash_link);+		if (in->obj_id == number) {+			/* Don't show if it is defered free */+			if (in->defered_free)+				return NULL;+			return in;+		}+	}++	return NULL;+}++static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number,+				enum yaffs_obj_type type)+{+	struct yaffs_obj *the_obj = NULL;+	struct yaffs_tnode *tn = NULL;++	if (number < 0)+		number = yaffs_new_obj_id(dev);++	if (type == YAFFS_OBJECT_TYPE_FILE) {+		tn = yaffs_get_tnode(dev);+		if (!tn)+			return NULL;+	}++	the_obj = yaffs_alloc_empty_obj(dev);+	if (!the_obj) {+		if (tn)+			yaffs_free_tnode(dev, tn);+		return NULL;+	}++	the_obj->fake = 0;+	the_obj->rename_allowed = 1;+	the_obj->unlink_allowed = 1;+	the_obj->obj_id = number;+	yaffs_hash_obj(the_obj);+	the_obj->variant_type = type;+	yaffs_load_current_time(the_obj, 1, 1);++	switch (type) {+	case YAFFS_OBJECT_TYPE_FILE:+		the_obj->variant.file_variant.file_size = 0;+		the_obj->variant.file_variant.scanned_size = 0;+		the_obj->variant.file_variant.shrink_size =+						yaffs_max_file_size(dev);+		the_obj->variant.file_variant.top_level = 0;+		the_obj->variant.file_variant.top = tn;+		break;+	case YAFFS_OBJECT_TYPE_DIRECTORY:+		INIT_LIST_HEAD(&the_obj->variant.dir_variant.children);+		INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty);+		break;+	case YAFFS_OBJECT_TYPE_SYMLINK:+	case YAFFS_OBJECT_TYPE_HARDLINK:+	case YAFFS_OBJECT_TYPE_SPECIAL:+		/* No action required */+		break;+	case YAFFS_OBJECT_TYPE_UNKNOWN:+		/* todo this should not happen */+		break;+	}+	return the_obj;+}++static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev,+					       int number, u32 mode)+{++	struct yaffs_obj *obj =+	    yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY);++	if (!obj)+		return NULL;++	obj->fake = 1;	/* it is fake so it might not use NAND */+	obj->rename_allowed = 0;+	obj->unlink_allowed = 0;+	obj->deleted = 0;+	obj->unlinked = 0;+	obj->yst_mode = mode;+	obj->my_dev = dev;+	obj->hdr_chunk = 0;	/* Not a valid chunk. */+	return obj;++}+++static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev)+{+	int i;++	dev->n_obj = 0;+	dev->n_tnodes = 0;+	yaffs_init_raw_tnodes_and_objs(dev);++	for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {+		INIT_LIST_HEAD(&dev->obj_bucket[i].list);+		dev->obj_bucket[i].count = 0;+	}+}++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev,+						 int number,+						 enum yaffs_obj_type type)+{+	struct yaffs_obj *the_obj = NULL;++	if (number > 0)+		the_obj = yaffs_find_by_number(dev, number);++	if (!the_obj)+		the_obj = yaffs_new_obj(dev, number, type);++	return the_obj;++}++YCHAR *yaffs_clone_str(const YCHAR *str)+{+	YCHAR *new_str = NULL;+	int len;++	if (!str)+		str = _Y("");++	len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH);+	new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS);+	if (new_str) {+		strncpy(new_str, str, len);+		new_str[len] = 0;+	}+	return new_str;++}+/*+ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new+ * link (ie. name) is created or deleted in the directory.+ *+ * ie.+ *   create dir/a : update dir's mtime/ctime+ *   rm dir/a:   update dir's mtime/ctime+ *   modify dir/a: don't update dir's mtimme/ctime+ *+ * This can be handled immediately or defered. Defering helps reduce the number+ * of updates when many files in a directory are changed within a brief period.+ *+ * If the directory updating is defered then yaffs_update_dirty_dirs must be+ * called periodically.+ */++static void yaffs_update_parent(struct yaffs_obj *obj)+{+	struct yaffs_dev *dev;++	if (!obj)+		return;+	dev = obj->my_dev;+	obj->dirty = 1;+	yaffs_load_current_time(obj, 0, 1);+	if (dev->param.defered_dir_update) {+		struct list_head *link = &obj->variant.dir_variant.dirty;++		if (list_empty(link)) {+			list_add(link, &dev->dirty_dirs);+			yaffs_trace(YAFFS_TRACE_BACKGROUND,+			  "Added object %d to dirty directories",+			   obj->obj_id);+		}++	} else {+		yaffs_update_oh(obj, NULL, 0, 0, 0, NULL);+	}+}++void yaffs_update_dirty_dirs(struct yaffs_dev *dev)+{+	struct list_head *link;+	struct yaffs_obj *obj;+	struct yaffs_dir_var *d_s;+	union yaffs_obj_var *o_v;++	yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories");++	while (!list_empty(&dev->dirty_dirs)) {+		link = dev->dirty_dirs.next;+		list_del_init(link);++		d_s = list_entry(link, struct yaffs_dir_var, dirty);+		o_v = list_entry(d_s, union yaffs_obj_var, dir_variant);+		obj = list_entry(o_v, struct yaffs_obj, variant);++		yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d",+			obj->obj_id);++		if (obj->dirty)+			yaffs_update_oh(obj, NULL, 0, 0, 0, NULL);+	}+}++/*+ * Mknod (create) a new object.+ * equiv_obj only has meaning for a hard link;+ * alias_str only has meaning for a symlink.+ * rdev only has meaning for devices (a subset of special objects)+ */++static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type,+					  struct yaffs_obj *parent,+					  const YCHAR *name,+					  u32 mode,+					  u32 uid,+					  u32 gid,+					  struct yaffs_obj *equiv_obj,+					  const YCHAR *alias_str, u32 rdev)+{+	struct yaffs_obj *in;+	YCHAR *str = NULL;+	struct yaffs_dev *dev = parent->my_dev;++	/* Check if the entry exists.+	 * If it does then fail the call since we don't want a dup. */+	if (yaffs_find_by_name(parent, name))+		return NULL;++	if (type == YAFFS_OBJECT_TYPE_SYMLINK) {+		str = yaffs_clone_str(alias_str);+		if (!str)+			return NULL;+	}++	in = yaffs_new_obj(dev, -1, type);++	if (!in) {+		kfree(str);+		return NULL;+	}++	in->hdr_chunk = 0;+	in->valid = 1;+	in->variant_type = type;++	in->yst_mode = mode;++	yaffs_attribs_init(in, gid, uid, rdev);++	in->n_data_chunks = 0;++	yaffs_set_obj_name(in, name);+	in->dirty = 1;++	yaffs_add_obj_to_dir(parent, in);++	in->my_dev = parent->my_dev;++	switch (type) {+	case YAFFS_OBJECT_TYPE_SYMLINK:+		in->variant.symlink_variant.alias = str;+		break;+	case YAFFS_OBJECT_TYPE_HARDLINK:+		in->variant.hardlink_variant.equiv_obj = equiv_obj;+		in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id;+		list_add(&in->hard_links, &equiv_obj->hard_links);+		break;+	case YAFFS_OBJECT_TYPE_FILE:+	case YAFFS_OBJECT_TYPE_DIRECTORY:+	case YAFFS_OBJECT_TYPE_SPECIAL:+	case YAFFS_OBJECT_TYPE_UNKNOWN:+		/* do nothing */+		break;+	}++	if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) {+		/* Could not create the object header, fail */+		yaffs_del_obj(in);+		in = NULL;+	}++	if (in)+		yaffs_update_parent(parent);++	return in;+}++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent,+				    const YCHAR *name, u32 mode, u32 uid,+				    u32 gid)+{+	return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode,+				uid, gid, NULL, NULL, 0);+}++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name,+				   u32 mode, u32 uid, u32 gid)+{+	return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name,+				mode, uid, gid, NULL, NULL, 0);+}++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent,+				       const YCHAR *name, u32 mode, u32 uid,+				       u32 gid, u32 rdev)+{+	return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode,+				uid, gid, NULL, NULL, rdev);+}++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent,+				       const YCHAR *name, u32 mode, u32 uid,+				       u32 gid, const YCHAR *alias)+{+	return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode,+				uid, gid, NULL, alias, 0);+}++/* yaffs_link_obj returns the object id of the equivalent object.*/+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name,+				 struct yaffs_obj *equiv_obj)+{+	/* Get the real object in case we were fed a hard link obj */+	equiv_obj = yaffs_get_equivalent_obj(equiv_obj);++	if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK,+			parent, name, 0, 0, 0,+			equiv_obj, NULL, 0))+		return equiv_obj;++	return NULL;++}++++/*---------------------- Block Management and Page Allocation -------------*/++static void yaffs_deinit_blocks(struct yaffs_dev *dev)+{+	if (dev->block_info_alt && dev->block_info)+		vfree(dev->block_info);+	else+		kfree(dev->block_info);++	dev->block_info_alt = 0;++	dev->block_info = NULL;++	if (dev->chunk_bits_alt && dev->chunk_bits)+		vfree(dev->chunk_bits);+	else+		kfree(dev->chunk_bits);+	dev->chunk_bits_alt = 0;+	dev->chunk_bits = NULL;+}++static int yaffs_init_blocks(struct yaffs_dev *dev)+{+	int n_blocks = dev->internal_end_block - dev->internal_start_block + 1;++	dev->block_info = NULL;+	dev->chunk_bits = NULL;+	dev->alloc_block = -1;	/* force it to get a new one */++	/* If the first allocation strategy fails, thry the alternate one */+	dev->block_info =+		kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS);+	if (!dev->block_info) {+		dev->block_info =+		    vmalloc(n_blocks * sizeof(struct yaffs_block_info));+		dev->block_info_alt = 1;+	} else {+		dev->block_info_alt = 0;+	}++	if (!dev->block_info)+		goto alloc_error;++	/* Set up dynamic blockinfo stuff. Round up bytes. */+	dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8;+	dev->chunk_bits =+		kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS);+	if (!dev->chunk_bits) {+		dev->chunk_bits =+		    vmalloc(dev->chunk_bit_stride * n_blocks);+		dev->chunk_bits_alt = 1;+	} else {+		dev->chunk_bits_alt = 0;+	}+	if (!dev->chunk_bits)+		goto alloc_error;+++	memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info));+	memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks);+	return YAFFS_OK;++alloc_error:+	yaffs_deinit_blocks(dev);+	return YAFFS_FAIL;+}+++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no)+{+	struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no);+	int erased_ok = 0;+	int i;++	/* If the block is still healthy erase it and mark as clean.+	 * If the block has had a data failure, then retire it.+	 */++	yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE,+		"yaffs_block_became_dirty block %d state %d %s",+		block_no, bi->block_state,+		(bi->needs_retiring) ? "needs retiring" : "");++	yaffs2_clear_oldest_dirty_seq(dev, bi);++	bi->block_state = YAFFS_BLOCK_STATE_DIRTY;++	/* If this is the block being garbage collected then stop gc'ing */+	if (block_no == dev->gc_block)+		dev->gc_block = 0;++	/* If this block is currently the best candidate for gc+	 * then drop as a candidate */+	if (block_no == dev->gc_dirtiest) {+		dev->gc_dirtiest = 0;+		dev->gc_pages_in_use = 0;+	}++	if (!bi->needs_retiring) {+		yaffs2_checkpt_invalidate(dev);+		erased_ok = yaffs_erase_block(dev, block_no);+		if (!erased_ok) {+			dev->n_erase_failures++;+			yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,+			  "**>> Erasure failed %d", block_no);+		}+	}++	/* Verify erasure if needed */+	if (erased_ok &&+	    ((yaffs_trace_mask & YAFFS_TRACE_ERASE) ||+	     !yaffs_skip_verification(dev))) {+		for (i = 0; i < dev->param.chunks_per_block; i++) {+			if (!yaffs_check_chunk_erased(dev,+				block_no * dev->param.chunks_per_block + i)) {+				yaffs_trace(YAFFS_TRACE_ERROR,+					">>Block %d erasure supposedly OK, but chunk %d not erased",+					block_no, i);+			}+		}+	}++	if (!erased_ok) {+		/* We lost a block of free space */+		dev->n_free_chunks -= dev->param.chunks_per_block;+		yaffs_retire_block(dev, block_no);+		yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,+			"**>> Block %d retired", block_no);+		return;+	}++	/* Clean it up... */+	bi->block_state = YAFFS_BLOCK_STATE_EMPTY;+	bi->seq_number = 0;+	dev->n_erased_blocks++;+	bi->pages_in_use = 0;+	bi->soft_del_pages = 0;+	bi->has_shrink_hdr = 0;+	bi->skip_erased_check = 1;	/* Clean, so no need to check */+	bi->gc_prioritise = 0;+	bi->has_summary = 0;++	yaffs_clear_chunk_bits(dev, block_no);++	yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no);+}++static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev,+					struct yaffs_block_info *bi,+					int old_chunk, u8 *buffer)+{+	int new_chunk;+	int mark_flash = 1;+	struct yaffs_ext_tags tags;+	struct yaffs_obj *object;+	int matching_chunk;+	int ret_val = YAFFS_OK;++	memset(&tags, 0, sizeof(tags));+	yaffs_rd_chunk_tags_nand(dev, old_chunk,+				 buffer, &tags);+	object = yaffs_find_by_number(dev, tags.obj_id);++	yaffs_trace(YAFFS_TRACE_GC_DETAIL,+		"Collecting chunk in block %d, %d %d %d ",+		dev->gc_chunk, tags.obj_id,+		tags.chunk_id, tags.n_bytes);++	if (object && !yaffs_skip_verification(dev)) {+		if (tags.chunk_id == 0)+			matching_chunk =+			    object->hdr_chunk;+		else if (object->soft_del)+			/* Defeat the test */+			matching_chunk = old_chunk;+		else+			matching_chunk =+			    yaffs_find_chunk_in_file+			    (object, tags.chunk_id,+			     NULL);++		if (old_chunk != matching_chunk)+			yaffs_trace(YAFFS_TRACE_ERROR,+				"gc: page in gc mismatch: %d %d %d %d",+				old_chunk,+				matching_chunk,+				tags.obj_id,+				tags.chunk_id);+	}++	if (!object) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"page %d in gc has no object: %d %d %d ",+			old_chunk,+			tags.obj_id, tags.chunk_id,+			tags.n_bytes);+	}++	if (object &&+	    object->deleted &&+	    object->soft_del && tags.chunk_id != 0) {+		/* Data chunk in a soft deleted file,+		 * throw it away.+		 * It's a soft deleted data chunk,+		 * No need to copy this, just forget+		 * about it and fix up the object.+		 */++		/* Free chunks already includes+		 * softdeleted chunks, how ever this+		 * chunk is going to soon be really+		 * deleted which will increment free+		 * chunks. We have to decrement free+		 * chunks so this works out properly.+		 */+		dev->n_free_chunks--;+		bi->soft_del_pages--;++		object->n_data_chunks--;+		if (object->n_data_chunks <= 0) {+			/* remeber to clean up obj */+			dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id;+			dev->n_clean_ups++;+		}+		mark_flash = 0;+	} else if (object) {+		/* It's either a data chunk in a live+		 * file or an ObjectHeader, so we're+		 * interested in it.+		 * NB Need to keep the ObjectHeaders of+		 * deleted files until the whole file+		 * has been deleted off+		 */+		tags.serial_number++;+		dev->n_gc_copies++;++		if (tags.chunk_id == 0) {+			/* It is an object Id,+			 * We need to nuke the+			 * shrinkheader flags since its+			 * work is done.+			 * Also need to clean up+			 * shadowing.+			 */+			struct yaffs_obj_hdr *oh;+			oh = (struct yaffs_obj_hdr *) buffer;++			oh->is_shrink = 0;+			tags.extra_is_shrink = 0;+			oh->shadows_obj = 0;+			oh->inband_shadowed_obj_id = 0;+			tags.extra_shadows = 0;++			/* Update file size */+			if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) {+				yaffs_oh_size_load(oh,+				    object->variant.file_variant.file_size);+				tags.extra_file_size =+				    object->variant.file_variant.file_size;+			}++			yaffs_verify_oh(object, oh, &tags, 1);+			new_chunk =+			    yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1);+		} else {+			new_chunk =+			    yaffs_write_new_chunk(dev, buffer, &tags, 1);+		}++		if (new_chunk < 0) {+			ret_val = YAFFS_FAIL;+		} else {++			/* Now fix up the Tnodes etc. */++			if (tags.chunk_id == 0) {+				/* It's a header */+				object->hdr_chunk = new_chunk;+				object->serial = tags.serial_number;+			} else {+				/* It's a data chunk */+				yaffs_put_chunk_in_file(object, tags.chunk_id,+							new_chunk, 0);+			}+		}+	}+	if (ret_val == YAFFS_OK)+		yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__);+	return ret_val;+}++static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block)+{+	int old_chunk;+	int ret_val = YAFFS_OK;+	int i;+	int is_checkpt_block;+	int max_copies;+	int chunks_before = yaffs_get_erased_chunks(dev);+	int chunks_after;+	struct yaffs_block_info *bi = yaffs_get_block_info(dev, block);++	is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT);++	yaffs_trace(YAFFS_TRACE_TRACING,+		"Collecting block %d, in use %d, shrink %d, whole_block %d",+		block, bi->pages_in_use, bi->has_shrink_hdr,+		whole_block);++	/*yaffs_verify_free_chunks(dev); */++	if (bi->block_state == YAFFS_BLOCK_STATE_FULL)+		bi->block_state = YAFFS_BLOCK_STATE_COLLECTING;++	bi->has_shrink_hdr = 0;	/* clear the flag so that the block can erase */++	dev->gc_disable = 1;++	yaffs_summary_gc(dev, block);++	if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) {+		yaffs_trace(YAFFS_TRACE_TRACING,+			"Collecting block %d that has no chunks in use",+			block);+		yaffs_block_became_dirty(dev, block);+	} else {++		u8 *buffer = yaffs_get_temp_buffer(dev);++		yaffs_verify_blk(dev, bi, block);++		max_copies = (whole_block) ? dev->param.chunks_per_block : 5;+		old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk;++		for (/* init already done */ ;+		     ret_val == YAFFS_OK &&+		     dev->gc_chunk < dev->param.chunks_per_block &&+		     (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) &&+		     max_copies > 0;+		     dev->gc_chunk++, old_chunk++) {+			if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) {+				/* Page is in use and might need to be copied */+				max_copies--;+				ret_val = yaffs_gc_process_chunk(dev, bi,+							old_chunk, buffer);+			}+		}+		yaffs_release_temp_buffer(dev, buffer);+	}++	yaffs_verify_collected_blk(dev, bi, block);++	if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) {+		/*+		 * The gc did not complete. Set block state back to FULL+		 * because checkpointing does not restore gc.+		 */+		bi->block_state = YAFFS_BLOCK_STATE_FULL;+	} else {+		/* The gc completed. */+		/* Do any required cleanups */+		for (i = 0; i < dev->n_clean_ups; i++) {+			/* Time to delete the file too */+			struct yaffs_obj *object =+			    yaffs_find_by_number(dev, dev->gc_cleanup_list[i]);+			if (object) {+				yaffs_free_tnode(dev,+					  object->variant.file_variant.top);+				object->variant.file_variant.top = NULL;+				yaffs_trace(YAFFS_TRACE_GC,+					"yaffs: About to finally delete object %d",+					object->obj_id);+				yaffs_generic_obj_del(object);+				object->my_dev->n_deleted_files--;+			}++		}+		chunks_after = yaffs_get_erased_chunks(dev);+		if (chunks_before >= chunks_after)+			yaffs_trace(YAFFS_TRACE_GC,+				"gc did not increase free chunks before %d after %d",+				chunks_before, chunks_after);+		dev->gc_block = 0;+		dev->gc_chunk = 0;+		dev->n_clean_ups = 0;+	}++	dev->gc_disable = 0;++	return ret_val;+}++/*+ * find_gc_block() selects the dirtiest block (or close enough)+ * for garbage collection.+ */++static unsigned yaffs_find_gc_block(struct yaffs_dev *dev,+				    int aggressive, int background)+{+	int i;+	int iterations;+	unsigned selected = 0;+	int prioritised = 0;+	int prioritised_exist = 0;+	struct yaffs_block_info *bi;+	int threshold;++	/* First let's see if we need to grab a prioritised block */+	if (dev->has_pending_prioritised_gc && !aggressive) {+		dev->gc_dirtiest = 0;+		bi = dev->block_info;+		for (i = dev->internal_start_block;+		     i <= dev->internal_end_block && !selected; i++) {++			if (bi->gc_prioritise) {+				prioritised_exist = 1;+				if (bi->block_state == YAFFS_BLOCK_STATE_FULL &&+				    yaffs_block_ok_for_gc(dev, bi)) {+					selected = i;+					prioritised = 1;+				}+			}+			bi++;+		}++		/*+		 * If there is a prioritised block and none was selected then+		 * this happened because there is at least one old dirty block+		 * gumming up the works. Let's gc the oldest dirty block.+		 */++		if (prioritised_exist &&+		    !selected && dev->oldest_dirty_block > 0)+			selected = dev->oldest_dirty_block;++		if (!prioritised_exist)	/* None found, so we can clear this */+			dev->has_pending_prioritised_gc = 0;+	}++	/* If we're doing aggressive GC then we are happy to take a less-dirty+	 * block, and search harder.+	 * else (leasurely gc), then we only bother to do this if the+	 * block has only a few pages in use.+	 */++	if (!selected) {+		int pages_used;+		int n_blocks =+		    dev->internal_end_block - dev->internal_start_block + 1;+		if (aggressive) {+			threshold = dev->param.chunks_per_block;+			iterations = n_blocks;+		} else {+			int max_threshold;++			if (background)+				max_threshold = dev->param.chunks_per_block / 2;+			else+				max_threshold = dev->param.chunks_per_block / 8;++			if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD)+				max_threshold = YAFFS_GC_PASSIVE_THRESHOLD;++			threshold = background ? (dev->gc_not_done + 2) * 2 : 0;+			if (threshold < YAFFS_GC_PASSIVE_THRESHOLD)+				threshold = YAFFS_GC_PASSIVE_THRESHOLD;+			if (threshold > max_threshold)+				threshold = max_threshold;++			iterations = n_blocks / 16 + 1;+			if (iterations > 100)+				iterations = 100;+		}++		for (i = 0;+		     i < iterations &&+		     (dev->gc_dirtiest < 1 ||+		      dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH);+		     i++) {+			dev->gc_block_finder++;+			if (dev->gc_block_finder < dev->internal_start_block ||+			    dev->gc_block_finder > dev->internal_end_block)+				dev->gc_block_finder =+				    dev->internal_start_block;++			bi = yaffs_get_block_info(dev, dev->gc_block_finder);++			pages_used = bi->pages_in_use - bi->soft_del_pages;++			if (bi->block_state == YAFFS_BLOCK_STATE_FULL &&+			    pages_used < dev->param.chunks_per_block &&+			    (dev->gc_dirtiest < 1 ||+			     pages_used < dev->gc_pages_in_use) &&+			    yaffs_block_ok_for_gc(dev, bi)) {+				dev->gc_dirtiest = dev->gc_block_finder;+				dev->gc_pages_in_use = pages_used;+			}+		}++		if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold)+			selected = dev->gc_dirtiest;+	}++	/*+	 * If nothing has been selected for a while, try the oldest dirty+	 * because that's gumming up the works.+	 */++	if (!selected && dev->param.is_yaffs2 &&+	    dev->gc_not_done >= (background ? 10 : 20)) {+		yaffs2_find_oldest_dirty_seq(dev);+		if (dev->oldest_dirty_block > 0) {+			selected = dev->oldest_dirty_block;+			dev->gc_dirtiest = selected;+			dev->oldest_dirty_gc_count++;+			bi = yaffs_get_block_info(dev, selected);+			dev->gc_pages_in_use =+			    bi->pages_in_use - bi->soft_del_pages;+		} else {+			dev->gc_not_done = 0;+		}+	}++	if (selected) {+		yaffs_trace(YAFFS_TRACE_GC,+			"GC Selected block %d with %d free, prioritised:%d",+			selected,+			dev->param.chunks_per_block - dev->gc_pages_in_use,+			prioritised);++		dev->n_gc_blocks++;+		if (background)+			dev->bg_gcs++;++		dev->gc_dirtiest = 0;+		dev->gc_pages_in_use = 0;+		dev->gc_not_done = 0;+		if (dev->refresh_skip > 0)+			dev->refresh_skip--;+	} else {+		dev->gc_not_done++;+		yaffs_trace(YAFFS_TRACE_GC,+			"GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s",+			dev->gc_block_finder, dev->gc_not_done, threshold,+			dev->gc_dirtiest, dev->gc_pages_in_use,+			dev->oldest_dirty_block, background ? " bg" : "");+	}++	return selected;+}++/* New garbage collector+ * If we're very low on erased blocks then we do aggressive garbage collection+ * otherwise we do "leasurely" garbage collection.+ * Aggressive gc looks further (whole array) and will accept less dirty blocks.+ * Passive gc only inspects smaller areas and only accepts more dirty blocks.+ *+ * The idea is to help clear out space in a more spread-out manner.+ * Dunno if it really does anything useful.+ */+static int yaffs_check_gc(struct yaffs_dev *dev, int background)+{+	int aggressive = 0;+	int gc_ok = YAFFS_OK;+	int max_tries = 0;+	int min_erased;+	int erased_chunks;+	int checkpt_block_adjust;++	if (dev->param.gc_control_fn &&+		(dev->param.gc_control_fn(dev) & 1) == 0)+		return YAFFS_OK;++	if (dev->gc_disable)+		/* Bail out so we don't get recursive gc */+		return YAFFS_OK;++	/* This loop should pass the first time.+	 * Only loops here if the collection does not increase space.+	 */++	do {+		max_tries++;++		checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev);++		min_erased =+		    dev->param.n_reserved_blocks + checkpt_block_adjust + 1;+		erased_chunks =+		    dev->n_erased_blocks * dev->param.chunks_per_block;++		/* If we need a block soon then do aggressive gc. */+		if (dev->n_erased_blocks < min_erased)+			aggressive = 1;+		else {+			if (!background+			    && erased_chunks > (dev->n_free_chunks / 4))+				break;++			if (dev->gc_skip > 20)+				dev->gc_skip = 20;+			if (erased_chunks < dev->n_free_chunks / 2 ||+			    dev->gc_skip < 1 || background)+				aggressive = 0;+			else {+				dev->gc_skip--;+				break;+			}+		}++		dev->gc_skip = 5;++		/* If we don't already have a block being gc'd then see if we+		 * should start another */++		if (dev->gc_block < 1 && !aggressive) {+			dev->gc_block = yaffs2_find_refresh_block(dev);+			dev->gc_chunk = 0;+			dev->n_clean_ups = 0;+		}+		if (dev->gc_block < 1) {+			dev->gc_block =+			    yaffs_find_gc_block(dev, aggressive, background);+			dev->gc_chunk = 0;+			dev->n_clean_ups = 0;+		}++		if (dev->gc_block > 0) {+			dev->all_gcs++;+			if (!aggressive)+				dev->passive_gc_count++;++			yaffs_trace(YAFFS_TRACE_GC,+				"yaffs: GC n_erased_blocks %d aggressive %d",+				dev->n_erased_blocks, aggressive);++			gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive);+		}++		if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) &&+		    dev->gc_block > 0) {+			yaffs_trace(YAFFS_TRACE_GC,+				"yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d",+				dev->n_erased_blocks, max_tries,+				dev->gc_block);+		}+	} while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) &&+		 (dev->gc_block > 0) && (max_tries < 2));++	return aggressive ? gc_ok : YAFFS_OK;+}++/*+ * yaffs_bg_gc()+ * Garbage collects. Intended to be called from a background thread.+ * Returns non-zero if at least half the free chunks are erased.+ */+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency)+{+	int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block;++	yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency);++	yaffs_check_gc(dev, 1);+	return erased_chunks > dev->n_free_chunks / 2;+}++/*-------------------- Data file manipulation -----------------*/++static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer)+{+	int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL);++	if (nand_chunk >= 0)+		return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk,+						buffer, NULL);+	else {+		yaffs_trace(YAFFS_TRACE_NANDACCESS,+			"Chunk %d not found zero instead",+			nand_chunk);+		/* get sane (zero) data if you read a hole */+		memset(buffer, 0, in->my_dev->data_bytes_per_chunk);+		return 0;+	}++}++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash,+		     int lyn)+{+	int block;+	int page;+	struct yaffs_ext_tags tags;+	struct yaffs_block_info *bi;++	if (chunk_id <= 0)+		return;++	dev->n_deletions++;+	block = chunk_id / dev->param.chunks_per_block;+	page = chunk_id % dev->param.chunks_per_block;++	if (!yaffs_check_chunk_bit(dev, block, page))+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Deleting invalid chunk %d", chunk_id);++	bi = yaffs_get_block_info(dev, block);++	yaffs2_update_oldest_dirty_seq(dev, block, bi);++	yaffs_trace(YAFFS_TRACE_DELETION,+		"line %d delete of chunk %d",+		lyn, chunk_id);++	if (!dev->param.is_yaffs2 && mark_flash &&+	    bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) {++		memset(&tags, 0, sizeof(tags));+		tags.is_deleted = 1;+		yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags);+		yaffs_handle_chunk_update(dev, chunk_id, &tags);+	} else {+		dev->n_unmarked_deletions++;+	}++	/* Pull out of the management area.+	 * If the whole block became dirty, this will kick off an erasure.+	 */+	if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING ||+	    bi->block_state == YAFFS_BLOCK_STATE_FULL ||+	    bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||+	    bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) {+		dev->n_free_chunks++;+		yaffs_clear_chunk_bit(dev, block, page);+		bi->pages_in_use--;++		if (bi->pages_in_use == 0 &&+		    !bi->has_shrink_hdr &&+		    bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING &&+		    bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) {+			yaffs_block_became_dirty(dev, block);+		}+	}+}++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk,+			     const u8 *buffer, int n_bytes, int use_reserve)+{+	/* Find old chunk Need to do this to get serial number+	 * Write new one and patch into tree.+	 * Invalidate old tags.+	 */++	int prev_chunk_id;+	struct yaffs_ext_tags prev_tags;+	int new_chunk_id;+	struct yaffs_ext_tags new_tags;+	struct yaffs_dev *dev = in->my_dev;++	yaffs_check_gc(dev, 0);++	/* Get the previous chunk at this location in the file if it exists.+	 * If it does not exist then put a zero into the tree. This creates+	 * the tnode now, rather than later when it is harder to clean up.+	 */+	prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags);+	if (prev_chunk_id < 1 &&+	    !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0))+		return 0;++	/* Set up new tags */+	memset(&new_tags, 0, sizeof(new_tags));++	new_tags.chunk_id = inode_chunk;+	new_tags.obj_id = in->obj_id;+	new_tags.serial_number =+	    (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1;+	new_tags.n_bytes = n_bytes;++	if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) {+		yaffs_trace(YAFFS_TRACE_ERROR,+		  "Writing %d bytes to chunk!!!!!!!!!",+		   n_bytes);+		BUG();+	}++	new_chunk_id =+	    yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve);++	if (new_chunk_id > 0) {+		yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0);++		if (prev_chunk_id > 0)+			yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__);++		yaffs_verify_file_sane(in);+	}+	return new_chunk_id;++}++++static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set,+				const YCHAR *name, const void *value, int size,+				int flags)+{+	struct yaffs_xattr_mod xmod;+	int result;++	xmod.set = set;+	xmod.name = name;+	xmod.data = value;+	xmod.size = size;+	xmod.flags = flags;+	xmod.result = -ENOSPC;++	result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod);++	if (result > 0)+		return xmod.result;+	else+		return -ENOSPC;+}++static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer,+				   struct yaffs_xattr_mod *xmod)+{+	int retval = 0;+	int x_offs = sizeof(struct yaffs_obj_hdr);+	struct yaffs_dev *dev = obj->my_dev;+	int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr);+	char *x_buffer = buffer + x_offs;++	if (xmod->set)+		retval =+		    nval_set(x_buffer, x_size, xmod->name, xmod->data,+			     xmod->size, xmod->flags);+	else+		retval = nval_del(x_buffer, x_size, xmod->name);++	obj->has_xattr = nval_hasvalues(x_buffer, x_size);+	obj->xattr_known = 1;+	xmod->result = retval;++	return retval;+}++static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name,+				  void *value, int size)+{+	char *buffer = NULL;+	int result;+	struct yaffs_ext_tags tags;+	struct yaffs_dev *dev = obj->my_dev;+	int x_offs = sizeof(struct yaffs_obj_hdr);+	int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr);+	char *x_buffer;+	int retval = 0;++	if (obj->hdr_chunk < 1)+		return -ENODATA;++	/* If we know that the object has no xattribs then don't do all the+	 * reading and parsing.+	 */+	if (obj->xattr_known && !obj->has_xattr) {+		if (name)+			return -ENODATA;+		else+			return 0;+	}++	buffer = (char *)yaffs_get_temp_buffer(dev);+	if (!buffer)+		return -ENOMEM;++	result =+	    yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags);++	if (result != YAFFS_OK)+		retval = -ENOENT;+	else {+		x_buffer = buffer + x_offs;++		if (!obj->xattr_known) {+			obj->has_xattr = nval_hasvalues(x_buffer, x_size);+			obj->xattr_known = 1;+		}++		if (name)+			retval = nval_get(x_buffer, x_size, name, value, size);+		else+			retval = nval_list(x_buffer, x_size, value, size);+	}+	yaffs_release_temp_buffer(dev, (u8 *) buffer);+	return retval;+}++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name,+		      const void *value, int size, int flags)+{+	return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags);+}++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name)+{+	return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0);+}++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value,+		      int size)+{+	return yaffs_do_xattrib_fetch(obj, name, value, size);+}++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size)+{+	return yaffs_do_xattrib_fetch(obj, NULL, buffer, size);+}++static void yaffs_check_obj_details_loaded(struct yaffs_obj *in)+{+	u8 *buf;+	struct yaffs_obj_hdr *oh;+	struct yaffs_dev *dev;+	struct yaffs_ext_tags tags;+	int result;+	int alloc_failed = 0;++	if (!in || !in->lazy_loaded || in->hdr_chunk < 1)+		return;++	dev = in->my_dev;+	in->lazy_loaded = 0;+	buf = yaffs_get_temp_buffer(dev);++	result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags);+	oh = (struct yaffs_obj_hdr *)buf;++	in->yst_mode = oh->yst_mode;+	yaffs_load_attribs(in, oh);+	yaffs_set_obj_name_from_oh(in, oh);++	if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) {+		in->variant.symlink_variant.alias =+		    yaffs_clone_str(oh->alias);+		if (!in->variant.symlink_variant.alias)+			alloc_failed = 1;	/* Not returned */+	}+	yaffs_release_temp_buffer(dev, buf);+}++static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name,+				    const YCHAR *oh_name, int buff_size)+{+#ifdef CONFIG_YAFFS_AUTO_UNICODE+	if (dev->param.auto_unicode) {+		if (*oh_name) {+			/* It is an ASCII name, do an ASCII to+			 * unicode conversion */+			const char *ascii_oh_name = (const char *)oh_name;+			int n = buff_size - 1;+			while (n > 0 && *ascii_oh_name) {+				*name = *ascii_oh_name;+				name++;+				ascii_oh_name++;+				n--;+			}+		} else {+			strncpy(name, oh_name + 1, buff_size - 1);+		}+	} else {+#else+	(void) dev;+	{+#endif+		strncpy(name, oh_name, buff_size - 1);+	}+}++static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name,+				    const YCHAR *name)+{+#ifdef CONFIG_YAFFS_AUTO_UNICODE++	int is_ascii;+	YCHAR *w;++	if (dev->param.auto_unicode) {++		is_ascii = 1;+		w = name;++		/* Figure out if the name will fit in ascii character set */+		while (is_ascii && *w) {+			if ((*w) & 0xff00)+				is_ascii = 0;+			w++;+		}++		if (is_ascii) {+			/* It is an ASCII name, so convert unicode to ascii */+			char *ascii_oh_name = (char *)oh_name;+			int n = YAFFS_MAX_NAME_LENGTH - 1;+			while (n > 0 && *name) {+				*ascii_oh_name = *name;+				name++;+				ascii_oh_name++;+				n--;+			}+		} else {+			/* Unicode name, so save starting at the second YCHAR */+			*oh_name = 0;+			strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2);+		}+	} else {+#else+	dev = dev;+	{+#endif+		strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1);+	}+}++/* UpdateObjectHeader updates the header on NAND for an object.+ * If name is not NULL, then that new name is used.+ */+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force,+		    int is_shrink, int shadows, struct yaffs_xattr_mod *xmod)+{++	struct yaffs_block_info *bi;+	struct yaffs_dev *dev = in->my_dev;+	int prev_chunk_id;+	int ret_val = 0;+	int result = 0;+	int new_chunk_id;+	struct yaffs_ext_tags new_tags;+	struct yaffs_ext_tags old_tags;+	const YCHAR *alias = NULL;+	u8 *buffer = NULL;+	YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1];+	struct yaffs_obj_hdr *oh = NULL;+	loff_t file_size = 0;++	strcpy(old_name, _Y("silly old name"));++	if (in->fake && in != dev->root_dir && !force && !xmod)+		return ret_val;++	yaffs_check_gc(dev, 0);+	yaffs_check_obj_details_loaded(in);++	buffer = yaffs_get_temp_buffer(in->my_dev);+	oh = (struct yaffs_obj_hdr *)buffer;++	prev_chunk_id = in->hdr_chunk;++	if (prev_chunk_id > 0) {+		result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id,+						  buffer, &old_tags);++		yaffs_verify_oh(in, oh, &old_tags, 0);+		memcpy(old_name, oh->name, sizeof(oh->name));+		memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr));+	} else {+		memset(buffer, 0xff, dev->data_bytes_per_chunk);+	}++	oh->type = in->variant_type;+	oh->yst_mode = in->yst_mode;+	oh->shadows_obj = oh->inband_shadowed_obj_id = shadows;++	yaffs_load_attribs_oh(oh, in);++	if (in->parent)+		oh->parent_obj_id = in->parent->obj_id;+	else+		oh->parent_obj_id = 0;++	if (name && *name) {+		memset(oh->name, 0, sizeof(oh->name));+		yaffs_load_oh_from_name(dev, oh->name, name);+	} else if (prev_chunk_id > 0) {+		memcpy(oh->name, old_name, sizeof(oh->name));+	} else {+		memset(oh->name, 0, sizeof(oh->name));+	}++	oh->is_shrink = is_shrink;++	switch (in->variant_type) {+	case YAFFS_OBJECT_TYPE_UNKNOWN:+		/* Should not happen */+		break;+	case YAFFS_OBJECT_TYPE_FILE:+		if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED &&+		    oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED)+			file_size = in->variant.file_variant.file_size;+		yaffs_oh_size_load(oh, file_size);+		break;+	case YAFFS_OBJECT_TYPE_HARDLINK:+		oh->equiv_id = in->variant.hardlink_variant.equiv_id;+		break;+	case YAFFS_OBJECT_TYPE_SPECIAL:+		/* Do nothing */+		break;+	case YAFFS_OBJECT_TYPE_DIRECTORY:+		/* Do nothing */+		break;+	case YAFFS_OBJECT_TYPE_SYMLINK:+		alias = in->variant.symlink_variant.alias;+		if (!alias)+			alias = _Y("no alias");+		strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH);+		oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;+		break;+	}++	/* process any xattrib modifications */+	if (xmod)+		yaffs_apply_xattrib_mod(in, (char *)buffer, xmod);++	/* Tags */+	memset(&new_tags, 0, sizeof(new_tags));+	in->serial++;+	new_tags.chunk_id = 0;+	new_tags.obj_id = in->obj_id;+	new_tags.serial_number = in->serial;++	/* Add extra info for file header */+	new_tags.extra_available = 1;+	new_tags.extra_parent_id = oh->parent_obj_id;+	new_tags.extra_file_size = file_size;+	new_tags.extra_is_shrink = oh->is_shrink;+	new_tags.extra_equiv_id = oh->equiv_id;+	new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0;+	new_tags.extra_obj_type = in->variant_type;+	yaffs_verify_oh(in, oh, &new_tags, 1);++	/* Create new chunk in NAND */+	new_chunk_id =+	    yaffs_write_new_chunk(dev, buffer, &new_tags,+				  (prev_chunk_id > 0) ? 1 : 0);++	if (buffer)+		yaffs_release_temp_buffer(dev, buffer);++	if (new_chunk_id < 0)+		return new_chunk_id;++	in->hdr_chunk = new_chunk_id;++	if (prev_chunk_id > 0)+		yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__);++	if (!yaffs_obj_cache_dirty(in))+		in->dirty = 0;++	/* If this was a shrink, then mark the block+	 * that the chunk lives on */+	if (is_shrink) {+		bi = yaffs_get_block_info(in->my_dev,+					  new_chunk_id /+					  in->my_dev->param.chunks_per_block);+		bi->has_shrink_hdr = 1;+	}+++	return new_chunk_id;+}++/*--------------------- File read/write ------------------------+ * Read and write have very similar structures.+ * In general the read/write has three parts to it+ * An incomplete chunk to start with (if the read/write is not chunk-aligned)+ * Some complete chunks+ * An incomplete chunk to end off with+ *+ * Curve-balls: the first chunk might also be the last chunk.+ */++int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes)+{+	int chunk;+	u32 start;+	int n_copy;+	int n = n_bytes;+	int n_done = 0;+	struct yaffs_cache *cache;+	struct yaffs_dev *dev;++	dev = in->my_dev;++	while (n > 0) {+		yaffs_addr_to_chunk(dev, offset, &chunk, &start);+		chunk++;++		/* OK now check for the curveball where the start and end are in+		 * the same chunk.+		 */+		if ((start + n) < dev->data_bytes_per_chunk)+			n_copy = n;+		else+			n_copy = dev->data_bytes_per_chunk - start;++		cache = yaffs_find_chunk_cache(in, chunk);++		/* If the chunk is already in the cache or it is less than+		 * a whole chunk or we're using inband tags then use the cache+		 * (if there is caching) else bypass the cache.+		 */+		if (cache || n_copy != dev->data_bytes_per_chunk ||+		    dev->param.inband_tags) {+			if (dev->param.n_caches > 0) {++				/* If we can't find the data in the cache,+				 * then load it up. */++				if (!cache) {+					cache =+					    yaffs_grab_chunk_cache(in->my_dev);+					cache->object = in;+					cache->chunk_id = chunk;+					cache->dirty = 0;+					cache->locked = 0;+					yaffs_rd_data_obj(in, chunk,+							  cache->data);+					cache->n_bytes = 0;+				}++				yaffs_use_cache(dev, cache, 0);++				cache->locked = 1;++				memcpy(buffer, &cache->data[start], n_copy);++				cache->locked = 0;+			} else {+				/* Read into the local buffer then copy.. */++				u8 *local_buffer =+				    yaffs_get_temp_buffer(dev);+				yaffs_rd_data_obj(in, chunk, local_buffer);++				memcpy(buffer, &local_buffer[start], n_copy);++				yaffs_release_temp_buffer(dev, local_buffer);+			}+		} else {+			/* A full chunk. Read directly into the buffer. */+			yaffs_rd_data_obj(in, chunk, buffer);+		}+		n -= n_copy;+		offset += n_copy;+		buffer += n_copy;+		n_done += n_copy;+	}+	return n_done;+}++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset,+		     int n_bytes, int write_through)+{++	int chunk;+	u32 start;+	int n_copy;+	int n = n_bytes;+	int n_done = 0;+	int n_writeback;+	loff_t start_write = offset;+	int chunk_written = 0;+	u32 n_bytes_read;+	loff_t chunk_start;+	struct yaffs_dev *dev;++	dev = in->my_dev;++	while (n > 0 && chunk_written >= 0) {+		yaffs_addr_to_chunk(dev, offset, &chunk, &start);++		if (((loff_t)chunk) *+		    dev->data_bytes_per_chunk + start != offset ||+		    start >= dev->data_bytes_per_chunk) {+			yaffs_trace(YAFFS_TRACE_ERROR,+				"AddrToChunk of offset %lld gives chunk %d start %d",+				offset, chunk, start);+		}+		chunk++;	/* File pos to chunk in file offset */++		/* OK now check for the curveball where the start and end are in+		 * the same chunk.+		 */++		if ((start + n) < dev->data_bytes_per_chunk) {+			n_copy = n;++			/* Now calculate how many bytes to write back....+			 * If we're overwriting and not writing to then end of+			 * file then we need to write back as much as was there+			 * before.+			 */++			chunk_start = (((loff_t)(chunk - 1)) *+					dev->data_bytes_per_chunk);++			if (chunk_start > in->variant.file_variant.file_size)+				n_bytes_read = 0;	/* Past end of file */+			else+				n_bytes_read =+				    in->variant.file_variant.file_size -+				    chunk_start;++			if (n_bytes_read > dev->data_bytes_per_chunk)+				n_bytes_read = dev->data_bytes_per_chunk;++			n_writeback =+			    (n_bytes_read >+			     (start + n)) ? n_bytes_read : (start + n);++			if (n_writeback < 0 ||+			    n_writeback > dev->data_bytes_per_chunk)+				BUG();++		} else {+			n_copy = dev->data_bytes_per_chunk - start;+			n_writeback = dev->data_bytes_per_chunk;+		}++		if (n_copy != dev->data_bytes_per_chunk ||+		    !dev->param.cache_bypass_aligned ||+		    dev->param.inband_tags) {+			/* An incomplete start or end chunk (or maybe both+			 * start and end chunk), or we're using inband tags,+			 * or we're forcing writes through the cache,+			 * so we want to use the cache buffers.+			 */+			if (dev->param.n_caches > 0) {+				struct yaffs_cache *cache;++				/* If we can't find the data in the cache, then+				 * load the cache */+				cache = yaffs_find_chunk_cache(in, chunk);++				if (!cache &&+				    yaffs_check_alloc_available(dev, 1)) {+					cache = yaffs_grab_chunk_cache(dev);+					cache->object = in;+					cache->chunk_id = chunk;+					cache->dirty = 0;+					cache->locked = 0;+					yaffs_rd_data_obj(in, chunk,+							  cache->data);+				} else if (cache &&+					   !cache->dirty &&+					   !yaffs_check_alloc_available(dev,+									1)) {+					/* Drop the cache if it was a read cache+					 * item and no space check has been made+					 * for it.+					 */+					cache = NULL;+				}++				if (cache) {+					yaffs_use_cache(dev, cache, 1);+					cache->locked = 1;++					memcpy(&cache->data[start], buffer,+					       n_copy);++					cache->locked = 0;+					cache->n_bytes = n_writeback;++					if (write_through) {+						chunk_written =+						    yaffs_wr_data_obj+						    (cache->object,+						     cache->chunk_id,+						     cache->data,+						     cache->n_bytes, 1);+						cache->dirty = 0;+					}+				} else {+					chunk_written = -1;	/* fail write */+				}+			} else {+				/* An incomplete start or end chunk (or maybe+				 * both start and end chunk). Read into the+				 * local buffer then copy over and write back.+				 */++				u8 *local_buffer = yaffs_get_temp_buffer(dev);++				yaffs_rd_data_obj(in, chunk, local_buffer);+				memcpy(&local_buffer[start], buffer, n_copy);++				chunk_written =+				    yaffs_wr_data_obj(in, chunk,+						      local_buffer,+						      n_writeback, 0);++				yaffs_release_temp_buffer(dev, local_buffer);+			}+		} else {+			/* A full chunk. Write directly from the buffer. */++			chunk_written =+			    yaffs_wr_data_obj(in, chunk, buffer,+					      dev->data_bytes_per_chunk, 0);++			/* Since we've overwritten the cached data,+			 * we better invalidate it. */+			yaffs_invalidate_chunk_cache(in, chunk);+		}++		if (chunk_written >= 0) {+			n -= n_copy;+			offset += n_copy;+			buffer += n_copy;+			n_done += n_copy;+		}+	}++	/* Update file object */++	if ((start_write + n_done) > in->variant.file_variant.file_size)+		in->variant.file_variant.file_size = (start_write + n_done);++	in->dirty = 1;+	return n_done;+}++int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset,+		  int n_bytes, int write_through)+{+	yaffs2_handle_hole(in, offset);+	return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through);+}++/* ---------------------- File resizing stuff ------------------ */++static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size)+{++	struct yaffs_dev *dev = in->my_dev;+	loff_t old_size = in->variant.file_variant.file_size;+	int i;+	int chunk_id;+	u32 dummy;+	int last_del;+	int start_del;++	if (old_size > 0)+		yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy);+	else+		last_del = 0;++	yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1,+				&start_del, &dummy);+	last_del++;+	start_del++;++	/* Delete backwards so that we don't end up with holes if+	 * power is lost part-way through the operation.+	 */+	for (i = last_del; i >= start_del; i--) {+		/* NB this could be optimised somewhat,+		 * eg. could retrieve the tags and write them without+		 * using yaffs_chunk_del+		 */++		chunk_id = yaffs_find_del_file_chunk(in, i, NULL);++		if (chunk_id < 1)+			continue;++		if (chunk_id <+		    (dev->internal_start_block * dev->param.chunks_per_block) ||+		    chunk_id >=+		    ((dev->internal_end_block + 1) *+		      dev->param.chunks_per_block)) {+			yaffs_trace(YAFFS_TRACE_ALWAYS,+				"Found daft chunk_id %d for %d",+				chunk_id, i);+		} else {+			in->n_data_chunks--;+			yaffs_chunk_del(dev, chunk_id, 1, __LINE__);+		}+	}+}++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size)+{+	int new_full;+	u32 new_partial;+	struct yaffs_dev *dev = obj->my_dev;++	yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial);++	yaffs_prune_chunks(obj, new_size);++	if (new_partial != 0) {+		int last_chunk = 1 + new_full;+		u8 *local_buffer = yaffs_get_temp_buffer(dev);++		/* Rewrite the last chunk with its new size and zero pad */+		yaffs_rd_data_obj(obj, last_chunk, local_buffer);+		memset(local_buffer + new_partial, 0,+		       dev->data_bytes_per_chunk - new_partial);++		yaffs_wr_data_obj(obj, last_chunk, local_buffer,+				  new_partial, 1);++		yaffs_release_temp_buffer(dev, local_buffer);+	}++	obj->variant.file_variant.file_size = new_size;++	yaffs_prune_tree(dev, &obj->variant.file_variant);+}++int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size)+{+	struct yaffs_dev *dev = in->my_dev;+	loff_t old_size = in->variant.file_variant.file_size;++	yaffs_flush_file_cache(in);+	yaffs_invalidate_whole_cache(in);++	yaffs_check_gc(dev, 0);++	if (in->variant_type != YAFFS_OBJECT_TYPE_FILE)+		return YAFFS_FAIL;++	if (new_size == old_size)+		return YAFFS_OK;++	if (new_size > old_size) {+		yaffs2_handle_hole(in, new_size);+		in->variant.file_variant.file_size = new_size;+	} else {+		/* new_size < old_size */+		yaffs_resize_file_down(in, new_size);+	}++	/* Write a new object header to reflect the resize.+	 * show we've shrunk the file, if need be+	 * Do this only if the file is not in the deleted directories+	 * and is not shadowed.+	 */+	if (in->parent &&+	    !in->is_shadowed &&+	    in->parent->obj_id != YAFFS_OBJECTID_UNLINKED &&+	    in->parent->obj_id != YAFFS_OBJECTID_DELETED)+		yaffs_update_oh(in, NULL, 0, 0, 0, NULL);++	return YAFFS_OK;+}++int yaffs_flush_file(struct yaffs_obj *in, int update_time, int data_sync)+{+	if (!in->dirty)+		return YAFFS_OK;++	yaffs_flush_file_cache(in);++	if (data_sync)+		return YAFFS_OK;++	if (update_time)+		yaffs_load_current_time(in, 0, 0);++	return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ?+				YAFFS_OK : YAFFS_FAIL;+}+++/* yaffs_del_file deletes the whole file data+ * and the inode associated with the file.+ * It does not delete the links associated with the file.+ */+static int yaffs_unlink_file_if_needed(struct yaffs_obj *in)+{+	int ret_val;+	int del_now = 0;+	struct yaffs_dev *dev = in->my_dev;++	if (!in->my_inode)+		del_now = 1;++	if (del_now) {+		ret_val =+		    yaffs_change_obj_name(in, in->my_dev->del_dir,+					  _Y("deleted"), 0, 0);+		yaffs_trace(YAFFS_TRACE_TRACING,+			"yaffs: immediate deletion of file %d",+			in->obj_id);+		in->deleted = 1;+		in->my_dev->n_deleted_files++;+		if (dev->param.disable_soft_del || dev->param.is_yaffs2)+			yaffs_resize_file(in, 0);+		yaffs_soft_del_file(in);+	} else {+		ret_val =+		    yaffs_change_obj_name(in, in->my_dev->unlinked_dir,+					  _Y("unlinked"), 0, 0);+	}+	return ret_val;+}++static int yaffs_del_file(struct yaffs_obj *in)+{+	int ret_val = YAFFS_OK;+	int deleted;	/* Need to cache value on stack if in is freed */+	struct yaffs_dev *dev = in->my_dev;++	if (dev->param.disable_soft_del || dev->param.is_yaffs2)+		yaffs_resize_file(in, 0);++	if (in->n_data_chunks > 0) {+		/* Use soft deletion if there is data in the file.+		 * That won't be the case if it has been resized to zero.+		 */+		if (!in->unlinked)+			ret_val = yaffs_unlink_file_if_needed(in);++		deleted = in->deleted;++		if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) {+			in->deleted = 1;+			deleted = 1;+			in->my_dev->n_deleted_files++;+			yaffs_soft_del_file(in);+		}+		return deleted ? YAFFS_OK : YAFFS_FAIL;+	} else {+		/* The file has no data chunks so we toss it immediately */+		yaffs_free_tnode(in->my_dev, in->variant.file_variant.top);+		in->variant.file_variant.top = NULL;+		yaffs_generic_obj_del(in);++		return YAFFS_OK;+	}+}++int yaffs_is_non_empty_dir(struct yaffs_obj *obj)+{+	return (obj &&+		obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) &&+		!(list_empty(&obj->variant.dir_variant.children));+}++static int yaffs_del_dir(struct yaffs_obj *obj)+{+	/* First check that the directory is empty. */+	if (yaffs_is_non_empty_dir(obj))+		return YAFFS_FAIL;++	return yaffs_generic_obj_del(obj);+}++static int yaffs_del_symlink(struct yaffs_obj *in)+{+	kfree(in->variant.symlink_variant.alias);+	in->variant.symlink_variant.alias = NULL;++	return yaffs_generic_obj_del(in);+}++static int yaffs_del_link(struct yaffs_obj *in)+{+	/* remove this hardlink from the list associated with the equivalent+	 * object+	 */+	list_del_init(&in->hard_links);+	return yaffs_generic_obj_del(in);+}++int yaffs_del_obj(struct yaffs_obj *obj)+{+	int ret_val = -1;++	switch (obj->variant_type) {+	case YAFFS_OBJECT_TYPE_FILE:+		ret_val = yaffs_del_file(obj);+		break;+	case YAFFS_OBJECT_TYPE_DIRECTORY:+		if (!list_empty(&obj->variant.dir_variant.dirty)) {+			yaffs_trace(YAFFS_TRACE_BACKGROUND,+				"Remove object %d from dirty directories",+				obj->obj_id);+			list_del_init(&obj->variant.dir_variant.dirty);+		}+		return yaffs_del_dir(obj);+		break;+	case YAFFS_OBJECT_TYPE_SYMLINK:+		ret_val = yaffs_del_symlink(obj);+		break;+	case YAFFS_OBJECT_TYPE_HARDLINK:+		ret_val = yaffs_del_link(obj);+		break;+	case YAFFS_OBJECT_TYPE_SPECIAL:+		ret_val = yaffs_generic_obj_del(obj);+		break;+	case YAFFS_OBJECT_TYPE_UNKNOWN:+		ret_val = 0;+		break;		/* should not happen. */+	}+	return ret_val;+}+++static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir,+				   struct yaffs_obj *to_dir)+{+	struct yaffs_obj *obj;+	struct list_head *lh;+	struct list_head *n;++	list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) {+		obj = list_entry(lh, struct yaffs_obj, siblings);+		yaffs_add_obj_to_dir(to_dir, obj);+	}+}++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj,+				   enum yaffs_obj_type type)+{+	/* Tear down the old variant */+	switch (obj->variant_type) {+	case YAFFS_OBJECT_TYPE_FILE:+		/* Nuke file data */+		yaffs_resize_file(obj, 0);+		yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top);+		obj->variant.file_variant.top = NULL;+		break;+	case YAFFS_OBJECT_TYPE_DIRECTORY:+		/* Put the children in lost and found. */+		yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found);+		if (!list_empty(&obj->variant.dir_variant.dirty))+			list_del_init(&obj->variant.dir_variant.dirty);+		break;+	case YAFFS_OBJECT_TYPE_SYMLINK:+		/* Nuke symplink data */+		kfree(obj->variant.symlink_variant.alias);+		obj->variant.symlink_variant.alias = NULL;+		break;+	case YAFFS_OBJECT_TYPE_HARDLINK:+		list_del_init(&obj->hard_links);+		break;+	default:+		break;+	}++	memset(&obj->variant, 0, sizeof(obj->variant));++	/*Set up new variant if the memset is not enough. */+	switch (type) {+	case YAFFS_OBJECT_TYPE_DIRECTORY:+		INIT_LIST_HEAD(&obj->variant.dir_variant.children);+		INIT_LIST_HEAD(&obj->variant.dir_variant.dirty);+		break;+	case YAFFS_OBJECT_TYPE_FILE:+	case YAFFS_OBJECT_TYPE_SYMLINK:+	case YAFFS_OBJECT_TYPE_HARDLINK:+	default:+		break;+	}++	obj->variant_type = type;++	return obj;++}++static int yaffs_unlink_worker(struct yaffs_obj *obj)+{+	int del_now = 0;++	if (!obj)+		return YAFFS_FAIL;++	if (!obj->my_inode)+		del_now = 1;++	yaffs_update_parent(obj->parent);++	if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) {+		return yaffs_del_link(obj);+	} else if (!list_empty(&obj->hard_links)) {+		/* Curve ball: We're unlinking an object that has a hardlink.+		 *+		 * This problem arises because we are not strictly following+		 * The Linux link/inode model.+		 *+		 * We can't really delete the object.+		 * Instead, we do the following:+		 * - Select a hardlink.+		 * - Unhook it from the hard links+		 * - Move it from its parent directory so that the rename works.+		 * - Rename the object to the hardlink's name.+		 * - Delete the hardlink+		 */++		struct yaffs_obj *hl;+		struct yaffs_obj *parent;+		int ret_val;+		YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];++		hl = list_entry(obj->hard_links.next, struct yaffs_obj,+				hard_links);++		yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1);+		parent = hl->parent;++		list_del_init(&hl->hard_links);++		yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl);++		ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0);++		if (ret_val == YAFFS_OK)+			ret_val = yaffs_generic_obj_del(hl);++		return ret_val;++	} else if (del_now) {+		switch (obj->variant_type) {+		case YAFFS_OBJECT_TYPE_FILE:+			return yaffs_del_file(obj);+			break;+		case YAFFS_OBJECT_TYPE_DIRECTORY:+			list_del_init(&obj->variant.dir_variant.dirty);+			return yaffs_del_dir(obj);+			break;+		case YAFFS_OBJECT_TYPE_SYMLINK:+			return yaffs_del_symlink(obj);+			break;+		case YAFFS_OBJECT_TYPE_SPECIAL:+			return yaffs_generic_obj_del(obj);+			break;+		case YAFFS_OBJECT_TYPE_HARDLINK:+		case YAFFS_OBJECT_TYPE_UNKNOWN:+		default:+			return YAFFS_FAIL;+		}+	} else if (yaffs_is_non_empty_dir(obj)) {+		return YAFFS_FAIL;+	} else {+		return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir,+						_Y("unlinked"), 0, 0);+	}+}++static int yaffs_unlink_obj(struct yaffs_obj *obj)+{+	if (obj && obj->unlink_allowed)+		return yaffs_unlink_worker(obj);++	return YAFFS_FAIL;+}++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name)+{+	struct yaffs_obj *obj;++	obj = yaffs_find_by_name(dir, name);+	return yaffs_unlink_obj(obj);+}++/* Note:+ * If old_name is NULL then we take old_dir as the object to be renamed.+ */+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name,+		     struct yaffs_obj *new_dir, const YCHAR *new_name)+{+	struct yaffs_obj *obj = NULL;+	struct yaffs_obj *existing_target = NULL;+	int force = 0;+	int result;+	struct yaffs_dev *dev;++	if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {+		BUG();+		return YAFFS_FAIL;+	}+	if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {+		BUG();+		return YAFFS_FAIL;+	}++	dev = old_dir->my_dev;++#ifdef CONFIG_YAFFS_CASE_INSENSITIVE+	/* Special case for case insemsitive systems.+	 * While look-up is case insensitive, the name isn't.+	 * Therefore we might want to change x.txt to X.txt+	 */+	if (old_dir == new_dir &&+		old_name && new_name &&+		strcmp(old_name, new_name) == 0)+		force = 1;+#endif++	if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) >+	    YAFFS_MAX_NAME_LENGTH)+		/* ENAMETOOLONG */+		return YAFFS_FAIL;++	if (old_name)+		obj = yaffs_find_by_name(old_dir, old_name);+	else{+		obj = old_dir;+		old_dir = obj->parent;+	}++	if (obj && obj->rename_allowed) {+		/* Now handle an existing target, if there is one */+		existing_target = yaffs_find_by_name(new_dir, new_name);+		if (yaffs_is_non_empty_dir(existing_target)) {+			return YAFFS_FAIL;	/* ENOTEMPTY */+		} else if (existing_target && existing_target != obj) {+			/* Nuke the target first, using shadowing,+			 * but only if it isn't the same object.+			 *+			 * Note we must disable gc here otherwise it can mess+			 * up the shadowing.+			 *+			 */+			dev->gc_disable = 1;+			yaffs_change_obj_name(obj, new_dir, new_name, force,+					      existing_target->obj_id);+			existing_target->is_shadowed = 1;+			yaffs_unlink_obj(existing_target);+			dev->gc_disable = 0;+		}++		result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0);++		yaffs_update_parent(old_dir);+		if (new_dir != old_dir)+			yaffs_update_parent(new_dir);++		return result;+	}+	return YAFFS_FAIL;+}++/*----------------------- Initialisation Scanning ---------------------- */++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id,+			       int backward_scanning)+{+	struct yaffs_obj *obj;++	if (backward_scanning) {+		/* Handle YAFFS2 case (backward scanning)+		 * If the shadowed object exists then ignore.+		 */+		obj = yaffs_find_by_number(dev, obj_id);+		if (obj)+			return;+	}++	/* Let's create it (if it does not exist) assuming it is a file so that+	 * it can do shrinking etc.+	 * We put it in unlinked dir to be cleaned up after the scanning+	 */+	obj =+	    yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE);+	if (!obj)+		return;+	obj->is_shadowed = 1;+	yaffs_add_obj_to_dir(dev->unlinked_dir, obj);+	obj->variant.file_variant.shrink_size = 0;+	obj->valid = 1;		/* So that we don't read any other info. */+}++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list)+{+	struct list_head *lh;+	struct list_head *save;+	struct yaffs_obj *hl;+	struct yaffs_obj *in;++	list_for_each_safe(lh, save, hard_list) {+		hl = list_entry(lh, struct yaffs_obj, hard_links);+		in = yaffs_find_by_number(dev,+					hl->variant.hardlink_variant.equiv_id);++		if (in) {+			/* Add the hardlink pointers */+			hl->variant.hardlink_variant.equiv_obj = in;+			list_add(&hl->hard_links, &in->hard_links);+		} else {+			/* Todo Need to report/handle this better.+			 * Got a problem... hardlink to a non-existant object+			 */+			hl->variant.hardlink_variant.equiv_obj = NULL;+			INIT_LIST_HEAD(&hl->hard_links);+		}+	}+}++static void yaffs_strip_deleted_objs(struct yaffs_dev *dev)+{+	/*+	 *  Sort out state of unlinked and deleted objects after scanning.+	 */+	struct list_head *i;+	struct list_head *n;+	struct yaffs_obj *l;++	if (dev->read_only)+		return;++	/* Soft delete all the unlinked files */+	list_for_each_safe(i, n,+			   &dev->unlinked_dir->variant.dir_variant.children) {+		l = list_entry(i, struct yaffs_obj, siblings);+		yaffs_del_obj(l);+	}++	list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) {+		l = list_entry(i, struct yaffs_obj, siblings);+		yaffs_del_obj(l);+	}+}++/*+ * This code iterates through all the objects making sure that they are rooted.+ * Any unrooted objects are re-rooted in lost+found.+ * An object needs to be in one of:+ * - Directly under deleted, unlinked+ * - Directly or indirectly under root.+ *+ * Note:+ *  This code assumes that we don't ever change the current relationships+ *  between directories:+ *   root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL+ *   lost-n-found->parent == root_dir+ *+ * This fixes the problem where directories might have inadvertently been+ * deleted leaving the object "hanging" without being rooted in the+ * directory tree.+ */++static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj)+{+	return (obj == dev->del_dir ||+		obj == dev->unlinked_dir || obj == dev->root_dir);+}++static void yaffs_fix_hanging_objs(struct yaffs_dev *dev)+{+	struct yaffs_obj *obj;+	struct yaffs_obj *parent;+	int i;+	struct list_head *lh;+	struct list_head *n;+	int depth_limit;+	int hanging;++	if (dev->read_only)+		return;++	/* Iterate through the objects in each hash entry,+	 * looking at each object.+	 * Make sure it is rooted.+	 */++	for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {+		list_for_each_safe(lh, n, &dev->obj_bucket[i].list) {+			obj = list_entry(lh, struct yaffs_obj, hash_link);+			parent = obj->parent;++			if (yaffs_has_null_parent(dev, obj)) {+				/* These directories are not hanging */+				hanging = 0;+			} else if (!parent ||+				   parent->variant_type !=+				   YAFFS_OBJECT_TYPE_DIRECTORY) {+				hanging = 1;+			} else if (yaffs_has_null_parent(dev, parent)) {+				hanging = 0;+			} else {+				/*+				 * Need to follow the parent chain to+				 * see if it is hanging.+				 */+				hanging = 0;+				depth_limit = 100;++				while (parent != dev->root_dir &&+				       parent->parent &&+				       parent->parent->variant_type ==+				       YAFFS_OBJECT_TYPE_DIRECTORY &&+				       depth_limit > 0) {+					parent = parent->parent;+					depth_limit--;+				}+				if (parent != dev->root_dir)+					hanging = 1;+			}+			if (hanging) {+				yaffs_trace(YAFFS_TRACE_SCAN,+					"Hanging object %d moved to lost and found",+					obj->obj_id);+				yaffs_add_obj_to_dir(dev->lost_n_found, obj);+			}+		}+	}+}++/*+ * Delete directory contents for cleaning up lost and found.+ */+static void yaffs_del_dir_contents(struct yaffs_obj *dir)+{+	struct yaffs_obj *obj;+	struct list_head *lh;+	struct list_head *n;++	if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)+		BUG();++	list_for_each_safe(lh, n, &dir->variant.dir_variant.children) {+		obj = list_entry(lh, struct yaffs_obj, siblings);+		if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)+			yaffs_del_dir_contents(obj);+		yaffs_trace(YAFFS_TRACE_SCAN,+			"Deleting lost_found object %d",+			obj->obj_id);+		yaffs_unlink_obj(obj);+	}+}++static void yaffs_empty_l_n_f(struct yaffs_dev *dev)+{+	yaffs_del_dir_contents(dev->lost_n_found);+}+++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory,+				     const YCHAR *name)+{+	int sum;+	struct list_head *i;+	YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1];+	struct yaffs_obj *l;++	if (!name)+		return NULL;++	if (!directory) {+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"tragedy: yaffs_find_by_name: null pointer directory"+			);+		BUG();+		return NULL;+	}+	if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"tragedy: yaffs_find_by_name: non-directory"+			);+		BUG();+	}++	sum = yaffs_calc_name_sum(name);++	list_for_each(i, &directory->variant.dir_variant.children) {+		l = list_entry(i, struct yaffs_obj, siblings);++		if (l->parent != directory)+			BUG();++		yaffs_check_obj_details_loaded(l);++		/* Special case for lost-n-found */+		if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) {+			if (!strcmp(name, YAFFS_LOSTNFOUND_NAME))+				return l;+		} else if (l->sum == sum || l->hdr_chunk <= 0) {+			/* LostnFound chunk called Objxxx+			 * Do a real check+			 */+			yaffs_get_obj_name(l, buffer,+				YAFFS_MAX_NAME_LENGTH + 1);+			if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH))+				return l;+		}+	}+	return NULL;+}++/* GetEquivalentObject dereferences any hard links to get to the+ * actual object.+ */++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj)+{+	if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) {+		obj = obj->variant.hardlink_variant.equiv_obj;+		yaffs_check_obj_details_loaded(obj);+	}+	return obj;+}++/*+ *  A note or two on object names.+ *  * If the object name is missing, we then make one up in the form objnnn+ *+ *  * ASCII names are stored in the object header's name field from byte zero+ *  * Unicode names are historically stored starting from byte zero.+ *+ * Then there are automatic Unicode names...+ * The purpose of these is to save names in a way that can be read as+ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII+ * system to share files.+ *+ * These automatic unicode are stored slightly differently...+ *  - If the name can fit in the ASCII character space then they are saved as+ *    ascii names as per above.+ *  - If the name needs Unicode then the name is saved in Unicode+ *    starting at oh->name[1].++ */+static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name,+				int buffer_size)+{+	/* Create an object name if we could not find one. */+	if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) {+		YCHAR local_name[20];+		YCHAR num_string[20];+		YCHAR *x = &num_string[19];+		unsigned v = obj->obj_id;+		num_string[19] = 0;+		while (v > 0) {+			x--;+			*x = '0' + (v % 10);+			v /= 10;+		}+		/* make up a name */+		strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX);+		strcat(local_name, x);+		strncpy(name, local_name, buffer_size - 1);+	}+}++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size)+{+	memset(name, 0, buffer_size * sizeof(YCHAR));+	yaffs_check_obj_details_loaded(obj);+	if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) {+		strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1);+	} else if (obj->short_name[0]) {+		strcpy(name, obj->short_name);+	} else if (obj->hdr_chunk > 0) {+		int result;+		u8 *buffer = yaffs_get_temp_buffer(obj->my_dev);++		struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer;++		memset(buffer, 0, obj->my_dev->data_bytes_per_chunk);++		if (obj->hdr_chunk > 0) {+			result = yaffs_rd_chunk_tags_nand(obj->my_dev,+							  obj->hdr_chunk,+							  buffer, NULL);+		}+		yaffs_load_name_from_oh(obj->my_dev, name, oh->name,+					buffer_size);++		yaffs_release_temp_buffer(obj->my_dev, buffer);+	}++	yaffs_fix_null_name(obj, name, buffer_size);++	return strnlen(name, YAFFS_MAX_NAME_LENGTH);+}++loff_t yaffs_get_obj_length(struct yaffs_obj *obj)+{+	/* Dereference any hard linking */+	obj = yaffs_get_equivalent_obj(obj);++	if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)+		return obj->variant.file_variant.file_size;+	if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) {+		if (!obj->variant.symlink_variant.alias)+			return 0;+		return strnlen(obj->variant.symlink_variant.alias,+				     YAFFS_MAX_ALIAS_LENGTH);+	} else {+		/* Only a directory should drop through to here */+		return obj->my_dev->data_bytes_per_chunk;+	}+}++int yaffs_get_obj_link_count(struct yaffs_obj *obj)+{+	int count = 0;+	struct list_head *i;++	if (!obj->unlinked)+		count++;	/* the object itself */++	list_for_each(i, &obj->hard_links)+	    count++;		/* add the hard links; */++	return count;+}++int yaffs_get_obj_inode(struct yaffs_obj *obj)+{+	obj = yaffs_get_equivalent_obj(obj);++	return obj->obj_id;+}++unsigned yaffs_get_obj_type(struct yaffs_obj *obj)+{+	obj = yaffs_get_equivalent_obj(obj);++	switch (obj->variant_type) {+	case YAFFS_OBJECT_TYPE_FILE:+		return DT_REG;+		break;+	case YAFFS_OBJECT_TYPE_DIRECTORY:+		return DT_DIR;+		break;+	case YAFFS_OBJECT_TYPE_SYMLINK:+		return DT_LNK;+		break;+	case YAFFS_OBJECT_TYPE_HARDLINK:+		return DT_REG;+		break;+	case YAFFS_OBJECT_TYPE_SPECIAL:+		if (S_ISFIFO(obj->yst_mode))+			return DT_FIFO;+		if (S_ISCHR(obj->yst_mode))+			return DT_CHR;+		if (S_ISBLK(obj->yst_mode))+			return DT_BLK;+		if (S_ISSOCK(obj->yst_mode))+			return DT_SOCK;+		return DT_REG;+		break;+	default:+		return DT_REG;+		break;+	}+}++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj)+{+	obj = yaffs_get_equivalent_obj(obj);+	if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK)+		return yaffs_clone_str(obj->variant.symlink_variant.alias);+	else+		return yaffs_clone_str(_Y(""));+}++/*--------------------------- Initialisation code -------------------------- */++static int yaffs_check_dev_fns(struct yaffs_dev *dev)+{+	struct yaffs_driver *drv = &dev->drv;+	struct yaffs_tags_handler *tagger = &dev->tagger;++	/* Common functions, gotta have */+	if (!drv->drv_read_chunk_fn ||+	    !drv->drv_write_chunk_fn ||+	    !drv->drv_erase_fn)+		return 0;++	if (dev->param.is_yaffs2 &&+	     (!drv->drv_mark_bad_fn  || !drv->drv_check_bad_fn))+		return 0;++	/* Install the default tags marshalling functions if needed. */+	yaffs_tags_compat_install(dev);+	yaffs_tags_marshall_install(dev);++	/* Check we now have the marshalling functions required. */+	if (!tagger->write_chunk_tags_fn ||+	    !tagger->read_chunk_tags_fn ||+	    !tagger->query_block_fn ||+	    !tagger->mark_bad_fn)+		return 0;++	return 1;+}++static int yaffs_create_initial_dir(struct yaffs_dev *dev)+{+	/* Initialise the unlinked, deleted, root and lost+found directories */+	dev->lost_n_found = dev->root_dir = NULL;+	dev->unlinked_dir = dev->del_dir = NULL;+	dev->unlinked_dir =+	    yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR);+	dev->del_dir =+	    yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR);+	dev->root_dir =+	    yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT,+				  YAFFS_ROOT_MODE | S_IFDIR);+	dev->lost_n_found =+	    yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND,+				  YAFFS_LOSTNFOUND_MODE | S_IFDIR);++	if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir+	    && dev->del_dir) {+		yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found);+		return YAFFS_OK;+	}+	return YAFFS_FAIL;+}++/* Low level init.+ * Typically only used by yaffs_guts_initialise, but also used by the+ * Low level yaffs driver tests.+ */++int yaffs_guts_ll_init(struct yaffs_dev *dev)+{+++	yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()");++	if (!dev) {+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"yaffs: Need a device"+			);+		return YAFFS_FAIL;+	}++	if (dev->ll_init)+		return YAFFS_OK;++	dev->internal_start_block = dev->param.start_block;+	dev->internal_end_block = dev->param.end_block;+	dev->block_offset = 0;+	dev->chunk_offset = 0;+	dev->n_free_chunks = 0;++	dev->gc_block = 0;++	if (dev->param.start_block == 0) {+		dev->internal_start_block = dev->param.start_block + 1;+		dev->internal_end_block = dev->param.end_block + 1;+		dev->block_offset = 1;+		dev->chunk_offset = dev->param.chunks_per_block;+	}++	/* Check geometry parameters. */++	if ((!dev->param.inband_tags && dev->param.is_yaffs2 &&+		dev->param.total_bytes_per_chunk < 1024) ||+		(!dev->param.is_yaffs2 &&+			dev->param.total_bytes_per_chunk < 512) ||+		(dev->param.inband_tags && !dev->param.is_yaffs2) ||+		 dev->param.chunks_per_block < 2 ||+		 dev->param.n_reserved_blocks < 2 ||+		dev->internal_start_block <= 0 ||+		dev->internal_end_block <= 0 ||+		dev->internal_end_block <=+		(dev->internal_start_block + dev->param.n_reserved_blocks + 2)+		) {+		/* otherwise it is too small */+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ",+			dev->param.total_bytes_per_chunk,+			dev->param.is_yaffs2 ? "2" : "",+			dev->param.inband_tags);+		return YAFFS_FAIL;+	}++	/* Sort out space for inband tags, if required */+	if (dev->param.inband_tags)+		dev->data_bytes_per_chunk =+		    dev->param.total_bytes_per_chunk -+		    sizeof(struct yaffs_packed_tags2_tags_only);+	else+		dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk;++	/* Got the right mix of functions? */+	if (!yaffs_check_dev_fns(dev)) {+		/* Function missing */+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"device function(s) missing or wrong");++		return YAFFS_FAIL;+	}++	if (yaffs_init_nand(dev) != YAFFS_OK) {+		yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed");+		return YAFFS_FAIL;+	}++	return YAFFS_OK;+}+++int yaffs_guts_format_dev(struct yaffs_dev *dev)+{+	int i;+	enum yaffs_block_state state;+	u32 dummy;++	if(yaffs_guts_ll_init(dev) != YAFFS_OK)+		return YAFFS_FAIL;++	if(dev->is_mounted)+		return YAFFS_FAIL;++	for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {+		yaffs_query_init_block_state(dev, i, &state, &dummy);+		if (state != YAFFS_BLOCK_STATE_DEAD)+			yaffs_erase_block(dev, i);+	}++	return YAFFS_OK;+}+++int yaffs_guts_initialise(struct yaffs_dev *dev)+{+	int init_failed = 0;+	unsigned x;+	int bits;++	if(yaffs_guts_ll_init(dev) != YAFFS_OK)+		return YAFFS_FAIL;++	if (dev->is_mounted) {+		yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted");+		return YAFFS_FAIL;+	}++	dev->is_mounted = 1;++	/* OK now calculate a few things for the device */++	/*+	 *  Calculate all the chunk size manipulation numbers:+	 */+	x = dev->data_bytes_per_chunk;+	/* We always use dev->chunk_shift and dev->chunk_div */+	dev->chunk_shift = calc_shifts(x);+	x >>= dev->chunk_shift;+	dev->chunk_div = x;+	/* We only use chunk mask if chunk_div is 1 */+	dev->chunk_mask = (1 << dev->chunk_shift) - 1;++	/*+	 * Calculate chunk_grp_bits.+	 * We need to find the next power of 2 > than internal_end_block+	 */++	x = dev->param.chunks_per_block * (dev->internal_end_block + 1);++	bits = calc_shifts_ceiling(x);++	/* Set up tnode width if wide tnodes are enabled. */+	if (!dev->param.wide_tnodes_disabled) {+		/* bits must be even so that we end up with 32-bit words */+		if (bits & 1)+			bits++;+		if (bits < 16)+			dev->tnode_width = 16;+		else+			dev->tnode_width = bits;+	} else {+		dev->tnode_width = 16;+	}++	dev->tnode_mask = (1 << dev->tnode_width) - 1;++	/* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled),+	 * so if the bitwidth of the+	 * chunk range we're using is greater than 16 we need+	 * to figure out chunk shift and chunk_grp_size+	 */++	if (bits <= dev->tnode_width)+		dev->chunk_grp_bits = 0;+	else+		dev->chunk_grp_bits = bits - dev->tnode_width;++	dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8;+	if (dev->tnode_size < sizeof(struct yaffs_tnode))+		dev->tnode_size = sizeof(struct yaffs_tnode);++	dev->chunk_grp_size = 1 << dev->chunk_grp_bits;++	if (dev->param.chunks_per_block < dev->chunk_grp_size) {+		/* We have a problem because the soft delete won't work if+		 * the chunk group size > chunks per block.+		 * This can be remedied by using larger "virtual blocks".+		 */+		yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large");++		return YAFFS_FAIL;+	}++	/* Finished verifying the device, continue with initialisation */++	/* More device initialisation */+	dev->all_gcs = 0;+	dev->passive_gc_count = 0;+	dev->oldest_dirty_gc_count = 0;+	dev->bg_gcs = 0;+	dev->gc_block_finder = 0;+	dev->buffered_block = -1;+	dev->doing_buffered_block_rewrite = 0;+	dev->n_deleted_files = 0;+	dev->n_bg_deletions = 0;+	dev->n_unlinked_files = 0;+	dev->n_ecc_fixed = 0;+	dev->n_ecc_unfixed = 0;+	dev->n_tags_ecc_fixed = 0;+	dev->n_tags_ecc_unfixed = 0;+	dev->n_erase_failures = 0;+	dev->n_erased_blocks = 0;+	dev->gc_disable = 0;+	dev->has_pending_prioritised_gc = 1;+		/* Assume the worst for now, will get fixed on first GC */+	INIT_LIST_HEAD(&dev->dirty_dirs);+	dev->oldest_dirty_seq = 0;+	dev->oldest_dirty_block = 0;++	/* Initialise temporary buffers and caches. */+	if (!yaffs_init_tmp_buffers(dev))+		init_failed = 1;++	dev->cache = NULL;+	dev->gc_cleanup_list = NULL;++	if (!init_failed && dev->param.n_caches > 0) {+		int i;+		void *buf;+		int cache_bytes =+		    dev->param.n_caches * sizeof(struct yaffs_cache);++		if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES)+			dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES;++		dev->cache = kmalloc(cache_bytes, GFP_NOFS);++		buf = (u8 *) dev->cache;++		if (dev->cache)+			memset(dev->cache, 0, cache_bytes);++		for (i = 0; i < dev->param.n_caches && buf; i++) {+			dev->cache[i].object = NULL;+			dev->cache[i].last_use = 0;+			dev->cache[i].dirty = 0;+			dev->cache[i].data = buf =+			    kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);+		}+		if (!buf)+			init_failed = 1;++		dev->cache_last_use = 0;+	}++	dev->cache_hits = 0;++	if (!init_failed) {+		dev->gc_cleanup_list =+		    kmalloc(dev->param.chunks_per_block * sizeof(u32),+					GFP_NOFS);+		if (!dev->gc_cleanup_list)+			init_failed = 1;+	}++	if (dev->param.is_yaffs2)+		dev->param.use_header_file_size = 1;++	if (!init_failed && !yaffs_init_blocks(dev))+		init_failed = 1;++	yaffs_init_tnodes_and_objs(dev);++	if (!init_failed && !yaffs_create_initial_dir(dev))+		init_failed = 1;++	if (!init_failed && dev->param.is_yaffs2 &&+		!dev->param.disable_summary &&+		!yaffs_summary_init(dev))+		init_failed = 1;++	if (!init_failed) {+		/* Now scan the flash. */+		if (dev->param.is_yaffs2) {+			if (yaffs2_checkpt_restore(dev)) {+				yaffs_check_obj_details_loaded(dev->root_dir);+				yaffs_trace(YAFFS_TRACE_CHECKPOINT |+					YAFFS_TRACE_MOUNT,+					"yaffs: restored from checkpoint"+					);+			} else {++				/* Clean up the mess caused by an aborted+				 * checkpoint load then scan backwards.+				 */+				yaffs_deinit_blocks(dev);++				yaffs_deinit_tnodes_and_objs(dev);++				dev->n_erased_blocks = 0;+				dev->n_free_chunks = 0;+				dev->alloc_block = -1;+				dev->alloc_page = -1;+				dev->n_deleted_files = 0;+				dev->n_unlinked_files = 0;+				dev->n_bg_deletions = 0;++				if (!init_failed && !yaffs_init_blocks(dev))+					init_failed = 1;++				yaffs_init_tnodes_and_objs(dev);++				if (!init_failed+				    && !yaffs_create_initial_dir(dev))+					init_failed = 1;++				if (!init_failed && !yaffs2_scan_backwards(dev))+					init_failed = 1;+			}+		} else if (!yaffs1_scan(dev)) {+			init_failed = 1;+		}++		yaffs_strip_deleted_objs(dev);+		yaffs_fix_hanging_objs(dev);+		if (dev->param.empty_lost_n_found)+			yaffs_empty_l_n_f(dev);+	}++	if (init_failed) {+		/* Clean up the mess */+		yaffs_trace(YAFFS_TRACE_TRACING,+		  "yaffs: yaffs_guts_initialise() aborted.");++		yaffs_deinitialise(dev);+		return YAFFS_FAIL;+	}++	/* Zero out stats */+	dev->n_page_reads = 0;+	dev->n_page_writes = 0;+	dev->n_erasures = 0;+	dev->n_gc_copies = 0;+	dev->n_retried_writes = 0;++	dev->n_retired_blocks = 0;++	yaffs_verify_free_chunks(dev);+	yaffs_verify_blocks(dev);++	/* Clean up any aborted checkpoint data */+	if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0)+		yaffs2_checkpt_invalidate(dev);++	yaffs_trace(YAFFS_TRACE_TRACING,+	  "yaffs: yaffs_guts_initialise() done.");+	return YAFFS_OK;+}++void yaffs_deinitialise(struct yaffs_dev *dev)+{+	if (dev->is_mounted) {+		int i;++		yaffs_deinit_blocks(dev);+		yaffs_deinit_tnodes_and_objs(dev);+		yaffs_summary_deinit(dev);++		if (dev->param.n_caches > 0 && dev->cache) {++			for (i = 0; i < dev->param.n_caches; i++) {+				kfree(dev->cache[i].data);+				dev->cache[i].data = NULL;+			}++			kfree(dev->cache);+			dev->cache = NULL;+		}++		kfree(dev->gc_cleanup_list);++		for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++)+			kfree(dev->temp_buffer[i].buffer);++		dev->is_mounted = 0;++		yaffs_deinit_nand(dev);+	}+}++int yaffs_count_free_chunks(struct yaffs_dev *dev)+{+	int n_free = 0;+	int b;+	struct yaffs_block_info *blk;++	blk = dev->block_info;+	for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) {+		switch (blk->block_state) {+		case YAFFS_BLOCK_STATE_EMPTY:+		case YAFFS_BLOCK_STATE_ALLOCATING:+		case YAFFS_BLOCK_STATE_COLLECTING:+		case YAFFS_BLOCK_STATE_FULL:+			n_free +=+			    (dev->param.chunks_per_block - blk->pages_in_use ++			     blk->soft_del_pages);+			break;+		default:+			break;+		}+		blk++;+	}+	return n_free;+}++int yaffs_get_n_free_chunks(struct yaffs_dev *dev)+{+	/* This is what we report to the outside world */+	int n_free;+	int n_dirty_caches;+	int blocks_for_checkpt;+	int i;++	n_free = dev->n_free_chunks;+	n_free += dev->n_deleted_files;++	/* Now count and subtract the number of dirty chunks in the cache. */++	for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) {+		if (dev->cache[i].dirty)+			n_dirty_caches++;+	}++	n_free -= n_dirty_caches;++	n_free -=+	    ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block);++	/* Now figure checkpoint space and report that... */+	blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev);++	n_free -= (blocks_for_checkpt * dev->param.chunks_per_block);++	if (n_free < 0)+		n_free = 0;++	return n_free;+}++++/*+ * Marshalling functions to get loff_t file sizes into and out of+ * object headers.+ */+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize)+{+	oh->file_size_low = (fsize & 0xFFFFFFFF);+	oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF);+}++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh)+{+	loff_t retval;++	if (sizeof(loff_t) >= 8 && ~(oh->file_size_high))+		retval = (((loff_t) oh->file_size_high) << 32) |+			(((loff_t) oh->file_size_low) & 0xFFFFFFFF);+	else+		retval = (loff_t) oh->file_size_low;++	return retval;+}+++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10])+{+	int i;+	struct yaffs_block_info *bi;+	int s;++	for(i = 0; i < 10; i++)+		bs[i] = 0;++	for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) {+		bi = yaffs_get_block_info(dev, i);+		s = bi->block_state;+		if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN)+			bs[0]++;+		else+			bs[s]++;+	}+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_guts.h linux-3.4.90/fs/yaffs2/yaffs_guts.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_guts.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_guts.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,1007 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_GUTS_H__+#define __YAFFS_GUTS_H__++#include "yportenv.h"++#define YAFFS_OK	1+#define YAFFS_FAIL  0++/* Give us a  Y=0x59,+ * Give us an A=0x41,+ * Give us an FF=0xff+ * Give us an S=0x53+ * And what have we got...+ */+#define YAFFS_MAGIC			0x5941ff53++/*+ * Tnodes form a tree with the tnodes in "levels"+ * Levels greater than 0 hold 8 slots which point to other tnodes.+ * Those at level 0 hold 16 slots which point to chunks in NAND.+ *+ * A maximum level of 8 thust supports files of size up to:+ *+ * 2^(3*MAX_LEVEL+4)+ *+ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives+ * a maximum file size of around 512Gbytees with 2k chunks.+ */+#define YAFFS_NTNODES_LEVEL0		16+#define YAFFS_TNODES_LEVEL0_BITS	4+#define YAFFS_TNODES_LEVEL0_MASK	0xf++#define YAFFS_NTNODES_INTERNAL		(YAFFS_NTNODES_LEVEL0 / 2)+#define YAFFS_TNODES_INTERNAL_BITS	(YAFFS_TNODES_LEVEL0_BITS - 1)+#define YAFFS_TNODES_INTERNAL_MASK	0x7+#define YAFFS_TNODES_MAX_LEVEL		8+#define YAFFS_TNODES_MAX_BITS		(YAFFS_TNODES_LEVEL0_BITS + \+					YAFFS_TNODES_INTERNAL_BITS * \+					YAFFS_TNODES_MAX_LEVEL)+#define YAFFS_MAX_CHUNK_ID		((1 << YAFFS_TNODES_MAX_BITS) - 1)++#define YAFFS_MAX_FILE_SIZE_32		0x7fffffff++/* Constants for YAFFS1 mode */+#define YAFFS_BYTES_PER_SPARE		16+#define YAFFS_BYTES_PER_CHUNK		512+#define YAFFS_CHUNK_SIZE_SHIFT		9+#define YAFFS_CHUNKS_PER_BLOCK		32+#define YAFFS_BYTES_PER_BLOCK	(YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)++#define YAFFS_MIN_YAFFS2_CHUNK_SIZE	1024+#define YAFFS_MIN_YAFFS2_SPARE_SIZE	32++++#define YAFFS_ALLOCATION_NOBJECTS	100+#define YAFFS_ALLOCATION_NTNODES	100+#define YAFFS_ALLOCATION_NLINKS		100++#define YAFFS_NOBJECT_BUCKETS		256++#define YAFFS_OBJECT_SPACE		0x40000+#define YAFFS_MAX_OBJECT_ID		(YAFFS_OBJECT_SPACE - 1)++/* Binary data version stamps */+#define YAFFS_SUMMARY_VERSION		1+#define YAFFS_CHECKPOINT_VERSION	7++#ifdef CONFIG_YAFFS_UNICODE+#define YAFFS_MAX_NAME_LENGTH		127+#define YAFFS_MAX_ALIAS_LENGTH		79+#else+#define YAFFS_MAX_NAME_LENGTH		255+#define YAFFS_MAX_ALIAS_LENGTH		159+#endif++#define YAFFS_SHORT_NAME_LENGTH		15++/* Some special object ids for pseudo objects */+#define YAFFS_OBJECTID_ROOT		1+#define YAFFS_OBJECTID_LOSTNFOUND	2+#define YAFFS_OBJECTID_UNLINKED		3+#define YAFFS_OBJECTID_DELETED		4++/* Fake object Id for summary data */+#define YAFFS_OBJECTID_SUMMARY		0x10++/* Pseudo object ids for checkpointing */+#define YAFFS_OBJECTID_CHECKPOINT_DATA	0x20+#define YAFFS_SEQUENCE_CHECKPOINT_DATA	0x21++#define YAFFS_MAX_SHORT_OP_CACHES	20++#define YAFFS_N_TEMP_BUFFERS		6++/* We limit the number attempts at sucessfully saving a chunk of data.+ * Small-page devices have 32 pages per block; large-page devices have 64.+ * Default to something in the order of 5 to 10 blocks worth of chunks.+ */+#define YAFFS_WR_ATTEMPTS		(5*64)++/* Sequence numbers are used in YAFFS2 to determine block allocation order.+ * The range is limited slightly to help distinguish bad numbers from good.+ * This also allows us to perhaps in the future use special numbers for+ * special purposes.+ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years,+ * and is a larger number than the lifetime of a 2GB device.+ */+#define YAFFS_LOWEST_SEQUENCE_NUMBER	0x00001000+#define YAFFS_HIGHEST_SEQUENCE_NUMBER	0xefffff00++/* Special sequence number for bad block that failed to be marked bad */+#define YAFFS_SEQUENCE_BAD_BLOCK	0xffff0000++/* ChunkCache is used for short read/write operations.*/+struct yaffs_cache {+	struct yaffs_obj *object;+	int chunk_id;+	int last_use;+	int dirty;+	int n_bytes;		/* Only valid if the cache is dirty */+	int locked;		/* Can't push out or flush while locked. */+	u8 *data;+};++/* yaffs1 tags structures in RAM+ * NB This uses bitfield. Bitfields should not straddle a u32 boundary+ * otherwise the structure size will get blown out.+ */++struct yaffs_tags {+	u32 chunk_id:20;+	u32 serial_number:2;+	u32 n_bytes_lsb:10;+	u32 obj_id:18;+	u32 ecc:12;+	u32 n_bytes_msb:2;+};++union yaffs_tags_union {+	struct yaffs_tags as_tags;+	u8 as_bytes[8];+};+++/* Stuff used for extended tags in YAFFS2 */++enum yaffs_ecc_result {+	YAFFS_ECC_RESULT_UNKNOWN,+	YAFFS_ECC_RESULT_NO_ERROR,+	YAFFS_ECC_RESULT_FIXED,+	YAFFS_ECC_RESULT_UNFIXED+};++enum yaffs_obj_type {+	YAFFS_OBJECT_TYPE_UNKNOWN,+	YAFFS_OBJECT_TYPE_FILE,+	YAFFS_OBJECT_TYPE_SYMLINK,+	YAFFS_OBJECT_TYPE_DIRECTORY,+	YAFFS_OBJECT_TYPE_HARDLINK,+	YAFFS_OBJECT_TYPE_SPECIAL+};++#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL++struct yaffs_ext_tags {+	unsigned chunk_used;	/*  Status of the chunk: used or unused */+	unsigned obj_id;	/* If 0 this is not used */+	unsigned chunk_id;	/* If 0 this is a header, else a data chunk */+	unsigned n_bytes;	/* Only valid for data chunks */++	/* The following stuff only has meaning when we read */+	enum yaffs_ecc_result ecc_result;+	unsigned block_bad;++	/* YAFFS 1 stuff */+	unsigned is_deleted;	/* The chunk is marked deleted */+	unsigned serial_number;	/* Yaffs1 2-bit serial number */++	/* YAFFS2 stuff */+	unsigned seq_number;	/* The sequence number of this block */++	/* Extra info if this is an object header (YAFFS2 only) */++	unsigned extra_available;	/* Extra info available if not zero */+	unsigned extra_parent_id;	/* The parent object */+	unsigned extra_is_shrink;	/* Is it a shrink header? */+	unsigned extra_shadows;	/* Does this shadow another object? */++	enum yaffs_obj_type extra_obj_type;	/* What object type? */++	loff_t extra_file_size;		/* Length if it is a file */+	unsigned extra_equiv_id;	/* Equivalent object for a hard link */+};++/* Spare structure for YAFFS1 */+struct yaffs_spare {+	u8 tb0;+	u8 tb1;+	u8 tb2;+	u8 tb3;+	u8 page_status;		/* set to 0 to delete the chunk */+	u8 block_status;+	u8 tb4;+	u8 tb5;+	u8 ecc1[3];+	u8 tb6;+	u8 tb7;+	u8 ecc2[3];+};++/*Special structure for passing through to mtd */+struct yaffs_nand_spare {+	struct yaffs_spare spare;+	int eccres1;+	int eccres2;+};++/* Block data in RAM */++enum yaffs_block_state {+	YAFFS_BLOCK_STATE_UNKNOWN = 0,++	YAFFS_BLOCK_STATE_SCANNING,+	/* Being scanned */++	YAFFS_BLOCK_STATE_NEEDS_SCAN,+	/* The block might have something on it (ie it is allocating or full,+	 * perhaps empty) but it needs to be scanned to determine its true+	 * state.+	 * This state is only valid during scanning.+	 * NB We tolerate empty because the pre-scanner might be incapable of+	 * deciding+	 * However, if this state is returned on a YAFFS2 device,+	 * then we expect a sequence number+	 */++	YAFFS_BLOCK_STATE_EMPTY,+	/* This block is empty */++	YAFFS_BLOCK_STATE_ALLOCATING,+	/* This block is partially allocated.+	 * At least one page holds valid data.+	 * This is the one currently being used for page+	 * allocation. Should never be more than one of these.+	 * If a block is only partially allocated at mount it is treated as+	 * full.+	 */++	YAFFS_BLOCK_STATE_FULL,+	/* All the pages in this block have been allocated.+	 * If a block was only partially allocated when mounted we treat+	 * it as fully allocated.+	 */++	YAFFS_BLOCK_STATE_DIRTY,+	/* The block was full and now all chunks have been deleted.+	 * Erase me, reuse me.+	 */++	YAFFS_BLOCK_STATE_CHECKPOINT,+	/* This block is assigned to holding checkpoint data. */++	YAFFS_BLOCK_STATE_COLLECTING,+	/* This block is being garbage collected */++	YAFFS_BLOCK_STATE_DEAD+	    /* This block has failed and is not in use */+};++#define	YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1)++struct yaffs_block_info {++	s32 soft_del_pages:10;	/* number of soft deleted pages */+	s32 pages_in_use:10;	/* number of pages in use */+	u32 block_state:4;	/* One of the above block states. */+				/* NB use unsigned because enum is sometimes+				 * an int */+	u32 needs_retiring:1;	/* Data has failed on this block, */+				/*need to get valid data off and retire*/+	u32 skip_erased_check:1;/* Skip the erased check on this block */+	u32 gc_prioritise:1;	/* An ECC check or blank check has failed.+				   Block should be prioritised for GC */+	u32 chunk_error_strikes:3;	/* How many times we've had ecc etc+				failures on this block and tried to reuse it */+	u32 has_summary:1;	/* The block has a summary */++	u32 has_shrink_hdr:1;	/* This block has at least one shrink header */+	u32 seq_number;		/* block sequence number for yaffs2 */++};++/* -------------------------- Object structure -------------------------------*/+/* This is the object structure as stored on NAND */++struct yaffs_obj_hdr {+	enum yaffs_obj_type type;++	/* Apply to everything  */+	int parent_obj_id;+	u16 sum_no_longer_used;	/* checksum of name. No longer used */+	YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];++	/* The following apply to all object types except for hard links */+	u32 yst_mode;		/* protection */++	u32 yst_uid;+	u32 yst_gid;+	u32 yst_atime;+	u32 yst_mtime;+	u32 yst_ctime;++	/* File size  applies to files only */+	u32 file_size_low;++	/* Equivalent object id applies to hard links only. */+	int equiv_id;++	/* Alias is for symlinks only. */+	YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1];++	u32 yst_rdev;	/* stuff for block and char devices (major/min) */++	u32 win_ctime[2];+	u32 win_atime[2];+	u32 win_mtime[2];++	u32 inband_shadowed_obj_id;+	u32 inband_is_shrink;++	u32 file_size_high;+	u32 reserved[1];+	int shadows_obj;	/* This object header shadows the+				specified object if > 0 */++	/* is_shrink applies to object headers written when wemake a hole. */+	u32 is_shrink;++};++/*--------------------------- Tnode -------------------------- */++struct yaffs_tnode {+	struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL];+};++/*------------------------  Object -----------------------------*/+/* An object can be one of:+ * - a directory (no data, has children links+ * - a regular file (data.... not prunes :->).+ * - a symlink [symbolic link] (the alias).+ * - a hard link+ */++struct yaffs_file_var {+	loff_t file_size;+	loff_t scanned_size;+	loff_t shrink_size;+	int top_level;+	struct yaffs_tnode *top;+};++struct yaffs_dir_var {+	struct list_head children;	/* list of child links */+	struct list_head dirty;	/* Entry for list of dirty directories */+};++struct yaffs_symlink_var {+	YCHAR *alias;+};++struct yaffs_hardlink_var {+	struct yaffs_obj *equiv_obj;+	u32 equiv_id;+};++union yaffs_obj_var {+	struct yaffs_file_var file_variant;+	struct yaffs_dir_var dir_variant;+	struct yaffs_symlink_var symlink_variant;+	struct yaffs_hardlink_var hardlink_variant;+};++struct yaffs_obj {+	u8 deleted:1;		/* This should only apply to unlinked files. */+	u8 soft_del:1;		/* it has also been soft deleted */+	u8 unlinked:1;		/* An unlinked file.*/+	u8 fake:1;		/* A fake object has no presence on NAND. */+	u8 rename_allowed:1;	/* Some objects cannot be renamed. */+	u8 unlink_allowed:1;+	u8 dirty:1;		/* the object needs to be written to flash */+	u8 valid:1;		/* When the file system is being loaded up, this+				 * object might be created before the data+				 * is available+				 * ie. file data chunks encountered before+				* the header.+				 */+	u8 lazy_loaded:1;	/* This object has been lazy loaded and+				 * is missing some detail */++	u8 defered_free:1;	/* Object is removed from NAND, but is+				 * still in the inode cache.+				 * Free of object is defered.+				 * until the inode is released.+				 */+	u8 being_created:1;	/* This object is still being created+				 * so skip some verification checks. */+	u8 is_shadowed:1;	/* This object is shadowed on the way+				 * to being renamed. */++	u8 xattr_known:1;	/* We know if this has object has xattribs+				 * or not. */+	u8 has_xattr:1;		/* This object has xattribs.+				 * Only valid if xattr_known. */++	u8 serial;		/* serial number of chunk in NAND.*/+	u16 sum;		/* sum of the name to speed searching */++	struct yaffs_dev *my_dev;	/* The device I'm on */++	struct list_head hash_link;	/* list of objects in hash bucket */++	struct list_head hard_links;	/* hard linked object chain*/++	/* directory structure stuff */+	/* also used for linking up the free list */+	struct yaffs_obj *parent;+	struct list_head siblings;++	/* Where's my object header in NAND? */+	int hdr_chunk;++	int n_data_chunks;	/* Number of data chunks for this file. */++	u32 obj_id;		/* the object id value */++	u32 yst_mode;++	YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1];++#ifdef CONFIG_YAFFS_WINCE+	u32 win_ctime[2];+	u32 win_mtime[2];+	u32 win_atime[2];+#else+	u32 yst_uid;+	u32 yst_gid;+	u32 yst_atime;+	u32 yst_mtime;+	u32 yst_ctime;+#endif++	u32 yst_rdev;++	void *my_inode;++	enum yaffs_obj_type variant_type;++	union yaffs_obj_var variant;++};++struct yaffs_obj_bucket {+	struct list_head list;+	int count;+};++/* yaffs_checkpt_obj holds the definition of an object as dumped+ * by checkpointing.+ */++struct yaffs_checkpt_obj {+	int struct_type;+	u32 obj_id;+	u32 parent_id;+	int hdr_chunk;+	enum yaffs_obj_type variant_type:3;+	u8 deleted:1;+	u8 soft_del:1;+	u8 unlinked:1;+	u8 fake:1;+	u8 rename_allowed:1;+	u8 unlink_allowed:1;+	u8 serial;+	int n_data_chunks;+	loff_t size_or_equiv_obj;+};++/*--------------------- Temporary buffers ----------------+ *+ * These are chunk-sized working buffers. Each device has a few.+ */++struct yaffs_buffer {+	u8 *buffer;+	int in_use;+};++/*----------------- Device ---------------------------------*/++struct yaffs_param {+	const YCHAR *name;++	/*+	 * Entry parameters set up way early. Yaffs sets up the rest.+	 * The structure should be zeroed out before use so that unused+	 * and default values are zero.+	 */++	int inband_tags;	/* Use unband tags */+	u32 total_bytes_per_chunk;	/* Should be >= 512, does not need to+					 be a power of 2 */+	int chunks_per_block;	/* does not need to be a power of 2 */+	int spare_bytes_per_chunk;	/* spare area size */+	int start_block;	/* Start block we're allowed to use */+	int end_block;		/* End block we're allowed to use */+	int n_reserved_blocks;	/* Tuneable so that we can reduce+				 * reserved blocks on NOR and RAM. */++	int n_caches;		/* If <= 0, then short op caching is disabled,+				 * else the number of short op caches.+				 */+	int cache_bypass_aligned; /* If non-zero then bypass the cache for+				   * aligned writes.+				   */++	int use_nand_ecc;	/* Flag to decide whether or not to use+				 * NAND driver ECC on data (yaffs1) */+	int tags_9bytes;	/* Use 9 byte tags */+	int no_tags_ecc;	/* Flag to decide whether or not to do ECC+				 * on packed tags (yaffs2) */++	int is_yaffs2;		/* Use yaffs2 mode on this device */++	int empty_lost_n_found;	/* Auto-empty lost+found directory on mount */++	int refresh_period;	/* How often to check for a block refresh */++	/* Checkpoint control. Can be set before or after initialisation */+	u8 skip_checkpt_rd;+	u8 skip_checkpt_wr;++	int enable_xattr;	/* Enable xattribs */++	int max_objects;	/*+				 * Set to limit the number of objects created.+				 * 0 = no limit.+				*/++	/* The remove_obj_fn function must be supplied by OS flavours that+	 * need it.+	 * yaffs direct uses it to implement the faster readdir.+	 * Linux uses it to protect the directory during unlocking.+	 */+	void (*remove_obj_fn) (struct yaffs_obj *obj);++	/* Callback to mark the superblock dirty */+	void (*sb_dirty_fn) (struct yaffs_dev *dev);++	/*  Callback to control garbage collection. */+	unsigned (*gc_control_fn) (struct yaffs_dev *dev);++	/* Debug control flags. Don't use unless you know what you're doing */+	int use_header_file_size;	/* Flag to determine if we should use+					 * file sizes from the header */+	int disable_lazy_load;	/* Disable lazy loading on this device */+	int wide_tnodes_disabled;	/* Set to disable wide tnodes */+	int disable_soft_del;	/* yaffs 1 only: Set to disable the use of+				 * softdeletion. */++	int defered_dir_update;	/* Set to defer directory updates */++#ifdef CONFIG_YAFFS_AUTO_UNICODE+	int auto_unicode;+#endif+	int always_check_erased;	/* Force chunk erased check always on */++	int disable_summary;+	int disable_bad_block_marking;++};++struct yaffs_driver {+	int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk,+				   const u8 *data, int data_len,+				   const u8 *oob, int oob_len);+	int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk,+				   u8 *data, int data_len,+				   u8 *oob, int oob_len,+				   enum yaffs_ecc_result *ecc_result);+	int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no);+	int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no);+	int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no);+	int (*drv_initialise_fn) (struct yaffs_dev *dev);+	int (*drv_deinitialise_fn) (struct yaffs_dev *dev);+};++struct yaffs_tags_handler {+	int (*write_chunk_tags_fn) (struct yaffs_dev *dev,+				    int nand_chunk, const u8 *data,+				    const struct yaffs_ext_tags *tags);+	int (*read_chunk_tags_fn) (struct yaffs_dev *dev,+				   int nand_chunk, u8 *data,+				   struct yaffs_ext_tags *tags);++	int (*query_block_fn) (struct yaffs_dev *dev, int block_no,+			       enum yaffs_block_state *state,+			       u32 *seq_number);+	int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no);+};++struct yaffs_dev {+	struct yaffs_param param;+	struct yaffs_driver drv;+	struct yaffs_tags_handler tagger;++	/* Context storage. Holds extra OS specific data for this device */++	void *os_context;+	void *driver_context;++	struct list_head dev_list;++	int ll_init;+	/* Runtime parameters. Set up by YAFFS. */+	int data_bytes_per_chunk;++	/* Non-wide tnode stuff */+	u16 chunk_grp_bits;	/* Number of bits that need to be resolved if+				 * the tnodes are not wide enough.+				 */+	u16 chunk_grp_size;	/* == 2^^chunk_grp_bits */++	/* Stuff to support wide tnodes */+	u32 tnode_width;+	u32 tnode_mask;+	u32 tnode_size;++	/* Stuff for figuring out file offset to chunk conversions */+	u32 chunk_shift;	/* Shift value */+	u32 chunk_div;		/* Divisor after shifting: 1 for 2^n sizes */+	u32 chunk_mask;		/* Mask to use for power-of-2 case */++	int is_mounted;+	int read_only;+	int is_checkpointed;++	/* Stuff to support block offsetting to support start block zero */+	int internal_start_block;+	int internal_end_block;+	int block_offset;+	int chunk_offset;++	/* Runtime checkpointing stuff */+	int checkpt_page_seq;	/* running sequence number of checkpt pages */+	int checkpt_byte_count;+	int checkpt_byte_offs;+	u8 *checkpt_buffer;+	int checkpt_open_write;+	int blocks_in_checkpt;+	int checkpt_cur_chunk;+	int checkpt_cur_block;+	int checkpt_next_block;+	int *checkpt_block_list;+	int checkpt_max_blocks;+	u32 checkpt_sum;+	u32 checkpt_xor;++	int checkpoint_blocks_required;	/* Number of blocks needed to store+					 * current checkpoint set */++	/* Block Info */+	struct yaffs_block_info *block_info;+	u8 *chunk_bits;		/* bitmap of chunks in use */+	u8 block_info_alt:1;	/* allocated using alternative alloc */+	u8 chunk_bits_alt:1;	/* allocated using alternative alloc */+	int chunk_bit_stride;	/* Number of bytes of chunk_bits per block.+				 * Must be consistent with chunks_per_block.+				 */++	int n_erased_blocks;+	int alloc_block;	/* Current block being allocated off */+	u32 alloc_page;+	int alloc_block_finder;	/* Used to search for next allocation block */++	/* Object and Tnode memory management */+	void *allocator;+	int n_obj;+	int n_tnodes;++	int n_hardlinks;++	struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS];+	u32 bucket_finder;++	int n_free_chunks;++	/* Garbage collection control */+	u32 *gc_cleanup_list;	/* objects to delete at the end of a GC. */+	u32 n_clean_ups;++	unsigned has_pending_prioritised_gc;	/* We think this device might+						have pending prioritised gcs */+	unsigned gc_disable;+	unsigned gc_block_finder;+	unsigned gc_dirtiest;+	unsigned gc_pages_in_use;+	unsigned gc_not_done;+	unsigned gc_block;+	unsigned gc_chunk;+	unsigned gc_skip;+	struct yaffs_summary_tags *gc_sum_tags;++	/* Special directories */+	struct yaffs_obj *root_dir;+	struct yaffs_obj *lost_n_found;++	int buffered_block;	/* Which block is buffered here? */+	int doing_buffered_block_rewrite;++	struct yaffs_cache *cache;+	int cache_last_use;++	/* Stuff for background deletion and unlinked files. */+	struct yaffs_obj *unlinked_dir;	/* Directory where unlinked and deleted+					 files live. */+	struct yaffs_obj *del_dir;	/* Directory where deleted objects are+					sent to disappear. */+	struct yaffs_obj *unlinked_deletion;	/* Current file being+							background deleted. */+	int n_deleted_files;	/* Count of files awaiting deletion; */+	int n_unlinked_files;	/* Count of unlinked files. */+	int n_bg_deletions;	/* Count of background deletions. */++	/* Temporary buffer management */+	struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS];+	int max_temp;+	int temp_in_use;+	int unmanaged_buffer_allocs;+	int unmanaged_buffer_deallocs;++	/* yaffs2 runtime stuff */+	unsigned seq_number;	/* Sequence number of currently+					allocating block */+	unsigned oldest_dirty_seq;+	unsigned oldest_dirty_block;++	/* Block refreshing */+	int refresh_skip;	/* A skip down counter.+				 * Refresh happens when this gets to zero. */++	/* Dirty directory handling */+	struct list_head dirty_dirs;	/* List of dirty directories */++	/* Summary */+	int chunks_per_summary;+	struct yaffs_summary_tags *sum_tags;++	/* Statistics */+	u32 n_page_writes;+	u32 n_page_reads;+	u32 n_erasures;+	u32 n_bad_queries;+	u32 n_bad_markings;+	u32 n_erase_failures;+	u32 n_gc_copies;+	u32 all_gcs;+	u32 passive_gc_count;+	u32 oldest_dirty_gc_count;+	u32 n_gc_blocks;+	u32 bg_gcs;+	u32 n_retried_writes;+	u32 n_retired_blocks;+	u32 n_ecc_fixed;+	u32 n_ecc_unfixed;+	u32 n_tags_ecc_fixed;+	u32 n_tags_ecc_unfixed;+	u32 n_deletions;+	u32 n_unmarked_deletions;+	u32 refresh_count;+	u32 cache_hits;+	u32 tags_used;+	u32 summary_used;++};++/* The CheckpointDevice structure holds the device information that changes+ *at runtime and must be preserved over unmount/mount cycles.+ */+struct yaffs_checkpt_dev {+	int struct_type;+	int n_erased_blocks;+	int alloc_block;	/* Current block being allocated off */+	u32 alloc_page;+	int n_free_chunks;++	int n_deleted_files;	/* Count of files awaiting deletion; */+	int n_unlinked_files;	/* Count of unlinked files. */+	int n_bg_deletions;	/* Count of background deletions. */++	/* yaffs2 runtime stuff */+	unsigned seq_number;	/* Sequence number of currently+				 * allocating block */++};++struct yaffs_checkpt_validity {+	int struct_type;+	u32 magic;+	u32 version;+	u32 head;+};++struct yaffs_shadow_fixer {+	int obj_id;+	int shadowed_id;+	struct yaffs_shadow_fixer *next;+};++/* Structure for doing xattr modifications */+struct yaffs_xattr_mod {+	int set;		/* If 0 then this is a deletion */+	const YCHAR *name;+	const void *data;+	int size;+	int flags;+	int result;+};++/*----------------------- YAFFS Functions -----------------------*/++int yaffs_guts_initialise(struct yaffs_dev *dev);+void yaffs_deinitialise(struct yaffs_dev *dev);++int yaffs_get_n_free_chunks(struct yaffs_dev *dev);++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name,+		     struct yaffs_obj *new_dir, const YCHAR * new_name);++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name);+int yaffs_del_obj(struct yaffs_obj *obj);+struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj,+				   enum yaffs_obj_type type);+++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size);+loff_t yaffs_get_obj_length(struct yaffs_obj *obj);+int yaffs_get_obj_inode(struct yaffs_obj *obj);+unsigned yaffs_get_obj_type(struct yaffs_obj *obj);+int yaffs_get_obj_link_count(struct yaffs_obj *obj);++/* File operations */+int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset,+		  int n_bytes);+int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset,+		  int n_bytes, int write_trhrough);+int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size);++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent,+				    const YCHAR *name, u32 mode, u32 uid,+				    u32 gid);++int yaffs_flush_file(struct yaffs_obj *obj, int update_time, int data_sync);++/* Flushing and checkpointing */+void yaffs_flush_whole_cache(struct yaffs_dev *dev);++int yaffs_checkpoint_save(struct yaffs_dev *dev);+int yaffs_checkpoint_restore(struct yaffs_dev *dev);++/* Directory operations */+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name,+				   u32 mode, u32 uid, u32 gid);+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir,+				     const YCHAR *name);+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number);++/* Link operations */+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name,+				 struct yaffs_obj *equiv_obj);++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj);++/* Symlink operations */+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent,+				       const YCHAR *name, u32 mode, u32 uid,+				       u32 gid, const YCHAR *alias);+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj);++/* Special inodes (fifos, sockets and devices) */+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent,+				       const YCHAR *name, u32 mode, u32 uid,+				       u32 gid, u32 rdev);++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name,+		      const void *value, int size, int flags);+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value,+		      int size);+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size);+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name);++/* Special directories */+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev);+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev);++void yaffs_handle_defered_free(struct yaffs_obj *obj);++void yaffs_update_dirty_dirs(struct yaffs_dev *dev);++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency);++/* Debug dump  */+int yaffs_dump_obj(struct yaffs_obj *obj);++void yaffs_guts_test(struct yaffs_dev *dev);+int yaffs_guts_ll_init(struct yaffs_dev *dev);+++/* A few useful functions to be used within the core files*/+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash,+		     int lyn);+int yaffs_check_ff(u8 *buffer, int n_bytes);+void yaffs_handle_chunk_error(struct yaffs_dev *dev,+			      struct yaffs_block_info *bi);++u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev);+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer);++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev,+						 int number,+						 enum yaffs_obj_type type);+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk,+			    int nand_chunk, int in_scan);+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name);+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj,+				const struct yaffs_obj_hdr *oh);+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj);+YCHAR *yaffs_clone_str(const YCHAR *str);+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list);+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no);+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name,+		    int force, int is_shrink, int shadows,+		    struct yaffs_xattr_mod *xop);+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id,+			       int backward_scanning);+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks);+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev);+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev,+					   struct yaffs_file_var *file_struct,+					   u32 chunk_id,+					   struct yaffs_tnode *passed_tn);++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset,+		     int n_bytes, int write_trhrough);+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size);+void yaffs_skip_rest_of_block(struct yaffs_dev *dev);++int yaffs_count_free_chunks(struct yaffs_dev *dev);++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev,+				       struct yaffs_file_var *file_struct,+				       u32 chunk_id);++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn,+			 unsigned pos);++int yaffs_is_non_empty_dir(struct yaffs_obj *obj);++int yaffs_guts_format_dev(struct yaffs_dev *dev);++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr,+				int *chunk_out, u32 *offset_out);+/*+ * Marshalling functions to get loff_t file sizes into aand out of+ * object headers.+ */+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize);+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh);+loff_t yaffs_max_file_size(struct yaffs_dev *dev);++/*+ * Debug function to count number of blocks in each state+ * NB Needs to be called with correct number of integers+ */++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]);++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk,+				    struct yaffs_ext_tags *tags);++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_linux.h linux-3.4.90/fs/yaffs2/yaffs_linux.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_linux.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_linux.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,48 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_LINUX_H__+#define __YAFFS_LINUX_H__++#include "yportenv.h"++struct yaffs_linux_context {+	struct list_head context_list;	/* List of these we have mounted */+	struct yaffs_dev *dev;+	struct super_block *super;+	struct task_struct *bg_thread;	/* Background thread for this device */+	int bg_running;+	struct mutex gross_lock;	/* Gross locking mutex*/+	u8 *spare_buffer;	/* For mtdif2 use. Don't know the buffer size+				 * at compile time so we have to allocate it.+				 */+	struct list_head search_contexts;+	struct task_struct *readdir_process;+	unsigned mount_id;+	int dirty;+};++#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context))+#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context))++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))+#define WRITE_SIZE_STR "writesize"+#define WRITE_SIZE(mtd) ((mtd)->writesize)+#else+#define WRITE_SIZE_STR "oobblock"+#define WRITE_SIZE(mtd) ((mtd)->oobblock)+#endif++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_mtdif.c linux-3.4.90/fs/yaffs2/yaffs_mtdif.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_mtdif.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_mtdif.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,309 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yportenv.h"++#include "yaffs_mtdif.h"++#include "linux/mtd/mtd.h"+#include "linux/types.h"+#include "linux/time.h"+#include "linux/major.h"+#include "linux/mtd/nand.h"+#include "linux/kernel.h"+#include "linux/version.h"+#include "linux/types.h"++#include "yaffs_trace.h"+#include "yaffs_guts.h"+#include "yaffs_linux.h"+++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))+#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO+#endif+++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0))+#define mtd_erase(m, ei) (m)->erase(m, ei)+#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops)+#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops)+#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs)+#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs)+#endif++++int nandmtd_erase_block(struct yaffs_dev *dev, int block_no)+{+	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);+	u32 addr =+	    ((loff_t) block_no) * dev->param.total_bytes_per_chunk *+	    dev->param.chunks_per_block;+	struct erase_info ei;+	int retval = 0;++	ei.mtd = mtd;+	ei.addr = addr;+	ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block;+	ei.time = 1000;+	ei.retries = 2;+	ei.callback = NULL;+	ei.priv = (u_long) dev;++	retval = mtd_erase(mtd, &ei);++	if (retval == 0)+		return YAFFS_OK;++	return YAFFS_FAIL;+}+++static 	int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk,+				   const u8 *data, int data_len,+				   const u8 *oob, int oob_len)+{+	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);+	loff_t addr;+	struct mtd_oob_ops ops;+	int retval;++	yaffs_trace(YAFFS_TRACE_MTD,+			"yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n",+			dev, nand_chunk, data, data_len, oob, oob_len);++	if (!data || !data_len) {+		data = NULL;+		data_len = 0;+	}++	if (!oob || !oob_len) {+		oob = NULL;+		oob_len = 0;+	}++	addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;+	memset(&ops, 0, sizeof(ops));+	ops.mode = MTD_OPS_AUTO_OOB;+	ops.len = (data) ? data_len : 0;+	ops.ooblen = oob_len;+	ops.datbuf = (u8 *)data;+	ops.oobbuf = (u8 *)oob;++	retval = mtd_write_oob(mtd, addr, &ops);+	if (retval) {+		yaffs_trace(YAFFS_TRACE_MTD,+			"write_oob failed, chunk %d, mtd error %d",+			nand_chunk, retval);+	}+	return retval ? YAFFS_FAIL : YAFFS_OK;+}++static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk,+				   u8 *data, int data_len,+				   u8 *oob, int oob_len,+				   enum yaffs_ecc_result *ecc_result)+{+	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);+	loff_t addr;+	struct mtd_oob_ops ops;+	int retval;++	addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;+	memset(&ops, 0, sizeof(ops));+	ops.mode = MTD_OPS_AUTO_OOB;+	ops.len = (data) ? data_len : 0;+	ops.ooblen = oob_len;+	ops.datbuf = data;+	ops.oobbuf = oob;++#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20))+	/* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug;+	 * help it out with ops.len = ops.ooblen when ops.datbuf == NULL.+	 */+	ops.len = (ops.datbuf) ? ops.len : ops.ooblen;+#endif+	/* Read page and oob using MTD.+	 * Check status and determine ECC result.+	 */+	retval = mtd_read_oob(mtd, addr, &ops);+	if (retval)+		yaffs_trace(YAFFS_TRACE_MTD,+			"read_oob failed, chunk %d, mtd error %d",+			nand_chunk, retval);++	switch (retval) {+	case 0:+		/* no error */+		if(ecc_result)+			*ecc_result = YAFFS_ECC_RESULT_NO_ERROR;+		break;++	case -EUCLEAN:+		/* MTD's ECC fixed the data */+		if(ecc_result)+			*ecc_result = YAFFS_ECC_RESULT_FIXED;+		dev->n_ecc_fixed++;+		break;++	case -EBADMSG:+	default:+		/* MTD's ECC could not fix the data */+		dev->n_ecc_unfixed++;+		if(ecc_result)+			*ecc_result = YAFFS_ECC_RESULT_UNFIXED;+		return YAFFS_FAIL;+	}++	return YAFFS_OK;+}++static 	int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no)+{+	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);++	loff_t addr;+	struct erase_info ei;+	int retval = 0;+	u32 block_size;++	block_size = dev->param.total_bytes_per_chunk *+		     dev->param.chunks_per_block;+	addr = ((loff_t) block_no) * block_size;++	ei.mtd = mtd;+	ei.addr = addr;+	ei.len = block_size;+	ei.time = 1000;+	ei.retries = 2;+	ei.callback = NULL;+	ei.priv = (u_long) dev;++	retval = mtd_erase(mtd, &ei);++	if (retval == 0)+		return YAFFS_OK;++	return YAFFS_FAIL;+}++static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no)+{+	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);+	int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;+	int retval;++	yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no);++	retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no);+	return (retval) ? YAFFS_FAIL : YAFFS_OK;+}++static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no)+{+	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);+	int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;+	int retval;++	yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no);++	retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no);+	return (retval) ? YAFFS_FAIL : YAFFS_OK;+}++static int yaffs_mtd_initialise(struct yaffs_dev *dev)+{+	return YAFFS_OK;+}++static int yaffs_mtd_deinitialise(struct yaffs_dev *dev)+{+	return YAFFS_OK;+}+++void yaffs_mtd_drv_install(struct yaffs_dev *dev)+{+	struct yaffs_driver *drv = &dev->drv;++	drv->drv_write_chunk_fn = yaffs_mtd_write;+	drv->drv_read_chunk_fn = yaffs_mtd_read;+	drv->drv_erase_fn = yaffs_mtd_erase;+	drv->drv_mark_bad_fn = yaffs_mtd_mark_bad;+	drv->drv_check_bad_fn = yaffs_mtd_check_bad;+	drv->drv_initialise_fn = yaffs_mtd_initialise;+	drv->drv_deinitialise_fn = yaffs_mtd_deinitialise;+}+++struct mtd_info * yaffs_get_mtd_device(dev_t sdev)+{+	struct mtd_info *mtd;++	mtd = yaffs_get_mtd_device(sdev);++	/* Check it's an mtd device..... */+	if (MAJOR(sdev) != MTD_BLOCK_MAJOR)+		return NULL;	/* This isn't an mtd device */++	/* Check it's NAND */+	if (mtd->type != MTD_NANDFLASH) {+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"yaffs: MTD device is not NAND it's type %d",+			mtd->type);+		return NULL;+	}++	yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd));+	yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize);+	yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize);+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)+	yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size);+#else+	yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size);+#endif++	return mtd;+}++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)+{+	if (yaffs_version == 2) {+		if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||+		     mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) &&+		    !inband_tags) {+			yaffs_trace(YAFFS_TRACE_ALWAYS,+				"MTD device does not have the right page sizes"+			);+			return -1;+		}+	} else {+		if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK ||+		    mtd->oobsize != YAFFS_BYTES_PER_SPARE) {+			yaffs_trace(YAFFS_TRACE_ALWAYS,+				"MTD device does not support have the right page sizes"+			);+			return -1;+		}+	}++	return 0;+}+++void yaffs_put_mtd_device(struct mtd_info *mtd)+{+	if(mtd)+		put_mtd_device(mtd);+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_mtdif.h linux-3.4.90/fs/yaffs2/yaffs_mtdif.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_mtdif.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_mtdif.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,25 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_MTDIF_H__+#define __YAFFS_MTDIF_H__++#include "yaffs_guts.h"++void yaffs_mtd_drv_install(struct yaffs_dev *dev);+struct mtd_info * yaffs_get_mtd_device(dev_t sdev);+void yaffs_put_mtd_device(struct mtd_info *mtd);+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags);+#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_nameval.c linux-3.4.90/fs/yaffs2/yaffs_nameval.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_nameval.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_nameval.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,208 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++/*+ * This simple implementation of a name-value store assumes a small number of+* values and fits into a small finite buffer.+ *+ * Each attribute is stored as a record:+ *  sizeof(int) bytes   record size.+ *  strnlen+1 bytes name null terminated.+ *  nbytes    value.+ *  ----------+ *  total size  stored in record size+ *+ * This code has not been tested with unicode yet.+ */++#include "yaffs_nameval.h"++#include "yportenv.h"++static int nval_find(const char *xb, int xb_size, const YCHAR *name,+		     int *exist_size)+{+	int pos = 0;+	int size;++	memcpy(&size, xb, sizeof(int));+	while (size > 0 && (size < xb_size) && (pos + size < xb_size)) {+		if (!strncmp((YCHAR *) (xb + pos + sizeof(int)),+				name, size)) {+			if (exist_size)+				*exist_size = size;+			return pos;+		}+		pos += size;+		if (pos < xb_size - sizeof(int))+			memcpy(&size, xb + pos, sizeof(int));+		else+			size = 0;+	}+	if (exist_size)+		*exist_size = 0;+	return -ENODATA;+}++static int nval_used(const char *xb, int xb_size)+{+	int pos = 0;+	int size;++	memcpy(&size, xb + pos, sizeof(int));+	while (size > 0 && (size < xb_size) && (pos + size < xb_size)) {+		pos += size;+		if (pos < xb_size - sizeof(int))+			memcpy(&size, xb + pos, sizeof(int));+		else+			size = 0;+	}+	return pos;+}++int nval_del(char *xb, int xb_size, const YCHAR *name)+{+	int pos = nval_find(xb, xb_size, name, NULL);+	int size;++	if (pos < 0 || pos >= xb_size)+		return -ENODATA;++	/* Find size, shift rest over this record,+	 * then zero out the rest of buffer */+	memcpy(&size, xb + pos, sizeof(int));+	memcpy(xb + pos, xb + pos + size, xb_size - (pos + size));+	memset(xb + (xb_size - size), 0, size);+	return 0;+}++int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf,+		int bsize, int flags)+{+	int pos;+	int namelen = strnlen(name, xb_size);+	int reclen;+	int size_exist = 0;+	int space;+	int start;++	pos = nval_find(xb, xb_size, name, &size_exist);++	if (flags & XATTR_CREATE && pos >= 0)+		return -EEXIST;+	if (flags & XATTR_REPLACE && pos < 0)+		return -ENODATA;++	start = nval_used(xb, xb_size);+	space = xb_size - start + size_exist;++	reclen = (sizeof(int) + namelen + 1 + bsize);++	if (reclen > space)+		return -ENOSPC;++	if (pos >= 0) {+		nval_del(xb, xb_size, name);+		start = nval_used(xb, xb_size);+	}++	pos = start;++	memcpy(xb + pos, &reclen, sizeof(int));+	pos += sizeof(int);+	strncpy((YCHAR *) (xb + pos), name, reclen);+	pos += (namelen + 1);+	memcpy(xb + pos, buf, bsize);+	return 0;+}++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf,+	     int bsize)+{+	int pos = nval_find(xb, xb_size, name, NULL);+	int size;++	if (pos >= 0 && pos < xb_size) {++		memcpy(&size, xb + pos, sizeof(int));+		pos += sizeof(int);	/* advance past record length */+		size -= sizeof(int);++		/* Advance over name string */+		while (xb[pos] && size > 0 && pos < xb_size) {+			pos++;+			size--;+		}+		/*Advance over NUL */+		pos++;+		size--;++		/* If bsize is zero then this is a size query.+		 * Return the size, but don't copy.+		 */+		if (!bsize)+			return size;++		if (size <= bsize) {+			memcpy(buf, xb + pos, size);+			return size;+		}+	}+	if (pos >= 0)+		return -ERANGE;++	return -ENODATA;+}++int nval_list(const char *xb, int xb_size, char *buf, int bsize)+{+	int pos = 0;+	int size;+	int name_len;+	int ncopied = 0;+	int filled = 0;++	memcpy(&size, xb + pos, sizeof(int));+	while (size > sizeof(int) &&+		size <= xb_size &&+		(pos + size) < xb_size &&+		!filled) {+		pos += sizeof(int);+		size -= sizeof(int);+		name_len = strnlen((YCHAR *) (xb + pos), size);+		if (ncopied + name_len + 1 < bsize) {+			memcpy(buf, xb + pos, name_len * sizeof(YCHAR));+			buf += name_len;+			*buf = '\0';+			buf++;+			if (sizeof(YCHAR) > 1) {+				*buf = '\0';+				buf++;+			}+			ncopied += (name_len + 1);+		} else {+			filled = 1;+		}+		pos += size;+		if (pos < xb_size - sizeof(int))+			memcpy(&size, xb + pos, sizeof(int));+		else+			size = 0;+	}+	return ncopied;+}++int nval_hasvalues(const char *xb, int xb_size)+{+	return nval_used(xb, xb_size) > 0;+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_nameval.h linux-3.4.90/fs/yaffs2/yaffs_nameval.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_nameval.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_nameval.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,28 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __NAMEVAL_H__+#define __NAMEVAL_H__++#include "yportenv.h"++int nval_del(char *xb, int xb_size, const YCHAR * name);+int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf,+	     int bsize, int flags);+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf,+	     int bsize);+int nval_list(const char *xb, int xb_size, char *buf, int bsize);+int nval_hasvalues(const char *xb, int xb_size);+#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_nand.c linux-3.4.90/fs/yaffs2/yaffs_nand.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_nand.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_nand.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,122 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yaffs_nand.h"+#include "yaffs_tagscompat.h"++#include "yaffs_getblockinfo.h"+#include "yaffs_summary.h"++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk)+{+	return chunk - dev->chunk_offset;+}++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk,+			     u8 *buffer, struct yaffs_ext_tags *tags)+{+	int result;+	struct yaffs_ext_tags local_tags;+	int flash_chunk = apply_chunk_offset(dev, nand_chunk);++	dev->n_page_reads++;++	/* If there are no tags provided use local tags. */+	if (!tags)+		tags = &local_tags;++	result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags);+	if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) {++		struct yaffs_block_info *bi;+		bi = yaffs_get_block_info(dev,+					  nand_chunk /+					  dev->param.chunks_per_block);+		yaffs_handle_chunk_error(dev, bi);+	}+	return result;+}++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev,+				int nand_chunk,+				const u8 *buffer, struct yaffs_ext_tags *tags)+{+	int result;+	int flash_chunk = apply_chunk_offset(dev, nand_chunk);++	dev->n_page_writes++;++	if (!tags) {+		yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags");+		BUG();+		return YAFFS_FAIL;+	}++	tags->seq_number = dev->seq_number;+	tags->chunk_used = 1;+	yaffs_trace(YAFFS_TRACE_WRITE,+		"Writing chunk %d tags %d %d",+		nand_chunk, tags->obj_id, tags->chunk_id);++	result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk,+							buffer, tags);++	yaffs_summary_add(dev, tags, nand_chunk);++	return result;+}++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no)+{+	block_no -= dev->block_offset;+	dev->n_bad_markings++;++	if (dev->param.disable_bad_block_marking)+		return YAFFS_OK;++	return dev->tagger.mark_bad_fn(dev, block_no);+}+++int yaffs_query_init_block_state(struct yaffs_dev *dev,+				 int block_no,+				 enum yaffs_block_state *state,+				 u32 *seq_number)+{+	block_no -= dev->block_offset;+	return dev->tagger.query_block_fn(dev, block_no, state, seq_number);+}++int yaffs_erase_block(struct yaffs_dev *dev, int block_no)+{+	int result;++	block_no -= dev->block_offset;+	dev->n_erasures++;+	result = dev->drv.drv_erase_fn(dev, block_no);+	return result;+}++int yaffs_init_nand(struct yaffs_dev *dev)+{+	if (dev->drv.drv_initialise_fn)+		return dev->drv.drv_initialise_fn(dev);+	return YAFFS_OK;+}++int yaffs_deinit_nand(struct yaffs_dev *dev)+{+	if (dev->drv.drv_deinitialise_fn)+		return dev->drv.drv_deinitialise_fn(dev);+	return YAFFS_OK;+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_nand.h linux-3.4.90/fs/yaffs2/yaffs_nand.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_nand.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_nand.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,39 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_NAND_H__+#define __YAFFS_NAND_H__+#include "yaffs_guts.h"++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk,+			     u8 *buffer, struct yaffs_ext_tags *tags);++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev,+			     int nand_chunk,+			     const u8 *buffer, struct yaffs_ext_tags *tags);++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no);++int yaffs_query_init_block_state(struct yaffs_dev *dev,+				 int block_no,+				 enum yaffs_block_state *state,+				 unsigned *seq_number);++int yaffs_erase_block(struct yaffs_dev *dev, int flash_block);++int yaffs_init_nand(struct yaffs_dev *dev);+int yaffs_deinit_nand(struct yaffs_dev *dev);++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.4.90/fs/yaffs2/yaffs_packedtags1.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags1.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_packedtags1.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,56 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yaffs_packedtags1.h"+#include "yportenv.h"++static const u8 all_ff[20] = {+	0xff, 0xff, 0xff, 0xff,+	0xff, 0xff, 0xff, 0xff,+	0xff, 0xff, 0xff, 0xff,+	0xff, 0xff, 0xff, 0xff,+	0xff, 0xff, 0xff, 0xff+};++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt,+		      const struct yaffs_ext_tags *t)+{+	pt->chunk_id = t->chunk_id;+	pt->serial_number = t->serial_number;+	pt->n_bytes = t->n_bytes;+	pt->obj_id = t->obj_id;+	pt->ecc = 0;+	pt->deleted = (t->is_deleted) ? 0 : 1;+	pt->unused_stuff = 0;+	pt->should_be_ff = 0xffffffff;+}++void yaffs_unpack_tags1(struct yaffs_ext_tags *t,+			const struct yaffs_packed_tags1 *pt)+{++	if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) {+		t->block_bad = 0;+		if (pt->should_be_ff != 0xffffffff)+			t->block_bad = 1;+		t->chunk_used = 1;+		t->obj_id = pt->obj_id;+		t->chunk_id = pt->chunk_id;+		t->n_bytes = pt->n_bytes;+		t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR;+		t->is_deleted = (pt->deleted) ? 0 : 1;+		t->serial_number = pt->serial_number;+	} else {+		memset(t, 0, sizeof(struct yaffs_ext_tags));+	}+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.4.90/fs/yaffs2/yaffs_packedtags1.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags1.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_packedtags1.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,39 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */++#ifndef __YAFFS_PACKEDTAGS1_H__+#define __YAFFS_PACKEDTAGS1_H__++#include "yaffs_guts.h"++struct yaffs_packed_tags1 {+	u32 chunk_id:20;+	u32 serial_number:2;+	u32 n_bytes:10;+	u32 obj_id:18;+	u32 ecc:12;+	u32 deleted:1;+	u32 unused_stuff:1;+	unsigned should_be_ff;++};++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt,+		      const struct yaffs_ext_tags *t);+void yaffs_unpack_tags1(struct yaffs_ext_tags *t,+			const struct yaffs_packed_tags1 *pt);+#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.4.90/fs/yaffs2/yaffs_packedtags2.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags2.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_packedtags2.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,197 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yaffs_packedtags2.h"+#include "yportenv.h"+#include "yaffs_trace.h"++/* This code packs a set of extended tags into a binary structure for+ * NAND storage+ */++/* Some of the information is "extra" struff which can be packed in to+ * speed scanning+ * This is defined by having the EXTRA_HEADER_INFO_FLAG set.+ */++/* Extra flags applied to chunk_id */++#define EXTRA_HEADER_INFO_FLAG	0x80000000+#define EXTRA_SHRINK_FLAG	0x40000000+#define EXTRA_SHADOWS_FLAG	0x20000000+#define EXTRA_SPARE_FLAGS	0x10000000++#define ALL_EXTRA_FLAGS		0xf0000000++/* Also, the top 4 bits of the object Id are set to the object type. */+#define EXTRA_OBJECT_TYPE_SHIFT (28)+#define EXTRA_OBJECT_TYPE_MASK  ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT)++static void yaffs_dump_packed_tags2_tags_only(+				const struct yaffs_packed_tags2_tags_only *ptt)+{+	yaffs_trace(YAFFS_TRACE_MTD,+		"packed tags obj %d chunk %d byte %d seq %d",+		ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number);+}++static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt)+{+	yaffs_dump_packed_tags2_tags_only(&pt->t);+}++static void yaffs_dump_tags2(const struct yaffs_ext_tags *t)+{+	yaffs_trace(YAFFS_TRACE_MTD,+		"ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d",+		t->ecc_result, t->block_bad, t->chunk_used, t->obj_id,+		t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number,+		t->seq_number);++}++static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t)+{+	if (t->chunk_id != 0 || !t->extra_available)+		return 0;++	/* Check if the file size is too long to store */+	if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE &&+	    (t->extra_file_size >> 31) != 0)+		return 0;+	return 1;+}++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt,+				const struct yaffs_ext_tags *t)+{+	ptt->chunk_id = t->chunk_id;+	ptt->seq_number = t->seq_number;+	ptt->n_bytes = t->n_bytes;+	ptt->obj_id = t->obj_id;++	/* Only store extra tags for object headers.+	 * If it is a file then only store  if the file size is short\+	 * enough to fit.+	 */+	if (yaffs_check_tags_extra_packable(t)) {+		/* Store the extra header info instead */+		/* We save the parent object in the chunk_id */+		ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id;+		if (t->extra_is_shrink)+			ptt->chunk_id |= EXTRA_SHRINK_FLAG;+		if (t->extra_shadows)+			ptt->chunk_id |= EXTRA_SHADOWS_FLAG;++		ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK;+		ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT);++		if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK)+			ptt->n_bytes = t->extra_equiv_id;+		else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE)+			ptt->n_bytes = (unsigned) t->extra_file_size;+		else+			ptt->n_bytes = 0;+	}++	yaffs_dump_packed_tags2_tags_only(ptt);+	yaffs_dump_tags2(t);+}++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt,+		      const struct yaffs_ext_tags *t, int tags_ecc)+{+	yaffs_pack_tags2_tags_only(&pt->t, t);++	if (tags_ecc)+		yaffs_ecc_calc_other((unsigned char *)&pt->t,+				    sizeof(struct yaffs_packed_tags2_tags_only),+				    &pt->ecc);+}++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t,+				  struct yaffs_packed_tags2_tags_only *ptt)+{+	memset(t, 0, sizeof(struct yaffs_ext_tags));++	if (ptt->seq_number == 0xffffffff)+		return;++	t->block_bad = 0;+	t->chunk_used = 1;+	t->obj_id = ptt->obj_id;+	t->chunk_id = ptt->chunk_id;+	t->n_bytes = ptt->n_bytes;+	t->is_deleted = 0;+	t->serial_number = 0;+	t->seq_number = ptt->seq_number;++	/* Do extra header info stuff */+	if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) {+		t->chunk_id = 0;+		t->n_bytes = 0;++		t->extra_available = 1;+		t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS));+		t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0;+		t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0;+		t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT;+		t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK;++		if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK)+			t->extra_equiv_id = ptt->n_bytes;+		else+			t->extra_file_size = ptt->n_bytes;+	}+	yaffs_dump_packed_tags2_tags_only(ptt);+	yaffs_dump_tags2(t);+}++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt,+			int tags_ecc)+{+	enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR;++	if (pt->t.seq_number != 0xffffffff && tags_ecc) {+		/* Chunk is in use and we need to do ECC */++		struct yaffs_ecc_other ecc;+		int result;+		yaffs_ecc_calc_other((unsigned char *)&pt->t,+				sizeof(struct yaffs_packed_tags2_tags_only),+				&ecc);+		result =+		    yaffs_ecc_correct_other((unsigned char *)&pt->t,+				sizeof(struct yaffs_packed_tags2_tags_only),+				&pt->ecc, &ecc);+		switch (result) {+		case 0:+			ecc_result = YAFFS_ECC_RESULT_NO_ERROR;+			break;+		case 1:+			ecc_result = YAFFS_ECC_RESULT_FIXED;+			break;+		case -1:+			ecc_result = YAFFS_ECC_RESULT_UNFIXED;+			break;+		default:+			ecc_result = YAFFS_ECC_RESULT_UNKNOWN;+		}+	}+	yaffs_unpack_tags2_tags_only(t, &pt->t);++	t->ecc_result = ecc_result;++	yaffs_dump_packed_tags2(pt);+	yaffs_dump_tags2(t);+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.4.90/fs/yaffs2/yaffs_packedtags2.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags2.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_packedtags2.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,47 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++/* This is used to pack YAFFS2 tags, not YAFFS1tags. */++#ifndef __YAFFS_PACKEDTAGS2_H__+#define __YAFFS_PACKEDTAGS2_H__++#include "yaffs_guts.h"+#include "yaffs_ecc.h"++struct yaffs_packed_tags2_tags_only {+	unsigned seq_number;+	unsigned obj_id;+	unsigned chunk_id;+	unsigned n_bytes;+};++struct yaffs_packed_tags2 {+	struct yaffs_packed_tags2_tags_only t;+	struct yaffs_ecc_other ecc;+};++/* Full packed tags with ECC, used for oob tags */+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt,+		      const struct yaffs_ext_tags *t, int tags_ecc);+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt,+			int tags_ecc);++/* Only the tags part (no ECC for use with inband tags */+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt,+				const struct yaffs_ext_tags *t);+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t,+				  struct yaffs_packed_tags2_tags_only *pt);+#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_summary.c linux-3.4.90/fs/yaffs2/yaffs_summary.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_summary.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_summary.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,312 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++/* Summaries write the useful part of the tags for the chunks in a block into an+ * an array which is written to the last n chunks of the block.+ * Reading the summaries gives all the tags for the block in one read. Much+ * faster.+ *+ * Chunks holding summaries are marked with tags making it look like+ * they are part of a fake file.+ *+ * The summary could also be used during gc.+ *+ */++#include "yaffs_summary.h"+#include "yaffs_packedtags2.h"+#include "yaffs_nand.h"+#include "yaffs_getblockinfo.h"+#include "yaffs_bitmap.h"++/*+ * The summary is built up in an array of summary tags.+ * This gets written to the last one or two (maybe more) chunks in a block.+ * A summary header is written as the first part of each chunk of summary data.+ * The summary header must match or the summary is rejected.+ */++/* Summary tags don't need the sequence number because that is redundant. */+struct yaffs_summary_tags {+	unsigned obj_id;+	unsigned chunk_id;+	unsigned n_bytes;+};++/* Summary header */+struct yaffs_summary_header {+	unsigned version;	/* Must match current version */+	unsigned block;		/* Must be this block */+	unsigned seq;		/* Must be this sequence number */+	unsigned sum;		/* Just add up all the bytes in the tags */+};+++static void yaffs_summary_clear(struct yaffs_dev *dev)+{+	if (!dev->sum_tags)+		return;+	memset(dev->sum_tags, 0, dev->chunks_per_summary *+		sizeof(struct yaffs_summary_tags));+}+++void yaffs_summary_deinit(struct yaffs_dev *dev)+{+	kfree(dev->sum_tags);+	dev->sum_tags = NULL;+	kfree(dev->gc_sum_tags);+	dev->gc_sum_tags = NULL;+	dev->chunks_per_summary = 0;+}++int yaffs_summary_init(struct yaffs_dev *dev)+{+	int sum_bytes;+	int chunks_used; /* Number of chunks used by summary */+	int sum_tags_bytes;++	sum_bytes = dev->param.chunks_per_block *+			sizeof(struct yaffs_summary_tags);++	chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/+			(dev->data_bytes_per_chunk -+				sizeof(struct yaffs_summary_header));++	dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used;+	sum_tags_bytes = sizeof(struct yaffs_summary_tags) *+				dev->chunks_per_summary;+	dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS);+	dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS);+	if (!dev->sum_tags || !dev->gc_sum_tags) {+		yaffs_summary_deinit(dev);+		return YAFFS_FAIL;+	}++	yaffs_summary_clear(dev);++	return YAFFS_OK;+}++static unsigned yaffs_summary_sum(struct yaffs_dev *dev)+{+	u8 *sum_buffer = (u8 *)dev->sum_tags;+	int i;+	unsigned sum = 0;++	i = sizeof(struct yaffs_summary_tags) *+				dev->chunks_per_summary;+	while (i > 0) {+		sum += *sum_buffer;+		sum_buffer++;+		i--;+	}++	return sum;+}++static int yaffs_summary_write(struct yaffs_dev *dev, int blk)+{+	struct yaffs_ext_tags tags;+	u8 *buffer;+	u8 *sum_buffer = (u8 *)dev->sum_tags;+	int n_bytes;+	int chunk_in_nand;+	int chunk_in_block;+	int result;+	int this_tx;+	struct yaffs_summary_header hdr;+	int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr);+	struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);++	buffer = yaffs_get_temp_buffer(dev);+	n_bytes = sizeof(struct yaffs_summary_tags) *+				dev->chunks_per_summary;+	memset(&tags, 0, sizeof(struct yaffs_ext_tags));+	tags.obj_id = YAFFS_OBJECTID_SUMMARY;+	tags.chunk_id = 1;+	chunk_in_block = dev->chunks_per_summary;+	chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block ++						dev->chunks_per_summary;+	hdr.version = YAFFS_SUMMARY_VERSION;+	hdr.block = blk;+	hdr.seq = bi->seq_number;+	hdr.sum = yaffs_summary_sum(dev);++	do {+		this_tx = n_bytes;+		if (this_tx > sum_bytes_per_chunk)+			this_tx = sum_bytes_per_chunk;+		memcpy(buffer, &hdr, sizeof(hdr));+		memcpy(buffer + sizeof(hdr), sum_buffer, this_tx);+		tags.n_bytes = this_tx + sizeof(hdr);+		result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand,+						buffer, &tags);++		if (result != YAFFS_OK)+			break;+		yaffs_set_chunk_bit(dev, blk, chunk_in_block);+		bi->pages_in_use++;+		dev->n_free_chunks--;++		n_bytes -= this_tx;+		sum_buffer += this_tx;+		chunk_in_nand++;+		chunk_in_block++;+		tags.chunk_id++;+	} while (result == YAFFS_OK && n_bytes > 0);+	yaffs_release_temp_buffer(dev, buffer);+++	if (result == YAFFS_OK)+		bi->has_summary = 1;+++	return result;+}++int yaffs_summary_read(struct yaffs_dev *dev,+			struct yaffs_summary_tags *st,+			int blk)+{+	struct yaffs_ext_tags tags;+	u8 *buffer;+	u8 *sum_buffer = (u8 *)st;+	int n_bytes;+	int chunk_id;+	int chunk_in_nand;+	int chunk_in_block;+	int result;+	int this_tx;+	struct yaffs_summary_header hdr;+	struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);+	int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr);+	int sum_tags_bytes;++	sum_tags_bytes = sizeof(struct yaffs_summary_tags) *+				dev->chunks_per_summary;+	buffer = yaffs_get_temp_buffer(dev);+	n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary;+	chunk_in_block = dev->chunks_per_summary;+	chunk_in_nand = blk * dev->param.chunks_per_block ++							dev->chunks_per_summary;+	chunk_id = 1;+	do {+		this_tx = n_bytes;+		if (this_tx > sum_bytes_per_chunk)+			this_tx = sum_bytes_per_chunk;+		result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand,+						buffer, &tags);++		if (tags.chunk_id != chunk_id ||+			tags.obj_id != YAFFS_OBJECTID_SUMMARY ||+			tags.chunk_used == 0 ||+			tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||+			tags.n_bytes != (this_tx + sizeof(hdr)))+				result = YAFFS_FAIL;+		if (result != YAFFS_OK)+			break;++		if (st == dev->sum_tags) {+			/* If we're scanning then update the block info */+			yaffs_set_chunk_bit(dev, blk, chunk_in_block);+			bi->pages_in_use++;+		}+		memcpy(&hdr, buffer, sizeof(hdr));+		memcpy(sum_buffer, buffer + sizeof(hdr), this_tx);+		n_bytes -= this_tx;+		sum_buffer += this_tx;+		chunk_in_nand++;+		chunk_in_block++;+		chunk_id++;+	} while (result == YAFFS_OK && n_bytes > 0);+	yaffs_release_temp_buffer(dev, buffer);++	if (result == YAFFS_OK) {+		/* Verify header */+		if (hdr.version != YAFFS_SUMMARY_VERSION ||+		    hdr.seq != bi->seq_number ||+		    hdr.sum != yaffs_summary_sum(dev))+			result = YAFFS_FAIL;+	}++	if (st == dev->sum_tags && result == YAFFS_OK)+		bi->has_summary = 1;++	return result;+}++int yaffs_summary_add(struct yaffs_dev *dev,+			struct yaffs_ext_tags *tags,+			int chunk_in_nand)+{+	struct yaffs_packed_tags2_tags_only tags_only;+	struct yaffs_summary_tags *sum_tags;+	int block_in_nand = chunk_in_nand / dev->param.chunks_per_block;+	int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block;++	if (!dev->sum_tags)+		return YAFFS_OK;++	if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {+		yaffs_pack_tags2_tags_only(&tags_only, tags);+		sum_tags = &dev->sum_tags[chunk_in_block];+		sum_tags->chunk_id = tags_only.chunk_id;+		sum_tags->n_bytes = tags_only.n_bytes;+		sum_tags->obj_id = tags_only.obj_id;++		if (chunk_in_block == dev->chunks_per_summary - 1) {+			/* Time to write out the summary */+			yaffs_summary_write(dev, block_in_nand);+			yaffs_summary_clear(dev);+			yaffs_skip_rest_of_block(dev);+		}+	}+	return YAFFS_OK;+}++int yaffs_summary_fetch(struct yaffs_dev *dev,+			struct yaffs_ext_tags *tags,+			int chunk_in_block)+{+	struct yaffs_packed_tags2_tags_only tags_only;+	struct yaffs_summary_tags *sum_tags;+	if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {+		sum_tags = &dev->sum_tags[chunk_in_block];+		tags_only.chunk_id = sum_tags->chunk_id;+		tags_only.n_bytes = sum_tags->n_bytes;+		tags_only.obj_id = sum_tags->obj_id;+		yaffs_unpack_tags2_tags_only(tags, &tags_only);+		return YAFFS_OK;+	}+	return YAFFS_FAIL;+}++void yaffs_summary_gc(struct yaffs_dev *dev, int blk)+{+	struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);+	int i;++	if (!bi->has_summary)+		return;++	for (i = dev->chunks_per_summary;+	     i < dev->param.chunks_per_block;+	     i++) {+		if (yaffs_check_chunk_bit(dev, blk, i)) {+			yaffs_clear_chunk_bit(dev, blk, i);+			bi->pages_in_use--;+			dev->n_free_chunks++;+		}+	}+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_summary.h linux-3.4.90/fs/yaffs2/yaffs_summary.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_summary.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_summary.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,37 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_SUMMARY_H__+#define __YAFFS_SUMMARY_H__++#include "yaffs_packedtags2.h"+++int yaffs_summary_init(struct yaffs_dev *dev);+void yaffs_summary_deinit(struct yaffs_dev *dev);++int yaffs_summary_add(struct yaffs_dev *dev,+			struct yaffs_ext_tags *tags,+			int chunk_in_block);+int yaffs_summary_fetch(struct yaffs_dev *dev,+			struct yaffs_ext_tags *tags,+			int chunk_in_block);+int yaffs_summary_read(struct yaffs_dev *dev,+			struct yaffs_summary_tags *st,+			int blk);+void yaffs_summary_gc(struct yaffs_dev *dev, int blk);+++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.4.90/fs/yaffs2/yaffs_tagscompat.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_tagscompat.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_tagscompat.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,381 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yaffs_guts.h"+#include "yaffs_tagscompat.h"+#include "yaffs_ecc.h"+#include "yaffs_getblockinfo.h"+#include "yaffs_trace.h"++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);+++/********** Tags ECC calculations  *********/+++void yaffs_calc_tags_ecc(struct yaffs_tags *tags)+{+	/* Calculate an ecc */+	unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;+	unsigned i, j;+	unsigned ecc = 0;+	unsigned bit = 0;++	tags->ecc = 0;++	for (i = 0; i < 8; i++) {+		for (j = 1; j & 0xff; j <<= 1) {+			bit++;+			if (b[i] & j)+				ecc ^= bit;+		}+	}+	tags->ecc = ecc;+}++int yaffs_check_tags_ecc(struct yaffs_tags *tags)+{+	unsigned ecc = tags->ecc;++	yaffs_calc_tags_ecc(tags);++	ecc ^= tags->ecc;++	if (ecc && ecc <= 64) {+		/* TODO: Handle the failure better. Retire? */+		unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;++		ecc--;++		b[ecc / 8] ^= (1 << (ecc & 7));++		/* Now recvalc the ecc */+		yaffs_calc_tags_ecc(tags);++		return 1;	/* recovered error */+	} else if (ecc) {+		/* Wierd ecc failure value */+		/* TODO Need to do somethiong here */+		return -1;	/* unrecovered error */+	}+	return 0;+}++/********** Tags **********/++static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,+				     struct yaffs_tags *tags_ptr)+{+	union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;++	yaffs_calc_tags_ecc(tags_ptr);++	spare_ptr->tb0 = tu->as_bytes[0];+	spare_ptr->tb1 = tu->as_bytes[1];+	spare_ptr->tb2 = tu->as_bytes[2];+	spare_ptr->tb3 = tu->as_bytes[3];+	spare_ptr->tb4 = tu->as_bytes[4];+	spare_ptr->tb5 = tu->as_bytes[5];+	spare_ptr->tb6 = tu->as_bytes[6];+	spare_ptr->tb7 = tu->as_bytes[7];+}++static void yaffs_get_tags_from_spare(struct yaffs_dev *dev,+				      struct yaffs_spare *spare_ptr,+				      struct yaffs_tags *tags_ptr)+{+	union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;+	int result;++	tu->as_bytes[0] = spare_ptr->tb0;+	tu->as_bytes[1] = spare_ptr->tb1;+	tu->as_bytes[2] = spare_ptr->tb2;+	tu->as_bytes[3] = spare_ptr->tb3;+	tu->as_bytes[4] = spare_ptr->tb4;+	tu->as_bytes[5] = spare_ptr->tb5;+	tu->as_bytes[6] = spare_ptr->tb6;+	tu->as_bytes[7] = spare_ptr->tb7;++	result = yaffs_check_tags_ecc(tags_ptr);+	if (result > 0)+		dev->n_tags_ecc_fixed++;+	else if (result < 0)+		dev->n_tags_ecc_unfixed++;+}++static void yaffs_spare_init(struct yaffs_spare *spare)+{+	memset(spare, 0xff, sizeof(struct yaffs_spare));+}++static int yaffs_wr_nand(struct yaffs_dev *dev,+			 int nand_chunk, const u8 *data,+			 struct yaffs_spare *spare)+{+	int data_size = dev->data_bytes_per_chunk;++	return dev->drv.drv_write_chunk_fn(dev, nand_chunk,+				data, data_size,+				(u8 *) spare, sizeof(*spare));+}++static int yaffs_rd_chunk_nand(struct yaffs_dev *dev,+			       int nand_chunk,+			       u8 *data,+			       struct yaffs_spare *spare,+			       enum yaffs_ecc_result *ecc_result,+			       int correct_errors)+{+	int ret_val;+	struct yaffs_spare local_spare;+	int data_size;+	int spare_size;+	int ecc_result1, ecc_result2;+	u8 calc_ecc[3];++	if (!spare) {+		/* If we don't have a real spare, then we use a local one. */+		/* Need this for the calculation of the ecc */+		spare = &local_spare;+	}+	data_size = dev->data_bytes_per_chunk;+	spare_size = sizeof(struct yaffs_spare);++	if (dev->param.use_nand_ecc)+		return dev->drv.drv_read_chunk_fn(dev, nand_chunk,+						data, data_size,+						(u8 *) spare, spare_size,+						ecc_result);+++	/* Handle the ECC at this level. */++	ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk,+						 data, data_size,+						 (u8 *)spare, spare_size,+						NULL);+	if (!data || !correct_errors)+		return ret_val;++	/* Do ECC correction if needed. */+	yaffs_ecc_calc(data, calc_ecc);+	ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc);+	yaffs_ecc_calc(&data[256], calc_ecc);+	ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc);++	if (ecc_result1 > 0) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"**>>yaffs ecc error fix performed on chunk %d:0",+			nand_chunk);+		dev->n_ecc_fixed++;+	} else if (ecc_result1 < 0) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"**>>yaffs ecc error unfixed on chunk %d:0",+			nand_chunk);+		dev->n_ecc_unfixed++;+	}++	if (ecc_result2 > 0) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"**>>yaffs ecc error fix performed on chunk %d:1",+			nand_chunk);+		dev->n_ecc_fixed++;+	} else if (ecc_result2 < 0) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"**>>yaffs ecc error unfixed on chunk %d:1",+			nand_chunk);+		dev->n_ecc_unfixed++;+	}++	if (ecc_result1 || ecc_result2) {+		/* We had a data problem on this page */+		yaffs_handle_rd_data_error(dev, nand_chunk);+	}++	if (ecc_result1 < 0 || ecc_result2 < 0)+		*ecc_result = YAFFS_ECC_RESULT_UNFIXED;+	else if (ecc_result1 > 0 || ecc_result2 > 0)+		*ecc_result = YAFFS_ECC_RESULT_FIXED;+	else+		*ecc_result = YAFFS_ECC_RESULT_NO_ERROR;++	return ret_val;+}++/*+ * Functions for robustisizing+ */++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk)+{+	int flash_block = nand_chunk / dev->param.chunks_per_block;++	/* Mark the block for retirement */+	yaffs_get_block_info(dev, flash_block + dev->block_offset)->+		needs_retiring = 1;+	yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,+		"**>>Block %d marked for retirement",+		flash_block);++	/* TODO:+	 * Just do a garbage collection on the affected block+	 * then retire the block+	 * NB recursion+	 */+}++static int yaffs_tags_compat_wr(struct yaffs_dev *dev,+			 int nand_chunk,+			 const u8 *data, const struct yaffs_ext_tags *ext_tags)+{+	struct yaffs_spare spare;+	struct yaffs_tags tags;++	yaffs_spare_init(&spare);++	if (ext_tags->is_deleted)+		spare.page_status = 0;+	else {+		tags.obj_id = ext_tags->obj_id;+		tags.chunk_id = ext_tags->chunk_id;++		tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1);++		if (dev->data_bytes_per_chunk >= 1024)+			tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3;+		else+			tags.n_bytes_msb = 3;++		tags.serial_number = ext_tags->serial_number;++		if (!dev->param.use_nand_ecc && data) {+			yaffs_ecc_calc(data, spare.ecc1);+			yaffs_ecc_calc(&data[256], spare.ecc2);+		}++		yaffs_load_tags_to_spare(&spare, &tags);+	}+	return yaffs_wr_nand(dev, nand_chunk, data, &spare);+}++static int yaffs_tags_compat_rd(struct yaffs_dev *dev,+			 int nand_chunk,+			 u8 *data, struct yaffs_ext_tags *ext_tags)+{+	struct yaffs_spare spare;+	struct yaffs_tags tags;+	enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN;+	static struct yaffs_spare spare_ff;+	static int init;+	int deleted;++	if (!init) {+		memset(&spare_ff, 0xff, sizeof(spare_ff));+		init = 1;+	}++	if (!yaffs_rd_chunk_nand(dev, nand_chunk,+					data, &spare, &ecc_result, 1))+		return YAFFS_FAIL;++	/* ext_tags may be NULL */+	if (!ext_tags)+		return YAFFS_OK;++	deleted = (hweight8(spare.page_status) < 7) ? 1 : 0;++	ext_tags->is_deleted = deleted;+	ext_tags->ecc_result = ecc_result;+	ext_tags->block_bad = 0;	/* We're reading it */+	/* therefore it is not a bad block */+	ext_tags->chunk_used =+		memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0;++	if (ext_tags->chunk_used) {+		yaffs_get_tags_from_spare(dev, &spare, &tags);+		ext_tags->obj_id = tags.obj_id;+		ext_tags->chunk_id = tags.chunk_id;+		ext_tags->n_bytes = tags.n_bytes_lsb;++		if (dev->data_bytes_per_chunk >= 1024)+			ext_tags->n_bytes |=+				(((unsigned)tags.n_bytes_msb) << 10);++		ext_tags->serial_number = tags.serial_number;+	}++	return YAFFS_OK;+}++static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block)+{+	struct yaffs_spare spare;++	memset(&spare, 0xff, sizeof(struct yaffs_spare));++	spare.block_status = 'Y';++	yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL,+		      &spare);+	yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1,+		      NULL, &spare);++	return YAFFS_OK;+}++static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,+				  int block_no,+				  enum yaffs_block_state *state,+				  u32 *seq_number)+{+	struct yaffs_spare spare0, spare1;+	static struct yaffs_spare spare_ff;+	static int init;+	enum yaffs_ecc_result dummy;++	if (!init) {+		memset(&spare_ff, 0xff, sizeof(spare_ff));+		init = 1;+	}++	*seq_number = 0;++	/* Look for bad block markers in the first two chunks */+	yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block,+			    NULL, &spare0, &dummy, 0);+	yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1,+			    NULL, &spare1, &dummy, 0);++	if (hweight8(spare0.block_status & spare1.block_status) < 7)+		*state = YAFFS_BLOCK_STATE_DEAD;+	else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0)+		*state = YAFFS_BLOCK_STATE_EMPTY;+	else+		*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;++	return YAFFS_OK;+}++void yaffs_tags_compat_install(struct yaffs_dev *dev)+{+	if(dev->param.is_yaffs2)+		return;+	if(!dev->tagger.write_chunk_tags_fn)+		dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr;+	if(!dev->tagger.read_chunk_tags_fn)+		dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd;+	if(!dev->tagger.query_block_fn)+		dev->tagger.query_block_fn = yaffs_tags_compat_query_block;+	if(!dev->tagger.mark_bad_fn)+		dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.4.90/fs/yaffs2/yaffs_tagscompat.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_tagscompat.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_tagscompat.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,44 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_TAGSCOMPAT_H__+#define __YAFFS_TAGSCOMPAT_H__+++#include "yaffs_guts.h"++#if 0+++int yaffs_tags_compat_wr(struct yaffs_dev *dev,+			 int nand_chunk,+			 const u8 *data, const struct yaffs_ext_tags *tags);+int yaffs_tags_compat_rd(struct yaffs_dev *dev,+			 int nand_chunk,+			 u8 *data, struct yaffs_ext_tags *tags);+int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no);+int yaffs_tags_compat_query_block(struct yaffs_dev *dev,+				  int block_no,+				  enum yaffs_block_state *state,+				  u32 *seq_number);++#endif+++void yaffs_tags_compat_install(struct yaffs_dev *dev);+void yaffs_calc_tags_ecc(struct yaffs_tags *tags);+int yaffs_check_tags_ecc(struct yaffs_tags *tags);++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.4.90/fs/yaffs2/yaffs_tagsmarshall.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_tagsmarshall.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_tagsmarshall.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,199 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yaffs_guts.h"+#include "yaffs_trace.h"+#include "yaffs_packedtags2.h"++static int yaffs_tags_marshall_write(struct yaffs_dev *dev,+				    int nand_chunk, const u8 *data,+				    const struct yaffs_ext_tags *tags)+{+	struct yaffs_packed_tags2 pt;+	int retval;++	int packed_tags_size =+	    dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);+	void *packed_tags_ptr =+	    dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;++	yaffs_trace(YAFFS_TRACE_MTD,+		"yaffs_tags_marshall_write chunk %d data %p tags %p",+		nand_chunk, data, tags);++	/* For yaffs2 writing there must be both data and tags.+	 * If we're using inband tags, then the tags are stuffed into+	 * the end of the data buffer.+	 */+	if (!data || !tags)+		BUG();+	else if (dev->param.inband_tags) {+		struct yaffs_packed_tags2_tags_only *pt2tp;+		pt2tp =+		    (struct yaffs_packed_tags2_tags_only *)(data ++							dev->+							data_bytes_per_chunk);+		yaffs_pack_tags2_tags_only(pt2tp, tags);+	} else {+		yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);+	}++	retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk,+			data, dev->param.total_bytes_per_chunk,+			(dev->param.inband_tags) ? NULL : packed_tags_ptr,+			(dev->param.inband_tags) ? 0 : packed_tags_size);++	return retval;+}++static int yaffs_tags_marshall_read(struct yaffs_dev *dev,+				   int nand_chunk, u8 *data,+				   struct yaffs_ext_tags *tags)+{+	int retval = 0;+	int local_data = 0;+	u8 spare_buffer[100];+	enum yaffs_ecc_result ecc_result;++	struct yaffs_packed_tags2 pt;++	int packed_tags_size =+	    dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);+	void *packed_tags_ptr =+	    dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;++	yaffs_trace(YAFFS_TRACE_MTD,+		"yaffs_tags_marshall_read chunk %d data %p tags %p",+		nand_chunk, data, tags);++	if (dev->param.inband_tags) {+		if (!data) {+			local_data = 1;+			data = yaffs_get_temp_buffer(dev);+		}+	}++	if (dev->param.inband_tags || (data && !tags))+		retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk,+					data, dev->param.total_bytes_per_chunk,+					NULL, 0,+					&ecc_result);+	else if (tags)+		retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk,+					data, dev->param.total_bytes_per_chunk,+					spare_buffer, packed_tags_size,+					&ecc_result);+	else+		BUG();+++	if (dev->param.inband_tags) {+		if (tags) {+			struct yaffs_packed_tags2_tags_only *pt2tp;+			pt2tp =+				(struct yaffs_packed_tags2_tags_only *)+				&data[dev->data_bytes_per_chunk];+			yaffs_unpack_tags2_tags_only(tags, pt2tp);+		}+	} else if (tags) {+		memcpy(packed_tags_ptr, spare_buffer, packed_tags_size);+		yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);+	}++	if (local_data)+		yaffs_release_temp_buffer(dev, data);++	if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) {+		tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;+		dev->n_ecc_unfixed++;+	}++	if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) {+		if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR)+			tags->ecc_result = YAFFS_ECC_RESULT_FIXED;+		dev->n_ecc_fixed++;+	}++	if (ecc_result < YAFFS_ECC_RESULT_UNFIXED)+		return YAFFS_OK;+	else+		return YAFFS_FAIL;+}++static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no,+			       enum yaffs_block_state *state,+			       u32 *seq_number)+{+	int retval;++	yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d",+			block_no);++	retval = dev->drv.drv_check_bad_fn(dev, block_no);++	if (retval== YAFFS_FAIL) {+		yaffs_trace(YAFFS_TRACE_MTD, "block is bad");++		*state = YAFFS_BLOCK_STATE_DEAD;+		*seq_number = 0;+	} else {+		struct yaffs_ext_tags t;++		yaffs_tags_marshall_read(dev,+				    block_no * dev->param.chunks_per_block,+				    NULL, &t);++		if (t.chunk_used) {+			*seq_number = t.seq_number;+			*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;+		} else {+			*seq_number = 0;+			*state = YAFFS_BLOCK_STATE_EMPTY;+		}+	}++	yaffs_trace(YAFFS_TRACE_MTD,+		"block query returns  seq %d state %d",+		*seq_number, *state);++	if (retval == 0)+		return YAFFS_OK;+	else+		return YAFFS_FAIL;+}++static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no)+{+	return dev->drv.drv_mark_bad_fn(dev, block_no);++}+++void yaffs_tags_marshall_install(struct yaffs_dev *dev)+{+	if (!dev->param.is_yaffs2)+		return;++	if (!dev->tagger.write_chunk_tags_fn)+		dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write;++	if (!dev->tagger.read_chunk_tags_fn)+		dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read;++	if (!dev->tagger.query_block_fn)+		dev->tagger.query_block_fn = yaffs_tags_marshall_query_block;++	if (!dev->tagger.mark_bad_fn)+		dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad;++}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.4.90/fs/yaffs2/yaffs_tagsmarshall.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_tagsmarshall.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_tagsmarshall.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,22 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_TAGSMARSHALL_H__+#define __YAFFS_TAGSMARSHALL_H__++#include "yaffs_guts.h"+void yaffs_tags_marshall_install(struct yaffs_dev *dev);++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_trace.h linux-3.4.90/fs/yaffs2/yaffs_trace.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_trace.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_trace.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,57 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YTRACE_H__+#define __YTRACE_H__++extern unsigned int yaffs_trace_mask;+extern unsigned int yaffs_wr_attempts;++/*+ * Tracing flags.+ * The flags masked in YAFFS_TRACE_ALWAYS are always traced.+ */++#define YAFFS_TRACE_OS			0x00000002+#define YAFFS_TRACE_ALLOCATE		0x00000004+#define YAFFS_TRACE_SCAN		0x00000008+#define YAFFS_TRACE_BAD_BLOCKS		0x00000010+#define YAFFS_TRACE_ERASE		0x00000020+#define YAFFS_TRACE_GC			0x00000040+#define YAFFS_TRACE_WRITE		0x00000080+#define YAFFS_TRACE_TRACING		0x00000100+#define YAFFS_TRACE_DELETION		0x00000200+#define YAFFS_TRACE_BUFFERS		0x00000400+#define YAFFS_TRACE_NANDACCESS		0x00000800+#define YAFFS_TRACE_GC_DETAIL		0x00001000+#define YAFFS_TRACE_SCAN_DEBUG		0x00002000+#define YAFFS_TRACE_MTD			0x00004000+#define YAFFS_TRACE_CHECKPOINT		0x00008000++#define YAFFS_TRACE_VERIFY		0x00010000+#define YAFFS_TRACE_VERIFY_NAND		0x00020000+#define YAFFS_TRACE_VERIFY_FULL		0x00040000+#define YAFFS_TRACE_VERIFY_ALL		0x000f0000++#define YAFFS_TRACE_SYNC		0x00100000+#define YAFFS_TRACE_BACKGROUND		0x00200000+#define YAFFS_TRACE_LOCK		0x00400000+#define YAFFS_TRACE_MOUNT		0x00800000++#define YAFFS_TRACE_ERROR		0x40000000+#define YAFFS_TRACE_BUG			0x80000000+#define YAFFS_TRACE_ALWAYS		0xf0000000++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_verify.c linux-3.4.90/fs/yaffs2/yaffs_verify.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_verify.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_verify.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,529 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yaffs_verify.h"+#include "yaffs_trace.h"+#include "yaffs_bitmap.h"+#include "yaffs_getblockinfo.h"+#include "yaffs_nand.h"++int yaffs_skip_verification(struct yaffs_dev *dev)+{+	(void) dev;+	return !(yaffs_trace_mask &+		 (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL));+}++static int yaffs_skip_full_verification(struct yaffs_dev *dev)+{+	(void) dev;+	return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL));+}++static int yaffs_skip_nand_verification(struct yaffs_dev *dev)+{+	(void) dev;+	return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND));+}++static const char * const block_state_name[] = {+	"Unknown",+	"Needs scan",+	"Scanning",+	"Empty",+	"Allocating",+	"Full",+	"Dirty",+	"Checkpoint",+	"Collecting",+	"Dead"+};++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n)+{+	int actually_used;+	int in_use;++	if (yaffs_skip_verification(dev))+		return;++	/* Report illegal runtime states */+	if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES)+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Block %d has undefined state %d",+			n, bi->block_state);++	switch (bi->block_state) {+	case YAFFS_BLOCK_STATE_UNKNOWN:+	case YAFFS_BLOCK_STATE_SCANNING:+	case YAFFS_BLOCK_STATE_NEEDS_SCAN:+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Block %d has bad run-state %s",+			n, block_state_name[bi->block_state]);+	}++	/* Check pages in use and soft deletions are legal */++	actually_used = bi->pages_in_use - bi->soft_del_pages;++	if (bi->pages_in_use < 0 ||+	    bi->pages_in_use > dev->param.chunks_per_block ||+	    bi->soft_del_pages < 0 ||+	    bi->soft_del_pages > dev->param.chunks_per_block ||+	    actually_used < 0 || actually_used > dev->param.chunks_per_block)+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Block %d has illegal values pages_in_used %d soft_del_pages %d",+			n, bi->pages_in_use, bi->soft_del_pages);++	/* Check chunk bitmap legal */+	in_use = yaffs_count_chunk_bits(dev, n);+	if (in_use != bi->pages_in_use)+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Block %d has inconsistent values pages_in_use %d counted chunk bits %d",+			n, bi->pages_in_use, in_use);+}++void yaffs_verify_collected_blk(struct yaffs_dev *dev,+				struct yaffs_block_info *bi, int n)+{+	yaffs_verify_blk(dev, bi, n);++	/* After collection the block should be in the erased state */++	if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING &&+	    bi->block_state != YAFFS_BLOCK_STATE_EMPTY) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"Block %d is in state %d after gc, should be erased",+			n, bi->block_state);+	}+}++void yaffs_verify_blocks(struct yaffs_dev *dev)+{+	int i;+	int state_count[YAFFS_NUMBER_OF_BLOCK_STATES];+	int illegal_states = 0;++	if (yaffs_skip_verification(dev))+		return;++	memset(state_count, 0, sizeof(state_count));++	for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {+		struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);+		yaffs_verify_blk(dev, bi, i);++		if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES)+			state_count[bi->block_state]++;+		else+			illegal_states++;+	}++	yaffs_trace(YAFFS_TRACE_VERIFY,	"Block summary");++	yaffs_trace(YAFFS_TRACE_VERIFY,+		"%d blocks have illegal states",+		illegal_states);+	if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1)+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Too many allocating blocks");++	for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++)+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"%s %d blocks",+			block_state_name[i], state_count[i]);++	if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT])+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Checkpoint block count wrong dev %d count %d",+			dev->blocks_in_checkpt,+			state_count[YAFFS_BLOCK_STATE_CHECKPOINT]);++	if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY])+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Erased block count wrong dev %d count %d",+			dev->n_erased_blocks,+			state_count[YAFFS_BLOCK_STATE_EMPTY]);++	if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1)+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Too many collecting blocks %d (max is 1)",+			state_count[YAFFS_BLOCK_STATE_COLLECTING]);+}++/*+ * Verify the object header. oh must be valid, but obj and tags may be NULL in+ * which case those tests will not be performed.+ */+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh,+		     struct yaffs_ext_tags *tags, int parent_check)+{+	if (obj && yaffs_skip_verification(obj->my_dev))+		return;++	if (!(tags && obj && oh)) {+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Verifying object header tags %p obj %p oh %p",+			tags, obj, oh);+		return;+	}++	if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN ||+	    oh->type > YAFFS_OBJECT_TYPE_MAX)+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Obj %d header type is illegal value 0x%x",+			tags->obj_id, oh->type);++	if (tags->obj_id != obj->obj_id)+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Obj %d header mismatch obj_id %d",+			tags->obj_id, obj->obj_id);++	/*+	 * Check that the object's parent ids match if parent_check requested.+	 *+	 * Tests do not apply to the root object.+	 */++	if (parent_check && tags->obj_id > 1 && !obj->parent)+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Obj %d header mismatch parent_id %d obj->parent is NULL",+			tags->obj_id, oh->parent_obj_id);++	if (parent_check && obj->parent &&+	    oh->parent_obj_id != obj->parent->obj_id &&+	    (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED ||+	     obj->parent->obj_id != YAFFS_OBJECTID_DELETED))+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Obj %d header mismatch parent_id %d parent_obj_id %d",+			tags->obj_id, oh->parent_obj_id,+			obj->parent->obj_id);++	if (tags->obj_id > 1 && oh->name[0] == 0)	/* Null name */+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Obj %d header name is NULL",+			obj->obj_id);++	if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff)	/* Junk name */+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Obj %d header name is 0xff",+			obj->obj_id);+}++void yaffs_verify_file(struct yaffs_obj *obj)+{+	u32 x;+	int required_depth;+	int actual_depth;+	int last_chunk;+	u32 offset_in_chunk;+	u32 the_chunk;++	u32 i;+	struct yaffs_dev *dev;+	struct yaffs_ext_tags tags;+	struct yaffs_tnode *tn;+	u32 obj_id;++	if (!obj)+		return;++	if (yaffs_skip_verification(obj->my_dev))+		return;++	dev = obj->my_dev;+	obj_id = obj->obj_id;+++	/* Check file size is consistent with tnode depth */+	yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size,+				&last_chunk, &offset_in_chunk);+	last_chunk++;+	x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS;+	required_depth = 0;+	while (x > 0) {+		x >>= YAFFS_TNODES_INTERNAL_BITS;+		required_depth++;+	}++	actual_depth = obj->variant.file_variant.top_level;++	/* Check that the chunks in the tnode tree are all correct.+	 * We do this by scanning through the tnode tree and+	 * checking the tags for every chunk match.+	 */++	if (yaffs_skip_nand_verification(dev))+		return;++	for (i = 1; i <= last_chunk; i++) {+		tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i);++		if (!tn)+			continue;++		the_chunk = yaffs_get_group_base(dev, tn, i);+		if (the_chunk > 0) {+			yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL,+						 &tags);+			if (tags.obj_id != obj_id || tags.chunk_id != i)+				yaffs_trace(YAFFS_TRACE_VERIFY,+					"Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)",+					obj_id, i, the_chunk,+					tags.obj_id, tags.chunk_id);+		}+	}+}++void yaffs_verify_link(struct yaffs_obj *obj)+{+	if (obj && yaffs_skip_verification(obj->my_dev))+		return;++	/* Verify sane equivalent object */+}++void yaffs_verify_symlink(struct yaffs_obj *obj)+{+	if (obj && yaffs_skip_verification(obj->my_dev))+		return;++	/* Verify symlink string */+}++void yaffs_verify_special(struct yaffs_obj *obj)+{+	if (obj && yaffs_skip_verification(obj->my_dev))+		return;+}++void yaffs_verify_obj(struct yaffs_obj *obj)+{+	struct yaffs_dev *dev;+	u32 chunk_min;+	u32 chunk_max;+	u32 chunk_id_ok;+	u32 chunk_in_range;+	u32 chunk_wrongly_deleted;+	u32 chunk_valid;++	if (!obj)+		return;++	if (obj->being_created)+		return;++	dev = obj->my_dev;++	if (yaffs_skip_verification(dev))+		return;++	/* Check sane object header chunk */++	chunk_min = dev->internal_start_block * dev->param.chunks_per_block;+	chunk_max =+	    (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1;++	chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min &&+			  ((unsigned)(obj->hdr_chunk)) <= chunk_max);+	chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0);+	chunk_valid = chunk_in_range &&+	    yaffs_check_chunk_bit(dev,+				  obj->hdr_chunk / dev->param.chunks_per_block,+				  obj->hdr_chunk % dev->param.chunks_per_block);+	chunk_wrongly_deleted = chunk_in_range && !chunk_valid;++	if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted))+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Obj %d has chunk_id %d %s %s",+			obj->obj_id, obj->hdr_chunk,+			chunk_id_ok ? "" : ",out of range",+			chunk_wrongly_deleted ? ",marked as deleted" : "");++	if (chunk_valid && !yaffs_skip_nand_verification(dev)) {+		struct yaffs_ext_tags tags;+		struct yaffs_obj_hdr *oh;+		u8 *buffer = yaffs_get_temp_buffer(dev);++		oh = (struct yaffs_obj_hdr *)buffer;++		yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags);++		yaffs_verify_oh(obj, oh, &tags, 1);++		yaffs_release_temp_buffer(dev, buffer);+	}++	/* Verify it has a parent */+	if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) {+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Obj %d has parent pointer %p which does not look like an object",+			obj->obj_id, obj->parent);+	}++	/* Verify parent is a directory */+	if (obj->parent &&+	    obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Obj %d's parent is not a directory (type %d)",+			obj->obj_id, obj->parent->variant_type);+	}++	switch (obj->variant_type) {+	case YAFFS_OBJECT_TYPE_FILE:+		yaffs_verify_file(obj);+		break;+	case YAFFS_OBJECT_TYPE_SYMLINK:+		yaffs_verify_symlink(obj);+		break;+	case YAFFS_OBJECT_TYPE_DIRECTORY:+		yaffs_verify_dir(obj);+		break;+	case YAFFS_OBJECT_TYPE_HARDLINK:+		yaffs_verify_link(obj);+		break;+	case YAFFS_OBJECT_TYPE_SPECIAL:+		yaffs_verify_special(obj);+		break;+	case YAFFS_OBJECT_TYPE_UNKNOWN:+	default:+		yaffs_trace(YAFFS_TRACE_VERIFY,+			"Obj %d has illegaltype %d",+		   obj->obj_id, obj->variant_type);+		break;+	}+}++void yaffs_verify_objects(struct yaffs_dev *dev)+{+	struct yaffs_obj *obj;+	int i;+	struct list_head *lh;++	if (yaffs_skip_verification(dev))+		return;++	/* Iterate through the objects in each hash entry */++	for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {+		list_for_each(lh, &dev->obj_bucket[i].list) {+			obj = list_entry(lh, struct yaffs_obj, hash_link);+			yaffs_verify_obj(obj);+		}+	}+}++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj)+{+	struct list_head *lh;+	struct yaffs_obj *list_obj;+	int count = 0;++	if (!obj) {+		yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify");+		BUG();+		return;+	}++	if (yaffs_skip_verification(obj->my_dev))+		return;++	if (!obj->parent) {+		yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent");+		BUG();+		return;+	}++	if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {+		yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory");+		BUG();+	}++	/* Iterate through the objects in each hash entry */++	list_for_each(lh, &obj->parent->variant.dir_variant.children) {+		list_obj = list_entry(lh, struct yaffs_obj, siblings);+		yaffs_verify_obj(list_obj);+		if (obj == list_obj)+			count++;+	}++	if (count != 1) {+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"Object in directory %d times",+			count);+		BUG();+	}+}++void yaffs_verify_dir(struct yaffs_obj *directory)+{+	struct list_head *lh;+	struct yaffs_obj *list_obj;++	if (!directory) {+		BUG();+		return;+	}++	if (yaffs_skip_full_verification(directory->my_dev))+		return;++	if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"Directory has wrong type: %d",+			directory->variant_type);+		BUG();+	}++	/* Iterate through the objects in each hash entry */++	list_for_each(lh, &directory->variant.dir_variant.children) {+		list_obj = list_entry(lh, struct yaffs_obj, siblings);+		if (list_obj->parent != directory) {+			yaffs_trace(YAFFS_TRACE_ALWAYS,+				"Object in directory list has wrong parent %p",+				list_obj->parent);+			BUG();+		}+		yaffs_verify_obj_in_dir(list_obj);+	}+}++static int yaffs_free_verification_failures;++void yaffs_verify_free_chunks(struct yaffs_dev *dev)+{+	int counted;+	int difference;++	if (yaffs_skip_verification(dev))+		return;++	counted = yaffs_count_free_chunks(dev);++	difference = dev->n_free_chunks - counted;++	if (difference) {+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"Freechunks verification failure %d %d %d",+			dev->n_free_chunks, counted, difference);+		yaffs_free_verification_failures++;+	}+}++int yaffs_verify_file_sane(struct yaffs_obj *in)+{+	(void) in;+	return YAFFS_OK;+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_verify.h linux-3.4.90/fs/yaffs2/yaffs_verify.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_verify.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_verify.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,43 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_VERIFY_H__+#define __YAFFS_VERIFY_H__++#include "yaffs_guts.h"++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi,+		      int n);+void yaffs_verify_collected_blk(struct yaffs_dev *dev,+				struct yaffs_block_info *bi, int n);+void yaffs_verify_blocks(struct yaffs_dev *dev);++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh,+		     struct yaffs_ext_tags *tags, int parent_check);+void yaffs_verify_file(struct yaffs_obj *obj);+void yaffs_verify_link(struct yaffs_obj *obj);+void yaffs_verify_symlink(struct yaffs_obj *obj);+void yaffs_verify_special(struct yaffs_obj *obj);+void yaffs_verify_obj(struct yaffs_obj *obj);+void yaffs_verify_objects(struct yaffs_dev *dev);+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj);+void yaffs_verify_dir(struct yaffs_obj *directory);+void yaffs_verify_free_chunks(struct yaffs_dev *dev);++int yaffs_verify_file_sane(struct yaffs_obj *obj);++int yaffs_skip_verification(struct yaffs_dev *dev);++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_vfs.c linux-3.4.90/fs/yaffs2/yaffs_vfs.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_vfs.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_vfs.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,3600 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ * Acknowledgements:+ * Luc van OostenRyck for numerous patches.+ * Nick Bane for numerous patches.+ * Nick Bane for 2.5/2.6 integration.+ * Andras Toth for mknod rdev issue.+ * Michael Fischer for finding the problem with inode inconsistency.+ * Some code bodily lifted from JFFS+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++/*+ *+ * This is the file system front-end to YAFFS that hooks it up to+ * the VFS.+ *+ * Special notes:+ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with+ *         this superblock+ * >> 2.6: sb->s_fs_info  points to the struct yaffs_dev associated with this+ *         superblock+ * >> inode->u.generic_ip points to the associated struct yaffs_obj.+ */++/*+ * There are two variants of the VFS glue code. This variant should compile+ * for any version of Linux.+ */+#include <linux/version.h>++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10))+#define YAFFS_COMPILE_BACKGROUND+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23))+#define YAFFS_COMPILE_FREEZER+#endif+#endif++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28))+#define YAFFS_COMPILE_EXPORTFS+#endif++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35))+#define YAFFS_USE_SETATTR_COPY+#define YAFFS_USE_TRUNCATE_SETSIZE+#endif+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35))+#define YAFFS_HAS_EVICT_INODE+#endif++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13))+#define YAFFS_NEW_FOLLOW_LINK 1+#else+#define YAFFS_NEW_FOLLOW_LINK 0+#endif++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))+#define YAFFS_HAS_WRITE_SUPER+#endif+++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19))+#include <linux/config.h>+#endif++#include <linux/kernel.h>+#include <linux/module.h>+#include <linux/slab.h>+#include <linux/init.h>+#include <linux/fs.h>+#include <linux/proc_fs.h>+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39))+#include <linux/smp_lock.h>+#endif+#include <linux/pagemap.h>+#include <linux/mtd/mtd.h>+#include <linux/interrupt.h>+#include <linux/string.h>+#include <linux/ctype.h>++#if (YAFFS_NEW_FOLLOW_LINK == 1)+#include <linux/namei.h>+#endif++#ifdef YAFFS_COMPILE_EXPORTFS+#include <linux/exportfs.h>+#endif++#ifdef YAFFS_COMPILE_BACKGROUND+#include <linux/kthread.h>+#include <linux/delay.h>+#endif+#ifdef YAFFS_COMPILE_FREEZER+#include <linux/freezer.h>+#endif++#include <asm/div64.h>++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))++#include <linux/statfs.h>++#define UnlockPage(p) unlock_page(p)+#define Page_Uptodate(page)	test_bit(PG_uptodate, &(page)->flags)++/* FIXME: use sb->s_id instead ? */+#define yaffs_devname(sb, buf)	bdevname(sb->s_bdev, buf)++#else++#include <linux/locks.h>+#define	BDEVNAME_SIZE		0+#define	yaffs_devname(sb, buf)	kdevname(sb->s_dev)++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0))+/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */+#define __user+#endif++#endif++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26))+#define YPROC_ROOT  (&proc_root)+#else+#define YPROC_ROOT  NULL+#endif++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26))+#define Y_INIT_TIMER(a)	init_timer(a)+#else+#define Y_INIT_TIMER(a)	init_timer_on_stack(a)+#endif++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27))+#define YAFFS_USE_WRITE_BEGIN_END 1+#else+#define YAFFS_USE_WRITE_BEGIN_END 0+#endif++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))+#define YAFFS_SUPER_HAS_DIRTY+#endif+++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))+#define set_nlink(inode, count)  do { (inode)->i_nlink = (count); } while(0)+#endif++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28))+static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size)+{+	uint64_t result = partition_size;+	do_div(result, block_size);+	return (uint32_t) result;+}+#else+#define YCALCBLOCKS(s, b) ((s)/(b))+#endif++#include <linux/uaccess.h>+#include <linux/mtd/mtd.h>++#include "yportenv.h"+#include "yaffs_trace.h"+#include "yaffs_guts.h"+#include "yaffs_attribs.h"++#include "yaffs_linux.h"++#include "yaffs_mtdif.h"+#include "yaffs_packedtags2.h"+#include "yaffs_getblockinfo.h"++unsigned int yaffs_trace_mask =+		YAFFS_TRACE_BAD_BLOCKS |+		YAFFS_TRACE_ALWAYS |+		0;++unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS;+unsigned int yaffs_auto_checkpoint = 1;+unsigned int yaffs_gc_control = 1;+unsigned int yaffs_bg_enable = 1;+unsigned int yaffs_auto_select = 1;+/* Module Parameters */+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+module_param(yaffs_trace_mask, uint, 0644);+module_param(yaffs_wr_attempts, uint, 0644);+module_param(yaffs_auto_checkpoint, uint, 0644);+module_param(yaffs_gc_control, uint, 0644);+module_param(yaffs_bg_enable, uint, 0644);+#else+MODULE_PARM(yaffs_trace_mask, "i");+MODULE_PARM(yaffs_wr_attempts, "i");+MODULE_PARM(yaffs_auto_checkpoint, "i");+MODULE_PARM(yaffs_gc_control, "i");+#endif++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))+/* use iget and read_inode */+#define Y_IGET(sb, inum) iget((sb), (inum))++#else+/* Call local equivalent */+#define YAFFS_USE_OWN_IGET+#define Y_IGET(sb, inum) yaffs_iget((sb), (inum))++#endif++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18))+#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private)+#else+#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip)+#endif++#define yaffs_inode_to_obj(iptr) \+	((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr)))+#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode)++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+#define yaffs_super_to_dev(sb)	((struct yaffs_dev *)sb->s_fs_info)+#else+#define yaffs_super_to_dev(sb)	((struct yaffs_dev *)sb->u.generic_sbp)+#endif++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))+#define Y_CLEAR_INODE(i) clear_inode(i)+#else+#define Y_CLEAR_INODE(i) end_writeback(i)+#endif+++#define update_dir_time(dir) do {\+			(dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \+		} while (0)++static void yaffs_fill_inode_from_obj(struct inode *inode,+				      struct yaffs_obj *obj);+++static void yaffs_gross_lock(struct yaffs_dev *dev)+{+	yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current);+	mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock));+	yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current);+}++static void yaffs_gross_unlock(struct yaffs_dev *dev)+{+	yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current);+	mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock));+}+++static int yaffs_readpage_nolock(struct file *f, struct page *pg)+{+	/* Lifted from jffs2 */++	struct yaffs_obj *obj;+	unsigned char *pg_buf;+	int ret;+	loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT;+	struct yaffs_dev *dev;++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_readpage_nolock at %lld, size %08x",+		(long long)pos,+		(unsigned)PAGE_CACHE_SIZE);++	obj = yaffs_dentry_to_obj(f->f_dentry);++	dev = obj->my_dev;++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+	BUG_ON(!PageLocked(pg));+#else+	if (!PageLocked(pg))+		PAGE_BUG(pg);+#endif++	pg_buf = kmap(pg);+	/* FIXME: Can kmap fail? */++	yaffs_gross_lock(dev);++	ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE);++	yaffs_gross_unlock(dev);++	if (ret >= 0)+		ret = 0;++	if (ret) {+		ClearPageUptodate(pg);+		SetPageError(pg);+	} else {+		SetPageUptodate(pg);+		ClearPageError(pg);+	}++	flush_dcache_page(pg);+	kunmap(pg);++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done");+	return ret;+}++static int yaffs_readpage_unlock(struct file *f, struct page *pg)+{+	int ret = yaffs_readpage_nolock(f, pg);+	UnlockPage(pg);+	return ret;+}++static int yaffs_readpage(struct file *f, struct page *pg)+{+	int ret;++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage");+	ret = yaffs_readpage_unlock(f, pg);+	yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done");+	return ret;+}++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))+#define YCRED_FSUID()	from_kuid(&init_user_ns, current_fsuid())+#define YCRED_FSGID()	from_kgid(&init_user_ns, current_fsgid())+#else+#define YCRED_FSUID()	YCRED(current)->fsuid+#define YCRED_FSGID()	YCRED(current)->fsgid++static inline uid_t i_uid_read(const struct inode *inode)+{+	return inode->i_uid;+}++static inline gid_t i_gid_read(const struct inode *inode)+{+	return inode->i_gid;+}++static inline void i_uid_write(struct inode *inode, uid_t uid)+{+	inode->i_uid = uid;+}++static inline void i_gid_write(struct inode *inode, gid_t gid)+{+	inode->i_gid = gid;+}+#endif++static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val)+{+	struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev);++	if (lc)+		lc->dirty = val;++# ifdef YAFFS_SUPER_HAS_DIRTY+	{+		struct super_block *sb = lc->super;++		if (sb)+			sb->s_dirt = val;+	}+#endif++}++static void yaffs_set_super_dirty(struct yaffs_dev *dev)+{+	yaffs_set_super_dirty_val(dev, 1);+}++static void yaffs_clear_super_dirty(struct yaffs_dev *dev)+{+	yaffs_set_super_dirty_val(dev, 0);+}++static int yaffs_check_super_dirty(struct yaffs_dev *dev)+{+	struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev);++	if (lc && lc->dirty)+		return 1;++# ifdef YAFFS_SUPER_HAS_DIRTY+	{+		struct super_block *sb = lc->super;++		if (sb && sb->s_dirt)+			return 1;+	}+#endif+	return 0;++}++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+static int yaffs_writepage(struct page *page, struct writeback_control *wbc)+#else+static int yaffs_writepage(struct page *page)+#endif+{+	struct yaffs_dev *dev;+	struct address_space *mapping = page->mapping;+	struct inode *inode;+	unsigned long end_index;+	char *buffer;+	struct yaffs_obj *obj;+	int n_written = 0;+	unsigned n_bytes;+	loff_t i_size;++	if (!mapping)+		BUG();+	inode = mapping->host;+	if (!inode)+		BUG();+	i_size = i_size_read(inode);++	end_index = i_size >> PAGE_CACHE_SHIFT;++	if (page->index < end_index)+		n_bytes = PAGE_CACHE_SIZE;+	else {+		n_bytes = i_size & (PAGE_CACHE_SIZE - 1);++		if (page->index > end_index || !n_bytes) {+			yaffs_trace(YAFFS_TRACE_OS,+				"yaffs_writepage at %lld, inode size = %lld!!",+				((loff_t)page->index) << PAGE_CACHE_SHIFT,+				inode->i_size);+			yaffs_trace(YAFFS_TRACE_OS,+				"                -> don't care!!");++			zero_user_segment(page, 0, PAGE_CACHE_SIZE);+			set_page_writeback(page);+			unlock_page(page);+			end_page_writeback(page);+			return 0;+		}+	}++	if (n_bytes != PAGE_CACHE_SIZE)+		zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE);++	get_page(page);++	buffer = kmap(page);++	obj = yaffs_inode_to_obj(inode);+	dev = obj->my_dev;+	yaffs_gross_lock(dev);++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_writepage at %lld, size %08x",+		((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes);+	yaffs_trace(YAFFS_TRACE_OS,+		"writepag0: obj = %lld, ino = %lld",+		obj->variant.file_variant.file_size, inode->i_size);++	n_written = yaffs_wr_file(obj, buffer,+				  ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0);++	yaffs_set_super_dirty(dev);++	yaffs_trace(YAFFS_TRACE_OS,+		"writepag1: obj = %lld, ino = %lld",+		obj->variant.file_variant.file_size, inode->i_size);++	yaffs_gross_unlock(dev);++	kunmap(page);+	set_page_writeback(page);+	unlock_page(page);+	end_page_writeback(page);+	put_page(page);++	return (n_written == n_bytes) ? 0 : -ENOSPC;+}++/* Space holding and freeing is done to ensure we have space available for write_begin/end */+/* For now we just assume few parallel writes and check against a small number. */+/* Todo: need to do this with a counter to handle parallel reads better */++static ssize_t yaffs_hold_space(struct file *f)+{+	struct yaffs_obj *obj;+	struct yaffs_dev *dev;++	int n_free_chunks;++	obj = yaffs_dentry_to_obj(f->f_dentry);++	dev = obj->my_dev;++	yaffs_gross_lock(dev);++	n_free_chunks = yaffs_get_n_free_chunks(dev);++	yaffs_gross_unlock(dev);++	return (n_free_chunks > 20) ? 1 : 0;+}++static void yaffs_release_space(struct file *f)+{+	struct yaffs_obj *obj;+	struct yaffs_dev *dev;++	obj = yaffs_dentry_to_obj(f->f_dentry);++	dev = obj->my_dev;++	yaffs_gross_lock(dev);++	yaffs_gross_unlock(dev);+}++#if (YAFFS_USE_WRITE_BEGIN_END > 0)+static int yaffs_write_begin(struct file *filp, struct address_space *mapping,+			     loff_t pos, unsigned len, unsigned flags,+			     struct page **pagep, void **fsdata)+{+	struct page *pg = NULL;+	pgoff_t index = pos >> PAGE_CACHE_SHIFT;++	int ret = 0;+	int space_held = 0;++	/* Get a page */+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)+	pg = grab_cache_page_write_begin(mapping, index, flags);+#else+	pg = __grab_cache_page(mapping, index);+#endif++	*pagep = pg;+	if (!pg) {+		ret = -ENOMEM;+		goto out;+	}+	yaffs_trace(YAFFS_TRACE_OS,+		"start yaffs_write_begin index %d(%x) uptodate %d",+		(int)index, (int)index, Page_Uptodate(pg) ? 1 : 0);++	/* Get fs space */+	space_held = yaffs_hold_space(filp);++	if (!space_held) {+		ret = -ENOSPC;+		goto out;+	}++	/* Update page if required */++	if (!Page_Uptodate(pg))+		ret = yaffs_readpage_nolock(filp, pg);++	if (ret)+		goto out;++	/* Happy path return */+	yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok");++	return 0;++out:+	yaffs_trace(YAFFS_TRACE_OS,+		"end yaffs_write_begin fail returning %d", ret);+	if (space_held)+		yaffs_release_space(filp);+	if (pg) {+		unlock_page(pg);+		page_cache_release(pg);+	}+	return ret;+}++#else++static int yaffs_prepare_write(struct file *f, struct page *pg,+			       unsigned offset, unsigned to)+{+	yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write");++	if (!Page_Uptodate(pg))+		return yaffs_readpage_nolock(f, pg);+	return 0;+}+#endif+++static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n,+				loff_t * pos)+{+	struct yaffs_obj *obj;+	int n_written;+	loff_t ipos;+	struct inode *inode;+	struct yaffs_dev *dev;++	obj = yaffs_dentry_to_obj(f->f_dentry);++	if (!obj) {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_file_write: hey obj is null!");+                return -EINVAL;+        }++	dev = obj->my_dev;++	yaffs_gross_lock(dev);++	inode = f->f_dentry->d_inode;++	if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND)+		ipos = inode->i_size;+	else+		ipos = *pos;++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld",+		(unsigned)n, (unsigned)n, obj->obj_id, ipos);++	n_written = yaffs_wr_file(obj, buf, ipos, n, 0);++	yaffs_set_super_dirty(dev);++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_file_write: %d(%x) bytes written",+		(unsigned)n, (unsigned)n);++	if (n_written > 0) {+		ipos += n_written;+		*pos = ipos;+		if (ipos > inode->i_size) {+			inode->i_size = ipos;+			inode->i_blocks = (ipos + 511) >> 9;++			yaffs_trace(YAFFS_TRACE_OS,+				"yaffs_file_write size updated to %lld bytes, %d blocks",+				ipos, (int)(inode->i_blocks));+		}++	}+	yaffs_gross_unlock(dev);+	return (n_written == 0) && (n > 0) ? -ENOSPC : n_written;+}+++#if (YAFFS_USE_WRITE_BEGIN_END > 0)+static int yaffs_write_end(struct file *filp, struct address_space *mapping,+			   loff_t pos, unsigned len, unsigned copied,+			   struct page *pg, void *fsdadata)+{+	int ret = 0;+	void *addr, *kva;+	uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1);++	kva = kmap(pg);+	addr = kva + offset_into_page;++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_write_end addr %p pos %lld n_bytes %d",+		addr, pos, copied);++	ret = yaffs_file_write(filp, addr, copied, &pos);++	if (ret != copied) {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_write_end not same size ret %d  copied %d",+			ret, copied);+		SetPageError(pg);+	}++	kunmap(pg);++	yaffs_release_space(filp);+	unlock_page(pg);+	page_cache_release(pg);+	return ret;+}+#else++static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset,+			      unsigned to)+{+	void *addr, *kva;++	loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset;+	int n_bytes = to - offset;+	int n_written;++	kva = kmap(pg);+	addr = kva + offset;++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_commit_write addr %p pos %lld n_bytes %d",+		addr, pos, n_bytes);++	n_written = yaffs_file_write(f, addr, n_bytes, &pos);++	if (n_written != n_bytes) {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_commit_write not same size n_written %d  n_bytes %d",+			n_written, n_bytes);+		SetPageError(pg);+	}+	kunmap(pg);++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_commit_write returning %d",+		n_written == n_bytes ? 0 : n_written);++	return n_written == n_bytes ? 0 : n_written;+}+#endif++static struct address_space_operations yaffs_file_address_operations = {+	.readpage = yaffs_readpage,+	.writepage = yaffs_writepage,+#if (YAFFS_USE_WRITE_BEGIN_END > 0)+	.write_begin = yaffs_write_begin,+	.write_end = yaffs_write_end,+#else+	.prepare_write = yaffs_prepare_write,+	.commit_write = yaffs_commit_write,+#endif+};+++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))+static int yaffs_file_flush(struct file *file, fl_owner_t id)+#else+static int yaffs_file_flush(struct file *file)+#endif+{+	struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry);++	struct yaffs_dev *dev = obj->my_dev;++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_file_flush object %d (%s)",+		obj->obj_id,+		obj->dirty ? "dirty" : "clean");++	yaffs_gross_lock(dev);++	yaffs_flush_file(obj, 1, 0);++	yaffs_gross_unlock(dev);++	return 0;+}+++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))+static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync)+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34))+static int yaffs_sync_object(struct file *file, int datasync)+#else+static int yaffs_sync_object(struct file *file, struct dentry *dentry,+			     int datasync)+#endif+{+	struct yaffs_obj *obj;+	struct yaffs_dev *dev;+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34))+	struct dentry *dentry = file->f_path.dentry;+#endif++	obj = yaffs_dentry_to_obj(dentry);++	dev = obj->my_dev;++	yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC,+		"yaffs_sync_object");+	yaffs_gross_lock(dev);+	yaffs_flush_file(obj, 1, datasync);+	yaffs_gross_unlock(dev);+	return 0;+}+++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22))+static const struct file_operations yaffs_file_operations = {+	.read = do_sync_read,+	.write = do_sync_write,+	.aio_read = generic_file_aio_read,+	.aio_write = generic_file_aio_write,+	.mmap = generic_file_mmap,+	.flush = yaffs_file_flush,+	.fsync = yaffs_sync_object,+	.splice_read = generic_file_splice_read,+	.splice_write = generic_file_splice_write,+	.llseek = generic_file_llseek,+};++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18))++static const struct file_operations yaffs_file_operations = {+	.read = do_sync_read,+	.write = do_sync_write,+	.aio_read = generic_file_aio_read,+	.aio_write = generic_file_aio_write,+	.mmap = generic_file_mmap,+	.flush = yaffs_file_flush,+	.fsync = yaffs_sync_object,+	.sendfile = generic_file_sendfile,+};++#else++static const struct file_operations yaffs_file_operations = {+	.read = generic_file_read,+	.write = generic_file_write,+	.mmap = generic_file_mmap,+	.flush = yaffs_file_flush,+	.fsync = yaffs_sync_object,+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+	.sendfile = generic_file_sendfile,+#endif+};+#endif+++++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))+static void zero_user_segment(struct page *page, unsigned start, unsigned end)+{+	void *kaddr = kmap_atomic(page, KM_USER0);+	memset(kaddr + start, 0, end - start);+	kunmap_atomic(kaddr, KM_USER0);+	flush_dcache_page(page);+}+#endif+++static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize)+{+#ifdef YAFFS_USE_TRUNCATE_SETSIZE+	truncate_setsize(inode, newsize);+	return 0;+#else+	truncate_inode_pages(&inode->i_data, newsize);+	return 0;+#endif++}+++static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr)+{+#ifdef YAFFS_USE_SETATTR_COPY+	setattr_copy(inode, attr);+	return 0;+#else+	return inode_setattr(inode, attr);+#endif++}++static int yaffs_setattr(struct dentry *dentry, struct iattr *attr)+{+	struct inode *inode = dentry->d_inode;+	int error = 0;+	struct yaffs_dev *dev;++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_setattr of object %d",+		yaffs_inode_to_obj(inode)->obj_id);+#if 0+	/* Fail if a requested resize >= 2GB */+	if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31))+		error = -EINVAL;+#endif++	if (error == 0)+		error = inode_change_ok(inode, attr);+	if (error == 0) {+		int result;+		if (!error) {+			error = yaffs_vfs_setattr(inode, attr);+			yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called");+			if (attr->ia_valid & ATTR_SIZE) {+				yaffs_vfs_setsize(inode, attr->ia_size);+				inode->i_blocks = (inode->i_size + 511) >> 9;+			}+		}+		dev = yaffs_inode_to_obj(inode)->my_dev;+		if (attr->ia_valid & ATTR_SIZE) {+			yaffs_trace(YAFFS_TRACE_OS,+				"resize to %d(%x)",+				(int)(attr->ia_size),+				(int)(attr->ia_size));+		}+		yaffs_gross_lock(dev);+		result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr);+		if (result == YAFFS_OK) {+			error = 0;+		} else {+			error = -EPERM;+		}+		yaffs_gross_unlock(dev);++	}++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error);++	return error;+}++static int yaffs_setxattr(struct dentry *dentry, const char *name,+		   const void *value, size_t size, int flags)+{+	struct inode *inode = dentry->d_inode;+	int error = 0;+	struct yaffs_dev *dev;+	struct yaffs_obj *obj = yaffs_inode_to_obj(inode);++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id);++	if (error == 0) {+		int result;+		dev = obj->my_dev;+		yaffs_gross_lock(dev);+		result = yaffs_set_xattrib(obj, name, value, size, flags);+		if (result == YAFFS_OK)+			error = 0;+		else if (result < 0)+			error = result;+		yaffs_gross_unlock(dev);++	}+	yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error);++	return error;+}++static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name,+			void *buff, size_t size)+{+	struct inode *inode = dentry->d_inode;+	int error = 0;+	struct yaffs_dev *dev;+	struct yaffs_obj *obj = yaffs_inode_to_obj(inode);++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_getxattr \"%s\" from object %d",+		name, obj->obj_id);++	if (error == 0) {+		dev = obj->my_dev;+		yaffs_gross_lock(dev);+		error = yaffs_get_xattrib(obj, name, buff, size);+		yaffs_gross_unlock(dev);++	}+	yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error);++	return error;+}++static int yaffs_removexattr(struct dentry *dentry, const char *name)+{+	struct inode *inode = dentry->d_inode;+	int error = 0;+	struct yaffs_dev *dev;+	struct yaffs_obj *obj = yaffs_inode_to_obj(inode);++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_removexattr of object %d", obj->obj_id);++	if (error == 0) {+		int result;+		dev = obj->my_dev;+		yaffs_gross_lock(dev);+		result = yaffs_remove_xattrib(obj, name);+		if (result == YAFFS_OK)+			error = 0;+		else if (result < 0)+			error = result;+		yaffs_gross_unlock(dev);++	}+	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_removexattr done returning %d", error);++	return error;+}++static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size)+{+	struct inode *inode = dentry->d_inode;+	int error = 0;+	struct yaffs_dev *dev;+	struct yaffs_obj *obj = yaffs_inode_to_obj(inode);++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_listxattr of object %d", obj->obj_id);++	if (error == 0) {+		dev = obj->my_dev;+		yaffs_gross_lock(dev);+		error = yaffs_list_xattrib(obj, buff, size);+		yaffs_gross_unlock(dev);++	}+	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_listxattr done returning %d", error);++	return error;+}+++static const struct inode_operations yaffs_file_inode_operations = {+	.setattr = yaffs_setattr,+	.setxattr = yaffs_setxattr,+	.getxattr = yaffs_getxattr,+	.listxattr = yaffs_listxattr,+	.removexattr = yaffs_removexattr,+};+++static int yaffs_readlink(struct dentry *dentry, char __user * buffer,+			  int buflen)+{+	unsigned char *alias;+	int ret;++	struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev;++	yaffs_gross_lock(dev);++	alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry));++	yaffs_gross_unlock(dev);++	if (!alias)+		return -ENOMEM;++	ret = vfs_readlink(dentry, buffer, buflen, alias);+	kfree(alias);+	return ret;+}++#if (YAFFS_NEW_FOLLOW_LINK == 1)+static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)+{+	void *ret;+#else+static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)+{+	int ret+#endif+	unsigned char *alias;+	int ret_int = 0;+	struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev;++	yaffs_gross_lock(dev);++	alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry));+	yaffs_gross_unlock(dev);++	if (!alias) {+		ret_int = -ENOMEM;+		goto out;+	}+#if (YAFFS_NEW_FOLLOW_LINK == 1)+	nd_set_link(nd, alias);+	ret = alias;+out:+	if (ret_int)+		ret = ERR_PTR(ret_int);+	return ret;+#else+	ret = vfs_follow_link(nd, alias);+	kfree(alias);+out:+	if (ret_int)+		ret = ret_int;+	return ret;+#endif+}+++#ifdef YAFFS_HAS_PUT_INODE++/* For now put inode is just for debugging+ * Put inode is called when the inode **structure** is put.+ */+static void yaffs_put_inode(struct inode *inode)+{+	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_put_inode: ino %d, count %d"),+		(int)inode->i_ino, atomic_read(&inode->i_count);++}+#endif++#if (YAFFS_NEW_FOLLOW_LINK == 1)+void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias)+{+	kfree(alias);+}+#endif++static const struct inode_operations yaffs_symlink_inode_operations = {+	.readlink = yaffs_readlink,+	.follow_link = yaffs_follow_link,+#if (YAFFS_NEW_FOLLOW_LINK == 1)+	.put_link = yaffs_put_link,+#endif+	.setattr = yaffs_setattr,+	.setxattr = yaffs_setxattr,+	.getxattr = yaffs_getxattr,+	.listxattr = yaffs_listxattr,+	.removexattr = yaffs_removexattr,+};++#ifdef YAFFS_USE_OWN_IGET++static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino)+{+	struct inode *inode;+	struct yaffs_obj *obj;+	struct yaffs_dev *dev = yaffs_super_to_dev(sb);++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino);++	inode = iget_locked(sb, ino);+	if (!inode)+		return ERR_PTR(-ENOMEM);+	if (!(inode->i_state & I_NEW))+		return inode;++	/* NB This is called as a side effect of other functions, but+	 * we had to release the lock to prevent deadlocks, so+	 * need to lock again.+	 */++	yaffs_gross_lock(dev);++	obj = yaffs_find_by_number(dev, inode->i_ino);++	yaffs_fill_inode_from_obj(inode, obj);++	yaffs_gross_unlock(dev);++	unlock_new_inode(inode);+	return inode;+}++#else++static void yaffs_read_inode(struct inode *inode)+{+	/* NB This is called as a side effect of other functions, but+	 * we had to release the lock to prevent deadlocks, so+	 * need to lock again.+	 */++	struct yaffs_obj *obj;+	struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb);++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_read_inode for %d", (int)inode->i_ino);++	if (current != yaffs_dev_to_lc(dev)->readdir_process)+		yaffs_gross_lock(dev);++	obj = yaffs_find_by_number(dev, inode->i_ino);++	yaffs_fill_inode_from_obj(inode, obj);++	if (current != yaffs_dev_to_lc(dev)->readdir_process)+		yaffs_gross_unlock(dev);+}++#endif++++struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,+			      struct yaffs_obj *obj)+{+	struct inode *inode;++	if (!sb) {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_get_inode for NULL super_block!!");+		return NULL;++	}++	if (!obj) {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_get_inode for NULL object!!");+		return NULL;++	}++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_get_inode for object %d", obj->obj_id);++	inode = Y_IGET(sb, obj->obj_id);+	if (IS_ERR(inode))+		return NULL;++	/* NB Side effect: iget calls back to yaffs_read_inode(). */+	/* iget also increments the inode's i_count */+	/* NB You can't be holding gross_lock or deadlock will happen! */++	return inode;+}++++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)+#define YCRED(x) x+#else+#define YCRED(x) (x->cred)+#endif++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0))+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,+		       dev_t rdev)+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode,+		       dev_t rdev)+#else+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode,+		       int rdev)+#endif+{+	struct inode *inode;++	struct yaffs_obj *obj = NULL;+	struct yaffs_dev *dev;++	struct yaffs_obj *parent = yaffs_inode_to_obj(dir);++	int error = -ENOSPC;+	uid_t uid = YCRED_FSUID();+	gid_t gid =+	    (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID();++	if ((dir->i_mode & S_ISGID) && S_ISDIR(mode))+		mode |= S_ISGID;++	if (parent) {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_mknod: parent object %d type %d",+			parent->obj_id, parent->variant_type);+	} else {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_mknod: could not get parent object");+		return -EPERM;+	}++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_mknod: making oject for %s, mode %x dev %x",+		dentry->d_name.name, mode, rdev);++	dev = parent->my_dev;++	yaffs_gross_lock(dev);++	switch (mode & S_IFMT) {+	default:+		/* Special (socket, fifo, device...) */+		yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special");+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+		obj =+		    yaffs_create_special(parent, dentry->d_name.name, mode, uid,+					 gid, old_encode_dev(rdev));+#else+		obj =+		    yaffs_create_special(parent, dentry->d_name.name, mode, uid,+					 gid, rdev);+#endif+		break;+	case S_IFREG:		/* file          */+		yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file");+		obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid,+					gid);+		break;+	case S_IFDIR:		/* directory */+		yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory");+		obj = yaffs_create_dir(parent, dentry->d_name.name, mode,+				       uid, gid);+		break;+	case S_IFLNK:		/* symlink */+		yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink");+		obj = NULL;	/* Do we ever get here? */+		break;+	}++	/* Can not call yaffs_get_inode() with gross lock held */+	yaffs_gross_unlock(dev);++	if (obj) {+		inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj);+		d_instantiate(dentry, inode);+		update_dir_time(dir);+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_mknod created object %d count = %d",+			obj->obj_id, atomic_read(&inode->i_count));+		error = 0;+		yaffs_fill_inode_from_obj(dir, parent);+	} else {+		yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object");+		error = -ENOMEM;+	}++	return error;+}++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0))+static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)+#else+static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode)+#endif+{+	int ret_val;+	yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir");+	ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0);+	return ret_val;+}+++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode,+			bool dummy)+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0))+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode,+			struct nameidata *n)+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode,+			struct nameidata *n)+#else+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode)+#endif+{+	yaffs_trace(YAFFS_TRACE_OS, "yaffs_create");+	return yaffs_mknod(dir, dentry, mode | S_IFREG, 0);+}++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry,+				   unsigned int dummy)+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry,+				   struct nameidata *n)+#else+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry)+#endif+{+	struct yaffs_obj *obj;+	struct inode *inode = NULL;	/* NCB 2.5/2.6 needs NULL here */++	struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev;++	if (current != yaffs_dev_to_lc(dev)->readdir_process)+		yaffs_gross_lock(dev);++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s",+		yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name);++	obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name);++	obj = yaffs_get_equivalent_obj(obj);	/* in case it was a hardlink */++	/* Can't hold gross lock when calling yaffs_get_inode() */+	if (current != yaffs_dev_to_lc(dev)->readdir_process)+		yaffs_gross_unlock(dev);++	if (obj) {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_lookup found %d", obj->obj_id);++		inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj);+	} else {+		yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found");++	}++/* added NCB for 2.5/6 compatability - forces add even if inode is+ * NULL which creates dentry hash */+	d_add(dentry, inode);++	return NULL;+}++/*+ * Create a link...+ */+static int yaffs_link(struct dentry *old_dentry, struct inode *dir,+		      struct dentry *dentry)+{+	struct inode *inode = old_dentry->d_inode;+	struct yaffs_obj *obj = NULL;+	struct yaffs_obj *link = NULL;+	struct yaffs_dev *dev;++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_link");++	obj = yaffs_inode_to_obj(inode);+	dev = obj->my_dev;++	yaffs_gross_lock(dev);++	if (!S_ISDIR(inode->i_mode))	/* Don't link directories */+		link =+		    yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name,+				   obj);++	if (link) {+		set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj));+		d_instantiate(dentry, old_dentry->d_inode);+		atomic_inc(&old_dentry->d_inode->i_count);+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_link link count %d i_count %d",+			old_dentry->d_inode->i_nlink,+			atomic_read(&old_dentry->d_inode->i_count));+	}++	yaffs_gross_unlock(dev);++	if (link) {+		update_dir_time(dir);+		return 0;+	}++	return -EPERM;+}++static int yaffs_symlink(struct inode *dir, struct dentry *dentry,+			 const char *symname)+{+	struct yaffs_obj *obj;+	struct yaffs_dev *dev;+	uid_t uid = YCRED_FSUID();+	gid_t gid =+	    (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID();++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink");++	if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) >+				YAFFS_MAX_NAME_LENGTH)+		return -ENAMETOOLONG;++	if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) >+				YAFFS_MAX_ALIAS_LENGTH)+		return -ENAMETOOLONG;++	dev = yaffs_inode_to_obj(dir)->my_dev;+	yaffs_gross_lock(dev);+	obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name,+				   S_IFLNK | S_IRWXUGO, uid, gid, symname);+	yaffs_gross_unlock(dev);++	if (obj) {+		struct inode *inode;++		inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj);+		d_instantiate(dentry, inode);+		update_dir_time(dir);+		yaffs_trace(YAFFS_TRACE_OS, "symlink created OK");+		return 0;+	} else {+		yaffs_trace(YAFFS_TRACE_OS, "symlink not created");+	}++	return -ENOMEM;+}++/*+ * The VFS layer already does all the dentry stuff for rename.+ *+ * NB: POSIX says you can rename an object over an old object of the same name+ */+static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry,+			struct inode *new_dir, struct dentry *new_dentry)+{+	struct yaffs_dev *dev;+	int ret_val = YAFFS_FAIL;+	struct yaffs_obj *target;++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename");+	dev = yaffs_inode_to_obj(old_dir)->my_dev;++	yaffs_gross_lock(dev);++	/* Check if the target is an existing directory that is not empty. */+	target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir),+				    new_dentry->d_name.name);++	if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY &&+	    !list_empty(&target->variant.dir_variant.children)) {++		yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir");++		ret_val = YAFFS_FAIL;+	} else {+		/* Now does unlinking internally using shadowing mechanism */+		yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj");++		ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir),+					   old_dentry->d_name.name,+					   yaffs_inode_to_obj(new_dir),+					   new_dentry->d_name.name);+	}+	yaffs_gross_unlock(dev);++	if (ret_val == YAFFS_OK) {+		if (target)+			inode_dec_link_count(new_dentry->d_inode);++		update_dir_time(old_dir);+		if (old_dir != new_dir)+			update_dir_time(new_dir);+		return 0;+	} else {+		return -ENOTEMPTY;+	}+}+++++static int yaffs_unlink(struct inode *dir, struct dentry *dentry)+{+	int ret_val;++	struct yaffs_dev *dev;+	struct yaffs_obj *obj;++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s",+		(int)(dir->i_ino), dentry->d_name.name);+	obj = yaffs_inode_to_obj(dir);+	dev = obj->my_dev;++	yaffs_gross_lock(dev);++	ret_val = yaffs_unlinker(obj, dentry->d_name.name);++	if (ret_val == YAFFS_OK) {+		inode_dec_link_count(dentry->d_inode);+		dir->i_version++;+		yaffs_gross_unlock(dev);+		update_dir_time(dir);+		return 0;+	}+	yaffs_gross_unlock(dev);+	return -ENOTEMPTY;+}++++static const struct inode_operations yaffs_dir_inode_operations = {+	.create = yaffs_create,+	.lookup = yaffs_lookup,+	.link = yaffs_link,+	.unlink = yaffs_unlink,+	.symlink = yaffs_symlink,+	.mkdir = yaffs_mkdir,+	.rmdir = yaffs_unlink,+	.mknod = yaffs_mknod,+	.rename = yaffs_rename,+	.setattr = yaffs_setattr,+	.setxattr = yaffs_setxattr,+	.getxattr = yaffs_getxattr,+	.listxattr = yaffs_listxattr,+	.removexattr = yaffs_removexattr,+};++/*-----------------------------------------------------------------*/+/* Directory search context allows us to unlock access to yaffs during+ * filldir without causing problems with the directory being modified.+ * This is similar to the tried and tested mechanism used in yaffs direct.+ *+ * A search context iterates along a doubly linked list of siblings in the+ * directory. If the iterating object is deleted then this would corrupt+ * the list iteration, likely causing a crash. The search context avoids+ * this by using the remove_obj_fn to move the search context to the+ * next object before the object is deleted.+ *+ * Many readdirs (and thus seach conexts) may be alive simulateously so+ * each struct yaffs_dev has a list of these.+ *+ * A seach context lives for the duration of a readdir.+ *+ * All these functions must be called while yaffs is locked.+ */++struct yaffs_search_context {+	struct yaffs_dev *dev;+	struct yaffs_obj *dir_obj;+	struct yaffs_obj *next_return;+	struct list_head others;+};++/*+ * yaffs_new_search() creates a new search context, initialises it and+ * adds it to the device's search context list.+ *+ * Called at start of readdir.+ */+static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir)+{+	struct yaffs_dev *dev = dir->my_dev;+	struct yaffs_search_context *sc =+	    kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS);+	if (sc) {+		sc->dir_obj = dir;+		sc->dev = dev;+		if (list_empty(&sc->dir_obj->variant.dir_variant.children))+			sc->next_return = NULL;+		else+			sc->next_return =+			    list_entry(dir->variant.dir_variant.children.next,+				       struct yaffs_obj, siblings);+		INIT_LIST_HEAD(&sc->others);+		list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts));+	}+	return sc;+}++/*+ * yaffs_search_end() disposes of a search context and cleans up.+ */+static void yaffs_search_end(struct yaffs_search_context *sc)+{+	if (sc) {+		list_del(&sc->others);+		kfree(sc);+	}+}++/*+ * yaffs_search_advance() moves a search context to the next object.+ * Called when the search iterates or when an object removal causes+ * the search context to be moved to the next object.+ */+static void yaffs_search_advance(struct yaffs_search_context *sc)+{+	if (!sc)+		return;++	if (sc->next_return == NULL ||+	    list_empty(&sc->dir_obj->variant.dir_variant.children))+		sc->next_return = NULL;+	else {+		struct list_head *next = sc->next_return->siblings.next;++		if (next == &sc->dir_obj->variant.dir_variant.children)+			sc->next_return = NULL;	/* end of list */+		else+			sc->next_return =+			    list_entry(next, struct yaffs_obj, siblings);+	}+}++/*+ * yaffs_remove_obj_callback() is called when an object is unlinked.+ * We check open search contexts and advance any which are currently+ * on the object being iterated.+ */+static void yaffs_remove_obj_callback(struct yaffs_obj *obj)+{++	struct list_head *i;+	struct yaffs_search_context *sc;+	struct list_head *search_contexts =+	    &(yaffs_dev_to_lc(obj->my_dev)->search_contexts);++	/* Iterate through the directory search contexts.+	 * If any are currently on the object being removed, then advance+	 * the search context to the next object to prevent a hanging pointer.+	 */+	list_for_each(i, search_contexts) {+		sc = list_entry(i, struct yaffs_search_context, others);+		if (sc->next_return == obj)+			yaffs_search_advance(sc);+	}++}+++/*-----------------------------------------------------------------*/++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))+static int yaffs_readdir(struct file *file, struct dir_context *ctx)+{+	struct yaffs_obj *obj;+	struct yaffs_dev *dev;+	struct yaffs_search_context *sc;+	struct inode *inode = file->f_dentry->d_inode;+	unsigned long offset, curoffs;+	struct yaffs_obj *l;+	int ret_val = 0;++	char name[YAFFS_MAX_NAME_LENGTH + 1];++	obj = yaffs_dentry_to_obj(file->f_dentry);+	dev = obj->my_dev;++	yaffs_gross_lock(dev);++	yaffs_dev_to_lc(dev)->readdir_process = current;++	offset = ctx->pos;++	sc = yaffs_new_search(obj);+	if (!sc) {+		ret_val = -ENOMEM;+		goto out;+	}++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_readdir: starting at %d", (int)offset);++	if (offset == 0) {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_readdir: entry . ino %d",+			(int)inode->i_ino);+		yaffs_gross_unlock(dev);+		if (!dir_emit_dot(file, ctx)) {+			yaffs_gross_lock(dev);+			goto out;+		}+		yaffs_gross_lock(dev);+		offset++;+		ctx->pos++;+	}+	if (offset == 1) {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_readdir: entry .. ino %d",+			(int)file->f_dentry->d_parent->d_inode->i_ino);+		yaffs_gross_unlock(dev);+		if (!dir_emit_dotdot(file, ctx)) {+			yaffs_gross_lock(dev);+			goto out;+		}+		yaffs_gross_lock(dev);+		offset++;+		ctx->pos++;+	}++	curoffs = 1;++	/* If the directory has changed since the open or last call to+	   readdir, rewind to after the 2 canned entries. */+	if (file->f_version != inode->i_version) {+		offset = 2;+		ctx->pos = offset;+		file->f_version = inode->i_version;+	}++	while (sc->next_return) {+		curoffs++;+		l = sc->next_return;+		if (curoffs >= offset) {+			int this_inode = yaffs_get_obj_inode(l);+			int this_type = yaffs_get_obj_type(l);++			yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1);+			yaffs_trace(YAFFS_TRACE_OS,+				"yaffs_readdir: %s inode %d",+				name, yaffs_get_obj_inode(l));++			yaffs_gross_unlock(dev);++			if (!dir_emit(ctx, name, strlen(name),+				      this_inode, this_type) < 0) {+				yaffs_gross_lock(dev);+				goto out;+			}++			yaffs_gross_lock(dev);++			offset++;+			ctx->pos++;+		}+		yaffs_search_advance(sc);+	}++out:+	yaffs_search_end(sc);+	yaffs_dev_to_lc(dev)->readdir_process = NULL;+	yaffs_gross_unlock(dev);++	return ret_val;+}+#else+static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)+{+	struct yaffs_obj *obj;+	struct yaffs_dev *dev;+	struct yaffs_search_context *sc;+	struct inode *inode = f->f_dentry->d_inode;+	unsigned long offset, curoffs;+	struct yaffs_obj *l;+	int ret_val = 0;++	char name[YAFFS_MAX_NAME_LENGTH + 1];++	obj = yaffs_dentry_to_obj(f->f_dentry);+	dev = obj->my_dev;++	yaffs_gross_lock(dev);++	yaffs_dev_to_lc(dev)->readdir_process = current;++	offset = f->f_pos;++	sc = yaffs_new_search(obj);+	if (!sc) {+		ret_val = -ENOMEM;+		goto out;+	}++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_readdir: starting at %d", (int)offset);++	if (offset == 0) {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_readdir: entry . ino %d",+			(int)inode->i_ino);+		yaffs_gross_unlock(dev);+		if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) {+			yaffs_gross_lock(dev);+			goto out;+		}+		yaffs_gross_lock(dev);+		offset++;+		f->f_pos++;+	}+	if (offset == 1) {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_readdir: entry .. ino %d",+			(int)f->f_dentry->d_parent->d_inode->i_ino);+		yaffs_gross_unlock(dev);+		if (filldir(dirent, "..", 2, offset,+			    f->f_dentry->d_parent->d_inode->i_ino,+			    DT_DIR) < 0) {+			yaffs_gross_lock(dev);+			goto out;+		}+		yaffs_gross_lock(dev);+		offset++;+		f->f_pos++;+	}++	curoffs = 1;++	/* If the directory has changed since the open or last call to+	   readdir, rewind to after the 2 canned entries. */+	if (f->f_version != inode->i_version) {+		offset = 2;+		f->f_pos = offset;+		f->f_version = inode->i_version;+	}++	while (sc->next_return) {+		curoffs++;+		l = sc->next_return;+		if (curoffs >= offset) {+			int this_inode = yaffs_get_obj_inode(l);+			int this_type = yaffs_get_obj_type(l);++			yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1);+			yaffs_trace(YAFFS_TRACE_OS,+				"yaffs_readdir: %s inode %d",+				name, yaffs_get_obj_inode(l));++			yaffs_gross_unlock(dev);++			if (filldir(dirent,+				    name,+				    strlen(name),+				    offset, this_inode, this_type) < 0) {+				yaffs_gross_lock(dev);+				goto out;+			}++			yaffs_gross_lock(dev);++			offset++;+			f->f_pos++;+		}+		yaffs_search_advance(sc);+	}++out:+	yaffs_search_end(sc);+	yaffs_dev_to_lc(dev)->readdir_process = NULL;+	yaffs_gross_unlock(dev);++	return ret_val;+}+#endif++static const struct file_operations yaffs_dir_operations = {+	.read = generic_read_dir,+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))+	.iterate = yaffs_readdir,+#else+	.readdir = yaffs_readdir,+#endif+	.fsync = yaffs_sync_object,+	.llseek = generic_file_llseek,+};++static void yaffs_fill_inode_from_obj(struct inode *inode,+				      struct yaffs_obj *obj)+{+	if (inode && obj) {++		/* Check mode against the variant type and attempt to repair if broken. */+		u32 mode = obj->yst_mode;+		switch (obj->variant_type) {+		case YAFFS_OBJECT_TYPE_FILE:+			if (!S_ISREG(mode)) {+				obj->yst_mode &= ~S_IFMT;+				obj->yst_mode |= S_IFREG;+			}++			break;+		case YAFFS_OBJECT_TYPE_SYMLINK:+			if (!S_ISLNK(mode)) {+				obj->yst_mode &= ~S_IFMT;+				obj->yst_mode |= S_IFLNK;+			}++			break;+		case YAFFS_OBJECT_TYPE_DIRECTORY:+			if (!S_ISDIR(mode)) {+				obj->yst_mode &= ~S_IFMT;+				obj->yst_mode |= S_IFDIR;+			}++			break;+		case YAFFS_OBJECT_TYPE_UNKNOWN:+		case YAFFS_OBJECT_TYPE_HARDLINK:+		case YAFFS_OBJECT_TYPE_SPECIAL:+		default:+			/* TODO? */+			break;+		}++		inode->i_flags |= S_NOATIME;++		inode->i_ino = obj->obj_id;+		inode->i_mode = obj->yst_mode;+		i_uid_write(inode, obj->yst_uid);+		i_gid_write(inode, obj->yst_gid);+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19))+		inode->i_blksize = inode->i_sb->s_blocksize;+#endif+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))++		inode->i_rdev = old_decode_dev(obj->yst_rdev);+		inode->i_atime.tv_sec = (time_t) (obj->yst_atime);+		inode->i_atime.tv_nsec = 0;+		inode->i_mtime.tv_sec = (time_t) obj->yst_mtime;+		inode->i_mtime.tv_nsec = 0;+		inode->i_ctime.tv_sec = (time_t) obj->yst_ctime;+		inode->i_ctime.tv_nsec = 0;+#else+		inode->i_rdev = obj->yst_rdev;+		inode->i_atime = obj->yst_atime;+		inode->i_mtime = obj->yst_mtime;+		inode->i_ctime = obj->yst_ctime;+#endif+		inode->i_size = yaffs_get_obj_length(obj);+		inode->i_blocks = (inode->i_size + 511) >> 9;++		set_nlink(inode, yaffs_get_obj_link_count(obj));++		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_fill_inode mode %x uid %d gid %d size %lld count %d",+			inode->i_mode, i_uid_read(inode), i_gid_read(inode),+			inode->i_size, atomic_read(&inode->i_count));++		switch (obj->yst_mode & S_IFMT) {+		default:	/* fifo, device or socket */+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+			init_special_inode(inode, obj->yst_mode,+					   old_decode_dev(obj->yst_rdev));+#else+			init_special_inode(inode, obj->yst_mode,+					   (dev_t) (obj->yst_rdev));+#endif+			break;+		case S_IFREG:	/* file */+			inode->i_op = &yaffs_file_inode_operations;+			inode->i_fop = &yaffs_file_operations;+			inode->i_mapping->a_ops =+			    &yaffs_file_address_operations;+			break;+		case S_IFDIR:	/* directory */+			inode->i_op = &yaffs_dir_inode_operations;+			inode->i_fop = &yaffs_dir_operations;+			break;+		case S_IFLNK:	/* symlink */+			inode->i_op = &yaffs_symlink_inode_operations;+			break;+		}++		yaffs_inode_to_obj_lv(inode) = obj;++		obj->my_inode = inode;++	} else {+		yaffs_trace(YAFFS_TRACE_OS,+			"yaffs_fill_inode invalid parameters");+	}++}++++/*+ * yaffs background thread functions .+ * yaffs_bg_thread_fn() the thread function+ * yaffs_bg_start() launches the background thread.+ * yaffs_bg_stop() cleans up the background thread.+ *+ * NB:+ * The thread should only run after the yaffs is initialised+ * The thread should be stopped before yaffs is unmounted.+ * The thread should not do any writing while the fs is in read only.+ */++static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev)+{+	unsigned erased_chunks =+	    dev->n_erased_blocks * dev->param.chunks_per_block;+	struct yaffs_linux_context *context = yaffs_dev_to_lc(dev);+	unsigned scattered = 0;	/* Free chunks not in an erased block */++	if (erased_chunks < dev->n_free_chunks)+		scattered = (dev->n_free_chunks - erased_chunks);++	if (!context->bg_running)+		return 0;+	else if (scattered < (dev->param.chunks_per_block * 2))+		return 0;+	else if (erased_chunks > dev->n_free_chunks / 2)+		return 0;+	else if (erased_chunks > dev->n_free_chunks / 4)+		return 1;+	else+		return 2;+}++#ifdef YAFFS_COMPILE_BACKGROUND++void yaffs_background_waker(unsigned long data)+{+	wake_up_process((struct task_struct *)data);+}++static int yaffs_bg_thread_fn(void *data)+{+	struct yaffs_dev *dev = (struct yaffs_dev *)data;+	struct yaffs_linux_context *context = yaffs_dev_to_lc(dev);+	unsigned long now = jiffies;+	unsigned long next_dir_update = now;+	unsigned long next_gc = now;+	unsigned long expires;+	unsigned int urgency;++	int gc_result;+	struct timer_list timer;++	yaffs_trace(YAFFS_TRACE_BACKGROUND,+		"yaffs_background starting for dev %p", (void *)dev);++#ifdef YAFFS_COMPILE_FREEZER+	set_freezable();+#endif+	while (context->bg_running) {+		yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background");++		if (kthread_should_stop())+			break;++#ifdef YAFFS_COMPILE_FREEZER+		if (try_to_freeze())+			continue;+#endif+		yaffs_gross_lock(dev);++		now = jiffies;++		if (time_after(now, next_dir_update) && yaffs_bg_enable) {+			yaffs_update_dirty_dirs(dev);+			next_dir_update = now + HZ;+		}++		if (time_after(now, next_gc) && yaffs_bg_enable) {+			if (!dev->is_checkpointed) {+				urgency = yaffs_bg_gc_urgency(dev);+				gc_result = yaffs_bg_gc(dev, urgency);+				if (urgency > 1)+					next_gc = now + HZ / 20 + 1;+				else if (urgency > 0)+					next_gc = now + HZ / 10 + 1;+				else+					next_gc = now + HZ * 2;+			} else	{+			        /*+				 * gc not running so set to next_dir_update+				 * to cut down on wake ups+				 */+				next_gc = next_dir_update;+                        }+		}+		yaffs_gross_unlock(dev);+#if 1+		expires = next_dir_update;+		if (time_before(next_gc, expires))+			expires = next_gc;+		if (time_before(expires, now))+			expires = now + HZ;++		Y_INIT_TIMER(&timer);+		timer.expires = expires + 1;+		timer.data = (unsigned long)current;+		timer.function = yaffs_background_waker;++		set_current_state(TASK_INTERRUPTIBLE);+		add_timer(&timer);+		schedule();+		del_timer_sync(&timer);+#else+		msleep(10);+#endif+	}++	return 0;+}++static int yaffs_bg_start(struct yaffs_dev *dev)+{+	int retval = 0;+	struct yaffs_linux_context *context = yaffs_dev_to_lc(dev);++	if (dev->read_only)+		return -1;++	context->bg_running = 1;++	context->bg_thread = kthread_run(yaffs_bg_thread_fn,+					 (void *)dev, "yaffs-bg-%d",+					 context->mount_id);++	if (IS_ERR(context->bg_thread)) {+		retval = PTR_ERR(context->bg_thread);+		context->bg_thread = NULL;+		context->bg_running = 0;+	}+	return retval;+}++static void yaffs_bg_stop(struct yaffs_dev *dev)+{+	struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev);++	ctxt->bg_running = 0;++	if (ctxt->bg_thread) {+		kthread_stop(ctxt->bg_thread);+		ctxt->bg_thread = NULL;+	}+}+#else+static int yaffs_bg_thread_fn(void *data)+{+	return 0;+}++static int yaffs_bg_start(struct yaffs_dev *dev)+{+	return 0;+}++static void yaffs_bg_stop(struct yaffs_dev *dev)+{+}+#endif+++static void yaffs_flush_inodes(struct super_block *sb)+{+	struct inode *iptr;+	struct yaffs_obj *obj;++	list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) {+		obj = yaffs_inode_to_obj(iptr);+		if (obj) {+			yaffs_trace(YAFFS_TRACE_OS,+				"flushing obj %d",+				obj->obj_id);+			yaffs_flush_file(obj, 1, 0);+		}+	}+}++static void yaffs_flush_super(struct super_block *sb, int do_checkpoint)+{+	struct yaffs_dev *dev = yaffs_super_to_dev(sb);+	if (!dev)+		return;++	yaffs_flush_inodes(sb);+	yaffs_update_dirty_dirs(dev);+	yaffs_flush_whole_cache(dev);+	if (do_checkpoint)+		yaffs_checkpoint_save(dev);+}++static LIST_HEAD(yaffs_context_list);+struct mutex yaffs_context_lock;++static void yaffs_put_super(struct super_block *sb)+{+	struct yaffs_dev *dev = yaffs_super_to_dev(sb);+	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);++	yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS,+			"yaffs_put_super");++	yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND,+		"Shutting down yaffs background thread");+	yaffs_bg_stop(dev);+	yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND,+		"yaffs background thread shut down");++	yaffs_gross_lock(dev);++	yaffs_flush_super(sb, 1);++	yaffs_deinitialise(dev);++	yaffs_gross_unlock(dev);++	mutex_lock(&yaffs_context_lock);+	list_del_init(&(yaffs_dev_to_lc(dev)->context_list));+	mutex_unlock(&yaffs_context_lock);++	if (yaffs_dev_to_lc(dev)->spare_buffer) {+		kfree(yaffs_dev_to_lc(dev)->spare_buffer);+		yaffs_dev_to_lc(dev)->spare_buffer = NULL;+	}++	kfree(dev);++	yaffs_put_mtd_device(mtd);++	yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS,+			"yaffs_put_super done");+}+++static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev)+{+	return yaffs_gc_control;+}+++#ifdef YAFFS_COMPILE_EXPORTFS++static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino,+					  uint32_t generation)+{+	return Y_IGET(sb, ino);+}++static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb,+					  struct fid *fid, int fh_len,+					  int fh_type)+{+	return generic_fh_to_dentry(sb, fid, fh_len, fh_type,+				    yaffs2_nfs_get_inode);+}++static struct dentry *yaffs2_fh_to_parent(struct super_block *sb,+					  struct fid *fid, int fh_len,+					  int fh_type)+{+	return generic_fh_to_parent(sb, fid, fh_len, fh_type,+				    yaffs2_nfs_get_inode);+}++struct dentry *yaffs2_get_parent(struct dentry *dentry)+{++	struct super_block *sb = dentry->d_inode->i_sb;+	struct dentry *parent = ERR_PTR(-ENOENT);+	struct inode *inode;+	unsigned long parent_ino;+	struct yaffs_obj *d_obj;+	struct yaffs_obj *parent_obj;++	d_obj = yaffs_inode_to_obj(dentry->d_inode);++	if (d_obj) {+		parent_obj = d_obj->parent;+		if (parent_obj) {+			parent_ino = yaffs_get_obj_inode(parent_obj);+			inode = Y_IGET(sb, parent_ino);++			if (IS_ERR(inode)) {+				parent = ERR_CAST(inode);+			} else {+				parent = d_obtain_alias(inode);+				if (!IS_ERR(parent)) {+					parent = ERR_PTR(-ENOMEM);+					iput(inode);+				}+			}+		}+	}++	return parent;+}++/* Just declare a zero structure as a NULL value implies+ * using the default functions of exportfs.+ */++static struct export_operations yaffs_export_ops = {+	.fh_to_dentry = yaffs2_fh_to_dentry,+	.fh_to_parent = yaffs2_fh_to_parent,+	.get_parent = yaffs2_get_parent,+};++#endif++static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj)+{+	/* Clear the association between the inode and+	 * the struct yaffs_obj.+	 */+	obj->my_inode = NULL;+	yaffs_inode_to_obj_lv(inode) = NULL;++	/* If the object freeing was deferred, then the real+	 * free happens now.+	 * This should fix the inode inconsistency problem.+	 */+	yaffs_handle_defered_free(obj);+}++#ifdef YAFFS_HAS_EVICT_INODE+/* yaffs_evict_inode combines into one operation what was previously done in+ * yaffs_clear_inode() and yaffs_delete_inode()+ *+ */+static void yaffs_evict_inode(struct inode *inode)+{+	struct yaffs_obj *obj;+	struct yaffs_dev *dev;+	int deleteme = 0;++	obj = yaffs_inode_to_obj(inode);++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_evict_inode: ino %d, count %d %s",+		(int)inode->i_ino, atomic_read(&inode->i_count),+		obj ? "object exists" : "null object");++	if (!inode->i_nlink && !is_bad_inode(inode))+		deleteme = 1;+	truncate_inode_pages(&inode->i_data, 0);+	Y_CLEAR_INODE(inode);++	if (deleteme && obj) {+		dev = obj->my_dev;+		yaffs_gross_lock(dev);+		yaffs_del_obj(obj);+		yaffs_gross_unlock(dev);+	}+	if (obj) {+		dev = obj->my_dev;+		yaffs_gross_lock(dev);+		yaffs_unstitch_obj(inode, obj);+		yaffs_gross_unlock(dev);+	}+}+#else++/* clear is called to tell the fs to release any per-inode data it holds.+ * The object might still exist on disk and is just being thrown out of the cache+ * or else the object has actually been deleted and we're being called via+ * the chain+ *   yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode()+ */++static void yaffs_clear_inode(struct inode *inode)+{+	struct yaffs_obj *obj;+	struct yaffs_dev *dev;++	obj = yaffs_inode_to_obj(inode);++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_clear_inode: ino %d, count %d %s",+		(int)inode->i_ino, atomic_read(&inode->i_count),+		obj ? "object exists" : "null object");++	if (obj) {+		dev = obj->my_dev;+		yaffs_gross_lock(dev);+		yaffs_unstitch_obj(inode, obj);+		yaffs_gross_unlock(dev);+	}++}++/* delete is called when the link count is zero and the inode+ * is put (ie. nobody wants to know about it anymore, time to+ * delete the file).+ * NB Must call clear_inode()+ */+static void yaffs_delete_inode(struct inode *inode)+{+	struct yaffs_obj *obj = yaffs_inode_to_obj(inode);+	struct yaffs_dev *dev;++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_delete_inode: ino %d, count %d %s",+		(int)inode->i_ino, atomic_read(&inode->i_count),+		obj ? "object exists" : "null object");++	if (obj) {+		dev = obj->my_dev;+		yaffs_gross_lock(dev);+		yaffs_del_obj(obj);+		yaffs_gross_unlock(dev);+	}+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13))+	truncate_inode_pages(&inode->i_data, 0);+#endif+	clear_inode(inode);+}+#endif+++++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))+static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf)+{+	struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev;+	struct super_block *sb = dentry->d_sb;+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf)+{+	struct yaffs_dev *dev = yaffs_super_to_dev(sb);+#else+static int yaffs_statfs(struct super_block *sb, struct statfs *buf)+{+	struct yaffs_dev *dev = yaffs_super_to_dev(sb);+#endif++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs");++	yaffs_gross_lock(dev);++	buf->f_type = YAFFS_MAGIC;+	buf->f_bsize = sb->s_blocksize;+	buf->f_namelen = 255;++	if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) {+		/* Do this if chunk size is not a power of 2 */++		uint64_t bytes_in_dev;+		uint64_t bytes_free;++		bytes_in_dev =+		    ((uint64_t)+		     ((dev->param.end_block - dev->param.start_block ++		       1))) * ((uint64_t) (dev->param.chunks_per_block *+					   dev->data_bytes_per_chunk));++		do_div(bytes_in_dev, sb->s_blocksize);	/* bytes_in_dev becomes the number of blocks */+		buf->f_blocks = bytes_in_dev;++		bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) *+		    ((uint64_t) (dev->data_bytes_per_chunk));++		do_div(bytes_free, sb->s_blocksize);++		buf->f_bfree = bytes_free;++	} else if (sb->s_blocksize > dev->data_bytes_per_chunk) {++		buf->f_blocks =+		    (dev->param.end_block - dev->param.start_block + 1) *+		    dev->param.chunks_per_block /+		    (sb->s_blocksize / dev->data_bytes_per_chunk);+		buf->f_bfree =+		    yaffs_get_n_free_chunks(dev) /+		    (sb->s_blocksize / dev->data_bytes_per_chunk);+	} else {+		buf->f_blocks =+		    (dev->param.end_block - dev->param.start_block + 1) *+		    dev->param.chunks_per_block *+		    (dev->data_bytes_per_chunk / sb->s_blocksize);++		buf->f_bfree =+		    yaffs_get_n_free_chunks(dev) *+		    (dev->data_bytes_per_chunk / sb->s_blocksize);+	}++	buf->f_files = 0;+	buf->f_ffree = 0;+	buf->f_bavail = buf->f_bfree;++	yaffs_gross_unlock(dev);+	return 0;+}++++static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint)+{++	struct yaffs_dev *dev = yaffs_super_to_dev(sb);+	unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4);+	unsigned gc_urgent = yaffs_bg_gc_urgency(dev);+	int do_checkpoint;+	int dirty = yaffs_check_super_dirty(dev);++	yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND,+		"yaffs_do_sync_fs: gc-urgency %d %s %s%s",+		gc_urgent,+		dirty ? "dirty" : "clean",+		request_checkpoint ? "checkpoint requested" : "no checkpoint",+		oneshot_checkpoint ? " one-shot" : "");++	yaffs_gross_lock(dev);+	do_checkpoint = ((request_checkpoint && !gc_urgent) ||+			 oneshot_checkpoint) && !dev->is_checkpointed;++	if (dirty || do_checkpoint) {+		yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint);+		yaffs_clear_super_dirty(dev);+		if (oneshot_checkpoint)+			yaffs_auto_checkpoint &= ~4;+	}+	yaffs_gross_unlock(dev);++	return 0;+}+++#ifdef YAFFS_HAS_WRITE_SUPER+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))+static void yaffs_write_super(struct super_block *sb)+#else+static int yaffs_write_super(struct super_block *sb)+#endif+{+	unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2);++	yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND,+		"yaffs_write_super %s",+		request_checkpoint ? " checkpt" : "");++	yaffs_do_sync_fs(sb, request_checkpoint);++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))+	return 0;+#endif+}+#endif++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))+static int yaffs_sync_fs(struct super_block *sb, int wait)+#else+static int yaffs_sync_fs(struct super_block *sb)+#endif+{+	unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1);++	yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC,+		"yaffs_sync_fs%s", request_checkpoint ? " checkpt" : "");++	yaffs_do_sync_fs(sb, request_checkpoint);++	return 0;+}++++static const struct super_operations yaffs_super_ops = {+	.statfs = yaffs_statfs,++#ifndef YAFFS_USE_OWN_IGET+	.read_inode = yaffs_read_inode,+#endif+#ifdef YAFFS_HAS_PUT_INODE+	.put_inode = yaffs_put_inode,+#endif+	.put_super = yaffs_put_super,+#ifdef YAFFS_HAS_EVICT_INODE+	.evict_inode = yaffs_evict_inode,+#else+	.delete_inode = yaffs_delete_inode,+	.clear_inode = yaffs_clear_inode,+#endif+	.sync_fs = yaffs_sync_fs,+#ifdef YAFFS_HAS_WRITE_SUPER+	.write_super = yaffs_write_super,+#endif+};++struct yaffs_options {+	int inband_tags;+	int skip_checkpoint_read;+	int skip_checkpoint_write;+	int no_cache;+	int tags_ecc_on;+	int tags_ecc_overridden;+	int lazy_loading_enabled;+	int lazy_loading_overridden;+	int empty_lost_and_found;+	int empty_lost_and_found_overridden;+	int disable_summary;+};++#define MAX_OPT_LEN 30+static int yaffs_parse_options(struct yaffs_options *options,+			       const char *options_str)+{+	char cur_opt[MAX_OPT_LEN + 1];+	int p;+	int error = 0;++	/* Parse through the options which is a comma seperated list */++	while (options_str && *options_str && !error) {+		memset(cur_opt, 0, MAX_OPT_LEN + 1);+		p = 0;++		while (*options_str == ',')+			options_str++;++		while (*options_str && *options_str != ',') {+			if (p < MAX_OPT_LEN) {+				cur_opt[p] = *options_str;+				p++;+			}+			options_str++;+		}++		if (!strcmp(cur_opt, "inband-tags")) {+			options->inband_tags = 1;+		} else if (!strcmp(cur_opt, "tags-ecc-off")) {+			options->tags_ecc_on = 0;+			options->tags_ecc_overridden = 1;+		} else if (!strcmp(cur_opt, "tags-ecc-on")) {+			options->tags_ecc_on = 1;+			options->tags_ecc_overridden = 1;+		} else if (!strcmp(cur_opt, "lazy-loading-off")) {+			options->lazy_loading_enabled = 0;+			options->lazy_loading_overridden = 1;+		} else if (!strcmp(cur_opt, "lazy-loading-on")) {+			options->lazy_loading_enabled = 1;+			options->lazy_loading_overridden = 1;+		} else if (!strcmp(cur_opt, "disable-summary")) {+			options->disable_summary = 1;+		} else if (!strcmp(cur_opt, "empty-lost-and-found-off")) {+			options->empty_lost_and_found = 0;+			options->empty_lost_and_found_overridden = 1;+		} else if (!strcmp(cur_opt, "empty-lost-and-found-on")) {+			options->empty_lost_and_found = 1;+			options->empty_lost_and_found_overridden = 1;+		} else if (!strcmp(cur_opt, "no-cache")) {+			options->no_cache = 1;+		} else if (!strcmp(cur_opt, "no-checkpoint-read")) {+			options->skip_checkpoint_read = 1;+		} else if (!strcmp(cur_opt, "no-checkpoint-write")) {+			options->skip_checkpoint_write = 1;+		} else if (!strcmp(cur_opt, "no-checkpoint")) {+			options->skip_checkpoint_read = 1;+			options->skip_checkpoint_write = 1;+		} else {+			printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n",+			       cur_opt);+			error = 1;+		}+	}++	return error;+}+++static struct dentry *yaffs_make_root(struct inode *inode)+{+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0))+	struct dentry *root = d_alloc_root(inode);++	if (!root)+		iput(inode);++        return root;+#else+        return d_make_root(inode);+#endif+}+++++static struct super_block *yaffs_internal_read_super(int yaffs_version,+						     struct super_block *sb,+						     void *data, int silent)+{+	int n_blocks;+	struct inode *inode = NULL;+	struct dentry *root;+	struct yaffs_dev *dev = 0;+	char devname_buf[BDEVNAME_SIZE + 1];+	struct mtd_info *mtd;+	int err;+	char *data_str = (char *)data;+	struct yaffs_linux_context *context = NULL;+	struct yaffs_param *param;++	int read_only = 0;+	int inband_tags = 0;++	struct yaffs_options options;++	unsigned mount_id;+	int found;+	struct yaffs_linux_context *context_iterator;+	struct list_head *l;++	if (!sb) {+		printk(KERN_INFO "yaffs: sb is NULL\n");+		return NULL;+        }++	sb->s_magic = YAFFS_MAGIC;+	sb->s_op = &yaffs_super_ops;+	sb->s_flags |= MS_NOATIME;++	read_only = ((sb->s_flags & MS_RDONLY) != 0);++#ifdef YAFFS_COMPILE_EXPORTFS+	sb->s_export_op = &yaffs_export_ops;+#endif++	if (!sb->s_dev)+		printk(KERN_INFO "yaffs: sb->s_dev is NULL\n");+	else if (!yaffs_devname(sb, devname_buf))+		printk(KERN_INFO "yaffs: devname is NULL\n");+	else+		printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n",+		       sb->s_dev,+		       yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw");++	if (!data_str)+		data_str = "";++	printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str);++	memset(&options, 0, sizeof(options));++	if (yaffs_parse_options(&options, data_str)) {+		/* Option parsing failed */+		return NULL;+	}++	sb->s_blocksize = PAGE_CACHE_SIZE;+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_read_super: Using yaffs%d", yaffs_version);+	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_read_super: block size %d", (int)(sb->s_blocksize));++	yaffs_trace(YAFFS_TRACE_ALWAYS,+		"yaffs: Attempting MTD mount of %u.%u,\"%s\"",+		MAJOR(sb->s_dev), MINOR(sb->s_dev),+		yaffs_devname(sb, devname_buf));++	/* Get the device */+	mtd = get_mtd_device(NULL, MINOR(sb->s_dev));+	if (IS_ERR(mtd)) {+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"yaffs: MTD device %u either not valid or unavailable",+			MINOR(sb->s_dev));+		return NULL;+	}++	if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) {+		yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2");+		yaffs_version = 2;+	}++	/* Added NCB 26/5/2006 for completeness */+	if (yaffs_version == 2 && !options.inband_tags+	    && WRITE_SIZE(mtd) == 512) {+		yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1");+		yaffs_version = 1;+	}++	if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) ||+	    options.inband_tags)+		inband_tags = 1;++	if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0)+		return NULL;++	/* OK, so if we got here, we have an MTD that's NAND and looks+	 * like it has the right capabilities+	 * Set the struct yaffs_dev up for mtd+	 */++	if (!read_only && !(mtd->flags & MTD_WRITEABLE)) {+		read_only = 1;+		printk(KERN_INFO+		       "yaffs: mtd is read only, setting superblock read only\n"+		);+		sb->s_flags |= MS_RDONLY;+	}++	dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL);+	context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL);++	if (!dev || !context) {+		kfree(dev);+		kfree(context);+		dev = NULL;+		context = NULL;++		/* Deep shit could not allocate device structure */+		yaffs_trace(YAFFS_TRACE_ALWAYS,+			"yaffs_read_super: Failed trying to allocate struct yaffs_dev."+		);+		return NULL;+	}+	memset(dev, 0, sizeof(struct yaffs_dev));+	param = &(dev->param);++	memset(context, 0, sizeof(struct yaffs_linux_context));+	dev->os_context = context;+	INIT_LIST_HEAD(&(context->context_list));+	context->dev = dev;+	context->super = sb;++	dev->read_only = read_only;++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+	sb->s_fs_info = dev;+#else+	sb->u.generic_sbp = dev;+#endif+++	dev->driver_context = mtd;+	param->name = mtd->name;++	/* Set up the memory size parameters.... */+++	param->n_reserved_blocks = 5;+	param->n_caches = (options.no_cache) ? 0 : 10;+	param->inband_tags = inband_tags;++	param->enable_xattr = 1;+	if (options.lazy_loading_overridden)+		param->disable_lazy_load = !options.lazy_loading_enabled;++	param->defered_dir_update = 1;++	if (options.tags_ecc_overridden)+		param->no_tags_ecc = !options.tags_ecc_on;++	param->empty_lost_n_found = 1;+	param->refresh_period = 500;+	param->disable_summary = options.disable_summary;+++#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING+	param->disable_bad_block_marking  = 1;+#endif+	if (options.empty_lost_and_found_overridden)+		param->empty_lost_n_found = options.empty_lost_and_found;++	/* ... and the functions. */+	if (yaffs_version == 2) {+		param->is_yaffs2 = 1;+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))+		param->total_bytes_per_chunk = mtd->writesize;+		param->chunks_per_block = mtd->erasesize / mtd->writesize;+#else+		param->total_bytes_per_chunk = mtd->oobblock;+		param->chunks_per_block = mtd->erasesize / mtd->oobblock;+#endif+		n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize);++		param->start_block = 0;+		param->end_block = n_blocks - 1;+	} else {+		param->is_yaffs2 = 0;+		n_blocks = YCALCBLOCKS(mtd->size,+			     YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);++		param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK;+		param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK;+	}++	param->start_block = 0;+	param->end_block = n_blocks - 1;++	yaffs_mtd_drv_install(dev);++	param->sb_dirty_fn = yaffs_set_super_dirty;+	param->gc_control_fn = yaffs_gc_control_callback;++	yaffs_dev_to_lc(dev)->super = sb;++	param->use_nand_ecc = 1;++	param->skip_checkpt_rd = options.skip_checkpoint_read;+	param->skip_checkpt_wr = options.skip_checkpoint_write;++	mutex_lock(&yaffs_context_lock);+	/* Get a mount id */+	found = 0;+	for (mount_id = 0; !found; mount_id++) {+		found = 1;+		list_for_each(l, &yaffs_context_list) {+			context_iterator =+			    list_entry(l, struct yaffs_linux_context,+				       context_list);+			if (context_iterator->mount_id == mount_id)+				found = 0;+		}+	}+	context->mount_id = mount_id;++	list_add_tail(&(yaffs_dev_to_lc(dev)->context_list),+		      &yaffs_context_list);+	mutex_unlock(&yaffs_context_lock);++	/* Directory search handling... */+	INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts));+	param->remove_obj_fn = yaffs_remove_obj_callback;++	mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock));++	yaffs_gross_lock(dev);++	err = yaffs_guts_initialise(dev);++	yaffs_trace(YAFFS_TRACE_OS,+		"yaffs_read_super: guts initialised %s",+		(err == YAFFS_OK) ? "OK" : "FAILED");++	if (err == YAFFS_OK)+		yaffs_bg_start(dev);++	if (!context->bg_thread)+		param->defered_dir_update = 0;++	sb->s_maxbytes = yaffs_max_file_size(dev);++	/* Release lock before yaffs_get_inode() */+	yaffs_gross_unlock(dev);++	/* Create root inode */+	if (err == YAFFS_OK)+		inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev));++	if (!inode)+		return NULL;++	inode->i_op = &yaffs_dir_inode_operations;+	inode->i_fop = &yaffs_dir_operations;++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode");++	root = yaffs_make_root(inode);++	if (!root)+		return NULL;++	sb->s_root = root;+	if(!dev->is_checkpointed)+		yaffs_set_super_dirty(dev);++	yaffs_trace(YAFFS_TRACE_ALWAYS,+		"yaffs_read_super: is_checkpointed %d",+		dev->is_checkpointed);++	yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done");+	return sb;+}++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data,+					 int silent)+{+	return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL;+}++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))+static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags,+        const char *dev_name, void *data)+{+    return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd);+}+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))+static int yaffs_read_super(struct file_system_type *fs,+			    int flags, const char *dev_name,+			    void *data, struct vfsmount *mnt)+{++	return get_sb_bdev(fs, flags, dev_name, data,+			   yaffs_internal_read_super_mtd, mnt);+}+#else+static struct super_block *yaffs_read_super(struct file_system_type *fs,+					    int flags, const char *dev_name,+					    void *data)+{++	return get_sb_bdev(fs, flags, dev_name, data,+			   yaffs_internal_read_super_mtd);+}+#endif++static struct file_system_type yaffs_fs_type = {+	.owner = THIS_MODULE,+	.name = "yaffs",+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))+        .mount = yaffs_mount,+#else+        .get_sb = yaffs_read_super,+#endif+     	.kill_sb = kill_block_super,+	.fs_flags = FS_REQUIRES_DEV,+};+#else+static struct super_block *yaffs_read_super(struct super_block *sb, void *data,+					    int silent)+{+	return yaffs_internal_read_super(1, sb, data, silent);+}++static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super,+		      FS_REQUIRES_DEV);+#endif+++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data,+					  int silent)+{+	return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL;+}++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))+static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags,+        const char *dev_name, void *data)+{+        return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd);+}+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))+static int yaffs2_read_super(struct file_system_type *fs,+			     int flags, const char *dev_name, void *data,+			     struct vfsmount *mnt)+{+	return get_sb_bdev(fs, flags, dev_name, data,+			   yaffs2_internal_read_super_mtd, mnt);+}+#else+static struct super_block *yaffs2_read_super(struct file_system_type *fs,+					     int flags, const char *dev_name,+					     void *data)+{++	return get_sb_bdev(fs, flags, dev_name, data,+			   yaffs2_internal_read_super_mtd);+}+#endif++static struct file_system_type yaffs2_fs_type = {+	.owner = THIS_MODULE,+	.name = "yaffs2",+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))+        .mount = yaffs2_mount,+#else+        .get_sb = yaffs2_read_super,+#endif+     	.kill_sb = kill_block_super,+	.fs_flags = FS_REQUIRES_DEV,+};+#else+static struct super_block *yaffs2_read_super(struct super_block *sb,+					     void *data, int silent)+{+	return yaffs_internal_read_super(2, sb, data, silent);+}++static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super,+		      FS_REQUIRES_DEV);+#endif+++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0))+static struct proc_dir_entry *my_proc_entry;++static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev)+{+	struct yaffs_param *param = &dev->param;+	int bs[10];++	yaffs_count_blocks_by_state(dev,bs);++	buf += sprintf(buf, "start_block.......... %d\n", param->start_block);+	buf += sprintf(buf, "end_block............ %d\n", param->end_block);+	buf += sprintf(buf, "total_bytes_per_chunk %d\n",+				param->total_bytes_per_chunk);+	buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc);+	buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc);+	buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2);+	buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags);+	buf += sprintf(buf, "empty_lost_n_found... %d\n",+				param->empty_lost_n_found);+	buf += sprintf(buf, "disable_lazy_load.... %d\n",+				param->disable_lazy_load);+	buf += sprintf(buf, "disable_bad_block_mrk %d\n",+				param->disable_bad_block_marking);+	buf += sprintf(buf, "refresh_period....... %d\n",+				param->refresh_period);+	buf += sprintf(buf, "n_caches............. %d\n", param->n_caches);+	buf += sprintf(buf, "n_reserved_blocks.... %d\n",+				param->n_reserved_blocks);+	buf += sprintf(buf, "always_check_erased.. %d\n",+				param->always_check_erased);+	buf += sprintf(buf, "\n");+	buf += sprintf(buf, "block count by state\n");+	buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n",+				bs[0], bs[1], bs[2], bs[3], bs[4]);+	buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n",+				bs[5], bs[6], bs[7], bs[8], bs[9]);++	return buf;+}++static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev)+{+	buf += sprintf(buf, "max file size....... %lld\n",+				(long long) yaffs_max_file_size(dev));+	buf += sprintf(buf, "data_bytes_per_chunk. %d\n",+				dev->data_bytes_per_chunk);+	buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits);+	buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size);+	buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks);+	buf += sprintf(buf, "blocks_in_checkpt.... %d\n",+				dev->blocks_in_checkpt);+	buf += sprintf(buf, "\n");+	buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes);+	buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj);+	buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks);+	buf += sprintf(buf, "\n");+	buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes);+	buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads);+	buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures);+	buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies);+	buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs);+	buf += sprintf(buf, "passive_gc_count..... %u\n",+				dev->passive_gc_count);+	buf += sprintf(buf, "oldest_dirty_gc_count %u\n",+				dev->oldest_dirty_gc_count);+	buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks);+	buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs);+	buf += sprintf(buf, "n_retried_writes..... %u\n",+				dev->n_retried_writes);+	buf += sprintf(buf, "n_retired_blocks..... %u\n",+				dev->n_retired_blocks);+	buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed);+	buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed);+	buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n",+				dev->n_tags_ecc_fixed);+	buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n",+				dev->n_tags_ecc_unfixed);+	buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits);+	buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files);+	buf += sprintf(buf, "n_unlinked_files..... %u\n",+				dev->n_unlinked_files);+	buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count);+	buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions);+	buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used);+	buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used);++	return buf;+}++static int yaffs_proc_read(char *page,+			   char **start,+			   off_t offset, int count, int *eof, void *data)+{+	struct list_head *item;+	char *buf = page;+	int step = offset;+	int n = 0;++	/* Get proc_file_read() to step 'offset' by one on each sucessive call.+	 * We use 'offset' (*ppos) to indicate where we are in dev_list.+	 * This also assumes the user has posted a read buffer large+	 * enough to hold the complete output; but that's life in /proc.+	 */++	*(int *)start = 1;++	/* Print header first */+	if (step == 0)+		buf +=+		    sprintf(buf,+			    "Multi-version YAFFS built:" __DATE__ " " __TIME__+			    "\n");+	else if (step == 1)+		buf += sprintf(buf, "\n");+	else {+		step -= 2;++		mutex_lock(&yaffs_context_lock);++		/* Locate and print the Nth entry.  Order N-squared but N is small. */+		list_for_each(item, &yaffs_context_list) {+			struct yaffs_linux_context *dc =+			    list_entry(item, struct yaffs_linux_context,+				       context_list);+			struct yaffs_dev *dev = dc->dev;++			if (n < (step & ~1)) {+				n += 2;+				continue;+			}+			if ((step & 1) == 0) {+				buf +=+				    sprintf(buf, "\nDevice %d \"%s\"\n", n,+					    dev->param.name);+				buf = yaffs_dump_dev_part0(buf, dev);+			} else {+				buf = yaffs_dump_dev_part1(buf, dev);+                        }++			break;+		}+		mutex_unlock(&yaffs_context_lock);+	}++	return buf - page < count ? buf - page : count;+}++/**+ * Set the verbosity of the warnings and error messages.+ *+ * Note that the names can only be a..z or _ with the current code.+ */++static struct {+	char *mask_name;+	unsigned mask_bitfield;+} mask_flags[] = {+	{"allocate", YAFFS_TRACE_ALLOCATE},+	{"always", YAFFS_TRACE_ALWAYS},+	{"background", YAFFS_TRACE_BACKGROUND},+	{"bad_blocks", YAFFS_TRACE_BAD_BLOCKS},+	{"buffers", YAFFS_TRACE_BUFFERS},+	{"bug", YAFFS_TRACE_BUG},+	{"checkpt", YAFFS_TRACE_CHECKPOINT},+	{"deletion", YAFFS_TRACE_DELETION},+	{"erase", YAFFS_TRACE_ERASE},+	{"error", YAFFS_TRACE_ERROR},+	{"gc_detail", YAFFS_TRACE_GC_DETAIL},+	{"gc", YAFFS_TRACE_GC},+	{"lock", YAFFS_TRACE_LOCK},+	{"mtd", YAFFS_TRACE_MTD},+	{"nandaccess", YAFFS_TRACE_NANDACCESS},+	{"os", YAFFS_TRACE_OS},+	{"scan_debug", YAFFS_TRACE_SCAN_DEBUG},+	{"scan", YAFFS_TRACE_SCAN},+	{"mount", YAFFS_TRACE_MOUNT},+	{"tracing", YAFFS_TRACE_TRACING},+	{"sync", YAFFS_TRACE_SYNC},+	{"write", YAFFS_TRACE_WRITE},+	{"verify", YAFFS_TRACE_VERIFY},+	{"verify_nand", YAFFS_TRACE_VERIFY_NAND},+	{"verify_full", YAFFS_TRACE_VERIFY_FULL},+	{"verify_all", YAFFS_TRACE_VERIFY_ALL},+	{"all", 0xffffffff},+	{"none", 0},+	{NULL, 0},+};++#define MAX_MASK_NAME_LENGTH 40+static int yaffs_proc_write_trace_options(struct file *file, const char *buf,+					  unsigned long count, void *data)+{+	unsigned rg = 0, mask_bitfield;+	char *end;+	char *mask_name;+	const char *x;+	char substring[MAX_MASK_NAME_LENGTH + 1];+	int i;+	int done = 0;+	int add, len = 0;+	int pos = 0;++	rg = yaffs_trace_mask;++	while (!done && (pos < count)) {+		done = 1;+		while ((pos < count) && isspace(buf[pos]))+			pos++;++		switch (buf[pos]) {+		case '+':+		case '-':+		case '=':+			add = buf[pos];+			pos++;+			break;++		default:+			add = ' ';+			break;+		}+		mask_name = NULL;++		mask_bitfield = simple_strtoul(buf + pos, &end, 0);++		if (end > buf + pos) {+			mask_name = "numeral";+			len = end - (buf + pos);+			pos += len;+			done = 0;+		} else {+			for (x = buf + pos, i = 0;+			     (*x == '_' || (*x >= 'a' && *x <= 'z')) &&+			     i < MAX_MASK_NAME_LENGTH; x++, i++, pos++)+				substring[i] = *x;+			substring[i] = '\0';++			for (i = 0; mask_flags[i].mask_name != NULL; i++) {+				if (strcmp(substring, mask_flags[i].mask_name)+				    == 0) {+					mask_name = mask_flags[i].mask_name;+					mask_bitfield =+					    mask_flags[i].mask_bitfield;+					done = 0;+					break;+				}+			}+		}++		if (mask_name != NULL) {+			done = 0;+			switch (add) {+			case '-':+				rg &= ~mask_bitfield;+				break;+			case '+':+				rg |= mask_bitfield;+				break;+			case '=':+				rg = mask_bitfield;+				break;+			default:+				rg |= mask_bitfield;+				break;+			}+		}+	}++	yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS;++	printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask);++	if (rg & YAFFS_TRACE_ALWAYS) {+		for (i = 0; mask_flags[i].mask_name != NULL; i++) {+			char flag;+			flag = ((rg & mask_flags[i].mask_bitfield) ==+				mask_flags[i].mask_bitfield) ? '+' : '-';+			printk(KERN_DEBUG "%c%s\n", flag,+			       mask_flags[i].mask_name);+		}+	}++	return count;+}++/* Debug strings are of the form:+ * .bnnn         print info on block n+ * .cobjn,chunkn print nand chunk id for objn:chunkn+ */++static int yaffs_proc_debug_write(struct file *file, const char *buf,+					  unsigned long count, void *data)+{++	char str[100];+	char *p0;+	char *p1;+	long p1_val;+	long p0_val;+	char cmd;+	struct list_head *item;++	memset(str, 0, sizeof(str));+	memcpy(str, buf, min(count, sizeof(str) -1));++	cmd = str[1];++	p0 = str + 2;++	p1 = p0;++	while (*p1 && *p1 != ',') {+		p1++;+	}+	*p1 = '\0';+	p1++;++	p0_val = simple_strtol(p0, NULL, 0);+	p1_val = simple_strtol(p1, NULL, 0);+++	mutex_lock(&yaffs_context_lock);++	/* Locate and print the Nth entry.  Order N-squared but N is small. */+	list_for_each(item, &yaffs_context_list) {+		struct yaffs_linux_context *dc =+		    list_entry(item, struct yaffs_linux_context,+			       context_list);+		struct yaffs_dev *dev = dc->dev;++		if (cmd == 'b') {+			struct yaffs_block_info *bi;++			bi = yaffs_get_block_info(dev,p0_val);++			if(bi) {+				printk("Block %d: state %d, retire %d, use %d, seq %d\n",+					(int)p0_val, bi->block_state,+					bi->needs_retiring, bi->pages_in_use,+					bi->seq_number);+			}+		} else if (cmd == 'c') {+			struct yaffs_obj *obj;+			int nand_chunk;++			obj = yaffs_find_by_number(dev, p0_val);+			if (!obj)+				printk("No obj %d\n", (int)p0_val);+			else {+				if(p1_val == 0)+					nand_chunk = obj->hdr_chunk;+				else+					nand_chunk =+						yaffs_find_chunk_in_file(obj,+							p1_val, NULL);+				printk("Nand chunk for %d:%d is %d\n",+					(int)p0_val, (int)p1_val, nand_chunk);+			}+		}+	}++	mutex_unlock(&yaffs_context_lock);++	return count;+}++static int yaffs_proc_write(struct file *file, const char *buf,+			    unsigned long count, void *data)+{+	if (buf[0] == '.')+		return yaffs_proc_debug_write(file, buf, count, data);+	return yaffs_proc_write_trace_options(file, buf, count, data);+}+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) */++/* Stuff to handle installation of file systems */+struct file_system_to_install {+	struct file_system_type *fst;+	int installed;+};++static struct file_system_to_install fs_to_install[] = {+	{&yaffs_fs_type, 0},+	{&yaffs2_fs_type, 0},+	{NULL, 0}+};++static int __init init_yaffs_fs(void)+{+	int error = 0;+	struct file_system_to_install *fsinst;++	yaffs_trace(YAFFS_TRACE_ALWAYS,+		"yaffs built " __DATE__ " " __TIME__ " Installing.");++	mutex_init(&yaffs_context_lock);++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0))+	/* Install the proc_fs entries */+	my_proc_entry = create_proc_entry("yaffs",+					  S_IRUGO | S_IFREG, YPROC_ROOT);++	if (my_proc_entry) {+		my_proc_entry->write_proc = yaffs_proc_write;+		my_proc_entry->read_proc = yaffs_proc_read;+		my_proc_entry->data = NULL;+	} else {+		return -ENOMEM;+        }+#endif++	/* Now add the file system entries */++	fsinst = fs_to_install;++	while (fsinst->fst && !error) {+		error = register_filesystem(fsinst->fst);+		if (!error)+			fsinst->installed = 1;+		fsinst++;+	}++	/* Any errors? uninstall  */+	if (error) {+		fsinst = fs_to_install;++		while (fsinst->fst) {+			if (fsinst->installed) {+				unregister_filesystem(fsinst->fst);+				fsinst->installed = 0;+			}+			fsinst++;+		}+	}++	return error;+}++static void __exit exit_yaffs_fs(void)+{++	struct file_system_to_install *fsinst;++	yaffs_trace(YAFFS_TRACE_ALWAYS,+		"yaffs built " __DATE__ " " __TIME__ " removing.");++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0))+	remove_proc_entry("yaffs", YPROC_ROOT);+#endif++	fsinst = fs_to_install;++	while (fsinst->fst) {+		if (fsinst->installed) {+			unregister_filesystem(fsinst->fst);+			fsinst->installed = 0;+		}+		fsinst++;+	}+}++module_init(init_yaffs_fs)+    module_exit(exit_yaffs_fs)++    MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system");+MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011");+MODULE_LICENSE("GPL");diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.4.90/fs/yaffs2/yaffs_yaffs1.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs1.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_yaffs1.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,422 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yaffs_yaffs1.h"+#include "yportenv.h"+#include "yaffs_trace.h"+#include "yaffs_bitmap.h"+#include "yaffs_getblockinfo.h"+#include "yaffs_nand.h"+#include "yaffs_attribs.h"++int yaffs1_scan(struct yaffs_dev *dev)+{+	struct yaffs_ext_tags tags;+	int blk;+	int result;+	int chunk;+	int c;+	int deleted;+	enum yaffs_block_state state;+	LIST_HEAD(hard_list);+	struct yaffs_block_info *bi;+	u32 seq_number;+	struct yaffs_obj_hdr *oh;+	struct yaffs_obj *in;+	struct yaffs_obj *parent;+	int alloc_failed = 0;+	struct yaffs_shadow_fixer *shadow_fixers = NULL;+	u8 *chunk_data;++	yaffs_trace(YAFFS_TRACE_SCAN,+		"yaffs1_scan starts  intstartblk %d intendblk %d...",+		dev->internal_start_block, dev->internal_end_block);++	chunk_data = yaffs_get_temp_buffer(dev);++	dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;++	/* Scan all the blocks to determine their state */+	bi = dev->block_info;+	for (blk = dev->internal_start_block; blk <= dev->internal_end_block;+	     blk++) {+		yaffs_clear_chunk_bits(dev, blk);+		bi->pages_in_use = 0;+		bi->soft_del_pages = 0;++		yaffs_query_init_block_state(dev, blk, &state, &seq_number);++		bi->block_state = state;+		bi->seq_number = seq_number;++		if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK)+			bi->block_state = state = YAFFS_BLOCK_STATE_DEAD;++		yaffs_trace(YAFFS_TRACE_SCAN_DEBUG,+			"Block scanning block %d state %d seq %d",+			blk, state, seq_number);++		if (state == YAFFS_BLOCK_STATE_DEAD) {+			yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,+				"block %d is bad", blk);+		} else if (state == YAFFS_BLOCK_STATE_EMPTY) {+			yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty ");+			dev->n_erased_blocks++;+			dev->n_free_chunks += dev->param.chunks_per_block;+		}+		bi++;+	}++	/* For each block.... */+	for (blk = dev->internal_start_block;+	     !alloc_failed && blk <= dev->internal_end_block; blk++) {++		cond_resched();++		bi = yaffs_get_block_info(dev, blk);+		state = bi->block_state;++		deleted = 0;++		/* For each chunk in each block that needs scanning.... */+		for (c = 0;+			!alloc_failed && c < dev->param.chunks_per_block &&+			state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) {+			/* Read the tags and decide what to do */+			chunk = blk * dev->param.chunks_per_block + c;++			result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL,+							  &tags);++			/* Let's have a good look at this chunk... */++			if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED ||+			    tags.is_deleted) {+				/* YAFFS1 only...+				 * A deleted chunk+				 */+				deleted++;+				dev->n_free_chunks++;+			} else if (!tags.chunk_used) {+				/* An unassigned chunk in the block+				 * This means that either the block is empty or+				 * this is the one being allocated from+				 */++				if (c == 0) {+					/* We're looking at the first chunk in+					 *the block so the block is unused */+					state = YAFFS_BLOCK_STATE_EMPTY;+					dev->n_erased_blocks++;+				} else {+					/* this is the block being allocated */+					yaffs_trace(YAFFS_TRACE_SCAN,+						" Allocating from %d %d",+						blk, c);+					state = YAFFS_BLOCK_STATE_ALLOCATING;+					dev->alloc_block = blk;+					dev->alloc_page = c;+					dev->alloc_block_finder = blk;++				}++				dev->n_free_chunks +=+				    (dev->param.chunks_per_block - c);+			} else if (tags.chunk_id > 0) {+				/* chunk_id > 0 so it is a data chunk... */+				unsigned int endpos;++				yaffs_set_chunk_bit(dev, blk, c);+				bi->pages_in_use++;++				in = yaffs_find_or_create_by_number(dev,+							tags.obj_id,+							YAFFS_OBJECT_TYPE_FILE);+				/* PutChunkIntoFile checks for a clash+				 * (two data chunks with the same chunk_id).+				 */++				if (!in)+					alloc_failed = 1;++				if (in) {+					if (!yaffs_put_chunk_in_file+					    (in, tags.chunk_id, chunk, 1))+						alloc_failed = 1;+				}++				endpos =+				    (tags.chunk_id - 1) *+				    dev->data_bytes_per_chunk ++				    tags.n_bytes;+				if (in &&+				    in->variant_type ==+				     YAFFS_OBJECT_TYPE_FILE &&+				    in->variant.file_variant.scanned_size <+				      endpos) {+					in->variant.file_variant.scanned_size =+					    endpos;+					if (!dev->param.use_header_file_size) {+						in->variant.+						    file_variant.file_size =+						    in->variant.+						    file_variant.scanned_size;+					}++				}+			} else {+				/* chunk_id == 0, so it is an ObjectHeader.+				 * Make the object+				 */+				yaffs_set_chunk_bit(dev, blk, c);+				bi->pages_in_use++;++				result = yaffs_rd_chunk_tags_nand(dev, chunk,+								  chunk_data,+								  NULL);++				oh = (struct yaffs_obj_hdr *)chunk_data;++				in = yaffs_find_by_number(dev, tags.obj_id);+				if (in && in->variant_type != oh->type) {+					/* This should not happen, but somehow+					 * Wev'e ended up with an obj_id that+					 * has been reused but not yet deleted,+					 * and worse still it has changed type.+					 * Delete the old object.+					 */++					yaffs_del_obj(in);+					in = NULL;+				}++				in = yaffs_find_or_create_by_number(dev,+								tags.obj_id,+								oh->type);++				if (!in)+					alloc_failed = 1;++				if (in && oh->shadows_obj > 0) {++					struct yaffs_shadow_fixer *fixer;+					fixer =+						kmalloc(sizeof+						(struct yaffs_shadow_fixer),+						GFP_NOFS);+					if (fixer) {+						fixer->next = shadow_fixers;+						shadow_fixers = fixer;+						fixer->obj_id = tags.obj_id;+						fixer->shadowed_id =+						    oh->shadows_obj;+						yaffs_trace(YAFFS_TRACE_SCAN,+							" Shadow fixer: %d shadows %d",+							fixer->obj_id,+							fixer->shadowed_id);++					}++				}++				if (in && in->valid) {+					/* We have already filled this one.+					 * We have a duplicate and need to+					 * resolve it. */++					unsigned existing_serial = in->serial;+					unsigned new_serial =+					    tags.serial_number;++					if (((existing_serial + 1) & 3) ==+					    new_serial) {+						/* Use new one - destroy the+						 * exisiting one */+						yaffs_chunk_del(dev,+								in->hdr_chunk,+								1, __LINE__);+						in->valid = 0;+					} else {+						/* Use existing - destroy+						 * this one. */+						yaffs_chunk_del(dev, chunk, 1,+								__LINE__);+					}+				}++				if (in && !in->valid &&+				    (tags.obj_id == YAFFS_OBJECTID_ROOT ||+				     tags.obj_id ==+				     YAFFS_OBJECTID_LOSTNFOUND)) {+					/* We only load some info, don't fiddle+					 * with directory structure */+					in->valid = 1;+					in->variant_type = oh->type;++					in->yst_mode = oh->yst_mode;+					yaffs_load_attribs(in, oh);+					in->hdr_chunk = chunk;+					in->serial = tags.serial_number;++				} else if (in && !in->valid) {+					/* we need to load this info */++					in->valid = 1;+					in->variant_type = oh->type;++					in->yst_mode = oh->yst_mode;+					yaffs_load_attribs(in, oh);+					in->hdr_chunk = chunk;+					in->serial = tags.serial_number;++					yaffs_set_obj_name_from_oh(in, oh);+					in->dirty = 0;++					/* directory stuff...+					 * hook up to parent+					 */++					parent =+					    yaffs_find_or_create_by_number+					    (dev, oh->parent_obj_id,+					     YAFFS_OBJECT_TYPE_DIRECTORY);+					if (!parent)+						alloc_failed = 1;+					if (parent && parent->variant_type ==+					    YAFFS_OBJECT_TYPE_UNKNOWN) {+						/* Set up as a directory */+						parent->variant_type =+						    YAFFS_OBJECT_TYPE_DIRECTORY;+						INIT_LIST_HEAD(&parent->+							variant.dir_variant.+							children);+					} else if (!parent ||+						parent->variant_type !=+						YAFFS_OBJECT_TYPE_DIRECTORY) {+						/* Hoosterman, a problem....+						 * We're trying to use a+						 * non-directory as a directory+						 */++						yaffs_trace(YAFFS_TRACE_ERROR,+							"yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."+							);+						parent = dev->lost_n_found;+					}++					yaffs_add_obj_to_dir(parent, in);++					switch (in->variant_type) {+					case YAFFS_OBJECT_TYPE_UNKNOWN:+						/* Todo got a problem */+						break;+					case YAFFS_OBJECT_TYPE_FILE:+						if (dev->param.+						    use_header_file_size)+							in->variant.+							file_variant.file_size+							= yaffs_oh_to_size(oh);+						break;+					case YAFFS_OBJECT_TYPE_HARDLINK:+						in->variant.+						    hardlink_variant.equiv_id =+						    oh->equiv_id;+						list_add(&in->hard_links,+								&hard_list);+						break;+					case YAFFS_OBJECT_TYPE_DIRECTORY:+						/* Do nothing */+						break;+					case YAFFS_OBJECT_TYPE_SPECIAL:+						/* Do nothing */+						break;+					case YAFFS_OBJECT_TYPE_SYMLINK:+						in->variant.symlink_variant.+						    alias =+						    yaffs_clone_str(oh->alias);+						if (!in->variant.+						    symlink_variant.alias)+							alloc_failed = 1;+						break;+					}+				}+			}+		}++		if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) {+			/* If we got this far while scanning,+			 * then the block is fully allocated. */+			state = YAFFS_BLOCK_STATE_FULL;+		}++		if (state == YAFFS_BLOCK_STATE_ALLOCATING) {+			/* If the block was partially allocated then+			 * treat it as fully allocated. */+			state = YAFFS_BLOCK_STATE_FULL;+			dev->alloc_block = -1;+		}++		bi->block_state = state;++		/* Now let's see if it was dirty */+		if (bi->pages_in_use == 0 &&+		    !bi->has_shrink_hdr &&+		    bi->block_state == YAFFS_BLOCK_STATE_FULL)+			yaffs_block_became_dirty(dev, blk);+	}++	/* Ok, we've done all the scanning.+	 * Fix up the hard link chains.+	 * We should now have scanned all the objects, now it's time to add+	 * these hardlinks.+	 */++	yaffs_link_fixup(dev, &hard_list);++	/*+	 * Fix up any shadowed objects.+	 * There should not be more than one of these.+	 */+	{+		struct yaffs_shadow_fixer *fixer;+		struct yaffs_obj *obj;++		while (shadow_fixers) {+			fixer = shadow_fixers;+			shadow_fixers = fixer->next;+			/* Complete the rename transaction by deleting the+			 * shadowed object then setting the object header+			 to unshadowed.+			 */+			obj = yaffs_find_by_number(dev, fixer->shadowed_id);+			if (obj)+				yaffs_del_obj(obj);++			obj = yaffs_find_by_number(dev, fixer->obj_id);++			if (obj)+				yaffs_update_oh(obj, NULL, 1, 0, 0, NULL);++			kfree(fixer);+		}+	}++	yaffs_release_temp_buffer(dev, chunk_data);++	if (alloc_failed)+		return YAFFS_FAIL;++	yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends");++	return YAFFS_OK;+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.4.90/fs/yaffs2/yaffs_yaffs1.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs1.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_yaffs1.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,22 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_YAFFS1_H__+#define __YAFFS_YAFFS1_H__++#include "yaffs_guts.h"+int yaffs1_scan(struct yaffs_dev *dev);++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.4.90/fs/yaffs2/yaffs_yaffs2.c--- linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs2.c	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_yaffs2.c	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,1534 @@+/*+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License version 2 as+ * published by the Free Software Foundation.+ */++#include "yaffs_guts.h"+#include "yaffs_trace.h"+#include "yaffs_yaffs2.h"+#include "yaffs_checkptrw.h"+#include "yaffs_bitmap.h"+#include "yaffs_nand.h"+#include "yaffs_getblockinfo.h"+#include "yaffs_verify.h"+#include "yaffs_attribs.h"+#include "yaffs_summary.h"++/*+ * Checkpoints are really no benefit on very small partitions.+ *+ * To save space on small partitions don't bother with checkpoints unless+ * the partition is at least this big.+ */+#define YAFFS_CHECKPOINT_MIN_BLOCKS 60+#define YAFFS_SMALL_HOLE_THRESHOLD 4++/*+ * Oldest Dirty Sequence Number handling.+ */++/* yaffs_calc_oldest_dirty_seq()+ * yaffs2_find_oldest_dirty_seq()+ * Calculate the oldest dirty sequence number if we don't know it.+ */+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev)+{+	int i;+	unsigned seq;+	unsigned block_no = 0;+	struct yaffs_block_info *b;++	if (!dev->param.is_yaffs2)+		return;++	/* Find the oldest dirty sequence number. */+	seq = dev->seq_number + 1;+	b = dev->block_info;+	for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {+		if (b->block_state == YAFFS_BLOCK_STATE_FULL &&+		    (b->pages_in_use - b->soft_del_pages) <+		    dev->param.chunks_per_block &&+		    b->seq_number < seq) {+			seq = b->seq_number;+			block_no = i;+		}+		b++;+	}++	if (block_no) {+		dev->oldest_dirty_seq = seq;+		dev->oldest_dirty_block = block_no;+	}+}++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev)+{+	if (!dev->param.is_yaffs2)+		return;++	if (!dev->oldest_dirty_seq)+		yaffs_calc_oldest_dirty_seq(dev);+}++/*+ * yaffs_clear_oldest_dirty_seq()+ * Called when a block is erased or marked bad. (ie. when its seq_number+ * becomes invalid). If the value matches the oldest then we clear+ * dev->oldest_dirty_seq to force its recomputation.+ */+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev,+				   struct yaffs_block_info *bi)+{++	if (!dev->param.is_yaffs2)+		return;++	if (!bi || bi->seq_number == dev->oldest_dirty_seq) {+		dev->oldest_dirty_seq = 0;+		dev->oldest_dirty_block = 0;+	}+}++/*+ * yaffs2_update_oldest_dirty_seq()+ * Update the oldest dirty sequence number whenever we dirty a block.+ * Only do this if the oldest_dirty_seq is actually being tracked.+ */+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no,+				    struct yaffs_block_info *bi)+{+	if (!dev->param.is_yaffs2)+		return;++	if (dev->oldest_dirty_seq) {+		if (dev->oldest_dirty_seq > bi->seq_number) {+			dev->oldest_dirty_seq = bi->seq_number;+			dev->oldest_dirty_block = block_no;+		}+	}+}++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi)+{++	if (!dev->param.is_yaffs2)+		return 1;	/* disqualification only applies to yaffs2. */++	if (!bi->has_shrink_hdr)+		return 1;	/* can gc */++	yaffs2_find_oldest_dirty_seq(dev);++	/* Can't do gc of this block if there are any blocks older than this+	 * one that have discarded pages.+	 */+	return (bi->seq_number <= dev->oldest_dirty_seq);+}++/*+ * yaffs2_find_refresh_block()+ * periodically finds the oldest full block by sequence number for refreshing.+ * Only for yaffs2.+ */+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev)+{+	u32 b;+	u32 oldest = 0;+	u32 oldest_seq = 0;+	struct yaffs_block_info *bi;++	if (!dev->param.is_yaffs2)+		return oldest;++	/*+	 * If refresh period < 10 then refreshing is disabled.+	 */+	if (dev->param.refresh_period < 10)+		return oldest;++	/*+	 * Fix broken values.+	 */+	if (dev->refresh_skip > dev->param.refresh_period)+		dev->refresh_skip = dev->param.refresh_period;++	if (dev->refresh_skip > 0)+		return oldest;++	/*+	 * Refresh skip is now zero.+	 * We'll do a refresh this time around....+	 * Update the refresh skip and find the oldest block.+	 */+	dev->refresh_skip = dev->param.refresh_period;+	dev->refresh_count++;+	bi = dev->block_info;+	for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) {++		if (bi->block_state == YAFFS_BLOCK_STATE_FULL) {++			if (oldest < 1 || bi->seq_number < oldest_seq) {+				oldest = b;+				oldest_seq = bi->seq_number;+			}+		}+		bi++;+	}++	if (oldest > 0) {+		yaffs_trace(YAFFS_TRACE_GC,+			"GC refresh count %d selected block %d with seq_number %d",+			dev->refresh_count, oldest, oldest_seq);+	}++	return oldest;+}++int yaffs2_checkpt_required(struct yaffs_dev *dev)+{+	int nblocks;++	if (!dev->param.is_yaffs2)+		return 0;++	nblocks = dev->internal_end_block - dev->internal_start_block + 1;++	return !dev->param.skip_checkpt_wr &&+	    !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS);+}++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev)+{+	int retval;+	int n_bytes = 0;+	int n_blocks;+	int dev_blocks;++	if (!dev->param.is_yaffs2)+		return 0;++	if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) {+		/* Not a valid value so recalculate */+		dev_blocks = dev->param.end_block - dev->param.start_block + 1;+		n_bytes += sizeof(struct yaffs_checkpt_validity);+		n_bytes += sizeof(struct yaffs_checkpt_dev);+		n_bytes += dev_blocks * sizeof(struct yaffs_block_info);+		n_bytes += dev_blocks * dev->chunk_bit_stride;+		n_bytes +=+		    (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) *+		    dev->n_obj;+		n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes;+		n_bytes += sizeof(struct yaffs_checkpt_validity);+		n_bytes += sizeof(u32);	/* checksum */++		/* Round up and add 2 blocks to allow for some bad blocks,+		 * so add 3 */++		n_blocks =+		    (n_bytes /+		     (dev->data_bytes_per_chunk *+		      dev->param.chunks_per_block)) + 3;++		dev->checkpoint_blocks_required = n_blocks;+	}++	retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt;+	if (retval < 0)+		retval = 0;+	return retval;+}++/*--------------------- Checkpointing --------------------*/++static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head)+{+	struct yaffs_checkpt_validity cp;++	memset(&cp, 0, sizeof(cp));++	cp.struct_type = sizeof(cp);+	cp.magic = YAFFS_MAGIC;+	cp.version = YAFFS_CHECKPOINT_VERSION;+	cp.head = (head) ? 1 : 0;++	return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0;+}++static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head)+{+	struct yaffs_checkpt_validity cp;+	int ok;++	ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp));++	if (ok)+		ok = (cp.struct_type == sizeof(cp)) &&+		    (cp.magic == YAFFS_MAGIC) &&+		    (cp.version == YAFFS_CHECKPOINT_VERSION) &&+		    (cp.head == ((head) ? 1 : 0));+	return ok ? 1 : 0;+}++static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp,+				      struct yaffs_dev *dev)+{+	cp->n_erased_blocks = dev->n_erased_blocks;+	cp->alloc_block = dev->alloc_block;+	cp->alloc_page = dev->alloc_page;+	cp->n_free_chunks = dev->n_free_chunks;++	cp->n_deleted_files = dev->n_deleted_files;+	cp->n_unlinked_files = dev->n_unlinked_files;+	cp->n_bg_deletions = dev->n_bg_deletions;+	cp->seq_number = dev->seq_number;++}++static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev,+				     struct yaffs_checkpt_dev *cp)+{+	dev->n_erased_blocks = cp->n_erased_blocks;+	dev->alloc_block = cp->alloc_block;+	dev->alloc_page = cp->alloc_page;+	dev->n_free_chunks = cp->n_free_chunks;++	dev->n_deleted_files = cp->n_deleted_files;+	dev->n_unlinked_files = cp->n_unlinked_files;+	dev->n_bg_deletions = cp->n_bg_deletions;+	dev->seq_number = cp->seq_number;+}++static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev)+{+	struct yaffs_checkpt_dev cp;+	u32 n_bytes;+	u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1;+	int ok;++	/* Write device runtime values */+	yaffs2_dev_to_checkpt_dev(&cp, dev);+	cp.struct_type = sizeof(cp);++	ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp));+	if (!ok)+		return 0;++	/* Write block info */+	n_bytes = n_blocks * sizeof(struct yaffs_block_info);+	ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes);+	if (!ok)+		return 0;++	/* Write chunk bits */+	n_bytes = n_blocks * dev->chunk_bit_stride;+	ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes);++	return ok ? 1 : 0;+}++static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev)+{+	struct yaffs_checkpt_dev cp;+	u32 n_bytes;+	u32 n_blocks =+	    (dev->internal_end_block - dev->internal_start_block + 1);+	int ok;++	ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp));+	if (!ok)+		return 0;++	if (cp.struct_type != sizeof(cp))+		return 0;++	yaffs_checkpt_dev_to_dev(dev, &cp);++	n_bytes = n_blocks * sizeof(struct yaffs_block_info);++	ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes);++	if (!ok)+		return 0;++	n_bytes = n_blocks * dev->chunk_bit_stride;++	ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes);++	return ok ? 1 : 0;+}++static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp,+				   struct yaffs_obj *obj)+{+	cp->obj_id = obj->obj_id;+	cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0;+	cp->hdr_chunk = obj->hdr_chunk;+	cp->variant_type = obj->variant_type;+	cp->deleted = obj->deleted;+	cp->soft_del = obj->soft_del;+	cp->unlinked = obj->unlinked;+	cp->fake = obj->fake;+	cp->rename_allowed = obj->rename_allowed;+	cp->unlink_allowed = obj->unlink_allowed;+	cp->serial = obj->serial;+	cp->n_data_chunks = obj->n_data_chunks;++	if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)+		cp->size_or_equiv_obj = obj->variant.file_variant.file_size;+	else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK)+		cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id;+}++static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj,+				     struct yaffs_checkpt_obj *cp)+{+	struct yaffs_obj *parent;++	if (obj->variant_type != cp->variant_type) {+		yaffs_trace(YAFFS_TRACE_ERROR,+			"Checkpoint read object %d type %d chunk %d does not match existing object type %d",+			cp->obj_id, cp->variant_type, cp->hdr_chunk,+			obj->variant_type);+		return 0;+	}++	obj->obj_id = cp->obj_id;++	if (cp->parent_id)+		parent = yaffs_find_or_create_by_number(obj->my_dev,+						cp->parent_id,+						YAFFS_OBJECT_TYPE_DIRECTORY);+	else+		parent = NULL;++	if (parent) {+		if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {+			yaffs_trace(YAFFS_TRACE_ALWAYS,+				"Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory",+				cp->obj_id, cp->parent_id,+				cp->variant_type, cp->hdr_chunk,+				parent->variant_type);+			return 0;+		}+		yaffs_add_obj_to_dir(parent, obj);+	}++	obj->hdr_chunk = cp->hdr_chunk;+	obj->variant_type = cp->variant_type;+	obj->deleted = cp->deleted;+	obj->soft_del = cp->soft_del;+	obj->unlinked = cp->unlinked;+	obj->fake = cp->fake;+	obj->rename_allowed = cp->rename_allowed;+	obj->unlink_allowed = cp->unlink_allowed;+	obj->serial = cp->serial;+	obj->n_data_chunks = cp->n_data_chunks;++	if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)+		obj->variant.file_variant.file_size = cp->size_or_equiv_obj;+	else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK)+		obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj;++	if (obj->hdr_chunk > 0)+		obj->lazy_loaded = 1;+	return 1;+}++static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in,+				       struct yaffs_tnode *tn, u32 level,+				       int chunk_offset)+{+	int i;+	struct yaffs_dev *dev = in->my_dev;+	int ok = 1;+	u32 base_offset;++	if (!tn)+		return 1;++	if (level > 0) {+		for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) {+			if (!tn->internal[i])+				continue;+			ok = yaffs2_checkpt_tnode_worker(in,+				 tn->internal[i],+				 level - 1,+				 (chunk_offset <<+				  YAFFS_TNODES_INTERNAL_BITS) + i);+		}+		return ok;+	}++	/* Level 0 tnode */+	base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS;+	ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) ==+			sizeof(base_offset));+	if (ok)+		ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) ==+			dev->tnode_size);++	return ok;+}++static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj)+{+	u32 end_marker = ~0;+	int ok = 1;++	if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)+		return ok;++	ok = yaffs2_checkpt_tnode_worker(obj,+					 obj->variant.file_variant.top,+					 obj->variant.file_variant.+					 top_level, 0);+	if (ok)+		ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker,+				sizeof(end_marker)) == sizeof(end_marker));++	return ok ? 1 : 0;+}++static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj)+{+	u32 base_chunk;+	int ok = 1;+	struct yaffs_dev *dev = obj->my_dev;+	struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant;+	struct yaffs_tnode *tn;+	int nread = 0;++	ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) ==+	      sizeof(base_chunk));++	while (ok && (~base_chunk)) {+		nread++;+		/* Read level 0 tnode */++		tn = yaffs_get_tnode(dev);+		if (tn)+			ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) ==+				dev->tnode_size);+		else+			ok = 0;++		if (tn && ok)+			ok = yaffs_add_find_tnode_0(dev,+						    file_stuct_ptr,+						    base_chunk, tn) ? 1 : 0;++		if (ok)+			ok = (yaffs2_checkpt_rd+			      (dev, &base_chunk,+			       sizeof(base_chunk)) == sizeof(base_chunk));+	}++	yaffs_trace(YAFFS_TRACE_CHECKPOINT,+		"Checkpoint read tnodes %d records, last %d. ok %d",+		nread, base_chunk, ok);++	return ok ? 1 : 0;+}++static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev)+{+	struct yaffs_obj *obj;+	struct yaffs_checkpt_obj cp;+	int i;+	int ok = 1;+	struct list_head *lh;++	/* Iterate through the objects in each hash entry,+	 * dumping them to the checkpointing stream.+	 */++	for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) {+		list_for_each(lh, &dev->obj_bucket[i].list) {+			obj = list_entry(lh, struct yaffs_obj, hash_link);+			if (!obj->defered_free) {+				yaffs2_obj_checkpt_obj(&cp, obj);+				cp.struct_type = sizeof(cp);++				yaffs_trace(YAFFS_TRACE_CHECKPOINT,+					"Checkpoint write object %d parent %d type %d chunk %d obj addr %p",+					cp.obj_id, cp.parent_id,+					cp.variant_type, cp.hdr_chunk, obj);++				ok = (yaffs2_checkpt_wr(dev, &cp,+						sizeof(cp)) == sizeof(cp));++				if (ok &&+					obj->variant_type ==+					YAFFS_OBJECT_TYPE_FILE)+					ok = yaffs2_wr_checkpt_tnodes(obj);+			}+		}+	}++	/* Dump end of list */+	memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj));+	cp.struct_type = sizeof(cp);++	if (ok)+		ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp));++	return ok ? 1 : 0;+}++static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev)+{+	struct yaffs_obj *obj;+	struct yaffs_checkpt_obj cp;+	int ok = 1;+	int done = 0;+	LIST_HEAD(hard_list);+++	while (ok && !done) {+		ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp));+		if (cp.struct_type != sizeof(cp)) {+			yaffs_trace(YAFFS_TRACE_CHECKPOINT,+				"struct size %d instead of %d ok %d",+				cp.struct_type, (int)sizeof(cp), ok);+			ok = 0;+		}++		yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"Checkpoint read object %d parent %d type %d chunk %d ",+			cp.obj_id, cp.parent_id, cp.variant_type,+			cp.hdr_chunk);++		if (ok && cp.obj_id == ~0) {+			done = 1;+		} else if (ok) {+			obj =+			    yaffs_find_or_create_by_number(dev, cp.obj_id,+							   cp.variant_type);+			if (obj) {+				ok = yaffs2_checkpt_obj_to_obj(obj, &cp);+				if (!ok)+					break;+				if (obj->variant_type ==+					YAFFS_OBJECT_TYPE_FILE) {+					ok = yaffs2_rd_checkpt_tnodes(obj);+				} else if (obj->variant_type ==+					YAFFS_OBJECT_TYPE_HARDLINK) {+					list_add(&obj->hard_links, &hard_list);+				}+			} else {+				ok = 0;+			}+		}+	}++	if (ok)+		yaffs_link_fixup(dev, &hard_list);++	return ok ? 1 : 0;+}++static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev)+{+	u32 checkpt_sum;+	int ok;++	yaffs2_get_checkpt_sum(dev, &checkpt_sum);++	ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) ==+		sizeof(checkpt_sum));++	if (!ok)+		return 0;++	return 1;+}++static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev)+{+	u32 checkpt_sum0;+	u32 checkpt_sum1;+	int ok;++	yaffs2_get_checkpt_sum(dev, &checkpt_sum0);++	ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) ==+		sizeof(checkpt_sum1));++	if (!ok)+		return 0;++	if (checkpt_sum0 != checkpt_sum1)+		return 0;++	return 1;+}++static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev)+{+	int ok = 1;++	if (!yaffs2_checkpt_required(dev)) {+		yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"skipping checkpoint write");+		ok = 0;+	}++	if (ok)+		ok = yaffs2_checkpt_open(dev, 1);++	if (ok) {+		yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"write checkpoint validity");+		ok = yaffs2_wr_checkpt_validity_marker(dev, 1);+	}+	if (ok) {+		yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"write checkpoint device");+		ok = yaffs2_wr_checkpt_dev(dev);+	}+	if (ok) {+		yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"write checkpoint objects");+		ok = yaffs2_wr_checkpt_objs(dev);+	}+	if (ok) {+		yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"write checkpoint validity");+		ok = yaffs2_wr_checkpt_validity_marker(dev, 0);+	}++	if (ok)+		ok = yaffs2_wr_checkpt_sum(dev);++	if (!yaffs_checkpt_close(dev))+		ok = 0;++	if (ok)+		dev->is_checkpointed = 1;+	else+		dev->is_checkpointed = 0;++	return dev->is_checkpointed;+}++static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev)+{+	int ok = 1;++	if (!dev->param.is_yaffs2)+		ok = 0;++	if (ok && dev->param.skip_checkpt_rd) {+		yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"skipping checkpoint read");+		ok = 0;+	}++	if (ok)+		ok = yaffs2_checkpt_open(dev, 0); /* open for read */++	if (ok) {+		yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"read checkpoint validity");+		ok = yaffs2_rd_checkpt_validity_marker(dev, 1);+	}+	if (ok) {+		yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"read checkpoint device");+		ok = yaffs2_rd_checkpt_dev(dev);+	}+	if (ok) {+		yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"read checkpoint objects");+		ok = yaffs2_rd_checkpt_objs(dev);+	}+	if (ok) {+		yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"read checkpoint validity");+		ok = yaffs2_rd_checkpt_validity_marker(dev, 0);+	}++	if (ok) {+		ok = yaffs2_rd_checkpt_sum(dev);+		yaffs_trace(YAFFS_TRACE_CHECKPOINT,+			"read checkpoint checksum %d", ok);+	}++	if (!yaffs_checkpt_close(dev))+		ok = 0;++	if (ok)+		dev->is_checkpointed = 1;+	else+		dev->is_checkpointed = 0;++	return ok ? 1 : 0;+}++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev)+{+	if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) {+		dev->is_checkpointed = 0;+		yaffs2_checkpt_invalidate_stream(dev);+	}+	if (dev->param.sb_dirty_fn)+		dev->param.sb_dirty_fn(dev);+}++int yaffs_checkpoint_save(struct yaffs_dev *dev)+{+	yaffs_trace(YAFFS_TRACE_CHECKPOINT,+		"save entry: is_checkpointed %d",+		dev->is_checkpointed);++	yaffs_verify_objects(dev);+	yaffs_verify_blocks(dev);+	yaffs_verify_free_chunks(dev);++	if (!dev->is_checkpointed) {+		yaffs2_checkpt_invalidate(dev);+		yaffs2_wr_checkpt_data(dev);+	}++	yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT,+		"save exit: is_checkpointed %d",+		dev->is_checkpointed);++	return dev->is_checkpointed;+}++int yaffs2_checkpt_restore(struct yaffs_dev *dev)+{+	int retval;++	yaffs_trace(YAFFS_TRACE_CHECKPOINT,+		"restore entry: is_checkpointed %d",+		dev->is_checkpointed);++	retval = yaffs2_rd_checkpt_data(dev);++	if (dev->is_checkpointed) {+		yaffs_verify_objects(dev);+		yaffs_verify_blocks(dev);+		yaffs_verify_free_chunks(dev);+	}++	yaffs_trace(YAFFS_TRACE_CHECKPOINT,+		"restore exit: is_checkpointed %d",+		dev->is_checkpointed);++	return retval;+}++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size)+{+	/* if new_size > old_file_size.+	 * We're going to be writing a hole.+	 * If the hole is small then write zeros otherwise write a start+	 * of hole marker.+	 */+	loff_t old_file_size;+	loff_t increase;+	int small_hole;+	int result = YAFFS_OK;+	struct yaffs_dev *dev = NULL;+	u8 *local_buffer = NULL;+	int small_increase_ok = 0;++	if (!obj)+		return YAFFS_FAIL;++	if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)+		return YAFFS_FAIL;++	dev = obj->my_dev;++	/* Bail out if not yaffs2 mode */+	if (!dev->param.is_yaffs2)+		return YAFFS_OK;++	old_file_size = obj->variant.file_variant.file_size;++	if (new_size <= old_file_size)+		return YAFFS_OK;++	increase = new_size - old_file_size;++	if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk &&+	    yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1))+		small_hole = 1;+	else+		small_hole = 0;++	if (small_hole)+		local_buffer = yaffs_get_temp_buffer(dev);++	if (local_buffer) {+		/* fill hole with zero bytes */+		loff_t pos = old_file_size;+		int this_write;+		int written;+		memset(local_buffer, 0, dev->data_bytes_per_chunk);+		small_increase_ok = 1;++		while (increase > 0 && small_increase_ok) {+			this_write = increase;+			if (this_write > dev->data_bytes_per_chunk)+				this_write = dev->data_bytes_per_chunk;+			written =+			    yaffs_do_file_wr(obj, local_buffer, pos, this_write,+					     0);+			if (written == this_write) {+				pos += this_write;+				increase -= this_write;+			} else {+				small_increase_ok = 0;+			}+		}++		yaffs_release_temp_buffer(dev, local_buffer);++		/* If out of space then reverse any chunks we've added */+		if (!small_increase_ok)+			yaffs_resize_file_down(obj, old_file_size);+	}++	if (!small_increase_ok &&+	    obj->parent &&+	    obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED &&+	    obj->parent->obj_id != YAFFS_OBJECTID_DELETED) {+		/* Write a hole start header with the old file size */+		yaffs_update_oh(obj, NULL, 0, 1, 0, NULL);+	}++	return result;+}++struct yaffs_block_index {+	int seq;+	int block;+};++static int yaffs2_ybicmp(const void *a, const void *b)+{+	int aseq = ((struct yaffs_block_index *)a)->seq;+	int bseq = ((struct yaffs_block_index *)b)->seq;+	int ablock = ((struct yaffs_block_index *)a)->block;+	int bblock = ((struct yaffs_block_index *)b)->block;++	if (aseq == bseq)+		return ablock - bblock;++	return aseq - bseq;+}++static inline int yaffs2_scan_chunk(struct yaffs_dev *dev,+		struct yaffs_block_info *bi,+		int blk, int chunk_in_block,+		int *found_chunks,+		u8 *chunk_data,+		struct list_head *hard_list,+		int summary_available)+{+	struct yaffs_obj_hdr *oh;+	struct yaffs_obj *in;+	struct yaffs_obj *parent;+	int equiv_id;+	loff_t file_size;+	int is_shrink;+	int is_unlinked;+	struct yaffs_ext_tags tags;+	int result;+	int alloc_failed = 0;+	int chunk = blk * dev->param.chunks_per_block + chunk_in_block;+	struct yaffs_file_var *file_var;+	struct yaffs_hardlink_var *hl_var;+	struct yaffs_symlink_var *sl_var;++	if (summary_available) {+		result = yaffs_summary_fetch(dev, &tags, chunk_in_block);+		tags.seq_number = bi->seq_number;+	}++	if (!summary_available || tags.obj_id == 0) {+		result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags);+		dev->tags_used++;+	} else {+		dev->summary_used++;+	}++	/* Let's have a good look at this chunk... */++	if (!tags.chunk_used) {+		/* An unassigned chunk in the block.+		 * If there are used chunks after this one, then+		 * it is a chunk that was skipped due to failing+		 * the erased check. Just skip it so that it can+		 * be deleted.+		 * But, more typically, We get here when this is+		 * an unallocated chunk and his means that+		 * either the block is empty or this is the one+		 * being allocated from+		 */++		if (*found_chunks) {+			/* This is a chunk that was skipped due+			 * to failing the erased check */+		} else if (chunk_in_block == 0) {+			/* We're looking at the first chunk in+			 * the block so the block is unused */+			bi->block_state = YAFFS_BLOCK_STATE_EMPTY;+			dev->n_erased_blocks++;+		} else {+			if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||+			    bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) {+				if (dev->seq_number == bi->seq_number) {+					/* Allocating from this block*/+					yaffs_trace(YAFFS_TRACE_SCAN,+					    " Allocating from %d %d",+					    blk, chunk_in_block);++					bi->block_state =+						YAFFS_BLOCK_STATE_ALLOCATING;+					dev->alloc_block = blk;+					dev->alloc_page = chunk_in_block;+					dev->alloc_block_finder = blk;+				} else {+					/* This is a partially written block+					 * that is not the current+					 * allocation block.+					 */+					yaffs_trace(YAFFS_TRACE_SCAN,+						"Partially written block %d detected. gc will fix this.",+						blk);+				}+			}+		}++		dev->n_free_chunks++;++	} else if (tags.ecc_result ==+		YAFFS_ECC_RESULT_UNFIXED) {+		yaffs_trace(YAFFS_TRACE_SCAN,+			" Unfixed ECC in chunk(%d:%d), chunk ignored",+			blk, chunk_in_block);+			dev->n_free_chunks++;+	} else if (tags.obj_id > YAFFS_MAX_OBJECT_ID ||+		   tags.chunk_id > YAFFS_MAX_CHUNK_ID ||+		   tags.obj_id == YAFFS_OBJECTID_SUMMARY ||+		   (tags.chunk_id > 0 &&+		     tags.n_bytes > dev->data_bytes_per_chunk) ||+		   tags.seq_number != bi->seq_number) {+		yaffs_trace(YAFFS_TRACE_SCAN,+			"Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored",+			blk, chunk_in_block, tags.obj_id,+			tags.chunk_id, tags.n_bytes);+		dev->n_free_chunks++;+	} else if (tags.chunk_id > 0) {+		/* chunk_id > 0 so it is a data chunk... */+		loff_t endpos;+		loff_t chunk_base = (tags.chunk_id - 1) *+					dev->data_bytes_per_chunk;++		*found_chunks = 1;++		yaffs_set_chunk_bit(dev, blk, chunk_in_block);+		bi->pages_in_use++;++		in = yaffs_find_or_create_by_number(dev,+					tags.obj_id,+					YAFFS_OBJECT_TYPE_FILE);+		if (!in)+			/* Out of memory */+			alloc_failed = 1;++		if (in &&+		    in->variant_type == YAFFS_OBJECT_TYPE_FILE &&+		    chunk_base < in->variant.file_variant.shrink_size) {+			/* This has not been invalidated by+			 * a resize */+			if (!yaffs_put_chunk_in_file(in, tags.chunk_id,+								chunk, -1))+				alloc_failed = 1;++			/* File size is calculated by looking at+			 * the data chunks if we have not+			 * seen an object header yet.+			 * Stop this practice once we find an+			 * object header.+			 */+			endpos = chunk_base + tags.n_bytes;++			if (!in->valid &&+			    in->variant.file_variant.scanned_size < endpos) {+				in->variant.file_variant.+				    scanned_size = endpos;+				in->variant.file_variant.+				    file_size = endpos;+			}+		} else if (in) {+			/* This chunk has been invalidated by a+			 * resize, or a past file deletion+			 * so delete the chunk*/+			yaffs_chunk_del(dev, chunk, 1, __LINE__);+		}+	} else {+		/* chunk_id == 0, so it is an ObjectHeader.+		 * Thus, we read in the object header and make+		 * the object+		 */+		*found_chunks = 1;++		yaffs_set_chunk_bit(dev, blk, chunk_in_block);+		bi->pages_in_use++;++		oh = NULL;+		in = NULL;++		if (tags.extra_available) {+			in = yaffs_find_or_create_by_number(dev,+					tags.obj_id,+					tags.extra_obj_type);+			if (!in)+				alloc_failed = 1;+		}++		if (!in ||+		    (!in->valid && dev->param.disable_lazy_load) ||+		    tags.extra_shadows ||+		    (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT ||+				 tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) {++			/* If we don't have  valid info then we+			 * need to read the chunk+			 * TODO In future we can probably defer+			 * reading the chunk and living with+			 * invalid data until needed.+			 */++			result = yaffs_rd_chunk_tags_nand(dev,+						  chunk,+						  chunk_data,+						  NULL);++			oh = (struct yaffs_obj_hdr *)chunk_data;++			if (dev->param.inband_tags) {+				/* Fix up the header if they got+				 * corrupted by inband tags */+				oh->shadows_obj =+				    oh->inband_shadowed_obj_id;+				oh->is_shrink =+				    oh->inband_is_shrink;+			}++			if (!in) {+				in = yaffs_find_or_create_by_number(dev,+							tags.obj_id, oh->type);+				if (!in)+					alloc_failed = 1;+			}+		}++		if (!in) {+			/* TODO Hoosterman we have a problem! */+			yaffs_trace(YAFFS_TRACE_ERROR,+				"yaffs tragedy: Could not make object for object  %d at chunk %d during scan",+				tags.obj_id, chunk);+			return YAFFS_FAIL;+		}++		if (in->valid) {+			/* We have already filled this one.+			 * We have a duplicate that will be+			 * discarded, but we first have to suck+			 * out resize info if it is a file.+			 */+			if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) &&+				((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) ||+				 (tags.extra_available &&+				  tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE)+				)) {+				loff_t this_size = (oh) ?+					yaffs_oh_to_size(oh) :+					tags.extra_file_size;+				u32 parent_obj_id = (oh) ?+					oh->parent_obj_id :+					tags.extra_parent_id;++				is_shrink = (oh) ?+					oh->is_shrink :+					tags.extra_is_shrink;++				/* If it is deleted (unlinked+				 * at start also means deleted)+				 * we treat the file size as+				 * being zeroed at this point.+				 */+				if (parent_obj_id == YAFFS_OBJECTID_DELETED ||+				    parent_obj_id == YAFFS_OBJECTID_UNLINKED) {+					this_size = 0;+					is_shrink = 1;+				}++				if (is_shrink &&+				    in->variant.file_variant.shrink_size >+				    this_size)+					in->variant.file_variant.shrink_size =+					this_size;++				if (is_shrink)+					bi->has_shrink_hdr = 1;+			}+			/* Use existing - destroy this one. */+			yaffs_chunk_del(dev, chunk, 1, __LINE__);+		}++		if (!in->valid && in->variant_type !=+		    (oh ? oh->type : tags.extra_obj_type)) {+			yaffs_trace(YAFFS_TRACE_ERROR,+				"yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan",+				oh ? oh->type : tags.extra_obj_type,+				in->variant_type, tags.obj_id,+				chunk);+			in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type);+		}++		if (!in->valid &&+		    (tags.obj_id == YAFFS_OBJECTID_ROOT ||+		     tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) {+			/* We only load some info, don't fiddle+			 * with directory structure */+			in->valid = 1;++			if (oh) {+				in->yst_mode = oh->yst_mode;+				yaffs_load_attribs(in, oh);+				in->lazy_loaded = 0;+			} else {+				in->lazy_loaded = 1;+			}+			in->hdr_chunk = chunk;++		} else if (!in->valid) {+			/* we need to load this info */+			in->valid = 1;+			in->hdr_chunk = chunk;+			if (oh) {+				in->variant_type = oh->type;+				in->yst_mode = oh->yst_mode;+				yaffs_load_attribs(in, oh);++				if (oh->shadows_obj > 0)+					yaffs_handle_shadowed_obj(dev,+					     oh->shadows_obj, 1);++				yaffs_set_obj_name_from_oh(in, oh);+				parent = yaffs_find_or_create_by_number(dev,+						oh->parent_obj_id,+						YAFFS_OBJECT_TYPE_DIRECTORY);+				file_size = yaffs_oh_to_size(oh);+				is_shrink = oh->is_shrink;+				equiv_id = oh->equiv_id;+			} else {+				in->variant_type = tags.extra_obj_type;+				parent = yaffs_find_or_create_by_number(dev,+						tags.extra_parent_id,+						YAFFS_OBJECT_TYPE_DIRECTORY);+				file_size = tags.extra_file_size;+				is_shrink = tags.extra_is_shrink;+				equiv_id = tags.extra_equiv_id;+				in->lazy_loaded = 1;+			}+			in->dirty = 0;++			if (!parent)+				alloc_failed = 1;++			/* directory stuff...+			 * hook up to parent+			 */++			if (parent &&+			    parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) {+				/* Set up as a directory */+				parent->variant_type =+					YAFFS_OBJECT_TYPE_DIRECTORY;+				INIT_LIST_HEAD(&parent->+						variant.dir_variant.children);+			} else if (!parent ||+				   parent->variant_type !=+					YAFFS_OBJECT_TYPE_DIRECTORY) {+				/* Hoosterman, another problem....+				 * Trying to use a non-directory as a directory+				 */++				yaffs_trace(YAFFS_TRACE_ERROR,+					"yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."+					);+				parent = dev->lost_n_found;+			}+			yaffs_add_obj_to_dir(parent, in);++			is_unlinked = (parent == dev->del_dir) ||+					(parent == dev->unlinked_dir);++			if (is_shrink)+				/* Mark the block */+				bi->has_shrink_hdr = 1;++			/* Note re hardlinks.+			 * Since we might scan a hardlink before its equivalent+			 * object is scanned we put them all in a list.+			 * After scanning is complete, we should have all the+			 * objects, so we run through this list and fix up all+			 * the chains.+			 */++			switch (in->variant_type) {+			case YAFFS_OBJECT_TYPE_UNKNOWN:+				/* Todo got a problem */+				break;+			case YAFFS_OBJECT_TYPE_FILE:+				file_var = &in->variant.file_variant;+				if (file_var->scanned_size < file_size) {+					/* This covers the case where the file+					 * size is greater than the data held.+					 * This will happen if the file is+					 * resized to be larger than its+					 * current data extents.+					 */+					file_var->file_size = file_size;+					file_var->scanned_size = file_size;+				}++				if (file_var->shrink_size > file_size)+					file_var->shrink_size = file_size;++				break;+			case YAFFS_OBJECT_TYPE_HARDLINK:+				hl_var = &in->variant.hardlink_variant;+				if (!is_unlinked) {+					hl_var->equiv_id = equiv_id;+					list_add(&in->hard_links, hard_list);+				}+				break;+			case YAFFS_OBJECT_TYPE_DIRECTORY:+				/* Do nothing */+				break;+			case YAFFS_OBJECT_TYPE_SPECIAL:+				/* Do nothing */+				break;+			case YAFFS_OBJECT_TYPE_SYMLINK:+				sl_var = &in->variant.symlink_variant;+				if (oh) {+					sl_var->alias =+					    yaffs_clone_str(oh->alias);+					if (!sl_var->alias)+						alloc_failed = 1;+				}+				break;+			}+		}+	}+	return alloc_failed ? YAFFS_FAIL : YAFFS_OK;+}++int yaffs2_scan_backwards(struct yaffs_dev *dev)+{+	int blk;+	int block_iter;+	int start_iter;+	int end_iter;+	int n_to_scan = 0;+	enum yaffs_block_state state;+	int c;+	int deleted;+	LIST_HEAD(hard_list);+	struct yaffs_block_info *bi;+	u32 seq_number;+	int n_blocks = dev->internal_end_block - dev->internal_start_block + 1;+	u8 *chunk_data;+	int found_chunks;+	int alloc_failed = 0;+	struct yaffs_block_index *block_index = NULL;+	int alt_block_index = 0;+	int summary_available;++	yaffs_trace(YAFFS_TRACE_SCAN,+		"yaffs2_scan_backwards starts  intstartblk %d intendblk %d...",+		dev->internal_start_block, dev->internal_end_block);++	dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;++	block_index =+		kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS);++	if (!block_index) {+		block_index =+		    vmalloc(n_blocks * sizeof(struct yaffs_block_index));+		alt_block_index = 1;+	}++	if (!block_index) {+		yaffs_trace(YAFFS_TRACE_SCAN,+			"yaffs2_scan_backwards() could not allocate block index!"+			);+		return YAFFS_FAIL;+	}++	dev->blocks_in_checkpt = 0;++	chunk_data = yaffs_get_temp_buffer(dev);++	/* Scan all the blocks to determine their state */+	bi = dev->block_info;+	for (blk = dev->internal_start_block; blk <= dev->internal_end_block;+	     blk++) {+		yaffs_clear_chunk_bits(dev, blk);+		bi->pages_in_use = 0;+		bi->soft_del_pages = 0;++		yaffs_query_init_block_state(dev, blk, &state, &seq_number);++		bi->block_state = state;+		bi->seq_number = seq_number;++		if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA)+			bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;+		if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK)+			bi->block_state = YAFFS_BLOCK_STATE_DEAD;++		yaffs_trace(YAFFS_TRACE_SCAN_DEBUG,+			"Block scanning block %d state %d seq %d",+			blk, bi->block_state, seq_number);++		if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {+			dev->blocks_in_checkpt++;++		} else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) {+			yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,+				"block %d is bad", blk);+		} else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {+			yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty ");+			dev->n_erased_blocks++;+			dev->n_free_chunks += dev->param.chunks_per_block;+		} else if (bi->block_state ==+				YAFFS_BLOCK_STATE_NEEDS_SCAN) {+			/* Determine the highest sequence number */+			if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER &&+			    seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) {+				block_index[n_to_scan].seq = seq_number;+				block_index[n_to_scan].block = blk;+				n_to_scan++;+				if (seq_number >= dev->seq_number)+					dev->seq_number = seq_number;+			} else {+				/* TODO: Nasty sequence number! */+				yaffs_trace(YAFFS_TRACE_SCAN,+					"Block scanning block %d has bad sequence number %d",+					blk, seq_number);+			}+		}+		bi++;+	}++	yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan);++	cond_resched();++	/* Sort the blocks by sequence number */+	sort(block_index, n_to_scan, sizeof(struct yaffs_block_index),+		   yaffs2_ybicmp, NULL);++	cond_resched();++	yaffs_trace(YAFFS_TRACE_SCAN, "...done");++	/* Now scan the blocks looking at the data. */+	start_iter = 0;+	end_iter = n_to_scan - 1;+	yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan);++	/* For each block.... backwards */+	for (block_iter = end_iter;+	     !alloc_failed && block_iter >= start_iter;+	     block_iter--) {+		/* Cooperative multitasking! This loop can run for so+		   long that watchdog timers expire. */+		cond_resched();++		/* get the block to scan in the correct order */+		blk = block_index[block_iter].block;+		bi = yaffs_get_block_info(dev, blk);+		deleted = 0;++		summary_available = yaffs_summary_read(dev, dev->sum_tags, blk);++		/* For each chunk in each block that needs scanning.... */+		found_chunks = 0;+		if (summary_available)+			c = dev->chunks_per_summary - 1;+		else+			c = dev->param.chunks_per_block - 1;++		for (/* c is already initialised */;+		     !alloc_failed && c >= 0 &&+		     (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||+		      bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING);+		      c--) {+			/* Scan backwards...+			 * Read the tags and decide what to do+			 */+			if (yaffs2_scan_chunk(dev, bi, blk, c,+					&found_chunks, chunk_data,+					&hard_list, summary_available) ==+					YAFFS_FAIL)+				alloc_failed = 1;+		}++		if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) {+			/* If we got this far while scanning, then the block+			 * is fully allocated. */+			bi->block_state = YAFFS_BLOCK_STATE_FULL;+		}++		/* Now let's see if it was dirty */+		if (bi->pages_in_use == 0 &&+		    !bi->has_shrink_hdr &&+		    bi->block_state == YAFFS_BLOCK_STATE_FULL) {+			yaffs_block_became_dirty(dev, blk);+		}+	}++	yaffs_skip_rest_of_block(dev);++	if (alt_block_index)+		vfree(block_index);+	else+		kfree(block_index);++	/* Ok, we've done all the scanning.+	 * Fix up the hard link chains.+	 * We have scanned all the objects, now it's time to add these+	 * hardlinks.+	 */+	yaffs_link_fixup(dev, &hard_list);++	yaffs_release_temp_buffer(dev, chunk_data);++	if (alloc_failed)+		return YAFFS_FAIL;++	yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends");++	return YAFFS_OK;+}diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.4.90/fs/yaffs2/yaffs_yaffs2.h--- linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs2.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yaffs_yaffs2.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,39 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YAFFS_YAFFS2_H__+#define __YAFFS_YAFFS2_H__++#include "yaffs_guts.h"++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev);+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev);+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev,+				   struct yaffs_block_info *bi);+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no,+				    struct yaffs_block_info *bi);+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi);+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev);+int yaffs2_checkpt_required(struct yaffs_dev *dev);+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev);++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev);+int yaffs2_checkpt_save(struct yaffs_dev *dev);+int yaffs2_checkpt_restore(struct yaffs_dev *dev);++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size);+int yaffs2_scan_backwards(struct yaffs_dev *dev);++#endifdiff -Nur linux-3.4.90.orig/fs/yaffs2/yportenv.h linux-3.4.90/fs/yaffs2/yportenv.h--- linux-3.4.90.orig/fs/yaffs2/yportenv.h	1970-01-01 01:00:00.000000000 +0100+++ linux-3.4.90/fs/yaffs2/yportenv.h	2014-05-17 15:08:09.000000000 +0200@@ -0,0 +1,85 @@+/*+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system.+ *+ * Copyright (C) 2002-2011 Aleph One Ltd.+ *   for Toby Churchill Ltd and Brightstar Engineering+ *+ * Created by Charles Manning <charles@aleph1.co.uk>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU Lesser General Public License version 2.1 as+ * published by the Free Software Foundation.+ *+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.+ */++#ifndef __YPORTENV_H__+#define __YPORTENV_H__++/*+ * Define the MTD version in terms of Linux Kernel versions+ * This allows yaffs to be used independantly of the kernel+ * as well as with it.+ */++#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))++#ifdef YAFFS_OUT_OF_TREE+#include "moduleconfig.h"+#endif++#include <linux/version.h>+#define MTD_VERSION_CODE LINUX_VERSION_CODE++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19))+#include <linux/config.h>+#endif+#include <linux/version.h>+#include <linux/kernel.h>+#include <linux/mm.h>+#include <linux/sched.h>+#include <linux/string.h>+#include <linux/slab.h>+#include <linux/vmalloc.h>+#include <linux/xattr.h>+#include <linux/list.h>+#include <linux/types.h>+#include <linux/fs.h>+#include <linux/stat.h>+#include <linux/sort.h>+#include <linux/bitops.h>++/*  These type wrappings are used to support Unicode names in WinCE. */+#define YCHAR char+#define YUCHAR unsigned char+#define _Y(x)     x++#define YAFFS_LOSTNFOUND_NAME		"lost+found"+#define YAFFS_LOSTNFOUND_PREFIX		"obj"+++#define YAFFS_ROOT_MODE			0755+#define YAFFS_LOSTNFOUND_MODE		0700++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))+#define Y_CURRENT_TIME CURRENT_TIME.tv_sec+#define Y_TIME_CONVERT(x) (x).tv_sec+#else+#define Y_CURRENT_TIME CURRENT_TIME+#define Y_TIME_CONVERT(x) (x)+#endif++#define compile_time_assertion(assertion) \+	({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; })+++#define yaffs_printf(msk, fmt, ...) \+	printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__)++#define yaffs_trace(msk, fmt, ...) do { \+	if (yaffs_trace_mask & (msk)) \+		printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \+} while (0)+++#endif
 |