123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- /****************************************************************************
- * fs/nxffs/nxffs_read.c
- *
- * Copyright (C) 2011, 2013, 2017-2018 Gregory Nutt. All rights reserved.
- * Author: Gregory Nutt <gnutt@nuttx.org>
- *
- * References: Linux/Documentation/filesystems/romfs.txt
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the name NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ****************************************************************************/
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <nuttx/config.h>
- #include <string.h>
- #include <fcntl.h>
- #include <crc32.h>
- #include <assert.h>
- #include <errno.h>
- #include <debug.h>
- #include <nuttx/semaphore.h>
- #include <nuttx/fs/fs.h>
- #include <nuttx/mtd/mtd.h>
- #include "nxffs.h"
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: nxffs_rdseek
- *
- * Description:
- * Seek to the file position before read or write access. Note that the
- * simplier nxffs_ioseek() cannot be used for this purpose. File offsets
- * are not easily mapped to FLASH offsets due to intervening block and
- * data headers.
- *
- * Input Parameters:
- * volume - Describes the current volume
- * entry - Describes the open inode
- * fpos - The desired file position
- * blkentry - Describes the block entry that we are positioned in
- *
- ****************************************************************************/
- static ssize_t nxffs_rdseek(FAR struct nxffs_volume_s *volume,
- FAR struct nxffs_entry_s *entry,
- off_t fpos,
- FAR struct nxffs_blkentry_s *blkentry)
- {
- size_t datstart;
- size_t datend;
- off_t offset;
- int ret;
- /* The initial FLASH offset will be the offset to first data block of
- * the inode
- */
- offset = entry->doffset;
- if (offset == 0)
- {
- /* Zero length files will have no data blocks */
- return -ENOSPC;
- }
- /* Loop until we read the data block containing the desired position */
- datend = 0;
- do
- {
- /* Check if the next data block contains the sought after file position */
- ret = nxffs_nextblock(volume, offset, blkentry);
- if (ret < 0)
- {
- ferr("ERROR: nxffs_nextblock failed: %d\n", -ret);
- return ret;
- }
- /* Get the range of data offsets for this data block */
- datstart = datend;
- datend += blkentry->datlen;
- /* Offset to search for the next data block */
- offset = blkentry->hoffset + SIZEOF_NXFFS_DATA_HDR + blkentry->datlen;
- }
- while (datend <= fpos);
- /* Return the offset to the data within the current data block */
- blkentry->foffset = fpos - datstart;
- nxffs_ioseek(volume, blkentry->hoffset + SIZEOF_NXFFS_DATA_HDR + blkentry->foffset);
- return OK;
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: nxffs_read
- *
- * Description:
- * This is an implementation of the NuttX standard file system read
- * method.
- *
- ****************************************************************************/
- ssize_t nxffs_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
- {
- FAR struct nxffs_volume_s *volume;
- FAR struct nxffs_ofile_s *ofile;
- struct nxffs_blkentry_s blkentry;
- ssize_t total;
- size_t available;
- size_t readsize;
- int ret;
- finfo("Read %d bytes from offset %d\n", buflen, filep->f_pos);
- /* Sanity checks */
- DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
- /* Recover the open file state from the struct file instance */
- ofile = (FAR struct nxffs_ofile_s *)filep->f_priv;
- /* Recover the volume state from the open file */
- volume = (FAR struct nxffs_volume_s *)filep->f_inode->i_private;
- DEBUGASSERT(volume != NULL);
- /* Get exclusive access to the volume. Note that the volume exclsem
- * protects the open file list.
- */
- ret = nxsem_wait(&volume->exclsem);
- if (ret < 0)
- {
- ferr("ERROR: nxsem_wait failed: %d\n", ret);
- goto errout;
- }
- /* Check if the file was opened with read access */
- if ((ofile->oflags & O_RDOK) == 0)
- {
- ferr("ERROR: File not open for read access\n");
- ret = -EACCES;
- goto errout_with_semaphore;
- }
- /* Loop until all bytes have been read */
- for (total = 0; total < buflen; )
- {
- /* Don't seek past the end of the file */
- if (filep->f_pos >= ofile->entry.datlen)
- {
- /* Return the partial read */
- filep->f_pos = ofile->entry.datlen;
- break;
- }
- /* Seek to the current file offset */
- ret = nxffs_rdseek(volume, &ofile->entry, filep->f_pos, &blkentry);
- if (ret < 0)
- {
- ferr("ERROR: nxffs_rdseek failed: %d\n", -ret);
- ret = -EACCES;
- goto errout_with_semaphore;
- }
- /* How many bytes are available at this offset */
- available = blkentry.datlen - blkentry.foffset;
- /* Don't read more than we need to */
- readsize = buflen - total;
- if (readsize > available)
- {
- readsize = available;
- }
- /* Read data from that file offset */
- memcpy(&buffer[total], &volume->cache[volume->iooffset], readsize);
- /* Update the file offset */
- filep->f_pos += readsize;
- total += readsize;
- }
- nxsem_post(&volume->exclsem);
- return total;
- errout_with_semaphore:
- nxsem_post(&volume->exclsem);
- errout:
- return (ssize_t)ret;
- }
- /****************************************************************************
- * Name: nxffs_nextblock
- *
- * Description:
- * Search for the next valid data block starting at the provided
- * FLASH offset.
- *
- * Input Parameters:
- * volume - Describes the NXFFS volume.
- * datlen - A memory location to return the data block length.
- *
- * Returned Value:
- * Zero is returned on success. Otherwise, a negated errno is returned
- * that indicates the nature of the failure.
- *
- ****************************************************************************/
- int nxffs_nextblock(FAR struct nxffs_volume_s *volume, off_t offset,
- FAR struct nxffs_blkentry_s *blkentry)
- {
- int nmagic;
- int ch;
- int nerased;
- int ret;
- /* Seek to the first FLASH offset provided by the caller. */
- nxffs_ioseek(volume, offset);
- /* Skip the block header */
- if (volume->iooffset < SIZEOF_NXFFS_BLOCK_HDR)
- {
- volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR;
- }
- /* Then begin searching */
- nerased = 0;
- nmagic = 0;
- for (; ; )
- {
- /* Read the next character */
- ch = nxffs_getc(volume, SIZEOF_NXFFS_DATA_HDR - nmagic);
- if (ch < 0)
- {
- ferr("ERROR: nxffs_getc failed: %d\n", -ch);
- return ch;
- }
- /* Check for another erased byte */
- else if (ch == CONFIG_NXFFS_ERASEDSTATE)
- {
- /* If we have encountered NXFFS_NERASED number of consecutive
- * erased bytes, then presume we have reached the end of valid
- * data.
- */
- if (++nerased >= NXFFS_NERASED)
- {
- finfo("No entry found\n");
- return -ENOENT;
- }
- }
- else
- {
- nerased = 0;
- /* Check for the magic sequence indicating the start of an NXFFS
- * data block or start of the next inode. There is the possibility
- * of this magic sequnce occurring in FLASH data. However, the
- * data block CRC should distinguish between real NXFFS data blocks
- * headers and such false alarms.
- */
- if (ch != g_datamagic[nmagic])
- {
- /* Ooops... this is the not the right character for the magic
- * Sequence. Check if we need to restart or to cancel the sequence:
- */
- if (ch == g_datamagic[0])
- {
- nmagic = 1;
- }
- else
- {
- nmagic = 0;
- }
- }
- else if (nmagic < NXFFS_MAGICSIZE - 1)
- {
- /* We have one more character in the magic sequence */
- nmagic++;
- }
- /* We have found the magic sequence in the FLASH data that may
- * indicate the beginning of an NXFFS data block.
- */
- else
- {
- /* The offset to the header must be 4 bytes before the current
- * FLASH seek location.
- */
- blkentry->hoffset = nxffs_iotell(volume) - NXFFS_MAGICSIZE;
- /* Read the block header and verify the block at that address */
- ret = nxffs_rdblkhdr(volume, blkentry->hoffset, &blkentry->datlen);
- if (ret == OK)
- {
- finfo("Found a valid data block, offset: %d datlen: %d\n",
- blkentry->hoffset, blkentry->datlen);
- return OK;
- }
- /* False alarm.. Restore the volume cache position (that was
- * destroyed by nxfs_rdblkhdr()) and keep looking.
- */
- nxffs_ioseek(volume, blkentry->hoffset + NXFFS_MAGICSIZE);
- nmagic = 0;
- }
- }
- }
- /* We won't get here, but to keep some compilers happy: */
- return -ENOENT;
- }
- /****************************************************************************
- * Name: nxffs_rdblkhdr
- *
- * Description:
- * Read and verify the data block header at the specified offset.
- *
- * Input Parameters:
- * volume - Describes the current volume.
- * offset - The byte offset from the beginning of FLASH where the data block
- * header is expected.
- * datlen - A memory location to return the data block length.
- *
- * Returned Value:
- * Zero on success. Otherwise, a negated errno value is returned
- * indicating the nature of the failure.
- *
- ****************************************************************************/
- int nxffs_rdblkhdr(FAR struct nxffs_volume_s *volume, off_t offset,
- FAR uint16_t *datlen)
- {
- struct nxffs_data_s blkhdr;
- uint32_t ecrc;
- uint32_t crc;
- uint16_t doffset;
- uint16_t dlen;
- int ret;
- /* Make sure that the block containing the data block header is in the cache */
- nxffs_ioseek(volume, offset);
- ret = nxffs_rdcache(volume, volume->ioblock);
- if (ret < 0)
- {
- ferr("ERROR: Failed to read data into cache: %d\n", ret);
- return ret;
- }
- /* Read the header at the FLASH offset */
- doffset = volume->iooffset;
- memcpy(&blkhdr, &volume->cache[doffset], SIZEOF_NXFFS_DATA_HDR);
- /* Extract the data length */
- dlen = nxffs_rdle16(blkhdr.datlen);
- /* Get the offset to the beginning of the data */
- doffset += SIZEOF_NXFFS_DATA_HDR;
- /* Make sure that all of the data fits within the block */
- if ((uint32_t)doffset + (uint32_t)dlen > (uint32_t)volume->geo.blocksize)
- {
- ferr("ERROR: Data length=%d is unreasonable at offset=%d\n", dlen, doffset);
- return -EIO;
- }
- /* Extract the expected CRC and calculate the CRC of the data block */
- ecrc = nxffs_rdle32(blkhdr.crc);
- nxffs_wrle32(blkhdr.crc, 0);
- crc = crc32((FAR const uint8_t *)&blkhdr, SIZEOF_NXFFS_DATA_HDR);
- crc = crc32part(&volume->cache[doffset], dlen, crc);
- if (crc != ecrc)
- {
- ferr("ERROR: CRC failure\n");
- return -EIO;
- }
- /* Looks good! Return the data length and success */
- *datlen = dlen;
- return OK;
- }
|