mirror of
https://github.com/rvtr/twl_wrapsdk.git
synced 2025-10-31 06:11:10 -04:00
git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/twl_wrapsdk/trunk@96 4ee2a332-4b2b-5046-8439-1ba90f034370
1072 lines
37 KiB
C
1072 lines
37 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.
|
|
*/
|
|
/* RTFATXX.C - Low level File allocation table management functions.
|
|
|
|
Routines in this file include:
|
|
|
|
fatxx_alloc_chain - Allocate a chain from the FAT.
|
|
fatxx_find_free_cluster- Find the first free cluster in a given range.
|
|
fatxx_clalloc - Allocate a single cluster in the fragmented region.
|
|
fatxx_clgrow - Grow a directory chain in the fragmented region.
|
|
fatxx_clnext - Get the next cluster in a chain.
|
|
fatxx_clrelease - Return a cluster to the free list.
|
|
fatxx_faxx - Get a value from the FAT.
|
|
fatxx_flushfat - Make sure the FAT is up to date on disk.
|
|
fatxx_freechain - Release a chain to the free list.
|
|
fatxx_cl_truncate_dir - Truncate a cluster chain.
|
|
fatxx_get_chain - Return contiguous clusters in a chain.
|
|
fatxx_pfaxx - Put a value to the FAT.
|
|
fatxx_pfswap - Swap a block of the FAT into the cache.
|
|
fatxx_fword - Get or put a value from the swap cache.
|
|
*/
|
|
|
|
|
|
|
|
#include <rtfs.h>
|
|
|
|
CLUSTERTYPE fatxx_alloc_chain(DDRIVE *pdr, CLUSTERTYPE *pstart_cluster, CLUSTERTYPE n_clusters, BOOLEAN dolink);
|
|
CLUSTERTYPE fatxx_find_free_cluster(DDRIVE *pdr, CLUSTERTYPE startpt, CLUSTERTYPE endpt, int *is_error);
|
|
CLUSTERTYPE fatxx_clnext(DDRIVE *pdr, CLUSTERTYPE clno);
|
|
BOOLEAN fatxx_clrelease_dir(DDRIVE *pdr, CLUSTERTYPE clno);
|
|
BOOLEAN fatxx_faxx(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE *pvalue);
|
|
BOOLEAN fatxx_flushfat(int driveno);
|
|
BOOLEAN fatxx_freechain(DDRIVE *pdr, CLUSTERTYPE cluster, dword min_clusters_to_free, dword max_clusters_to_free);
|
|
CLUSTERTYPE fatxx_cl_truncate_dir(DDRIVE *pdr, CLUSTERTYPE cluster, CLUSTERTYPE l_cluster);
|
|
CLUSTERTYPE fatxx_get_chain(DDRIVE *pdr, CLUSTERTYPE start_cluster, CLUSTERTYPE *pnext_cluster, CLUSTERTYPE n_clusters, int *end_of_chain);
|
|
BOOLEAN fatxx_pfaxxterm(DDRIVE *pdr, CLUSTERTYPE clno);
|
|
BOOLEAN fatxx_pfaxx(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE value);
|
|
BOOLEAN fatxx_fword(DDRIVE *pdr, CLUSTERTYPE index, word *pvalue, BOOLEAN putting);
|
|
BOOLEAN fatxx_pfpdword(DDRIVE *pdr, dword index, dword *pvalue);
|
|
BOOLEAN fatxx_pfgdword(DDRIVE *pdr, dword index, dword *value);
|
|
|
|
/******************************************************************************
|
|
PC_ALLOC_CHAIN - Allocate as many contiguous clusters as possible.
|
|
|
|
Description
|
|
Reserve up to n_clusters contiguous clusters from the FAT and
|
|
return the number of contiguous clusters reserved.
|
|
If pstart_cluster points to a valid cluster and dolink is true
|
|
then link the new chain to it.
|
|
|
|
Returns
|
|
Returns the number of contiguous clusters found. Or zero on an error.
|
|
pstart_cluster contains the address of the start of the chain on
|
|
return.
|
|
|
|
*****************************************************************************/
|
|
#if (RTFS_WRITE)
|
|
|
|
CLUSTERTYPE fatxx_alloc_chain(DDRIVE *pdr, CLUSTERTYPE *pstart_cluster, CLUSTERTYPE n_clusters, BOOLEAN dolink) /*__fatfn__*/
|
|
{
|
|
CLUSTERTYPE start_cluster;
|
|
CLUSTERTYPE first_new_cluster;
|
|
CLUSTERTYPE clno;
|
|
CLUSTERTYPE n_contig;
|
|
CLUSTERTYPE value;
|
|
CLUSTERTYPE last_cluster; /* NEW for 5/18/97 bug fix */
|
|
int is_error;
|
|
|
|
is_error = 0;
|
|
start_cluster = *pstart_cluster;
|
|
if (start_cluster &&
|
|
( (start_cluster < 2) || (start_cluster > pdr->maxfindex) ) )
|
|
{
|
|
rtfs_set_errno(PEINVALIDCLUSTER); /* fatxx_alloc_chain: bad cluster value internal error */
|
|
return (0);
|
|
}
|
|
|
|
/* If the user provided a cluster we find the next cluster beyond that
|
|
one. Otherwise we look at the disk structure and find the next
|
|
free cluster in the free cluster region after the current best guess
|
|
of the region. If that fails we look to the beginning of the region
|
|
and if that fails we look in the non-contiguous region. */
|
|
|
|
/* Begin changes for the 5/18/97 bug fix */
|
|
clno = 0;
|
|
/* NEW */
|
|
if (start_cluster)
|
|
{
|
|
/* search from the start_cluster hint to the end of the fat */
|
|
/* use: pdr->maxfindex+1 because find_free_cluster uses < end not <= */
|
|
clno = fatxx_find_free_cluster(pdr, start_cluster, pdr->maxfindex+1, &is_error);
|
|
if (is_error) /* Error reading fat */
|
|
return(0);
|
|
/* If we search again search only to the start_cluster */
|
|
last_cluster = start_cluster;
|
|
}
|
|
else
|
|
/* When we search again search to the end */
|
|
/* use: pdr->maxfindex+1 because find_free_cluster uses < end not <= */
|
|
last_cluster = pdr->maxfindex + 1;
|
|
/* Check the most likely place to find contiguous space */
|
|
if (!clno)
|
|
{
|
|
/* NEW */
|
|
if (!start_cluster || start_cluster >= pdr->free_contig_pointer)
|
|
{
|
|
/* search from free_contig_pointer to start_cluster or maxfindex whichever
|
|
is less */
|
|
clno = fatxx_find_free_cluster(pdr, pdr->free_contig_pointer, last_cluster, &is_error);
|
|
if (is_error) /* Error reading fat */
|
|
return(0);
|
|
/* If we search again search only to the free_contig_pointer */
|
|
last_cluster = pdr->free_contig_pointer;
|
|
}
|
|
}
|
|
/* Check the area of the disk beyond where we typically write fragments */
|
|
if (!clno)
|
|
{
|
|
/* NEW */
|
|
if (!start_cluster || start_cluster > pdr->free_contig_base)
|
|
/* search from free_contig_base to start_cluster or free_contig_pointer whichever
|
|
is less */
|
|
clno = fatxx_find_free_cluster(pdr, pdr->free_contig_base, last_cluster, &is_error);
|
|
if (is_error) /* Error reading fat */
|
|
return(0);
|
|
}
|
|
/* Check the beginning of the the disk where we typically write fragments */
|
|
if (!clno)
|
|
{
|
|
clno = fatxx_find_free_cluster(pdr, 2, pdr->free_contig_base, &is_error);
|
|
if (is_error) /* Error reading fat */
|
|
return(0);
|
|
}
|
|
/* We did not find any clusters. Scan the whole fat again this should
|
|
never work but we did have a bug in this area once before ... */
|
|
if (!clno)
|
|
{
|
|
clno = fatxx_find_free_cluster(pdr, 2, pdr->maxfindex, &is_error);
|
|
if (is_error) /* Error reading fat */
|
|
return(0);
|
|
}
|
|
if (!clno)
|
|
{
|
|
rtfs_set_errno(PENOSPC); /* fatxx_alloc_chain: No free clusters */
|
|
return(0);
|
|
}
|
|
/* End changes for the 5/18/97 bug fix */
|
|
|
|
first_new_cluster = clno;
|
|
value = 0;
|
|
n_contig = 1;
|
|
|
|
/* look up the FAT. If the next cluster is free we link to it
|
|
and up the contig count. */
|
|
while ( (n_contig < n_clusters) && (clno < pdr->maxfindex) )
|
|
{
|
|
if (!fatxx_faxx(pdr,(CLUSTERTYPE)(clno+1), &value))
|
|
return(0);
|
|
|
|
/* If the next cluster is in-use we are done. */
|
|
if (value)
|
|
break;
|
|
|
|
/* Link the current cluster to the next one */
|
|
if (!fatxx_pfaxx(pdr, clno, (CLUSTERTYPE)(clno+1)))
|
|
return (0);
|
|
n_contig += (CLUSTERTYPE)1; /* Yep.. we got another */
|
|
clno += (CLUSTERTYPE)1; /* Up the FAT table */
|
|
}
|
|
/* Terminate the list we just made */
|
|
if (!fatxx_pfaxxterm(pdr, clno))
|
|
return (0);
|
|
|
|
/* Update the hint of most likeley place to find a free cluster */
|
|
if ((clno < pdr->maxfindex) && (clno >= pdr->free_contig_pointer))
|
|
pdr->free_contig_pointer = (CLUSTERTYPE)(clno+1);
|
|
|
|
/* If we were handed a starting cluster we have to stitch our new
|
|
chain after it. */
|
|
if (dolink && start_cluster)
|
|
{
|
|
if (!fatxx_pfaxx(pdr, start_cluster, first_new_cluster))
|
|
return (0);
|
|
}
|
|
|
|
*pstart_cluster = first_new_cluster;
|
|
|
|
pdr->known_free_clusters = (long)(pdr->known_free_clusters - n_contig);
|
|
|
|
return(n_contig);
|
|
}
|
|
|
|
/* Find the first free cluster in a range */
|
|
/* Note: The caller locks the fat before calling this routine */
|
|
CLUSTERTYPE fatxx_find_free_cluster(DDRIVE *pdr, CLUSTERTYPE startpt, CLUSTERTYPE endpt, int *is_error) /*__fatfn__*/
|
|
{
|
|
CLUSTERTYPE i;
|
|
CLUSTERTYPE value;
|
|
|
|
*is_error = 0;
|
|
for (i = startpt; i < endpt; i++)
|
|
{
|
|
if ( !fatxx_faxx(pdr, i, &value) )
|
|
{
|
|
*is_error = 1;
|
|
return(0);
|
|
}
|
|
if (value == 0)
|
|
{
|
|
return(i);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
#if (RTFS_SUBDIRS)
|
|
/***************************************************************************
|
|
PC_CLALLOC - Reserve and return the next free cluster on a drive
|
|
|
|
Description
|
|
Given a DDRIVE, mark the next available cluster in the file allocation
|
|
table as used and return the associated cluster number. Clhint provides
|
|
a means of selecting clusters that are near eachother. This should
|
|
reduce fragmentation.
|
|
|
|
NOTE: This routine is used to allocate single cluster chunks for
|
|
maintaining directories. We artificially break the disks into
|
|
two regions. The first region is where single clusters chunks
|
|
used in directory files come from. These are allocated by this
|
|
routine only. Data file clusters are allocated by fatxx_alloc_chain.
|
|
|
|
THE DISK IS NOT REALLY PARTITIONED. If this routine runs out of
|
|
space in the first region it grabs a cluster from the second
|
|
region.
|
|
Returns
|
|
Return a new cluster number or 0 if the disk is full.
|
|
|
|
****************************************************************************/
|
|
/* Note: The caller locks the fat before calling this routine */
|
|
CLUSTERTYPE fatxx_clalloc(DDRIVE *pdr, CLUSTERTYPE clhint) /*__fatfn__*/
|
|
{
|
|
CLUSTERTYPE clno;
|
|
int is_error;
|
|
|
|
if (clhint < 2)
|
|
clhint = 2;
|
|
if (clhint >= pdr->free_contig_base)
|
|
clhint = 2;
|
|
|
|
/* Look in the fragmentable region first from clhint up */
|
|
clno = fatxx_find_free_cluster(pdr, clhint, pdr->free_contig_base, &is_error);
|
|
if (is_error) /* Error reading fat */
|
|
return(0);
|
|
|
|
/* Look in the fragmentable region up to clhint */
|
|
if (!clno)
|
|
{
|
|
clno = fatxx_find_free_cluster(pdr, 2, clhint, &is_error);
|
|
if (is_error) /* Error reading fat */
|
|
return(0);
|
|
}
|
|
|
|
/* Look in the contiguos region if the fragmentable region is full */
|
|
if (!clno)
|
|
{
|
|
/* use: pdr->maxfindex+1 because find_free_cluster uses < end not <= */
|
|
clno = fatxx_find_free_cluster(pdr, pdr->free_contig_base, pdr->maxfindex+1, &is_error);
|
|
if (is_error) /* Error reading fat */
|
|
return(0);
|
|
}
|
|
|
|
if (!clno)
|
|
rtfs_set_errno(PENOSPC); /* fatxx_clalloc: No free clusters */
|
|
else
|
|
{
|
|
/* Mark the cluster in use */
|
|
if (!fatxx_pfaxxterm(pdr, clno))
|
|
return (0);
|
|
pdr->known_free_clusters = pdr->known_free_clusters - 1;
|
|
}
|
|
return(clno);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PC_CLGROW - Extend a cluster chain and return the next free cluster
|
|
|
|
Description
|
|
Given a DDRIVE and a cluster, extend the chain containing the cluster
|
|
by allocating a new cluster and linking clno to it. If clno is zero
|
|
assume it is the start of a new file and allocate a new cluster.
|
|
|
|
Note: The chain is traversed to the end before linking in the new
|
|
cluster. The new cluster terminates the chain.
|
|
Returns
|
|
Return a new cluster number or 0 if the disk is full.
|
|
|
|
****************************************************************************/
|
|
|
|
/* Note: The caller locks the fat before calling this routine */
|
|
CLUSTERTYPE fatxx_clgrow(DDRIVE *pdr, CLUSTERTYPE clno) /*__fatfn__*/
|
|
{
|
|
CLUSTERTYPE nxt;
|
|
CLUSTERTYPE nextcluster;
|
|
long range_check;
|
|
|
|
/* Check the incoming argument. Should be a valid cluster */
|
|
if ((clno < 2) || (clno > pdr->maxfindex) )
|
|
{
|
|
rtfs_set_errno(PEINVALIDCLUSTER);
|
|
return (0);
|
|
}
|
|
/* Make sure we are at the end of chain */
|
|
range_check = 0;
|
|
nextcluster = fatxx_clnext(pdr , clno);
|
|
while (nextcluster != 0xffffffff && ++range_check < MAX_CLUSTERS_PER_DIR)
|
|
{
|
|
if (!nextcluster) /* fatxx_clnext - set errno */
|
|
return (0);
|
|
clno = nextcluster;
|
|
nextcluster = fatxx_clnext(pdr , clno);
|
|
}
|
|
if (get_errno())
|
|
return (0);
|
|
if (range_check == MAX_CLUSTERS_PER_DIR)
|
|
{
|
|
rtfs_set_errno(PEINVALIDCLUSTER);
|
|
return (0);
|
|
}
|
|
/* Get a cluster, clno provides a hint for more efficient cluster
|
|
allocation */
|
|
nxt = fatxx_clalloc(pdr,clno);
|
|
if (!nxt)
|
|
return((CLUSTERTYPE) 0);
|
|
/* Attach it to the current cluster if not at the begining of the chain */
|
|
if (!fatxx_pfaxx(pdr, clno, nxt))
|
|
return((CLUSTERTYPE) 0);
|
|
return(nxt);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PC_CLRELEASE - Return a cluster to the pool of free space on a disk
|
|
|
|
Description
|
|
Given a DDRIVE and a cluster, mark the cluster in the file allocation
|
|
table as free. It will be used again by calls to fatxx_clalloc().
|
|
|
|
Returns
|
|
Nothing
|
|
|
|
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
/* Return a cluster to the free list */
|
|
/* Note: The caller locks the fat before calling this routine */
|
|
BOOLEAN fatxx_clrelease_dir(DDRIVE *pdr, CLUSTERTYPE clno) /*__fatfn__*/
|
|
{
|
|
int current_errno;
|
|
/* This is a cleanup routine, an earlier event is the interesting errno
|
|
to the application, so we restore errno if we fail */
|
|
current_errno = get_errno();
|
|
/* No need to check clno value, pfaxx will catch it */
|
|
/* Do not catch any lower level errors here. You will catch them soon enough */
|
|
if (fatxx_pfaxx(pdr, clno, (CLUSTERTYPE) 0x0)) /* Mark it as free */
|
|
{
|
|
/* If freeing in the contiguous region reset the hint if we
|
|
free space earlier than it. */
|
|
if (clno >= pdr->free_contig_base && clno <= pdr->free_contig_pointer)
|
|
pdr->free_contig_pointer = clno;
|
|
pdr->known_free_clusters = pdr->known_free_clusters + 1;
|
|
return(TRUE);
|
|
}
|
|
else
|
|
{
|
|
rtfs_set_errno(current_errno);
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
PC_FLUSHFAT - Write any dirty FAT blocks to disk
|
|
|
|
Description
|
|
Given a valid drive number. Write any fat blocks to disk that
|
|
have been modified. Updates all copies of the fat.
|
|
|
|
Returns
|
|
Returns FALSE if driveno is not an open drive. Or a write failed.
|
|
|
|
****************************************************************************/
|
|
|
|
/* Consult the dirty fat block list and write any. write all copies
|
|
of the fat */
|
|
/* Note: The caller locks the fat before calling this routine */
|
|
BOOLEAN fatxx_flushfat(int driveno) /*__fatfn__*/
|
|
{
|
|
DDRIVE *pdr;
|
|
|
|
pdr = pc_drno2dr(driveno);
|
|
|
|
if (!pdr)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
if (!pdr->fat_is_dirty)
|
|
return(TRUE);
|
|
|
|
/* write the alloc hints into the info block (no-op on non-FAT32 systems) */
|
|
if (!fat_flushinfo(pdr))
|
|
return(FALSE);
|
|
|
|
if (pc_flush_fat_blocks(pdr))
|
|
{
|
|
pdr->fat_is_dirty = FALSE;
|
|
return(TRUE);
|
|
}
|
|
else
|
|
return(FALSE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PC_FREECHAIN - Free a cluster chain associated with an inode.
|
|
|
|
Description
|
|
Trace the cluster chain starting at cluster and return all the clusters to
|
|
the free state for re-use. The FAT is not flushed.
|
|
|
|
Returns
|
|
Nothing.
|
|
|
|
****************************************************************************/
|
|
/* Note: The caller locks the fat before calling this routine */
|
|
|
|
BOOLEAN fatxx_freechain(DDRIVE *pdr, CLUSTERTYPE cluster, dword min_clusters_to_free, dword max_clusters_to_free) /*__fatfn__*/
|
|
{
|
|
CLUSTERTYPE nextcluster;
|
|
dword clusters_freed;
|
|
|
|
if (max_clusters_to_free==0)
|
|
return(TRUE);
|
|
|
|
if ((cluster < 2) || (cluster > pdr->maxfindex) )
|
|
{
|
|
rtfs_set_errno(PEINVALIDCLUSTER); /* fatxx_pfaxx: bad cluster value internal error */
|
|
return (FALSE);
|
|
}
|
|
clusters_freed = 0;
|
|
|
|
nextcluster = fatxx_clnext(pdr , cluster);
|
|
while (cluster != 0xffffffff)
|
|
{
|
|
if (!nextcluster)
|
|
return (FALSE); /* Clnext set errno */
|
|
/* check if we are about to free more than we should */
|
|
if (clusters_freed >= max_clusters_to_free)
|
|
{
|
|
rtfs_set_errno(PEINVALIDCLUSTER);
|
|
return (FALSE);
|
|
}
|
|
if (fatxx_pfaxx(pdr, cluster, (CLUSTERTYPE) 0x0)) /* Mark it as free */
|
|
{
|
|
/* If freeing in the contiguous region reset the hint if we
|
|
free space earlier than it. */
|
|
if (cluster >= pdr->free_contig_base && cluster <= pdr->free_contig_pointer)
|
|
pdr->free_contig_pointer = cluster;
|
|
pdr->known_free_clusters = pdr->known_free_clusters + 1;
|
|
}
|
|
else
|
|
return(FALSE); /* pfaxx set errno */
|
|
clusters_freed += 1;
|
|
cluster = nextcluster;
|
|
if (nextcluster != 0xffffffff)
|
|
nextcluster = fatxx_clnext(pdr , nextcluster);
|
|
}
|
|
if (clusters_freed < min_clusters_to_free)
|
|
{
|
|
rtfs_set_errno(PEINVALIDCLUSTER);
|
|
return (FALSE);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
PC_CL_TRUNCATE - Truncate a cluster chain.
|
|
|
|
Description
|
|
Trace the cluster chain starting at cluster until l_cluster is reached.
|
|
Then terminate the chain and free from l_cluster on.
|
|
The FAT is not flushed.
|
|
|
|
If cluster == l_cluster does nothing. This condition should be handled
|
|
higher up by updating dirents appropriately and then calling fatxx_freechain.
|
|
|
|
Returns
|
|
The last cluster in the chain after truncation.
|
|
****************************************************************************/
|
|
/* Note: The caller locks the fat before calling this routine */
|
|
CLUSTERTYPE fatxx_cl_truncate_dir(DDRIVE *pdr, CLUSTERTYPE cluster, CLUSTERTYPE l_cluster)/*__fatfn__*/
|
|
{
|
|
CLUSTERTYPE nextcluster;
|
|
long range_check;
|
|
int current_errno;
|
|
if ((cluster < 2) || (cluster > pdr->maxfindex) )
|
|
{ /* Don't set errno in this function, it is a cleanup */
|
|
return (0);
|
|
}
|
|
/* This is a cleanup routine, an earlier event is the interesting errno
|
|
to the application, so we restore errno if we fail */
|
|
current_errno = get_errno();
|
|
|
|
nextcluster = fatxx_clnext(pdr , cluster);
|
|
|
|
range_check = 0;
|
|
while (nextcluster != 0xffffffff && ++range_check < MAX_CLUSTERS_PER_DIR)
|
|
{
|
|
if (!nextcluster) /* fatxx_clnext detected error */
|
|
{ /* Don't set errno in this function, it is a cleanup */
|
|
rtfs_set_errno(current_errno);
|
|
return (0);
|
|
}
|
|
if (nextcluster == l_cluster)
|
|
{
|
|
if (!fatxx_pfaxxterm(pdr, cluster)) /* Terminate the chain */
|
|
break; /* Error break to ret 0 */
|
|
/* We don't know the size so free between 0 and max */
|
|
if (!fatxx_freechain(pdr, l_cluster, 0, MAX_CLUSTERS_PER_DIR))
|
|
{
|
|
rtfs_set_errno(current_errno);
|
|
return(0);
|
|
}
|
|
return(cluster);
|
|
}
|
|
else
|
|
{
|
|
cluster = nextcluster;
|
|
nextcluster = fatxx_clnext(pdr , nextcluster);
|
|
}
|
|
}
|
|
rtfs_set_errno(current_errno);
|
|
return(0);
|
|
}
|
|
|
|
#endif /* (RTFS_SUBDIRS) */
|
|
|
|
/******************************************************************************
|
|
PC_PFAXXTERM - Write a terminating value to the FAT at clno.
|
|
|
|
Description
|
|
Given a DDRIVE,cluster number and value. Write the value 0xffffffff or
|
|
0xffff in the fat at clusterno. Handle 32, 16 and 12 bit fats correctly.
|
|
|
|
Returns
|
|
FALSE if an io error occurred during fat swapping, else TRUE
|
|
|
|
*****************************************************************************/
|
|
|
|
/* Given a clno & fatval Put the value in the table at the index (clno) */
|
|
/* Note: The caller locks the fat before calling this routine */
|
|
BOOLEAN fatxx_pfaxxterm(DDRIVE *pdr, CLUSTERTYPE clno) /*__fatfn__*/
|
|
{
|
|
#if (FAT32)
|
|
if (pdr->fasize == 8)
|
|
return(fatxx_pfaxx(pdr, clno, 0x0ffffffful));
|
|
else
|
|
#endif
|
|
return(fatxx_pfaxx(pdr, clno, 0xffff));
|
|
|
|
}
|
|
/******************************************************************************
|
|
PC_PFAXX - Write a value to the FAT at clno.
|
|
|
|
Description
|
|
Given a DDRIVE,cluster number and value. Write the value in the fat
|
|
at clusterno. Handle 16 and 12 bit fats correctly.
|
|
|
|
Returns
|
|
No if an io error occurred during fat swapping, else TRUE.
|
|
|
|
*****************************************************************************/
|
|
|
|
/* Given a clno & fatval Put the value in the table at the index (clno) */
|
|
/* Note: The caller locks the fat before calling this routine */
|
|
BOOLEAN fatxx_pfaxx(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE value) /*__fatfn__*/
|
|
{
|
|
union align1 {
|
|
byte wrdbuf[4]; /* Temp storage area */
|
|
word fill[2];
|
|
} u;
|
|
CLUSTERTYPE nibble,index,offset, t;
|
|
|
|
if ((clno < 2) || (clno > pdr->maxfindex) )
|
|
{
|
|
rtfs_set_errno(PEINVALIDCLUSTER); /* fatxx_pfaxx: bad cluster value internal error */
|
|
return (FALSE);
|
|
}
|
|
pdr->fat_is_dirty = TRUE;
|
|
if (pdr->fasize == 3) /* 3 nibble ? */
|
|
{
|
|
nibble = (CLUSTERTYPE)(clno * 3);
|
|
index = (CLUSTERTYPE)(nibble >> 2);
|
|
offset = (CLUSTERTYPE)(clno & 0x03);
|
|
/* Read the first word */
|
|
if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], FALSE ))
|
|
return(FALSE);
|
|
/*
|
|
| W0 | W1 | W2 |
|
|
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
|
|
xx xx xx
|
|
*/
|
|
if (offset == 0) /* (A2 << 8) | A1 A2 */
|
|
{
|
|
/* Low nibble of b[1] is hi nibble of value */
|
|
u.wrdbuf[1] &= 0xf0;
|
|
t = (CLUSTERTYPE)((value >> 8) & 0x0f);
|
|
u.wrdbuf[1] |= (byte) t;
|
|
/* b[0] is lo byte of value */
|
|
t = (CLUSTERTYPE)(value & 0x00ff);
|
|
u.wrdbuf[0] = (byte) t;
|
|
if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], TRUE ))
|
|
return (FALSE);
|
|
}
|
|
/*
|
|
| W0 | W1 | W2 |
|
|
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
|
|
xx xx xx
|
|
*/
|
|
|
|
else if (offset == 1) /* (B1 B2 << 4) | B0 */
|
|
{
|
|
/* Hi nibble of b[1] is lo nibble of value */
|
|
u.wrdbuf[1] &= 0x0f;
|
|
t = (CLUSTERTYPE)((value << 4) & 0x00f0);
|
|
u.wrdbuf[1] |= (byte)t;
|
|
if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], TRUE ))
|
|
return (FALSE);
|
|
/* b[0] is hi byte of value */
|
|
if (!fatxx_fword( pdr, (word)(index+1), (word *) &u.wrdbuf[0], FALSE ))
|
|
return(FALSE);
|
|
t = (CLUSTERTYPE)((value >> 4) & 0x00ff);
|
|
u.wrdbuf[0] = (byte) t;
|
|
if (!fatxx_fword( pdr, (word)(index+1), (word *) &u.wrdbuf[0], TRUE ))
|
|
return (FALSE);
|
|
}
|
|
/*
|
|
| W0 | W1 | W2 |
|
|
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
|
|
xx xx xx
|
|
*/
|
|
|
|
else if (offset == 2) /*(C2 << 8) | C1 C2 */
|
|
{
|
|
/* b[1] = low byte of value */
|
|
t = (CLUSTERTYPE)(value & 0x00ff);
|
|
u.wrdbuf[1] = (byte) t;
|
|
if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], TRUE ))
|
|
return (FALSE);
|
|
/* lo nibble of b[0] == hi nibble of value */
|
|
if (!fatxx_fword( pdr, (word)(index+1), (word *) &u.wrdbuf[0], FALSE ))
|
|
return(FALSE);
|
|
u.wrdbuf[0] &= 0xf0;
|
|
t = (CLUSTERTYPE)((value >> 8) & 0x0f);
|
|
u.wrdbuf[0] |= (byte) t;
|
|
if (!fatxx_fword( pdr, (word)(index+1), (word *) &u.wrdbuf[0], TRUE ))
|
|
return (FALSE);
|
|
}
|
|
/*
|
|
| W0 | W1 | W2 |
|
|
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
|
|
xx xx xx
|
|
*/
|
|
|
|
else if (offset == 3) /* (D2 D1) << 4 | D0 */
|
|
{
|
|
/* Hi nibble b[0] == low nible of value */
|
|
u.wrdbuf[0] &= 0x0f;
|
|
t = (CLUSTERTYPE)((value << 4) & 0x00f0);
|
|
u.wrdbuf[0] |= (byte) t;
|
|
t = (CLUSTERTYPE)((value >> 4) & 0x00ff);
|
|
u.wrdbuf[1] = (byte) t;
|
|
if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], TRUE ))
|
|
return (FALSE);
|
|
}
|
|
}
|
|
#if (FAT32) /* FAT32 only supported if FAT swapping is enabled */
|
|
else if (pdr->fasize == 8)
|
|
{
|
|
fr_DWORD(&u.wrdbuf[0],value); /*X*/
|
|
/* Now put the values back into the FAT */
|
|
if (!fatxx_pfpdword( pdr, clno, (dword *) &u.wrdbuf[0] ))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
#endif /* FAT32 */
|
|
else /* 16 BIT entries */
|
|
{
|
|
fr_WORD((byte *) &u.wrdbuf[0],(word)value); /*X*/
|
|
/* Now put the values back into the FAT */
|
|
if (!fatxx_fword( pdr, clno, (word *) &u.wrdbuf[0], TRUE ))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
#endif /* (RTFS_WRITE) */
|
|
|
|
/***************************************************************************
|
|
PC_CLNEXT - Return the next cluster in a cluster chain
|
|
|
|
|
|
Description
|
|
Given a DDRIVE and a cluster number, return the next cluster in the
|
|
chain containing clno. Return 0 on end of chain.
|
|
|
|
Returns
|
|
Return a new cluster number or 0xffffffff on end of chain.
|
|
|
|
****************************************************************************/
|
|
|
|
/* Return the next cluster in a chain or 0xffffffff */
|
|
CLUSTERTYPE fatxx_clnext(DDRIVE *pdr, CLUSTERTYPE clno) /*__fatfn__ */
|
|
{
|
|
CLUSTERTYPE nxt;
|
|
#if (FAT32)
|
|
dword _Oxffffffful;
|
|
#endif
|
|
/* Get the value at clno. return 0 on any io errors */
|
|
if (! fatxx_faxx(pdr,clno,&nxt) )
|
|
return (0);
|
|
|
|
if (pdr->fasize == 3) /* 3 nibble ? */
|
|
{
|
|
if ( (0xff7 < nxt) && (nxt <= 0xfff) )
|
|
nxt = 0xffffffff; /* end of chain */
|
|
}
|
|
else
|
|
#if (FAT32)
|
|
if (pdr->fasize == 8)
|
|
{
|
|
_Oxffffffful = 0x0ffffffful;
|
|
nxt &= _Oxffffffful;
|
|
if ( nxt == 0x0ffffffful )
|
|
nxt = 0xffffffff; /* end of chain */
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#if (FAT32)
|
|
if ( (nxt >= (CLUSTERTYPE)0xfff7) && (nxt <= (CLUSTERTYPE)0xffff) )
|
|
#else
|
|
/* If fat32 is not defined the nxt is always <= 0xffff. picky compilers
|
|
notice this and emit a warning */
|
|
if (nxt >= (CLUSTERTYPE)0xfff7)
|
|
#endif
|
|
nxt = 0xffffffff; /* end of chain */
|
|
}
|
|
if (nxt != 0xffffffff && (nxt < 2 || nxt > pdr->maxfindex) )
|
|
{
|
|
rtfs_set_errno(PEINVALIDCLUSTER);
|
|
return (0);
|
|
}
|
|
return(nxt);
|
|
}
|
|
|
|
/***************************************************************************
|
|
PC_FAXX - Get the value store in the FAT at clno.
|
|
|
|
Description
|
|
Given a DDRIVE and a cluster number. Get the value in the fat at clusterno
|
|
(the next cluster in a chain.) Handle 16 and 12 bit fats correctly.
|
|
|
|
Returns
|
|
Returns the the value at clno. In pvalue.
|
|
If any error occured while FAT swapping return FALSE else return TRUE.
|
|
***************************************************************************/
|
|
|
|
/* Retrieve a value from the fat */
|
|
BOOLEAN fatxx_faxx(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE *pvalue) /*__fatfn__*/
|
|
{
|
|
CLUSTERTYPE nibble,index, offset, result;
|
|
byte c;
|
|
union align1 {
|
|
byte wrdbuf[4]; /* Temp storage area */
|
|
word fill[2];
|
|
} u;
|
|
union align2 {
|
|
byte wrdbuf2[4]; /* Temp storage area */
|
|
word fill[2];
|
|
} u2;
|
|
if ((clno < 2) || (clno > pdr->maxfindex) )
|
|
{
|
|
rtfs_set_errno(PEINVALIDCLUSTER); /* fatxx_faxx: bad cluster value internal error */
|
|
return (FALSE);
|
|
}
|
|
result = 0;
|
|
if (pdr->fasize == 3) /* 3 nibble ? */
|
|
{
|
|
nibble = (word)(clno * 3);
|
|
index = (word)(nibble >> 2);
|
|
offset = (word)(clno & 0x03);
|
|
/* Read the first word */
|
|
if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], FALSE ))
|
|
return(FALSE);
|
|
/*
|
|
| W0 | W1 | W2 |
|
|
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
|
|
xx xx xx
|
|
*/
|
|
if (offset == 0) /* (A2 << 8) | A1 A2 */
|
|
{
|
|
/* BYTE 0 == s Low byte, byte 1 low nibble == s high byte */
|
|
u.wrdbuf[1] &= 0x0f;
|
|
result = u.wrdbuf[1];
|
|
result <<= 8;
|
|
result |= u.wrdbuf[0];
|
|
}
|
|
/*
|
|
| W0 | W1 | W2 |
|
|
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
|
|
xx xx xx
|
|
*/
|
|
|
|
else if (offset == 1) /* (B1 B2 << 4) | B0 */
|
|
{
|
|
/* BYTE 2 == High byte Byte 1 high nibb == low nib */
|
|
if (!fatxx_fword( pdr, (word)(index+1), (word *) &u2.wrdbuf2[0], FALSE ))
|
|
return(FALSE);
|
|
c = (byte) (u.wrdbuf[1] >> 4);
|
|
result = u2.wrdbuf2[0];
|
|
result <<= 4;
|
|
result |= c;
|
|
}
|
|
/*
|
|
| W0 | W1 | W2 |
|
|
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
|
|
xx xx xx
|
|
*/
|
|
else if (offset == 2) /*(C2 << 8) | C1 C2 */
|
|
{
|
|
if (!fatxx_fword( pdr, (word)(index+1), (word *) &u2.wrdbuf2[0], FALSE ))
|
|
return(FALSE);
|
|
/* BYTE 1 == s Low byte, byte 2 low nibble == s high byte */
|
|
result = (word) (u2.wrdbuf2[0] & 0x0f);
|
|
result <<= 8;
|
|
result |= u.wrdbuf[1];
|
|
}
|
|
/*
|
|
| W0 | W1 | W2 |
|
|
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
|
|
xx xx xx
|
|
*/
|
|
|
|
else if (offset == 3) /* (D2 D1) << 4 | D0 */
|
|
{
|
|
result = u.wrdbuf[1];
|
|
result <<= 4;
|
|
c = u.wrdbuf[0];
|
|
c >>= 4;
|
|
result |= c;
|
|
}
|
|
}
|
|
else if (pdr->fasize == 8) /* 32 BIT fat. ret the value at 4 * clno */
|
|
{
|
|
#if (FAT32)
|
|
#if KS_LITTLE_ENDIAN
|
|
if (!fatxx_pfgdword( pdr, clno, (dword *) &result ))
|
|
return (FALSE);
|
|
#else
|
|
if ( fatxx_pfgdword( pdr, clno, (dword *) &u.wrdbuf[0] ))
|
|
result = (CLUSTERTYPE) to_DWORD(&u.wrdbuf[0]);
|
|
else
|
|
return (FALSE);
|
|
#endif
|
|
#else
|
|
return (FALSE);
|
|
#endif
|
|
}
|
|
else /* 16 BIT fat. ret the value at 2 * clno */
|
|
{
|
|
if ( fatxx_fword( pdr, clno, (word *) &u.wrdbuf[0], FALSE ))
|
|
result = (CLUSTERTYPE) to_WORD(&u.wrdbuf[0]); /*X*/ /* And use the product as index */
|
|
else
|
|
return (FALSE);
|
|
}
|
|
*pvalue = result;
|
|
return (TRUE);
|
|
}
|
|
|
|
/******************************************************************************
|
|
PC_GET_CHAIN - Return as many contiguous clusters as possible.
|
|
|
|
|
|
Description
|
|
Starting at start_cluster return the number of contiguous clusters
|
|
allocated in the chain containing start_cluster or n_clusters,
|
|
whichever is less.
|
|
|
|
Returns
|
|
Returns the number of contiguous clusters found. Or zero on an error.
|
|
This function should always return at least one. (start_cluster). Unless
|
|
an error occurs.
|
|
|
|
The word at *pnext_cluster is filled with on of the following:
|
|
. If we went beyond a contiguous section it contains
|
|
the first cluster in the next segment of the chain.
|
|
. If we are still in a section it contains
|
|
the next cluster in the current segment of the chain.
|
|
. If we are at the end of the chain it contains the last cluster
|
|
in the chain.
|
|
|
|
****************************************************************************/
|
|
/* Note: The caller locks the fat before calling this routine */
|
|
|
|
CLUSTERTYPE fatxx_get_chain(DDRIVE *pdr, CLUSTERTYPE start_cluster, CLUSTERTYPE *pnext_cluster, CLUSTERTYPE n_clusters, int *end_of_chain)
|
|
{
|
|
CLUSTERTYPE clno, next_cluster;
|
|
CLUSTERTYPE n_contig;
|
|
CLUSTERTYPE value;
|
|
|
|
value = 0;
|
|
clno = start_cluster;
|
|
n_contig = 1;
|
|
*pnext_cluster = 0;
|
|
|
|
/* Get each FAT entry. If its value points to the next contiguous entry
|
|
continue. Otherwise we have reached the end of the contiguous chain.
|
|
At which point we return the number of contig s found and by reference
|
|
the address of the FAT entry beginning the next chain segment.
|
|
*/
|
|
*end_of_chain = 0;
|
|
for (;;)
|
|
{
|
|
next_cluster = fatxx_clnext(pdr, clno);
|
|
if (!next_cluster) /* clnext detected an error */
|
|
return(0);
|
|
/* check for end markers set next cluster to the last
|
|
cluster in the chain if we are at the end */
|
|
if (next_cluster == 0xffffffff) /* clnext detected end */
|
|
{
|
|
*end_of_chain = 1;
|
|
value = clno;
|
|
break;
|
|
}
|
|
else if (next_cluster == ++clno)
|
|
{
|
|
value = next_cluster;
|
|
if (n_contig >= n_clusters)
|
|
break;
|
|
n_contig++;
|
|
}
|
|
else /* (next_cluster != ++clno) and we know it is not an error condition */
|
|
{
|
|
value = next_cluster;
|
|
break;
|
|
}
|
|
}
|
|
*pnext_cluster = value;
|
|
return (n_contig);
|
|
}
|
|
|
|
/***************************************************************************
|
|
PC_FATSW - Map in a page of the FAT
|
|
****************************************************************************/
|
|
|
|
/* Swap in the page containing index */
|
|
/* Note: The caller locks the fat before calling this routine */
|
|
byte * fatxx_pfswap(DDRIVE *pdr, CLUSTERTYPE index, BOOLEAN for_write) /*__fatfn__*/
|
|
{
|
|
dword block_offset_in_fat;
|
|
dword flags;
|
|
|
|
if (pdr->fasize == 8)
|
|
block_offset_in_fat = (dword)(index >> 7);
|
|
else
|
|
block_offset_in_fat = (word) (index >> 8);
|
|
|
|
if (block_offset_in_fat >= pdr->secpfat) /* Check range */
|
|
return (0);
|
|
if (for_write)
|
|
flags = 0x80000000ul;
|
|
else
|
|
flags = 0x00000000ul;
|
|
return (pc_map_fat_block(pdr,pdr->fatblock+block_offset_in_fat, flags));
|
|
}
|
|
|
|
/* Put or get a WORD value into the fat at index */
|
|
BOOLEAN fatxx_fword(DDRIVE *pdr, CLUSTERTYPE index, word *pvalue, BOOLEAN putting) /*__fatfn__*/
|
|
{
|
|
word *ppage;
|
|
word offset;
|
|
/* Make sure we have access to the page. Mark it for writing (if a put) */
|
|
ppage = (word *)fatxx_pfswap(pdr,index,putting);
|
|
|
|
if (!ppage)
|
|
return(FALSE);
|
|
else
|
|
{
|
|
/* there are 256 entries per page */
|
|
offset = (word) (index & 0xff);
|
|
if (putting)
|
|
ppage[offset] = *pvalue;
|
|
else
|
|
*pvalue = ppage[offset];
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
FAT_DRIVER fatxx_d;
|
|
|
|
static BOOLEAN init_fat(DDRIVE *pdr)
|
|
{
|
|
FAT_DRIVER *pfd;
|
|
/* int driveno;
|
|
driveno = pdr->driveno;*/
|
|
pfd = &fatxx_d;
|
|
pfd->fatop_alloc_chain = fatxx_alloc_chain;
|
|
pfd->fatop_clnext = fatxx_clnext;
|
|
pfd->fatop_clrelease_dir = fatxx_clrelease_dir;
|
|
pfd->fatop_faxx = fatxx_faxx;
|
|
pfd->fatop_flushfat = fatxx_flushfat;
|
|
pfd->fatop_freechain = fatxx_freechain;
|
|
pfd->fatop_cl_truncate_dir = fatxx_cl_truncate_dir;
|
|
pfd->fatop_get_chain = fatxx_get_chain;
|
|
pfd->fatop_pfaxxterm = fatxx_pfaxxterm;
|
|
pfd->fatop_pfaxx = fatxx_pfaxx;
|
|
pdr->fad = (void *) pfd;
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOLEAN faxx_check_free_space(DDRIVE *pdr)
|
|
{
|
|
CLUSTERTYPE i;
|
|
CLUSTERTYPE nxt;
|
|
long freecount = 0;
|
|
|
|
for (i = 2 ; i <= pdr->maxfindex; i++)
|
|
{
|
|
if (!FATOP(pdr)->fatop_faxx(pdr, i, &nxt)) /* Any (ifree) */
|
|
return(FALSE);
|
|
if (nxt == 0)
|
|
freecount++;
|
|
}
|
|
pdr->known_free_clusters = freecount;
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOLEAN init_fat32(DDRIVE *pdr)
|
|
{
|
|
return(init_fat(pdr) && faxx_check_free_space(pdr));
|
|
}
|
|
BOOLEAN init_fat16(DDRIVE *pdr)
|
|
{
|
|
return(init_fat(pdr) && faxx_check_free_space(pdr));
|
|
}
|
|
BOOLEAN init_fat12(DDRIVE *pdr)
|
|
{
|
|
return(init_fat(pdr) && faxx_check_free_space(pdr));
|
|
}
|
|
|
|
|
|
|