mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-11-03 00:11:36 +08:00
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:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user