ff_stdio.c 71 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174
  1. /*
  2. * FreeRTOS+FAT V2.3.3
  3. * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy of
  6. * this software and associated documentation files (the "Software"), to deal in
  7. * the Software without restriction, including without limitation the rights to
  8. * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  9. * the Software, and to permit persons to whom the Software is furnished to do so,
  10. * subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in all
  13. * copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  17. * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  18. * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  19. * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  20. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. *
  22. * https://www.FreeRTOS.org
  23. * https://github.com/FreeRTOS
  24. *
  25. */
  26. /* FreeRTOS includes. */
  27. #include "FreeRTOS.h"
  28. #include "task.h"
  29. #include "portable.h"
  30. /* FreeRTOS+FAT includes. */
  31. #include "ff_headers.h"
  32. #include "ff_stdio.h"
  33. #if ( ffconfigTIME_SUPPORT != 0 )
  34. #include <time.h>
  35. #endif
  36. #ifndef SIZE_MAX
  37. #define SIZE_MAX ( ( size_t ) -1 )
  38. #endif
  39. /* The number of bytes to write at a time when extending the length of a file
  40. * in a call to ff_truncate(). */
  41. #define stdioTRUNCATE_WRITE_LENGTH 512
  42. /* Bits set to indicate whether ".." should be included as well as ".". */
  43. #define stdioDIR_ENTRY_DOT_1 ( 1U & 0x03U )
  44. #define stdioDIR_ENTRY_DOT_2 ( 2U & 0x03U )
  45. /* The directory entries '.' and '..' will show a file size of 1 KB. */
  46. #define stdioDOT_ENTRY_FILE_SIZE 1024
  47. /*-----------------------------------------------------------*/
  48. /*
  49. * Add the CWD to the beginning of a relative path, and copy the resultant
  50. * absolute path into a thread local non const buffer.
  51. */
  52. /*static*/ const char * prvABSPath( const char * pcPath );
  53. /*
  54. * Translate a +FAT error to a value compatible with errno.h
  55. * If the value represents an error, it is negative
  56. * The return value of this function will always be positive
  57. */
  58. int prvFFErrorToErrno( FF_Error_t xError );
  59. /*
  60. * Generate a time stamp for the file.
  61. */
  62. #if ( ffconfigTIME_SUPPORT == 1 )
  63. static uint32_t prvFileTime( FF_SystemTime_t * pxTime );
  64. #endif
  65. #if ( ffconfigHAS_CWD == 1 )
  66. /* FreeRTOS+FAT requires two thread local storage pointers. One for errno
  67. * and one for the CWD structure. */
  68. #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS < 2 )
  69. #error FreeRTOS+FAT requires two thread local storage pointers so configNUM_THREAD_LOCAL_STORAGE_POINTERS must be at least 2 in FreeRTOSConfig.h
  70. #endif
  71. /* Each task has its own Current Working Directory (CWD). The CWD is used
  72. * to extend relative paths to absolute paths. */
  73. typedef struct WORKING_DIR
  74. {
  75. char pcCWD[ ffconfigMAX_FILENAME ]; /* The current working directory. */
  76. char pcFileName[ ffconfigMAX_FILENAME ]; /* The created absolute path. */
  77. } WorkingDirectory_t;
  78. /*
  79. * Lookup the CWD of the current task.
  80. */
  81. static WorkingDirectory_t * pxFindCWD( void );
  82. /*
  83. * Convert a string which may contain a relative path into a string that
  84. * will only contain an absolute path.
  85. */
  86. static const char * prvProcessRelativePaths( const char * pcPath );
  87. #else /* ffconfigHAS_CWD */
  88. /* FreeRTOS+FAT requires one thread local storage pointers for errno. */
  89. #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS < 2 )
  90. #error FreeRTOS+FAT requires one thread local storage pointers so configNUM_THREAD_LOCAL_STORAGE_POINTERS must be at least 1 in FreeRTOSConfig.h
  91. #endif
  92. /* Only absolute paths are supported so define away the prvABSPath()
  93. * function. */
  94. /*static*/ const char * prvABSPath( const char * pcPath )
  95. {
  96. return pcPath;
  97. }
  98. #endif /* ffconfigHAS_CWD */
  99. #if ( ffconfigUSE_DELTREE != 0 )
  100. /*
  101. * Remove all files and directories starting from a certain path.
  102. * This function uses recursion - which breaches the coding standard. USE
  103. * WITH CARE.
  104. */
  105. static int ff_deltree_recurse( char * pcPath );
  106. #endif
  107. /*-----------------------------------------------------------*/
  108. FF_FILE * ff_fopen( const char * pcFile,
  109. const char * pcMode )
  110. {
  111. FF_FILE * pxStream = NULL;
  112. FF_DirHandler_t xHandler;
  113. FF_Error_t xError;
  114. uint8_t ucMode;
  115. /* Insert the current working directory in front of relative paths. */
  116. pcFile = prvABSPath( pcFile );
  117. /* Look-up the I/O manager for the file system. */
  118. if( FF_FS_Find( pcFile, &xHandler ) == pdFALSE )
  119. {
  120. stdioSET_ERRNO( pdFREERTOS_ERRNO_ENXIO ); /* No such device or address. */
  121. }
  122. else
  123. {
  124. /* Now 'xHandler.pcPath' contains an absolute path within the file system.
  125. * Translate a type string "r|w|a[+]" to +FAT's mode bits. */
  126. ucMode = FF_GetModeBits( pcMode );
  127. pxStream = FF_Open( xHandler.pxManager, xHandler.pcPath, ucMode, &xError );
  128. stdioSET_ERRNO( prvFFErrorToErrno( xError ) );
  129. #if ( ffconfigUSE_NOTIFY != 0 )
  130. {
  131. if( ( pxStream != NULL ) && ( ( ucMode & ( FF_MODE_WRITE | FF_MODE_APPEND ) ) != 0 ) )
  132. {
  133. /*_RB_ Function name needs updating. */
  134. callFileEvents( pcFile, eFileCreate );
  135. }
  136. }
  137. #endif /* ffconfigUSE_NOTIFY */
  138. #if ( ffconfigDEV_SUPPORT != 0 )
  139. {
  140. if( pxStream != NULL )
  141. {
  142. FF_Device_Open( pcFile, pxStream );
  143. }
  144. }
  145. #endif /* ffconfigDEV_SUPPORT */
  146. }
  147. return pxStream;
  148. }
  149. /*-----------------------------------------------------------*/
  150. int ff_fclose( FF_FILE * pxStream )
  151. {
  152. FF_Error_t xError;
  153. int iReturn, ff_errno;
  154. #if ( ffconfigDEV_SUPPORT != 0 )
  155. {
  156. /* Currently device support is in an experimental state. It will allow
  157. * to create virtual files. The I/O data to those files will be redirected
  158. * to their connected "drivers". */
  159. if( pxStream != NULL )
  160. {
  161. FF_Device_Close( pxStream );
  162. }
  163. }
  164. #endif
  165. xError = FF_Close( pxStream );
  166. ff_errno = prvFFErrorToErrno( xError );
  167. if( ff_errno == 0 )
  168. {
  169. iReturn = 0;
  170. }
  171. else
  172. {
  173. /* Return -1 for error as per normal fclose() semantics. */
  174. iReturn = -1;
  175. }
  176. /* Store the errno to thread local storage. */
  177. stdioSET_ERRNO( ff_errno );
  178. return iReturn;
  179. }
  180. /*-----------------------------------------------------------*/
  181. int ff_fseek( FF_FILE * pxStream,
  182. long lOffset,
  183. int iWhence )
  184. {
  185. FF_Error_t xError;
  186. int iReturn, ff_errno;
  187. #if ( ffconfigDEV_SUPPORT != 0 )
  188. if( pxStream->pxDevNode != NULL )
  189. {
  190. xError = FF_Device_Seek( pxStream, lOffset, iWhence );
  191. }
  192. else
  193. #endif
  194. {
  195. xError = FF_Seek( pxStream, lOffset, iWhence );
  196. }
  197. ff_errno = prvFFErrorToErrno( xError );
  198. if( ff_errno == 0 )
  199. {
  200. iReturn = 0;
  201. }
  202. else
  203. {
  204. if( xError == FF_ERR_FILE_SEEK_INVALID_POSITION )
  205. {
  206. /* Illegal position, outside the file's space */
  207. ff_errno = pdFREERTOS_ERRNO_ESPIPE;
  208. }
  209. else if( xError == FF_ERR_FILE_SEEK_INVALID_ORIGIN )
  210. {
  211. /* Illegal parameter value for iWhence: SET,CUR,END. */
  212. ff_errno = pdFREERTOS_ERRNO_EINVAL;
  213. }
  214. /* Return -1 for error as per normal fseek() semantics. */
  215. iReturn = -1;
  216. }
  217. /* Store the errno to thread local storage. */
  218. stdioSET_ERRNO( ff_errno );
  219. return iReturn;
  220. }
  221. /*-----------------------------------------------------------*/
  222. void ff_rewind( FF_FILE * pxStream )
  223. {
  224. ff_fseek( pxStream, 0, FF_SEEK_SET );
  225. /* Rewind is supposed to reset errno unconditionally. Store the errno to
  226. * thread local storage. */
  227. stdioSET_ERRNO( 0 );
  228. }
  229. /*-----------------------------------------------------------*/
  230. long ff_ftell( FF_FILE * pxStream )
  231. {
  232. long lResult;
  233. if( pxStream == NULL )
  234. {
  235. /* Store the errno to thread local storage. */
  236. stdioSET_ERRNO( pdFREERTOS_ERRNO_EBADF );
  237. /* Return -1 for error as per normal ftell() semantics. */
  238. lResult = -1;
  239. }
  240. else
  241. {
  242. lResult = ( long ) pxStream->ulFilePointer;
  243. }
  244. return lResult;
  245. }
  246. /*-----------------------------------------------------------*/
  247. int ff_feof( FF_FILE * pxStream )
  248. {
  249. int iResult;
  250. FF_Error_t xError;
  251. xError = FF_CheckValid( pxStream );
  252. if( FF_isERR( xError ) == pdFALSE )
  253. {
  254. /* Store the errno to thread local storage. */
  255. stdioSET_ERRNO( 0 );
  256. if( pxStream->ulFilePointer >= pxStream->ulFileSize )
  257. {
  258. iResult = pdTRUE;
  259. }
  260. else
  261. {
  262. iResult = pdFALSE;
  263. }
  264. }
  265. else
  266. {
  267. /* Store the errno to thread local storage. */
  268. stdioSET_ERRNO( prvFFErrorToErrno( xError ) );
  269. /* The file was invalid so a non-zero value cannot be returned. */
  270. iResult = pdFALSE;
  271. }
  272. return iResult;
  273. }
  274. /*-----------------------------------------------------------*/
  275. size_t ff_fread( void * pvBuffer,
  276. size_t xSize,
  277. size_t xItems,
  278. FF_FILE * pxStream )
  279. {
  280. int32_t iReturned;
  281. size_t xReturn;
  282. int ff_errno;
  283. #if ( ffconfigDEV_SUPPORT != 0 )
  284. if( pxStream->pxDevNode != NULL )
  285. {
  286. iReturned = FF_Device_Read( pvBuffer, xSize, xItems, pxStream );
  287. }
  288. else
  289. #endif
  290. {
  291. iReturned = FF_Read( pxStream, xSize, xItems, ( uint8_t * ) pvBuffer );
  292. }
  293. ff_errno = prvFFErrorToErrno( iReturned );
  294. if( ff_errno == pdFREERTOS_ERRNO_NONE )
  295. {
  296. /* As per the standard fread() semantics, the return value is the number
  297. * of complete items read, which will only equal the number of bytes
  298. * transferred when the item size is 1. */
  299. xReturn = ( size_t ) iReturned;
  300. }
  301. else
  302. {
  303. xReturn = 0;
  304. }
  305. /* Store the errno to thread local storage. */
  306. stdioSET_ERRNO( ff_errno );
  307. return xReturn;
  308. }
  309. /*-----------------------------------------------------------*/
  310. size_t ff_fwrite( const void * pvBuffer,
  311. size_t xSize,
  312. size_t xItems,
  313. FF_FILE * pxStream )
  314. {
  315. int32_t iReturned;
  316. size_t xReturn;
  317. int ff_errno;
  318. #if ( ffconfigDEV_SUPPORT != 0 )
  319. if( pxStream->pxDevNode != NULL )
  320. {
  321. iReturned = FF_Device_Write( pvBuffer, xSize, xItems, pxStream );
  322. }
  323. else
  324. #endif
  325. {
  326. iReturned = FF_Write( pxStream, xSize, xItems, ( uint8_t * ) pvBuffer );
  327. }
  328. ff_errno = prvFFErrorToErrno( iReturned );
  329. if( ff_errno == pdFREERTOS_ERRNO_NONE )
  330. {
  331. /* As per the standard fwrite() semantics, the return value is the
  332. * number of complete items read, which will only equal the number of bytes
  333. * transferred when the item size is 1. */
  334. xReturn = ( size_t ) iReturned;
  335. }
  336. else
  337. {
  338. xReturn = 0;
  339. }
  340. /* Store the errno to thread local storage. */
  341. stdioSET_ERRNO( ff_errno );
  342. return xReturn;
  343. }
  344. /*-----------------------------------------------------------*/
  345. int ff_fgetc( FF_FILE * pxStream )
  346. {
  347. int32_t iResult;
  348. int ff_errno;
  349. iResult = FF_GetC( pxStream );
  350. ff_errno = prvFFErrorToErrno( iResult );
  351. if( ff_errno != 0 )
  352. {
  353. iResult = FF_EOF;
  354. }
  355. /* Store the errno to thread local storage. */
  356. stdioSET_ERRNO( ff_errno );
  357. return iResult;
  358. }
  359. /*-----------------------------------------------------------*/
  360. int ff_fputc( int iChar,
  361. FF_FILE * pxStream )
  362. {
  363. int iResult, ff_errno;
  364. iResult = FF_PutC( pxStream, ( uint8_t ) iChar );
  365. ff_errno = prvFFErrorToErrno( iResult );
  366. if( ff_errno != 0 )
  367. {
  368. iResult = FF_EOF;
  369. }
  370. /* Store the errno to thread local storage. */
  371. stdioSET_ERRNO( ff_errno );
  372. return iResult;
  373. }
  374. /*-----------------------------------------------------------*/
  375. #if ( ffconfigFPRINTF_SUPPORT == 1 )
  376. int ff_fprintf( FF_FILE * pxStream,
  377. const char * pcFormat,
  378. ... )
  379. {
  380. int iCount;
  381. size_t xResult;
  382. char * pcBuffer;
  383. va_list xArgs;
  384. pcBuffer = ( char * ) ffconfigMALLOC( ffconfigFPRINTF_BUFFER_LENGTH );
  385. if( pcBuffer == NULL )
  386. {
  387. /* Store the errno to thread local storage. */
  388. stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );
  389. iCount = -1;
  390. }
  391. else
  392. {
  393. va_start( xArgs, pcFormat );
  394. iCount = vsnprintf( pcBuffer, ffconfigFPRINTF_BUFFER_LENGTH, pcFormat, xArgs );
  395. va_end( xArgs );
  396. /* ff_fwrite() will set ff_errno. */
  397. if( iCount > 0 )
  398. {
  399. xResult = ff_fwrite( pcBuffer, ( size_t ) 1, ( size_t ) iCount, pxStream );
  400. if( xResult < ( size_t ) iCount )
  401. {
  402. iCount = -1;
  403. }
  404. }
  405. ffconfigFREE( pcBuffer );
  406. }
  407. return iCount;
  408. }
  409. #endif /* if ( ffconfigFPRINTF_SUPPORT == 1 ) */
  410. /*-----------------------------------------------------------*/
  411. /*_RB_ to comply with the norm, the second parameter should be an int, but size_t
  412. * is more appropriate. */
  413. char * ff_fgets( char * pcBuffer,
  414. size_t xCount,
  415. FF_FILE * pxStream )
  416. {
  417. int32_t xResult;
  418. int ff_errno;
  419. xResult = FF_GetLine( pxStream, ( char * ) pcBuffer, xCount );
  420. /* This call seems to result in errno being incorrectly set to
  421. * FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION when an EOF is encountered. */
  422. ff_errno = prvFFErrorToErrno( xResult );
  423. if( ff_errno != 0 )
  424. {
  425. pcBuffer = NULL;
  426. }
  427. /* Store the errno to thread local storage. */
  428. stdioSET_ERRNO( ff_errno );
  429. return pcBuffer;
  430. }
  431. /*-----------------------------------------------------------*/
  432. int ff_seteof( FF_FILE * pxStream )
  433. {
  434. FF_Error_t iResult;
  435. int iReturn, ff_errno;
  436. iResult = FF_SetEof( pxStream );
  437. ff_errno = prvFFErrorToErrno( iResult );
  438. if( ff_errno == 0 )
  439. {
  440. iReturn = 0;
  441. }
  442. else
  443. {
  444. iReturn = FF_EOF;
  445. }
  446. /* Store the errno to thread local storage. */
  447. stdioSET_ERRNO( ff_errno );
  448. return iReturn;
  449. }
  450. /*-----------------------------------------------------------*/
  451. /*_RB_ The norm would be to return an int, but in either case it is not clear
  452. * what state the file is left in (open/closed). */
  453. FF_FILE * ff_truncate( const char * pcFileName,
  454. long lTruncateSize )
  455. {
  456. FF_Error_t xResult = 0;
  457. FF_FILE * pxStream;
  458. size_t xReturned;
  459. uint32_t ulLength, ulBytesLeftToAdd, ulBytesToWrite;
  460. char * pcBufferToWrite;
  461. pxStream = ff_fopen( pcFileName, "a+" );
  462. if( pxStream != NULL )
  463. {
  464. ulLength = pxStream->ulFileSize;
  465. }
  466. else
  467. {
  468. ulLength = 0;
  469. }
  470. if( pxStream == NULL )
  471. {
  472. /* Store the errno to thread local storage. */
  473. stdioSET_ERRNO( prvFFErrorToErrno( xResult ) );
  474. }
  475. else if( ulLength > ( uint32_t ) lTruncateSize )
  476. {
  477. /* Seek the desired position */
  478. xResult = FF_Seek( pxStream, lTruncateSize, FF_SEEK_SET );
  479. /* Make the current position equal to its length */
  480. if( FF_isERR( xResult ) == pdFALSE )
  481. {
  482. xResult = FF_SetEof( pxStream );
  483. }
  484. if( FF_isERR( xResult ) != pdFALSE )
  485. {
  486. ff_fclose( pxStream );
  487. pxStream = NULL;
  488. }
  489. /* Store the errno to thread local storage. */
  490. stdioSET_ERRNO( prvFFErrorToErrno( xResult ) );
  491. }
  492. else if( ulLength == ( uint32_t ) lTruncateSize )
  493. {
  494. /* Nothing to do, the file has the desired size
  495. * and the open handle will be returned. */
  496. }
  497. else
  498. {
  499. /* lTruncateSize > ulLength. The user wants to open this file with a
  500. * larger size than it currently has. Fill it with zeros. */
  501. pcBufferToWrite = ( char * ) ffconfigMALLOC( stdioTRUNCATE_WRITE_LENGTH );
  502. if( pcBufferToWrite == NULL )
  503. {
  504. ff_fclose( pxStream );
  505. pxStream = NULL;
  506. /* Store the errno to thread local storage. */
  507. stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );
  508. }
  509. else
  510. {
  511. /* File has to grow */
  512. ulBytesLeftToAdd = ( ( uint32_t ) lTruncateSize ) - ulLength;
  513. /* Zeros must be written. */
  514. memset( pcBufferToWrite, '\0', stdioTRUNCATE_WRITE_LENGTH );
  515. while( ulBytesLeftToAdd > 0UL )
  516. {
  517. if( ( pxStream->ulFileSize % stdioTRUNCATE_WRITE_LENGTH ) != 0 )
  518. {
  519. /* Although +FAT's FF_Write() can handle any size at any
  520. * offset, the driver puts data more efficiently if blocks are
  521. * written at block boundaries. */
  522. ulBytesToWrite = stdioTRUNCATE_WRITE_LENGTH - ( pxStream->ulFileSize % stdioTRUNCATE_WRITE_LENGTH );
  523. if( ulBytesToWrite > ulBytesLeftToAdd )
  524. {
  525. ulBytesToWrite = ulBytesLeftToAdd;
  526. }
  527. }
  528. else
  529. {
  530. ulBytesToWrite = ulBytesLeftToAdd;
  531. if( ulBytesToWrite > stdioTRUNCATE_WRITE_LENGTH )
  532. {
  533. ulBytesToWrite = stdioTRUNCATE_WRITE_LENGTH;
  534. }
  535. }
  536. xReturned = ff_fwrite( pcBufferToWrite, sizeof( char ), ulBytesToWrite, pxStream );
  537. if( xReturned != ( size_t ) ulBytesToWrite )
  538. {
  539. /* Write error. Close the stream and set the proper .
  540. * errno. */
  541. ff_fclose( pxStream );
  542. pxStream = NULL;
  543. /* Not setting ff_errno because it has been set by other
  544. * functions from this ff_stdio. */
  545. break;
  546. }
  547. ulBytesLeftToAdd -= ulBytesToWrite;
  548. }
  549. ffconfigFREE( pcBufferToWrite );
  550. }
  551. }
  552. return pxStream;
  553. }
  554. /*-----------------------------------------------------------*/
  555. #if ( ffconfigMKDIR_RECURSIVE == 0 )
  556. /* The normal mkdir() : if assumes that the directories leading to the last
  557. * element of pcDirectory already exists. For instance: mkdir( "/a/b/c" ) will
  558. * succeed if the path "/a/b" already exists. */
  559. int ff_mkdir( const char * pcDirectory )
  560. {
  561. int iResult, ff_errno;
  562. FF_DirHandler_t xHandler;
  563. /* In case a CWD is used, get the absolute path. */
  564. pcDirectory = prvABSPath( pcDirectory );
  565. /* Find the i/o manager for this path */
  566. if( FF_FS_Find( pcDirectory, &xHandler ) == pdFALSE )
  567. {
  568. /* No such device or address. */
  569. stdioSET_ERRNO( pdFREERTOS_ERRNO_ENXIO );
  570. /* Return -1 for error as per normal mkdir() semantics. */
  571. iResult = -1;
  572. }
  573. else
  574. {
  575. /* A simple non-recursive make of a directory. */
  576. iResult = FF_MkDir( xHandler.pxManager, xHandler.pcPath );
  577. if( FF_GETERROR( iResult ) == FF_ERR_DIR_OBJECT_EXISTS )
  578. {
  579. /* No error if the target directory already exists. */
  580. iResult = FF_ERR_NONE;
  581. }
  582. ff_errno = prvFFErrorToErrno( iResult );
  583. /* Store the errno to thread local storage. */
  584. stdioSET_ERRNO( ff_errno );
  585. if( ff_errno == pdFREERTOS_ERRNO_NONE )
  586. {
  587. iResult = 0;
  588. }
  589. else
  590. {
  591. /* Return -1 for error as per normal mkdir() semantics. */
  592. iResult = -1;
  593. }
  594. }
  595. return iResult;
  596. }
  597. #else /* ffconfigMKDIR_RECURSIVE */
  598. #warning This path is not yet included in the regression tests.
  599. /* The 'recursive mkdir() : if the parameter 'xRecursive' is non-zero,
  600. * the function will try to create the complete path. */
  601. int ff_mkdir( const char * pcDirectory,
  602. int xRecursive )
  603. {
  604. int32_t lResult;
  605. FF_DirHandler_t xHandler;
  606. /* In case a CWD is used, get the absolute path. */
  607. pcDirectory = prvABSPath( pcDirectory );
  608. /* Find the i/o manager for this path */
  609. if( FF_FS_Find( pcDirectory, &xHandler ) == pdFALSE )
  610. {
  611. /* No such device or address. Store the errno to thread local
  612. * storage. */
  613. stdioSET_ERRNO( pdFREERTOS_ERRNO_ENXIO );
  614. /* Return -1 for error as per normal mkdir() semantics. */
  615. lResult = -1;
  616. }
  617. else
  618. {
  619. if( xRecursive == pdFALSE )
  620. {
  621. /* A simple non-recursive make of a directory. */
  622. lResult = FF_MkDir( xHandler.pxManager, xHandler.pcPath );
  623. if( FF_GETERROR( lResult ) == FF_ERR_DIR_OBJECT_EXISTS )
  624. {
  625. /* No error if the target directory already exists. */
  626. lResult = 0;
  627. }
  628. }
  629. else
  630. {
  631. /* The recursive option is used. */
  632. char pcTempPath[ ffconfigMAX_FILENAME ];
  633. FF_Error_t errCode;
  634. int iLength = snprintf( pcTempPath, sizeof( pcTempPath ), "%s", xHandler.pcPath );
  635. char * pcPtr = pcTempPath + 1, * pcPrev;
  636. const char * pcLast = pcTempPath + iLength;
  637. lResult = FF_ERR_NONE;
  638. for( ; ; )
  639. {
  640. for( pcPrev = pcPtr; pcPtr < pcLast; pcPtr++ )
  641. {
  642. if( *pcPtr == '/' )
  643. {
  644. *pcPtr = '\0';
  645. break;
  646. }
  647. }
  648. if( pcPrev == pcPtr )
  649. {
  650. break;
  651. }
  652. errCode = FF_MkDir( xHandler.pxManager, pcTempPath );
  653. if( FF_isERR( errCode ) && ( FF_GETERROR( errCode ) != FF_ERR_DIR_OBJECT_EXISTS ) )
  654. {
  655. lResult = errCode;
  656. break;
  657. }
  658. if( pcPtr >= ( pcLast - 1 ) )
  659. {
  660. break;
  661. }
  662. *( pcPtr++ ) = '/';
  663. }
  664. }
  665. /* Store the errno to thread local storage. */
  666. stdioSET_ERRNO( prvFFErrorToErrno( lResult ) );
  667. }
  668. return lResult;
  669. }
  670. #endif /* ffconfigMKDIR_RECURSIVE */
  671. /*-----------------------------------------------------------*/
  672. int ff_rmdir( const char * pcDirectory )
  673. {
  674. int32_t lResult;
  675. int iReturn, ff_errno;
  676. FF_DirHandler_t xHandler;
  677. /* In case a CWD is used, get the absolute path */
  678. pcDirectory = prvABSPath( pcDirectory );
  679. /* Find the i/o manager which can handle this path. */
  680. if( FF_FS_Find( pcDirectory, &xHandler ) == pdFALSE )
  681. {
  682. ff_errno = pdFREERTOS_ERRNO_ENXIO; /* No such device or address */
  683. /* Return -1 for error as per normal rmdir() semantics. */
  684. iReturn = -1;
  685. }
  686. else
  687. {
  688. lResult = FF_RmDir( xHandler.pxManager, xHandler.pcPath );
  689. ff_errno = prvFFErrorToErrno( lResult );
  690. if( ff_errno == 0 )
  691. {
  692. iReturn = 0;
  693. }
  694. else
  695. {
  696. /* Return -1 for error as per normal rmdir() semantics. */
  697. iReturn = -1;
  698. }
  699. }
  700. /* Store the errno to thread local storage. */
  701. stdioSET_ERRNO( ff_errno );
  702. return iReturn;
  703. }
  704. /*-----------------------------------------------------------*/
  705. int ff_remove( const char * pcPath )
  706. {
  707. FF_DirHandler_t xHandler;
  708. FF_Error_t xError;
  709. int iReturn, ff_errno;
  710. /* In case a CWD is used, get the absolute path */
  711. pcPath = prvABSPath( pcPath );
  712. /* Find the i/o manager which can handle this path. */
  713. if( FF_FS_Find( pcPath, &xHandler ) == pdFALSE )
  714. {
  715. /* No such device or address */
  716. ff_errno = pdFREERTOS_ERRNO_ENXIO;
  717. /* Return -1 for error as per normal remove() semantics. */
  718. iReturn = -1;
  719. }
  720. else
  721. {
  722. xError = FF_RmFile( xHandler.pxManager, xHandler.pcPath );
  723. ff_errno = prvFFErrorToErrno( xError );
  724. #if ffconfigUSE_NOTIFY
  725. {
  726. if( FF_isERR( xError ) == pdFALSE )
  727. {
  728. callFileEvents( pcPath, eFileRemove );
  729. }
  730. }
  731. #endif
  732. if( ff_errno == 0 )
  733. {
  734. iReturn = 0;
  735. }
  736. else
  737. {
  738. /* Return -1 for error as per normal remove() semantics. */
  739. iReturn = -1;
  740. }
  741. }
  742. /* Store the errno to thread local storage. */
  743. stdioSET_ERRNO( ff_errno );
  744. return iReturn;
  745. }
  746. /*-----------------------------------------------------------*/
  747. /*_RB_ Last parameter not documented. */
  748. int ff_rename( const char * pcOldName,
  749. const char * pcNewName,
  750. int bDeleteIfExists )
  751. {
  752. FF_DirHandler_t xHandlers[ 2 ];
  753. FF_Error_t xError = FF_ERR_NONE;
  754. int ff_errno = 0, iReturn;
  755. #if ( ffconfigHAS_CWD != 0 )
  756. char * pcOldCopy;
  757. size_t xSize;
  758. #endif
  759. /* In case a CWD is used, get the absolute path */
  760. pcOldName = prvABSPath( pcOldName );
  761. /* Find the i/o manager which can handle this path */
  762. if( FF_FS_Find( pcOldName, &xHandlers[ 0 ] ) == pdFALSE )
  763. {
  764. xError = ( int32_t ) ( FF_ERR_NULL_POINTER | FF_MOVE );
  765. ff_errno = pdFREERTOS_ERRNO_ENXIO; /* No such device or address */
  766. }
  767. else
  768. {
  769. #if ( ffconfigHAS_CWD != 0 )
  770. {
  771. xSize = strlen( xHandlers[ 0 ].pcPath ) + 1;
  772. pcOldCopy = ( char * ) ffconfigMALLOC( xSize );
  773. if( pcOldCopy == NULL )
  774. {
  775. /* Could not allocate space to store a file name. */
  776. ff_errno = pdFREERTOS_ERRNO_ENOMEM;
  777. xError = ( int32_t ) ( FF_ERR_NOT_ENOUGH_MEMORY | FF_MOVE );
  778. }
  779. else
  780. {
  781. /* The function prvABSPath() returns a pointer to the task
  782. * storage space. Rename needs to call it twice and therefore the
  783. * path must be stored before it gets overwritten. */
  784. memcpy( pcOldCopy, xHandlers[ 0 ].pcPath, xSize );
  785. xHandlers[ 0 ].pcPath = pcOldCopy;
  786. }
  787. }
  788. #endif /* ffconfigHAS_CWD != 0 */
  789. #if ( ffconfigHAS_CWD != 0 )
  790. if( pcOldCopy != NULL )
  791. #endif /* ffconfigHAS_CWD != 0 */
  792. {
  793. pcNewName = prvABSPath( pcNewName );
  794. /* Find the i/o manager which can handle this path */
  795. if( FF_FS_Find( pcNewName, &( xHandlers[ 1 ] ) ) == pdFALSE )
  796. {
  797. xError = ( int32_t ) ( FF_ERR_NULL_POINTER | FF_MOVE );
  798. ff_errno = pdFREERTOS_ERRNO_ENXIO; /* No such device or address */
  799. }
  800. else if( xHandlers[ 0 ].pxManager != xHandlers[ 1 ].pxManager )
  801. {
  802. xError = ( int32_t ) ( FF_ERR_NULL_POINTER | FF_MOVE );
  803. /* Cross-device link, which can not be done. */
  804. ff_errno = pdFREERTOS_ERRNO_EXDEV;
  805. }
  806. else
  807. {
  808. xError = FF_Move( xHandlers[ 0 ].pxManager, xHandlers[ 0 ].pcPath, xHandlers[ 1 ].pcPath, bDeleteIfExists );
  809. ff_errno = prvFFErrorToErrno( xError );
  810. #if ffconfigUSE_NOTIFY
  811. {
  812. if( FF_isERR( xError ) == pdFALSE )
  813. {
  814. callFileEvents( pcNewName, eFileChange );
  815. }
  816. }
  817. #endif
  818. }
  819. #if ( ffconfigHAS_CWD != 0 )
  820. {
  821. ffconfigFREE( pcOldCopy );
  822. }
  823. #endif
  824. }
  825. }
  826. /* Store the errno to thread local storage. */
  827. stdioSET_ERRNO( ff_errno );
  828. if( ff_errno == 0 )
  829. {
  830. iReturn = 0;
  831. }
  832. else
  833. {
  834. /* Return -1 for error as per normal rmdir() semantics. */
  835. iReturn = -1;
  836. }
  837. return iReturn;
  838. }
  839. /*-----------------------------------------------------------*/
  840. int ff_stat( const char * pcName,
  841. FF_Stat_t * pxStatBuffer )
  842. {
  843. FF_DirEnt_t xDirEntry;
  844. uint32_t ulFileCluster;
  845. FF_Error_t xError;
  846. int iResult;
  847. FF_DirHandler_t xHandler;
  848. BaseType_t xIndex;
  849. FF_FindParams_t xFindParams;
  850. #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 )
  851. const FF_T_WCHAR * pcFileName = NULL;
  852. #else
  853. /* Initialised to prevent MSVC incorrectly claiming the variable is used
  854. * without being initialised. */
  855. const char * pcFileName = NULL;
  856. #endif
  857. memset( &xFindParams, '\0', sizeof( xFindParams ) );
  858. /* Insert the current working directory in front of relative paths. */
  859. pcName = prvABSPath( pcName );
  860. /* Look-up the I/O manager for the file system. */
  861. if( FF_FS_Find( pcName, &xHandler ) == pdFALSE )
  862. {
  863. /* No such device or address. */
  864. xError = ( FF_Error_t ) ( pdFREERTOS_ERRNO_ENXIO | FF_STAT_FUNC );
  865. }
  866. else
  867. {
  868. xError = FF_ERR_NONE;
  869. pcName = xHandler.pcPath;
  870. /* Let xIndex point to the last occurrence of '/' or '\', to separate
  871. * the path from the file name. */
  872. xIndex = ( BaseType_t ) STRLEN( pcName );
  873. while( xIndex != 0 )
  874. {
  875. if( ( pcName[ xIndex ] == '\\' ) || ( pcName[ xIndex ] == '/' ) )
  876. {
  877. break;
  878. }
  879. xIndex--;
  880. }
  881. /* Copy the file name, i.e. the string that comes after the last
  882. * separator. */
  883. pcFileName = pcName + xIndex + 1;
  884. if( xIndex == 0 )
  885. {
  886. /* Only for the root, the slash is part of the directory name.
  887. * 'xIndex' now equals to the length of the path name. */
  888. xIndex = 1;
  889. }
  890. /* FF_CreateShortName() might set flags FIND_FLAG_FITS_SHORT and
  891. * FIND_FLAG_SIZE_OK. */
  892. FF_CreateShortName( &xFindParams, pcFileName );
  893. /* Lookup the path and find the cluster pointing to the directory: */
  894. xFindParams.ulDirCluster = FF_FindDir( xHandler.pxManager, pcName, xIndex, &xError );
  895. }
  896. if( FF_isERR( xError ) == pdFALSE )
  897. {
  898. /* See if the file does exist within the given directory. */
  899. ulFileCluster = FF_FindEntryInDir( xHandler.pxManager, &xFindParams, pcFileName, 0x00, &xDirEntry, &xError );
  900. if( ulFileCluster == 0ul )
  901. {
  902. /* If cluster 0 was returned, it might be because the file has no allocated cluster,
  903. * i.e. only a directory entry and no stored data. */
  904. if( STRLEN( pcFileName ) == STRLEN( xDirEntry.pcFileName ) )
  905. {
  906. if( ( xDirEntry.ulFileSize == 0 ) && ( FF_strmatch( pcFileName, xDirEntry.pcFileName, ( BaseType_t ) STRLEN( pcFileName ) ) == pdTRUE ) )
  907. {
  908. /* It is the file, give it a pseudo cluster number '1'. */
  909. ulFileCluster = 1;
  910. /* And reset any error. */
  911. xError = FF_ERR_NONE;
  912. }
  913. }
  914. }
  915. /* Test 'ulFileCluster' again, it might have been changed. */
  916. if( ulFileCluster == 0ul )
  917. {
  918. xError = FF_ERR_FILE_NOT_FOUND | FF_STAT_FUNC;
  919. }
  920. }
  921. if( ( pxStatBuffer != NULL ) && ( FF_isERR( xError ) == pdFALSE ) )
  922. {
  923. if( ( xDirEntry.ucAttrib & FF_FAT_ATTR_DIR ) != 0 )
  924. {
  925. pxStatBuffer->st_mode = ( unsigned short ) FF_IFDIR;
  926. }
  927. else
  928. {
  929. pxStatBuffer->st_mode = ( unsigned short ) FF_IFREG;
  930. }
  931. #if ( ffconfigDEV_SUPPORT != 0 )
  932. {
  933. BaseType_t bIsDeviceDir = xCheckDevicePath( pcFileName );
  934. if( bIsDeviceDir != pdFALSE )
  935. {
  936. FF_Device_GetDirEnt( xHandler.pcPath, &( xDirEntry ) );
  937. }
  938. }
  939. #endif
  940. /* Despite the warning output by MSVC - it is not possible to get here
  941. * if xDirEntry has not been initialised. */
  942. pxStatBuffer->st_size = xDirEntry.ulFileSize;
  943. pxStatBuffer->st_ino = xDirEntry.ulObjectCluster;
  944. pxStatBuffer->st_dev = ( short ) xHandler.xFSIndex;
  945. #if ( ffconfigTIME_SUPPORT == 1 )
  946. {
  947. pxStatBuffer->st_atime = ( unsigned long ) prvFileTime( &( xDirEntry.xAccessedTime ) );
  948. pxStatBuffer->st_mtime = ( unsigned long ) prvFileTime( &( xDirEntry.xModifiedTime ) );
  949. pxStatBuffer->st_ctime = ( unsigned long ) prvFileTime( &( xDirEntry.xCreateTime ) );
  950. }
  951. #endif
  952. }
  953. stdioSET_ERRNO( prvFFErrorToErrno( xError ) );
  954. if( FF_isERR( xError ) == pdFALSE )
  955. {
  956. iResult = 0;
  957. }
  958. else
  959. {
  960. iResult = -1;
  961. }
  962. return iResult;
  963. } /* ff_stat() */
  964. /*-----------------------------------------------------------*/
  965. #if ( ffconfigHAS_CWD == 1 )
  966. int ff_chdir( const char * pcDirectoryName )
  967. {
  968. int iResult, iLength, iValid = pdFALSE;
  969. WorkingDirectory_t * pxDir = NULL;
  970. /* Not all paths set an errno. */
  971. stdioSET_ERRNO( 0 );
  972. /* Is there a file system mounted? */
  973. if( FF_FS_Count() != 0 )
  974. {
  975. /* In case a CWD is used, get the absolute path. */
  976. pcDirectoryName = prvABSPath( pcDirectoryName );
  977. pxDir = pxFindCWD();
  978. if( pxDir == NULL )
  979. {
  980. /* Store the errno to thread local storage. */
  981. stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );
  982. /* Return -1 for error as per normal chdir() semantics. */
  983. iResult = -1;
  984. }
  985. else
  986. {
  987. /* The CWD will be stored without a trailing '/'. If "/"
  988. * happens to be the CWD, it will be stored as an empty string. */
  989. iLength = strlen( pcDirectoryName );
  990. /* Knock off the trailing / if one exits - being careful not to
  991. * remove the trailing slash if this is the root directory. */
  992. if( ( iLength > 1 ) && ( pxDir->pcFileName[ iLength - 1 ] == '/' ) )
  993. {
  994. pxDir->pcFileName[ iLength - 1 ] = '\0';
  995. }
  996. stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOENT );
  997. /* Does the directory exist? */
  998. if( strcmp( pcDirectoryName, "/" ) == 0 )
  999. {
  1000. /* Moving to the root - which exists. */
  1001. iValid = pdTRUE;
  1002. }
  1003. else if( ff_finddir( pxDir->pcFileName ) != pdFALSE )
  1004. {
  1005. iValid = pdTRUE;
  1006. }
  1007. }
  1008. }
  1009. if( iValid == pdTRUE )
  1010. {
  1011. /* The generated name becomes the CWD. No need to test for overflow
  1012. * as pcPath and pcFileName are the same size. */
  1013. strcpy( pxDir->pcCWD, pxDir->pcFileName );
  1014. /* chdir returns 0 for success. */
  1015. iResult = FF_ERR_NONE;
  1016. }
  1017. else
  1018. {
  1019. /* Return -1 for error as per normal chdir() semantics. */
  1020. iResult = -1;
  1021. }
  1022. return iResult;
  1023. }
  1024. #endif /* ffconfigHAS_CWD == 1 */
  1025. /*-----------------------------------------------------------*/
  1026. #if ( ffconfigHAS_CWD == 1 )
  1027. char * ff_getcwd( char * pcBuffer,
  1028. size_t xBufferLength )
  1029. {
  1030. WorkingDirectory_t * pxDir = pxFindCWD();
  1031. stdioSET_ERRNO( 0 );
  1032. if( ( pxDir == NULL ) || ( pxDir->pcCWD[ 0 ] == '\0' ) )
  1033. {
  1034. if( xBufferLength > strlen( "/" ) )
  1035. {
  1036. strncpy( pcBuffer, "/", xBufferLength );
  1037. }
  1038. else
  1039. {
  1040. pcBuffer = NULL;
  1041. }
  1042. }
  1043. else
  1044. {
  1045. if( strlen( pxDir->pcCWD ) < xBufferLength )
  1046. {
  1047. strncpy( pcBuffer, pxDir->pcCWD, xBufferLength );
  1048. }
  1049. else
  1050. {
  1051. pcBuffer = NULL;
  1052. }
  1053. }
  1054. return pcBuffer;
  1055. }
  1056. #endif /* ffconfigHAS_CWD */
  1057. /*-----------------------------------------------------------*/
  1058. int ff_findfirst( const char * pcPath,
  1059. FF_FindData_t * pxFindData )
  1060. {
  1061. int iIsRootDir, iReturn;
  1062. const char * pcDirectory;
  1063. iReturn = 0;
  1064. memset( pxFindData, '\0', sizeof( *pxFindData ) );
  1065. pxFindData->pcFileName = pxFindData->xDirectoryEntry.pcFileName;
  1066. /* In case a CWD is used, get the absolute path. */
  1067. pcDirectory = prvABSPath( pcPath );
  1068. if( ( pcDirectory[ 0 ] == '/' ) && ( pcDirectory[ 1 ] == 0x00 ) )
  1069. {
  1070. iIsRootDir = pdTRUE;
  1071. }
  1072. else
  1073. {
  1074. iIsRootDir = pdFALSE;
  1075. }
  1076. /* Find the i/o manager that can handle this path. */
  1077. if( FF_FS_Find( pcDirectory, &( pxFindData->xDirectoryHandler ) ) == pdFALSE )
  1078. {
  1079. if( ( iIsRootDir == pdFALSE ) || ( FF_FS_Count() == 0 ) )
  1080. {
  1081. stdioSET_ERRNO( prvFFErrorToErrno( ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_FINDFIRST ) ) );
  1082. iReturn = -1;
  1083. }
  1084. }
  1085. /* Check no errors before continuing. */
  1086. if( iReturn == 0 )
  1087. {
  1088. #if ( ffconfigDEV_SUPPORT != 0 )
  1089. {
  1090. pxFindData->bIsDeviceDir = xCheckDevicePath( pcDirectory );
  1091. }
  1092. #endif
  1093. if( iIsRootDir != pdFALSE )
  1094. {
  1095. /* A listing of the root directory will include pseudo entries
  1096. * such as /ram /nand. */
  1097. pxFindData->xDirectoryHandler.xFSIndex = FF_FS_Count();
  1098. /* Only add '.' */
  1099. pxFindData->xDirectoryHandler.u.bits.bAddDotEntries = stdioDIR_ENTRY_DOT_1;
  1100. }
  1101. else
  1102. {
  1103. /* This is the root of a sub file system, add "." and ".." */
  1104. pxFindData->xDirectoryHandler.u.bits.bAddDotEntries = stdioDIR_ENTRY_DOT_1 | stdioDIR_ENTRY_DOT_2;
  1105. }
  1106. pxFindData->xDirectoryHandler.u.bits.bIsValid = pdTRUE;
  1107. iReturn = ff_findnext( pxFindData );
  1108. }
  1109. else
  1110. {
  1111. /* errno has already been set. */
  1112. }
  1113. return iReturn;
  1114. }
  1115. /*-----------------------------------------------------------*/
  1116. int ff_findnext( FF_FindData_t * pxFindData )
  1117. {
  1118. FF_Error_t xError;
  1119. #if ( ffconfigTIME_SUPPORT != 0 )
  1120. BaseType_t xSetTime = 0;
  1121. #endif /* ffconfigTIME_SUPPORT */
  1122. if( pxFindData->xDirectoryHandler.u.bits.bIsValid == pdFALSE )
  1123. {
  1124. xError = ( FF_Error_t ) ( FF_ERR_DIR_INVALID_PARAMETER | FF_FINDNEXT );
  1125. FF_PRINTF( "ff_findnext: xDirectoryHandler not valid\n" );
  1126. }
  1127. else
  1128. {
  1129. xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_FINDNEXT );
  1130. if( pxFindData->xDirectoryHandler.pxManager != NULL )
  1131. {
  1132. if( pxFindData->xDirectoryHandler.u.bits.bFirstCalled == pdFALSE )
  1133. {
  1134. pxFindData->xDirectoryHandler.u.bits.bFirstCalled = pdTRUE;
  1135. xError = FF_FindFirst( pxFindData->xDirectoryHandler.pxManager, &( pxFindData->xDirectoryEntry ),
  1136. pxFindData->xDirectoryHandler.pcPath );
  1137. }
  1138. else if( pxFindData->xDirectoryHandler.u.bits.bEndOfDir == pdFALSE )
  1139. {
  1140. xError = FF_FindNext( pxFindData->xDirectoryHandler.pxManager, &( pxFindData->xDirectoryEntry ) );
  1141. }
  1142. if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR )
  1143. {
  1144. /* Stop further calls to FF_FindNext(). */
  1145. pxFindData->xDirectoryHandler.u.bits.bEndOfDir = pdTRUE;
  1146. }
  1147. #if ( ffconfigDEV_SUPPORT != 0 )
  1148. {
  1149. if( pxFindData->bIsDeviceDir != pdFALSE )
  1150. {
  1151. FF_Device_GetDirEnt( pxFindData->xDirectoryHandler.pcPath, &( pxFindData->xDirectoryEntry ) );
  1152. }
  1153. }
  1154. #endif
  1155. }
  1156. if( FF_isERR( xError ) == pdFALSE )
  1157. {
  1158. /* If an entry is found, see if it is a dot-entry. Dot-entries
  1159. * ("." and "..") need a time-stamp. */
  1160. if( pxFindData->xDirectoryEntry.pcFileName[ 0 ] == '.' )
  1161. {
  1162. if( ( pxFindData->xDirectoryEntry.pcFileName[ 1 ] == '.' ) &&
  1163. ( pxFindData->xDirectoryEntry.pcFileName[ 2 ] == '\0' ) )
  1164. {
  1165. /* This is a directory "..". Clear the flag for DOT_2. */
  1166. pxFindData->xDirectoryHandler.u.bits.bAddDotEntries &= stdioDIR_ENTRY_DOT_1;
  1167. #if ( ffconfigTIME_SUPPORT != 0 )
  1168. {
  1169. /* The dot-entries do not have a proper time stamp, add
  1170. * it here. */
  1171. xSetTime = pdTRUE;
  1172. }
  1173. #endif /* ffconfigTIME_SUPPORT */
  1174. }
  1175. else if( pxFindData->xDirectoryEntry.pcFileName[ 1 ] == '\0' )
  1176. {
  1177. /* This is a directory ".". Clear the flag for DOT_1. */
  1178. pxFindData->xDirectoryHandler.u.bits.bAddDotEntries &= stdioDIR_ENTRY_DOT_2;
  1179. #if ( ffconfigTIME_SUPPORT != 0 )
  1180. {
  1181. xSetTime = pdTRUE;
  1182. }
  1183. #endif /* ffconfigTIME_SUPPORT */
  1184. }
  1185. }
  1186. }
  1187. if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR )
  1188. {
  1189. /* No more physical entries were found. Now see if there are FS
  1190. * entries or dot-entries to be added: */
  1191. while( ( pxFindData->xDirectoryHandler.xFSIndex > 0 ) ||
  1192. ( pxFindData->xDirectoryHandler.u.bits.bAddDotEntries != 0 ) )
  1193. {
  1194. if( pxFindData->xDirectoryHandler.xFSIndex > 0 )
  1195. {
  1196. FF_SubSystem_t xSubSystem;
  1197. int found;
  1198. pxFindData->xDirectoryHandler.xFSIndex--;
  1199. found = FF_FS_Get( pxFindData->xDirectoryHandler.xFSIndex, &xSubSystem );
  1200. if( ( found == pdFALSE ) || ( xSubSystem.pcPath[ 1 ] == '\0' ) )
  1201. {
  1202. continue;
  1203. }
  1204. snprintf( pxFindData->xDirectoryEntry.pcFileName, sizeof( pxFindData->xDirectoryEntry.pcFileName ), "%s", xSubSystem.pcPath + 1 );
  1205. if( xSubSystem.pxManager != NULL )
  1206. {
  1207. pxFindData->xDirectoryEntry.ulObjectCluster = xSubSystem.pxManager->xPartition.ulRootDirCluster;
  1208. }
  1209. else
  1210. {
  1211. pxFindData->xDirectoryEntry.ulObjectCluster = 0;
  1212. }
  1213. }
  1214. else if( ( pxFindData->xDirectoryHandler.u.bits.bAddDotEntries & stdioDIR_ENTRY_DOT_2 ) != 0 )
  1215. {
  1216. strcpy( pxFindData->xDirectoryEntry.pcFileName, ".." );
  1217. /* Clear DOT_2 (keep DOT_1). */
  1218. pxFindData->xDirectoryHandler.u.bits.bAddDotEntries &= stdioDIR_ENTRY_DOT_1;
  1219. }
  1220. else
  1221. {
  1222. strcpy( pxFindData->xDirectoryEntry.pcFileName, "." );
  1223. pxFindData->xDirectoryHandler.u.bits.bAddDotEntries = 0;
  1224. }
  1225. pxFindData->xDirectoryEntry.ucAttrib = FF_FAT_ATTR_READONLY | FF_FAT_ATTR_DIR;
  1226. pxFindData->xDirectoryEntry.ulFileSize = stdioDOT_ENTRY_FILE_SIZE;
  1227. #if ( ffconfigTIME_SUPPORT != 0 )
  1228. {
  1229. xSetTime = pdTRUE;
  1230. }
  1231. #endif /* ffconfigTIME_SUPPORT */
  1232. xError = FF_ERR_NONE;
  1233. break;
  1234. }
  1235. }
  1236. #if ( ffconfigTIME_SUPPORT != 0 )
  1237. {
  1238. if( xSetTime != pdFALSE )
  1239. {
  1240. FF_TimeStruct_t xTimeStruct;
  1241. time_t xSeconds;
  1242. xSeconds = FreeRTOS_time( NULL );
  1243. FreeRTOS_gmtime_r( &xSeconds, &xTimeStruct );
  1244. pxFindData->xDirectoryEntry.xCreateTime.Year = ( uint16_t ) ( xTimeStruct.tm_year + 1900 ); /* Year (e.g. 2009). */
  1245. pxFindData->xDirectoryEntry.xCreateTime.Month = ( uint16_t ) ( xTimeStruct.tm_mon + 1 ); /* Month (e.g. 1 = Jan, 12 = Dec). */
  1246. pxFindData->xDirectoryEntry.xCreateTime.Day = ( uint16_t ) xTimeStruct.tm_mday; /* Day (1 - 31). */
  1247. pxFindData->xDirectoryEntry.xCreateTime.Hour = ( uint16_t ) xTimeStruct.tm_hour; /* Hour (0 - 23). */
  1248. pxFindData->xDirectoryEntry.xCreateTime.Minute = ( uint16_t ) xTimeStruct.tm_min; /* Min (0 - 59). */
  1249. pxFindData->xDirectoryEntry.xCreateTime.Second = ( uint16_t ) xTimeStruct.tm_sec; /* Second (0 - 59). */
  1250. pxFindData->xDirectoryEntry.xModifiedTime = pxFindData->xDirectoryEntry.xCreateTime; /* Date and Time Modified. */
  1251. pxFindData->xDirectoryEntry.xAccessedTime = pxFindData->xDirectoryEntry.xCreateTime; /* Date of Last Access. */
  1252. }
  1253. }
  1254. #endif /* ffconfigTIME_SUPPORT */
  1255. if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR )
  1256. {
  1257. /* FF_ERR_DIR_END_OF_DIR will be returned. */
  1258. pxFindData->xDirectoryHandler.u.bits.bIsValid = 0;
  1259. }
  1260. pxFindData->ucAttributes = pxFindData->xDirectoryEntry.ucAttrib;
  1261. pxFindData->ulFileSize = pxFindData->xDirectoryEntry.ulFileSize;
  1262. }
  1263. stdioSET_ERRNO( prvFFErrorToErrno( xError ) );
  1264. return xError;
  1265. }
  1266. /*-----------------------------------------------------------*/
  1267. /*-----------------------------------------------------------
  1268. * ff_isdirempty() returns 1 if a given directory is empty
  1269. * (has no entries)
  1270. *-----------------------------------------------------------*/
  1271. int ff_isdirempty( const char * pcPath )
  1272. {
  1273. FF_DirHandler_t xHandler;
  1274. int iResult;
  1275. /* In case a CWD is used, get the absolute path */
  1276. pcPath = prvABSPath( pcPath );
  1277. /* Find the i/o manager which can handle this path */
  1278. if( FF_FS_Find( pcPath, &xHandler ) == pdFALSE )
  1279. {
  1280. iResult = ( int ) ( FF_ERR_NULL_POINTER | FF_ISDIREMPTY );
  1281. }
  1282. else
  1283. {
  1284. iResult = FF_isDirEmpty( xHandler.pxManager, xHandler.pcPath );
  1285. }
  1286. /* Store the errno to thread local storage. */
  1287. stdioSET_ERRNO( prvFFErrorToErrno( iResult ) );
  1288. return iResult;
  1289. }
  1290. /*-----------------------------------------------------------*/
  1291. #if ( ffconfig64_NUM_SUPPORT != 0 )
  1292. int64_t ff_diskfree( const char * pcPath,
  1293. uint32_t * pxSectorCount )
  1294. #else
  1295. int32_t ff_diskfree( const char * pcPath,
  1296. uint32_t * pxSectorCount )
  1297. #endif
  1298. {
  1299. FF_DirHandler_t xHandler;
  1300. FF_Error_t xError;
  1301. #if ( ffconfig64_NUM_SUPPORT != 0 )
  1302. #define DISKFREE_RETURN_TYPE int64_t
  1303. int64_t lReturn;
  1304. #else
  1305. #define DISKFREE_RETURN_TYPE int32_t
  1306. int32_t lReturn;
  1307. #endif
  1308. if( FF_FS_Find( pcPath, &xHandler ) == pdFALSE )
  1309. {
  1310. /* Return cluster 0 for error. */
  1311. lReturn = 0ul;
  1312. /* Store the errno to thread local storage. */
  1313. stdioSET_ERRNO( pdFREERTOS_ERRNO_ENXIO ); /* No such device or address */
  1314. }
  1315. else
  1316. {
  1317. if( pxSectorCount != NULL )
  1318. {
  1319. *pxSectorCount = xHandler.pxManager->xPartition.ulDataSectors;
  1320. }
  1321. lReturn = ( DISKFREE_RETURN_TYPE ) FF_GetFreeSize( xHandler.pxManager, &xError ) / 512;
  1322. /* Store the errno to thread local storage. */
  1323. stdioSET_ERRNO( prvFFErrorToErrno( xError ) );
  1324. }
  1325. return lReturn;
  1326. }
  1327. /*-----------------------------------------------------------*/
  1328. int ff_finddir( const char * pcPath )
  1329. {
  1330. int iResult;
  1331. FF_DirHandler_t xHandler;
  1332. FF_Error_t errCode;
  1333. if( FF_FS_Find( pcPath, &xHandler ) == pdFALSE )
  1334. {
  1335. /* Return cluster 0 for error. */
  1336. iResult = 0;
  1337. }
  1338. else
  1339. {
  1340. iResult = ( int ) FF_FindDir( xHandler.pxManager, xHandler.pcPath, ( uint16_t ) strlen( xHandler.pcPath ), &errCode );
  1341. }
  1342. return iResult;
  1343. }
  1344. /*-----------------------------------------------------------*/
  1345. size_t ff_filelength( FF_FILE * pxStream )
  1346. {
  1347. FF_Error_t xReturned;
  1348. uint32_t ulLength;
  1349. xReturned = FF_GetFileSize( pxStream, &( ulLength ) );
  1350. if( FF_isERR( xReturned ) != pdFALSE )
  1351. {
  1352. /* An error. */
  1353. ulLength = ( uint32_t ) 0u;
  1354. stdioSET_ERRNO( prvFFErrorToErrno( xReturned ) );
  1355. }
  1356. else
  1357. {
  1358. stdioSET_ERRNO( pdFREERTOS_ERRNO_NONE );
  1359. }
  1360. return ( size_t ) ulLength;
  1361. }
  1362. /*-----------------------------------------------------------*/
  1363. /*-----------------------------------------------------------
  1364. * Delete a directory and, recursively, all of its contents
  1365. *-----------------------------------------------------------*/
  1366. #if ( ffconfigUSE_DELTREE != 0 )
  1367. int ff_deltree( const char * pcDirectory )
  1368. {
  1369. int iResult;
  1370. char * pcPath;
  1371. pcPath = ( char * ) ffconfigMALLOC( ffconfigMAX_FILENAME );
  1372. if( pcPath != NULL )
  1373. {
  1374. /* In case a CWD is used, get the absolute path */
  1375. pcDirectory = prvABSPath( pcDirectory );
  1376. snprintf( pcPath, ffconfigMAX_FILENAME, "%s", pcDirectory );
  1377. /* This recursive function will do all the work */
  1378. iResult = ff_deltree_recurse( pcPath );
  1379. if( iResult >= 0 )
  1380. {
  1381. iResult = ff_rmdir( pcPath );
  1382. if( iResult )
  1383. {
  1384. FF_PRINTF( "ff_deltree(%s): %s\n", pcPath, strerror( stdioGET_ERRNO() ) );
  1385. }
  1386. }
  1387. ffconfigFREE( pcPath );
  1388. }
  1389. else
  1390. {
  1391. iResult = -1;
  1392. stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );
  1393. }
  1394. return iResult;
  1395. }
  1396. #endif /* ffconfigUSE_DELTREE */
  1397. /*-----------------------------------------------------------*/
  1398. #if ( ffconfigUSE_DELTREE != 0 )
  1399. static int ff_deltree_recurse( char * pcPath )
  1400. {
  1401. FF_FindData_t * pxFindData;
  1402. BaseType_t xIsDir, xIsDotDir;
  1403. FF_Error_t xError;
  1404. int iResult, iNext, iNameLength, pass, iCount = 0;
  1405. pxFindData = ( FF_FindData_t * ) ffconfigMALLOC( sizeof( *pxFindData ) );
  1406. if( pxFindData != NULL )
  1407. {
  1408. iNameLength = ( int ) strlen( pcPath );
  1409. /* The directory will be scanned 2 times. First the sub-directories will be
  1410. * entered and their contents deleted. In the second pass the files in the
  1411. * current directory will be removed. In this way 'pcPath' can be constantly
  1412. * used and reused recursively which is cheaper than allocating 'ffconfigMAX_FILENAME'
  1413. * bytes within each recursion. */
  1414. for( pass = 0; pass < 2; pass++ )
  1415. {
  1416. for( iResult = ff_findfirst( pcPath, pxFindData );
  1417. iResult == 0;
  1418. iResult = iNext )
  1419. {
  1420. xIsDir = ( pxFindData->xDirectoryEntry.ucAttrib & FF_FAT_ATTR_DIR ) != 0;
  1421. if( ( pass == 0 ) && ( xIsDir != pdFALSE ) )
  1422. {
  1423. /* This entry is a directory. Don't traverse '.' or '..' */
  1424. xIsDotDir = 0;
  1425. if( pxFindData->pcFileName[ 0 ] == '.' )
  1426. {
  1427. if( ( pxFindData->pcFileName[ 1 ] == '.' ) &&
  1428. ( pxFindData->pcFileName[ 2 ] == '\0' ) )
  1429. {
  1430. xIsDotDir = 2;
  1431. }
  1432. else if( pxFindData->pcFileName[ 1 ] == '\0' )
  1433. {
  1434. xIsDotDir = 1;
  1435. }
  1436. }
  1437. if( xIsDotDir == 0 )
  1438. {
  1439. snprintf( pcPath + iNameLength, ( size_t ) ( ffconfigMAX_FILENAME - iNameLength ), "%s%s",
  1440. pcPath[ iNameLength - 1 ] == '/' ? "" : "/", pxFindData->pcFileName );
  1441. /* Let pxFindData point to the next element before
  1442. * the current will get removed. */
  1443. iNext = ff_findnext( pxFindData );
  1444. /* Remove the contents of this directory. */
  1445. iResult = ff_deltree_recurse( pcPath );
  1446. if( iResult < 0 )
  1447. {
  1448. iCount = -1;
  1449. break;
  1450. }
  1451. iCount += iResult;
  1452. /* remove the directory itself */
  1453. xError = ff_rmdir( pcPath );
  1454. if( xError != 0 )
  1455. {
  1456. FF_PRINTF( "ff_rmdir( %s ): errno %d\n", pcPath, stdioGET_ERRNO() );
  1457. }
  1458. else
  1459. {
  1460. iCount++;
  1461. }
  1462. }
  1463. else
  1464. {
  1465. iNext = ff_findnext( pxFindData );
  1466. }
  1467. }
  1468. else if( ( pass == 1 ) && ( xIsDir == pdFALSE ) )
  1469. {
  1470. snprintf( pcPath + iNameLength, ( size_t ) ( ffconfigMAX_FILENAME - iNameLength ), "%s%s",
  1471. pcPath[ iNameLength - 1 ] == '/' ? "" : "/", pxFindData->pcFileName );
  1472. /* Let pxFindData point to the next element before
  1473. * the current will get removed. */
  1474. iNext = ff_findnext( pxFindData );
  1475. /* Remove a plain file. */
  1476. xError = ff_remove( pcPath );
  1477. if( xError != 0 )
  1478. {
  1479. FF_PRINTF( "ff_remove( %s ): errno %d\n", pcPath, stdioGET_ERRNO() );
  1480. }
  1481. else
  1482. {
  1483. iCount++;
  1484. }
  1485. }
  1486. else
  1487. {
  1488. iNext = ff_findnext( pxFindData );
  1489. }
  1490. pcPath[ iNameLength ] = '\0';
  1491. }
  1492. if( FF_GETERROR( iResult ) == FF_ERR_DIR_INVALID_PATH )
  1493. {
  1494. break;
  1495. }
  1496. if( ( FF_GETERROR( iResult ) != FF_ERR_DIR_END_OF_DIR ) && ( FF_GETERROR( iResult ) != FF_ERR_FILE_INVALID_PATH ) )
  1497. {
  1498. FF_PRINTF( "ff_deltree_recurse[%s]: %s\n", pcPath, ( const char * ) FF_GetErrMessage( iResult ) );
  1499. }
  1500. }
  1501. ffconfigFREE( pxFindData );
  1502. }
  1503. else
  1504. {
  1505. iCount = -1;
  1506. stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );
  1507. }
  1508. return iCount;
  1509. }
  1510. #endif /* ffconfigUSE_DELTREE */
  1511. /*-----------------------------------------------------------*/
  1512. int prvFFErrorToErrno( FF_Error_t xError )
  1513. {
  1514. if( FF_isERR( xError ) == pdFALSE )
  1515. {
  1516. return 0;
  1517. }
  1518. /* Store the last +FAT error code received. */
  1519. stdioSET_FF_ERROR( xError );
  1520. switch( FF_GETERROR( xError ) )
  1521. {
  1522. /* Global Error Codes. */
  1523. case FF_ERR_NONE:
  1524. return 0; /* No Error. */
  1525. case FF_ERR_NULL_POINTER:
  1526. return pdFREERTOS_ERRNO_EBADF; /* pxIOManager was NULL. */
  1527. case FF_ERR_NOT_ENOUGH_MEMORY:
  1528. return pdFREERTOS_ERRNO_ENOMEM; /* malloc() failed! - Could not allocate handle memory. */
  1529. case FF_ERR_DEVICE_DRIVER_FAILED:
  1530. return pdFREERTOS_ERRNO_EIO; /* The Block Device driver reported a FATAL error, cannot continue. */
  1531. /* User return codes for Rd/Wr functions:. */
  1532. case FF_ERR_IOMAN_DRIVER_BUSY:
  1533. return pdFREERTOS_ERRNO_EBUSY; /* 10. */
  1534. case FF_ERR_IOMAN_DRIVER_FATAL_ERROR:
  1535. return pdFREERTOS_ERRNO_EUNATCH; /* Protocol driver not attached. */
  1536. /* IOMAN Error Codes. */
  1537. case FF_ERR_IOMAN_BAD_BLKSIZE:
  1538. return pdFREERTOS_ERRNO_EINVAL; /* The provided blocksize was not a multiple of 512. */
  1539. case FF_ERR_IOMAN_BAD_MEMSIZE:
  1540. return pdFREERTOS_ERRNO_EINVAL; /* The memory size was not a multiple of the blocksize. */
  1541. case FF_ERR_IOMAN_DEV_ALREADY_REGD:
  1542. return pdFREERTOS_ERRNO_EADDRINUSE; /* Device was already registered. Use FF_UnRegister() to re-use this IOMAN with another device. */
  1543. case FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION:
  1544. return pdFREERTOS_ERRNO_ENOMEDIUM; /* A mountable partition could not be found on the device. */
  1545. case FF_ERR_IOMAN_INVALID_FORMAT:
  1546. return pdFREERTOS_ERRNO_EFTYPE; /* The. */
  1547. case FF_ERR_IOMAN_INVALID_PARTITION_NUM:
  1548. return pdFREERTOS_ERRNO_EINVAL; /* The partition number provided was out of range. */
  1549. case FF_ERR_IOMAN_NOT_FAT_FORMATTED:
  1550. return pdFREERTOS_ERRNO_EFTYPE; /* The partition did not look like a FAT partition. */
  1551. case FF_ERR_IOMAN_DEV_INVALID_BLKSIZE:
  1552. return pdFREERTOS_ERRNO_EINVAL; /* IOMAN object BlkSize is not compatible with the blocksize of this device driver. */
  1553. case FF_ERR_IOMAN_PARTITION_MOUNTED:
  1554. return pdFREERTOS_ERRNO_EADDRINUSE; /* Device is in use by an actively mounted partition. Unmount the partition first. */
  1555. case FF_ERR_IOMAN_ACTIVE_HANDLES:
  1556. return pdFREERTOS_ERRNO_EBUSY; /* The partition cannot be unmounted until all active file handles are closed. (There may also be active handles on the cache). */
  1557. case FF_ERR_IOMAN_GPT_HEADER_CORRUPT:
  1558. return pdFREERTOS_ERRNO_EBADE; /* The GPT partition table appears to be corrupt, refusing to mount. */
  1559. case FF_ERR_IOMAN_NOT_ENOUGH_FREE_SPACE:
  1560. return pdFREERTOS_ERRNO_ENOSPC; /* 22. */
  1561. case FF_ERR_IOMAN_OUT_OF_BOUNDS_READ:
  1562. return pdFREERTOS_ERRNO_ESPIPE; /* 23, return 'Illegal seek'. */
  1563. case FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE:
  1564. return pdFREERTOS_ERRNO_ESPIPE; /* 24. */
  1565. case FF_ERR_IOMAN_DRIVER_NOMEDIUM:
  1566. return pdFREERTOS_ERRNO_ENOMEDIUM; /* The medium (e.g. SD-card) has been extracted. */
  1567. /* File Error Codes 30 +. */
  1568. case FF_ERR_FILE_ALREADY_OPEN:
  1569. return pdFREERTOS_ERRNO_EALREADY; /* File is in use. */
  1570. case FF_ERR_FILE_NOT_FOUND:
  1571. return pdFREERTOS_ERRNO_ENOENT; /* File was not found. */
  1572. case FF_ERR_FILE_OBJECT_IS_A_DIR:
  1573. return pdFREERTOS_ERRNO_EISDIR; /* Tried to FF_Open() a Directory. */
  1574. case FF_ERR_FILE_IS_READ_ONLY:
  1575. return pdFREERTOS_ERRNO_EROFS; /* Tried to FF_Open() a file marked read only. */
  1576. case FF_ERR_FILE_INVALID_PATH:
  1577. return pdFREERTOS_ERRNO_ENOTDIR; /* The path of the file was not found. */
  1578. case FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE:
  1579. return pdFREERTOS_ERRNO_EACCES; /* 35. */
  1580. case FF_ERR_FILE_NOT_OPENED_IN_READ_MODE:
  1581. return pdFREERTOS_ERRNO_EACCES; /* 36. */
  1582. case FF_ERR_FILE_EXTEND_FAILED:
  1583. return pdFREERTOS_ERRNO_ENOSPC; /* Could not extend the file. */
  1584. case FF_ERR_FILE_DESTINATION_EXISTS:
  1585. return pdFREERTOS_ERRNO_EEXIST; /* 38. */
  1586. case FF_ERR_FILE_SOURCE_NOT_FOUND:
  1587. return pdFREERTOS_ERRNO_ENOENT; /* 39. */
  1588. case FF_ERR_FILE_DIR_NOT_FOUND:
  1589. return pdFREERTOS_ERRNO_ENOENT; /* 40. */
  1590. case FF_ERR_FILE_COULD_NOT_CREATE_DIRENT:
  1591. return pdFREERTOS_ERRNO_EIO; /* 41. */
  1592. case FF_ERR_FILE_BAD_HANDLE:
  1593. return pdFREERTOS_ERRNO_EBADF; /* A file handle was invalid. */
  1594. case FF_ERR_FILE_MEDIA_REMOVED:
  1595. return pdFREERTOS_ERRNO_ENODEV; /* File handle got invalid because media was removed. */
  1596. case FF_ERR_FILE_SEEK_INVALID_POSITION:
  1597. return pdFREERTOS_ERRNO_ESPIPE; /* Illegal position, outside the file's space */
  1598. case FF_ERR_FILE_SEEK_INVALID_ORIGIN:
  1599. return pdFREERTOS_ERRNO_EINVAL; /* Seeking beyond end of file. */
  1600. /* Directory Error Codes 50 +. */
  1601. case FF_ERR_DIR_OBJECT_EXISTS:
  1602. return pdFREERTOS_ERRNO_EEXIST; /* A file or folder of the same name already exists in the current directory. */
  1603. case FF_ERR_DIR_DIRECTORY_FULL:
  1604. return pdFREERTOS_ERRNO_ENOSPC; /* No more items could be added to the directory. */
  1605. case FF_ERR_DIR_END_OF_DIR:
  1606. return pdFREERTOS_ERRNO_ENMFILE; /*/. */
  1607. case FF_ERR_DIR_NOT_EMPTY:
  1608. return pdFREERTOS_ERRNO_ENOTEMPTY; /* Cannot delete a directory that contains files or folders. */
  1609. case FF_ERR_DIR_INVALID_PATH:
  1610. return pdFREERTOS_ERRNO_EINVAL; /* Could not find the directory specified by the path. */
  1611. case FF_ERR_DIR_CANT_EXTEND_ROOT_DIR:
  1612. return pdFREERTOS_ERRNO_ENOSPC; /* Can't extend the root dir. */
  1613. case FF_ERR_DIR_EXTEND_FAILED:
  1614. return pdFREERTOS_ERRNO_ENOSPC; /* Not enough space to extend the directory. */
  1615. case FF_ERR_DIR_NAME_TOO_LONG:
  1616. return pdFREERTOS_ERRNO_ENAMETOOLONG; /* Name exceeds the number of allowed characters for a filename. */
  1617. /* Fat Error Codes 70 +. */
  1618. case FF_ERR_FAT_NO_FREE_CLUSTERS:
  1619. return pdFREERTOS_ERRNO_ENOSPC; /* No more free space is available on the disk. */
  1620. /* UNICODE Error Codes 100 +. */
  1621. case FF_ERR_UNICODE_INVALID_CODE:
  1622. return pdFREERTOS_ERRNO_EBADE; /* An invalid Unicode character was provided!. */
  1623. case FF_ERR_UNICODE_DEST_TOO_SMALL:
  1624. return pdFREERTOS_ERRNO_ENOBUFS; /* Not enough space in the UTF-16 buffer to encode the entire sequence as UTF-16. */
  1625. case FF_ERR_UNICODE_INVALID_SEQUENCE:
  1626. return pdFREERTOS_ERRNO_EILSEQ; /* An invalid UTF-16 sequence was encountered. */
  1627. case FF_ERR_UNICODE_CONVERSION_EXCEEDED:
  1628. return pdFREERTOS_ERRNO_ENAMETOOLONG; /* Filename exceeds MAX long-filename length when converted to UTF-16. */
  1629. }
  1630. return pdFREERTOS_ERRNO_EFAULT;
  1631. }
  1632. /*-----------------------------------------------------------*/
  1633. #if ( ffconfigHAS_CWD == 1 )
  1634. void ff_free_CWD_space( void )
  1635. {
  1636. WorkingDirectory_t * pxSpace;
  1637. /* Obtain the CWD used by the current task. */
  1638. pxSpace = ( WorkingDirectory_t * ) pvTaskGetThreadLocalStoragePointer( NULL, stdioCWD_THREAD_LOCAL_OFFSET );
  1639. if( pxSpace != NULL )
  1640. {
  1641. vTaskSetThreadLocalStoragePointer( NULL, stdioCWD_THREAD_LOCAL_OFFSET, ( void * ) NULL );
  1642. ffconfigFREE( pxSpace );
  1643. }
  1644. }
  1645. #endif /* ffconfigHAS_CWD */
  1646. /*-----------------------------------------------------------*/
  1647. #if ( ffconfigHAS_CWD == 1 )
  1648. static WorkingDirectory_t * pxFindCWD( void )
  1649. {
  1650. WorkingDirectory_t * pxReturn;
  1651. /* Obtain the CWD used by the current task. */
  1652. pxReturn = ( WorkingDirectory_t * ) pvTaskGetThreadLocalStoragePointer( NULL, stdioCWD_THREAD_LOCAL_OFFSET );
  1653. if( pxReturn == NULL )
  1654. {
  1655. /* This task does not yet have a WorkingDirectory_t structure - create and
  1656. * initialise one now. */
  1657. pxReturn = ( WorkingDirectory_t * ) ffconfigMALLOC( sizeof( WorkingDirectory_t ) );
  1658. if( pxReturn != NULL )
  1659. {
  1660. pxReturn->pcCWD[ 0 ] = '\0';
  1661. vTaskSetThreadLocalStoragePointer( NULL, stdioCWD_THREAD_LOCAL_OFFSET, ( void * ) pxReturn );
  1662. }
  1663. }
  1664. return pxReturn;
  1665. }
  1666. #endif /* ffconfigHAS_CWD */
  1667. /*-----------------------------------------------------------*/
  1668. #if ( ffconfigHAS_CWD == 1 )
  1669. static const char * prvProcessRelativePaths( const char * pcPath )
  1670. {
  1671. const char * pcReturn;
  1672. char * pcChar, * pcTokenStart = NULL, * pcFollowingToken, cPreviousChar = 0x00;
  1673. BaseType_t xByte;
  1674. /* Scan the string looking for a relative path. */
  1675. pcReturn = pcPath;
  1676. pcChar = ( char * ) pcReturn;
  1677. configASSERT( pcPath );
  1678. while( *pcChar != 0x00 )
  1679. {
  1680. if( *pcChar == '.' )
  1681. {
  1682. /* A potential relative path was found. Is this a "." or a "..". */
  1683. if( *( pcChar + 1 ) == '.' )
  1684. {
  1685. /* Nothing can be done if this is at the start of the string. */
  1686. if( pcTokenStart != NULL )
  1687. {
  1688. /* A ".." was found. Where does the next token start? */
  1689. pcFollowingToken = pcChar + 2;
  1690. if( *pcFollowingToken == '/' )
  1691. {
  1692. /* The next token starts after the "../" */
  1693. pcFollowingToken += sizeof( char );
  1694. }
  1695. /* Remove the ".." and the previous token. */
  1696. xByte = 0;
  1697. while( pcFollowingToken[ xByte ] != 0x00 )
  1698. {
  1699. pcTokenStart[ xByte ] = pcFollowingToken[ xByte ];
  1700. xByte++;
  1701. }
  1702. /* Terminate. */
  1703. pcTokenStart[ xByte ] = 0x00;
  1704. /* The pointer to the previous token will now be wrong if
  1705. * there are multiple if "../.." appears in the string. So
  1706. * reset the variables to continue scanning. */
  1707. pcChar = ( char * ) pcReturn;
  1708. cPreviousChar = 0x00;
  1709. pcTokenStart = NULL;
  1710. continue;
  1711. }
  1712. }
  1713. else
  1714. {
  1715. /* A "." was found. Remove it. */
  1716. }
  1717. }
  1718. if( cPreviousChar == '/' )
  1719. {
  1720. /* This is the start of a new token. */
  1721. pcTokenStart = pcChar;
  1722. }
  1723. cPreviousChar = *pcChar;
  1724. pcChar++;
  1725. }
  1726. /* Make sure there is no / on the end of the string, being careful not to
  1727. * remove the / at the beginning of the string. */
  1728. if( *( pcChar - 1 ) == '/' )
  1729. {
  1730. if( ( pcChar - 1 ) != pcReturn )
  1731. {
  1732. *( pcChar - 1 ) = 0x00;
  1733. }
  1734. }
  1735. return pcReturn;
  1736. }
  1737. #endif /* ffconfigHAS_CWD */
  1738. /*-----------------------------------------------------------*/
  1739. #if ( ffconfigHAS_CWD == 1 )
  1740. /*static*/ const char * prvABSPath( const char * pcPath )
  1741. {
  1742. char * pcReturn;
  1743. WorkingDirectory_t * pxWorkingDirectory = pxFindCWD();
  1744. configASSERT( pxWorkingDirectory );
  1745. if( ( pcPath[ 0 ] ) == '/' )
  1746. {
  1747. /* If the path starts with a slash it does not start with a relative
  1748. * path. Copy the string into a thread local buffer so it can be
  1749. * manipulated without risk of attempting to write to read only
  1750. * memory. */
  1751. snprintf( pxWorkingDirectory->pcFileName, sizeof( pxWorkingDirectory->pcFileName ), "%s", pcPath );
  1752. pcReturn = pxWorkingDirectory->pcFileName;
  1753. }
  1754. else
  1755. {
  1756. /* Insert the working directory into the front of the path. */
  1757. if( pxWorkingDirectory->pcCWD[ 1 ] == 0x00 )
  1758. {
  1759. /* In the root, so don't add a '/' between the CWD and the
  1760. * file name. */
  1761. snprintf( pxWorkingDirectory->pcFileName, sizeof( pxWorkingDirectory->pcFileName ), "/%s", pcPath );
  1762. }
  1763. else
  1764. {
  1765. snprintf( pxWorkingDirectory->pcFileName, sizeof( pxWorkingDirectory->pcFileName ), "%s/%s", pxWorkingDirectory->pcCWD, pcPath );
  1766. }
  1767. pcReturn = pxWorkingDirectory->pcFileName;
  1768. }
  1769. /* Make any adjustments necessitated by relative paths. */
  1770. prvProcessRelativePaths( pcReturn );
  1771. return pcReturn;
  1772. }
  1773. #endif /* ffconfigHAS_CWD */
  1774. #if ( ffconfigTIME_SUPPORT == 1 )
  1775. static uint32_t prvFileTime( FF_SystemTime_t * pxTime )
  1776. {
  1777. FF_TimeStruct_t xTime;
  1778. time_t xReturn;
  1779. xTime.tm_sec = pxTime->Second;
  1780. xTime.tm_min = pxTime->Minute;
  1781. xTime.tm_hour = pxTime->Hour;
  1782. xTime.tm_mday = pxTime->Day;
  1783. xTime.tm_mon = pxTime->Month - 1;
  1784. xTime.tm_year = pxTime->Year - 1900;
  1785. xReturn = FreeRTOS_mktime( &xTime );
  1786. return xReturn;
  1787. }
  1788. #endif /* if ( ffconfigTIME_SUPPORT == 1 ) */
  1789. /*-----------------------------------------------------------*/