From 2364f6ee76abf480278ab12ff51cae2ddbd02482 Mon Sep 17 00:00:00 2001 From: "leon.chen" Date: Wed, 6 May 2026 11:41:00 +0800 Subject: [PATCH] [fs][fat] fix false EOF error on cluster-aligned reads When a file read reaches the end of a cluster and the total read length is exactly aligned with the cluster boundary, the iterator speculatively fetches the next sector. This causes the FAT driver to hit the EOF marker in the FAT table, returning ERR_OUT_OF_RANGE. This patch adds a check to skip the next sector fetch if the target length has already been met. Tested with a 4KB/8KB file on a FAT12/FAT16/FAT32 image for typical cluster sizes (e.g., 1KB, 2KB, 4KB), ensuring that the cluster boundary condition is triggered regardless of the specific FAT type. --- lib/fs/fat/file.cpp | 8 +++++--- lib/fs/fat/test/mkblk | 2 ++ lib/fs/fat/test/test.cpp | 4 ++++ lib/fs/fat/test/test_4kb.bin | Bin 0 -> 4096 bytes lib/fs/fat/test/test_8kb.bin | Bin 0 -> 8192 bytes 5 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 lib/fs/fat/test/test_4kb.bin create mode 100644 lib/fs/fat/test/test_8kb.bin diff --git a/lib/fs/fat/file.cpp b/lib/fs/fat/file.cpp index 6ccc688f02..86167c5938 100644 --- a/lib/fs/fat/file.cpp +++ b/lib/fs/fat/file.cpp @@ -194,9 +194,11 @@ ssize_t fat_file::read_file_priv(void *_buf, const off_t offset, size_t len) { if (offset_within_sector == fs_->info().bytes_per_sector) { offset_within_sector = 0; // push the iterator forward to next sector - err = fbi.next_sector(); - if (err < 0) { - return err; + if (buf_offset < len) { + err = fbi.next_sector(); + if (err < 0) { + return err; + } } } } diff --git a/lib/fs/fat/test/mkblk b/lib/fs/fat/test/mkblk index 01c434af68..8c02889357 100755 --- a/lib/fs/fat/test/mkblk +++ b/lib/fs/fat/test/mkblk @@ -31,6 +31,8 @@ for i in blk.bin.*; do mmd -i $i dir.c mcopy -i $i hello.txt ::dir.a/long_filename_hello.txt mcopy -i $i largefile ::largefile + mcopy -i $i test_4kb.bin ::test_4kb.bin + mcopy -i $i test_8kb.bin ::test_8kb.bin # add a bunch of entries to the root dir that cause it # to spill over to a few extra sectors diff --git a/lib/fs/fat/test/test.cpp b/lib/fs/fat/test/test.cpp index dd9315b63b..043ea084d4 100644 --- a/lib/fs/fat/test/test.cpp +++ b/lib/fs/fat/test/test.cpp @@ -26,6 +26,8 @@ // pull in a few test files into rodata to test against INCFILE(test_file_hello, test_file_hello_size, LOCAL_DIR "/hello.txt"); INCFILE(test_file_license, test_file_license_size, LOCAL_DIR "/LICENSE"); +INCFILE(test_file_4kb, test_file_4kb_size, LOCAL_DIR "/test_4kb.bin"); +INCFILE(test_file_8kb, test_file_8kb_size, LOCAL_DIR "/test_8kb.bin"); namespace { @@ -171,6 +173,8 @@ bool test_fat_read_file() { EXPECT_TRUE(test_file_read(test_path "/long_filename_hello.txt", test_file_hello, test_file_hello_size)); EXPECT_TRUE(test_file_read(test_path "/a_very_long_filename_hello_that_uses_at_least_a_few_entries.txt", test_file_hello, test_file_hello_size)); EXPECT_TRUE(test_file_read(test_path "/dir.a/long_filename_hello.txt", test_file_hello, test_file_hello_size)); + EXPECT_TRUE(test_file_read(test_path "/test_4kb.bin", test_file_4kb, test_file_4kb_size)); + EXPECT_TRUE(test_file_read(test_path "/test_8kb.bin", test_file_8kb, test_file_8kb_size)); // unmount the fs unmount_cleanup.cancel(); diff --git a/lib/fs/fat/test/test_4kb.bin b/lib/fs/fat/test/test_4kb.bin new file mode 100644 index 0000000000000000000000000000000000000000..02a0169ef88357f6a1552d25f4ed172aeb0a2338 GIT binary patch literal 4096 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 ynEAK-;B_YETxlK^GGFZ zChI;2`8)!w^`B*Qatgr#fU$t{28!{d^w-;%DJ4MJfx^pIy=VDjfjpUH1K7xqj|NXl zfiYUM*xBZP9!J+27qs2NOZZV=`(XO0rXwAA&{uc)g^Anq_35AT>=alDICFVt0$fzA zIYYMbwhZZuDzl4^T?+jkMkHC{k_2nu+xpj*TH14Knx8cYEOJqOIlAmRS=66L308F$ z1(SRf#&?&&iT{1!T8>l9=gwhux$OTmSP_Saw3#qy|glB)c^ zTG~s|Xu|O!hXGXc@$D#11|bfAd(Oi=Q^90-&p(2PbabG8h@h`{JiU!2$?=tCMk`a@ z`F(KC4EgXcBFE(o4&g|8(Uy5x*guAV*JYtTII`P@*vRj-_kiJ|b4yGuFB)khr-aTe zFL&l+-WHUxYSB8)T*ABQx(B`%>&CVo-uHo=-~B6$Za(Z|*OOw+?5&&prjsSxU6#M{ z_d3|bf1z6Na&_Y*hdskSPA@ubTNrQoZ6=#{1AWP9j7~M;xz6i>Bpl;8rUBHbQGI``Th0NgLz_zazUXNM2T7cqZ0@NKQFhdzuXOuBT^U)%Rwg+4q=CNV`wyVl^Th z>S3!<-B>l+F?3nw=$Ug<#v*LpdoX8WNi)>{R{Poj+wL<_7urex&87oyt}H=@zqrYw zY;0~^oyGS$b&bzw>04>1^K5T1yhZI1<6#}TkRfBTzI#H(&i7GI zNYHq$2lx5|T^I=XZtdyhEMn=*J8+}JU-*tni|t5m+fUwS2CPZF=-*2pY^|F``86-_ ziEkoNDZDD~9fXNv2BHB&OLe#YlbpjRf;SPEQUAD_854@)L)f zN3o9ScuOCn1g$e+x~sQw1mqnZa=RqQ_(63FA7v9I?=|T^xgU$dq(a%_nE)kV-81pghl}LLf{rOtSW0zRq`K}K^#39S5H1iB)bmL zps2c~S@C?C-HPPh+)>qFz8`Oo^%64#lXF+wVZe=JfaC;M#2o9?;!H0}(tA_9cdk}3 zz=#XYuu~E61QtafQ3i9$heEhIwxWEF81Yk|IKsk53p;%Uevou+J*Gulvw zH~+MtL8*qqH3!8;-5ihAtaN#1qn9R#_6lkumqmAxM z)3vW8;d%z}4q@=}vx>^k?^u?Ex`oYYKfJ7^1&%6Bu~`dgrkyXz1l}&I_#)MI;KNhc znWWx^wqSj5jl)XunMJCi^aec~F@Ch8eH1qe`z~sEji9)`mk!cp-mF!1V14DzR=L_7 zkpanPOo0BFk5m%@0dG(ZsH#j5t=*s}O$Or_Mv>1ys9wAIgoe6g3O7G4>MJ#rxY*>` zk|6VxvCV*SH+{0LIC?X^SFn!<0#RU!;^E4=H zAy%351afCB#p8_XV97j`6N1S>YR#ax_$4}uP%a*gx)#FD4oj}9kxAY0^=PT}sN!Zy z;yMwtG6>%xw<_e18WQhbI5m0<@lqyd8|s2rs?-4jmOCnUoT2*wa6(?=?kJLh`|-+1 zOZrnCi!AWBNO!@Ve<8E3!rwM*WjT|DBqptCq z@1r*@=T5yx_`Jv^Drl02)4pVB`<1<&fx-r)imq>=TW-=(d5Y>7NRzNNKyASNE2rYp zBZ>!D)j-T&9AydV9QtSgV$62e3y_tlyqEn2lPfh3C%ST?TxiwIvkH`o%n$C;;jZrI zLE|aJ4<422H8p+@iza#Z@bQQR1*D6qX&2SPSEIT5-Zt4MruZRMl^8fBAZqt)ADGdG zcrpq6ft2GVAPlp%t>Q(D(V}XRtJjEA!nFxFZ%eMw<9dH<|WB5&@K~S`9JAC4JUwCe^K^MB4O%FT5|Ykur!b z)85^t0^0_waPZkwdYxY}5AlaXX)W_=H=ZXNV|#It8;BAl zOS*x?EHxlA4D!J{G#Wso!wC}-;y^AYoLD?_aw4w!W?0iDQ;D%%fwDX?K08l(i;ga} z8`Hu+t+U<;9t53v|Gpt3%H77Zkt94ip0NwSII6*6ZXV+CO5Y;-`EgFO6nlZkS9ri} z{p{7b?H1q9j#19AS#_d)B3b7s^@4)Pq$28xVT{*B=FS2<6cCsvTHj-v#ZTskoKvMK z*_O!uWoEBx>d2@r=Mmf;ZJGgy0-c9Lx!*olcPxv@wu_ar)Y@lQuxY%MZ`~(3ML%*_ z?8zER3&uLNdH_o@&9|+t0$`{+dn~ar#^M}t83bii9%)LFva-fGYUfuG+T{$A=o2 z4Q0L+%r@r)L$s*OC`6#-qSpYhQ(V1!ZU=^&%rv|X{Pt#_KXB7GvbtaIheP^vuu*`v zwHN4lI0>}p4fwa5b^)}pQj>zchg=WVjI*v60Y1sW*})zIyJ&PoKlqRPp%LyaJJJnt zcpPb`X8W1Fnn1K#%_HH6B|2KZ#qLGdp67{q;17Sz+!P^uRD1nZuvS4O?EjF%IJ!JV z%PusqBiv8;hNL<90!9hw*(A*!YxAk)0b;-|HRVdn3;)ij5My6>-A~zxKi=kE@WeUK zbahdmbl!W4Vrh@!0!PktS{nCQKgK!($og4>y5Aw2Dm2Z8te8rdY<|)9{7qvdBg>nKo3mkQ39|Ujdl{{5nUT8rnso=HDM@ z1i@AQ8j60$5QYe|^=x%_V#ki^rN4hUE;q%d@oF6pFphtP3l`@sN_t=|S(QLCrc8Cg ziE3_p)jgXQyQz26rw`N67&f_})wR`9Biv#&S%vW)8zM+)_%{gO#P)$Uz?QtTEax9P z842tOI++i5ZypwBJ&i0M+>%dTig>k8*B^drLokd8S7#I@fs>9Q|GP%)zmsrBP+Jm{qjS_|Q6g>fq1@BeS2q|D`-c z+E9UfS2y)hAA(Sb0~7HKSq3Gl-QN#pxx0vN<~mef6O-4_U|NRwMOTv^YD79I{3VKg zeAW$oAOksLIgDbG8J9I5e>)+oC zD`R>Qy>oRBALXMbgDU9^lyT)2SS~_}(iCMq{7``Hw9}^=ZlgKjWMDQm)s;k&@dGn` zYT(Ly*$>R&N~MZsBf5-mZL55a(GIk;K-wC3^zB_AX-6Jc`5GMx`Gg&N491)08qG zGfu6yZalR@SWM*mNz5*Xr+E~O)Lj6=+a=HrPfOJuX&41Lhs8XIKd5c|qmHin^9isBE6BPWM;{zV z4_p^eZ5`|o4Y%SRpRr@u;|RRKR{x-F8W-%9={zNyIAmvtP-liE1CwX8k=QNMv<(ou zDyG)^eo(cTggxYzXv8%1Zk}H&!V<>x?xxfuSNm(~Js{b+nW~;o4|M5iH@fqWBoHqR ze&h7RdlVU{FHrlT|tlv-$x#vGUgqDv%`NCZ$Xs7?CX?Db2!00zD zaw0=(Seq&Skjawfyv_3z23E%MjAkOraPy8kmmGE8Te3EfVDhOWrti5#*<7bzPR+YY z_a2crGoq?Cxz`qPCfk8x>kuCn;gBa32~L5=YR(zt@Nx zOx2L6Q2wzIJ4ERy5C#la598a~%9Xjc=!luXTobbL_QM=^cX5bHP*}p>zlo+-Uq*VR zIY#VrtLpDLqCHdDTL#rT_~LSBYGCZ!9d%b0zs#dCWt?`PJN#cJiuIR(HSl|9bTspp zs(+a`_ijN?B4c9SH~w_!A+=Q%%K7CxvQHnQI@*X-j>&i3eyHMw7flGb2B=SQ7@zap z8Iz@o(a<7a+n@xhPzOudD^U$B)}G)Z!RjHOXR>0AsyWHSPrrlq{&)B)wx>+|OKr1R zx!J>0lSAdWfym~o8)Z6bH#YDMLEiQ%io9zTyLfu`_<%dC2DeK*kWrr@c3v- z?Vo`&3H&_zs8FsxA{)?)*!KUL;`B5Re^lThI6t1QeFuJ6%m>AlYMtR&*$YV_9ZW!7 z7_Ao(8fQCeJxHphV9FV`Uxl^vmdo{@xr#6EjgNLnnakEXR)nHhwNb91garOLmiEA;hN6&0q>X& zGHvf4BLq}A$PPWp-LUz-0~X&)@$y-{BJTA8O+|;MoXja!^slZG%b@BQ-Nw|y@kPu! zRG(}kmefLjU6zV3mx&TtAO7Lo_;7kN+h4xvSQXEq?0`MyBUEKOjPRD5;s1JrP!vmb zMP3H!Xd8Hh0z&lgk8t$7dNT@FvV05GRF$6v6QHB86x#FxTdV*Y6{=y%ipgdSW1DiT z6r$q8#|4$go$?|TCQrd~nRUo^=JUB`ZYw?@2yCGE8c(Ay7iN$#(cPoacR4XNmjJH$-{?kIDOz9!_m* z$OepQU}hhnE+GGo+<***+T~#q;edf#nLsY33Fess5GtN9a+ePpOC}S!I4n1z?ln_3 z6pC<0EWm?w5wo)WkN?LZKXSz82L5yMI zrFk?bcNK=xtO%iaB4967m87=HomO;7+@`sXyr!>QJQ>s@^z3OMuT6DOqtMw#*kByK zH(njawwNue*BczuA!p^Fy+-`+JbrQ=HPe0{U0Wl*YJUE2zipg+sgKm|l|@jUw1haJ z0WjXs@)GDs9j5`I(+IVw_SPQZ>F|lAAM!YZx82_@)T=sNtcyY|%0sXyrc%aL@7U7P zb!SB=!X&Id|NPp$xY(z%w9doPTqh%(D&y@MsyZ)N_A8xe`sVS`w4CFI4RSjcG4?E^ zmA)w!$IKDf%>Ob^P}~Vcy5%aEjG@?p32l|*=C3Z`cpn$J44S8afc5;;K%gAo4CGj{nT-rl43YGa;xcSok7_B5 zUmk(_?aS^WA8#O=F1>~I_%NdR=i=yZ$pm(LD?Qpp@?C+ij1lLbsW_7-x)eMd1#N(} zcuL4la?VB)b_A!K#VoMl)3}!QFy7dOb|#hM84yNfEONfUhdky$a%~TTfoOcT{q*pO zdXX52L+F}1KG=SRjyStg+VIje{US`9LVugtL2&gGv%bQ>%`p?` z+IK_XvKfDz_C86JBW*455|PSyqcTS2UYO<1*WqaGyDD#b?2LwuTvqRfhfk)ivm)#nEI`dPj)85vX1$PAZR&Y|j5 z%ToAgw)nhukJV50`qiBqnnRt`&w-^iF4%RC;IMg;VnptkC30}(A1a{;wgC!>&aNi% z*GXE&r14Kbz*+e0i)K!Wa-M2|-o#nCvqj*n#^jb4mrJAlPfBgrBs)t|T+F@jCBQ1S z?hw8|Oq}0u=#<>)BZAK1Rn#d2A;djc(cn3W#k?xFU`vZb2gD%k)R}9x!Hk&-6P@5U ztV5&U=fSI~=dnv&hxaaT@4z=t#WfapCLz}!Ui9)%=?AU}0Hv&W|Ey|KR$J&o5e4f= zw4Qni1#kTdl}R1DXo_uyZbKLsEO2B=G9!oMPL(tsifhNw2MKHr)^@;-k=&-pIWwKS zC&nnR-_cBVV5^0Vz3&;k3>{gzjeLzvJy;l52eoXU2xd^IJ3oq_~$tc_2gy#;g{h5x43j)EUa zG}!Yr;ai(-NM&70w+sAjec|hiWnh=_EC)F@k5c@&14VQ+i%e4STut=~5;lSs%sSjm z-UU;HWv8+BQD$tFL(Eky#PT$PzA0l05SZEIQ$YOaF#3fYmksj8(DC#ofvcTkK<#7A zl1-WmBG}BD)Uxvs3<5a=<>I-2qX76t3D`GU;l*7$8e!)y-Kfa>hk2}Wu|CV`VH1KG zXZWmYJ3&K>QgtAh{~*suMY0=o2)YhTZmSvPxYs07D>Zu}sgdBDDHqYCh1pOXE6TyO zuDnFGD5Y*FeHDA!{Q&cY{<)1{!@`nL_rvE~(9x?>W1Nks9Oo37#V}Na7@HoZWYLp9 ziNp~L8>sK;gS)$Dk7~`=kZmU`Q5#Y#92`ntT+PbO@!LCAD(#2whYMaKK(_8}s#Ntz zj+-BLgi-i5#+j?oCAv<@u$sOL&_^wklyUy;Qb*6M{EdphXXL~eFg3Dz8%xUNef>N3 z7qsU#@#p%F(0l_r>V$P^9$HI`>7=ei?az{K5my-oeOWu~w+jN*9-7@xptPdeUb>tT zySOGTMc8ALbz)G?Ya_iDC^|FFqN_iP4W>J_tmnc(ty1$He<$$n(1yBqx^+Odf(g$- zKwsvGa6FhQsO*Yh*C(hGS~kqHBJU|X`49A#YkbLD!xDod>tjauJ`#Mp4AJ)B~ zCt{8!28zKjLjz^LBY$+VpGISVbI%K&&H|hg9+S77#xGy-1v}d*?#zfc6|fb;yWxFz z1TEFy6u>*m9~AO?N+7OizjdhLoL_EyiJSwGzm>`=#hi*A8XM}|8j1_&g@Bqd#Ym5KD2KXxbJES;60#jZsfkI_FO{qzI5n-XlR ziWs(^lu@u;diel{;vx}$P9>ek>ztly@c5FejFXjgSyjw4xZ5@aQ2=s~a|fwgXY2}I z(M{QFrwPE9(`Urd6r+j1AgKu<(F&BtI0AUXSX3uI19(cdx_QeyZMH0GQb87sF;o(74U>yBp*AB{3AeEr|N m(6G$SbUCJ!YF4YEGcWmlho$|QuC6MDT@a0Q>(GuPQFyWE7VotH literal 0 HcmV?d00001