mirror of
https://github.com/peteratebs/tinyfatfs.git
synced 2025-06-18 16:55:42 -04:00
696 lines
30 KiB
C
696 lines
30 KiB
C
/*
|
|
* EBS - RTFS (Real Time File Manager)
|
|
*
|
|
* Copyright EBS Inc. 1987-2012
|
|
* All rights reserved.
|
|
* This code may not be redistributed in source or linkable object form
|
|
* without the consent of its author.
|
|
*/
|
|
|
|
#include "rtfslite.h"
|
|
#include <stdio.h>
|
|
#if (RTFSL_INCLUDE_FAILSAFE_SUPPORT)
|
|
|
|
/* Frame structure
|
|
1xxx :28:32 = 64 CLUSTER - OPCODE START LENGTH
|
|
031 :32 = 64 DOSINODE - OPCODE SECTOR INDEX|TABLEINDEX|DELETED
|
|
*/
|
|
|
|
#define DIRENT_RECORD 0x80000000
|
|
#define DIRENT_SECTOR_MASK 0x7fffffff
|
|
#define CLUSTER_DELETE_RECORD 0x30000000
|
|
#define CLUSTER_CHAIN_RECORD 0x20000000
|
|
#define CLUSTER_TCHAIN_RECORD 0x10000000
|
|
#define CLUSTER_INSTANCE_RECORD 0x00000000
|
|
#define CLUSTER_RECORD_MASK 0x30000000
|
|
#define CLUSTER_VALUE_MASK 0x0fffffff
|
|
|
|
#define REPLACEMENTRECORDSTART 1
|
|
|
|
#define DELETEEDINODEMARKER 0xfffe
|
|
|
|
#define RELATIONSHIP_NONE 0
|
|
#define RELATIONSHIP_OVERLAP 1
|
|
#define RELATIONSHIP_ADJACENT_LEFT 2
|
|
#define RELATIONSHIP_ADJACENT_RIGHT 3
|
|
int rtfslfs_cluster_map(unsigned long cluster_number,unsigned long value)
|
|
{
|
|
int i;
|
|
int get_next_record;
|
|
unsigned long cluster_type;
|
|
char emit_new_record;
|
|
|
|
struct rtfsl_failsafe_context *pfs=rtfsl.rtfsl_current_failsafe_context;
|
|
if (!pfs)
|
|
return 1;
|
|
/* We may need up to 3 replacement records, fail if we don't have them */
|
|
if (pfs->journal_buffer_free < (3*REPLACEMENT_RECORD_SIZE_BYTES))
|
|
return RTFSL_JOURNALFULL_CLUSTER;
|
|
|
|
/* set up default instructions in case we don't overlap a region */
|
|
emit_new_record=1;
|
|
if (value==0)
|
|
{
|
|
cluster_type = CLUSTER_DELETE_RECORD;
|
|
value = cluster_number;
|
|
}
|
|
else if (value == (rtfsl.current_dr.end_cluster_marker|0xf))
|
|
{
|
|
cluster_type = CLUSTER_TCHAIN_RECORD;
|
|
value = cluster_number;
|
|
}
|
|
else if (cluster_number+1 == value)
|
|
{
|
|
cluster_type = CLUSTER_CHAIN_RECORD;
|
|
value = cluster_number;
|
|
}
|
|
else
|
|
{
|
|
cluster_type = CLUSTER_INSTANCE_RECORD;
|
|
}
|
|
//
|
|
//
|
|
//REHERE - Add a new state to discard a cluster from all records if it is used.
|
|
get_next_record=1;
|
|
for (i = 0; get_next_record && i < (int)*pfs->preplacement_record_count;i++)
|
|
{
|
|
unsigned long end_record_cluster,start_record_cluster,record_type;
|
|
int relationship;
|
|
char overwrite_current_record,split_current_record,change_start_value,change_end_value,change_record_type;
|
|
if ((pfs->preplacement_records[i][0]&DIRENT_RECORD)!=0) /* Skip non cluster remap records */
|
|
continue;
|
|
|
|
start_record_cluster=pfs->preplacement_records[i][0]&CLUSTER_VALUE_MASK;
|
|
record_type=pfs->preplacement_records[i][0]&CLUSTER_RECORD_MASK;
|
|
if (record_type==CLUSTER_INSTANCE_RECORD)
|
|
end_record_cluster=start_record_cluster;
|
|
else
|
|
end_record_cluster=pfs->preplacement_records[i][1];
|
|
|
|
overwrite_current_record=split_current_record=change_start_value=change_end_value=change_record_type=0;
|
|
if (cluster_number+1 == start_record_cluster)
|
|
relationship=RELATIONSHIP_ADJACENT_LEFT;
|
|
else if (cluster_number-1 == end_record_cluster)
|
|
relationship=RELATIONSHIP_ADJACENT_RIGHT;
|
|
else if (start_record_cluster<=cluster_number&&end_record_cluster>=cluster_number)
|
|
relationship=RELATIONSHIP_OVERLAP;
|
|
else
|
|
relationship=RELATIONSHIP_NONE;
|
|
switch (relationship)
|
|
{
|
|
case RELATIONSHIP_NONE:
|
|
default:
|
|
break;
|
|
case RELATIONSHIP_ADJACENT_LEFT: /* Cluster is 1 to the left of the region */
|
|
switch (record_type)
|
|
{
|
|
case CLUSTER_TCHAIN_RECORD: /* Cluster is 1 to the left of a terminated chain record */
|
|
switch (cluster_type)
|
|
{
|
|
case CLUSTER_TCHAIN_RECORD:
|
|
case CLUSTER_INSTANCE_RECORD:
|
|
case CLUSTER_DELETE_RECORD:
|
|
default:
|
|
break;
|
|
case CLUSTER_CHAIN_RECORD:
|
|
change_start_value=1; /* Chain cluster immed to left of a terminated chain, set new start of terminated chain to cluster number. */;
|
|
break;
|
|
}
|
|
break;
|
|
case CLUSTER_INSTANCE_RECORD: /* Instance record is 1 to the left of a link record, no connection */
|
|
break;
|
|
case CLUSTER_DELETE_RECORD: /* Cluster is 1 to the left of an erase record */
|
|
{
|
|
switch (cluster_type)
|
|
{
|
|
case CLUSTER_TCHAIN_RECORD:
|
|
case CLUSTER_INSTANCE_RECORD:
|
|
case CLUSTER_CHAIN_RECORD:
|
|
default:
|
|
break;
|
|
case CLUSTER_DELETE_RECORD:
|
|
change_start_value=1; /* delete cluster immed to left of a deleted chain, set new start of deleted chain to cluster number. */;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case CLUSTER_CHAIN_RECORD: /* Cluster is 1 to the left of a chain record */
|
|
{
|
|
switch (cluster_type)
|
|
{
|
|
case CLUSTER_TCHAIN_RECORD:
|
|
case CLUSTER_INSTANCE_RECORD:
|
|
case CLUSTER_DELETE_RECORD:
|
|
default:
|
|
break;
|
|
case CLUSTER_CHAIN_RECORD:
|
|
change_start_value=1; /* Chain cluster immed to left of a chain, set new start of chain to cluster number. */;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case RELATIONSHIP_ADJACENT_RIGHT: /* Cluster is 1 to the right of the region */
|
|
switch (record_type)
|
|
{
|
|
case CLUSTER_TCHAIN_RECORD: /* Cluster is 1 to the right of a terminator record, no connection */
|
|
case CLUSTER_INSTANCE_RECORD: /* Cluster is 1 to the right of a link record, no connection */
|
|
default:
|
|
break;
|
|
case CLUSTER_DELETE_RECORD: /* Cluster is 1 to the right of an erase record */
|
|
{
|
|
switch (cluster_type)
|
|
{
|
|
case CLUSTER_TCHAIN_RECORD:
|
|
case CLUSTER_INSTANCE_RECORD:
|
|
case CLUSTER_CHAIN_RECORD:
|
|
break;
|
|
case CLUSTER_DELETE_RECORD:
|
|
change_end_value=1; /* delete cluster immed to right of a deleted region, set new end of deleted chain to cluster number. */;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case CLUSTER_CHAIN_RECORD: /* Cluster is 1 to the right of the chain region */
|
|
{
|
|
switch (cluster_type)
|
|
{
|
|
case CLUSTER_INSTANCE_RECORD:
|
|
case CLUSTER_DELETE_RECORD:
|
|
default:
|
|
break;
|
|
case CLUSTER_TCHAIN_RECORD:
|
|
change_record_type=1; /* Append a tchain to a chain so change type, fall through to change end */
|
|
case CLUSTER_CHAIN_RECORD:
|
|
change_end_value=1; /* Chain cluster immed to right of a chain, set new end of chain to cluster number. */;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case RELATIONSHIP_OVERLAP: /* Cluster overlaps the region */
|
|
switch (record_type)
|
|
{
|
|
case CLUSTER_TCHAIN_RECORD:
|
|
{
|
|
switch (cluster_type)
|
|
{
|
|
case CLUSTER_TCHAIN_RECORD: /* terminate cluster overlaps a terminated chain, split the record into 2 terminated chains and don't emit a new record */
|
|
{
|
|
if (cluster_number==end_record_cluster)
|
|
{
|
|
/* Writing a terminator at the current end of a terminated chain is a no-op */
|
|
get_next_record=0;
|
|
emit_new_record=0;
|
|
}
|
|
else
|
|
{
|
|
split_current_record=1; /* terminate cluster overlaps a terminated chain, split into two terminated chains no need to emit a new record */
|
|
emit_new_record=0;
|
|
}
|
|
}
|
|
break;
|
|
case CLUSTER_INSTANCE_RECORD: /* link cluster overlaps a terminated chain */
|
|
case CLUSTER_DELETE_RECORD: /* erase cluster overlaps a terminated chain */
|
|
{
|
|
split_current_record=1; /* Split current record in two and emit a new record where the hole is */
|
|
}
|
|
break;
|
|
case CLUSTER_CHAIN_RECORD: /* chain cluster overlaps a terminated chain */
|
|
{
|
|
if (cluster_number==end_record_cluster)
|
|
{
|
|
change_record_type=1; /* End of a terminated chain record is now a chain, change to a chain record and return */;
|
|
}
|
|
else
|
|
{
|
|
/* A chain cluster overlapping a tchain in the middle is a no-op */
|
|
get_next_record=0;
|
|
emit_new_record=0;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case CLUSTER_INSTANCE_RECORD: /* The record is a one cluster instance record and they overlap, force an overwrite of the record with the current cluster */
|
|
{
|
|
overwrite_current_record=1;
|
|
break;
|
|
}
|
|
case CLUSTER_DELETE_RECORD: /* The record is an erase and they overlap */
|
|
{
|
|
switch (cluster_type)
|
|
{
|
|
case CLUSTER_TCHAIN_RECORD:
|
|
case CLUSTER_CHAIN_RECORD:
|
|
case CLUSTER_INSTANCE_RECORD:
|
|
{
|
|
split_current_record=1;
|
|
}
|
|
break;
|
|
case CLUSTER_DELETE_RECORD:
|
|
get_next_record=0;
|
|
emit_new_record=0;
|
|
get_next_record=0;
|
|
/* Already erased.. is a no-op */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case CLUSTER_CHAIN_RECORD: /* The record i a chain and they overlap */
|
|
{
|
|
switch (cluster_type)
|
|
{
|
|
case CLUSTER_TCHAIN_RECORD:
|
|
{
|
|
if (cluster_number==end_record_cluster)
|
|
{
|
|
change_record_type=1;
|
|
}
|
|
else
|
|
{
|
|
split_current_record=1;
|
|
}
|
|
}
|
|
break;
|
|
case CLUSTER_DELETE_RECORD:
|
|
case CLUSTER_INSTANCE_RECORD:
|
|
{
|
|
split_current_record=1;
|
|
}
|
|
break;
|
|
case CLUSTER_CHAIN_RECORD:
|
|
emit_new_record=0;
|
|
/* A chain overlapping a chain is a no-op */
|
|
get_next_record=0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (change_record_type)
|
|
{
|
|
pfs->preplacement_records[i][0]=cluster_type|(pfs->preplacement_records[i][0]&CLUSTER_VALUE_MASK);
|
|
emit_new_record=0;
|
|
get_next_record=0;
|
|
}
|
|
if (change_start_value)
|
|
{
|
|
pfs->preplacement_records[i][0]=cluster_number|(pfs->preplacement_records[i][0]&CLUSTER_RECORD_MASK);
|
|
emit_new_record=0;
|
|
get_next_record=0;
|
|
}
|
|
if (change_end_value)
|
|
{
|
|
pfs->preplacement_records[i][1]=cluster_number;
|
|
emit_new_record=0;
|
|
get_next_record=0;
|
|
}
|
|
/* Overwrite the current record if instructed or if we are inserting a new record between
|
|
the start and end of a record that is only one cluster long */
|
|
if (overwrite_current_record||(split_current_record&&(end_record_cluster==start_record_cluster)))
|
|
{
|
|
pfs->preplacement_records[i][0]=start_record_cluster|cluster_type;
|
|
pfs->preplacement_records[i][1]=value;
|
|
split_current_record=0;
|
|
emit_new_record=0;
|
|
get_next_record=0;
|
|
}
|
|
/* We are splitting a record in two. If emit_new_record is non zero we need to remove one cluster from the range that
|
|
we are splitting. If emit_new_record is non zero we know that the start and end are not the same. */
|
|
if (split_current_record)
|
|
{
|
|
get_next_record=0;
|
|
/* Copy the record we are going to split into the next free record slot, don't reserve it yet */
|
|
*pfs->preplacement_records[*pfs->preplacement_record_count]=*pfs->preplacement_records[i];
|
|
|
|
if (emit_new_record)
|
|
{
|
|
if (cluster_number==start_record_cluster)
|
|
{ /* We're overwriting the first cluster in a range, don't split it just move the start, let emit create a new record */
|
|
pfs->preplacement_records[i][0]=(start_record_cluster+1)|(pfs->preplacement_records[i][0]&CLUSTER_RECORD_MASK);
|
|
}
|
|
else
|
|
{ /* We're overwriting a cluster in the range, it isn't the first cluster and the range is > 1, split the range */
|
|
/* Change the end of the original record */
|
|
pfs->preplacement_records[i][1]=cluster_number-1;
|
|
/* If the original record was a tchain make it a chain */
|
|
if (record_type==CLUSTER_TCHAIN_RECORD)
|
|
{
|
|
pfs->preplacement_records[i][0]=CLUSTER_CHAIN_RECORD|(pfs->preplacement_records[i][0]&CLUSTER_VALUE_MASK);
|
|
}
|
|
/* If we are at the end, don't split, the emit further down will creat the new record */
|
|
if (cluster_number==end_record_cluster)
|
|
{
|
|
;
|
|
}
|
|
else
|
|
{ /* Consume the cloned record, for the fragment of the region after the cluster we carved out. Just change the start point */
|
|
pfs->preplacement_records[*pfs->preplacement_record_count][0]=(start_record_cluster+1)|(pfs->preplacement_records[*pfs->preplacement_record_count][0]&CLUSTER_RECORD_MASK);
|
|
*pfs->preplacement_record_count= *pfs->preplacement_record_count + 1;
|
|
pfs->journal_buffer_free -= REPLACEMENT_RECORD_SIZE_BYTES;
|
|
}
|
|
}
|
|
}
|
|
else /* We splitting a record but not taking a cluster away */
|
|
{
|
|
if (cluster_number==start_record_cluster)
|
|
{ /* We're splitting at the start of a range, terminate the old record at the start */
|
|
pfs->preplacement_records[i][1]=cluster_number;
|
|
/* Consume the cloned record, for the fragment of the region after the cluster we carved out. Just change the start point */
|
|
pfs->preplacement_records[*pfs->preplacement_record_count][0]=(cluster_number+1)|(pfs->preplacement_records[*pfs->preplacement_record_count][0]&CLUSTER_RECORD_MASK);
|
|
}
|
|
else
|
|
{ /* We're splitting but not at the start of a range, terminate the old record at the cluster - 1 */
|
|
pfs->preplacement_records[i][1]=cluster_number-1;
|
|
pfs->preplacement_records[*pfs->preplacement_record_count][0]=(cluster_number)|(pfs->preplacement_records[*pfs->preplacement_record_count][0]&CLUSTER_RECORD_MASK);
|
|
}
|
|
/* If the original record was a tchain make it a chain */
|
|
if (record_type==CLUSTER_TCHAIN_RECORD)
|
|
{
|
|
pfs->preplacement_records[i][0]=CLUSTER_CHAIN_RECORD|(pfs->preplacement_records[i][0]&CLUSTER_VALUE_MASK);
|
|
}
|
|
*pfs->preplacement_record_count= *pfs->preplacement_record_count + 1;
|
|
pfs->journal_buffer_free -= REPLACEMENT_RECORD_SIZE_BYTES;
|
|
}
|
|
}
|
|
} /* End for loop */
|
|
if (emit_new_record)
|
|
{ /* or one past if we are making a hole */
|
|
pfs->preplacement_records[*pfs->preplacement_record_count][0]= cluster_number|cluster_type;
|
|
pfs->preplacement_records[*pfs->preplacement_record_count][1]= value;
|
|
*pfs->preplacement_record_count= *pfs->preplacement_record_count + 1;
|
|
pfs->journal_buffer_free -= REPLACEMENT_RECORD_SIZE_BYTES;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned long rtfslfs_cluster_remap(unsigned long cluster,unsigned long value)
|
|
{
|
|
int i;
|
|
unsigned long new_instance_cluster,new_delete_start,new_chain_start,new_instance_value,new_chain_end,new_delete_end;
|
|
struct rtfsl_failsafe_context *pfs=rtfsl.rtfsl_current_failsafe_context;
|
|
if (!pfs)
|
|
return value;
|
|
new_delete_start=new_chain_start=new_instance_cluster=new_delete_end=new_chain_end=new_instance_value=0;
|
|
|
|
|
|
for (i = 0; i < (int)*pfs->preplacement_record_count;i++)
|
|
{
|
|
unsigned long cluster_record_type;
|
|
unsigned long end_record_cluster,start_record_cluster;
|
|
|
|
if ((pfs->preplacement_records[i][0]&DIRENT_RECORD)!=0) /* Skip non cluster remap records */
|
|
continue;
|
|
start_record_cluster=pfs->preplacement_records[i][0]&CLUSTER_VALUE_MASK;
|
|
cluster_record_type=pfs->preplacement_records[i][0]&CLUSTER_RECORD_MASK;
|
|
if (cluster_record_type==CLUSTER_INSTANCE_RECORD)
|
|
end_record_cluster=start_record_cluster;
|
|
else
|
|
end_record_cluster=pfs->preplacement_records[i][1];
|
|
if (cluster<start_record_cluster) /* No relationship */
|
|
continue;
|
|
if (cluster_record_type==CLUSTER_INSTANCE_RECORD)
|
|
{
|
|
if (start_record_cluster==cluster)
|
|
return pfs->preplacement_records[i][1];
|
|
}
|
|
else
|
|
{ /* It a delete, chain or tchain */
|
|
if (start_record_cluster<=cluster&&end_record_cluster>=cluster)
|
|
{
|
|
if (cluster_record_type==CLUSTER_DELETE_RECORD)
|
|
return RTFSL_JOURNALDELETED_CLUSTER;
|
|
/* It's either a tchain or a chain */
|
|
/* last cluster of a tchain is chain terminator */
|
|
if (cluster_record_type==CLUSTER_TCHAIN_RECORD && end_record_cluster==cluster)
|
|
return rtfsl.current_dr.end_cluster_marker|0xf;
|
|
else
|
|
return cluster+1;
|
|
}
|
|
}
|
|
}
|
|
/* Fall through and return the unmapped value */
|
|
return value;
|
|
}
|
|
|
|
|
|
int rtfslfs_dirent_remap(unsigned long sector,unsigned long index, struct rtfsl_dosinode *p_dos_inode,int reading)
|
|
{
|
|
struct rtfsl_failsafe_context *pfs=rtfsl.rtfsl_current_failsafe_context;
|
|
int replacement_record_index=-1;
|
|
int remap_index=0;
|
|
if (pfs)
|
|
{
|
|
int i;
|
|
for (i=0; i < (int)*pfs->preplacement_record_count;i++)
|
|
{
|
|
if ((pfs->preplacement_records[i][0]&DIRENT_RECORD)==0) /* Skip non dirent remap records */
|
|
continue;
|
|
if ((pfs->preplacement_records[i][0]&DIRENT_SECTOR_MASK)==sector)
|
|
{
|
|
if ((pfs->preplacement_records[i][1]&0x0000ffff)==index)
|
|
{
|
|
unsigned long remap_index = pfs->preplacement_records[i][1]>>16;
|
|
if (reading)
|
|
{
|
|
if (remap_index == DELETEEDINODEMARKER)
|
|
p_dos_inode->fname[0]=PCDELETE;
|
|
else
|
|
ANSImemcpy(p_dos_inode, pfs->journal_buffer+remap_index,REPLACEMENT_DOSINODESIZE_SIZE_BYTES);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if (p_dos_inode->fname[0]==PCDELETE)
|
|
{ /* Mark the dosinode deleted. we can't reclaim the dossinode space in the buffer but it will be ignored */
|
|
pfs->preplacement_records[i][1]= (unsigned long)(DELETEEDINODEMARKER)<<16|(unsigned long)index;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if (remap_index != DELETEEDINODEMARKER)
|
|
{
|
|
ANSImemcpy(pfs->journal_buffer+remap_index, p_dos_inode,REPLACEMENT_DOSINODESIZE_SIZE_BYTES);
|
|
return 1;
|
|
}
|
|
/* We found a record for the dossinode but its flagged deleted and we need to copy record in place */
|
|
replacement_record_index=i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (reading)
|
|
return 0;
|
|
/* If we get here we have to allocate a records */
|
|
{
|
|
int bytes_needed=0;
|
|
if (p_dos_inode->fname[0]!=PCDELETE)
|
|
bytes_needed+=REPLACEMENT_DOSINODESIZE_SIZE_BYTES;
|
|
if (replacement_record_index<0)
|
|
bytes_needed+=REPLACEMENT_RECORD_SIZE_BYTES;
|
|
if (pfs->journal_buffer_free < bytes_needed)
|
|
return RTFSL_ERROR_JOURNAL_FULL;
|
|
}
|
|
if (p_dos_inode->fname[0]==PCDELETE)
|
|
remap_index=DELETEEDINODEMARKER;
|
|
else
|
|
{
|
|
*pfs->pcurrent_dosinode_offset-=REPLACEMENT_DOSINODESIZE_SIZE_BYTES;
|
|
pfs->journal_buffer_free-=REPLACEMENT_DOSINODESIZE_SIZE_BYTES;
|
|
remap_index=(int)*pfs->pcurrent_dosinode_offset;
|
|
ANSImemcpy(pfs->journal_buffer+remap_index,p_dos_inode,REPLACEMENT_DOSINODESIZE_SIZE_BYTES);
|
|
}
|
|
if (replacement_record_index<0)
|
|
{
|
|
replacement_record_index=*pfs->preplacement_record_count;
|
|
*pfs->preplacement_record_count= *pfs->preplacement_record_count + 1;
|
|
}
|
|
pfs->preplacement_records[replacement_record_index][0]=sector|DIRENT_RECORD;
|
|
pfs->preplacement_records[replacement_record_index][1]=remap_index<<16|index;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
/* Format of the buffer: replacement_record_count|replacement_records|->growsup Grows down<-dosinode1|dosinode0|]*/
|
|
int rtfslfs_start(void) /*__apifn__*/
|
|
{
|
|
int rval;
|
|
struct rtfsl_failsafe_context *pfs;
|
|
|
|
pfs=rtfsl.rtfsl_current_failsafe_context=&rtfsl.rtfsl_failsafe_context;
|
|
ANSImemset(rtfsl.rtfsl_current_failsafe_context,0,sizeof(*rtfsl.rtfsl_current_failsafe_context));
|
|
rtfsl.rtfsl_current_failsafe_context->journal_buffer=rtfsl.rtfslfs_sector_buffer;
|
|
rtfsl.rtfsl_current_failsafe_context->journal_buffer_size=RTFSL_CFG_FSBUFFERSIZEBYTES;
|
|
rval=rtfslfs_access_journal(RTFSLFS_JTEST);
|
|
if (rval==0)
|
|
{
|
|
pfs->preplacement_record_count=(unsigned long *) pfs->journal_buffer;
|
|
pfs->pcurrent_dosinode_offset=(unsigned long *) (pfs->journal_buffer+4);
|
|
pfs->preplacement_records = (treplacement_record *) (pfs->journal_buffer+8);
|
|
*pfs->pcurrent_dosinode_offset=pfs->journal_buffer_size;
|
|
*pfs->preplacement_record_count=0;
|
|
pfs->journal_buffer_free = pfs->journal_buffer_size-8;
|
|
/*
|
|
For reload..
|
|
pfs->journal_buffer_free = (pfs->journal_buffer_size-8-( (*pfs->preplacement_record_count*8)+ pfs->journal_buffer_size-*pfs->pcurrent_dosinode_offset) );
|
|
*/
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
int rtfslfs_flush(void) /*__apifn__*/
|
|
{
|
|
int rval;
|
|
rval=rtfsl_flush_info_sec();
|
|
if (rval==0)
|
|
rval=rtfslfs_access_journal(RTFSLFS_JWRITE);
|
|
return rval;
|
|
}
|
|
|
|
static int _rtfslfs_sync(void);
|
|
int rtfslfs_sync(void) /*__apifn__*/
|
|
{
|
|
int rval;
|
|
rval=_rtfslfs_sync();
|
|
if (rval==0)
|
|
rval=rtfslfs_start();
|
|
return rval;
|
|
}
|
|
|
|
int rtfslfs_restore(void) /*__apifn__*/
|
|
{
|
|
int rval=0;
|
|
rval=rtfslfs_start(); /* Initialize offset, pointers etc. */
|
|
if (rval==0)
|
|
{
|
|
rval=rtfslfs_access_journal(RTFSLFS_JREAD); /* read the journal */
|
|
if (rval==0)
|
|
rval=_rtfslfs_sync();
|
|
}
|
|
if (rval==0)
|
|
rval=rtfsl_diskopen();
|
|
return rval;
|
|
}
|
|
|
|
static int _rtfslfs_sync(void)
|
|
{
|
|
int rval=0;
|
|
struct rtfsl_failsafe_context *pfs=rtfsl.rtfsl_current_failsafe_context;
|
|
if (pfs)
|
|
{
|
|
unsigned char *b=0;
|
|
struct rtfsl_dosinode *p_dos_inode;
|
|
unsigned long sector,offset,index;
|
|
int replacement_record_index;
|
|
int buffernumber;
|
|
/* null out the failsafe context pointer so the cluster routines go to the volume, not the journal */
|
|
rtfsl.rtfsl_current_failsafe_context=0;
|
|
|
|
for (replacement_record_index=0;replacement_record_index<(int)*pfs->preplacement_record_count;replacement_record_index++)
|
|
{
|
|
if (pfs->preplacement_records[replacement_record_index][0]&DIRENT_RECORD)
|
|
sector=pfs->preplacement_records[replacement_record_index][0]&DIRENT_SECTOR_MASK;
|
|
else
|
|
sector=0;
|
|
if (pfs->preplacement_records[replacement_record_index][0]&DIRENT_RECORD)
|
|
{
|
|
index = pfs->preplacement_records[replacement_record_index][1]&0x0000ffff;
|
|
offset = pfs->preplacement_records[replacement_record_index][1]>>16;
|
|
buffernumber=rtfsl_read_sector_buffer(sector);
|
|
if (buffernumber<0)
|
|
return buffernumber;
|
|
b = rtfsl_buffer_address(buffernumber);
|
|
p_dos_inode = (struct rtfsl_dosinode *) b;
|
|
p_dos_inode += index;
|
|
if (offset == DELETEEDINODEMARKER && p_dos_inode->fname[0]!=PCDELETE)
|
|
{
|
|
p_dos_inode->fname[0]=PCDELETE;
|
|
rtfsl_mark_sector_buffer(buffernumber); /* Mark the buffer dirty */
|
|
}
|
|
#if (RTFSL_INCLUDE_FAT32)
|
|
else if (sector==rtfsl.current_dr.infosec)
|
|
{
|
|
unsigned long *pl= (unsigned long *)p_dos_inode;
|
|
pl++;
|
|
rtfsl.current_dr.free_alloc=*pl++;
|
|
rtfsl.current_dr.next_alloc=*pl;
|
|
rtfsl.current_dr.flags|=RTFSL_FAT_CHANGED_FLAG;
|
|
rval=rtfsl_flush_info_sec();
|
|
if (rval<0)
|
|
return rval;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
if (ANSImemcmp(p_dos_inode,pfs->journal_buffer+offset,REPLACEMENT_DOSINODESIZE_SIZE_BYTES)!=0)
|
|
{
|
|
ANSImemcpy(p_dos_inode,pfs->journal_buffer+offset,REPLACEMENT_DOSINODESIZE_SIZE_BYTES);
|
|
rtfsl_mark_sector_buffer(buffernumber); /* Mark the buffer dirty */
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned long cluster_record_type,end_record_cluster,current_cluster,value;
|
|
current_cluster=pfs->preplacement_records[replacement_record_index][0]&CLUSTER_VALUE_MASK;
|
|
cluster_record_type=pfs->preplacement_records[replacement_record_index][0]&CLUSTER_RECORD_MASK;
|
|
end_record_cluster=pfs->preplacement_records[replacement_record_index][1];
|
|
if (cluster_record_type==CLUSTER_DELETE_RECORD)
|
|
{
|
|
value=0;
|
|
do {
|
|
rval=fatop_buff_get_frag(current_cluster|RTFSL_WRITE_CLUSTER, &value, 1);
|
|
} while(rval==0 && current_cluster++<end_record_cluster);
|
|
}
|
|
else if (cluster_record_type==CLUSTER_INSTANCE_RECORD)
|
|
{
|
|
value=end_record_cluster;
|
|
rval=fatop_buff_get_frag(current_cluster|RTFSL_WRITE_CLUSTER, &value, 1);
|
|
}
|
|
else /* if (cluster_record_type==CLUSTER_CHAIN_RECORD)||cluster_record_type==CLUSTER_TCHAIN_RECORD */
|
|
{
|
|
value=current_cluster+1;
|
|
while (rval==0 && current_cluster!=end_record_cluster)
|
|
{
|
|
rval=fatop_buff_get_frag(current_cluster|RTFSL_WRITE_CLUSTER, &value, 1);
|
|
value+=1;
|
|
current_cluster+=1;
|
|
};
|
|
if (cluster_record_type==CLUSTER_TCHAIN_RECORD)
|
|
value = rtfsl.current_dr.end_cluster_marker|0xf;
|
|
if (rval==0)
|
|
rval=fatop_buff_get_frag(current_cluster|RTFSL_WRITE_CLUSTER, &value, 1);
|
|
}
|
|
}
|
|
}
|
|
if (rval==0)
|
|
rval=rtfsl_flush_all_buffers();
|
|
if (rval==0 && *pfs->preplacement_record_count)
|
|
{
|
|
rtfsl.rtfsl_current_failsafe_context=pfs;
|
|
rtfslfs_access_journal(RTFSLFS_JCLEAR);
|
|
rtfsl.rtfsl_current_failsafe_context=0;
|
|
}
|
|
}
|
|
return rval;
|
|
}
|
|
#endif
|