| /**************************************************************************** |
| * fs/nxffs/nxffs_write.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdint.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <nuttx/crc32.h> |
| #include <nuttx/fs/fs.h> |
| #include <nuttx/mtd/mtd.h> |
| |
| #include "nxffs.h" |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nxffs_hdrpos |
| * |
| * Description: |
| * Find a valid location for the data block header. A valid location will |
| * have these properties: |
| * |
| * 1. It will lie in the free flash region. |
| * 2. It will have enough contiguous memory to hold the entire header |
| * PLUS some meaningful amount of data (NXFFS_MINDATA). |
| * 3. The memory at this location will be fully erased. |
| * |
| * This function will only perform the checks of 1) and 2). |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * wrfile - Contains the current guess for the header position. On |
| * successful return, this field will hold the selected header |
| * position. |
| * size - The minimum size of the current write. |
| * |
| * Returned Value: |
| * Zero is returned on success. Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. Of special interest |
| * the return error of -ENOSPC which means that the FLASH volume is |
| * full and should be repacked. |
| * |
| * On successful return the following are also valid: |
| * |
| * wrfile->doffset - Flash offset to candidate data block header position |
| * volume->ioblock - Read/write block number of the block containing the |
| * header position |
| * volume->iooffset - The offset in the block to the candidate header |
| * position. |
| * volume->froffset - Updated offset to the first free FLASH block. |
| * |
| ****************************************************************************/ |
| |
| static inline int nxffs_hdrpos(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile, |
| size_t size) |
| { |
| int ret; |
| |
| /* Reserve memory for the object */ |
| |
| ret = nxffs_wrreserve(volume, SIZEOF_NXFFS_DATA_HDR + size); |
| if (ret == OK) |
| { |
| /* Save the offset to the FLASH region reserved for the data block |
| * header |
| */ |
| |
| wrfile->doffset = nxffs_iotell(volume); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_hdrerased |
| * |
| * Description: |
| * Find a valid location for the data block header. A valid location will |
| * have these properties: |
| * |
| * 1. It will lie in the free flash region. |
| * 2. It will have enough contiguous memory to hold the entire header |
| * PLUS some meaningful amount of data (NXFFS_MINDATA). |
| * 3. The memory at this location will be fully erased. |
| * |
| * This function will only perform the check 3). On entry it assumes: |
| * |
| * volume->ioblock - Read/write block number of the block containing the |
| * header position |
| * volume->iooffset - The offset in the block to the candidate header |
| * position. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * wrfile - Contains the current guess for the header position. On |
| * successful return, this field will hold the selected header |
| * position. |
| * size - The minimum size of the current write. |
| * |
| * Returned Value: |
| * Zero is returned on success. Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. Of special interest |
| * the return error of -ENOSPC which means that the FLASH volume is |
| * full and should be repacked. |
| * |
| * On successful return the following are also valid: |
| * |
| * wrfile->doffset - Flash offset to candidate data block header position |
| * volume->ioblock - Read/write block number of the block containing the |
| * header position |
| * volume->iooffset - The offset in the block to the candidate header |
| * position. |
| * volume->froffset - Updated offset to the first free FLASH block. |
| * |
| ****************************************************************************/ |
| |
| static inline int nxffs_hdrerased(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile, |
| size_t size) |
| { |
| int ret; |
| |
| /* Find a valid location to save the inode header */ |
| |
| ret = nxffs_wrverify(volume, SIZEOF_NXFFS_DATA_HDR + size); |
| if (ret == OK) |
| { |
| /* This is where we will put the data block header */ |
| |
| wrfile->doffset = nxffs_iotell(volume); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_wralloc |
| * |
| * Description: |
| * Allocate FLASH memory for the data block. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * wrfile - Describes the open file to be written. |
| * size - Size of the current write operation. |
| * |
| * Returned Value: |
| * Zero is returned on success. Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| static inline int nxffs_wralloc(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile, |
| size_t size) |
| { |
| bool packed; |
| int ret; |
| |
| /* Allocate FLASH memory for the data block. |
| * |
| * Loop until the data block header is configured or until a failure |
| * occurs. Note that nothing is written to FLASH. The data block header |
| * is not written until either (1) the file is closed, or (2) the data |
| * region is fully populated. |
| */ |
| |
| packed = false; |
| for (; ; ) |
| { |
| size_t mindata = MIN(NXFFS_MINDATA, size); |
| |
| /* File a valid location to position the data block. Start with |
| * the first byte in the free FLASH region. |
| */ |
| |
| ret = nxffs_hdrpos(volume, wrfile, mindata); |
| if (ret == OK) |
| { |
| /* Find a region of memory in the block that is fully erased */ |
| |
| ret = nxffs_hdrerased(volume, wrfile, mindata); |
| if (ret == OK) |
| { |
| /* Valid memory for the data block was found. Return |
| * success. |
| */ |
| |
| return OK; |
| } |
| } |
| |
| /* If no valid memory is found searching to the end of the volume, |
| * then -ENOSPC will be returned. Other errors are not handled. |
| */ |
| |
| if (ret != -ENOSPC || packed) |
| { |
| ferr("ERROR: Failed to find inode header memory: %d\n", -ret); |
| return -ENOSPC; |
| } |
| |
| /* -ENOSPC is a special case.. It means that the volume is full. |
| * Try to pack the volume in order to free up some space. |
| */ |
| |
| ret = nxffs_pack(volume); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to pack the volume: %d\n", -ret); |
| return ret; |
| } |
| |
| /* After packing the volume, froffset will be updated to point to the |
| * new free flash region. Try again. |
| */ |
| |
| nxffs_ioseek(volume, volume->froffset); |
| packed = true; |
| } |
| |
| /* Can't get here */ |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_reverify |
| * |
| * Description: |
| * Verify that the partial flash data block in the volume cache is valid. |
| * On entry, this function assumes: |
| * |
| * volume->ioblock - Read/write block number of the block containing the |
| * data block. |
| * volume->iooffset - The offset in the block to the data block. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * wrfile - Describes the open file to be written. |
| * |
| * Returned Value: |
| * Zero is returned on success. Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| static inline int nxffs_reverify(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile) |
| { |
| uint32_t crc; |
| off_t offset; |
| |
| if (wrfile->datlen > 0) |
| { |
| /* Get the offset to the start of the data */ |
| |
| offset = volume->iooffset + SIZEOF_NXFFS_DATA_HDR; |
| DEBUGASSERT(offset + wrfile->datlen <= volume->geo.blocksize); |
| |
| /* Calculate the CRC of the partial data block */ |
| |
| crc = crc32(&volume->cache[offset], wrfile->datlen); |
| |
| /* It must match the previously calculated CRC value */ |
| |
| if (crc != wrfile->crc) |
| { |
| ferr("ERROR: CRC failure\n"); |
| return -EIO; |
| } |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_wrappend |
| * |
| * Description: |
| * Append FLASH data to the data block. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * wrfile - Describes the open file to be written. |
| * buffer - Address of buffer of data to be written. |
| * buflen - The number of bytes remaining to be written |
| * |
| * Returned Value: |
| * The number of bytes written is returned on success. Otherwise, a |
| * negated errno value is returned indicating the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| static inline ssize_t nxffs_wrappend(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile, |
| FAR const char *buffer, size_t buflen) |
| { |
| ssize_t maxsize; |
| size_t nbytestowrite; |
| ssize_t nbytesleft; |
| off_t offset; |
| int ret; |
| |
| /* Get the offset to the start of unwritten data */ |
| |
| offset = volume->iooffset + wrfile->datlen + SIZEOF_NXFFS_DATA_HDR; |
| |
| /* Determine that maximum amount of data that can be written to this |
| * block. |
| */ |
| |
| maxsize = volume->geo.blocksize - offset; |
| DEBUGASSERT(maxsize > 0); |
| |
| /* But don't try to write over any unerased bytes */ |
| |
| maxsize = nxffs_erased(&volume->cache[offset], maxsize); |
| |
| /* Write as many bytes as we can into the data buffer */ |
| |
| nbytestowrite = MIN(maxsize, buflen); |
| nbytesleft = maxsize - nbytestowrite; |
| |
| if (nbytestowrite > 0) |
| { |
| /* Copy the data into the volume write cache */ |
| |
| memcpy(&volume->cache[offset], buffer, nbytestowrite); |
| |
| /* Increment the number of bytes written to the data block */ |
| |
| wrfile->datlen += nbytestowrite; |
| |
| /* Re-calculate the CRC */ |
| |
| offset = volume->iooffset + SIZEOF_NXFFS_DATA_HDR; |
| wrfile->crc = crc32(&volume->cache[offset], wrfile->datlen); |
| |
| /* And write the partial write block to FLASH -- unless the data |
| * block is full. In that case, the block will be written below. |
| */ |
| |
| if (nbytesleft > 0) |
| { |
| ret = nxffs_wrcache(volume); |
| if (ret < 0) |
| { |
| ferr("ERROR: nxffs_wrcache failed: %d\n", -ret); |
| return ret; |
| } |
| } |
| } |
| |
| /* Check if the data block is now full */ |
| |
| if (nbytesleft <= 0) |
| { |
| /* The data block is full, write the block to FLASH */ |
| |
| ret = nxffs_wrblkhdr(volume, wrfile); |
| if (ret < 0) |
| { |
| ferr("ERROR: nxffs_wrblkdhr failed: %d\n", -ret); |
| return ret; |
| } |
| } |
| |
| /* Return the number of bytes written to FLASH this time */ |
| |
| return nbytestowrite; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_zappend |
| * |
| * Description: |
| * Zero-extend FLASH data to the data block. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * wrfile - Describes the open file to be written. |
| * nzeros - The number of bytes of zeroed data to be written |
| * |
| * Returned Value: |
| * The number of zero bytes written is returned on success. Otherwise, a |
| * negated errno value is returned indicating the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| #ifdef __NO_TRUNCATE_SUPPORT__ |
| static inline ssize_t nxffs_zappend(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile, |
| off_t nzeros) |
| { |
| ssize_t maxsize; |
| size_t nbytestoclear; |
| ssize_t nbytesleft; |
| off_t offset; |
| int ret; |
| |
| /* Get the offset to the start of unwritten data */ |
| |
| offset = volume->iooffset + wrfile->datlen + SIZEOF_NXFFS_DATA_HDR; |
| |
| /* Determine that maximum amount of data that can be written to this |
| * block. |
| */ |
| |
| maxsize = volume->geo.blocksize - offset; |
| DEBUGASSERT(maxsize > 0); |
| |
| /* Write as many bytes as we can into the data buffer */ |
| |
| nbytestoclear = MIN(maxsize, nzeros); |
| nbytesleft = maxsize - nbytestoclear; |
| |
| if (nbytestoclear > 0) |
| { |
| /* Zero the data into the volume write cache */ |
| |
| memset(&volume->cache[offset], 0, nbytestoclear); |
| |
| /* Increment the number of bytes written to the data block */ |
| |
| wrfile->datlen += nbytestoclear; |
| |
| /* Re-calculate the CRC */ |
| |
| offset = volume->iooffset + SIZEOF_NXFFS_DATA_HDR; |
| wrfile->crc = crc32(&volume->cache[offset], wrfile->datlen); |
| |
| /* And write the partial write block to FLASH -- unless the data |
| * block is full. In that case, the block will be written below. |
| */ |
| |
| if (nbytesleft > 0) |
| { |
| ret = nxffs_wrcache(volume); |
| if (ret < 0) |
| { |
| ferr("ERROR: nxffs_wrcache failed: %d\n", -ret); |
| return ret; |
| } |
| } |
| } |
| |
| /* Check if the data block is now full */ |
| |
| if (nbytesleft <= 0) |
| { |
| /* The data block is full, write the block to FLASH */ |
| |
| ret = nxffs_wrblkhdr(volume, wrfile); |
| if (ret < 0) |
| { |
| ferr("ERROR: nxffs_wrblkdhr failed: %d\n", -ret); |
| return ret; |
| } |
| } |
| |
| return nbytestoclear; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nxffs_write |
| * |
| * Description: |
| * This is an implementation of the NuttX standard file system write |
| * method. |
| * |
| ****************************************************************************/ |
| |
| ssize_t nxffs_write(FAR struct file *filep, FAR const char *buffer, |
| size_t buflen) |
| { |
| FAR struct nxffs_volume_s *volume; |
| FAR struct nxffs_wrfile_s *wrfile; |
| ssize_t remaining; |
| ssize_t nwritten; |
| ssize_t total; |
| int ret; |
| |
| finfo("Write %zd bytes to offset %jd\n", buflen, (intmax_t)filep->f_pos); |
| |
| /* Sanity checks */ |
| |
| DEBUGASSERT(filep->f_priv != NULL); |
| |
| /* Recover the open file state from the struct file instance */ |
| |
| wrfile = (FAR struct nxffs_wrfile_s *)filep->f_priv; |
| |
| /* Recover the volume state from the open file */ |
| |
| volume = filep->f_inode->i_private; |
| DEBUGASSERT(volume != NULL); |
| |
| /* Get exclusive access to the volume. Note that the volume lock |
| * protects the open file list. |
| */ |
| |
| ret = nxmutex_lock(&volume->lock); |
| if (ret < 0) |
| { |
| ferr("ERROR: nxmutex_lock failed: %d\n", ret); |
| goto errout; |
| } |
| |
| /* Check if the file was opened with write access */ |
| |
| if ((wrfile->ofile.oflags & O_WROK) == 0) |
| { |
| ferr("ERROR: File not open for write access\n"); |
| ret = -EACCES; |
| goto errout_with_lock; |
| } |
| |
| /* Loop until we successfully appended all of the data to the file (or an |
| * error occurs) |
| */ |
| |
| for (total = 0; total < buflen; ) |
| { |
| remaining = buflen - total; |
| |
| /* Have we already allocated the data block? */ |
| |
| if (wrfile->doffset == 0) |
| { |
| /* No, allocate the data block now, re-packing if necessary. */ |
| |
| wrfile->datlen = 0; |
| ret = nxffs_wralloc(volume, wrfile, remaining); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to allocate a data block: %d\n", -ret); |
| goto errout_with_lock; |
| } |
| } |
| |
| /* Seek to the FLASH block containing the data block */ |
| |
| nxffs_ioseek(volume, wrfile->doffset); |
| |
| /* Verify that the FLASH data that was previously written is still |
| * intact |
| */ |
| |
| ret = nxffs_reverify(volume, wrfile); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to verify FLASH data block: %d\n", -ret); |
| goto errout_with_lock; |
| } |
| |
| /* Append the data to the end of the data block and write the updated |
| * block to flash. |
| */ |
| |
| nwritten = nxffs_wrappend(volume, wrfile, &buffer[total], remaining); |
| if (nwritten < 0) |
| { |
| ferr("ERROR: Failed to append to FLASH to a data block: %d\n", |
| -ret); |
| goto errout_with_lock; |
| } |
| |
| /* Decrement the number of bytes remaining to be written */ |
| |
| total += nwritten; |
| } |
| |
| /* Success.. return the number of bytes written */ |
| |
| ret = total; |
| filep->f_pos = wrfile->datlen; |
| |
| errout_with_lock: |
| nxmutex_unlock(&volume->lock); |
| errout: |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_wrextend |
| * |
| * Description: |
| * Zero-extend a file. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * entry - Describes the new inode entry |
| * length - The new, extended length of the file |
| * |
| * Assumptions: |
| * The caller holds the NXFFS semaphore. |
| * The caller has verified that the file is writable. |
| * |
| ****************************************************************************/ |
| |
| #ifdef __NO_TRUNCATE_SUPPORT__ |
| int nxffs_wrextend(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile, off_t length) |
| { |
| ssize_t remaining; |
| ssize_t nwritten; |
| off_t oldsize; |
| int ret; |
| |
| finfo("Extend file to %ld bytes to offset %d\n", (long)length); |
| DEBUGASSERT(volume != NULL && wrfile != NULL); |
| |
| oldsize = wrfile->ofile.entry.datlen; |
| DEBUGASSERT(length > oldsize); |
| |
| /* Loop until we successfully appended all of the data to the file (or an |
| * error occurs) |
| */ |
| |
| remaining = length - oldsize; |
| while (remaining > 0) |
| { |
| /* Have we already allocated the data block? */ |
| |
| if (wrfile->doffset == 0) |
| { |
| /* No, allocate the data block now, re-packing if necessary. */ |
| |
| wrfile->datlen = 0; |
| ret = nxffs_wralloc(volume, wrfile, remaining); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to allocate a data block: %d\n", -ret); |
| return ret; |
| } |
| } |
| |
| /* Seek to the FLASH block containing the data block */ |
| |
| nxffs_ioseek(volume, wrfile->doffset); |
| |
| /* Verify that the FLASH data that was previously written is still |
| * intact |
| */ |
| |
| ret = nxffs_reverify(volume, wrfile); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to verify FLASH data block: %d\n", -ret); |
| return ret; |
| } |
| |
| /* Append the data to the end of the data block and write the updated |
| * block to flash. |
| */ |
| |
| nwritten = nxffs_zappend(volume, wrfile, remaining); |
| if (nwritten < 0) |
| { |
| ferr("ERROR: Failed to zero extend FLASH data block: %d\n", -ret); |
| return (int)nwritten; |
| } |
| |
| /* Decrement the number of bytes remaining to be written */ |
| |
| remaining -= nwritten; |
| } |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: nxffs_wrreserve |
| * |
| * Description: |
| * Find a valid location for a file system object of 'size'. A valid |
| * location will have these properties: |
| * |
| * 1. It will lie in the free flash region. |
| * 2. It will have enough contiguous memory to hold the entire object |
| * 3. The memory at this location will be fully erased. |
| * |
| * This function will only perform the checks of 1) and 2). The |
| * end-of-filesystem offset, froffset, is update past this memory which, |
| * in effect, reserves the memory. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * size - The size of the object to be reserved. |
| * |
| * Returned Value: |
| * Zero is returned on success. Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. Of special interest |
| * the return error of -ENOSPC which means that the FLASH volume is |
| * full and should be repacked. |
| * |
| * On successful return the following are also valid: |
| * |
| * volume->ioblock - Read/write block number of the block containing the |
| * candidate object position |
| * volume->iooffset - The offset in the block to the candidate object |
| * position. |
| * volume->froffset - Updated offset to the first free FLASH block after |
| * the reserved memory. |
| * |
| ****************************************************************************/ |
| |
| int nxffs_wrreserve(FAR struct nxffs_volume_s *volume, size_t size) |
| { |
| int ret; |
| |
| /* Seek to the beginning of the free FLASH region */ |
| |
| nxffs_ioseek(volume, volume->froffset); |
| |
| /* Check for a seek past the end of the volume */ |
| |
| if (volume->ioblock >= volume->nblocks) |
| { |
| /* Return -ENOSPC to indicate that the volume is full */ |
| |
| return -ENOSPC; |
| } |
| |
| /* Skip over block headers */ |
| |
| if (volume->iooffset < SIZEOF_NXFFS_BLOCK_HDR) |
| { |
| volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR; |
| } |
| |
| /* Make sure that there is space there to hold the entire object */ |
| |
| if (volume->iooffset + size > volume->geo.blocksize) |
| { |
| /* We will need to skip to the next block. But first, check if we are |
| * already at the final block. |
| */ |
| |
| if (volume->ioblock + 1 >= volume->nblocks) |
| { |
| /* Return -ENOSPC to indicate that the volume is full */ |
| |
| ferr("ERROR: No space in last block\n"); |
| return -ENOSPC; |
| } |
| |
| /* This is not the last block in the volume, so just seek to the |
| * beginning of the next, valid block. |
| */ |
| |
| volume->ioblock++; |
| ret = nxffs_validblock(volume, &volume->ioblock); |
| if (ret < 0) |
| { |
| ferr("ERROR: No more valid blocks\n"); |
| return ret; |
| } |
| |
| volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR; |
| } |
| |
| /* Update the pointer to the first next free FLASH memory -- reserving this |
| * block of memory. |
| */ |
| |
| volume->froffset = nxffs_iotell(volume) + size; |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_wrverify |
| * |
| * Description: |
| * Find a valid location for the object. A valid location will have |
| * these properties: |
| * |
| * 1. It will lie in the free flash region. |
| * 2. It will have enough contiguous memory to hold the entire header |
| * (excluding the file name which may lie in the next block). |
| * 3. The memory at this location will be fully erased. |
| * |
| * This function will only perform the check 3). On entry it assumes the |
| * following settings (left by nxffs_wrreserve()): |
| * |
| * volume->ioblock - Read/write block number of the block containing the |
| * candidate object position |
| * volume->iooffset - The offset in the block to the candidate object |
| * position. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * size - The size of the object to be verified. |
| * |
| * Returned Value: |
| * Zero is returned on success. Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. Of special interest |
| * the return error of -ENOSPC which means that the FLASH volume is |
| * full and should be repacked. |
| * |
| * On successful return the following are also valid: |
| * |
| * volume->ioblock - Read/write block number of the block containing the |
| * verified object position |
| * volume->iooffset - The offset in the block to the verified object |
| * position. |
| * volume->froffset - Updated offset to the first free FLASH block. |
| * |
| ****************************************************************************/ |
| |
| int nxffs_wrverify(FAR struct nxffs_volume_s *volume, size_t size) |
| { |
| uint16_t iooffset; |
| int nerased; |
| int ret; |
| int i; |
| |
| /* Search to the very last block in the volume if we have to */ |
| |
| while (volume->ioblock < volume->nblocks) |
| { |
| /* Make sure that the block is in memory */ |
| |
| ret = nxffs_rdcache(volume, volume->ioblock); |
| if (ret < 0) |
| { |
| /* Ignore the error... just skip to the next block. This should |
| * never happen with normal FLASH, but could occur with NAND if |
| * the block has uncorrectable bit errors. |
| */ |
| |
| ferr("ERROR: Failed to read block %jd: %d\n", |
| (intmax_t)volume->ioblock, -ret); |
| } |
| |
| /* Search to the very end of this block if we have to */ |
| |
| else |
| { |
| iooffset = volume->iooffset; |
| nerased = 0; |
| |
| for (i = volume->iooffset; i < volume->geo.blocksize; i++) |
| { |
| /* Is this byte erased? */ |
| |
| if (volume->cache[i] == CONFIG_NXFFS_ERASEDSTATE) |
| { |
| /* Yes.. increment the count of contiguous, erased bytes */ |
| |
| nerased++; |
| |
| /* Is the whole header memory erased? */ |
| |
| if (nerased >= size) |
| { |
| /* Yes.. this is where we will put the object */ |
| |
| off_t offset = |
| volume->ioblock * volume->geo.blocksize + iooffset; |
| |
| /* Update the free flash offset and return success */ |
| |
| volume->froffset = offset + size; |
| return OK; |
| } |
| } |
| |
| /* This byte is not erased! (It should be unless the block is |
| * bad) |
| */ |
| |
| else |
| { |
| nerased = 0; |
| iooffset = i + 1; |
| } |
| } |
| } |
| |
| /* If we get here, then either (1) this block is not read-able, or |
| * (2) we have looked at every byte in the block and did not find |
| * any sequence of erased bytes long enough to hold the object. |
| * Skip to the next, valid block. |
| */ |
| |
| volume->ioblock++; |
| ret = nxffs_validblock(volume, &volume->ioblock); |
| if (ret < 0) |
| { |
| ferr("ERROR: No more valid blocks\n"); |
| return ret; |
| } |
| |
| volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR; |
| volume->froffset = volume->ioblock * volume->geo.blocksize + |
| SIZEOF_NXFFS_BLOCK_HDR; |
| } |
| |
| /* Return -ENOSPC if there is no erased memory left in the volume for |
| * the object. |
| */ |
| |
| ferr("ERROR: Not enough memory left to hold the file header\n"); |
| return -ENOSPC; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_wrblkhdr |
| * |
| * Description: |
| * Write the block header information. This is done (1) whenever the end- |
| * block is encountered and (2) also when the file is closed in order to |
| * flush the final block of data to FLASH. |
| * |
| * Input Parameters: |
| * volume - Describes the state of the NXFFS volume |
| * wrfile - Describes the state of the open file |
| * |
| * Returned Value: |
| * Zero is returned on success; Otherwise, a negated errno value is |
| * returned to indicate the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| int nxffs_wrblkhdr(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile) |
| { |
| FAR struct nxffs_data_s *dathdr; |
| int ret; |
| |
| /* Write the data block header to memory */ |
| |
| nxffs_ioseek(volume, wrfile->doffset); |
| dathdr = (FAR struct nxffs_data_s *)&volume->cache[volume->iooffset]; |
| memcpy(dathdr->magic, g_datamagic, NXFFS_MAGICSIZE); |
| nxffs_wrle32(dathdr->crc, 0); |
| nxffs_wrle16(dathdr->datlen, wrfile->datlen); |
| |
| /* Update the entire data block CRC (including the header) */ |
| |
| wrfile->crc = crc32(&volume->cache[volume->iooffset], |
| wrfile->datlen + SIZEOF_NXFFS_DATA_HDR); |
| nxffs_wrle32(dathdr->crc, wrfile->crc); |
| |
| /* And write the data block to FLASH */ |
| |
| ret = nxffs_wrcache(volume); |
| if (ret < 0) |
| { |
| ferr("ERROR: nxffs_wrcache failed: %d\n", -ret); |
| goto errout; |
| } |
| |
| /* After the block has been successfully written to flash, update the inode |
| * statistics and reset the write state. |
| * |
| * volume: |
| * froffset - The offset the next free FLASH region. Set to just after |
| * the inode data block that we just wrote. This is where we will |
| * begin the search for the next inode header or data block. |
| */ |
| |
| volume->froffset = (wrfile->doffset + wrfile->datlen + |
| SIZEOF_NXFFS_DATA_HDR); |
| |
| /* wrfile->file.entry: |
| * datlen: Total file length accumulated so far. When the file is |
| * closed, this will hold the file length. |
| * doffset: Offset to the first data block. Only the offset to the |
| * first data block is saved. |
| */ |
| |
| wrfile->ofile.entry.datlen += wrfile->datlen; |
| if (wrfile->ofile.entry.doffset == 0) |
| { |
| wrfile->ofile.entry.doffset = wrfile->doffset; |
| } |
| |
| /* Return success */ |
| |
| ret = OK; |
| |
| errout: |
| wrfile->crc = 0; |
| wrfile->doffset = 0; |
| wrfile->datlen = 0; |
| return ret; |
| } |