phatfs.c 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148
  1. /*
  2. * Copyright (C) 2005-2006 by egnite Software GmbH. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  24. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  25. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  27. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. * For additional information see http://www.ethernut.de/
  31. */
  32. /*!
  33. * \file fs/phatfs.c
  34. * \brief PHAT File System.
  35. *
  36. * \verbatim
  37. *
  38. * $Log$
  39. * Revision 1.15 2009/02/13 14:52:05 haraldkipp
  40. * Include memdebug.h for heap management debugging support.
  41. *
  42. * Revision 1.14 2008/08/25 14:26:32 haraldkipp
  43. * Append to existing files failed when file size was larger than one cluster.
  44. *
  45. * Revision 1.13 2008/08/22 16:22:22 olereinhardt
  46. * Check if filesystem is mounted when opening a file.
  47. * Prevents a system lockup because of a nullpointer access
  48. *
  49. * Revision 1.12 2008/08/11 06:59:42 haraldkipp
  50. * BSD types replaced by stdint types (feature request #1282721).
  51. *
  52. * Revision 1.11 2008/04/02 09:39:29 haraldkipp
  53. * Fixed another PHAT file pointer bug.
  54. *
  55. * Revision 1.10 2008/04/01 10:14:58 haraldkipp
  56. * Fixes bug #1903303. Still not fully correct, because fopen(name, "a+")
  57. * should set the read pointer at position 0 while all writes should go
  58. * to the end of the file. But setting the write pointer at the end will
  59. * work for now.
  60. *
  61. * Revision 1.9 2006/10/08 16:42:56 haraldkipp
  62. * Not optimal, but simple and reliable exclusive access implemented.
  63. * Fixes bug #1486539. Furthermore, bug #1567790, which had been rejected,
  64. * had been reported correctly and is now fixed.
  65. *
  66. * Revision 1.8 2006/07/11 12:20:57 haraldkipp
  67. * Added mutual exclusion protection during flush.
  68. * Honor Nut/OS file flushing, which uses a NULL pointer on read/write.
  69. *
  70. * Revision 1.7 2006/07/05 16:02:23 haraldkipp
  71. * Typically Nut/OS doesn't check parameters, but this one is missed often,
  72. * closing a file which points to NUTFILE_EOF.
  73. *
  74. * Revision 1.6 2006/06/18 16:38:28 haraldkipp
  75. * No need to set errno after malloc failed.
  76. * Support for long filenames (VFAT) added.
  77. * New function PhatDirReleaseChain() simplifies code.
  78. * Fixed positioning bug, which caused several problems like limiting
  79. * directories to one cluster.
  80. *
  81. * Revision 1.5 2006/05/15 11:47:18 haraldkipp
  82. * Added support for file seek.
  83. *
  84. * Revision 1.4 2006/03/02 19:59:05 haraldkipp
  85. * Added implementation of dev_size makes _filelength() work, which in turn
  86. * enables the use of these file systems in pro/httpd.c.
  87. *
  88. * Revision 1.3 2006/02/23 15:45:22 haraldkipp
  89. * PHAT file system now supports configurable number of sector buffers.
  90. * This dramatically increased write rates of no-name cards.
  91. * AVR compile errors corrected.
  92. *
  93. * Revision 1.2 2006/01/22 17:42:08 haraldkipp
  94. * Now file delete requests fail if used on directory entries.
  95. *
  96. * Revision 1.1 2006/01/05 16:31:39 haraldkipp
  97. * First check-in.
  98. *
  99. *
  100. * \endverbatim
  101. */
  102. #include <fs/fs.h>
  103. #include <dirent.h>
  104. #include <dev/blockdev.h>
  105. #include <sys/event.h>
  106. #include <fs/phatfs.h>
  107. #include <fs/phatvol.h>
  108. #include <fs/phatio.h>
  109. #include <fs/phatutil.h>
  110. #include <fs/phatdir.h>
  111. #include <stdlib.h>
  112. #include <errno.h>
  113. #include <string.h>
  114. #include <fcntl.h>
  115. #include <memdebug.h>
  116. #if defined(NUTDEBUG)
  117. #include <stdio.h>
  118. #include <fs/phatdbg.h>
  119. #endif
  120. #ifndef SEEK_SET
  121. # define SEEK_SET 0 /* Seek from beginning of file. */
  122. # define SEEK_CUR 1 /* Seek from current position. */
  123. # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
  124. #endif
  125. /*!
  126. * \addtogroup xgPhatFs
  127. */
  128. /*@{*/
  129. /*!
  130. * \brief Search free cluster.
  131. *
  132. * \param dev Specifies the file system device.
  133. * \param first First cluster to check. Must not be lower than 2.
  134. * \param last Last cluster to check.
  135. *
  136. * \return Number of the free cluster found. If an error occured, the
  137. * returned value will be lower than the first cluster searched.
  138. */
  139. static uint32_t SearchFreeCluster(NUTDEVICE * dev, uint32_t first, uint32_t last)
  140. {
  141. int rc = -1;
  142. uint32_t clust;
  143. uint32_t link = 1;
  144. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  145. if (vol->vol_type == 32) {
  146. for (clust = first; clust < last; clust++) {
  147. if ((rc = Phat32GetClusterLink(dev, clust, &link)) < 0 || link == 0) {
  148. break;
  149. }
  150. }
  151. } else if (vol->vol_type == 16) {
  152. for (clust = first; clust < last; clust++) {
  153. if ((rc = Phat16GetClusterLink(dev, clust, &link)) < 0 || link == 0) {
  154. break;
  155. }
  156. }
  157. } else {
  158. for (clust = first; clust < last; clust++) {
  159. if ((rc = Phat12GetClusterLink(dev, clust, &link)) < 0 || link == 0) {
  160. break;
  161. }
  162. }
  163. }
  164. if (rc || link) {
  165. return 0;
  166. }
  167. return clust;
  168. }
  169. /*!
  170. * \brief Allocate a new cluster.
  171. *
  172. * \param dev Specifies the file system device.
  173. *
  174. * \return Number of the allocated cluster, which is not lower than 2.
  175. * Any value lower than 2 indicates an error.
  176. */
  177. static uint32_t AllocCluster(NUTDEVICE * dev)
  178. {
  179. uint32_t clust;
  180. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  181. /*
  182. * If the hint for the next free cluster is invalid, start from the beginning,
  183. */
  184. if (vol->vol_nxtfree < 2 || vol->vol_nxtfree >= vol->vol_last_clust) {
  185. vol->vol_nxtfree = 2;
  186. }
  187. if ((clust = SearchFreeCluster(dev, vol->vol_nxtfree, vol->vol_last_clust)) < 2) {
  188. if ((clust = SearchFreeCluster(dev, 2, vol->vol_nxtfree)) < 2) {
  189. vol->vol_nxtfree = 0;
  190. errno = ENOSPC;
  191. return 0;
  192. }
  193. }
  194. vol->vol_nxtfree = clust;
  195. return clust;
  196. }
  197. /*!
  198. * \brief Allocate the first cluster of a file.
  199. *
  200. * \param nfp The file for which a cluster is allocated.
  201. *
  202. * \return Number of the allocated cluster, which is not lower than 2.
  203. * Any value lower than 2 indicates an error.
  204. */
  205. uint32_t AllocFirstCluster(NUTFILE * nfp)
  206. {
  207. uint32_t clust;
  208. NUTDEVICE *dev = nfp->nf_dev;
  209. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  210. PHATFILE *fcb = nfp->nf_fcb;
  211. if ((clust = AllocCluster(dev)) < 2) {
  212. return 0;
  213. }
  214. /* Set the pointer to the first cluster in out directory entry. */
  215. fcb->f_dirent.dent_clusthi = (uint16_t) (clust >> 16);
  216. fcb->f_dirent.dent_clust = (uint16_t) clust;
  217. fcb->f_de_dirty = 1;
  218. /* The first cluster entry will be set to EOC. */
  219. if (vol->vol_type == 32) {
  220. if (Phat32SetClusterLink(dev, clust, PHAT32CMASK)) {
  221. return 0;
  222. }
  223. } else if (vol->vol_type == 16) {
  224. if (Phat16SetClusterLink(dev, clust, PHAT16CMASK)) {
  225. return 0;
  226. }
  227. } else if (Phat12SetClusterLink(dev, clust, PHAT12CMASK)) {
  228. return 0;
  229. }
  230. vol->vol_numfree--;
  231. return clust;
  232. }
  233. /*!
  234. * \brief Allocate the next cluster of a file.
  235. *
  236. * \param The file for which a cluster is allocated.
  237. *
  238. * \return Number of the allocated cluster, which is not lower than 2.
  239. * Any value lower than 2 indicates an error.
  240. */
  241. static uint32_t AllocNextCluster(NUTFILE * nfp)
  242. {
  243. uint32_t clust;
  244. NUTDEVICE *dev = nfp->nf_dev;
  245. PHATFILE *fcb = nfp->nf_fcb;
  246. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  247. /* Allocate a free cluster. */
  248. if ((clust = AllocCluster(dev)) < 2) {
  249. return 0;
  250. }
  251. /* Link the previous cluster to the new one and set
  252. * the entry of the new one to EOC.
  253. */
  254. if (vol->vol_type == 32) {
  255. if (Phat32SetClusterLink(dev, fcb->f_clust, clust)) {
  256. return 0;
  257. }
  258. if (Phat32SetClusterLink(dev, clust, PHAT32CMASK)) {
  259. return 0;
  260. }
  261. } else if (vol->vol_type == 16) {
  262. if (Phat16SetClusterLink(dev, fcb->f_clust, clust)) {
  263. return 0;
  264. }
  265. if (Phat16SetClusterLink(dev, clust, PHAT16CMASK)) {
  266. return 0;
  267. }
  268. } else if (Phat12SetClusterLink(dev, fcb->f_clust, clust)) {
  269. return 0;
  270. } else if (Phat12SetClusterLink(dev, clust, PHAT12CMASK)) {
  271. return 0;
  272. }
  273. vol->vol_numfree--;
  274. return clust;
  275. }
  276. /*!
  277. * \brief Flush file buffers.
  278. *
  279. * \param nfp Specifies the file.
  280. *
  281. * \return 0 on success, -1 otherwise.
  282. */
  283. static int PhatFileFlush(NUTFILE * nfp)
  284. {
  285. int rc;
  286. NUTDEVICE *dev = nfp->nf_dev;
  287. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  288. /* Update the file's directory entry. */
  289. if ((rc = PhatDirEntryUpdate(nfp)) == 0) {
  290. /* Gain mutex access. */
  291. NutEventWait(&vol->vol_iomutex, 0);
  292. /* Flush sector buffers. */
  293. rc = PhatSectorFlush(nfp->nf_dev, -1);
  294. /* Release mutex access. */
  295. NutEventPost(&vol->vol_iomutex);
  296. }
  297. return rc;
  298. }
  299. /*!
  300. * \brief Close a file.
  301. *
  302. * \param nfp File descriptor.
  303. *
  304. * \return 0 on success, -1 otherwise.
  305. */
  306. int PhatFileClose(NUTFILE * nfp)
  307. {
  308. int rc;
  309. if (nfp == NULL || nfp == NUTFILE_EOF) {
  310. errno = EBADF;
  311. return -1;
  312. }
  313. #ifdef NUTDEBUG
  314. PhatDbgFileInfo(stdout, "Close file", (PHATFILE *) nfp->nf_fcb);
  315. #endif
  316. rc = PhatFileFlush(nfp);
  317. if (nfp->nf_fcb) {
  318. free(nfp->nf_fcb);
  319. }
  320. free(nfp);
  321. return rc;
  322. }
  323. /*!
  324. * \brief Open a file.
  325. *
  326. * This function is called by the low level open routine of the C runtime
  327. * library, using the _NUTDEVICE::dev_open entry.
  328. *
  329. * \param dev Specifies the file system device.
  330. * \param path Pathname of the file to open. If the last character is a
  331. * slash, then a directory will be opened.
  332. * \param mode Operation mode.
  333. * \param acc File attribute.
  334. *
  335. * \return Pointer to a NUTFILE structure if successful or NUTFILE_EOF otherwise.
  336. *
  337. * \bug Append mode not working as expected.
  338. */
  339. NUTFILE *PhatFileOpen(NUTDEVICE * dev, const char *path, int mode, int acc)
  340. {
  341. NUTFILE *nfp = NUTFILE_EOF;
  342. NUTFILE *ndp = NUTFILE_EOF;
  343. PHATFILE *ffcb;
  344. PHATFILE *dfcb;
  345. PHATFIND *srch;
  346. const char *fname;
  347. /* Open the parent directory and return the basename. */
  348. if ((ndp = PhatDirOpenParent(dev, path, &fname)) == NUTFILE_EOF) {
  349. return NUTFILE_EOF;
  350. }
  351. /*
  352. * We successfully opened the directory. If no file name had been specified,
  353. * then the caller wants to open the directory itself. In this case, simply
  354. * return the NUTFILE for it.
  355. */
  356. dfcb = ndp->nf_fcb;
  357. if (*fname == 0) {
  358. dfcb->f_mode = mode;
  359. return ndp;
  360. }
  361. /*
  362. * Allocate a file information and a temporary search structure.
  363. */
  364. nfp = malloc(sizeof(NUTFILE));
  365. ffcb = malloc(sizeof(PHATFILE));
  366. srch = malloc(sizeof(PHATFIND));
  367. if (nfp == NULL || ffcb == NULL || srch == NULL) {
  368. PhatFileClose(ndp);
  369. if (nfp) {
  370. free(nfp);
  371. }
  372. if (ffcb) {
  373. free(ffcb);
  374. }
  375. if (srch) {
  376. free(srch);
  377. }
  378. return NUTFILE_EOF;
  379. }
  380. memset(ffcb, 0, sizeof(PHATFILE));
  381. nfp->nf_dev = dev;
  382. nfp->nf_fcb = ffcb;
  383. /*
  384. * Check if the specified file already exists.
  385. *
  386. * Note, that directories are handled differently in PhatDirCreate(),
  387. * where the first cluster is initialized with zeros.
  388. */
  389. if (PhatDirEntryFind(ndp, fname, PHAT_FATTR_FILEMASK, srch)) {
  390. /*
  391. * File doesn't exist. Does the opening mode allow to create
  392. * a new file?
  393. */
  394. if ((mode & _O_CREAT) == 0) {
  395. free(srch);
  396. PhatFileClose(ndp);
  397. PhatFileClose(nfp);
  398. errno = ENOENT;
  399. return NUTFILE_EOF;
  400. }
  401. /* Create a new directory entry. */
  402. if (PhatDirEntryCreate(ndp, fname, acc, &ffcb->f_dirent)) {
  403. free(srch);
  404. PhatFileClose(ndp);
  405. PhatFileClose(nfp);
  406. return NUTFILE_EOF;
  407. }
  408. ffcb->f_de_dirty = 1;
  409. #ifdef NUTDEBUG
  410. PhatDbgFileInfo(stdout, "New entry", ffcb);
  411. #endif
  412. } else {
  413. /*
  414. * We return an error, if the file exists and _O_EXCL has been
  415. * set with _O_CREAT.
  416. */
  417. if ((mode & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL)) {
  418. free(srch);
  419. PhatFileClose(ndp);
  420. PhatFileClose(nfp);
  421. errno = EEXIST;
  422. return NUTFILE_EOF;
  423. }
  424. #ifdef NUTDEBUG
  425. PhatDbgFileInfo(stdout, "Existing entry", ffcb);
  426. #endif
  427. /*
  428. * Truncate an existing file.
  429. */
  430. if (mode & _O_TRUNC) {
  431. /*
  432. * Relase all clusters allocated by this entry.
  433. */
  434. if (PhatDirReleaseChain(dev, &srch->phfind_ent)) {
  435. PhatFileClose(ndp);
  436. PhatFileClose(nfp);
  437. free(srch);
  438. return NUTFILE_EOF;
  439. }
  440. memset(ffcb, 0, sizeof(PHATFILE));
  441. memcpy(ffcb->f_dirent.dent_name, srch->phfind_ent.dent_name, sizeof(ffcb->f_dirent.dent_name));
  442. ffcb->f_dirent.dent_attr = srch->phfind_ent.dent_attr;
  443. ffcb->f_dirent.dent_rsvdnt = srch->phfind_ent.dent_rsvdnt;
  444. ffcb->f_dirent.dent_ctsecs = srch->phfind_ent.dent_ctsecs;
  445. ffcb->f_dirent.dent_ctime = srch->phfind_ent.dent_ctime;
  446. ffcb->f_dirent.dent_cdate = srch->phfind_ent.dent_cdate;
  447. ffcb->f_de_dirty = 1;
  448. }
  449. else {
  450. ffcb->f_dirent = srch->phfind_ent;
  451. }
  452. }
  453. free(srch);
  454. /* Store position of our directory entry. */
  455. ffcb->f_de_sect = PhatClusterSector(ndp, dfcb->f_clust) + dfcb->f_clust_pos;
  456. ffcb->f_de_offs = dfcb->f_sect_pos - 32;
  457. /* Store first cluster of parent. */
  458. ffcb->f_pde_clusthi = dfcb->f_dirent.dent_clusthi;
  459. ffcb->f_pde_clust = dfcb->f_dirent.dent_clust;
  460. /* Set the current cluster. */
  461. ffcb->f_clust = ffcb->f_dirent.dent_clusthi;
  462. ffcb->f_clust <<= 16;
  463. ffcb->f_clust += ffcb->f_dirent.dent_clust;
  464. /* Store the opening mode. */
  465. ffcb->f_mode = mode;
  466. /* Close the directory. */
  467. PhatFileClose(ndp);
  468. /*
  469. * Append to an existing file.
  470. */
  471. if ((mode & _O_APPEND) != 0 && ffcb->f_dirent.dent_fsize) {
  472. if (PhatFilePosSet(nfp, ffcb->f_dirent.dent_fsize)) {
  473. PhatFileClose(nfp);
  474. return NUTFILE_EOF;
  475. }
  476. }
  477. #ifdef NUTDEBUG
  478. PhatDbgFileInfo(stdout, "File opened", ffcb);
  479. #endif
  480. return nfp;
  481. }
  482. /*!
  483. * \brief Write data to a file.
  484. *
  485. * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
  486. * call to PnutFileOpen().
  487. * \param buffer Pointer to the data to be written. If zero, then the
  488. * output buffer will be flushed.
  489. * \param len Number of bytes to write.
  490. *
  491. * \return The number of bytes written. A return value of -1 indicates an
  492. * error.
  493. */
  494. int PhatFileWrite(NUTFILE * nfp, const void *buffer, int len)
  495. {
  496. int rc;
  497. int step;
  498. uint32_t clust;
  499. int sbn;
  500. uint8_t *buf = (uint8_t *) buffer;
  501. NUTDEVICE *dev = nfp->nf_dev;
  502. PHATFILE *fcb = nfp->nf_fcb;
  503. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  504. /*
  505. * Refuse to write to files with RDONLY attribute set.
  506. */
  507. if (fcb->f_dirent.dent_attr & PHAT_FATTR_RDONLY) {
  508. errno = EACCES;
  509. return -1;
  510. }
  511. /*
  512. * Flush file if buffer is a NULL pointer.
  513. */
  514. if (buf == NULL || len == 0) {
  515. return PhatFileFlush(nfp);
  516. }
  517. /*
  518. * In case of normal files, check for sufficient space.
  519. */
  520. if ((fcb->f_dirent.dent_attr & PHAT_FATTR_DIR) == 0) {
  521. /* Bytes per cluster. */
  522. uint32_t num = vol->vol_sectsz * vol->vol_clustsz;
  523. /* Number of clusters already used. */
  524. uint32_t cur = (fcb->f_dirent.dent_fsize + num - 1) / num;
  525. /* Number of clusters used after writing. */
  526. num = (fcb->f_pos + len + num - 1) / num;
  527. /* If additional clusters are required, are they available? */
  528. if (num > cur && num - cur > vol->vol_numfree) {
  529. errno = ENOSPC;
  530. return -1;
  531. }
  532. /* If the file is empty, allocate the first cluster. */
  533. if (fcb->f_dirent.dent_fsize == 0) {
  534. if ((clust = AllocFirstCluster(nfp)) < 2) {
  535. return -1;
  536. }
  537. fcb->f_clust_prv = clust;
  538. fcb->f_clust = clust;
  539. }
  540. }
  541. /*
  542. * Write the data.
  543. */
  544. for (rc = 0, step = 0; rc < len; rc += step) {
  545. /* Did we reach the end of a sector? */
  546. if (fcb->f_sect_pos >= vol->vol_sectsz) {
  547. /* Move to the next sector within the cluster. */
  548. if (IsFixedRootDir(nfp)) {
  549. if (fcb->f_clust_pos + 1 >= vol->vol_rootsz) {
  550. /* End of root directory, abort writing. */
  551. break;
  552. }
  553. fcb->f_clust_pos++;
  554. }
  555. else {
  556. /* Did we reach the last sector of this cluster? */
  557. if (fcb->f_clust_pos + 1 >= vol->vol_clustsz) {
  558. /* Move to the next cluster. */
  559. if (vol->vol_type == 32) {
  560. if (Phat32GetClusterLink(dev, fcb->f_clust, &clust)) {
  561. rc = -1;
  562. break;
  563. }
  564. if (clust >= (PHATEOC & PHAT32CMASK)) {
  565. if ((clust = AllocNextCluster(nfp)) < 2) {
  566. rc = -1;
  567. break;
  568. }
  569. }
  570. } else if (vol->vol_type == 16) {
  571. if (Phat16GetClusterLink(dev, fcb->f_clust, &clust)) {
  572. rc = -1;
  573. break;
  574. }
  575. if (clust >= (PHATEOC & PHAT16CMASK)) {
  576. if ((clust = AllocNextCluster(nfp)) < 2) {
  577. rc = -1;
  578. break;
  579. }
  580. }
  581. } else if (Phat12GetClusterLink(dev, fcb->f_clust, &clust)) {
  582. rc = -1;
  583. break;
  584. } else if (clust >= (PHATEOC & PHAT12CMASK)) {
  585. if ((clust = AllocNextCluster(nfp)) < 2) {
  586. rc = -1;
  587. break;
  588. }
  589. }
  590. fcb->f_clust_pos = 0;
  591. fcb->f_clust_prv = fcb->f_clust;
  592. fcb->f_clust = clust;
  593. }
  594. else {
  595. fcb->f_clust_pos++;
  596. }
  597. }
  598. fcb->f_sect_pos = 0;
  599. }
  600. /* Write full sectors. */
  601. if (fcb->f_sect_pos == 0 && len - rc >= vol->vol_sectsz) {
  602. uint32_t sect;
  603. int cnt;
  604. step = (int) (vol->vol_clustsz - fcb->f_clust_pos) * vol->vol_sectsz;
  605. if (step > len - rc) {
  606. step = len - rc;
  607. }
  608. sect = PhatClusterSector(nfp, fcb->f_clust) + fcb->f_clust_pos;
  609. /* Calculate number of sectors. */
  610. cnt = step / vol->vol_sectsz;
  611. /* Update step size to sector size multiple. */
  612. step = cnt * vol->vol_sectsz;
  613. if (PhatSectorWrite(nfp->nf_dev, sect, &buf[rc], cnt)) {
  614. rc = -1;
  615. break;
  616. }
  617. /* Advance file pointers. */
  618. fcb->f_clust_pos += cnt - 1;
  619. fcb->f_pos += step;
  620. fcb->f_sect_pos = vol->vol_sectsz;
  621. } else {
  622. /* Load the sector we want to write to. */
  623. if ((sbn = PhatSectorLoad(nfp->nf_dev, PhatClusterSector(nfp, fcb->f_clust) + fcb->f_clust_pos)) < 0) {
  624. rc = -1;
  625. break;
  626. }
  627. /* The number of bytes we write to this sector. */
  628. step = (int) (vol->vol_sectsz - fcb->f_sect_pos);
  629. if (step > len - rc) {
  630. step = len - rc;
  631. }
  632. /* Copy data to this sector. */
  633. memcpy(&vol->vol_buf[sbn].sect_data[fcb->f_sect_pos], &buf[rc], step);
  634. vol->vol_buf[sbn].sect_dirty = 1;
  635. PhatSectorBufferRelease(dev, sbn);
  636. /* Advance file pointers. */
  637. fcb->f_pos += step;
  638. fcb->f_sect_pos += step;
  639. }
  640. }
  641. if (rc > 0) {
  642. /*
  643. * Update directory entry. Note that directory entries of directories
  644. * are never updated in a PHAT file system.
  645. */
  646. if ((fcb->f_dirent.dent_attr & PHAT_FATTR_DIR) == 0) {
  647. GetDosTimeStamp(&fcb->f_dirent.dent_mtime, &fcb->f_dirent.dent_mdate);
  648. fcb->f_dirent.dent_adate = fcb->f_dirent.dent_mdate;
  649. fcb->f_dirent.dent_attr |= PHAT_FATTR_ARCHIV;
  650. if(fcb->f_dirent.dent_fsize < fcb->f_pos) {
  651. fcb->f_dirent.dent_fsize = fcb->f_pos;
  652. }
  653. fcb->f_de_dirty = 1;
  654. }
  655. }
  656. return rc;
  657. }
  658. #ifdef __HARVARD_ARCH__
  659. /*!
  660. * \brief Write data from program space to a file.
  661. *
  662. * This function is not yet implemented and will always return -1.
  663. *
  664. * Similar to PhatFileWrite() except that the data is located in
  665. * program memory.
  666. *
  667. * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
  668. * call to PnutFileOpen().
  669. * \param buffer Pointer to the data in program space. If zero, then the
  670. * output buffer will be flushed.
  671. * \param len Number of bytes to write.
  672. *
  673. * \return The number of bytes written. A return value of -1 indicates an
  674. * error.
  675. */
  676. int PhatFileWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
  677. {
  678. return -1;
  679. }
  680. #endif
  681. /*!
  682. * \brief Read data from a file.
  683. *
  684. * \param nfp Pointer to a ::NUTFILE structure, obtained by a previous
  685. * call to PnutFileOpen().
  686. * \param buffer Pointer to the data buffer to fill.
  687. * \param size Maximum number of bytes to read.
  688. *
  689. * \return The number of bytes actually read. A return value of -1 indicates
  690. * an error.
  691. */
  692. int PhatFileRead(NUTFILE * nfp, void *buffer, int size)
  693. {
  694. int rc;
  695. int step;
  696. int sbn;
  697. uint8_t *buf = (uint8_t *) buffer;
  698. NUTDEVICE *dev = nfp->nf_dev;
  699. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  700. PHATFILE *fcb = nfp->nf_fcb;
  701. /*
  702. * Ignore input flush.
  703. */
  704. if (buf == NULL || size == 0) {
  705. return 0;
  706. }
  707. /* Respect the end of normal files. */
  708. if ((fcb->f_dirent.dent_attr & PHAT_FATTR_DIR) == 0) {
  709. if (fcb->f_pos + size >= fcb->f_dirent.dent_fsize) {
  710. size = fcb->f_dirent.dent_fsize - fcb->f_pos;
  711. }
  712. }
  713. for (rc = 0, step = 0; rc < size; rc += step) {
  714. /* Did we reach the end of a sector? */
  715. if (fcb->f_sect_pos >= vol->vol_sectsz) {
  716. /* Move to the next sector. */
  717. if (IsFixedRootDir(nfp)) {
  718. if (fcb->f_clust_pos + 1 >= vol->vol_rootsz) {
  719. /* End of root directory, abort reading. */
  720. break;
  721. }
  722. fcb->f_clust_pos++;
  723. }
  724. else {
  725. /* Did we reach the last sector of this cluster? */
  726. if (fcb->f_clust_pos + 1 >= vol->vol_clustsz) {
  727. /* Move to the next cluster. */
  728. uint32_t clust;
  729. if (vol->vol_type == 32) {
  730. if (Phat32GetClusterLink(dev, fcb->f_clust, &clust)) {
  731. break;
  732. }
  733. if (clust >= (PHATEOC & PHAT32CMASK)) {
  734. break;
  735. }
  736. } else if (vol->vol_type == 16) {
  737. if (Phat16GetClusterLink(dev, fcb->f_clust, &clust)) {
  738. break;
  739. }
  740. if (clust >= (PHATEOC & PHAT16CMASK)) {
  741. break;
  742. }
  743. } else if (Phat12GetClusterLink(dev, fcb->f_clust, &clust)) {
  744. break;
  745. }
  746. else if (clust >= (PHATEOC & PHAT12CMASK)) {
  747. break;
  748. }
  749. fcb->f_clust_pos = 0;
  750. fcb->f_clust_prv = fcb->f_clust;
  751. fcb->f_clust = clust;
  752. }
  753. else {
  754. fcb->f_clust_pos++;
  755. }
  756. }
  757. fcb->f_sect_pos = 0;
  758. }
  759. /* Make sure that the required sector is loaded. */
  760. if ((sbn = PhatSectorLoad(nfp->nf_dev, PhatClusterSector(nfp, fcb->f_clust) + fcb->f_clust_pos)) < 0) {
  761. rc = -1;
  762. break;
  763. }
  764. step = (int) (vol->vol_sectsz - fcb->f_sect_pos);
  765. if (step > size - rc) {
  766. step = size - rc;
  767. }
  768. memcpy(&buf[rc], &vol->vol_buf[sbn].sect_data[fcb->f_sect_pos], step);
  769. PhatSectorBufferRelease(dev, sbn);
  770. fcb->f_pos += step;
  771. fcb->f_sect_pos += step;
  772. }
  773. return rc;
  774. }
  775. /*!
  776. * \brief Retrieve the size of a previously opened file.
  777. *
  778. * This function is called by the low level size routine of the C runtime
  779. * library, using the _NUTDEVICE::dev_size entry.
  780. *
  781. * \param nfp Pointer to a \ref _NUTFILE structure, obtained by a
  782. * previous call to PhatFileOpen().
  783. *
  784. * \return Size of the file.
  785. */
  786. static long PhatFileSize(NUTFILE *nfp)
  787. {
  788. PHATFILE *fcb = nfp->nf_fcb;
  789. return fcb->f_dirent.dent_fsize;
  790. }
  791. static int PhatFileSeek(NUTFILE * nfp, long *pos, int whence)
  792. {
  793. int rc = 0;
  794. long npos = *pos;
  795. PHATFILE *fcb = nfp->nf_fcb;
  796. switch (whence) {
  797. case SEEK_CUR:
  798. npos += fcb->f_pos;
  799. break;
  800. case SEEK_END:
  801. npos += PhatFileSize(nfp);
  802. break;
  803. }
  804. if (npos < 0 || npos > PhatFileSize(nfp)) {
  805. rc = EINVAL;
  806. } else {
  807. rc = PhatFilePosSet(nfp, npos);
  808. *pos = fcb->f_pos;
  809. }
  810. return rc;
  811. }
  812. /*!
  813. * \brief File system specific functions.
  814. * \param dev Specifies the file system device.
  815. */
  816. static int PhatIOCtl(NUTDEVICE * dev, int req, void *conf)
  817. {
  818. int rc = -1;
  819. switch (req) {
  820. case FS_STATUS:
  821. {
  822. FSCP_STATUS *par = (FSCP_STATUS *) conf;
  823. rc = PhatDirEntryStatus(dev, par->par_path, par->par_stp);
  824. }
  825. break;
  826. case FS_DIR_CREATE:
  827. rc = PhatDirCreate(dev, (char *) conf);
  828. break;
  829. case FS_DIR_REMOVE:
  830. rc = PhatDirRemove(dev, (char *) conf);
  831. break;
  832. case FS_DIR_OPEN:
  833. /* Open a directory for reading entries. */
  834. {
  835. DIR *dir = (DIR *) conf;
  836. if ((dir->dd_fd = PhatDirOpen(dev, dir->dd_buf)) != NUTFILE_EOF) {
  837. rc = 0;
  838. }
  839. }
  840. break;
  841. case FS_DIR_CLOSE:
  842. rc = PhatFileClose(((DIR *) conf)->dd_fd);
  843. break;
  844. case FS_DIR_READ:
  845. rc = PhatDirRead((DIR *) conf);
  846. break;
  847. case FS_FILE_STATUS:
  848. /* TODO */
  849. break;
  850. case FS_FILE_DELETE:
  851. rc = PhatDirDelEntry(dev, (char *) conf, PHAT_FATTR_FILEMASK & ~PHAT_FATTR_DIR);
  852. break;
  853. case FS_FILE_SEEK:
  854. PhatFileSeek((NUTFILE *) ((IOCTL_ARG3 *) conf)->arg1, /* */
  855. (long *) ((IOCTL_ARG3 *) conf)->arg2, /* */
  856. (int) ((IOCTL_ARG3 *) conf)->arg3);
  857. break;
  858. case FS_RENAME:
  859. /* Rename an existing file or directory. */
  860. {
  861. FSCP_RENAME *par = (FSCP_RENAME *) conf;
  862. rc = PhatDirRenameEntry(dev, par->par_old, par->par_new);
  863. }
  864. break;
  865. case FS_VOL_MOUNT:
  866. {
  867. /* Mount a volume. */
  868. FSCP_VOL_MOUNT *par = (FSCP_VOL_MOUNT *) conf;
  869. rc = PhatVolMount(dev, par->fscp_bmnt, par->fscp_part_type);
  870. if (rc) {
  871. /* Release resources on failures. */
  872. PhatVolUnmount(dev);
  873. }
  874. }
  875. break;
  876. case FS_VOL_UNMOUNT:
  877. /* Unmount a volume. */
  878. rc = PhatVolUnmount(dev);
  879. break;
  880. }
  881. return rc;
  882. }
  883. /*!
  884. * \brief Initialize the PHAT file system driver.
  885. *
  886. * This routine is called during device registration.
  887. *
  888. * \param dev Specifies the file system device.
  889. *
  890. * \return Zero on success. Otherwise an error code is returned.
  891. */
  892. static int PhatInit(NUTDEVICE * dev)
  893. {
  894. /* Nothing to do. */
  895. return 0;
  896. }
  897. /*!
  898. * \brief Reentrant variant of PhatFileOpen().
  899. */
  900. static NUTFILE *PhatApiFileOpen(NUTDEVICE * dev, const char *path, int mode, int acc)
  901. {
  902. NUTFILE *rc;
  903. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  904. /* Make sure the volume is mounted. */
  905. if (vol == NULL) {
  906. errno = ENOENT;
  907. return NUTFILE_EOF;
  908. }
  909. /* Lock filesystem access. */
  910. NutEventWait(&vol->vol_fsmutex, 0);
  911. /* Call worker routine. */
  912. rc = PhatFileOpen(dev, path, mode, acc);
  913. /* Release filesystem lock. */
  914. NutEventPost(&vol->vol_fsmutex);
  915. return rc;
  916. }
  917. /*!
  918. * \brief Reentrant variant of PhatFileClose().
  919. */
  920. static int PhatApiFileClose(NUTFILE * nfp)
  921. {
  922. int rc;
  923. NUTDEVICE *dev = nfp->nf_dev;
  924. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  925. /* Lock filesystem access. */
  926. NutEventWait(&vol->vol_fsmutex, 0);
  927. /* Call worker routine. */
  928. rc = PhatFileClose(nfp);
  929. /* Release filesystem lock. */
  930. NutEventPost(&vol->vol_fsmutex);
  931. return rc;
  932. }
  933. /*!
  934. * \brief Reentrant variant of PhatFileWrite().
  935. */
  936. static int PhatApiFileWrite(NUTFILE * nfp, const void *buffer, int len)
  937. {
  938. int rc;
  939. NUTDEVICE *dev = nfp->nf_dev;
  940. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  941. /* Lock filesystem access. */
  942. NutEventWait(&vol->vol_fsmutex, 0);
  943. /* Call worker routine. */
  944. rc = PhatFileWrite(nfp, buffer, len);
  945. /* Release filesystem lock. */
  946. NutEventPost(&vol->vol_fsmutex);
  947. return rc;
  948. }
  949. #ifdef __HARVARD_ARCH__
  950. /*!
  951. * \brief Reentrant variant of PhatFileWrite_P().
  952. */
  953. static int PhatApiFileWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
  954. {
  955. int rc;
  956. NUTDEVICE *dev = nfp->nf_dev;
  957. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  958. /* Lock filesystem access. */
  959. NutEventWait(&vol->vol_fsmutex, 0);
  960. /* Call worker routine. */
  961. rc = PhatFileWrite_P(nfp, buffer, len);
  962. /* Release filesystem lock. */
  963. NutEventPost(&vol->vol_fsmutex);
  964. return rc;
  965. }
  966. #endif
  967. /*!
  968. * \brief Reentrant variant of PhatFileRead().
  969. */
  970. static int PhatApiFileRead(NUTFILE * nfp, void *buffer, int size)
  971. {
  972. int rc;
  973. NUTDEVICE *dev = nfp->nf_dev;
  974. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  975. /* Lock filesystem access. */
  976. NutEventWait(&vol->vol_fsmutex, 0);
  977. /* Call worker routine. */
  978. rc = PhatFileRead(nfp, buffer, size);
  979. /* Release filesystem lock. */
  980. NutEventPost(&vol->vol_fsmutex);
  981. return rc;
  982. }
  983. /*!
  984. * \brief Reentrant variant of PhatIOCtl().
  985. */
  986. static int PhatApiIOCtl(NUTDEVICE * dev, int req, void *conf)
  987. {
  988. int rc;
  989. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  990. /* Lock filesystem access. */
  991. if (req != FS_VOL_MOUNT && vol) {
  992. NutEventWait(&vol->vol_fsmutex, 0);
  993. }
  994. /* Call worker routine. */
  995. rc = PhatIOCtl(dev, req, conf);
  996. /* Release filesystem lock. */
  997. if (req != FS_VOL_MOUNT && req != FS_VOL_UNMOUNT && vol) {
  998. NutEventPost(&vol->vol_fsmutex);
  999. }
  1000. return rc;
  1001. }
  1002. /*!
  1003. * \brief PHAT file system driver information structure.
  1004. *
  1005. * A pointer to this structure must be passed to NutRegisterDevice()
  1006. * to bind this file system driver to the Nut/OS kernel.
  1007. * An application may then call
  1008. * /verbatim
  1009. * _open("MMC0:1/PHAT0", _O_RDWR | _O_BINARY);
  1010. * /endverbatim
  1011. * to mount partition 1 on the previously registered block
  1012. * device (devMmc0 in this example).
  1013. */
  1014. NUTDEVICE devPhat0 = {
  1015. 0, /*!< Pointer to next device, dev_next. */
  1016. {'P', 'H', 'A', 'T', '0', 0, 0, 0, 0}
  1017. , /*!< Unique device name, dev_name. */
  1018. IFTYP_FS, /*!< Type of device, dev_type. Obsolete. */
  1019. 0, /*!< Base address, dev_base. Unused. */
  1020. 0, /*!< First interrupt number, dev_irq. Unused. */
  1021. 0, /*!< Mounted block device partition, dev_icb. */
  1022. 0, /*!< Volume information, dev_dcb. */
  1023. PhatInit, /*!< Driver initialization routine, dev_init. */
  1024. PhatApiIOCtl, /*!< Driver specific control function, dev_ioctl. */
  1025. PhatApiFileRead, /*!< Read data from a file, dev_read. */
  1026. PhatApiFileWrite, /*!< Write data to a file, dev_write. */
  1027. #ifdef __HARVARD_ARCH__
  1028. PhatApiFileWrite_P, /*!< Write data from program space to a file, dev_write_P. */
  1029. #endif
  1030. PhatApiFileOpen, /*!< Open a file, dev_open. */
  1031. PhatApiFileClose, /*!< Close a file, dev_close. */
  1032. PhatFileSize, /*!< Return file size, dev_size. */
  1033. NULL, /*!< Select function, optional, not yet implemented */
  1034. };
  1035. NUTDEVICE devPhat1 = {
  1036. 0, /*!< Pointer to next device, dev_next. */
  1037. {'P', 'H', 'A', 'T', '1', 0, 0, 0, 0}
  1038. , /*!< Unique device name, dev_name. */
  1039. IFTYP_FS, /*!< Type of device, dev_type. Obsolete. */
  1040. 0, /*!< Base address, dev_base. Unused. */
  1041. 0, /*!< First interrupt number, dev_irq. Unused. */
  1042. 0, /*!< Mounted block device partition, dev_icb. */
  1043. 0, /*!< Volume information, dev_dcb. */
  1044. PhatInit, /*!< Driver initialization routine, dev_init. */
  1045. PhatApiIOCtl, /*!< Driver specific control function, dev_ioctl. */
  1046. PhatApiFileRead, /*!< Read data from a file, dev_read. */
  1047. PhatApiFileWrite, /*!< Write data to a file, dev_write. */
  1048. #ifdef __HARVARD_ARCH__
  1049. PhatApiFileWrite_P, /*!< Write data from program space to a file, dev_write_P. */
  1050. #endif
  1051. PhatApiFileOpen, /*!< Open a file, dev_open. */
  1052. PhatApiFileClose, /*!< Close a file, dev_close. */
  1053. PhatFileSize, /*!< Return file size, dev_size. */
  1054. NULL, /*!< Select function, optional, not yet implemented */
  1055. };
  1056. /*@}*/