Resync new repository with old repo r5166

git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5153 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo
2012-09-17 18:18:44 +00:00
commit 57623d42eb
5691 changed files with 1361853 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
#
# For a description of the syntax of this configuration file,
# see misc/tools/kconfig-language.txt.
#
config FS_NXFFS
bool "NXFFS file system"
default n
depends on !DISABLE_MOUNTPOINT
---help---
Enable NuttX FLASH file system (NXFF) support.
if FS_NXFFS
config NXFFS_ERASEDSTATE
bool "FLASH erased state"
default n
---help---
The erased state of FLASH.
This must have one of the values of 0xff or 0x00.
Default: 0xff.
config NXFFS_PACKTHRESHOLD
bool "Re-packing threshold"
default n
---help---
When packing flash file data,
don't both with file chunks smaller than this number of data bytes.
Default: 32.
config NXFFS_MAXNAMLEN
bool "Maximum file name length"
default n
---help---
The maximum size of an NXFFS file name.
Default: 255.
config NXFFS_TAILTHRESHOLD
bool "Tail threshold"
default n
---help---
clean-up can either mean
packing files together toward the end of the file or, if file are
deleted at the end of the file, clean up can simply mean erasing
the end of FLASH memory so that it can be re-used again. However,
doing this can also harm the life of the FLASH part because it can
mean that the tail end of the FLASH is re-used too often. This
threshold determines if/when it is worth erased the tail end of FLASH
and making it available for re-use (and possible over-wear).
Default: 8192.
endif
+46
View File
@@ -0,0 +1,46 @@
############################################################################
# fs/nxffs/Make.defs
#
# Copyright (C) 2011 Gregory Nutt. All rights reserved.
# Author: Gregory Nutt <gnutt@nuttx.org>
#
# 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.
#
############################################################################
ifeq ($(CONFIG_FS_NXFFS),y)
ASRCS +=
CSRCS += nxffs_block.c nxffs_blockstats.c nxffs_cache.c nxffs_dirent.c \
nxffs_dump.c nxffs_initialize.c nxffs_inode.c nxffs_ioctl.c \
nxffs_open.c nxffs_pack.c nxffs_read.c nxffs_reformat.c \
nxffs_stat.c nxffs_unlink.c nxffs_util.c nxffs_write.c
# Argument for dependency checking
NXFFSDEPPATH = --dep-path nxffs
endif
+180
View File
@@ -0,0 +1,180 @@
NXFFS README
^^^^^^^^^^^^
This README file contains information about the implemenation of the NuttX
wear-leveling FLASH file system, NXFFS.
Contents:
General NXFFS organization
General operation
Headers
NXFFS Limitations
Multiple Writers
ioctls
Things to Do
General NXFFS organization
==========================
The following example assumes 4 logical blocks per FLASH erase block. The
actual relationship is determined by the FLASH geometry reported by the MTD
driver.
ERASE LOGICAL Inodes begin with a inode header. inode may
BLOCK BLOCK CONTENTS be marked as "deleted," pending re-packing.
n 4*n --+--------------+
|BBBBBBBBBBBBBB| Logic block header
|IIIIIIIIIIIIII| Inodes begin with a inode header
|DDDDDDDDDDDDDD| Data block containing inode data block
| (Inode Data) |
4*n+1 --+--------------+
|BBBBBBBBBBBBBB| Logic block header
|DDDDDDDDDDDDDD| Inodes may consist of multiple data blocks
| (Inode Data) |
|IIIIIIIIIIIIII| Next inode header
| | Possibly a few unused bytes at the end of a block
4*n+2 --+--------------+
|BBBBBBBBBBBBBB| Logic block header
|DDDDDDDDDDDDDD|
| (Inode Data) |
4*n+3 --+--------------+
|BBBBBBBBBBBBBB| Logic block header
|IIIIIIIIIIIIII| Next inode header
|DDDDDDDDDDDDDD|
| (Inode Data) |
n+1 4*(n+1) --+--------------+
|BBBBBBBBBBBBBB| Logic block header
| | All FLASH is unused after the end of the final
| | inode.
--+--------------+
General operation
=================
Inodes are written starting at the beginning of FLASH. As inodes are
deleted, they are marked as deleted but not removed. As new inodes are
written, allocations proceed to toward the end of the FLASH -- thus,
supporting wear leveling by using all FLASH blocks equally.
When the FLASH becomes full (no more space at the end of the FLASH), a
re-packing operation must be performed: All inodes marked deleted are
finally removed and the remaining inodes are packed at the beginning of
the FLASH. Allocations then continue at the freed FLASH memory at the
end of the FLASH.
Headers
=======
BLOCK HEADER:
The block header is used to determine if the block has every been
formatted and also indicates bad blocks which should never be used.
INODE HEADER:
Each inode begins with an inode header that contains, among other things,
the name of the inode, the offset to the first data block, and the
length of the inode data.
At present, the only kind of inode support is a file. So for now, the
term file and inode are interchangeable.
INODE DATA HEADER:
Inode data is enclosed in a data header. For a given inode, there
is at most one inode data block per logical block. If the inode data
spans more than one logical block, then the inode data may be enclosed
in multiple data blocks, one per logical block.
NXFFS Limitations
=================
This implementation is very simple as, as a result, has several limitations
that you should be aware before opting to use NXFFS:
1. Since the files are contiguous in FLASH and since allocations always
proceed toward the end of the FLASH, there can only be one file opened
for writing at a time. Multiple files may be opened for reading.
2. Files may not be increased in size after they have been closed. The
O_APPEND open flag is not supported.
3. Files are always written sequential. Seeking within a file opened for
writing will not work.
4. There are no directories, however, '/' may be used within a file name
string providing some illusion of directories.
5. Files may be opened for reading or for writing, but not both: The O_RDWR
open flag is not supported.
6. The re-packing process occurs only during a write when the free FLASH
memory at the end of the FLASH is exhausted. Thus, occasionally, file
writing may take a long time.
7. Another limitation is that there can be only a single NXFFS volume
mounted at any time. This has to do with the fact that we bind to
an MTD driver (instead of a block driver) and bypass all of the normal
mount operations.
Multiple Writers
================
As mentioned in the limitations above, there can be only one file opened
for writing at a time. If one thread has a file opened for writing and
another thread attempts to open a file for writing, then that second
thread will be blocked and will have to wait for the first thread to
close the file.
Such behavior may or may not be a problem for your application, depending
(1) how long the first thread keeps the file open for writing and (2) how
critical the behavior of the second thread is. Note that writing to FLASH
can always trigger a major FLASH reorganization and, hence, there is no
way to guarantee the first condition: The first thread may have the file
open for a long time even if it only intends to write a small amount.
Also note that a deadlock condition would occur if the SAME thread
attempted to open two files for writing. The thread would would be
blocked waiting for itself to close the first file.
ioctls
======
The file system supports to ioctls:
FIOC_REFORMAT: Will force the flash to be erased and a fresh, empty
NXFFS file system to be written on it.
FIOC_OPTIMIZE: Will force immediate repacking of the file system. This
will increase the amount of wear on the FLASH if you use this!
Things to Do
============
- The statfs() implementation is minimal. It whould have some calcuation
of the f_bfree, f_bavail, f_files, f_ffree return values.
- There are too many allocs and frees. More structures may need to be
pre-allocated.
- The file name is always extracted and held in allocated, variable-length
memory. The file name is not used during reading and eliminating the
file name in the entry structure would improve performance.
- There is a big inefficiency in reading. On each read, the logic searches
for the read position from the beginning of the file each time. This
may be necessary whenever an lseek() is done, but not in general. Read
performance could be improved by keeping FLASH offset and read positional
information in the read open file structure.
- Fault tolerance must be improved. We need to be absolutely certain that
any FLASH errors do not cause the file system to behavior incorrectly.
- Wear leveling might be improved (?). Files are re-packed at the front
of FLASH as part of the clean-up operation. However, that means the files
that are not modified often become fixed in place at the beginning of
FLASH. This reduces the size of the pool moving files at the end of the
FLASH. As the file system becomes more filled with fixed files at the
front of the device, the level of wear on the blocks at the end of the
FLASH increases.
- When the time comes to reorganization the FLASH, the system may be
inavailable for a long time. That is a bad behavior. What is needed,
I think, is a garbage collection task that runs periodically so that
when the big reorganizaiton event occurs, most of the work is already
done. That garbarge collection should search for valid blocks that no
longer contain valid data. It should pre-erase them, put them in
a good but empty state... all ready for file system re-organization.
File diff suppressed because it is too large Load Diff
+186
View File
@@ -0,0 +1,186 @@
/****************************************************************************
* fs/nxffs/nxffs_block.c
*
* Copyright (C) 2011 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 <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/mtd.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_verifyblock
*
* Description:
* Assure the the provided (logical) block number is in the block cache
* and that it has a valid block header (i.e., proper magic and
* marked good)
*
* Input Parameters:
* volume - Describes the NXFFS volume
* block - The (logical) block number to load and verify.
*
* Returned Values:
* Zero is returned on success. Otherwise, a negated errno value is
* returned indicating the nature of the failure. -ENOENT is returned
* if the block is a bad block.
*
****************************************************************************/
int nxffs_verifyblock(FAR struct nxffs_volume_s *volume, off_t block)
{
FAR struct nxffs_block_s *blkhdr;
int ret;
/* Make sure the the block is in the cache */
ret = nxffs_rdcache(volume, block);
if (ret < 0)
{
/* Perhaps we are at the end of the media */
fvdbg("Failed to read data into cache: %d\n", ret);
return ret;
}
/* Check if the block has a magic number (meaning that it is not
* erased) and that it is valid (meaning that it is not marked
* for deletion)
*/
blkhdr = (FAR struct nxffs_block_s *)volume->cache;
if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) == 0)
{
/* This does appear to be a block */
if (blkhdr->state == BLOCK_STATE_GOOD)
{
/* The block is valid */
return OK;
}
else if (blkhdr->state == BLOCK_STATE_BAD)
{
/* -ENOENT is a special indication that this is a properly marked
* bad block
*/
return -ENOENT;
}
}
/* Whatever is here where a block header should be is invalid */
return -EINVAL;
}
/****************************************************************************
* Name: nxffs_validblock
*
* Description:
* Find the next valid (logical) block in the volume.
*
* Input Parameters:
* volume - Describes the NXFFS volume
* block - On entry, this provides the starting block number. If the
* function is succesfful, then this memory location will hold the
* block number of the next valid block on return.
*
* Returned Value:
* Zero on success otherwise a negated errno value indicating the nature
* of the failure.
*
****************************************************************************/
int nxffs_validblock(struct nxffs_volume_s *volume, off_t *block)
{
off_t i;
int ret;
DEBUGASSERT(volume && block);
/* Loop for each possible block or until a valid block is found */
for (i = *block; i < volume->nblocks; i++)
{
/* Loop until we find a valid block */
ret = nxffs_verifyblock(volume, i);
if (ret == OK)
{
/* We found it, return the block number */
*block = i;
return OK;
}
}
/* ENOSPC is special return value that means that there is no further,
* valid blocks left in the volume.
*/
fdbg("No valid block found\n");
return -ENOSPC;
}
+152
View File
@@ -0,0 +1,152 @@
/****************************************************************************
* fs/nxffs/nxffs_blockstats.c
*
* Copyright (C) 2011 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 <errno.h>
#include <debug.h>
#include <nuttx/mtd.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_blockstats
*
* Description:
* Analyze the NXFFS volume. This operation must be performed when the
* volume is first mounted in order to detect if the volume has been
* formatted and contains a usable NXFFS file system.
*
* Input Parameters:
* volume - Describes the current NXFFS volume.
* stats - On return, will hold nformation describing the state of the
* volume.
*
* Returned Value:
* Negated errnos are returned only in the case of MTD reported failures.
* Nothing in the volume data itself will generate errors.
*
****************************************************************************/
int nxffs_blockstats(FAR struct nxffs_volume_s *volume,
FAR struct nxffs_blkstats_s *stats)
{
FAR uint8_t *bptr; /* Pointer to next block data */
off_t ioblock; /* I/O block number */
int lblock; /* Logical block index */
int ret;
/* Process each erase block */
memset(stats, 0, sizeof(struct nxffs_blkstats_s));
for (ioblock = 0; ioblock < volume->nblocks; ioblock += volume->blkper)
{
/* Read the full erase block */
ret = MTD_BREAD(volume->mtd, ioblock, volume->blkper, volume->pack);
if (ret < volume->blkper)
{
fdbg("Failed to read erase block %d: %d\n", ioblock / volume->blkper, -ret);
return ret;
}
/* Process each logical block */
for (bptr = volume->pack, lblock = 0;
lblock < volume->blkper;
bptr += volume->geo.blocksize, lblock++)
{
FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s*)bptr;
/* Collect statistics */
stats->nblocks++;
if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) != 0)
{
stats->nunformat++;
}
else if (blkhdr->state == BLOCK_STATE_BAD)
{
stats->nbad++;
}
else if (blkhdr->state == BLOCK_STATE_GOOD)
{
stats-> ngood++;
}
else
{
stats->ncorrupt++;
}
}
}
fdbg("Number blocks: %d\n", stats->nblocks);
fdbg(" Good blocks: %d\n", stats->ngood);
fdbg(" Bad blocks: %d\n", stats->nbad);
fdbg(" Unformatted blocks: %d\n", stats->nunformat);
fdbg(" Corrupt blocks: %d\n", stats->ncorrupt);
return OK;
}
+261
View File
@@ -0,0 +1,261 @@
/****************************************************************************
* fs/nxffs/nxffs_cache.c
*
* Copyright (C) 2011 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 <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/mtd.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_rdcache
*
* Description:
* Read one I/O block into the volume block cache memory.
*
* Input Parameters:
* volume - Describes the current volume
* block - The first logical block to read
*
* Returned Value:
* Negated errnos are returned only in the case of MTD reported failures.
* Nothing in the volume data itself will generate errors.
*
****************************************************************************/
int nxffs_rdcache(FAR struct nxffs_volume_s *volume, off_t block)
{
size_t nxfrd;
/* Check if the requested data is already in the cache */
if (block != volume->cblock)
{
/* Read the specified blocks into cache */
nxfrd = MTD_BREAD(volume->mtd, block, 1, volume->cache);
if (nxfrd != 1)
{
fvdbg("Read block %d failed: %d\n", block, nxfrd);
return -EIO;
}
/* Remember what is in the cache */
volume->cblock = block;
}
return OK;
}
/****************************************************************************
* Name: nxffs_wrcache
*
* Description:
* Write one or more logical blocks from the volume cache memory.
*
* Input Parameters:
* volume - Describes the current volume
*
* Returned Value:
* Negated errnos are returned only in the case of MTD reported failures.
*
****************************************************************************/
int nxffs_wrcache(FAR struct nxffs_volume_s *volume)
{
size_t nxfrd;
/* Write the current block from the cache */
nxfrd = MTD_BWRITE(volume->mtd, volume->cblock, 1, volume->cache);
if (nxfrd != 1)
{
fdbg("Write block %d failed: %d\n", volume->cblock, nxfrd);
return -EIO;
}
/* Write was successful */
return OK;
}
/****************************************************************************
* Name: nxffs_ioseek
*
* Description:
* Seek to a position in FLASH memory. This simply sets up the offsets
* and pointer values. This is a necessary step prior to using
* nxffs_getc().
*
* Input Parameters:
* volume - Describes the NXFFS volume
* offset - The physical offset in bytes from the beginning of the FLASH
* in bytes.
*
****************************************************************************/
void nxffs_ioseek(FAR struct nxffs_volume_s *volume, off_t offset)
{
/* Convert the offset into a block number and a byte offset into the
* block.
*/
volume->ioblock = offset / volume->geo.blocksize;
volume->iooffset = offset - volume->geo.blocksize * volume->ioblock;
}
/****************************************************************************
* Name: nxffs_iotell
*
* Description:
* Report the current position.
*
* Input Parameters:
* volume - Describes the NXFFS volume
*
* Returned Value:
* The offset from the beginning of FLASH to the current seek position.
*
****************************************************************************/
off_t nxffs_iotell(FAR struct nxffs_volume_s *volume)
{
return volume->ioblock * volume->geo.blocksize + volume->iooffset;
}
/****************************************************************************
* Name: nxffs_getc
*
* Description:
* Get the next byte from FLASH. This function allows the data in the
* formatted FLASH blocks to be read as a continuous byte stream, skipping
* over bad blocks and block headers as necessary.
*
* Input Parameters:
* volume - Describes the NXFFS volume. The paramters ioblock and iooffset
* in the volume structure determine the behavior of nxffs_getc().
* reserve - If less than this much space is available at the end of the
* block, then skip to the next block.
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno indicating the
* nature of the failure.
*
****************************************************************************/
int nxffs_getc(FAR struct nxffs_volume_s *volume, uint16_t reserve)
{
int ret;
DEBUGASSERT(reserve > 0);
/* Loop to skip over bad blocks */
do
{
/* Check if we have the reserve amount at the end of the current block */
if (volume->iooffset + reserve > volume->geo.blocksize)
{
/* Check for attempt to read past the end of FLASH */
off_t nextblock = volume->ioblock + 1;
if (nextblock >= volume->nblocks)
{
fvdbg("End of FLASH encountered\n");
return -ENOSPC;
}
/* Set up the seek to the data just after the header in the
* next block.
*/
volume->ioblock = nextblock;
volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR;
}
/* Make sure that the block is in the cache. The special error
* -ENOENT indicates the block was read successfully but was not
* marked as a good block. In this case we need to skip to the
* next block. All other errors are fatal.
*/
ret = nxffs_verifyblock(volume, volume->ioblock);
if (ret < 0 && ret != -ENOENT)
{
fvdbg("Failed to read valid data into cache: %d\n", ret);
return ret;
}
}
while (ret != OK);
/* Return the the character at this offset. Note that on return,
* iooffset could point to the byte outside of the current block.
*/
ret = (int)volume->cache[volume->iooffset];
volume->iooffset++;
return ret;
}
+217
View File
@@ -0,0 +1,217 @@
/****************************************************************************
* fs/nxffs/nxffs_dirent.c
*
* Copyright (C) 2011 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 <dirent.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/fs/fs.h>
#include <nuttx/mtd.h>
#include <nuttx/fs/dirent.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_opendir
*
* Description:
* Open a directory for read access
*
****************************************************************************/
int nxffs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct fs_dirent_s *dir)
{
struct nxffs_volume_s *volume;
int ret;
fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL");
/* Sanity checks */
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
/* Recover the file system state from the NuttX inode instance */
volume = mountpt->i_private;
ret = sem_wait(&volume->exclsem);
if (ret != OK)
{
goto errout;
}
/* The requested directory must be the volume-relative "root" directory */
if (relpath && relpath[0] != '\0')
{
ret = -ENOENT;
goto errout_with_semaphore;
}
/* Set the offset to the offset to the first valid inode */
dir->u.nxffs.nx_offset = volume->inoffset;
ret = OK;
errout_with_semaphore:
sem_post(&volume->exclsem);
errout:
return ret;
}
/****************************************************************************
* Name: nxffs_readdir
*
* Description: Read the next directory entry
*
****************************************************************************/
int nxffs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir)
{
FAR struct nxffs_volume_s *volume;
FAR struct nxffs_entry_s entry;
off_t offset;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
/* Recover the file system state from the NuttX inode instance */
volume = mountpt->i_private;
ret = sem_wait(&volume->exclsem);
if (ret != OK)
{
goto errout;
}
/* Read the next inode header from the offset */
offset = dir->u.nxffs.nx_offset;
ret = nxffs_nextentry(volume, offset, &entry);
/* If the read was successful, then handle the reported inode. Note
* that when the last inode has been reported, the value -ENOENT will
* be returned.. which is correct for the readdir() method.
*/
if (ret == OK)
{
/* Return the filename and file type */
fvdbg("Offset %d: \"%s\"\n", entry.hoffset, entry.name);
dir->fd_dir.d_type = DTYPE_FILE;
strncpy(dir->fd_dir.d_name, entry.name, NAME_MAX+1);
/* Discard this entry and set the next offset. */
dir->u.nxffs.nx_offset = nxffs_inodeend(volume, &entry);
nxffs_freeentry(&entry);
ret = OK;
}
sem_post(&volume->exclsem);
errout:
return ret;
}
/****************************************************************************
* Name: nxffs_rewindir
*
* Description:
* Reset directory read to the first entry
*
****************************************************************************/
int nxffs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir)
{
FAR struct nxffs_volume_s *volume;
int ret;
fvdbg("Entry\n");
/* Sanity checks */
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
/* Recover the file system state from the NuttX inode instance */
volume = mountpt->i_private;
ret = sem_wait(&volume->exclsem);
if (ret != OK)
{
goto errout;
}
/* Reset the offset to the FLASH offset to the first valid inode */
dir->u.nxffs.nx_offset = volume->inoffset;
ret = OK;
sem_post(&volume->exclsem);
errout:
return ret;
}
+479
View File
@@ -0,0 +1,479 @@
/****************************************************************************
* fs/nxffs/nxffs_dump.c
*
* Copyright (C) 2011 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 <debug.h>
#include <errno.h>
#include <crc32.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/mtd.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Re-define fdbg so that the output does not have so much diagnostic info.
* This should still, however, always agree with the defintion in debug.h.
*/
#undef fdbg
#define fdbg lib_rawprintf
/****************************************************************************
* Private Types
****************************************************************************/
struct nxffs_blkinfo_s
{
struct mtd_geometry_s geo;
FAR uint8_t *buffer;
off_t nblocks;
off_t block;
off_t offset;
bool verbose;
};
/****************************************************************************
* Private Data
****************************************************************************/
static const char g_hdrformat[] = " BLOCK:OFFS TYPE STATE LENGTH\n";
static const char g_format[] = " %5d:%-5d %s %s %5d\n";
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_analyzeinode
*
* Description:
* Analyze one candidate inode found in the block.
*
****************************************************************************/
#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_FS)
static inline ssize_t nxffs_analyzeinode(FAR struct nxffs_blkinfo_s *blkinfo,
int offset)
{
FAR struct nxffs_inode_s inode;
off_t nextblock;
uint8_t state;
uint32_t noffs;
uint32_t doffs;
uint32_t utc;
uint32_t ecrc;
uint32_t datlen;
uint32_t crc;
size_t spaceleft;
/* Verify that there is space for an inode header remaining in the block */
if (offset + SIZEOF_NXFFS_INODE_HDR > blkinfo->geo.blocksize)
{
/* No.. then this can't be an inode header */
return ERROR;
}
/* Unpack the header */
memcpy(&inode, &blkinfo->buffer[offset], SIZEOF_NXFFS_INODE_HDR);
noffs = nxffs_rdle32(inode.noffs);
doffs = nxffs_rdle32(inode.doffs);
utc = nxffs_rdle32(inode.utc);
ecrc = nxffs_rdle32(inode.crc);
datlen = nxffs_rdle32(inode.datlen);
/* Misc. sanity checks */
if (noffs < blkinfo->offset + offset + SIZEOF_NXFFS_BLOCK_HDR)
{
/* The name begins before the inode header. This can't can't be
* a real inode header (or it is a corrupted one).
*/
return ERROR;
}
/* Can we verify the inode? We need to have the inode name in the same
* block to do that (or get access to the next block)
*/
if (doffs < blkinfo->offset + offset + SIZEOF_NXFFS_BLOCK_HDR)
{
/* The first data block begins before the inode header. This can't can't
* be a real inode header (or it is a corrupted one).
*/
return ERROR;
}
spaceleft = (blkinfo->nblocks - blkinfo->block) * blkinfo->geo.blocksize;
spaceleft -= (offset + SIZEOF_NXFFS_BLOCK_HDR);
if (datlen > spaceleft)
{
/* The data length is greater than what would fit in the rest of FLASH
* (even ignoring block and data header sizes.
*/
return ERROR;
}
/* The name begins after the inode header. Does it begin in this block? */
nextblock = blkinfo->offset + blkinfo->geo.blocksize;
if (noffs > nextblock)
{
/* Not than we cannot verify the inode header */
if (blkinfo->verbose)
{
fdbg(g_format, blkinfo->block, offset, "INODE", "UNVERFD", datlen);
}
return ERROR;
}
/* The name begins in this block. Does it also end in this block? */
if (noffs + inode.namlen > nextblock)
{
/* No.. Assume that this is not an inode. */
return ERROR;
}
/* Calculate the CRC */
state = inode.state;
inode.state = CONFIG_NXFFS_ERASEDSTATE;
nxffs_wrle32(inode.crc, 0);
crc = crc32((FAR const uint8_t *)&inode, SIZEOF_NXFFS_INODE_HDR);
crc = crc32part(&blkinfo->buffer[noffs - blkinfo->offset], inode.namlen, crc);
if (crc != ecrc)
{
fdbg(g_format, blkinfo->block, offset, "INODE", "CRC BAD", datlen);
return ERROR;
}
/* If must be a good header */
if (state == INODE_STATE_FILE)
{
if (blkinfo->verbose)
{
fdbg(g_format, blkinfo->block, offset, "INODE", "OK ", datlen);
}
}
else if (state == INODE_STATE_DELETED)
{
if (blkinfo->verbose)
{
fdbg(g_format, blkinfo->block, offset, "INODE", "DELETED", datlen);
}
}
else
{
fdbg(g_format, blkinfo->block, offset, "INODE", "CORRUPT", datlen);
}
/* Return the block-relative offset to the next byte after the inode name */
return noffs + inode.namlen - offset - blkinfo->offset;
}
#endif
/****************************************************************************
* Name: nxffs_analyzedata
*
* Description:
* Analyze one candidate inode found in the block.
*
****************************************************************************/
#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_FS)
static inline ssize_t nxffs_analyzedata(FAR struct nxffs_blkinfo_s *blkinfo,
int offset)
{
struct nxffs_data_s dathdr;
uint32_t ecrc;
uint16_t datlen;
uint32_t crc;
/* Copy and unpack the data block header */
memcpy(&dathdr, &blkinfo->buffer[offset], SIZEOF_NXFFS_DATA_HDR);
ecrc = nxffs_rdle32(dathdr.crc);
datlen = nxffs_rdle16(dathdr.datlen);
/* Sanity checks */
if (offset + SIZEOF_NXFFS_DATA_HDR + datlen > blkinfo->geo.blocksize)
{
/* Data does not fit in within the block, this can't be a data block */
return ERROR;
}
/* Calculate the CRC */
nxffs_wrle32(dathdr.crc, 0);
crc = crc32((FAR const uint8_t *)&dathdr, SIZEOF_NXFFS_DATA_HDR);
crc = crc32part(&blkinfo->buffer[offset + SIZEOF_NXFFS_DATA_HDR], datlen, crc);
if (crc != ecrc)
{
fdbg(g_format, blkinfo->block, offset, "DATA ", "CRC BAD", datlen);
return ERROR;
}
/* If must be a good header */
if (blkinfo->verbose)
{
fdbg(g_format, blkinfo->block, offset, "DATA ", "OK ", datlen);
}
return SIZEOF_NXFFS_DATA_HDR + datlen;
}
#endif
/****************************************************************************
* Name: nxffs_analyze
*
* Description:
* Analyze the content of one block.
*
****************************************************************************/
#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_FS)
static inline void nxffs_analyze(FAR struct nxffs_blkinfo_s *blkinfo)
{
FAR struct nxffs_block_s *blkhdr;
ssize_t nbytes;
int hdrndx;
int datndx;
int inndx;
int i;
/* Verify that there is a header on the block */
blkhdr = (FAR struct nxffs_block_s *)blkinfo->buffer;
if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) != 0)
{
fdbg(g_format, blkinfo->block, 0, "BLOCK", "NO FRMT",
blkinfo->geo.blocksize);
}
else if (blkhdr->state == BLOCK_STATE_GOOD)
{
size_t datsize = blkinfo->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR;
size_t nerased = nxffs_erased(blkinfo->buffer + SIZEOF_NXFFS_BLOCK_HDR, datsize);
if (nerased == datsize)
{
if (blkinfo->verbose)
{
fdbg(g_format, blkinfo->block, 0, "BLOCK", "ERASED ",
blkinfo->geo.blocksize);
}
return;
}
#if 0 /* Too much output, to little information */
else
{
fdbg(g_format, blkinfo->block, 0, "BLOCK", "IN USE ",
blkinfo->geo.blocksize);
}
#endif
}
else if (blkhdr->state == BLOCK_STATE_BAD)
{
fdbg(g_format, blkinfo->block, 0, "BLOCK", "BAD ",
blkinfo->geo.blocksize);
}
else
{
fdbg(g_format, blkinfo->block, 0, "BLOCK", "CORRUPT",
blkinfo->geo.blocksize);
}
/* Serach for Inode and data block headers. */
inndx = 0;
datndx = 0;
for (i = SIZEOF_NXFFS_BLOCK_HDR; i < blkinfo->geo.blocksize; i++)
{
uint8_t ch = blkinfo->buffer[i];
if (ch == g_inodemagic[inndx])
{
inndx++;
datndx = 0;
if (inndx == NXFFS_MAGICSIZE)
{
hdrndx = i - NXFFS_MAGICSIZE + 1;
nbytes = nxffs_analyzeinode(blkinfo, hdrndx);
if (nbytes > 0)
{
i = hdrndx + nbytes - 1;
}
inndx = 0;
}
}
else if (ch == g_datamagic[datndx])
{
datndx++;
inndx = 0;
if (datndx == NXFFS_MAGICSIZE)
{
hdrndx = i - NXFFS_MAGICSIZE + 1;
nbytes = nxffs_analyzedata(blkinfo, hdrndx);
if (nbytes > 0)
{
i = hdrndx + nbytes - 1;
}
datndx = 0;
}
}
}
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_dump
*
* Description:
* Dump a summary of the contents of an NXFFS file system. CONFIG_DEBUG
* and CONFIG_DEBUG_FS must be enabled for this function to do anything.
*
* Input Parameters:
* mtd - The MTD device that provides the interface to NXFFS-formatted
* media.
* verbose - FALSE: only show errors
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned to indicate the nature of the failure.
*
****************************************************************************/
int nxffs_dump(FAR struct mtd_dev_s *mtd, bool verbose)
{
#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_FS)
struct nxffs_blkinfo_s blkinfo;
int ret;
/* Get the volume geometry. (casting to uintptr_t first eliminates
* complaints on some architectures where the sizeof long is different
* from the size of a pointer).
*/
memset(&blkinfo, 0, sizeof(struct nxffs_blkinfo_s));
ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&blkinfo.geo));
if (ret < 0)
{
fdbg("ERROR: MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", -ret);
goto errout;
}
/* Save the verbose output indication */
blkinfo.verbose = verbose;
/* Allocate a buffer to hold one block */
blkinfo.buffer = (FAR uint8_t *)kmalloc(blkinfo.geo.blocksize);
if (!blkinfo.buffer)
{
fdbg("ERROR: Failed to allocate block cache\n");
ret = -ENOMEM;
goto errout;
}
/* Now read every block on the device */
fdbg("NXFFS Dump:\n");
fdbg(g_hdrformat);
blkinfo.nblocks = blkinfo.geo.erasesize * blkinfo.geo.neraseblocks / blkinfo.geo.blocksize;
for (blkinfo.block = 0, blkinfo.offset = 0;
blkinfo.block < blkinfo.nblocks;
blkinfo.block++, blkinfo.offset += blkinfo.geo.blocksize)
{
/* Read the next block */
ret = MTD_BREAD(mtd, blkinfo.block, 1, blkinfo.buffer);
if (ret < 0)
{
fdbg("ERROR: Failed to read block %d\n", blkinfo.block);
goto errout_with_block;
}
/* Analyze the block */
nxffs_analyze(&blkinfo);
}
fdbg("%d blocks analyzed\n", blkinfo.nblocks);
errout_with_block:
kfree(blkinfo.buffer);
errout:
return ret;
#else
return -ENOSYS;
#endif
}
+520
View File
@@ -0,0 +1,520 @@
/****************************************************************************
* fs/nxffs/nxffs_initialize.c
*
* Copyright (C) 2011 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 <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mtd.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Private Variables
****************************************************************************/
/* See fs_mount.c -- this structure is explicitly externed there.
* We use the old-fashioned kind of initializers so that this will compile
* with any compiler.
*/
const struct mountpt_operations nxffs_operations =
{
nxffs_open, /* open */
nxffs_close, /* close */
nxffs_read, /* read */
nxffs_write, /* write */
NULL, /* seek -- Use f_pos in struct file */
nxffs_ioctl, /* ioctl */
NULL, /* sync -- No buffered data */
nxffs_opendir, /* opendir */
NULL, /* closedir */
nxffs_readdir, /* readdir */
nxffs_rewinddir, /* rewinddir */
nxffs_bind, /* bind */
nxffs_unbind, /* unbind */
nxffs_statfs, /* statfs */
nxffs_unlink, /* unlink */
NULL, /* mkdir -- no directories */
NULL, /* rmdir -- no directories */
NULL, /* rename -- cannot rename in place if name is longer */
nxffs_stat /* stat */
};
/****************************************************************************
* Public Variables
****************************************************************************/
/* The magic number that appears that the beginning of each NXFFS (logical)
* block
*/
const uint8_t g_blockmagic[NXFFS_MAGICSIZE] = { 'B', 'l', 'c', 'k' };
/* The magic number that appears that the beginning of each NXFFS inode */
const uint8_t g_inodemagic[NXFFS_MAGICSIZE] = { 'I', 'n', 'o', 'd' };
/* The magic number that appears that the beginning of each NXFFS inode
* data block.
*/
const uint8_t g_datamagic[NXFFS_MAGICSIZE] = { 'D', 'a', 't', 'a' };
/* If CONFIG_NXFSS_PREALLOCATED is defined, then this is the single, pre-
* allocated NXFFS volume instance.
*/
#ifdef CONFIG_NXFSS_PREALLOCATED
struct nxffs_volume_s g_volume;
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_initialize
*
* Description:
* Initialize to provide NXFFS on an MTD interface
*
* Input Parameters:
* mtd - The MTD device that supports the FLASH interface.
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned to indicate the nature of the failure.
*
****************************************************************************/
int nxffs_initialize(FAR struct mtd_dev_s *mtd)
{
FAR struct nxffs_volume_s *volume;
struct nxffs_blkstats_s stats;
off_t threshold;
int ret;
/* If CONFIG_NXFSS_PREALLOCATED is defined, then this is the single, pre-
* allocated NXFFS volume instance.
*/
#ifdef CONFIG_NXFSS_PREALLOCATED
volume = &g_volume;
memset(volume, 0, sizeof(struct nxffs_volume_s));
#else
/* Allocate a NXFFS volume structure */
volume = (FAR struct nxffs_volume_s *)kzalloc(sizeof(struct nxffs_volume_s));
if (!volume)
{
ret = -ENOMEM;
goto errout;
}
#endif
/* Initialize the NXFFS volume structure */
volume->mtd = mtd;
volume->cblock = (off_t)-1;
sem_init(&volume->exclsem, 0, 1);
sem_init(&volume->wrsem, 0, 1);
/* Get the volume geometry. (casting to uintptr_t first eliminates
* complaints on some architectures where the sizeof long is different
* from the size of a pointer).
*/
ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&volume->geo));
if (ret < 0)
{
fdbg("MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", -ret);
goto errout_with_volume;
}
/* Allocate one I/O block buffer to general files system access */
volume->cache = (FAR uint8_t *)kmalloc(volume->geo.blocksize);
if (!volume->cache)
{
fdbg("Failed to allocate an erase block buffer\n");
ret = -ENOMEM;
goto errout_with_volume;
}
/* Pre-allocate one, full, in-memory erase block. This is needed for filesystem
* packing (but is useful in other places as well). This buffer is not needed
* often, but is best to have pre-allocated and in-place.
*/
volume->pack = (FAR uint8_t *)kmalloc(volume->geo.erasesize);
if (!volume->pack)
{
fdbg("Failed to allocate an I/O block buffer\n");
ret = -ENOMEM;
goto errout_with_cache;
}
/* Get the number of R/W blocks per erase block and the total number o
* R/W blocks
*/
volume->blkper = volume->geo.erasesize / volume->geo.blocksize;
volume->nblocks = volume->geo.neraseblocks * volume->blkper;
DEBUGASSERT((off_t)volume->blkper * volume->geo.blocksize == volume->geo.erasesize);
/* Check if there is a valid NXFFS file system on the flash */
ret = nxffs_blockstats(volume, &stats);
if (ret < 0)
{
fdbg("Failed to collect block statistics: %d\n", -ret);
goto errout_with_buffer;
}
/* If the proportion of good blocks is low or the proportion of unformatted
* blocks is high, then reformat the FLASH.
*/
threshold = stats.nblocks / 5;
if (stats.ngood < threshold || stats.nunformat > threshold)
{
/* Reformat the volume */
ret = nxffs_reformat(volume);
if (ret < 0)
{
fdbg("Failed to reformat the volume: %d\n", -ret);
goto errout_with_buffer;
}
/* Get statistics on the re-formatted volume */
#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_FS)
ret = nxffs_blockstats(volume, &stats);
if (ret < 0)
{
fdbg("Failed to collect block statistics: %d\n", -ret);
goto errout_with_buffer;
}
#endif
}
/* Get the file system limits */
ret = nxffs_limits(volume);
if (ret == OK)
{
return OK;
}
fdbg("Failed to calculate file system limits: %d\n", -ret);
errout_with_buffer:
kfree(volume->pack);
errout_with_cache:
kfree(volume->cache);
errout_with_volume:
#ifndef CONFIG_NXFSS_PREALLOCATED
kfree(volume);
#endif
return ret;
}
/****************************************************************************
* Name: nxffs_limits
*
* Description:
* Recalculate file system limits: (1) the FLASH offset to the first,
* valid inode, and (2) the FLASH offset to the first, unused byte after
* the last inode (invalid or not).
*
* The first, lower limit must be recalculated: (1) initially, (2)
* whenever the first inode is deleted, or (3) whenever inode is moved
* as part of the file system packing operation.
*
* The second, upper limit must be (1) incremented whenever new file
* data is written, or (2) recalculated as part of the file system packing
* operation.
*
* Input Parameters:
* volume - Identifies the NXFFS volume
*
* Returned Value:
* Zero on success. Otherwise, a negated error is returned indicating the
* nature of the failure.
*
****************************************************************************/
int nxffs_limits(FAR struct nxffs_volume_s *volume)
{
FAR struct nxffs_entry_s entry;
off_t block;
off_t offset;
bool noinodes = false;
int nerased;
int ret;
/* Get the offset to the first valid block on the FLASH */
block = 0;
ret = nxffs_validblock(volume, &block);
if (ret < 0)
{
fdbg("Failed to find a valid block: %d\n", -ret);
return ret;
}
/* Then find the first valid inode in or beyond the first valid block */
offset = block * volume->geo.blocksize;
ret = nxffs_nextentry(volume, offset, &entry);
if (ret < 0)
{
/* The value -ENOENT is special. This simply means that the FLASH
* was searched to the end and no valid inode was found... the file
* system is empty (or, in more perverse cases, all inodes are
* deleted or corrupted).
*/
if (ret != -ENOENT)
{
fdbg("nxffs_nextentry failed: %d\n", -ret);
return ret;
}
/* Set a flag the just indicates that no inodes were found. Later,
* we will set the location of the first inode to be the same as
* the location of the free FLASH region.
*/
fvdbg("No inodes found\n");
noinodes = true;
}
else
{
/* Save the offset to the first inode */
volume->inoffset = entry.hoffset;
fvdbg("First inode at offset %d\n", volume->inoffset);
/* Discard this entry and set the next offset. */
offset = nxffs_inodeend(volume, &entry);
nxffs_freeentry(&entry);
}
/* Now, search for the last valid entry */
if (!noinodes)
{
while ((ret = nxffs_nextentry(volume, offset, &entry)) == OK)
{
/* Discard the entry and guess the next offset. */
offset = nxffs_inodeend(volume, &entry);
nxffs_freeentry(&entry);
}
fvdbg("Last inode before offset %d\n", offset);
}
/* No inodes were found after this offset. Now search for a block of
* erased flash.
*/
nxffs_ioseek(volume, offset);
nerased = 0;
for (;;)
{
int ch = nxffs_getc(volume, 1);
if (ch < 0)
{
/* Failed to read the next byte... this could mean that the FLASH
* is full?
*/
if (volume->ioblock + 1 >= volume->nblocks &&
volume->iooffset + 1 >= volume->geo.blocksize)
{
/* Yes.. the FLASH is full. Force the offsets to the end of FLASH */
volume->froffset = volume->nblocks * volume->geo.blocksize;
fvdbg("Assume no free FLASH, froffset: %d\n", volume->froffset);
if (noinodes)
{
volume->inoffset = volume->froffset;
fvdbg("No inodes, inoffset: %d\n", volume->inoffset);
}
return OK;
}
// No? Then it is some other failure that we do not know how to handle
fdbg("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)
{
/* Okay.. we have a long stretch of erased FLASH in a valid
* FLASH block. Let's say that this is the beginning of
* the free FLASH region.
*/
volume->froffset = offset;
fvdbg("Free FLASH region begins at offset: %d\n", volume->froffset);
if (noinodes)
{
volume->inoffset = offset;
fvdbg("First inode at offset %d\n", volume->inoffset);
}
return OK;
}
}
else
{
offset += nerased + 1;
nerased = 0;
}
}
/* Won't get here */
return OK;
}
/****************************************************************************
* Name: nxffs_bind
*
* Description:
* This function implements a portion of the mount operation. Normmally,
* the bind() method allocates and initializes the mountpoint private data
* then binds the blockdriver inode to the filesystem private data. The
* final binding of the private data (containing the blockdriver) to the
* mountpoint is performed by mount().
*
* For the NXFFS, this sequence is quite different for the following
* reasons:
*
* 1. A block driver is not used. Instead, an MTD instance was provided
* to nxfs_initialize prior to mounting. So, in this sense, the NXFFS
* file system is already bound.
*
* 2. Since the volume was already bound to the MTD driver, all allocations
* and initializations have already been performed. Essentially, all
* mount operations have been bypassed and now we just need to provide
* the pre-allocated volume instance.
*
* 3. The tricky thing is that there is no mechanism to associate multiple
* NXFFS volumes to the multiple volumes bound to different MTD drivers.
* Hence, the limitation of a single NXFFS volume.
*
****************************************************************************/
int nxffs_bind(FAR struct inode *blkdriver, FAR const void *data,
FAR void **handle)
{
#ifndef CONFIG_NXFSS_PREALLOCATED
# error "No design to support dynamic allocation of volumes"
#else
/* If CONFIG_NXFSS_PREALLOCATED is defined, then this is the single, pre-
* allocated NXFFS volume instance.
*/
DEBUGASSERT(g_volume.cache);
*handle = &g_volume;
#endif
return OK;
}
/****************************************************************************
* Name: nxffs_unbind
*
* Description: This implements the filesystem portion of the umount
* operation.
*
****************************************************************************/
int nxffs_unbind(FAR void *handle, FAR struct inode **blkdriver)
{
#ifndef CONFIG_NXFSS_PREALLOCATED
# error "No design to support dynamic allocation of volumes"
#else
return g_volume.ofiles ? -EBUSY : OK;
#endif
}
+502
View File
@@ -0,0 +1,502 @@
/****************************************************************************
* fs/nxffs/nxffs_inode.c
*
* Copyright (C) 2011 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 <crc32.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mtd.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_rdentry
*
* Description:
* Read the inode entry at this offset. Called only from nxffs_nextentry().
*
* Input Parameters:
* volume - Describes the current volume.
* offset - The byte offset from the beginning of FLASH where the inode
* header is expected.
* entry - A memory location to return the expanded inode header
* information.
*
* Returned Value:
* Zero on success. Otherwise, a negated errno value is returned
* indicating the nature of the failure.
*
* On return, the
*
****************************************************************************/
static int nxffs_rdentry(FAR struct nxffs_volume_s *volume, off_t offset,
FAR struct nxffs_entry_s *entry)
{
struct nxffs_inode_s inode;
uint32_t ecrc;
uint32_t crc;
uint8_t state;
int namlen;
int ret;
DEBUGASSERT(volume && entry);
memset(entry, 0, sizeof(struct nxffs_entry_s));
/* Read the header at the FLASH offset */
nxffs_ioseek(volume, offset);
memcpy(&inode, &volume->cache[volume->iooffset], SIZEOF_NXFFS_INODE_HDR);
/* Check if the file state is recognized. */
state = inode.state;
if (state != INODE_STATE_FILE && state != INODE_STATE_DELETED)
{
/* This can't be a valid inode.. don't bother with the rest */
ret = -ENOENT;
goto errout_no_offset;
}
/* Copy the packed header into the user-friendly buffer */
entry->hoffset = offset;
entry->noffset = nxffs_rdle32(inode.noffs);
entry->doffset = nxffs_rdle32(inode.doffs);
entry->utc = nxffs_rdle32(inode.utc);
entry->datlen = nxffs_rdle32(inode.datlen);
/* Modify the packed header and perform the (partial) CRC calculation */
ecrc = nxffs_rdle32(inode.crc);
inode.state = CONFIG_NXFFS_ERASEDSTATE;
memset(inode.crc, 0, 4);
crc = crc32((FAR const uint8_t *)&inode, SIZEOF_NXFFS_INODE_HDR);
/* Allocate memory to hold the variable-length file name */
namlen = inode.namlen;
entry->name = (FAR char *)kmalloc(namlen + 1);
if (!entry->name)
{
fdbg("Failed to allocate name, namlen: %d\n", namlen);
ret = -ENOMEM;
goto errout_no_offset;
}
/* Seek to the expected location of the name in FLASH */
nxffs_ioseek(volume, entry->noffset);
/* Make sure that the block is in memory (the name may not be in the
* same block as the inode header.
*/
ret = nxffs_rdcache(volume, volume->ioblock);
if (ret < 0)
{
fdbg("nxffsx_rdcache failed: %d\n", -ret);
goto errout_with_name;
}
/* Read the file name from the expected offset in FLASH */
memcpy(entry->name, &volume->cache[volume->iooffset], namlen);
entry->name[namlen] = '\0';
/* Finish the CRC calculation and verify the entry */
crc = crc32part((FAR const uint8_t *)entry->name, namlen, crc);
if (crc != ecrc)
{
fdbg("CRC entry: %08x CRC calculated: %08x\n", ecrc, crc);
ret = -EIO;
goto errout_with_name;
}
/* We have a good inode header.. but it still could a deleted file.
* Check the file state.
*/
if (state != INODE_STATE_FILE)
{
/* It is a deleted file. But still, the data offset and the
* start size are good so we can use this information to advance
* further in FLASH memory and reduce the search time.
*/
offset = nxffs_inodeend(volume, entry);
nxffs_freeentry(entry);
ret = -ENOENT;
goto errout;
}
/* Everything is good.. leave the offset pointing to the valid inode
* header.
*/
return OK;
/* On errors where we are suspicious of the validity of the inode header,
* we need to increment the file position to just after the "good" magic
* word.
*/
errout_with_name:
nxffs_freeentry(entry);
errout_no_offset:
offset += NXFFS_MAGICSIZE;
errout:
nxffs_ioseek(volume, offset);
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_freeentry
*
* Description:
* The inode values returned by nxffs_nextentry() include allocated memory
* (specifically, the file name string). This function should be called
* to dispose of that memory when the inode entry is no longer needed.
*
* Note that the nxffs_entry_s containing structure is not freed. The
* caller may call kfree upon return of this function if necessary to
* free the entry container.
*
* Input parameters:
* entry - The entry to be freed.
*
* Returned Value:
* None
*
****************************************************************************/
void nxffs_freeentry(FAR struct nxffs_entry_s *entry)
{
if (entry->name)
{
kfree(entry->name);
entry->name = NULL;
}
}
/****************************************************************************
* Name: nxffs_nextentry
*
* Description:
* Search for the next valid inode starting at the provided FLASH offset.
*
* Input Parameters:
* volume - Describes the NXFFS volume.
* offset - The FLASH memory offset to begin searching.
* entry - A pointer to memory provided by the caller in which to return
* the inode description.
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno is returned
* that indicates the nature of the failure.
*
****************************************************************************/
int nxffs_nextentry(FAR struct nxffs_volume_s *volume, off_t offset,
FAR struct nxffs_entry_s *entry)
{
int nmagic;
int ch;
int nerased;
int ret;
/* Seek to the first FLASH offset provided by the caller. */
nxffs_ioseek(volume, offset);
/* Then begin searching */
nerased = 0;
nmagic = 0;
for (;;)
{
/* Read the next character */
ch = nxffs_getc(volume, SIZEOF_NXFFS_INODE_HDR - nmagic);
if (ch < 0)
{
fvdbg("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)
{
fvdbg("No entry found\n");
return -ENOENT;
}
}
else
{
nerased = 0;
/* Check for the magic sequence indicating the start of an NXFFS
* inode. There is the possibility of this magic sequnce occurring
* in FLASH data. However, the header CRC should distinguish
* between real NXFFS inode headers and such false alarms.
*/
if (ch != g_inodemagic[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_inodemagic[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 inode.
*/
else
{
/* The the FLASH offset where we found the matching magic number */
offset = nxffs_iotell(volume) - NXFFS_MAGICSIZE;
/* Try to extract the inode header from that position */
ret = nxffs_rdentry(volume, offset, entry);
if (ret == OK)
{
fvdbg("Found a valid fileheader, offset: %d\n", offset);
return OK;
}
/* False alarm.. keep looking */
nmagic = 0;
}
}
}
/* We won't get here, but to keep some compilers happy: */
return -ENOENT;
}
/****************************************************************************
* Name: nxffs_findinode
*
* Description:
* Search for an inode with the provided name starting with the first
* valid inode and proceeding to the end FLASH or until the matching
* inode is found.
*
* Input Parameters:
* volume - Describes the NXFFS volume
* name - The name of the inode to find
* entry - The location to return information about the inode.
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno is returned
* that indicates the nature of the failure.
*
****************************************************************************/
int nxffs_findinode(FAR struct nxffs_volume_s *volume, FAR const char *name,
FAR struct nxffs_entry_s *entry)
{
off_t offset;
int ret;
/* Start with the first valid inode that was discovered when the volume
* was created (or modified after the last file system re-packing).
*/
offset = volume->inoffset;
/* Loop, checking each NXFFS inode until either: (1) we find the NXFFS inode
* with the matching name, or (2) we reach the end of data written on the
* media.
*/
for (;;)
{
/* Get the next, valid NXFFS inode entry */
ret = nxffs_nextentry(volume, offset, entry);
if (ret < 0)
{
fvdbg("No inode found: %d\n", -ret);
return ret;
}
/* Is this the NXFFS inode we are looking for? */
else if (strcmp(name, entry->name) == 0)
{
/* Yes, return success with the entry data in 'entry' */
return OK;
}
/* Discard this entry and try the next one. Here we set the
* next offset using the raw data length as the offset
* increment. This is, of course, not accurate because it
* does not account for the data headers that enclose the
* data. But it is guaranteed to be less than or equal to
* the correct offset and, hence, better then searching
* byte-for-byte.
*/
offset = nxffs_inodeend(volume, entry);
nxffs_freeentry(entry);
}
/* We won't get here, but for some compilers: */
return -ENOENT;
}
/****************************************************************************
* Name: nxffs_inodeend
*
* Description:
* Return an *approximiate* FLASH offset to end of the inode data. The
* returned value is guaranteed to be be less then or equal to the offset
* of the thing-of-interest in FLASH. Parsing for interesting things
* can begin at that point.
*
* Assumption: The inode header has been verified by the caller and is
* known to contain valid data.
*
* Input Parameters:
* volume - Describes the NXFFS volume
* entry - Describes the inode.
*
* Returned Value:
* A FLASH offset to the (approximate) end of the inode data. No errors
* are detected.
*
****************************************************************************/
off_t nxffs_inodeend(FAR struct nxffs_volume_s *volume,
FAR struct nxffs_entry_s *entry)
{
/* A zero length file will have no data blocks */
if (entry->doffset)
{
/* This is the maximum size of one data block. It is the physcal size
* of the block minus the minimum number of headers: block sna data
*/
uint16_t maxsize = volume->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR - SIZEOF_NXFFS_DATA_HDR;
/* This is the minimum number of blocks require to span all of the
* inode data. One additional block could possibly be required -- we
* could make this accurate by looking at the size of the first, perhaps
* partial, data block.
*/
off_t minblocks = (entry->datlen + maxsize - 1) / maxsize;
/* And this is our best, simple guess at the end of the inode data */
return entry->doffset + entry->datlen + minblocks * SIZEOF_NXFFS_DATA_HDR;
}
/* Otherwise, return an offset that accounts only for the inode header and
* the inode name.
*/
/* All valid inodes will have a name associated with them */
DEBUGASSERT(entry->noffset);
return entry->noffset + strlen(entry->name);
}
+150
View File
@@ -0,0 +1,150 @@
/****************************************************************************
* fs/nxffs/nxffs_ioctl.c
*
* Copyright (C) 2011 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 <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/mtd.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_ioctl
*
* Description:
* Standard mountpoint ioctl method.
*
****************************************************************************/
int nxffs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct nxffs_volume_s *volume;
int ret;
fvdbg("cmd: %d arg: %08lx\n", cmd, arg);
/* Sanity checks */
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
/* Recover the file system state from the open file */
volume = 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 = sem_wait(&volume->exclsem);
if (ret != OK)
{
ret = -errno;
fdbg("sem_wait failed: %d\n", ret);
goto errout;
}
/* Only a reformat and optimize commands are supported */
if (cmd == FIOC_REFORMAT)
{
fvdbg("Reformat command\n");
/* We cannot reformat the volume if there are any open inodes */
if (volume->ofiles)
{
fdbg("Open files\n");
ret = -EBUSY;
goto errout_with_semaphore;
}
/* Re-format the volume -- all is lost */
ret = nxffs_reformat(volume);
}
else if (cmd == FIOC_OPTIMIZE)
{
fvdbg("Optimize command\n");
/* Pack the volume */
ret = nxffs_pack(volume);
}
else
{
/* No other commands supported */
ret = -ENOTTY;
}
errout_with_semaphore:
sem_post(&volume->exclsem);
errout:
return ret;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+473
View File
@@ -0,0 +1,473 @@
/****************************************************************************
* fs/nxffs/nxffs_read.c
*
* Copyright (C) 2011 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/fs/fs.h>
#include <nuttx/mtd.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* 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)
{
fdbg("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 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;
fvdbg("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 = sem_wait(&volume->exclsem);
if (ret != OK)
{
ret = -errno;
fdbg("sem_wait failed: %d\n", ret);
goto errout;
}
/* Check if the file was opened with read access */
if ((ofile->oflags & O_RDOK) == 0)
{
fdbg("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)
{
fdbg("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;
}
sem_post(&volume->exclsem);
return total;
errout_with_semaphore:
sem_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)
{
fvdbg("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)
{
fvdbg("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)
{
fvdbg("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 the the block containing the data block header is in the cache */
nxffs_ioseek(volume, offset);
ret = nxffs_rdcache(volume, volume->ioblock);
if (ret < 0)
{
fvdbg("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)
{
fdbg("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)
{
fdbg("CRC failure\n");
return -EIO;
}
/* Looks good! Return the data length and success */
*datlen = dlen;
return OK;
}
+267
View File
@@ -0,0 +1,267 @@
/****************************************************************************
* fs/nxffs/nxffs_reformat.c
*
* Copyright (C) 2011 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 <errno.h>
#include <debug.h>
#include <nuttx/mtd.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_format
*
* Description:
* Erase and reformat the entire volume.
*
* Input Parameters:
* volume - Describes the NXFFS volume to be reformatted.
*
* Returned Value:
* Zero on success or a negated errno on a failure. Failures will be
* returned n the case of MTD reported failures o.
* Nothing in the volume data itself will generate errors.
*
****************************************************************************/
static int nxffs_format(FAR struct nxffs_volume_s *volume)
{
FAR uint8_t *blkptr; /* Pointer to next block data */
off_t eblock; /* Erase block number */
off_t lblock; /* Logical block number */
ssize_t nxfrd; /* Number of blocks transferred */
int i;
int ret;
/* Create an image of one properly formatted erase sector */
memset(volume->pack, CONFIG_NXFFS_ERASEDSTATE, volume->geo.erasesize);
for (blkptr = volume->pack, i = 0;
i < volume->blkper;
blkptr += volume->geo.blocksize, i++)
{
FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s*)blkptr;
memcpy(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE);
blkhdr->state = BLOCK_STATE_GOOD;
}
/* Erase and format each erase block */
for (eblock = 0; eblock < volume->geo.neraseblocks; eblock++)
{
/* Erase the block */
ret = MTD_ERASE(volume->mtd, eblock, 1);
if (ret < 0)
{
fdbg("Erase block %d failed: %d\n", eblock, ret);
return ret;
}
/* Write the formatted image to the erase block */
lblock = eblock * volume->blkper;
nxfrd = MTD_BWRITE(volume->mtd, lblock, volume->blkper, volume->pack);
if (nxfrd != volume->blkper)
{
fdbg("Write erase block %d failed: %d\n", lblock, nxfrd);
return -EIO;
}
}
return OK;
}
/****************************************************************************
* Name: nxffs_badblocks
*
* Description:
* Verify each block and mark improperly erased blocks as bad.
*
* Input Parameters:
* volume - Describes the NXFFS volume to be reformatted.
*
* Returned Value:
* Zero on success or a negated errno on a failure. Failures will be
* returned n the case of MTD reported failures o.
* Nothing in the volume data itself will generate errors.
*
****************************************************************************/
static int nxffs_badblocks(FAR struct nxffs_volume_s *volume)
{
FAR uint8_t *blkptr; /* Pointer to next block data */
off_t eblock; /* Erase block number */
off_t lblock; /* Logical block number */
ssize_t nxfrd; /* Number of blocks transferred */
bool good; /* TRUE: block is good */
bool modified; /* TRUE: The erase block has been modified */
int i;
/* Read and verify each erase block */
for (eblock = 0; eblock < volume->geo.neraseblocks; eblock++)
{
/* Read the entire erase block */
lblock = eblock * volume->blkper;
nxfrd = MTD_BREAD(volume->mtd, lblock, volume->blkper, volume->pack);
if (nxfrd != volume->blkper)
{
fdbg("Read erase block %d failed: %d\n", lblock, nxfrd);
return -EIO;
}
/* Process each logical block */
modified = false;
for (blkptr = volume->pack, i = 0;
i < volume->blkper;
blkptr += volume->geo.blocksize, i++)
{
FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s*)blkptr;
/* Check block header */
good = true;
if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) != 0 ||
blkhdr->state != BLOCK_STATE_GOOD)
{
good = false;
}
/* Check that block data is erased */
else
{
size_t blocksize = volume->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR;
size_t erasesize = nxffs_erased(&blkptr[SIZEOF_NXFFS_BLOCK_HDR], blocksize);
good = (blocksize == erasesize);
}
/* If the block is bad, attempt to re-write the block header indicating
* a bad block (of course, if the block has failed, this may not be
* possible, depending upon failure modes.
*/
if (!good)
{
memcpy(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE);
blkhdr->state = BLOCK_STATE_BAD;
modified = true;
}
}
/* If the erase block was modified, then re-write it */
nxfrd = MTD_BWRITE(volume->mtd, lblock, volume->blkper, volume->pack);
if (nxfrd != volume->blkper)
{
fdbg("Write erase block %d failed: %d\n", lblock, nxfrd);
return -EIO;
}
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_reformat
*
* Description:
* Erase and reformat the entire volume. Verify each block and mark
* improperly erased blocks as bad.
*
* Input Parameters:
* volume - Describes the NXFFS volume to be reformatted.
*
* Returned Value:
* Zero on success or a negated errno on a failure. Failures will be
* returned n the case of MTD reported failures o.
* Nothing in the volume data itself will generate errors.
*
****************************************************************************/
int nxffs_reformat(FAR struct nxffs_volume_s *volume)
{
int ret;
/* Erase and reformat the entire volume */
ret = nxffs_format(volume);
if (ret < 0)
{
fdbg("Failed to reformat the volume: %d\n", -ret);
return ret;
}
/* Check for bad blocks */
ret = nxffs_badblocks(volume);
if (ret < 0)
{
fdbg("Bad block check failed: %d\n", -ret);
}
return ret;
}
+186
View File
@@ -0,0 +1,186 @@
/****************************************************************************
* fs/nxffs/nxffs_stat.c
*
* Copyright (C) 2011 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 <sys/stat.h>
#include <sys/statfs.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/fs/fs.h>
#include <nuttx/mtd.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_statfs
*
* Description: Return filesystem statistics
*
****************************************************************************/
int nxffs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
{
FAR struct nxffs_volume_s *volume;
int ret;
fvdbg("Entry\n");
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
/* Get the mountpoint private data from the NuttX inode structure */
volume = mountpt->i_private;
ret = sem_wait(&volume->exclsem);
if (ret != OK)
{
goto errout;
}
/* Fill in the statfs info */
#warning "Need f_bfree, f_bavail, f_files, f_ffree calculation"
memset(buf, 0, sizeof(struct statfs));
buf->f_type = NXFFS_MAGIC;
buf->f_bsize = volume->geo.blocksize;
buf->f_blocks = volume->nblocks;
buf->f_namelen = volume->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR - SIZEOF_NXFFS_INODE_HDR;
ret = OK;
sem_post(&volume->exclsem);
errout:
return ret;
}
/****************************************************************************
* Name: nxffs_stat
*
* Description: Return information about a file or directory
*
****************************************************************************/
int nxffs_stat(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct stat *buf)
{
FAR struct nxffs_volume_s *volume;
struct nxffs_entry_s entry;
int ret;
fvdbg("Entry\n");
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private && buf);
/* Get the mountpoint private data from the NuttX inode structure */
volume = mountpt->i_private;
ret = sem_wait(&volume->exclsem);
if (ret != OK)
{
goto errout;
}
/* Initialize the return stat instance */
memset(buf, 0, sizeof(struct stat));
buf->st_blksize = volume->geo.blocksize;
buf->st_blocks = entry.datlen / (volume->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR);
/* The requested directory must be the volume-relative "root" directory */
if (relpath && relpath[0] != '\0')
{
/* Not the top directory.. find the NXFFS inode with this name */
ret = nxffs_findinode(volume, relpath, &entry);
if (ret < 0)
{
fdbg("Inode '%s' not found: %d\n", -ret);
goto errout_with_semaphore;
}
buf->st_mode = S_IFREG|S_IXOTH|S_IXGRP|S_IXUSR;
buf->st_size = entry.datlen;
buf->st_atime = entry.utc;
buf->st_mtime = entry.utc;
buf->st_ctime = entry.utc;
}
else
{
/* It's a read/execute-only directory name */
buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR|S_IXOTH|S_IXGRP|S_IXUSR;
}
ret = OK;
errout_with_semaphore:
sem_post(&volume->exclsem);
errout:
return ret;
}
+187
View File
@@ -0,0 +1,187 @@
/****************************************************************************
* fs/nxffs/nxffs_unlink.c
*
* Copyright (C) 2011 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 <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/fs/fs.h>
#include <nuttx/mtd.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_rminode
*
* Description:
* Remove an inode from FLASH. This is the internal implementation of
* the file system unlinke operation.
*
* Input Parameters:
* volume - Describes the NXFFS volume.
* name - the name of the inode to be deleted.
*
* Returned Value:
* Zero is returned if the inode is successfully deleted. Otherwise, a
* negated errno value is returned indicating the nature of the failure.
*
****************************************************************************/
int nxffs_rminode(FAR struct nxffs_volume_s *volume, FAR const char *name)
{
FAR struct nxffs_ofile_s *ofile;
FAR struct nxffs_inode_s *inode;
struct nxffs_entry_s entry;
int ret;
/* Check if the file is open */
ofile = nxffs_findofile(volume, name);
if (ofile)
{
/* We can't remove the inode if it is open */
fdbg("Inode '%s' is open\n", name);
ret = -EBUSY;
goto errout;
}
/* Find the NXFFS inode */
ret = nxffs_findinode(volume, name, &entry);
if (ret < 0)
{
fdbg("Inode '%s' not found\n", name);
goto errout;
}
/* Set the position to the FLASH offset of the file header (nxffs_findinode
* should have left the block in the cache).
*/
nxffs_ioseek(volume, entry.hoffset);
/* Make sure the the block is in the cache */
ret = nxffs_rdcache(volume, volume->ioblock);
if (ret < 0)
{
fdbg("Failed to read data into cache: %d\n", ret);
goto errout_with_entry;
}
/* Change the file status... it is no longer valid */
inode = (FAR struct nxffs_inode_s *)&volume->cache[volume->iooffset];
inode->state = INODE_STATE_DELETED;
/* Then write the cached block back to FLASH */
ret = nxffs_wrcache(volume);
if (ret < 0)
{
fdbg("Failed to read data into cache: %d\n", ret);
}
errout_with_entry:
nxffs_freeentry(&entry);
errout:
return ret;
}
/****************************************************************************
* Name: nxffs_unlink
*
* Description: Remove a file
*
****************************************************************************/
int nxffs_unlink(FAR struct inode *mountpt, FAR const char *relpath)
{
FAR struct nxffs_volume_s *volume;
int ret;
fvdbg("Entry\n");
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
/* Get the mountpoint private data from the NuttX inode structure */
volume = mountpt->i_private;
ret = sem_wait(&volume->exclsem);
if (ret != OK)
{
goto errout;
}
/* Then remove the NXFFS inode */
ret = nxffs_rminode(volume, relpath);
sem_post(&volume->exclsem);
errout:
return ret;
}
+181
View File
@@ -0,0 +1,181 @@
/****************************************************************************
* fs/nxffs/nxffs_util.c
*
* Copyright (C) 2011 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 "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_rdle16
*
* Description:
* Get a (possibly unaligned) 16-bit little endian value.
*
* Input Parameters:
* val - A pointer to the first byte of the little endian value.
*
* Returned Value:
* A uint16_t representing the whole 16-bit integer value
*
****************************************************************************/
uint16_t nxffs_rdle16(FAR const uint8_t *val)
{
return (uint16_t)val[1] << 8 | (uint16_t)val[0];
}
/****************************************************************************
* Name: nxffs_wrle16
*
* Description:
* Put a (possibly unaligned) 16-bit little endian value.
*
* Input Parameters:
* dest - A pointer to the first byte to save the little endian value.
* val - The 16-bit value to be saved.
*
* Returned Value:
* None
*
****************************************************************************/
void nxffs_wrle16(uint8_t *dest, uint16_t val)
{
dest[0] = val & 0xff; /* Little endian means LS byte first in byte stream */
dest[1] = val >> 8;
}
/****************************************************************************
* Name: nxffs_rdle32
*
* Description:
* Get a (possibly unaligned) 32-bit little endian value.
*
* Input Parameters:
* val - A pointer to the first byte of the little endian value.
*
* Returned Value:
* A uint32_t representing the whole 32-bit integer value
*
****************************************************************************/
uint32_t nxffs_rdle32(FAR const uint8_t *val)
{
/* Little endian means LS halfword first in byte stream */
return (uint32_t)nxffs_rdle16(&val[2]) << 16 | (uint32_t)nxffs_rdle16(val);
}
/****************************************************************************
* Name: nxffs_wrle32
*
* Description:
* Put a (possibly unaligned) 32-bit little endian value.
*
* Input Parameters:
* dest - A pointer to the first byte to save the little endian value.
* val - The 32-bit value to be saved.
*
* Returned Value:
* None
*
****************************************************************************/
void nxffs_wrle32(uint8_t *dest, uint32_t val)
{
/* Little endian means LS halfword first in byte stream */
nxffs_wrle16(dest, (uint16_t)(val & 0xffff));
nxffs_wrle16(dest+2, (uint16_t)(val >> 16));
}
/****************************************************************************
* Name: nxffs_erased
*
* Description:
* Check if a block of memory is in the erased state.
*
* Input Parameters:
* buffer - Address of the start of the memory to check.
* buflen - The number of bytes to check.
*
* Returned Values:
* The number of erased bytes found at the beginning of the memory region.
*
****************************************************************************/
size_t nxffs_erased(FAR const uint8_t *buffer, size_t buflen)
{
size_t nerased = 0;
for (; nerased < buflen; nerased++)
{
if (*buffer != CONFIG_NXFFS_ERASEDSTATE)
{
break;
}
buffer++;
}
return nerased;
}
+853
View File
@@ -0,0 +1,853 @@
/****************************************************************************
* fs/nxffs/nxffs_write.c
*
* Copyright (C) 2011 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/fs/fs.h>
#include <nuttx/mtd.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* 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)
{
fdbg("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)
{
fdbg("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 previoulsy calculated CRC value */
if (crc != wrfile->crc)
{
fdbg("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 remaimining 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)
{
fdbg("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)
{
fdbg("nxffs_wrblkdhr failed: %d\n", -ret);
return ret;
}
}
/* Return the number of bytes written to FLASH this time */
return nbytestowrite;
}
/****************************************************************************
* 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;
fvdbg("Write %d bytes to 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 */
wrfile = (FAR struct nxffs_wrfile_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 = sem_wait(&volume->exclsem);
if (ret != OK)
{
ret = -errno;
fdbg("sem_wait failed: %d\n", ret);
goto errout;
}
/* Check if the file was opened with write access */
if ((wrfile->ofile.oflags & O_WROK) == 0)
{
fdbg("File not open for write access\n");
ret = -EACCES;
goto errout_with_semaphore;
}
/* 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)
{
fdbg("Failed to allocate a data block: %d\n", -ret);
goto errout_with_semaphore;
}
}
/* 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)
{
fdbg("Failed to verify FLASH data block: %d\n", -ret);
goto errout_with_semaphore;
}
/* 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)
{
fdbg("Failed to append to FLASH to a data block: %d\n", -ret);
goto errout_with_semaphore;
}
/* 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_semaphore:
sem_post(&volume->exclsem);
errout:
return ret;
}
/****************************************************************************
* 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 oject 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 */
fdbg("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)
{
fdbg("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 oject 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 verifed.
*
* 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)
{
fdbg("Failed to read block %d: %d\n", volume->ioblock, -ret);
return ret;
}
/* Search to the very end of this block if we have to */
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 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 we have looked at every byte in the 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)
{
fdbg("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.
*/
fdbg("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)
{
fdbg("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;
}