123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690 |
- /*
- * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
- /************************************************************************/
- /* */
- /* PROJECT : exFAT & FAT12/16/32 File System */
- /* FILE : core.c */
- /* PURPOSE : FAT & exFAT common core code for sdFAT */
- /* */
- /*----------------------------------------------------------------------*/
- /* NOTES */
- /* */
- /* */
- /************************************************************************/
- #include <linux/version.h>
- #include <linux/blkdev.h>
- #include <linux/workqueue.h>
- #include <linux/writeback.h>
- #include <linux/kernel.h>
- #include <linux/log2.h>
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
- #include <linux/iversion.h>
- #endif
- #include "sdfat.h"
- #include "core.h"
- #include <asm/byteorder.h>
- #include <asm/unaligned.h>
- /*************************************************************************
- * FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY
- *************************************************************************/
- #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)
- static inline u64 inode_peek_iversion(struct inode *inode)
- {
- return inode->i_version;
- }
- #endif
- /*----------------------------------------------------------------------*/
- /* Constant & Macro Definitions */
- /*----------------------------------------------------------------------*/
- static inline void __set_sb_dirty(struct super_block *sb)
- {
- #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
- sb->s_dirt = 1;
- #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) */
- struct sdfat_sb_info *sbi = SDFAT_SB(sb);
- sbi->s_dirt = 1;
- /* Insert work */
- spin_lock(&sbi->work_lock);
- if (!sbi->write_super_queued) {
- unsigned long delay;
- delay = msecs_to_jiffies(CONFIG_SDFAT_WRITE_SB_INTERVAL_CSECS * 10);
- queue_delayed_work(system_long_wq, &sbi->write_super_work, delay);
- sbi->write_super_queued = 1;
- }
- spin_unlock(&sbi->work_lock);
- #endif
- }
- void set_sb_dirty(struct super_block *sb)
- {
- __set_sb_dirty(sb);
- // XXX: to be removed later, prints too much output
- //TMSG("%s finished.\n", __func__);
- }
- /*----------------------------------------------------------------------*/
- /* Global Variable Definitions */
- /*----------------------------------------------------------------------*/
- /*----------------------------------------------------------------------*/
- /* Local Variable Definitions */
- /*----------------------------------------------------------------------*/
- static s8 *reserved_names[] = {
- "AUX ", "CON ", "NUL ", "PRN ",
- "COM1 ", "COM2 ", "COM3 ", "COM4 ",
- "COM5 ", "COM6 ", "COM7 ", "COM8 ", "COM9 ",
- "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ",
- "LPT5 ", "LPT6 ", "LPT7 ", "LPT8 ", "LPT9 ",
- NULL
- };
- /*======================================================================*/
- /* Local Function Definitions */
- /*======================================================================*/
- /*
- * File System Management Functions
- */
- static s32 check_type_size(void)
- {
- /* critical check for system requirement on size of DENTRY_T structure */
- if (sizeof(DENTRY_T) != DENTRY_SIZE)
- return -EINVAL;
- if (sizeof(DOS_DENTRY_T) != DENTRY_SIZE)
- return -EINVAL;
- if (sizeof(EXT_DENTRY_T) != DENTRY_SIZE)
- return -EINVAL;
- if (sizeof(FILE_DENTRY_T) != DENTRY_SIZE)
- return -EINVAL;
- if (sizeof(STRM_DENTRY_T) != DENTRY_SIZE)
- return -EINVAL;
- if (sizeof(NAME_DENTRY_T) != DENTRY_SIZE)
- return -EINVAL;
- if (sizeof(BMAP_DENTRY_T) != DENTRY_SIZE)
- return -EINVAL;
- if (sizeof(CASE_DENTRY_T) != DENTRY_SIZE)
- return -EINVAL;
- if (sizeof(VOLM_DENTRY_T) != DENTRY_SIZE)
- return -EINVAL;
- return 0;
- }
- static s32 __fs_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- s32 err;
- s32 sync = 0;
- /* flags are not changed */
- if (fsi->vol_flag == new_flag)
- return 0;
- fsi->vol_flag = new_flag;
- /* skip updating volume dirty flag,
- * if this volume has been mounted with read-only
- */
- if (sb_rdonly(sb))
- return 0;
- if (!fsi->pbr_bh) {
- err = read_sect(sb, 0, &(fsi->pbr_bh), 1);
- if (err) {
- EMSG("%s : failed to read boot sector\n", __func__);
- return err;
- }
- }
- if (fsi->vol_type == EXFAT) {
- pbr64_t *bpb = (pbr64_t *)fsi->pbr_bh->b_data;
- bpb->bsx.vol_flags = cpu_to_le16(new_flag);
- } else if (fsi->vol_type == FAT32) {
- pbr32_t *bpb = (pbr32_t *)fsi->pbr_bh->b_data;
- bpb->bsx.state = new_flag & VOL_DIRTY ? FAT_VOL_DIRTY : 0x00;
- } else { /* FAT16/12 */
- pbr16_t *bpb = (pbr16_t *) fsi->pbr_bh->b_data;
- bpb->bpb.f16.state = new_flag & VOL_DIRTY ?
- FAT_VOL_DIRTY : 0x00;
- }
- if (always_sync)
- sync = 1;
- else if ((new_flag == VOL_DIRTY) && (!buffer_dirty(fsi->pbr_bh)))
- sync = 1;
- else
- sync = 0;
- err = write_sect(sb, 0, fsi->pbr_bh, sync);
- if (err)
- EMSG("%s : failed to modify volume flag\n", __func__);
- return err;
- }
- static s32 fs_set_vol_flags(struct super_block *sb, u16 new_flag)
- {
- return __fs_set_vol_flags(sb, new_flag, 0);
- }
- s32 fscore_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync)
- {
- return __fs_set_vol_flags(sb, new_flag, always_sync);
- }
- static inline s32 __fs_meta_sync(struct super_block *sb, s32 do_sync)
- {
- #ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (fsi->vol_type != EXFAT) {
- MMSG("meta flush in fs_sync(sync=%d)\n", do_sync);
- fcache_flush(sb, 0);
- dcache_flush(sb, 0);
- }
- #else
- /* DO NOTHING */
- #endif
- return 0;
- }
- static s32 fs_sync(struct super_block *sb, s32 do_sync)
- {
- s32 err;
- if (!do_sync)
- return 0;
- err = __fs_meta_sync(sb, do_sync);
- if (!err)
- err = bdev_sync_all(sb);
- if (err)
- EMSG("%s : failed to sync. (err:%d)\n", __func__, err);
- return err;
- }
- /*
- * Cluster Management Functions
- */
- static s32 __clear_cluster(struct inode *inode, u32 clu)
- {
- u64 s, n;
- struct super_block *sb = inode->i_sb;
- u32 sect_size = (u32)sb->s_blocksize;
- s32 ret = 0;
- struct buffer_head *tmp_bh = NULL;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (IS_CLUS_FREE(clu)) { /* FAT16 root_dir */
- s = fsi->root_start_sector;
- n = fsi->data_start_sector;
- } else {
- s = CLUS_TO_SECT(fsi, clu);
- n = s + fsi->sect_per_clus;
- }
- if (IS_DIRSYNC(inode)) {
- ret = write_msect_zero(sb, s, (u64)fsi->sect_per_clus);
- if (ret != -EAGAIN)
- return ret;
- }
- /* Trying buffered zero writes
- * if it doesn't have DIRSYNC or write_msect_zero() returned -EAGAIN
- */
- for ( ; s < n; s++) {
- #if 0
- dcache_release(sb, s);
- #endif
- ret = read_sect(sb, s, &tmp_bh, 0);
- if (ret)
- goto out;
- memset((u8 *)tmp_bh->b_data, 0x0, sect_size);
- ret = write_sect(sb, s, tmp_bh, 0);
- if (ret)
- goto out;
- }
- out:
- brelse(tmp_bh);
- return ret;
- } /* end of __clear_cluster */
- static s32 __find_last_cluster(struct super_block *sb, CHAIN_T *p_chain, u32 *ret_clu)
- {
- u32 clu, next;
- u32 count = 0;
- next = p_chain->dir;
- if (p_chain->flags == 0x03) {
- *ret_clu = next + p_chain->size - 1;
- return 0;
- }
- do {
- count++;
- clu = next;
- if (fat_ent_get_safe(sb, clu, &next))
- return -EIO;
- } while (!IS_CLUS_EOF(next));
- if (p_chain->size != count) {
- sdfat_fs_error(sb, "bogus directory size "
- "(clus : ondisk(%d) != counted(%d))",
- p_chain->size, count);
- sdfat_debug_bug_on(1);
- return -EIO;
- }
- *ret_clu = clu;
- return 0;
- }
- static s32 __count_num_clusters(struct super_block *sb, CHAIN_T *p_chain, u32 *ret_count)
- {
- u32 i, count;
- u32 clu;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (!p_chain->dir || IS_CLUS_EOF(p_chain->dir)) {
- *ret_count = 0;
- return 0;
- }
- if (p_chain->flags == 0x03) {
- *ret_count = p_chain->size;
- return 0;
- }
- clu = p_chain->dir;
- count = 0;
- for (i = CLUS_BASE; i < fsi->num_clusters; i++) {
- count++;
- if (fat_ent_get_safe(sb, clu, &clu))
- return -EIO;
- if (IS_CLUS_EOF(clu))
- break;
- }
- *ret_count = count;
- return 0;
- }
- /*
- * Upcase table Management Functions
- */
- static void free_upcase_table(struct super_block *sb)
- {
- u32 i;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- u16 **upcase_table;
- upcase_table = fsi->vol_utbl;
- for (i = 0 ; i < UTBL_COL_COUNT ; i++) {
- /* kfree(NULL) is safe */
- kfree(upcase_table[i]);
- upcase_table[i] = NULL;
- }
- /* kfree(NULL) is safe */
- kfree(fsi->vol_utbl);
- fsi->vol_utbl = NULL;
- }
- static s32 __load_upcase_table(struct super_block *sb, u64 sector, u64 num_sectors, u32 utbl_checksum)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- struct buffer_head *tmp_bh = NULL;
- u32 sect_size = (u32)sb->s_blocksize;
- s32 ret = -EIO;
- u32 i, j;
- u8 skip = false;
- u32 index = 0;
- u32 checksum = 0;
- u16 **upcase_table = kzalloc((UTBL_COL_COUNT * sizeof(u16 *)), GFP_KERNEL);
- if (!upcase_table)
- return -ENOMEM;
- /* thanks for kzalloc
- * memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *));
- */
- fsi->vol_utbl = upcase_table;
- num_sectors += sector;
- while (sector < num_sectors) {
- ret = read_sect(sb, sector, &tmp_bh, 1);
- if (ret) {
- EMSG("%s: failed to read sector(0x%llx)\n",
- __func__, sector);
- goto error;
- }
- sector++;
- for (i = 0; i < sect_size && index <= 0xFFFF; i += 2) {
- /* FIXME : is __le16 ok? */
- //u16 uni = le16_to_cpu(((__le16*)(tmp_bh->b_data))[i]);
- u16 uni = get_unaligned_le16((u8 *)tmp_bh->b_data+i);
- checksum = ((checksum & 1) ? 0x80000000 : 0) +
- (checksum >> 1) + *(((u8 *)tmp_bh->b_data)+i);
- checksum = ((checksum & 1) ? 0x80000000 : 0) +
- (checksum >> 1) + *(((u8 *)tmp_bh->b_data)+(i+1));
- if (skip) {
- MMSG("skip from 0x%X to 0x%X(amount of 0x%X)\n",
- index, index+uni, uni);
- index += uni;
- skip = false;
- } else if (uni == index) {
- index++;
- } else if (uni == 0xFFFF) {
- skip = true;
- } else { /* uni != index , uni != 0xFFFF */
- u16 col_index = get_col_index(index);
- if (!upcase_table[col_index]) {
- upcase_table[col_index] =
- kmalloc((UTBL_ROW_COUNT * sizeof(u16)), GFP_KERNEL);
- if (!upcase_table[col_index]) {
- EMSG("failed to allocate memory"
- " for column 0x%X\n",
- col_index);
- ret = -ENOMEM;
- goto error;
- }
- for (j = 0; j < UTBL_ROW_COUNT; j++)
- upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j;
- }
- upcase_table[col_index][get_row_index(index)] = uni;
- index++;
- }
- }
- }
- if (index >= 0xFFFF && utbl_checksum == checksum) {
- DMSG("%s: load upcase table successfully"
- "(idx:0x%08x, utbl_chksum:0x%08x)\n",
- __func__, index, utbl_checksum);
- if (tmp_bh)
- brelse(tmp_bh);
- return 0;
- }
- EMSG("%s: failed to load upcase table"
- "(idx:0x%08x, chksum:0x%08x, utbl_chksum:0x%08x)\n",
- __func__, index, checksum, utbl_checksum);
- ret = -EINVAL;
- error:
- if (tmp_bh)
- brelse(tmp_bh);
- free_upcase_table(sb);
- return ret;
- }
- static s32 __load_default_upcase_table(struct super_block *sb)
- {
- s32 i, ret = -EIO;
- u32 j;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- u8 skip = false;
- u32 index = 0;
- u16 uni = 0;
- u16 **upcase_table;
- upcase_table = kmalloc((UTBL_COL_COUNT * sizeof(u16 *)), GFP_KERNEL);
- if (!upcase_table)
- return -ENOMEM;
- fsi->vol_utbl = upcase_table;
- memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *));
- for (i = 0; index <= 0xFFFF && i < SDFAT_NUM_UPCASE*2; i += 2) {
- /* FIXME : is __le16 ok? */
- //uni = le16_to_cpu(((__le16*)uni_def_upcase)[i>>1]);
- uni = get_unaligned_le16((u8 *)uni_def_upcase+i);
- if (skip) {
- MMSG("skip from 0x%x ", index);
- index += uni;
- MMSG("to 0x%x (amount of 0x%x)\n", index, uni);
- skip = false;
- } else if (uni == index) {
- index++;
- } else if (uni == 0xFFFF) {
- skip = true;
- } else { /* uni != index , uni != 0xFFFF */
- u16 col_index = get_col_index(index);
- if (!upcase_table[col_index]) {
- upcase_table[col_index] = kmalloc((UTBL_ROW_COUNT * sizeof(u16)), GFP_KERNEL);
- if (!upcase_table[col_index]) {
- EMSG("failed to allocate memory for "
- "new column 0x%x\n", col_index);
- ret = -ENOMEM;
- goto error;
- }
- for (j = 0; j < UTBL_ROW_COUNT; j++)
- upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j;
- }
- upcase_table[col_index][get_row_index(index)] = uni;
- index++;
- }
- }
- if (index >= 0xFFFF)
- return 0;
- error:
- /* FATAL error: default upcase table has error */
- free_upcase_table(sb);
- return ret;
- }
- static s32 load_upcase_table(struct super_block *sb)
- {
- s32 i, ret;
- u32 tbl_clu, type;
- u64 sector, tbl_size, num_sectors;
- u8 blksize_bits = sb->s_blocksize_bits;
- CHAIN_T clu;
- CASE_DENTRY_T *ep;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- clu.dir = fsi->root_dir;
- clu.flags = 0x01;
- if (fsi->vol_type != EXFAT)
- goto load_default;
- while (!IS_CLUS_EOF(clu.dir)) {
- for (i = 0; i < fsi->dentries_per_clu; i++) {
- ep = (CASE_DENTRY_T *) get_dentry_in_dir(sb, &clu, i, NULL);
- if (!ep)
- return -EIO;
- type = fsi->fs_func->get_entry_type((DENTRY_T *) ep);
- if (type == TYPE_UNUSED)
- break;
- if (type != TYPE_UPCASE)
- continue;
- tbl_clu = le32_to_cpu(ep->start_clu);
- tbl_size = le64_to_cpu(ep->size);
- sector = CLUS_TO_SECT(fsi, tbl_clu);
- num_sectors = ((tbl_size - 1) >> blksize_bits) + 1;
- ret = __load_upcase_table(sb, sector, num_sectors,
- le32_to_cpu(ep->checksum));
- if (ret && (ret != -EIO))
- goto load_default;
- /* load successfully */
- return ret;
- }
- if (get_next_clus_safe(sb, &(clu.dir)))
- return -EIO;
- }
- load_default:
- sdfat_log_msg(sb, KERN_INFO, "trying to load default upcase table");
- /* load default upcase table */
- return __load_default_upcase_table(sb);
- } /* end of load_upcase_table */
- /*
- * Directory Entry Management Functions
- */
- s32 walk_fat_chain(struct super_block *sb, CHAIN_T *p_dir, u32 byte_offset, u32 *clu)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- u32 clu_offset;
- u32 cur_clu;
- clu_offset = byte_offset >> fsi->cluster_size_bits;
- cur_clu = p_dir->dir;
- if (p_dir->flags == 0x03) {
- cur_clu += clu_offset;
- } else {
- while (clu_offset > 0) {
- if (get_next_clus_safe(sb, &cur_clu))
- return -EIO;
- if (IS_CLUS_EOF(cur_clu)) {
- sdfat_fs_error(sb, "invalid dentry access "
- "beyond EOF (clu : %u, eidx : %d)",
- p_dir->dir,
- byte_offset >> DENTRY_SIZE_BITS);
- return -EIO;
- }
- clu_offset--;
- }
- }
- if (clu)
- *clu = cur_clu;
- return 0;
- }
- static s32 find_location(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u64 *sector, s32 *offset)
- {
- s32 ret;
- u32 off, clu = 0;
- u32 blksize_mask = (u32)(sb->s_blocksize-1);
- u8 blksize_bits = sb->s_blocksize_bits;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- off = entry << DENTRY_SIZE_BITS;
- /* FAT16 root_dir */
- if (IS_CLUS_FREE(p_dir->dir)) {
- *offset = off & blksize_mask;
- *sector = off >> blksize_bits;
- *sector += fsi->root_start_sector;
- return 0;
- }
- ret = walk_fat_chain(sb, p_dir, off, &clu);
- if (ret)
- return ret;
- /* byte offset in cluster */
- off &= (fsi->cluster_size - 1);
- /* byte offset in sector */
- *offset = off & blksize_mask;
- /* sector offset in cluster */
- *sector = off >> blksize_bits;
- *sector += CLUS_TO_SECT(fsi, clu);
- return 0;
- } /* end of find_location */
- DENTRY_T *get_dentry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u64 *sector)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- u32 dentries_per_page = PAGE_SIZE >> DENTRY_SIZE_BITS;
- s32 off;
- u64 sec;
- u8 *buf;
- if (p_dir->dir == DIR_DELETED) {
- EMSG("%s : abnormal access to deleted dentry\n", __func__);
- BUG_ON(!fsi->prev_eio);
- return NULL;
- }
- if (find_location(sb, p_dir, entry, &sec, &off))
- return NULL;
- /* DIRECTORY READAHEAD :
- * Try to read ahead per a page except root directory of fat12/16
- */
- if ((!IS_CLUS_FREE(p_dir->dir)) &&
- !(entry & (dentries_per_page - 1)))
- dcache_readahead(sb, sec);
- buf = dcache_getblk(sb, sec);
- if (!buf)
- return NULL;
- if (sector)
- *sector = sec;
- return (DENTRY_T *)(buf + off);
- } /* end of get_dentry_in_dir */
- /* used only in search empty_slot() */
- #define CNT_UNUSED_NOHIT (-1)
- #define CNT_UNUSED_HIT (-2)
- /* search EMPTY CONTINUOUS "num_entries" entries */
- static s32 search_empty_slot(struct super_block *sb, HINT_FEMP_T *hint_femp, CHAIN_T *p_dir, s32 num_entries)
- {
- s32 i, dentry, num_empty = 0;
- s32 dentries_per_clu;
- u32 type;
- CHAIN_T clu;
- DENTRY_T *ep;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (IS_CLUS_FREE(p_dir->dir)) /* FAT16 root_dir */
- dentries_per_clu = fsi->dentries_in_root;
- else
- dentries_per_clu = fsi->dentries_per_clu;
- ASSERT(-1 <= hint_femp->eidx);
- if (hint_femp->eidx != -1) {
- clu.dir = hint_femp->cur.dir;
- clu.size = hint_femp->cur.size;
- clu.flags = hint_femp->cur.flags;
- dentry = hint_femp->eidx;
- if (num_entries <= hint_femp->count) {
- MMSG("%s: empty slot(HIT) - found "
- "(clu : 0x%08x eidx : %d)\n",
- __func__, hint_femp->cur.dir, hint_femp->eidx);
- hint_femp->eidx = -1;
- if (fsi->vol_type == EXFAT)
- return dentry;
- return dentry + (num_entries - 1);
- }
- MMSG("%s: empty slot(HIT) - search from "
- "(clu : 0x%08x eidx : %d)\n",
- __func__, hint_femp->cur.dir, hint_femp->eidx);
- } else {
- MMSG("%s: empty slot(MISS) - search from "
- "(clu:0x%08x eidx : 0)\n",
- __func__, p_dir->dir);
- clu.dir = p_dir->dir;
- clu.size = p_dir->size;
- clu.flags = p_dir->flags;
- dentry = 0;
- }
- while (!IS_CLUS_EOF(clu.dir)) {
- /* FAT16 root_dir */
- if (IS_CLUS_FREE(p_dir->dir))
- i = dentry % dentries_per_clu;
- else
- i = dentry & (dentries_per_clu-1);
- for ( ; i < dentries_per_clu; i++, dentry++) {
- ep = get_dentry_in_dir(sb, &clu, i, NULL);
- if (!ep)
- return -EIO;
- type = fsi->fs_func->get_entry_type(ep);
- if ((type == TYPE_UNUSED) || (type == TYPE_DELETED)) {
- num_empty++;
- if (hint_femp->eidx == -1) {
- hint_femp->eidx = dentry;
- hint_femp->count = CNT_UNUSED_NOHIT;
- hint_femp->cur.dir = clu.dir;
- hint_femp->cur.size = clu.size;
- hint_femp->cur.flags = clu.flags;
- }
- if ((type == TYPE_UNUSED) &&
- (hint_femp->count != CNT_UNUSED_HIT)) {
- hint_femp->count = CNT_UNUSED_HIT;
- }
- } else {
- if ((hint_femp->eidx != -1) &&
- (hint_femp->count == CNT_UNUSED_HIT)) {
- /* unused empty group means
- * an empty group which includes
- * unused dentry
- */
- sdfat_fs_error(sb,
- "found bogus dentry(%d) "
- "beyond unused empty group(%d) "
- "(start_clu : %u, cur_clu : %u)",
- dentry, hint_femp->eidx, p_dir->dir,
- clu.dir);
- return -EIO;
- }
- num_empty = 0;
- hint_femp->eidx = -1;
- }
- if (num_empty >= num_entries) {
- /* found and invalidate hint_femp */
- hint_femp->eidx = -1;
- if (fsi->vol_type == EXFAT)
- return (dentry - (num_entries-1));
- return dentry;
- }
- }
- if (IS_CLUS_FREE(p_dir->dir))
- break; /* FAT16 root_dir */
- if (clu.flags == 0x03) {
- if ((--clu.size) > 0)
- clu.dir++;
- else
- clu.dir = CLUS_EOF;
- } else {
- if (get_next_clus_safe(sb, &(clu.dir)))
- return -EIO;
- }
- }
- return -ENOSPC;
- } /* end of search_empty_slot */
- /* find empty directory entry.
- * if there isn't any empty slot, expand cluster chain.
- */
- static s32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, s32 num_entries)
- {
- s32 dentry;
- u32 ret, last_clu;
- u64 sector;
- u64 size = 0;
- CHAIN_T clu;
- DENTRY_T *ep = NULL;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- FILE_ID_T *fid = &(SDFAT_I(inode)->fid);
- HINT_FEMP_T hint_femp;
- hint_femp.eidx = -1;
- ASSERT(-1 <= fid->hint_femp.eidx);
- if (fid->hint_femp.eidx != -1) {
- memcpy(&hint_femp, &fid->hint_femp, sizeof(HINT_FEMP_T));
- fid->hint_femp.eidx = -1;
- }
- /* FAT16 root_dir */
- if (IS_CLUS_FREE(p_dir->dir))
- return search_empty_slot(sb, &hint_femp, p_dir, num_entries);
- while ((dentry = search_empty_slot(sb, &hint_femp, p_dir, num_entries)) < 0) {
- if (dentry == -EIO)
- break;
- if (fsi->fs_func->check_max_dentries(fid))
- return -ENOSPC;
- /* we trust p_dir->size regardless of FAT type */
- if (__find_last_cluster(sb, p_dir, &last_clu))
- return -EIO;
- /*
- * Allocate new cluster to this directory
- */
- clu.dir = last_clu + 1;
- clu.size = 0; /* UNUSED */
- clu.flags = p_dir->flags;
- /* (0) check if there are reserved clusters
- * (create_dir 의 주석 참고)
- */
- if (!IS_CLUS_EOF(fsi->used_clusters) &&
- ((fsi->used_clusters + fsi->reserved_clusters) >= (fsi->num_clusters - 2)))
- return -ENOSPC;
- /* (1) allocate a cluster */
- ret = fsi->fs_func->alloc_cluster(sb, 1, &clu, ALLOC_HOT);
- if (ret)
- return ret;
- if (__clear_cluster(inode, clu.dir))
- return -EIO;
- /* (2) append to the FAT chain */
- if (clu.flags != p_dir->flags) {
- /* no-fat-chain bit is disabled,
- * so fat-chain should be synced with alloc-bmp
- */
- chain_cont_cluster(sb, p_dir->dir, p_dir->size);
- p_dir->flags = 0x01;
- hint_femp.cur.flags = 0x01;
- }
- if (clu.flags == 0x01)
- if (fat_ent_set(sb, last_clu, clu.dir))
- return -EIO;
- if (hint_femp.eidx == -1) {
- /* the special case that new dentry
- * should be allocated from the start of new cluster
- */
- hint_femp.eidx = (s32)(p_dir->size <<
- (fsi->cluster_size_bits - DENTRY_SIZE_BITS));
- hint_femp.count = fsi->dentries_per_clu;
- hint_femp.cur.dir = clu.dir;
- hint_femp.cur.size = 0;
- hint_femp.cur.flags = clu.flags;
- }
- hint_femp.cur.size++;
- p_dir->size++;
- size = (p_dir->size << fsi->cluster_size_bits);
- /* (3) update the directory entry */
- if ((fsi->vol_type == EXFAT) && (p_dir->dir != fsi->root_dir)) {
- ep = get_dentry_in_dir(sb,
- &(fid->dir), fid->entry+1, §or);
- if (!ep)
- return -EIO;
- fsi->fs_func->set_entry_size(ep, size);
- fsi->fs_func->set_entry_flag(ep, p_dir->flags);
- if (dcache_modify(sb, sector))
- return -EIO;
- if (update_dir_chksum(sb, &(fid->dir), fid->entry))
- return -EIO;
- }
- /* directory inode should be updated in here */
- i_size_write(inode, (loff_t)size);
- SDFAT_I(inode)->i_size_ondisk += fsi->cluster_size;
- SDFAT_I(inode)->i_size_aligned += fsi->cluster_size;
- SDFAT_I(inode)->fid.size = size;
- SDFAT_I(inode)->fid.flags = p_dir->flags;
- inode->i_blocks += 1 << (fsi->cluster_size_bits - sb->s_blocksize_bits);
- }
- return dentry;
- } /* end of find_empty_entry */
- #define SDFAT_MIN_SUBDIR (2)
- static const char *dot_name[SDFAT_MIN_SUBDIR] = { DOS_CUR_DIR_NAME, DOS_PAR_DIR_NAME };
- static s32 __count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, u32 type, u32 *dotcnt)
- {
- s32 i, count = 0, check_dot = 0;
- s32 dentries_per_clu;
- u32 entry_type;
- CHAIN_T clu;
- DENTRY_T *ep;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (IS_CLUS_FREE(p_dir->dir)) /* FAT16 root_dir */
- dentries_per_clu = fsi->dentries_in_root;
- else
- dentries_per_clu = fsi->dentries_per_clu;
- clu.dir = p_dir->dir;
- clu.size = p_dir->size;
- clu.flags = p_dir->flags;
- if (dotcnt) {
- *dotcnt = 0;
- if (fsi->vol_type != EXFAT)
- check_dot = 1;
- }
- while (!IS_CLUS_EOF(clu.dir)) {
- for (i = 0; i < dentries_per_clu; i++) {
- ep = get_dentry_in_dir(sb, &clu, i, NULL);
- if (!ep)
- return -EIO;
- entry_type = fsi->fs_func->get_entry_type(ep);
- if (entry_type == TYPE_UNUSED)
- return count;
- if (!(type & TYPE_CRITICAL_PRI) && !(type & TYPE_BENIGN_PRI))
- continue;
- if ((type != TYPE_ALL) && (type != entry_type))
- continue;
- count++;
- if (check_dot && (i < SDFAT_MIN_SUBDIR)) {
- BUG_ON(fsi->vol_type == EXFAT);
- /* 11 is DOS_NAME_LENGTH */
- if (!strncmp(ep->dummy, dot_name[i], 11))
- (*dotcnt)++;
- }
- }
- /* FAT16 root_dir */
- if (IS_CLUS_FREE(p_dir->dir))
- break;
- if (clu.flags == 0x03) {
- if ((--clu.size) > 0)
- clu.dir++;
- else
- clu.dir = CLUS_EOF;
- } else {
- if (get_next_clus_safe(sb, &(clu.dir)))
- return -EIO;
- }
- check_dot = 0;
- }
- return count;
- }
- s32 check_dir_empty(struct super_block *sb, CHAIN_T *p_dir)
- {
- s32 i, count = 0;
- s32 dentries_per_clu;
- u32 type;
- CHAIN_T clu;
- DENTRY_T *ep;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (IS_CLUS_FREE(p_dir->dir)) /* FAT16 root_dir */
- dentries_per_clu = fsi->dentries_in_root;
- else
- dentries_per_clu = fsi->dentries_per_clu;
- clu.dir = p_dir->dir;
- clu.size = p_dir->size;
- clu.flags = p_dir->flags;
- while (!IS_CLUS_EOF(clu.dir)) {
- for (i = 0; i < dentries_per_clu; i++) {
- ep = get_dentry_in_dir(sb, &clu, i, NULL);
- if (!ep)
- return -EIO;
- type = fsi->fs_func->get_entry_type(ep);
- if (type == TYPE_UNUSED)
- return 0;
- if ((type != TYPE_FILE) && (type != TYPE_DIR))
- continue;
- /* FAT16 root_dir */
- if (IS_CLUS_FREE(p_dir->dir))
- return -ENOTEMPTY;
- if (fsi->vol_type == EXFAT)
- return -ENOTEMPTY;
- if ((p_dir->dir == fsi->root_dir) || (++count > 2))
- return -ENOTEMPTY;
- }
- /* FAT16 root_dir */
- if (IS_CLUS_FREE(p_dir->dir))
- return -ENOTEMPTY;
- if (clu.flags == 0x03) {
- if ((--clu.size) > 0)
- clu.dir++;
- else
- clu.dir = CLUS_EOF;
- } else {
- if (get_next_clus_safe(sb, &(clu.dir)))
- return -EIO;
- }
- }
- return 0;
- }
- /*
- * Name Conversion Functions
- */
- #ifdef CONFIG_SDFAT_ALLOW_LOOKUP_LOSSY_SFN
- /* over name length only */
- #define NEED_INVALIDATE_SFN(x) ((x) & NLS_NAME_OVERLEN)
- #else
- /* all lossy case */
- #define NEED_INVALIDATE_SFN(x) (x)
- #endif
- /* NOTE :
- * We should keep shortname code compatible with v1.0.15 or lower
- * So, we try to check ext-only-name at create-mode only.
- *
- * i.e. '.mtp' ->
- * v1.0.15 : ' MTP' with name_case, 0x10
- * v1.1.0 : 'MT????~?' with name_case, 0x00 and longname.
- */
- static inline void preprocess_ext_only_sfn(s32 lookup, u16 first_char, DOS_NAME_T *p_dosname, s32 *lossy)
- {
- #ifdef CONFIG_SDFAT_RESTRICT_EXT_ONLY_SFN
- int i;
- /* check ext-only-name at create-mode */
- if (*lossy || lookup || (first_char != (u16)'.'))
- return;
- p_dosname->name_case = 0xFF;
- /* move ext-name to base-name */
- for (i = 0; i < 3; i++) {
- p_dosname->name[i] = p_dosname->name[8+i];
- if (p_dosname->name[i] == ' ')
- p_dosname->name[i] = '_';
- }
- /* fill remained space with '_' */
- for (i = 3; i < 8; i++)
- p_dosname->name[i] = '_';
- /* eliminate ext-name */
- for (i = 8; i < 11; i++)
- p_dosname->name[i] = ' ';
- *lossy = NLS_NAME_LOSSY;
- #endif /* CONFIG_SDFAT_CAN_CREATE_EXT_ONLY_SFN */
- }
- /* input : dir, uni_name
- * output : num_of_entry, dos_name(format : aaaaaa~1.bbb)
- */
- static s32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir,
- UNI_NAME_T *p_uniname, s32 *entries,
- DOS_NAME_T *p_dosname, s32 lookup)
- {
- s32 ret, num_entries, lossy = NLS_NAME_NO_LOSSY;
- s8 **r;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- /* Init null char. */
- p_dosname->name[0] = '\0';
- num_entries = fsi->fs_func->calc_num_entries(p_uniname);
- if (num_entries == 0)
- return -EINVAL;
- if (fsi->vol_type == EXFAT)
- goto out;
- nls_uni16s_to_sfn(sb, p_uniname, p_dosname, &lossy);
- preprocess_ext_only_sfn(lookup, p_uniname->name[0], p_dosname, &lossy);
- if (!lossy) {
- for (r = reserved_names; *r; r++) {
- if (!strncmp((void *) p_dosname->name, *r, 8))
- return -EINVAL;
- }
- if (p_dosname->name_case != 0xFF)
- num_entries = 1;
- } else if (!lookup) {
- /* create new dos name */
- ret = fat_generate_dos_name_new(sb, p_dir, p_dosname,
- num_entries);
- if (ret)
- return ret;
- } else if (NEED_INVALIDATE_SFN(lossy)) {
- /* FIXME : We should check num_entries */
- p_dosname->name[0] = '\0';
- }
- if (num_entries > 1)
- p_dosname->name_case = 0x0;
- out:
- *entries = num_entries;
- return 0;
- } /* end of get_num_entries_and_dos_name */
- void get_uniname_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, u8 mode)
- {
- DOS_NAME_T dos_name;
- if (mode == 0x0)
- dos_name.name_case = 0x0;
- else
- dos_name.name_case = ep->lcase;
- memcpy(dos_name.name, ep->name, DOS_NAME_LENGTH);
- nls_sfn_to_uni16s(sb, &dos_name, p_uniname);
- } /* end of get_uniname_from_dos_entry */
- /* returns the length of a struct qstr, ignoring trailing dots */
- static inline unsigned int __striptail_len(unsigned int len, const char *name)
- {
- while (len && name[len - 1] == '.')
- len--;
- return len;
- }
- /*
- * Name Resolution Functions :
- * Zero if it was successful; otherwise nonzero.
- */
- static s32 __resolve_path(struct inode *inode, const u8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, int lookup)
- {
- s32 namelen;
- s32 lossy = NLS_NAME_NO_LOSSY;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- FILE_ID_T *fid = &(SDFAT_I(inode)->fid);
- /* DOT and DOTDOT are handled by VFS layer */
- /* strip all trailing spaces */
- /* DO NOTHING : Is needed? */
- /* strip all trailing periods */
- namelen = __striptail_len(strlen(path), path);
- if (!namelen)
- return -ENOENT;
- /* the limitation of linux? */
- if (strlen(path) > (MAX_NAME_LENGTH * MAX_CHARSET_SIZE))
- return -ENAMETOOLONG;
- /*
- * strip all leading spaces :
- * "MS windows 7" supports leading spaces.
- * So we should skip this preprocessing for compatibility.
- */
- /* file name conversion :
- * If lookup case, we allow bad-name for compatibility.
- */
- namelen = nls_vfsname_to_uni16s(sb, path, namelen, p_uniname, &lossy);
- if (namelen < 0)
- return namelen; /* return error value */
- if ((lossy && !lookup) || !namelen)
- return -EINVAL;
- sdfat_debug_bug_on(fid->size != i_size_read(inode));
- // fid->size = i_size_read(inode);
- p_dir->dir = fid->start_clu;
- p_dir->size = (u32)(fid->size >> fsi->cluster_size_bits);
- p_dir->flags = fid->flags;
- return 0;
- }
- static inline s32 resolve_path(struct inode *inode, const u8 *path, CHAIN_T *dir, UNI_NAME_T *uni)
- {
- return __resolve_path(inode, path, dir, uni, 0);
- }
- static inline s32 resolve_path_for_lookup(struct inode *inode, const u8 *path, CHAIN_T *dir, UNI_NAME_T *uni)
- {
- return __resolve_path(inode, path, dir, uni, 1);
- }
- static s32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid)
- {
- s32 dentry, num_entries;
- u64 ret;
- u64 size;
- CHAIN_T clu;
- DOS_NAME_T dos_name, dot_name;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name, 0);
- if (ret)
- return ret;
- /* find_empty_entry must be called before alloc_cluster */
- dentry = find_empty_entry(inode, p_dir, num_entries);
- if (dentry < 0)
- return dentry; /* -EIO or -ENOSPC */
- clu.dir = CLUS_EOF;
- clu.size = 0;
- clu.flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01;
- /* (0) Check if there are reserved clusters up to max. */
- if ((fsi->used_clusters + fsi->reserved_clusters) >= (fsi->num_clusters - CLUS_BASE))
- return -ENOSPC;
- /* (1) allocate a cluster */
- ret = fsi->fs_func->alloc_cluster(sb, 1, &clu, ALLOC_HOT);
- if (ret)
- return ret;
- ret = __clear_cluster(inode, clu.dir);
- if (ret)
- return ret;
- size = fsi->cluster_size;
- if (fsi->vol_type != EXFAT) {
- /* initialize the . and .. entry
- * Information for . points to itself
- * Information for .. points to parent dir
- */
- dot_name.name_case = 0x0;
- memcpy(dot_name.name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH);
- ret = fsi->fs_func->init_dir_entry(sb, &clu, 0, TYPE_DIR, clu.dir, 0);
- if (ret)
- return ret;
- ret = fsi->fs_func->init_ext_entry(sb, &clu, 0, 1, NULL, &dot_name);
- if (ret)
- return ret;
- memcpy(dot_name.name, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH);
- if (p_dir->dir == fsi->root_dir)
- ret = fsi->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, CLUS_FREE, 0);
- else
- ret = fsi->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, p_dir->dir, 0);
- if (ret)
- return ret;
- ret = fsi->fs_func->init_ext_entry(sb, &clu, 1, 1, NULL, &dot_name);
- if (ret)
- return ret;
- }
- /* (2) update the directory entry */
- /* make sub-dir entry in parent directory */
- ret = fsi->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_DIR, clu.dir, size);
- if (ret)
- return ret;
- ret = fsi->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name);
- if (ret)
- return ret;
- fid->dir.dir = p_dir->dir;
- fid->dir.size = p_dir->size;
- fid->dir.flags = p_dir->flags;
- fid->entry = dentry;
- fid->attr = ATTR_SUBDIR;
- fid->flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01;
- fid->size = size;
- fid->start_clu = clu.dir;
- fid->type = TYPE_DIR;
- fid->rwoffset = 0;
- fid->hint_bmap.off = CLUS_EOF;
- /* hint_stat will be used if this is directory. */
- fid->version = 0;
- fid->hint_stat.eidx = 0;
- fid->hint_stat.clu = fid->start_clu;
- fid->hint_femp.eidx = -1;
- return 0;
- } /* end of create_dir */
- static s32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, u8 mode, FILE_ID_T *fid)
- {
- s32 ret, dentry, num_entries;
- DOS_NAME_T dos_name;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name, 0);
- if (ret)
- return ret;
- /* find_empty_entry must be called before alloc_cluster() */
- dentry = find_empty_entry(inode, p_dir, num_entries);
- if (dentry < 0)
- return dentry; /* -EIO or -ENOSPC */
- /* (1) update the directory entry */
- /* fill the dos name directory entry information of the created file.
- * the first cluster is not determined yet. (0)
- */
- ret = fsi->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_FILE | mode, CLUS_FREE, 0);
- if (ret)
- return ret;
- ret = fsi->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name);
- if (ret)
- return ret;
- fid->dir.dir = p_dir->dir;
- fid->dir.size = p_dir->size;
- fid->dir.flags = p_dir->flags;
- fid->entry = dentry;
- fid->attr = ATTR_ARCHIVE | mode;
- fid->flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01;
- fid->size = 0;
- fid->start_clu = CLUS_EOF;
- fid->type = TYPE_FILE;
- fid->rwoffset = 0;
- fid->hint_bmap.off = CLUS_EOF;
- /* hint_stat will be used if this is directory. */
- fid->version = 0;
- fid->hint_stat.eidx = 0;
- fid->hint_stat.clu = fid->start_clu;
- fid->hint_femp.eidx = -1;
- return 0;
- } /* end of create_file */
- static s32 remove_file(struct inode *inode, CHAIN_T *p_dir, s32 entry)
- {
- s32 num_entries;
- u64 sector;
- DENTRY_T *ep;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- ep = get_dentry_in_dir(sb, p_dir, entry, §or);
- if (!ep)
- return -EIO;
- dcache_lock(sb, sector);
- /* dcache_lock() before call count_ext_entries() */
- num_entries = fsi->fs_func->count_ext_entries(sb, p_dir, entry, ep);
- if (num_entries < 0) {
- dcache_unlock(sb, sector);
- return -EIO;
- }
- num_entries++;
- dcache_unlock(sb, sector);
- /* (1) update the directory entry */
- return fsi->fs_func->delete_dir_entry(sb, p_dir, entry, 0, num_entries);
- } /* end of remove_file */
- static s32 rename_file(struct inode *inode, CHAIN_T *p_dir, s32 oldentry, UNI_NAME_T *p_uniname, FILE_ID_T *fid)
- {
- s32 ret, newentry = -1, num_old_entries, num_new_entries;
- u64 sector_old, sector_new;
- DOS_NAME_T dos_name;
- DENTRY_T *epold, *epnew;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- epold = get_dentry_in_dir(sb, p_dir, oldentry, §or_old);
- if (!epold)
- return -EIO;
- dcache_lock(sb, sector_old);
- /* dcache_lock() before call count_ext_entries() */
- num_old_entries = fsi->fs_func->count_ext_entries(sb, p_dir, oldentry, epold);
- if (num_old_entries < 0) {
- dcache_unlock(sb, sector_old);
- return -EIO;
- }
- num_old_entries++;
- ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_new_entries, &dos_name, 0);
- if (ret) {
- dcache_unlock(sb, sector_old);
- return ret;
- }
- if (num_old_entries < num_new_entries) {
- newentry = find_empty_entry(inode, p_dir, num_new_entries);
- if (newentry < 0) {
- dcache_unlock(sb, sector_old);
- return newentry; /* -EIO or -ENOSPC */
- }
- epnew = get_dentry_in_dir(sb, p_dir, newentry, §or_new);
- if (!epnew) {
- dcache_unlock(sb, sector_old);
- return -EIO;
- }
- memcpy((void *) epnew, (void *) epold, DENTRY_SIZE);
- if (fsi->fs_func->get_entry_type(epnew) == TYPE_FILE) {
- fsi->fs_func->set_entry_attr(epnew, fsi->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE);
- fid->attr |= ATTR_ARCHIVE;
- }
- dcache_modify(sb, sector_new);
- dcache_unlock(sb, sector_old);
- if (fsi->vol_type == EXFAT) {
- epold = get_dentry_in_dir(sb, p_dir, oldentry+1, §or_old);
- dcache_lock(sb, sector_old);
- epnew = get_dentry_in_dir(sb, p_dir, newentry+1, §or_new);
- if (!epold || !epnew) {
- dcache_unlock(sb, sector_old);
- return -EIO;
- }
- memcpy((void *) epnew, (void *) epold, DENTRY_SIZE);
- dcache_modify(sb, sector_new);
- dcache_unlock(sb, sector_old);
- }
- ret = fsi->fs_func->init_ext_entry(sb, p_dir, newentry, num_new_entries, p_uniname, &dos_name);
- if (ret)
- return ret;
- fsi->fs_func->delete_dir_entry(sb, p_dir, oldentry, 0, num_old_entries);
- fid->entry = newentry;
- } else {
- if (fsi->fs_func->get_entry_type(epold) == TYPE_FILE) {
- fsi->fs_func->set_entry_attr(epold, fsi->fs_func->get_entry_attr(epold) | ATTR_ARCHIVE);
- fid->attr |= ATTR_ARCHIVE;
- }
- dcache_modify(sb, sector_old);
- dcache_unlock(sb, sector_old);
- ret = fsi->fs_func->init_ext_entry(sb, p_dir, oldentry, num_new_entries, p_uniname, &dos_name);
- if (ret)
- return ret;
- fsi->fs_func->delete_dir_entry(sb, p_dir, oldentry, num_new_entries, num_old_entries);
- }
- return 0;
- } /* end of rename_file */
- static s32 move_file(struct inode *inode, CHAIN_T *p_olddir, s32 oldentry,
- CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid)
- {
- s32 ret, newentry, num_new_entries, num_old_entries;
- u64 sector_mov, sector_new;
- CHAIN_T clu;
- DOS_NAME_T dos_name;
- DENTRY_T *epmov, *epnew;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- epmov = get_dentry_in_dir(sb, p_olddir, oldentry, §or_mov);
- if (!epmov)
- return -EIO;
- /* check if the source and target directory is the same */
- if (fsi->fs_func->get_entry_type(epmov) == TYPE_DIR &&
- fsi->fs_func->get_entry_clu0(epmov) == p_newdir->dir)
- return -EINVAL;
- dcache_lock(sb, sector_mov);
- /* dcache_lock() before call count_ext_entries() */
- num_old_entries = fsi->fs_func->count_ext_entries(sb, p_olddir, oldentry, epmov);
- if (num_old_entries < 0) {
- dcache_unlock(sb, sector_mov);
- return -EIO;
- }
- num_old_entries++;
- ret = get_num_entries_and_dos_name(sb, p_newdir, p_uniname, &num_new_entries, &dos_name, 0);
- if (ret) {
- dcache_unlock(sb, sector_mov);
- return ret;
- }
- newentry = find_empty_entry(inode, p_newdir, num_new_entries);
- if (newentry < 0) {
- dcache_unlock(sb, sector_mov);
- return newentry; /* -EIO or -ENOSPC */
- }
- epnew = get_dentry_in_dir(sb, p_newdir, newentry, §or_new);
- if (!epnew) {
- dcache_unlock(sb, sector_mov);
- return -EIO;
- }
- memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE);
- if (fsi->fs_func->get_entry_type(epnew) == TYPE_FILE) {
- fsi->fs_func->set_entry_attr(epnew, fsi->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE);
- fid->attr |= ATTR_ARCHIVE;
- }
- dcache_modify(sb, sector_new);
- dcache_unlock(sb, sector_mov);
- if (fsi->vol_type == EXFAT) {
- epmov = get_dentry_in_dir(sb, p_olddir, oldentry+1, §or_mov);
- dcache_lock(sb, sector_mov);
- epnew = get_dentry_in_dir(sb, p_newdir, newentry+1, §or_new);
- if (!epmov || !epnew) {
- dcache_unlock(sb, sector_mov);
- return -EIO;
- }
- memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE);
- dcache_modify(sb, sector_new);
- dcache_unlock(sb, sector_mov);
- } else if (fsi->fs_func->get_entry_type(epnew) == TYPE_DIR) {
- /* change ".." pointer to new parent dir */
- clu.dir = fsi->fs_func->get_entry_clu0(epnew);
- clu.flags = 0x01;
- epnew = get_dentry_in_dir(sb, &clu, 1, §or_new);
- if (!epnew)
- return -EIO;
- if (p_newdir->dir == fsi->root_dir)
- fsi->fs_func->set_entry_clu0(epnew, CLUS_FREE);
- else
- fsi->fs_func->set_entry_clu0(epnew, p_newdir->dir);
- dcache_modify(sb, sector_new);
- }
- ret = fsi->fs_func->init_ext_entry(sb, p_newdir, newentry, num_new_entries, p_uniname, &dos_name);
- if (ret)
- return ret;
- fsi->fs_func->delete_dir_entry(sb, p_olddir, oldentry, 0, num_old_entries);
- fid->dir.dir = p_newdir->dir;
- fid->dir.size = p_newdir->size;
- fid->dir.flags = p_newdir->flags;
- fid->entry = newentry;
- return 0;
- } /* end of move_file */
- /*======================================================================*/
- /* Global Function Definitions */
- /*======================================================================*/
- /* roll back to the initial state of the file system */
- s32 fscore_init(void)
- {
- s32 ret;
- ret = check_type_size();
- if (ret)
- return ret;
- return extent_cache_init();
- }
- /* make free all memory-alloced global buffers */
- s32 fscore_shutdown(void)
- {
- extent_cache_shutdown();
- return 0;
- }
- /* check device is ejected */
- s32 fscore_check_bdi_valid(struct super_block *sb)
- {
- return bdev_check_bdi_valid(sb);
- }
- static bool is_exfat(pbr_t *pbr)
- {
- int i = 53;
- do {
- if (pbr->bpb.f64.res_zero[i-1])
- break;
- } while (--i);
- return i ? false : true;
- }
- static bool is_fat32(pbr_t *pbr)
- {
- if (le16_to_cpu(pbr->bpb.fat.num_fat_sectors))
- return false;
- return true;
- }
- inline pbr_t *read_pbr_with_logical_sector(struct super_block *sb, struct buffer_head **prev_bh)
- {
- pbr_t *p_pbr = (pbr_t *) (*prev_bh)->b_data;
- u16 logical_sect = 0;
- if (is_exfat(p_pbr))
- logical_sect = 1 << p_pbr->bsx.f64.sect_size_bits;
- else
- logical_sect = get_unaligned_le16(&p_pbr->bpb.fat.sect_size);
- /* is x a power of 2?
- * (x) != 0 && (((x) & ((x) - 1)) == 0)
- */
- if (!is_power_of_2(logical_sect)
- || (logical_sect < 512)
- || (logical_sect > 4096)) {
- sdfat_log_msg(sb, KERN_ERR, "bogus logical sector size %u",
- logical_sect);
- return NULL;
- }
- if (logical_sect < sb->s_blocksize) {
- sdfat_log_msg(sb, KERN_ERR,
- "logical sector size too small for device"
- " (logical sector size = %u)", logical_sect);
- return NULL;
- }
- if (logical_sect > sb->s_blocksize) {
- struct buffer_head *bh = NULL;
- __brelse(*prev_bh);
- *prev_bh = NULL;
- if (!sb_set_blocksize(sb, logical_sect)) {
- sdfat_log_msg(sb, KERN_ERR,
- "unable to set blocksize %u", logical_sect);
- return NULL;
- }
- bh = sb_bread(sb, 0);
- if (!bh) {
- sdfat_log_msg(sb, KERN_ERR,
- "unable to read boot sector "
- "(logical sector size = %lu)", sb->s_blocksize);
- return NULL;
- }
- *prev_bh = bh;
- p_pbr = (pbr_t *) bh->b_data;
- }
- sdfat_log_msg(sb, KERN_INFO,
- "set logical sector size : %lu", sb->s_blocksize);
- return p_pbr;
- }
- /* mount the file system volume */
- s32 fscore_mount(struct super_block *sb)
- {
- s32 ret;
- pbr_t *p_pbr;
- struct buffer_head *tmp_bh = NULL;
- struct gendisk *disk = sb->s_bdev->bd_disk;
- struct hd_struct *part = sb->s_bdev->bd_part;
- struct sdfat_mount_options *opts = &(SDFAT_SB(sb)->options);
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- /* initialize previous I/O error */
- fsi->prev_eio = 0;
- /* open the block device */
- if (bdev_open_dev(sb))
- return -EIO;
- /* set block size to read super block */
- sb_min_blocksize(sb, 512);
- /* read boot sector */
- ret = read_sect(sb, 0, &tmp_bh, 1);
- if (ret) {
- sdfat_log_msg(sb, KERN_ERR, "unable to read boot sector");
- ret = -EIO;
- goto bd_close;
- }
- /* PRB is read */
- p_pbr = (pbr_t *) tmp_bh->b_data;
- /* check the validity of PBR */
- if (le16_to_cpu((p_pbr->signature)) != PBR_SIGNATURE) {
- sdfat_log_msg(sb, KERN_ERR, "invalid boot record signature");
- brelse(tmp_bh);
- ret = -EINVAL;
- goto bd_close;
- }
- /* check logical sector size */
- p_pbr = read_pbr_with_logical_sector(sb, &tmp_bh);
- if (!p_pbr) {
- brelse(tmp_bh);
- ret = -EIO;
- goto bd_close;
- }
- /* fill fs_struct */
- if (is_exfat(p_pbr)) {
- if (opts->fs_type && opts->fs_type != FS_TYPE_EXFAT) {
- sdfat_log_msg(sb, KERN_ERR,
- "not specified filesystem type "
- "(media:exfat, opts:%s)",
- FS_TYPE_STR[opts->fs_type]);
- ret = -EINVAL;
- goto free_bh;
- }
- /* set maximum file size for exFAT */
- sb->s_maxbytes = 0x7fffffffffffffffLL;
- opts->improved_allocation = 0;
- opts->defrag = 0;
- ret = mount_exfat(sb, p_pbr);
- } else {
- if (opts->fs_type && opts->fs_type != FS_TYPE_VFAT) {
- sdfat_log_msg(sb, KERN_ERR,
- "not specified filesystem type "
- "(media:vfat, opts:%s)",
- FS_TYPE_STR[opts->fs_type]);
- ret = -EINVAL;
- goto free_bh;
- }
- /* set maximum file size for FAT */
- sb->s_maxbytes = 0xffffffff;
- if (is_fat32(p_pbr)) {
- ret = mount_fat32(sb, p_pbr);
- } else {
- opts->improved_allocation = 0;
- opts->defrag = 0;
- ret = mount_fat16(sb, p_pbr);
- }
- }
- free_bh:
- brelse(tmp_bh);
- if (ret) {
- sdfat_log_msg(sb, KERN_ERR, "failed to mount fs-core");
- goto bd_close;
- }
- /* warn misaligned data data start sector must be a multiple of clu_size */
- sdfat_log_msg(sb, KERN_INFO,
- "detected volume info : %s "
- "(%04hX-%04hX, bps : %lu, spc : %u, data start : %llu, %s)",
- sdfat_get_vol_type_str(fsi->vol_type),
- (fsi->vol_id >> 16) & 0xffff, fsi->vol_id & 0xffff,
- sb->s_blocksize, fsi->sect_per_clus, fsi->data_start_sector,
- (fsi->data_start_sector & (fsi->sect_per_clus - 1)) ?
- "misaligned" : "aligned");
- sdfat_log_msg(sb, KERN_INFO,
- "detected volume size : %llu KB (disk : %llu KB, "
- "part : %llu KB)",
- (fsi->num_sectors * (sb->s_blocksize >> SECTOR_SIZE_BITS)) >> 1,
- disk ? (u64)((disk->part0.nr_sects) >> 1) : 0,
- part ? (u64)((part->nr_sects) >> 1) : 0);
- ret = load_upcase_table(sb);
- if (ret) {
- sdfat_log_msg(sb, KERN_ERR, "failed to load upcase table");
- goto bd_close;
- }
- if (fsi->vol_type != EXFAT)
- goto update_used_clus;
- /* allocate-bitmap is only for exFAT */
- ret = load_alloc_bmp(sb);
- if (ret) {
- sdfat_log_msg(sb, KERN_ERR, "failed to load alloc-bitmap");
- goto free_upcase;
- }
- update_used_clus:
- if (fsi->used_clusters == (u32) ~0) {
- ret = fsi->fs_func->count_used_clusters(sb, &fsi->used_clusters);
- if (ret) {
- sdfat_log_msg(sb, KERN_ERR, "failed to scan clusters");
- goto free_alloc_bmp;
- }
- }
- return 0;
- free_alloc_bmp:
- if (fsi->vol_type == EXFAT)
- free_alloc_bmp(sb);
- free_upcase:
- free_upcase_table(sb);
- bd_close:
- bdev_close_dev(sb);
- return ret;
- } /* end of fscore_mount */
- /* umount the file system volume */
- s32 fscore_umount(struct super_block *sb)
- {
- s32 ret = 0;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (fs_sync(sb, 0))
- ret = -EIO;
- if (fs_set_vol_flags(sb, VOL_CLEAN))
- ret = -EIO;
- free_upcase_table(sb);
- if (fsi->vol_type == EXFAT)
- free_alloc_bmp(sb);
- if (fcache_release_all(sb))
- ret = -EIO;
- if (dcache_release_all(sb))
- ret = -EIO;
- amap_destroy(sb);
- if (fsi->prev_eio)
- ret = -EIO;
- /* close the block device */
- bdev_close_dev(sb);
- return ret;
- }
- /* get the information of a file system volume */
- s32 fscore_statfs(struct super_block *sb, VOL_INFO_T *info)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (fsi->used_clusters == (u32) ~0) {
- if (fsi->fs_func->count_used_clusters(sb, &fsi->used_clusters))
- return -EIO;
- }
- info->FatType = fsi->vol_type;
- info->ClusterSize = fsi->cluster_size;
- info->NumClusters = fsi->num_clusters - 2; /* clu 0 & 1 */
- info->UsedClusters = fsi->used_clusters + fsi->reserved_clusters;
- info->FreeClusters = info->NumClusters - info->UsedClusters;
- return 0;
- }
- /* synchronize all file system volumes */
- s32 fscore_sync_fs(struct super_block *sb, s32 do_sync)
- {
- /* synchronize the file system */
- if (fs_sync(sb, do_sync))
- return -EIO;
- if (fs_set_vol_flags(sb, VOL_CLEAN))
- return -EIO;
- return 0;
- }
- /* stat allocation unit of a file system volume */
- u32 fscore_get_au_stat(struct super_block *sb, s32 mode)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (fsi->fs_func->get_au_stat)
- return fsi->fs_func->get_au_stat(sb, mode);
- /* No error, just returns 0 */
- return 0;
- }
- /*----------------------------------------------------------------------*/
- /* File Operation Functions */
- /*----------------------------------------------------------------------*/
- /* lookup a file */
- s32 fscore_lookup(struct inode *inode, u8 *path, FILE_ID_T *fid)
- {
- s32 ret, dentry, num_entries;
- CHAIN_T dir;
- UNI_NAME_T uni_name;
- DOS_NAME_T dos_name;
- DENTRY_T *ep, *ep2;
- ENTRY_SET_CACHE_T *es = NULL;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- FILE_ID_T *dir_fid = &(SDFAT_I(inode)->fid);
- TMSG("%s entered\n", __func__);
- /* check the validity of directory name in the given pathname */
- ret = resolve_path_for_lookup(inode, path, &dir, &uni_name);
- if (ret)
- return ret;
- ret = get_num_entries_and_dos_name(sb, &dir, &uni_name, &num_entries, &dos_name, 1);
- if (ret)
- return ret;
- /* check the validation of hint_stat and initialize it if required */
- if (dir_fid->version != (u32)inode_peek_iversion(inode)) {
- dir_fid->hint_stat.clu = dir.dir;
- dir_fid->hint_stat.eidx = 0;
- dir_fid->version = (u32)inode_peek_iversion(inode);
- dir_fid->hint_femp.eidx = -1;
- }
- /* search the file name for directories */
- dentry = fsi->fs_func->find_dir_entry(sb, dir_fid, &dir, &uni_name,
- num_entries, &dos_name, TYPE_ALL);
- if ((dentry < 0) && (dentry != -EEXIST))
- return dentry; /* -error value */
- fid->dir.dir = dir.dir;
- fid->dir.size = dir.size;
- fid->dir.flags = dir.flags;
- fid->entry = dentry;
- /* root directory itself */
- if (unlikely(dentry == -EEXIST)) {
- fid->type = TYPE_DIR;
- fid->rwoffset = 0;
- fid->hint_bmap.off = CLUS_EOF;
- fid->attr = ATTR_SUBDIR;
- fid->flags = 0x01;
- fid->size = 0;
- fid->start_clu = fsi->root_dir;
- } else {
- if (fsi->vol_type == EXFAT) {
- es = get_dentry_set_in_dir(sb, &dir, dentry, ES_2_ENTRIES, &ep);
- if (!es)
- return -EIO;
- ep2 = ep+1;
- } else {
- ep = get_dentry_in_dir(sb, &dir, dentry, NULL);
- if (!ep)
- return -EIO;
- ep2 = ep;
- }
- fid->type = fsi->fs_func->get_entry_type(ep);
- fid->rwoffset = 0;
- fid->hint_bmap.off = CLUS_EOF;
- fid->attr = fsi->fs_func->get_entry_attr(ep);
- fid->size = fsi->fs_func->get_entry_size(ep2);
- if ((fid->type == TYPE_FILE) && (fid->size == 0)) {
- fid->flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01;
- fid->start_clu = CLUS_EOF;
- } else {
- fid->flags = fsi->fs_func->get_entry_flag(ep2);
- fid->start_clu = fsi->fs_func->get_entry_clu0(ep2);
- }
- if ((fid->type == TYPE_DIR) && (fsi->vol_type != EXFAT)) {
- u32 num_clu = 0;
- CHAIN_T tmp_dir;
- tmp_dir.dir = fid->start_clu;
- tmp_dir.flags = fid->flags;
- tmp_dir.size = 0; /* UNUSED */
- if (__count_num_clusters(sb, &tmp_dir, &num_clu))
- return -EIO;
- fid->size = (u64)num_clu << fsi->cluster_size_bits;
- }
- /* FOR GRACEFUL ERROR HANDLING */
- if (IS_CLUS_FREE(fid->start_clu)) {
- sdfat_fs_error(sb,
- "non-zero size file starts with zero cluster "
- "(size : %llu, p_dir : %u, entry : 0x%08x)",
- fid->size, fid->dir.dir, fid->entry);
- sdfat_debug_bug_on(1);
- return -EIO;
- }
- if (fsi->vol_type == EXFAT)
- release_dentry_set(es);
- }
- /* hint_stat will be used if this is directory. */
- fid->version = 0;
- fid->hint_stat.eidx = 0;
- fid->hint_stat.clu = fid->start_clu;
- fid->hint_femp.eidx = -1;
- TMSG("%s exited successfully\n", __func__);
- return 0;
- } /* end of fscore_lookup */
- /* create a file */
- s32 fscore_create(struct inode *inode, u8 *path, u8 mode, FILE_ID_T *fid)
- {
- s32 ret/*, dentry*/;
- CHAIN_T dir;
- UNI_NAME_T uni_name;
- struct super_block *sb = inode->i_sb;
- /* check the validity of directory name in the given pathname */
- ret = resolve_path(inode, path, &dir, &uni_name);
- if (ret)
- return ret;
- fs_set_vol_flags(sb, VOL_DIRTY);
- /* create a new file */
- ret = create_file(inode, &dir, &uni_name, mode, fid);
- fs_sync(sb, 0);
- fs_set_vol_flags(sb, VOL_CLEAN);
- return ret;
- }
- /* read data from a opened file */
- s32 fscore_read_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount)
- {
- s32 ret = 0;
- s32 offset, sec_offset;
- u32 clu_offset;
- u32 clu;
- u64 logsector, oneblkread, read_bytes;
- struct buffer_head *tmp_bh = NULL;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- /* check if the given file ID is opened */
- if (fid->type != TYPE_FILE)
- return -EPERM;
- if (fid->rwoffset > fid->size)
- fid->rwoffset = fid->size;
- if (count > (fid->size - fid->rwoffset))
- count = fid->size - fid->rwoffset;
- if (count == 0) {
- if (rcount)
- *rcount = 0;
- return 0;
- }
- read_bytes = 0;
- while (count > 0) {
- clu_offset = fid->rwoffset >> fsi->cluster_size_bits;
- clu = fid->start_clu;
- if (fid->flags == 0x03) {
- clu += clu_offset;
- } else {
- /* hint information */
- if ((clu_offset > 0) &&
- ((fid->hint_bmap.off != CLUS_EOF) && (fid->hint_bmap.off > 0)) &&
- (clu_offset >= fid->hint_bmap.off)) {
- clu_offset -= fid->hint_bmap.off;
- clu = fid->hint_bmap.clu;
- }
- while (clu_offset > 0) {
- ret = get_next_clus_safe(sb, &clu);
- if (ret)
- goto err_out;
- clu_offset--;
- }
- }
- /* hint information */
- fid->hint_bmap.off = fid->rwoffset >> fsi->cluster_size_bits;
- fid->hint_bmap.clu = clu;
- offset = (s32)(fid->rwoffset & (fsi->cluster_size - 1)); /* byte offset in cluster */
- sec_offset = offset >> sb->s_blocksize_bits; /* sector offset in cluster */
- offset &= (sb->s_blocksize - 1); /* byte offset in sector */
- logsector = CLUS_TO_SECT(fsi, clu) + sec_offset;
- oneblkread = (u64)(sb->s_blocksize - offset);
- if (oneblkread > count)
- oneblkread = count;
- if ((offset == 0) && (oneblkread == sb->s_blocksize)) {
- ret = read_sect(sb, logsector, &tmp_bh, 1);
- if (ret)
- goto err_out;
- memcpy(((s8 *) buffer)+read_bytes, ((s8 *) tmp_bh->b_data), (s32) oneblkread);
- } else {
- ret = read_sect(sb, logsector, &tmp_bh, 1);
- if (ret)
- goto err_out;
- memcpy(((s8 *) buffer)+read_bytes, ((s8 *) tmp_bh->b_data)+offset, (s32) oneblkread);
- }
- count -= oneblkread;
- read_bytes += oneblkread;
- fid->rwoffset += oneblkread;
- }
- err_out:
- brelse(tmp_bh);
- /* set the size of read bytes */
- if (rcount != NULL)
- *rcount = read_bytes;
- return ret;
- } /* end of fscore_read_link */
- /* write data into a opened file */
- s32 fscore_write_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount)
- {
- s32 ret = 0;
- s32 modified = false, offset, sec_offset;
- u32 clu_offset, num_clusters, num_alloc;
- u32 clu, last_clu;
- u64 logsector, sector, oneblkwrite, write_bytes;
- CHAIN_T new_clu;
- TIMESTAMP_T tm;
- DENTRY_T *ep, *ep2;
- ENTRY_SET_CACHE_T *es = NULL;
- struct buffer_head *tmp_bh = NULL;
- struct super_block *sb = inode->i_sb;
- u32 blksize = (u32)sb->s_blocksize;
- u32 blksize_mask = (u32)(sb->s_blocksize-1);
- u8 blksize_bits = sb->s_blocksize_bits;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- /* check if the given file ID is opened */
- if (fid->type != TYPE_FILE)
- return -EPERM;
- if (fid->rwoffset > fid->size)
- fid->rwoffset = fid->size;
- if (count == 0) {
- if (wcount)
- *wcount = 0;
- return 0;
- }
- fs_set_vol_flags(sb, VOL_DIRTY);
- if (fid->size == 0)
- num_clusters = 0;
- else
- num_clusters = ((fid->size-1) >> fsi->cluster_size_bits) + 1;
- write_bytes = 0;
- while (count > 0) {
- clu_offset = (fid->rwoffset >> fsi->cluster_size_bits);
- clu = last_clu = fid->start_clu;
- if (fid->flags == 0x03) {
- if ((clu_offset > 0) && (!IS_CLUS_EOF(clu))) {
- last_clu += clu_offset - 1;
- if (clu_offset == num_clusters)
- clu = CLUS_EOF;
- else
- clu += clu_offset;
- }
- } else {
- /* hint information */
- if ((clu_offset > 0) &&
- ((fid->hint_bmap.off != CLUS_EOF) && (fid->hint_bmap.off > 0)) &&
- (clu_offset >= fid->hint_bmap.off)) {
- clu_offset -= fid->hint_bmap.off;
- clu = fid->hint_bmap.clu;
- }
- while ((clu_offset > 0) && (!IS_CLUS_EOF(clu))) {
- last_clu = clu;
- ret = get_next_clus_safe(sb, &clu);
- if (ret)
- goto err_out;
- clu_offset--;
- }
- }
- if (IS_CLUS_EOF(clu)) {
- num_alloc = ((count-1) >> fsi->cluster_size_bits) + 1;
- new_clu.dir = IS_CLUS_EOF(last_clu) ? CLUS_EOF : last_clu+1;
- new_clu.size = 0;
- new_clu.flags = fid->flags;
- /* (1) allocate a chain of clusters */
- ret = fsi->fs_func->alloc_cluster(sb, num_alloc, &new_clu, ALLOC_COLD);
- if (ret)
- goto err_out;
- /* (2) append to the FAT chain */
- if (IS_CLUS_EOF(last_clu)) {
- if (new_clu.flags == 0x01)
- fid->flags = 0x01;
- fid->start_clu = new_clu.dir;
- modified = true;
- } else {
- if (new_clu.flags != fid->flags) {
- /* no-fat-chain bit is disabled,
- * so fat-chain should be synced with
- * alloc-bmp
- */
- chain_cont_cluster(sb, fid->start_clu, num_clusters);
- fid->flags = 0x01;
- modified = true;
- }
- if (new_clu.flags == 0x01) {
- ret = fat_ent_set(sb, last_clu, new_clu.dir);
- if (ret)
- goto err_out;
- }
- }
- num_clusters += num_alloc;
- clu = new_clu.dir;
- }
- /* hint information */
- fid->hint_bmap.off = fid->rwoffset >> fsi->cluster_size_bits;
- fid->hint_bmap.clu = clu;
- /* byte offset in cluster */
- offset = (s32)(fid->rwoffset & (fsi->cluster_size-1));
- /* sector offset in cluster */
- sec_offset = offset >> blksize_bits;
- /* byte offset in sector */
- offset &= blksize_mask;
- logsector = CLUS_TO_SECT(fsi, clu) + sec_offset;
- oneblkwrite = (u64)(blksize - offset);
- if (oneblkwrite > count)
- oneblkwrite = count;
- if ((offset == 0) && (oneblkwrite == blksize)) {
- ret = read_sect(sb, logsector, &tmp_bh, 0);
- if (ret)
- goto err_out;
- memcpy(((s8 *)tmp_bh->b_data),
- ((s8 *)buffer)+write_bytes,
- (s32)oneblkwrite);
- ret = write_sect(sb, logsector, tmp_bh, 0);
- if (ret) {
- brelse(tmp_bh);
- goto err_out;
- }
- } else {
- if ((offset > 0) || ((fid->rwoffset+oneblkwrite) < fid->size)) {
- ret = read_sect(sb, logsector, &tmp_bh, 1);
- if (ret)
- goto err_out;
- } else {
- ret = read_sect(sb, logsector, &tmp_bh, 0);
- if (ret)
- goto err_out;
- }
- memcpy(((s8 *) tmp_bh->b_data)+offset, ((s8 *) buffer)+write_bytes, (s32) oneblkwrite);
- ret = write_sect(sb, logsector, tmp_bh, 0);
- if (ret) {
- brelse(tmp_bh);
- goto err_out;
- }
- }
- count -= oneblkwrite;
- write_bytes += oneblkwrite;
- fid->rwoffset += oneblkwrite;
- fid->attr |= ATTR_ARCHIVE;
- if (fid->size < fid->rwoffset) {
- fid->size = fid->rwoffset;
- modified = true;
- }
- }
- brelse(tmp_bh);
- /* (3) update the direcoty entry */
- /* get_entry_(set_)in_dir shoulb be check DIR_DELETED flag. */
- if (fsi->vol_type == EXFAT) {
- es = get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep);
- if (!es) {
- ret = -EIO;
- goto err_out;
- }
- ep2 = ep+1;
- } else {
- ep = get_dentry_in_dir(sb, &(fid->dir), fid->entry, §or);
- if (!ep) {
- ret = -EIO;
- goto err_out;
- }
- ep2 = ep;
- }
- fsi->fs_func->set_entry_time(ep, tm_now(inode, &tm), TM_MODIFY);
- fsi->fs_func->set_entry_attr(ep, fid->attr);
- if (modified) {
- if (fsi->fs_func->get_entry_flag(ep2) != fid->flags)
- fsi->fs_func->set_entry_flag(ep2, fid->flags);
- if (fsi->fs_func->get_entry_size(ep2) != fid->size)
- fsi->fs_func->set_entry_size(ep2, fid->size);
- if (fsi->fs_func->get_entry_clu0(ep2) != fid->start_clu)
- fsi->fs_func->set_entry_clu0(ep2, fid->start_clu);
- }
- if (fsi->vol_type == EXFAT) {
- if (update_dir_chksum_with_entry_set(sb, es)) {
- ret = -EIO;
- goto err_out;
- }
- release_dentry_set(es);
- } else {
- if (dcache_modify(sb, sector)) {
- ret = -EIO;
- goto err_out;
- }
- }
- fs_sync(sb, 0);
- fs_set_vol_flags(sb, VOL_CLEAN);
- err_out:
- /* set the size of written bytes */
- if (wcount)
- *wcount = write_bytes;
- return ret;
- } /* end of fscore_write_link */
- /* resize the file length */
- s32 fscore_truncate(struct inode *inode, u64 old_size, u64 new_size)
- {
- u32 num_clusters_new, num_clusters_da, num_clusters_phys;
- u32 last_clu = CLUS_FREE;
- u64 sector;
- CHAIN_T clu;
- TIMESTAMP_T tm;
- DENTRY_T *ep, *ep2;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- FILE_ID_T *fid = &(SDFAT_I(inode)->fid);
- ENTRY_SET_CACHE_T *es = NULL;
- s32 evict = (fid->dir.dir == DIR_DELETED) ? 1 : 0;
- /* check if the given file ID is opened */
- if ((fid->type != TYPE_FILE) && (fid->type != TYPE_DIR))
- return -EPERM;
- /* TO CHECK inode type and size */
- MMSG("%s: inode(%p) type(%s) size:%lld->%lld\n", __func__, inode,
- (fid->type == TYPE_FILE) ? "file" : "dir", old_size, new_size);
- /* XXX : This is for debugging. */
- /* It can be when write failed */
- #if 0
- if (fid->size != old_size) {
- DMSG("%s: inode(%p) size-mismatch(old:%lld != fid:%lld)\n",
- __func__, inode, old_size, fid->size);
- WARN_ON(1);
- }
- #endif
- /*
- * There is no lock to protect fid->size.
- * So, we should get old_size and use it.
- */
- if (old_size <= new_size)
- return 0;
- fs_set_vol_flags(sb, VOL_DIRTY);
- /* Reserved count update */
- #define num_clusters(v) ((v) ? (u32)(((v) - 1) >> fsi->cluster_size_bits) + 1 : 0)
- num_clusters_da = num_clusters(SDFAT_I(inode)->i_size_aligned);
- num_clusters_new = num_clusters(i_size_read(inode));
- num_clusters_phys = num_clusters(SDFAT_I(inode)->i_size_ondisk);
- /* num_clusters(i_size_old) should be equal to num_clusters_da */
- BUG_ON((num_clusters(old_size)) != (num_clusters(SDFAT_I(inode)->i_size_aligned)));
- /* for debugging (FIXME: is okay on no-da case?) */
- BUG_ON(num_clusters_da < num_clusters_phys);
- if ((num_clusters_da != num_clusters_phys) &&
- (num_clusters_new < num_clusters_da)) {
- /* Decrement reserved clusters
- * n_reserved = num_clusters_da - max(new,phys)
- */
- int n_reserved = (num_clusters_new > num_clusters_phys) ?
- (num_clusters_da - num_clusters_new) :
- (num_clusters_da - num_clusters_phys);
- fsi->reserved_clusters -= n_reserved;
- BUG_ON(fsi->reserved_clusters < 0);
- }
- clu.dir = fid->start_clu;
- /* In no-da case, num_clusters_phys is equal to below value
- * clu.size = (u32)((old_size-1) >> fsi->cluster_size_bits) + 1;
- */
- clu.size = num_clusters_phys;
- clu.flags = fid->flags;
- /* For bigdata */
- sdfat_statistics_set_trunc(clu.flags, &clu);
- if (new_size > 0) {
- /* Truncate FAT chain num_clusters after the first cluster
- * num_clusters = min(new, phys);
- */
- u32 num_clusters = (num_clusters_new < num_clusters_phys) ?
- num_clusters_new : num_clusters_phys;
- /* Follow FAT chain
- * (defensive coding - works fine even with corrupted FAT table
- */
- if (clu.flags == 0x03) {
- clu.dir += num_clusters;
- clu.size -= num_clusters;
- #if 0
- /* extent_get_clus can`t know last_cluster
- * when find target cluster in cache.
- */
- } else if (fid->type == TYPE_FILE) {
- u32 fclus = 0;
- s32 err = extent_get_clus(inode, num_clusters,
- &fclus, &(clu.dir), &last_clu, 0);
- if (err)
- return -EIO;
- ASSERT(fclus == num_clusters);
- if ((num_clusters > 1) && (last_clu == fid->start_clu)) {
- u32 fclus_tmp = 0;
- u32 temp = 0;
- err = extent_get_clus(inode, num_clusters - 1,
- &fclus_tmp, &last_clu, &temp, 0);
- if (err)
- return -EIO;
- ASSERT(fclus_tmp == (num_clusters - 1));
- }
- num_clusters -= fclus;
- clu.size -= fclus;
- #endif
- } else {
- while (num_clusters > 0) {
- last_clu = clu.dir;
- if (get_next_clus_safe(sb, &(clu.dir)))
- return -EIO;
- num_clusters--;
- clu.size--;
- }
- }
- /* Optimization avialable: */
- #if 0
- if (num_clusters_new < num_clusters) {
- < loop >
- } else {
- // num_clusters_new >= num_clusters_phys
- // FAT truncation is not necessary
- clu.dir = CLUS_EOF;
- clu.size = 0;
- }
- #endif
- } else if (new_size == 0) {
- fid->flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01;
- fid->start_clu = CLUS_EOF;
- }
- fid->size = new_size;
- if (fid->type == TYPE_FILE)
- fid->attr |= ATTR_ARCHIVE;
- /*
- * clu.dir: free from
- * clu.size: # of clusters to free (exFAT, 0x03 only), no fat_free if 0
- * clu.flags: fid->flags (exFAT only)
- */
- /* (1) update the directory entry */
- if (!evict) {
- if (fsi->vol_type == EXFAT) {
- es = get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep);
- if (!es)
- return -EIO;
- ep2 = ep+1;
- } else {
- ep = get_dentry_in_dir(sb, &(fid->dir), fid->entry, §or);
- if (!ep)
- return -EIO;
- ep2 = ep;
- }
- fsi->fs_func->set_entry_time(ep, tm_now(inode, &tm), TM_MODIFY);
- fsi->fs_func->set_entry_attr(ep, fid->attr);
- /*
- * if (fsi->vol_type != EXFAT)
- * dcache_modify(sb, sector);
- */
- /* File size should be zero if there is no cluster allocated */
- if (IS_CLUS_EOF(fid->start_clu))
- fsi->fs_func->set_entry_size(ep2, 0);
- else
- fsi->fs_func->set_entry_size(ep2, new_size);
- if (new_size == 0) {
- /* Any directory can not be truncated to zero */
- BUG_ON(fid->type != TYPE_FILE);
- fsi->fs_func->set_entry_flag(ep2, 0x01);
- fsi->fs_func->set_entry_clu0(ep2, CLUS_FREE);
- }
- if (fsi->vol_type == EXFAT) {
- if (update_dir_chksum_with_entry_set(sb, es))
- return -EIO;
- release_dentry_set(es);
- } else {
- if (dcache_modify(sb, sector))
- return -EIO;
- }
- } /* end of if(fid->dir.dir != DIR_DELETED) */
- /* (2) cut off from the FAT chain */
- if ((fid->flags == 0x01) &&
- (!IS_CLUS_FREE(last_clu)) && (!IS_CLUS_EOF(last_clu))) {
- if (fat_ent_set(sb, last_clu, CLUS_EOF))
- return -EIO;
- }
- /* (3) invalidate cache and free the clusters */
- /* clear extent cache */
- extent_cache_inval_inode(inode);
- /* hint information */
- fid->hint_bmap.off = CLUS_EOF;
- fid->hint_bmap.clu = CLUS_EOF;
- if (fid->rwoffset > fid->size)
- fid->rwoffset = fid->size;
- /* hint_stat will be used if this is directory. */
- fid->hint_stat.eidx = 0;
- fid->hint_stat.clu = fid->start_clu;
- fid->hint_femp.eidx = -1;
- /* free the clusters */
- if (fsi->fs_func->free_cluster(sb, &clu, evict))
- return -EIO;
- fs_sync(sb, 0);
- fs_set_vol_flags(sb, VOL_CLEAN);
- return 0;
- } /* end of fscore_truncate */
- static void update_parent_info(FILE_ID_T *fid, struct inode *parent_inode)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(parent_inode->i_sb)->fsi);
- FILE_ID_T *parent_fid = &(SDFAT_I(parent_inode)->fid);
- /*
- * the problem that FILE_ID_T caches wrong parent info.
- *
- * because of flag-mismatch of fid->dir,
- * there is abnormal traversing cluster chain.
- */
- if (unlikely((parent_fid->flags != fid->dir.flags)
- || (parent_fid->size != (fid->dir.size<<fsi->cluster_size_bits))
- || (parent_fid->start_clu != fid->dir.dir))) {
- fid->dir.dir = parent_fid->start_clu;
- fid->dir.flags = parent_fid->flags;
- fid->dir.size = ((parent_fid->size + (fsi->cluster_size-1))
- >> fsi->cluster_size_bits);
- }
- }
- /* rename or move a old file into a new file */
- s32 fscore_rename(struct inode *old_parent_inode, FILE_ID_T *fid,
- struct inode *new_parent_inode, struct dentry *new_dentry)
- {
- s32 ret;
- s32 dentry;
- CHAIN_T olddir, newdir;
- CHAIN_T *p_dir = NULL;
- UNI_NAME_T uni_name;
- DENTRY_T *ep;
- struct super_block *sb = old_parent_inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- u8 *new_path = (u8 *) new_dentry->d_name.name;
- struct inode *new_inode = new_dentry->d_inode;
- int num_entries;
- FILE_ID_T *new_fid = NULL;
- u32 new_entry_type = TYPE_UNUSED;
- s32 new_entry = 0;
- /* check the validity of pointer parameters */
- if ((new_path == NULL) || (strlen(new_path) == 0))
- return -EINVAL;
- if (fid->dir.dir == DIR_DELETED) {
- EMSG("%s : abnormal access to deleted source dentry\n", __func__);
- return -ENOENT;
- }
- /* patch 1.2.4 : the problem that FILE_ID_T caches wrong parent info. */
- update_parent_info(fid, old_parent_inode);
- olddir.dir = fid->dir.dir;
- olddir.size = fid->dir.size;
- olddir.flags = fid->dir.flags;
- dentry = fid->entry;
- /* check if the old file is "." or ".." */
- if (fsi->vol_type != EXFAT) {
- if ((olddir.dir != fsi->root_dir) && (dentry < 2))
- return -EPERM;
- }
- ep = get_dentry_in_dir(sb, &olddir, dentry, NULL);
- if (!ep)
- return -EIO;
- #ifdef CONFIG_SDFAT_CHECK_RO_ATTR
- if (fsi->fs_func->get_entry_attr(ep) & ATTR_READONLY)
- return -EPERM;
- #endif
- /* check whether new dir is existing directory and empty */
- if (new_inode) {
- ret = -EIO;
- new_fid = &SDFAT_I(new_inode)->fid;
- if (new_fid->dir.dir == DIR_DELETED) {
- EMSG("%s : abnormal access to deleted target dentry\n", __func__);
- goto out;
- }
- /* patch 1.2.4 :
- * the problem that FILE_ID_T caches wrong parent info.
- *
- * FIXME : is needed?
- */
- update_parent_info(new_fid, new_parent_inode);
- p_dir = &(new_fid->dir);
- new_entry = new_fid->entry;
- ep = get_dentry_in_dir(sb, p_dir, new_entry, NULL);
- if (!ep)
- goto out;
- new_entry_type = fsi->fs_func->get_entry_type(ep);
- /* if new_inode exists, update fid */
- new_fid->size = i_size_read(new_inode);
- if (new_entry_type == TYPE_DIR) {
- CHAIN_T new_clu;
- new_clu.dir = new_fid->start_clu;
- new_clu.size = ((new_fid->size-1) >> fsi->cluster_size_bits) + 1;
- new_clu.flags = new_fid->flags;
- ret = check_dir_empty(sb, &new_clu);
- if (ret)
- return ret;
- }
- }
- /* check the validity of directory name in the given new pathname */
- ret = resolve_path(new_parent_inode, new_path, &newdir, &uni_name);
- if (ret)
- return ret;
- fs_set_vol_flags(sb, VOL_DIRTY);
- if (olddir.dir == newdir.dir)
- ret = rename_file(new_parent_inode, &olddir, dentry, &uni_name, fid);
- else
- ret = move_file(new_parent_inode, &olddir, dentry, &newdir, &uni_name, fid);
- if ((!ret) && new_inode) {
- /* delete entries of new_dir */
- ep = get_dentry_in_dir(sb, p_dir, new_entry, NULL);
- if (!ep) {
- ret = -EIO;
- goto del_out;
- }
- num_entries = fsi->fs_func->count_ext_entries(sb, p_dir, new_entry, ep);
- if (num_entries < 0) {
- ret = -EIO;
- goto del_out;
- }
- if (fsi->fs_func->delete_dir_entry(sb, p_dir, new_entry, 0, num_entries+1)) {
- ret = -EIO;
- goto del_out;
- }
- /* Free the clusters if new_inode is a dir(as if fscore_rmdir) */
- if (new_entry_type == TYPE_DIR) {
- /* new_fid, new_clu_to_free */
- CHAIN_T new_clu_to_free;
- new_clu_to_free.dir = new_fid->start_clu;
- new_clu_to_free.size = ((new_fid->size-1) >> fsi->cluster_size_bits) + 1;
- new_clu_to_free.flags = new_fid->flags;
- if (fsi->fs_func->free_cluster(sb, &new_clu_to_free, 1)) {
- /* just set I/O error only */
- ret = -EIO;
- }
- new_fid->size = 0;
- new_fid->start_clu = CLUS_EOF;
- new_fid->flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01;
- }
- del_out:
- /* Update new_inode fid
- * Prevent syncing removed new_inode
- * (new_fid is already initialized above code ("if (new_inode)")
- */
- new_fid->dir.dir = DIR_DELETED;
- }
- out:
- fs_sync(sb, 0);
- fs_set_vol_flags(sb, VOL_CLEAN);
- return ret;
- } /* end of fscore_rename */
- /* remove a file */
- s32 fscore_remove(struct inode *inode, FILE_ID_T *fid)
- {
- s32 ret;
- s32 dentry;
- CHAIN_T dir, clu_to_free;
- DENTRY_T *ep;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- dir.dir = fid->dir.dir;
- dir.size = fid->dir.size;
- dir.flags = fid->dir.flags;
- dentry = fid->entry;
- if (fid->dir.dir == DIR_DELETED) {
- EMSG("%s : abnormal access to deleted dentry\n", __func__);
- return -ENOENT;
- }
- ep = get_dentry_in_dir(sb, &dir, dentry, NULL);
- if (!ep)
- return -EIO;
- #ifdef CONFIG_SDFAT_CHECK_RO_ATTR
- if (fsi->fs_func->get_entry_attr(ep) & ATTR_READONLY)
- return -EPERM;
- #endif
- fs_set_vol_flags(sb, VOL_DIRTY);
- /* (1) update the directory entry */
- ret = remove_file(inode, &dir, dentry);
- if (ret)
- goto out;
- clu_to_free.dir = fid->start_clu;
- clu_to_free.size = ((fid->size-1) >> fsi->cluster_size_bits) + 1;
- clu_to_free.flags = fid->flags;
- /* (2) invalidate extent cache and free the clusters
- */
- /* clear extent cache */
- extent_cache_inval_inode(inode);
- ret = fsi->fs_func->free_cluster(sb, &clu_to_free, 0);
- /* WARN : DO NOT RETURN ERROR IN HERE */
- /* (3) update FILE_ID_T */
- fid->size = 0;
- fid->start_clu = CLUS_EOF;
- fid->flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01;
- fid->dir.dir = DIR_DELETED;
- fs_sync(sb, 0);
- fs_set_vol_flags(sb, VOL_CLEAN);
- out:
- return ret;
- } /* end of fscore_remove */
- /*
- * Get the information of a given file
- * REMARK : This function does not need any file name on linux
- *
- * info.Size means the value saved on disk.
- * But root directory doesn`t have real dentry,
- * so the size of root directory returns calculated one exceptively.
- */
- s32 fscore_read_inode(struct inode *inode, DIR_ENTRY_T *info)
- {
- u64 sector;
- s32 count;
- CHAIN_T dir;
- TIMESTAMP_T tm;
- DENTRY_T *ep, *ep2;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- FILE_ID_T *fid = &(SDFAT_I(inode)->fid);
- ENTRY_SET_CACHE_T *es = NULL;
- u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0;
- TMSG("%s entered\n", __func__);
- extent_cache_init_inode(inode);
- /* if root directory */
- if (is_dir && (fid->dir.dir == fsi->root_dir) && (fid->entry == -1)) {
- info->Attr = ATTR_SUBDIR;
- memset((s8 *) &info->CreateTimestamp, 0, sizeof(DATE_TIME_T));
- memset((s8 *) &info->ModifyTimestamp, 0, sizeof(DATE_TIME_T));
- memset((s8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T));
- //strcpy(info->NameBuf.sfn, ".");
- //strcpy(info->NameBuf.lfn, ".");
- dir.dir = fsi->root_dir;
- dir.flags = 0x01;
- dir.size = 0; /* UNUSED */
- /* FAT16 root_dir */
- if (IS_CLUS_FREE(fsi->root_dir)) {
- info->Size = fsi->dentries_in_root << DENTRY_SIZE_BITS;
- } else {
- u32 num_clu;
- if (__count_num_clusters(sb, &dir, &num_clu))
- return -EIO;
- info->Size = (u64)num_clu << fsi->cluster_size_bits;
- }
- count = __count_dos_name_entries(sb, &dir, TYPE_DIR, NULL);
- if (count < 0)
- return -EIO;
- info->NumSubdirs = count;
- return 0;
- }
- /* get the directory entry of given file or directory */
- if (fsi->vol_type == EXFAT) {
- /* es should be released */
- es = get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_2_ENTRIES, &ep);
- if (!es)
- return -EIO;
- ep2 = ep+1;
- } else {
- ep = get_dentry_in_dir(sb, &(fid->dir), fid->entry, §or);
- if (!ep)
- return -EIO;
- ep2 = ep;
- /* dcache should be unlocked */
- dcache_lock(sb, sector);
- }
- /* set FILE_INFO structure using the acquired DENTRY_T */
- info->Attr = fsi->fs_func->get_entry_attr(ep);
- fsi->fs_func->get_entry_time(ep, &tm, TM_CREATE);
- info->CreateTimestamp.Year = tm.year;
- info->CreateTimestamp.Month = tm.mon;
- info->CreateTimestamp.Day = tm.day;
- info->CreateTimestamp.Hour = tm.hour;
- info->CreateTimestamp.Minute = tm.min;
- info->CreateTimestamp.Second = tm.sec;
- info->CreateTimestamp.MilliSecond = 0;
- info->CreateTimestamp.Timezone.value = tm.tz.value;
- fsi->fs_func->get_entry_time(ep, &tm, TM_MODIFY);
- info->ModifyTimestamp.Year = tm.year;
- info->ModifyTimestamp.Month = tm.mon;
- info->ModifyTimestamp.Day = tm.day;
- info->ModifyTimestamp.Hour = tm.hour;
- info->ModifyTimestamp.Minute = tm.min;
- info->ModifyTimestamp.Second = tm.sec;
- info->ModifyTimestamp.MilliSecond = 0;
- info->ModifyTimestamp.Timezone.value = tm.tz.value;
- memset((s8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T));
- info->NumSubdirs = 0;
- info->Size = fsi->fs_func->get_entry_size(ep2);
- if (fsi->vol_type == EXFAT)
- release_dentry_set(es);
- else
- dcache_unlock(sb, sector);
- if (is_dir) {
- u32 dotcnt = 0;
- dir.dir = fid->start_clu;
- dir.flags = fid->flags;
- dir.size = fid->size >> fsi->cluster_size_bits;
- /*
- * NOTE :
- * If "dir.flags" has 0x01, "dir.size" is meaningless.
- */
- #if 0
- if (info->Size == 0) {
- s32 num_clu;
- if (__count_num_clusters(sb, &dir, &num_clu))
- return -EIO;
- info->Size = (u64)num_clu << fsi->cluster_size_bits;
- }
- #endif
- count = __count_dos_name_entries(sb, &dir, TYPE_DIR, &dotcnt);
- if (count < 0)
- return -EIO;
- if (fsi->vol_type == EXFAT) {
- count += SDFAT_MIN_SUBDIR;
- } else {
- /*
- * if directory has been corrupted,
- * we have to adjust subdir count.
- */
- BUG_ON(dotcnt > SDFAT_MIN_SUBDIR);
- if (dotcnt < SDFAT_MIN_SUBDIR) {
- EMSG("%s: contents of the directory has been "
- "corrupted (parent clus : %08x, idx : %d)",
- __func__, fid->dir.dir, fid->entry);
- }
- count += (SDFAT_MIN_SUBDIR - dotcnt);
- }
- info->NumSubdirs = count;
- }
- TMSG("%s exited successfully\n", __func__);
- return 0;
- } /* end of fscore_read_inode */
- /* set the information of a given file
- * REMARK : This function does not need any file name on linux
- */
- s32 fscore_write_inode(struct inode *inode, DIR_ENTRY_T *info, s32 sync)
- {
- s32 ret = -EIO;
- u64 sector;
- TIMESTAMP_T tm;
- DENTRY_T *ep, *ep2;
- ENTRY_SET_CACHE_T *es = NULL;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- FILE_ID_T *fid = &(SDFAT_I(inode)->fid);
- u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0;
- /* SKIP WRITING INODE :
- * if the indoe is already unlinked,
- * there is no need for updating inode
- */
- if (fid->dir.dir == DIR_DELETED)
- return 0;
- if (is_dir && (fid->dir.dir == fsi->root_dir) && (fid->entry == -1))
- return 0;
- fs_set_vol_flags(sb, VOL_DIRTY);
- /* get the directory entry of given file or directory */
- if (fsi->vol_type == EXFAT) {
- es = get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep);
- if (!es)
- return -EIO;
- ep2 = ep+1;
- } else {
- /* for other than exfat */
- ep = get_dentry_in_dir(sb, &(fid->dir), fid->entry, §or);
- if (!ep)
- return -EIO;
- ep2 = ep;
- }
- fsi->fs_func->set_entry_attr(ep, info->Attr);
- /* set FILE_INFO structure using the acquired DENTRY_T */
- tm.tz = info->CreateTimestamp.Timezone;
- tm.sec = info->CreateTimestamp.Second;
- tm.min = info->CreateTimestamp.Minute;
- tm.hour = info->CreateTimestamp.Hour;
- tm.day = info->CreateTimestamp.Day;
- tm.mon = info->CreateTimestamp.Month;
- tm.year = info->CreateTimestamp.Year;
- fsi->fs_func->set_entry_time(ep, &tm, TM_CREATE);
- tm.tz = info->ModifyTimestamp.Timezone;
- tm.sec = info->ModifyTimestamp.Second;
- tm.min = info->ModifyTimestamp.Minute;
- tm.hour = info->ModifyTimestamp.Hour;
- tm.day = info->ModifyTimestamp.Day;
- tm.mon = info->ModifyTimestamp.Month;
- tm.year = info->ModifyTimestamp.Year;
- fsi->fs_func->set_entry_time(ep, &tm, TM_MODIFY);
- if (is_dir && fsi->vol_type != EXFAT) {
- /* overwirte dirsize if FAT32 and dir size != 0 */
- if (fsi->fs_func->get_entry_size(ep2))
- fsi->fs_func->set_entry_size(ep2, 0);
- } else {
- /* File size should be zero if there is no cluster allocated */
- u64 on_disk_size = info->Size;
- if (IS_CLUS_EOF(fid->start_clu))
- on_disk_size = 0;
- fsi->fs_func->set_entry_size(ep2, on_disk_size);
- }
- if (fsi->vol_type == EXFAT) {
- ret = update_dir_chksum_with_entry_set(sb, es);
- release_dentry_set(es);
- } else {
- ret = dcache_modify(sb, sector);
- }
- fs_sync(sb, sync);
- /* Comment below code to prevent super block update frequently */
- //fs_set_vol_flags(sb, VOL_CLEAN);
- return ret;
- } /* end of fscore_write_inode */
- /*
- * Input: inode, (logical) clu_offset, target allocation area
- * Output: errcode, cluster number
- * *clu = (~0), if it's unable to allocate a new cluster
- */
- s32 fscore_map_clus(struct inode *inode, u32 clu_offset, u32 *clu, int dest)
- {
- s32 ret, modified = false;
- u32 last_clu;
- u64 sector;
- CHAIN_T new_clu;
- DENTRY_T *ep;
- ENTRY_SET_CACHE_T *es = NULL;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- FILE_ID_T *fid = &(SDFAT_I(inode)->fid);
- u32 local_clu_offset = clu_offset;
- s32 reserved_clusters = fsi->reserved_clusters;
- u32 num_to_be_allocated = 0, num_clusters = 0;
- fid->rwoffset = (s64)(clu_offset) << fsi->cluster_size_bits;
- if (SDFAT_I(inode)->i_size_ondisk > 0)
- num_clusters = (u32)((SDFAT_I(inode)->i_size_ondisk-1) >> fsi->cluster_size_bits) + 1;
- if (clu_offset >= num_clusters)
- num_to_be_allocated = clu_offset - num_clusters + 1;
- if ((dest == ALLOC_NOWHERE) && (num_to_be_allocated > 0)) {
- *clu = CLUS_EOF;
- return 0;
- }
- /* check always request cluster is 1 */
- //ASSERT(num_to_be_allocated == 1);
- sdfat_debug_check_clusters(inode);
- *clu = last_clu = fid->start_clu;
- /* XXX: Defensive code needed.
- * what if i_size_ondisk != # of allocated clusters
- */
- if (fid->flags == 0x03) {
- if ((clu_offset > 0) && (!IS_CLUS_EOF(*clu))) {
- last_clu += clu_offset - 1;
- if (clu_offset == num_clusters)
- *clu = CLUS_EOF;
- else
- *clu += clu_offset;
- }
- } else if (fid->type == TYPE_FILE) {
- u32 fclus = 0;
- s32 err = extent_get_clus(inode, clu_offset,
- &fclus, clu, &last_clu, 1);
- if (err)
- return -EIO;
- clu_offset -= fclus;
- } else {
- /* hint information */
- if ((clu_offset > 0) &&
- ((fid->hint_bmap.off != CLUS_EOF) && (fid->hint_bmap.off > 0)) &&
- (clu_offset >= fid->hint_bmap.off)) {
- clu_offset -= fid->hint_bmap.off;
- /* hint_bmap.clu should be valid */
- ASSERT(fid->hint_bmap.clu >= 2);
- *clu = fid->hint_bmap.clu;
- }
- while ((clu_offset > 0) && (!IS_CLUS_EOF(*clu))) {
- last_clu = *clu;
- if (get_next_clus_safe(sb, clu))
- return -EIO;
- clu_offset--;
- }
- }
- if (IS_CLUS_EOF(*clu)) {
- fs_set_vol_flags(sb, VOL_DIRTY);
- new_clu.dir = (IS_CLUS_EOF(last_clu)) ? CLUS_EOF : last_clu + 1;
- new_clu.size = 0;
- new_clu.flags = fid->flags;
- /* (1) allocate a cluster */
- if (num_to_be_allocated < 1) {
- /* Broken FAT (i_sze > allocated FAT) */
- EMSG("%s: invalid fat chain : inode(%p) "
- "num_to_be_allocated(%d) "
- "i_size_ondisk(%lld) fid->flags(%02x) "
- "fid->start(%08x) fid->hint_off(%u) "
- "fid->hint_clu(%u) fid->rwoffset(%llu) "
- "modified_clu_off(%d) last_clu(%08x) "
- "new_clu(%08x)", __func__, inode,
- num_to_be_allocated,
- (SDFAT_I(inode)->i_size_ondisk),
- fid->flags, fid->start_clu,
- fid->hint_bmap.off, fid->hint_bmap.clu,
- fid->rwoffset, clu_offset,
- last_clu, new_clu.dir);
- sdfat_fs_error(sb, "broken FAT chain.");
- return -EIO;
- }
- ret = fsi->fs_func->alloc_cluster(sb, num_to_be_allocated, &new_clu, ALLOC_COLD);
- if (ret)
- return ret;
- if (IS_CLUS_EOF(new_clu.dir) || IS_CLUS_FREE(new_clu.dir)) {
- sdfat_fs_error(sb, "bogus cluster new allocated"
- "(last_clu : %u, new_clu : %u)",
- last_clu, new_clu.dir);
- ASSERT(0);
- return -EIO;
- }
- /* Reserved cluster dec. */
- // XXX: Inode DA flag needed
- if (SDFAT_SB(sb)->options.improved_allocation & SDFAT_ALLOC_DELAY) {
- BUG_ON(reserved_clusters < num_to_be_allocated);
- reserved_clusters -= num_to_be_allocated;
- }
- /* (2) append to the FAT chain */
- if (IS_CLUS_EOF(last_clu)) {
- if (new_clu.flags == 0x01)
- fid->flags = 0x01;
- fid->start_clu = new_clu.dir;
- modified = true;
- } else {
- if (new_clu.flags != fid->flags) {
- /* no-fat-chain bit is disabled,
- * so fat-chain should be synced with alloc-bmp
- */
- chain_cont_cluster(sb, fid->start_clu, num_clusters);
- fid->flags = 0x01;
- modified = true;
- }
- if (new_clu.flags == 0x01)
- if (fat_ent_set(sb, last_clu, new_clu.dir))
- return -EIO;
- }
- num_clusters += num_to_be_allocated;
- *clu = new_clu.dir;
- if (fid->dir.dir != DIR_DELETED) {
- if (fsi->vol_type == EXFAT) {
- es = get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep);
- if (!es)
- return -EIO;
- /* get stream entry */
- ep++;
- }
- /* (3) update directory entry */
- if (modified) {
- if (fsi->vol_type != EXFAT) {
- ep = get_dentry_in_dir(sb, &(fid->dir), fid->entry, §or);
- if (!ep)
- return -EIO;
- }
- if (fsi->fs_func->get_entry_flag(ep) != fid->flags)
- fsi->fs_func->set_entry_flag(ep, fid->flags);
- if (fsi->fs_func->get_entry_clu0(ep) != fid->start_clu)
- fsi->fs_func->set_entry_clu0(ep, fid->start_clu);
- fsi->fs_func->set_entry_size(ep, fid->size);
- if (fsi->vol_type != EXFAT) {
- if (dcache_modify(sb, sector))
- return -EIO;
- }
- }
- if (fsi->vol_type == EXFAT) {
- if (update_dir_chksum_with_entry_set(sb, es))
- return -EIO;
- release_dentry_set(es);
- }
- } /* end of if != DIR_DELETED */
- /* add number of new blocks to inode (non-DA only) */
- if (!(SDFAT_SB(sb)->options.improved_allocation & SDFAT_ALLOC_DELAY)) {
- inode->i_blocks += num_to_be_allocated << (fsi->cluster_size_bits - sb->s_blocksize_bits);
- } else {
- // DA의 경우, i_blocks가 이미 증가해있어야 함.
- BUG_ON(clu_offset >= (inode->i_blocks >> (fsi->cluster_size_bits - sb->s_blocksize_bits)));
- }
- #if 0
- fs_sync(sb, 0);
- fs_set_vol_flags(sb, VOL_CLEAN);
- #endif
- /* (4) Move *clu pointer along FAT chains (hole care)
- * because the caller of this function expect *clu to be the last cluster.
- * This only works when num_to_be_allocated >= 2,
- * *clu = (the first cluster of the allocated chain) => (the last cluster of ...)
- */
- if (fid->flags == 0x03) {
- *clu += num_to_be_allocated - 1;
- } else {
- while (num_to_be_allocated > 1) {
- if (get_next_clus_safe(sb, clu))
- return -EIO;
- num_to_be_allocated--;
- }
- }
- }
- /* update reserved_clusters */
- fsi->reserved_clusters = reserved_clusters;
- /* hint information */
- fid->hint_bmap.off = local_clu_offset;
- fid->hint_bmap.clu = *clu;
- return 0;
- } /* end of fscore_map_clus */
- /* allocate reserved cluster */
- s32 fscore_reserve_clus(struct inode *inode)
- {
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if ((fsi->used_clusters + fsi->reserved_clusters) >= (fsi->num_clusters - 2))
- return -ENOSPC;
- if (bdev_check_bdi_valid(sb))
- return -EIO;
- fsi->reserved_clusters++;
- /* inode->i_blocks update */
- inode->i_blocks += 1 << (fsi->cluster_size_bits - sb->s_blocksize_bits);
- sdfat_debug_check_clusters(inode);
- return 0;
- }
- /* remove an entry, BUT don't truncate */
- s32 fscore_unlink(struct inode *inode, FILE_ID_T *fid)
- {
- s32 dentry;
- CHAIN_T dir;
- DENTRY_T *ep;
- struct super_block *sb = inode->i_sb;
- dir.dir = fid->dir.dir;
- dir.size = fid->dir.size;
- dir.flags = fid->dir.flags;
- dentry = fid->entry;
- if (fid->dir.dir == DIR_DELETED) {
- EMSG("%s : abnormal access to deleted dentry\n", __func__);
- return -ENOENT;
- }
- ep = get_dentry_in_dir(sb, &dir, dentry, NULL);
- if (!ep)
- return -EIO;
- #ifdef CONFIG_SDFAT_CHECK_RO_ATTR
- if (SDFAT_SB(sb)->fsi.fs_func->get_entry_attr(ep) & ATTR_READONLY)
- return -EPERM;
- #endif
- fs_set_vol_flags(sb, VOL_DIRTY);
- /* (1) update the directory entry */
- if (remove_file(inode, &dir, dentry))
- return -EIO;
- /* This doesn't modify fid */
- fid->dir.dir = DIR_DELETED;
- fs_sync(sb, 0);
- fs_set_vol_flags(sb, VOL_CLEAN);
- return 0;
- }
- /*----------------------------------------------------------------------*/
- /* Directory Operation Functions */
- /*----------------------------------------------------------------------*/
- /* create a directory */
- s32 fscore_mkdir(struct inode *inode, u8 *path, FILE_ID_T *fid)
- {
- s32 ret/*, dentry*/;
- CHAIN_T dir;
- UNI_NAME_T uni_name;
- struct super_block *sb = inode->i_sb;
- TMSG("%s entered\n", __func__);
- /* check the validity of directory name in the given old pathname */
- ret = resolve_path(inode, path, &dir, &uni_name);
- if (ret)
- goto out;
- fs_set_vol_flags(sb, VOL_DIRTY);
- ret = create_dir(inode, &dir, &uni_name, fid);
- fs_sync(sb, 0);
- fs_set_vol_flags(sb, VOL_CLEAN);
- out:
- TMSG("%s exited with err(%d)\n", __func__, ret);
- return ret;
- }
- /* read a directory entry from the opened directory */
- s32 fscore_readdir(struct inode *inode, DIR_ENTRY_T *dir_entry)
- {
- s32 i;
- s32 dentries_per_clu, dentries_per_clu_bits = 0;
- u32 type, clu_offset;
- u64 sector;
- CHAIN_T dir, clu;
- UNI_NAME_T uni_name;
- TIMESTAMP_T tm;
- DENTRY_T *ep;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- FILE_ID_T *fid = &(SDFAT_I(inode)->fid);
- u32 dentry = (u32)(fid->rwoffset & 0xFFFFFFFF); /* u32 is enough for directory */
- /* check if the given file ID is opened */
- if (fid->type != TYPE_DIR)
- return -EPERM;
- if (fid->entry == -1) {
- dir.dir = fsi->root_dir;
- dir.size = 0; /* just initialize, but will not use */
- dir.flags = 0x01;
- } else {
- dir.dir = fid->start_clu;
- dir.size = fid->size >> fsi->cluster_size_bits;
- dir.flags = fid->flags;
- sdfat_debug_bug_on(dentry >= (dir.size * fsi->dentries_per_clu));
- }
- if (IS_CLUS_FREE(dir.dir)) { /* FAT16 root_dir */
- dentries_per_clu = fsi->dentries_in_root;
- /* Prevent readdir over directory size */
- if (dentry >= dentries_per_clu) {
- clu.dir = CLUS_EOF;
- } else {
- clu.dir = dir.dir;
- clu.size = dir.size;
- clu.flags = dir.flags;
- }
- } else {
- dentries_per_clu = fsi->dentries_per_clu;
- dentries_per_clu_bits = ilog2(dentries_per_clu);
- clu_offset = dentry >> dentries_per_clu_bits;
- clu.dir = dir.dir;
- clu.size = dir.size;
- clu.flags = dir.flags;
- if (clu.flags == 0x03) {
- clu.dir += clu_offset;
- clu.size -= clu_offset;
- } else {
- /* hint_information */
- if ((clu_offset > 0) &&
- ((fid->hint_bmap.off != CLUS_EOF) && (fid->hint_bmap.off > 0)) &&
- (clu_offset >= fid->hint_bmap.off)) {
- clu_offset -= fid->hint_bmap.off;
- clu.dir = fid->hint_bmap.clu;
- }
- while (clu_offset > 0) {
- if (get_next_clus_safe(sb, &(clu.dir)))
- return -EIO;
- clu_offset--;
- }
- }
- }
- while (!IS_CLUS_EOF(clu.dir)) {
- if (IS_CLUS_FREE(dir.dir)) /* FAT16 root_dir */
- i = dentry % dentries_per_clu;
- else
- i = dentry & (dentries_per_clu-1);
- for ( ; i < dentries_per_clu; i++, dentry++) {
- ep = get_dentry_in_dir(sb, &clu, i, §or);
- if (!ep)
- return -EIO;
- type = fsi->fs_func->get_entry_type(ep);
- if (type == TYPE_UNUSED)
- break;
- if ((type != TYPE_FILE) && (type != TYPE_DIR))
- continue;
- dcache_lock(sb, sector);
- dir_entry->Attr = fsi->fs_func->get_entry_attr(ep);
- fsi->fs_func->get_entry_time(ep, &tm, TM_CREATE);
- dir_entry->CreateTimestamp.Year = tm.year;
- dir_entry->CreateTimestamp.Month = tm.mon;
- dir_entry->CreateTimestamp.Day = tm.day;
- dir_entry->CreateTimestamp.Hour = tm.hour;
- dir_entry->CreateTimestamp.Minute = tm.min;
- dir_entry->CreateTimestamp.Second = tm.sec;
- dir_entry->CreateTimestamp.MilliSecond = 0;
- fsi->fs_func->get_entry_time(ep, &tm, TM_MODIFY);
- dir_entry->ModifyTimestamp.Year = tm.year;
- dir_entry->ModifyTimestamp.Month = tm.mon;
- dir_entry->ModifyTimestamp.Day = tm.day;
- dir_entry->ModifyTimestamp.Hour = tm.hour;
- dir_entry->ModifyTimestamp.Minute = tm.min;
- dir_entry->ModifyTimestamp.Second = tm.sec;
- dir_entry->ModifyTimestamp.MilliSecond = 0;
- memset((s8 *) &dir_entry->AccessTimestamp, 0, sizeof(DATE_TIME_T));
- *(uni_name.name) = 0x0;
- fsi->fs_func->get_uniname_from_ext_entry(sb, &dir, dentry, uni_name.name);
- if (*(uni_name.name) == 0x0)
- get_uniname_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1);
- nls_uni16s_to_vfsname(sb, &uni_name,
- dir_entry->NameBuf.lfn,
- dir_entry->NameBuf.lfnbuf_len);
- dcache_unlock(sb, sector);
- if (fsi->vol_type == EXFAT) {
- ep = get_dentry_in_dir(sb, &clu, i+1, NULL);
- if (!ep)
- return -EIO;
- } else {
- get_uniname_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0);
- nls_uni16s_to_vfsname(sb, &uni_name,
- dir_entry->NameBuf.sfn,
- dir_entry->NameBuf.sfnbuf_len);
- }
- dir_entry->Size = fsi->fs_func->get_entry_size(ep);
- /*
- * Update hint information :
- * fat16 root directory does not need it.
- */
- if (!IS_CLUS_FREE(dir.dir)) {
- fid->hint_bmap.off = dentry >> dentries_per_clu_bits;
- fid->hint_bmap.clu = clu.dir;
- }
- fid->rwoffset = (s64) ++dentry;
- return 0;
- }
- /* fat16 root directory */
- if (IS_CLUS_FREE(dir.dir))
- break;
- if (clu.flags == 0x03) {
- if ((--clu.size) > 0)
- clu.dir++;
- else
- clu.dir = CLUS_EOF;
- } else {
- if (get_next_clus_safe(sb, &(clu.dir)))
- return -EIO;
- }
- }
- dir_entry->NameBuf.lfn[0] = '\0';
- fid->rwoffset = (s64)dentry;
- return 0;
- } /* end of fscore_readdir */
- /* remove a directory */
- s32 fscore_rmdir(struct inode *inode, FILE_ID_T *fid)
- {
- s32 ret;
- s32 dentry;
- DENTRY_T *ep;
- CHAIN_T dir, clu_to_free;
- struct super_block *sb = inode->i_sb;
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- dir.dir = fid->dir.dir;
- dir.size = fid->dir.size;
- dir.flags = fid->dir.flags;
- dentry = fid->entry;
- if (fid->dir.dir == DIR_DELETED) {
- EMSG("%s : abnormal access to deleted dentry\n", __func__);
- return -ENOENT;
- }
- /* check if the file is "." or ".." */
- if (fsi->vol_type != EXFAT) {
- if ((dir.dir != fsi->root_dir) && (dentry < 2))
- return -EPERM;
- }
- ep = get_dentry_in_dir(sb, &dir, dentry, NULL);
- if (!ep)
- return -EIO;
- #ifdef CONFIG_SDFAT_CHECK_RO_ATTR
- if (SDFAT_SB(sb)->fsi.fs_func->get_entry_attr(ep) & ATTR_READONLY)
- return -EPERM;
- #endif
- clu_to_free.dir = fid->start_clu;
- clu_to_free.size = ((fid->size-1) >> fsi->cluster_size_bits) + 1;
- clu_to_free.flags = fid->flags;
- ret = check_dir_empty(sb, &clu_to_free);
- if (ret) {
- if (ret == -EIO)
- EMSG("%s : failed to check_dir_empty : err(%d)\n",
- __func__, ret);
- return ret;
- }
- fs_set_vol_flags(sb, VOL_DIRTY);
- /* (1) update the directory entry */
- ret = remove_file(inode, &dir, dentry);
- if (ret) {
- EMSG("%s : failed to remove_file : err(%d)\n", __func__, ret);
- return ret;
- }
- fid->dir.dir = DIR_DELETED;
- fs_sync(sb, 0);
- fs_set_vol_flags(sb, VOL_CLEAN);
- return ret;
- } /* end of fscore_rmdir */
- /* end of core.c */
|