nxffs_read.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. /****************************************************************************
  2. * fs/nxffs/nxffs_read.c
  3. *
  4. * Copyright (C) 2011, 2013, 2017-2018 Gregory Nutt. All rights reserved.
  5. * Author: Gregory Nutt <gnutt@nuttx.org>
  6. *
  7. * References: Linux/Documentation/filesystems/romfs.txt
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. *
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. * 3. Neither the name NuttX nor the names of its contributors may be
  20. * used to endorse or promote products derived from this software
  21. * without specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  24. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  25. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  26. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  27. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  28. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  29. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  30. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  31. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  33. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  34. * POSSIBILITY OF SUCH DAMAGE.
  35. *
  36. ****************************************************************************/
  37. /****************************************************************************
  38. * Included Files
  39. ****************************************************************************/
  40. #include <nuttx/config.h>
  41. #include <string.h>
  42. #include <fcntl.h>
  43. #include <crc32.h>
  44. #include <assert.h>
  45. #include <errno.h>
  46. #include <debug.h>
  47. #include <nuttx/semaphore.h>
  48. #include <nuttx/fs/fs.h>
  49. #include <nuttx/mtd/mtd.h>
  50. #include "nxffs.h"
  51. /****************************************************************************
  52. * Private Functions
  53. ****************************************************************************/
  54. /****************************************************************************
  55. * Name: nxffs_rdseek
  56. *
  57. * Description:
  58. * Seek to the file position before read or write access. Note that the
  59. * simplier nxffs_ioseek() cannot be used for this purpose. File offsets
  60. * are not easily mapped to FLASH offsets due to intervening block and
  61. * data headers.
  62. *
  63. * Input Parameters:
  64. * volume - Describes the current volume
  65. * entry - Describes the open inode
  66. * fpos - The desired file position
  67. * blkentry - Describes the block entry that we are positioned in
  68. *
  69. ****************************************************************************/
  70. static ssize_t nxffs_rdseek(FAR struct nxffs_volume_s *volume,
  71. FAR struct nxffs_entry_s *entry,
  72. off_t fpos,
  73. FAR struct nxffs_blkentry_s *blkentry)
  74. {
  75. size_t datstart;
  76. size_t datend;
  77. off_t offset;
  78. int ret;
  79. /* The initial FLASH offset will be the offset to first data block of
  80. * the inode
  81. */
  82. offset = entry->doffset;
  83. if (offset == 0)
  84. {
  85. /* Zero length files will have no data blocks */
  86. return -ENOSPC;
  87. }
  88. /* Loop until we read the data block containing the desired position */
  89. datend = 0;
  90. do
  91. {
  92. /* Check if the next data block contains the sought after file position */
  93. ret = nxffs_nextblock(volume, offset, blkentry);
  94. if (ret < 0)
  95. {
  96. ferr("ERROR: nxffs_nextblock failed: %d\n", -ret);
  97. return ret;
  98. }
  99. /* Get the range of data offsets for this data block */
  100. datstart = datend;
  101. datend += blkentry->datlen;
  102. /* Offset to search for the next data block */
  103. offset = blkentry->hoffset + SIZEOF_NXFFS_DATA_HDR + blkentry->datlen;
  104. }
  105. while (datend <= fpos);
  106. /* Return the offset to the data within the current data block */
  107. blkentry->foffset = fpos - datstart;
  108. nxffs_ioseek(volume, blkentry->hoffset + SIZEOF_NXFFS_DATA_HDR + blkentry->foffset);
  109. return OK;
  110. }
  111. /****************************************************************************
  112. * Public Functions
  113. ****************************************************************************/
  114. /****************************************************************************
  115. * Name: nxffs_read
  116. *
  117. * Description:
  118. * This is an implementation of the NuttX standard file system read
  119. * method.
  120. *
  121. ****************************************************************************/
  122. ssize_t nxffs_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
  123. {
  124. FAR struct nxffs_volume_s *volume;
  125. FAR struct nxffs_ofile_s *ofile;
  126. struct nxffs_blkentry_s blkentry;
  127. ssize_t total;
  128. size_t available;
  129. size_t readsize;
  130. int ret;
  131. finfo("Read %d bytes from offset %d\n", buflen, filep->f_pos);
  132. /* Sanity checks */
  133. DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
  134. /* Recover the open file state from the struct file instance */
  135. ofile = (FAR struct nxffs_ofile_s *)filep->f_priv;
  136. /* Recover the volume state from the open file */
  137. volume = (FAR struct nxffs_volume_s *)filep->f_inode->i_private;
  138. DEBUGASSERT(volume != NULL);
  139. /* Get exclusive access to the volume. Note that the volume exclsem
  140. * protects the open file list.
  141. */
  142. ret = nxsem_wait(&volume->exclsem);
  143. if (ret < 0)
  144. {
  145. ferr("ERROR: nxsem_wait failed: %d\n", ret);
  146. goto errout;
  147. }
  148. /* Check if the file was opened with read access */
  149. if ((ofile->oflags & O_RDOK) == 0)
  150. {
  151. ferr("ERROR: File not open for read access\n");
  152. ret = -EACCES;
  153. goto errout_with_semaphore;
  154. }
  155. /* Loop until all bytes have been read */
  156. for (total = 0; total < buflen; )
  157. {
  158. /* Don't seek past the end of the file */
  159. if (filep->f_pos >= ofile->entry.datlen)
  160. {
  161. /* Return the partial read */
  162. filep->f_pos = ofile->entry.datlen;
  163. break;
  164. }
  165. /* Seek to the current file offset */
  166. ret = nxffs_rdseek(volume, &ofile->entry, filep->f_pos, &blkentry);
  167. if (ret < 0)
  168. {
  169. ferr("ERROR: nxffs_rdseek failed: %d\n", -ret);
  170. ret = -EACCES;
  171. goto errout_with_semaphore;
  172. }
  173. /* How many bytes are available at this offset */
  174. available = blkentry.datlen - blkentry.foffset;
  175. /* Don't read more than we need to */
  176. readsize = buflen - total;
  177. if (readsize > available)
  178. {
  179. readsize = available;
  180. }
  181. /* Read data from that file offset */
  182. memcpy(&buffer[total], &volume->cache[volume->iooffset], readsize);
  183. /* Update the file offset */
  184. filep->f_pos += readsize;
  185. total += readsize;
  186. }
  187. nxsem_post(&volume->exclsem);
  188. return total;
  189. errout_with_semaphore:
  190. nxsem_post(&volume->exclsem);
  191. errout:
  192. return (ssize_t)ret;
  193. }
  194. /****************************************************************************
  195. * Name: nxffs_nextblock
  196. *
  197. * Description:
  198. * Search for the next valid data block starting at the provided
  199. * FLASH offset.
  200. *
  201. * Input Parameters:
  202. * volume - Describes the NXFFS volume.
  203. * datlen - A memory location to return the data block length.
  204. *
  205. * Returned Value:
  206. * Zero is returned on success. Otherwise, a negated errno is returned
  207. * that indicates the nature of the failure.
  208. *
  209. ****************************************************************************/
  210. int nxffs_nextblock(FAR struct nxffs_volume_s *volume, off_t offset,
  211. FAR struct nxffs_blkentry_s *blkentry)
  212. {
  213. int nmagic;
  214. int ch;
  215. int nerased;
  216. int ret;
  217. /* Seek to the first FLASH offset provided by the caller. */
  218. nxffs_ioseek(volume, offset);
  219. /* Skip the block header */
  220. if (volume->iooffset < SIZEOF_NXFFS_BLOCK_HDR)
  221. {
  222. volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR;
  223. }
  224. /* Then begin searching */
  225. nerased = 0;
  226. nmagic = 0;
  227. for (; ; )
  228. {
  229. /* Read the next character */
  230. ch = nxffs_getc(volume, SIZEOF_NXFFS_DATA_HDR - nmagic);
  231. if (ch < 0)
  232. {
  233. ferr("ERROR: nxffs_getc failed: %d\n", -ch);
  234. return ch;
  235. }
  236. /* Check for another erased byte */
  237. else if (ch == CONFIG_NXFFS_ERASEDSTATE)
  238. {
  239. /* If we have encountered NXFFS_NERASED number of consecutive
  240. * erased bytes, then presume we have reached the end of valid
  241. * data.
  242. */
  243. if (++nerased >= NXFFS_NERASED)
  244. {
  245. finfo("No entry found\n");
  246. return -ENOENT;
  247. }
  248. }
  249. else
  250. {
  251. nerased = 0;
  252. /* Check for the magic sequence indicating the start of an NXFFS
  253. * data block or start of the next inode. There is the possibility
  254. * of this magic sequnce occurring in FLASH data. However, the
  255. * data block CRC should distinguish between real NXFFS data blocks
  256. * headers and such false alarms.
  257. */
  258. if (ch != g_datamagic[nmagic])
  259. {
  260. /* Ooops... this is the not the right character for the magic
  261. * Sequence. Check if we need to restart or to cancel the sequence:
  262. */
  263. if (ch == g_datamagic[0])
  264. {
  265. nmagic = 1;
  266. }
  267. else
  268. {
  269. nmagic = 0;
  270. }
  271. }
  272. else if (nmagic < NXFFS_MAGICSIZE - 1)
  273. {
  274. /* We have one more character in the magic sequence */
  275. nmagic++;
  276. }
  277. /* We have found the magic sequence in the FLASH data that may
  278. * indicate the beginning of an NXFFS data block.
  279. */
  280. else
  281. {
  282. /* The offset to the header must be 4 bytes before the current
  283. * FLASH seek location.
  284. */
  285. blkentry->hoffset = nxffs_iotell(volume) - NXFFS_MAGICSIZE;
  286. /* Read the block header and verify the block at that address */
  287. ret = nxffs_rdblkhdr(volume, blkentry->hoffset, &blkentry->datlen);
  288. if (ret == OK)
  289. {
  290. finfo("Found a valid data block, offset: %d datlen: %d\n",
  291. blkentry->hoffset, blkentry->datlen);
  292. return OK;
  293. }
  294. /* False alarm.. Restore the volume cache position (that was
  295. * destroyed by nxfs_rdblkhdr()) and keep looking.
  296. */
  297. nxffs_ioseek(volume, blkentry->hoffset + NXFFS_MAGICSIZE);
  298. nmagic = 0;
  299. }
  300. }
  301. }
  302. /* We won't get here, but to keep some compilers happy: */
  303. return -ENOENT;
  304. }
  305. /****************************************************************************
  306. * Name: nxffs_rdblkhdr
  307. *
  308. * Description:
  309. * Read and verify the data block header at the specified offset.
  310. *
  311. * Input Parameters:
  312. * volume - Describes the current volume.
  313. * offset - The byte offset from the beginning of FLASH where the data block
  314. * header is expected.
  315. * datlen - A memory location to return the data block length.
  316. *
  317. * Returned Value:
  318. * Zero on success. Otherwise, a negated errno value is returned
  319. * indicating the nature of the failure.
  320. *
  321. ****************************************************************************/
  322. int nxffs_rdblkhdr(FAR struct nxffs_volume_s *volume, off_t offset,
  323. FAR uint16_t *datlen)
  324. {
  325. struct nxffs_data_s blkhdr;
  326. uint32_t ecrc;
  327. uint32_t crc;
  328. uint16_t doffset;
  329. uint16_t dlen;
  330. int ret;
  331. /* Make sure that the block containing the data block header is in the cache */
  332. nxffs_ioseek(volume, offset);
  333. ret = nxffs_rdcache(volume, volume->ioblock);
  334. if (ret < 0)
  335. {
  336. ferr("ERROR: Failed to read data into cache: %d\n", ret);
  337. return ret;
  338. }
  339. /* Read the header at the FLASH offset */
  340. doffset = volume->iooffset;
  341. memcpy(&blkhdr, &volume->cache[doffset], SIZEOF_NXFFS_DATA_HDR);
  342. /* Extract the data length */
  343. dlen = nxffs_rdle16(blkhdr.datlen);
  344. /* Get the offset to the beginning of the data */
  345. doffset += SIZEOF_NXFFS_DATA_HDR;
  346. /* Make sure that all of the data fits within the block */
  347. if ((uint32_t)doffset + (uint32_t)dlen > (uint32_t)volume->geo.blocksize)
  348. {
  349. ferr("ERROR: Data length=%d is unreasonable at offset=%d\n", dlen, doffset);
  350. return -EIO;
  351. }
  352. /* Extract the expected CRC and calculate the CRC of the data block */
  353. ecrc = nxffs_rdle32(blkhdr.crc);
  354. nxffs_wrle32(blkhdr.crc, 0);
  355. crc = crc32((FAR const uint8_t *)&blkhdr, SIZEOF_NXFFS_DATA_HDR);
  356. crc = crc32part(&volume->cache[doffset], dlen, crc);
  357. if (crc != ecrc)
  358. {
  359. ferr("ERROR: CRC failure\n");
  360. return -EIO;
  361. }
  362. /* Looks good! Return the data length and success */
  363. *datlen = dlen;
  364. return OK;
  365. }