twl_wrapsdk/build/libraries/fatfs/ARM7/prfsnvio.c
Shirait 4fe5688d29 add SD driver and FATFS library (tentative)
git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/twl_wrapsdk/trunk@96 4ee2a332-4b2b-5046-8439-1ba90f034370
2007-05-30 10:22:28 +00:00

407 lines
14 KiB
C

/* EBS - RTFS (Real Time File Manager)
*
* Copyright EBS Inc. 1987-2003
* All rights reserved.
* This code may not be redistributed in source or linkable object form
* without the consent of its author.
*/
/* PRFSNVIO.C - ERTFS-PRO FailSafe journal file routines */
/* Contains source code to implement journal file accesses read, write,
open and create.
The following routines are exported:
failsafe_reopen_nv_buffer()
failsafe_create_nv_buffer()
failsafe_write_nv_buffer()
failsafe_read_nv_buffer()
Note: These routines create and maintains a journal file on the
disk. If you have a closed system and you prefer to use system non
volatile ram instead please modify these for functions to use that
resource instead of a disk based file.
*/
#include <rtfs.h>
#if (INCLUDE_FAILSAFE_CODE)
static BOOLEAN open_failsafe_file(FAILSAFECONTEXT *pfscntxt, BOOLEAN create);
/*
* failsafe_reopen_nv_buffer - Open the failsafe buffer.
*
* Summary:
* BOOLEAN failsafe_reopen_nv_buffer(FAILSAFECONTEXT *pfscntxt)
*
* Description:
*
* This routine must check for the existence of a failsafe buffer on the
* current disk or in system non volatile ram and return TRUE if one exists,
* or FALSE if one does not.
* It may use the field nv_buffer_handle in the structure pointed to
* by pfscntxt to store a handle for later access by failsafe_read_nv_buffer()
* and failsafe_write_nv_buffer().
*
*/
BOOLEAN failsafe_reopen_nv_buffer(FAILSAFECONTEXT *pfscntxt)
{
return(open_failsafe_file(pfscntxt, FALSE));
}
/*
* failsafe_create_nv_buffer - Create the failsafe buffer.
*
* Summary:
* BOOLEAN failsafe_create_nv_buffer(FAILSAFECONTEXT *pfscntxt)
*
* Description:
*
* This routine must create a failsafe buffer on the current disk or
* in system NV ram and return TRUE if successful, FALSE if it is
* unsuccessful.
* It may use the field nv_buffer_handle in the sructure pointed to
* by pfscntxt to store a handle for later access by failsafe_read_nv_buffer()
* and failsafe_write_nv_buffer().
*
* The failsafe buffer must contain space for:
* (pfscntxt->num_remap_blocks + pfscntxt->num_index_blocks)
* 512 byte blocks.
*
* The source code in prfailsf.c implements the failsafe buffer in a hidden
* file named \FAILSAFE on the disk.
* The reference implementation is convenient and should be adequate for
* most applications. If it is more desirable to implement the failsafe
* buffer in flash or NVRAM then these functions should be modified to access
* that media instead.
*
*/
BOOLEAN failsafe_create_nv_buffer(FAILSAFECONTEXT *pfscntxt)
{
/* Set the journal file size to zero to force the open routine to
open the file. If the first open succeeds and the size is as big
as pfscntxt->correct_journal_size then we won't have to reopen it
in subsequant calls to restore and create */
pfscntxt->journal_file_size = 0;
return(open_failsafe_file(pfscntxt, TRUE));
}
/*
* failsafe_read_nv_buffer - Write a block to the failsafe buffer.
*
* Summary:
* BOOLEAN failsafe_write_nv_buffer(
* FAILSAFECONTEXT *pfscntxt,
* dword block_no,
* byte *pblock)
*
* Description:
*
* This routine must write one block to the block at offset block_no
* in the failsafe buffer on the current disk or in system NV ram and
* return TRUE if successful, FALSE if it is unsuccessful.
*
*/
BOOLEAN failsafe_write_nv_buffer(FAILSAFECONTEXT *pfscntxt, dword block_no, byte *pblock)
{
if (!pfscntxt->nv_buffer_handle || (pfscntxt->journal_file_size <= block_no))
{
rtfs_set_errno(PEIOERRORWRITEJOURNAL);
return(FALSE);
}
return (devio_write(pfscntxt->pdrive->driveno,
pfscntxt->nv_buffer_handle+block_no, pblock,
(word)1, TRUE));
}
/*
* failsafe_read_nv_buffer - Read a block from the failsafe buffer.
*
* Summary:
* BOOLEAN failsafe_read_nv_buffer(
* FAILSAFECONTEXT *pfscntxt,
* dword block_no,
* byte *pblocks)
*
* Description:
*
* This routine must read one block from the block at offset block_no
* in the failsafe buffer on the current disk or in system NV ram and
* return TRUE if successful, FALSE if it is unsuccessful.
*
*/
BOOLEAN failsafe_read_nv_buffer(FAILSAFECONTEXT *pfscntxt, dword block_no, byte *pblock)
{
if (!pfscntxt->nv_buffer_handle || (pfscntxt->journal_file_size <= block_no))
{
rtfs_set_errno(PEIOERRORREADJOURNAL);
return(FALSE);
}
return(devio_read(pfscntxt->pdrive->driveno,
pfscntxt->nv_buffer_handle+block_no, pblock,
(word)1, TRUE));
}
/*
* open_failsafe_file - low level open/create of failsafe journal file
*
* Summary:
* BOOLEAN open_failsafe_file(FAILSAFECONTEXT *pfscntxt, BOOLEAN create)
*
* Description:
*
* This opens the failsafe file and check if it contains enough contiguous
* blocks to hold a worst case journal file. If create is not requested and
* it can't find the file or the file is too small then it fails.
* If create is requested then it creates and extends the file as necessary.
*
* This routine is called when from ERTFS with the drive already locked, it
* uses low level access routines to create or open a contiguous file
*
* returns TRUE if successful, FALSE if it is unsuccessful.
*
*/
static BOOLEAN validate_failsafe_blocks(FAILSAFECONTEXT *pfscntxt, BOOLEAN fix_errors);
static BOOLEAN open_failsafe_file(FAILSAFECONTEXT *pfscntxt, BOOLEAN create)
{
DROBJ *proot, *pobj;
byte path[32]; /* leave room for "FAILSAFE" 32 is plenty */
byte filename[32];
byte fileext[4];
dword first_cluster, contig_clusters, n_clusters,next_cluster,required_journal_file_size,first_hit;
int end_of_chain;
DDRIVE *pdrive;
FAILSAFECONTEXT *saved_pfscntxt;
BOOLEAN doupdate = FALSE;
BOOLEAN doflush = FALSE;
pfscntxt->nv_buffer_handle = 0;
pdrive = pfscntxt->pdrive;
if (!pdrive)
return(FALSE);
saved_pfscntxt = (FAILSAFECONTEXT *) pdrive->pfscntxt;
pdrive->pfscntxt = (void *) 0;
proot = pc_get_root(pdrive);
if (!proot ||
!pc_parsepath((byte *)&path[0],(byte *)&filename[0],fileext,
rtfs_strtab_user_string(USTRING_SYS_FSFILENAME)))
{
return_error:
pdrive->pfscntxt = (void *) saved_pfscntxt;
return(FALSE);
}
/* See if it exists */
pobj = pc_get_inode( 0, proot, filename, fileext, GET_INODE_MATCH);
if (!pobj)
{
if (!create)
goto return_error;
else
{
/* Create a file entry in the root, hidden, readonly */
pobj = pc_mknode( proot, (byte *)filename, fileext, ARDONLY|AHIDDEN, 0);
if (!pobj)
{
goto return_error;
}
doupdate = TRUE;
}
}
first_cluster = pc_finode_cluster(pobj->pdrive,pobj->finode);
if (first_cluster)
n_clusters = FATOP(pdrive)->fatop_get_chain(pdrive, first_cluster,&next_cluster, 0xffffffff, &end_of_chain);
else
n_clusters = 0;
pfscntxt->journal_file_size = n_clusters * pdrive->secpalloc;
if (create)
{ /* Creating or extend or truncate to the correct size */
n_clusters = (CLUSTERTYPE)
((pfscntxt->num_remap_blocks +
pfscntxt->num_index_blocks +
pdrive->secpalloc-1) >> pdrive->log2_secpalloc);
required_journal_file_size = n_clusters * pdrive->secpalloc;
if (pfscntxt->journal_file_size != required_journal_file_size)
{
doupdate = TRUE;
doflush = TRUE;
/* Truncate if necessary */
if (first_cluster)
{
FATOP(pdrive)->fatop_freechain(pdrive, first_cluster, 0, 0xffffffff);
pc_pfinode_cluster(pdrive,pobj->finode,0);
pobj->finode->fsize = 0;
}
/* Allocate */
first_cluster = 0;
#if (defined(AF_CONTIGUOUS_MODE_FORCE)) /* Should be true for version 5.0 and up */
contig_clusters = FATOP(pdrive)->fatop_alloc_chain(pdrive, &first_cluster, n_clusters, AF_CONTIGUOUS_MODE_FORCE);
#else
contig_clusters = 0;
first_hit = 0;
/* Bug fix Nan 9, 2006
fatop_alloc_chain - allocates clusters starting from
pdrive->free_contig_pointer. or from first_cluster, if
first_cluster is not zero.
The problem is that if first_cluster is not zero
we link it to the newly allocated cluster chain, which is
not what we want. So the code has been modified to advance
pdrive->free_contig_pointer and always have first_cluster == 0 */
pdrive->free_contig_pointer = pdrive->free_contig_base;
while (contig_clusters < n_clusters)
{ /* alloc chain can't guarantee n_clusters contiguous so
loop and free until we get contig_clusters == n_clusters */
first_cluster = 0; /* must be zero or it will be linked. */
contig_clusters = FATOP(pobj->pdrive)->fatop_alloc_chain(pobj->pdrive, &first_cluster, n_clusters, FALSE);
if (!first_hit)
first_hit = first_cluster;
else if (first_hit == first_cluster)
{
FATOP(pdrive)->fatop_freechain(pdrive, first_cluster, 0, 0xffffffff);
break;
}
if (contig_clusters == 0)
break;
if (contig_clusters < n_clusters)
{
FATOP(pdrive)->fatop_freechain(pdrive, first_cluster, 0, 0xffffffff);
pdrive->free_contig_pointer = first_cluster+contig_clusters;
}
}
pdrive->free_contig_pointer = pdrive->free_contig_base; /* restore pointer */
#endif
if (contig_clusters < n_clusters)
{
pfscntxt->journal_file_size = 0;
pobj->finode->fsize = 0;
pc_pfinode_cluster(pdrive,pobj->finode,0);
pc_rmnode(pobj);
doupdate = FALSE;
}
else
{
pfscntxt->journal_file_size = contig_clusters * pdrive->secpalloc;
pc_pfinode_cluster(pdrive,pobj->finode,first_cluster);
pobj->finode->fsize = pfscntxt->journal_file_size * 512;
}
}
if ((doflush && !FATOP(pdrive)->fatop_flushfat(pdrive->driveno)) ||
(doupdate && !pc_update_inode(pobj, TRUE, TRUE)) )
{
pfscntxt->journal_file_size = 0;
}
}
if (pfscntxt->journal_file_size == 0)
{
pfscntxt->nv_buffer_handle = 0;
pc_freeobj(pobj);
pc_free_all_blk(pdrive);
pc_free_all_fat_blocks(&pdrive->fatcontext);
goto return_error;
}
else
{
pfscntxt->nv_buffer_handle =
pobj->pdrive->partition_base +
pc_cl2sector(pobj->pdrive, first_cluster);
pc_freeobj(pobj);
/* Clear buffer pool and fat buffer pool */
pc_free_all_blk(pdrive);
pc_free_all_fat_blocks(&pdrive->fatcontext);
#if (CFG_VALIDATE_JOURNAL)
if (!validate_failsafe_blocks(pfscntxt, TRUE))
{
pfscntxt->journal_file_size = 0;
pfscntxt->nv_buffer_handle = 0;
/* Tell the upper levels that an IO error occured.
This is unrecoverable */
pfscntxt->open_status = FS_STATUS_IO_ERROR;
pdrive->pfscntxt = (void *) saved_pfscntxt;
return(FALSE);
}
#endif
pdrive->pfscntxt = (void *) saved_pfscntxt;
return(TRUE);
}
}
#if (CFG_VALIDATE_JOURNAL)
static byte static_validate_buffer[CFG_VALIDATE_BUFFER_SIZE*512];
byte *get_validate_buffer(word *pblocks_per_buffer)
{
*pblocks_per_buffer = CFG_VALIDATE_BUFFER_SIZE;
return(&static_validate_buffer[0]);
}
static BOOLEAN validate_failsafe_blocks(FAILSAFECONTEXT *pfscntxt, BOOLEAN fix_errors)
{
dword block_no,n_blocks_left;
word blocks_per_buffer, blocks_this_access;
byte *pbuffer;
int driveno;
driveno = pfscntxt->pdrive->driveno;
pbuffer = get_validate_buffer(&blocks_per_buffer);
if (blocks_per_buffer > 128)
blocks_per_buffer = 128;
/* Read the journal file */
block_no = pfscntxt->nv_buffer_handle;
n_blocks_left = pfscntxt->journal_file_size;
while (n_blocks_left)
{
if (n_blocks_left > (dword) blocks_per_buffer)
blocks_this_access = blocks_per_buffer;
else
blocks_this_access = (word) n_blocks_left;
if (!devio_read(driveno, block_no, pbuffer,blocks_this_access,TRUE))
break;
block_no += blocks_this_access;
n_blocks_left -= blocks_this_access;
}
if (n_blocks_left == 0)
return(TRUE); /* Read all blocks without failure */
if (!fix_errors)
return(FALSE);
/* We got here because a read error occured
Write all zeroes to the file to see if we can correct it. */
/* Write the journal file */
block_no = pfscntxt->nv_buffer_handle;
n_blocks_left = pfscntxt->journal_file_size;
while (n_blocks_left)
{
if (n_blocks_left > (dword) blocks_per_buffer)
blocks_this_access = blocks_per_buffer;
else
blocks_this_access = (word) n_blocks_left;
/* Zero the buffer every time we use it. This takes a little
extra time but in the unlikely event other threads are using it
for reads we will zero it before we write each time */
rtfs_memset((void *)pbuffer, 0, (int)(blocks_this_access*512));
if (!devio_write(driveno, block_no, pbuffer,blocks_this_access,TRUE))
break;
block_no += blocks_this_access;
n_blocks_left -= blocks_this_access;
}
if (n_blocks_left != 0)
return(FALSE); /* Fail because We could not write the file.. */
else
{
/* We overwrote, now revalidate */
return(validate_failsafe_blocks(pfscntxt, FALSE));
}
}
#endif /* (CFG_VALIDATE_JOURNAL) */
#endif /* (INCLUDE_FAILSAFE_CODE) */