📄 lowl.c
字号:
/*
* EBS - RTFS (Real Time File Manager)
*
* Copyright EBS Inc. 1996
* All rights reserved.
* This code may not be redistributed in source or linkable object form
* without the consent of its author.
*/
/* LOWL.C - Low level File allocation table management functions.
Routines in this file include:
pc_alloc_chain - Allocate a chain from the FAT.
pc_find_free_cluster- Find the first free cluster in a given range.
pc_clalloc - Allocate a single cluster in the fragmented region.
pc_clgrow - Grow a directory chain in the fragmented region.
pc_clnext - Get the next cluster in a chain.
pc_clrelease - Return a cluster to the free list.
pc_faxx - Get a value from the FAT.
pc_flushfat - Make sure the FAT is up to date on disk.
pc_freechain - Release a chain to the free list.
pc_cl_truncate - Truncate a cluster chain.
pc_get_chain - Return contiguous clusters in a chain.
pc_pfaxx - Put a value to the FAT.
pc_pfswap - Swap a block of the FAT into the cache.
pc_fword - Get or put a value from the swap cache.
pc_pfflush - Flush the swap cache to disk.
pc_gblk0 - Read block zero and set up internal structures.
pc_clzero - Write zeroes to a cluster on disk.
pc_drno2dr - Convert a drive number to a drive structure.
pc_dskfree - Free resources associated with a drive.
pc_ifree - Calculate free space from the FAT.
pc_sec2cluster - Convert a sector number to a cluster value.
pc_sec2index - Convert a sector number to a cluster offset.
pc_cl2sector - Convert a cluster value to a sector number.
partition_init - Interpret a partition table
*/
#ifndef __PCDISK__ /* This allows us to build the lib with subroutines split */
#include <pcdisk.h>
#endif
RTFS_FILE(allocch.c, pc_alloc_chain)
#ifndef __PCDISK__ /* This allows us to build the lib with subroutines split */
#include <pcdisk.h>
#endif
/******************************************************************************
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 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)
/* ======================================================================
A bug was just reported in rtfs where it was possible on a fairly full disk
to have a write fail reporting no space on the drive when there really is
enough space. The problem happens when the following condition is
true. A file is open for writing and there are no more free clusters
available in the file allocation table between the last cluster used by
the file and the and the end of the FAT .. and .. the first 1 / 32 of the
drive is also already filled up. RTFS favors putting directory information
in the first 1/32 of the drive so this region doesn't usually fill up.
The bug is quite old and has only recently been discovered.
To fix the problem replace the contents of the routine pc_alloc_chain()
in lowl.c with this code.
*/
CLUSTERTYPE pc_alloc_chain(DDRIVE *pdr, CLUSTERTYPE *pstart_cluster, CLUSTERTYPE n_clusters) /*__fn__*/
{
CLUSTERTYPE start_cluster;
CLUSTERTYPE first_new_cluster;
CLUSTERTYPE clno;
CLUSTERTYPE n_contig;
CLUSTERTYPE value;
CLUSTERTYPE last_cluster; /* NEW for 5/18/97 bug fix */
start_cluster = *pstart_cluster;
if (start_cluster &&
( (start_cluster < 2) || (start_cluster > pdr->maxfindex) ) )
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 */
clno = pc_find_free_cluster(pdr, start_cluster, pdr->maxfindex);
/* If we search again search only to the start_cluster */
last_cluster = start_cluster;
}
else
/* When we search again search to the end */
last_cluster = pdr->maxfindex;
/* 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 = pc_find_free_cluster(pdr, pdr->free_contig_pointer, last_cluster);
/* 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 = pc_find_free_cluster(pdr, pdr->free_contig_base, last_cluster);
}
/* Check the beginning of the the disk where we typically write fragments */
if (!clno)
clno = pc_find_free_cluster(pdr, 2, pdr->free_contig_base);
/* We didn't 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 = pc_find_free_cluster(pdr, 2, pdr->maxfindex);
if (!clno)
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 (!pc_faxx(pdr,(CLUSTERTYPE)(clno+1), &value))
return(0);
/* If the next cluster is in-use we're done. */
if (value)
break;
/* Link the current cluster to the next one */
if (!pc_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 (!pc_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 (start_cluster)
{
if (!pc_pfaxx(pdr, start_cluster, first_new_cluster))
return (0);
}
*pstart_cluster = first_new_cluster;
if (pdr->known_free_clusters)
pdr->known_free_clusters = (CLUSTERTYPE)(pdr->known_free_clusters - n_contig);
return(n_contig);
}
#endif
RTFS_FILE(fndfrcl.c, pc_find_free_cluster)
#ifndef __PCDISK__ /* This allows us to build the lib with subroutines split */
#include <pcdisk.h>
#endif
#if (RTFS_WRITE)
/* Find the first free cluster in a range */
/* Note: The caller locks the fat before calling this routine */
CLUSTERTYPE pc_find_free_cluster(DDRIVE *pdr, CLUSTERTYPE startpt, CLUSTERTYPE endpt)/*__fn__*/
{
CLUSTERTYPE i;
CLUSTERTYPE value;
for (i = startpt; i < endpt; i++)
{
if ( !pc_faxx(pdr, i, &value) )
return(0);
if (value == 0)
return(i);
}
return(0);
}
#endif
RTFS_FILE(clalloc.c, pc_clalloc)
#ifndef __PCDISK__ /* This allows us to build the lib with subroutines split */
#include <pcdisk.h>
#endif
#if (RTFS_WRITE)
#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 pc_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 pc_clalloc(DDRIVE *pdr, CLUSTERTYPE clhint) /*__fn__*/
{
CLUSTERTYPE clno;
if (clhint < 2)
clhint = 2;
if (clhint >= pdr->free_contig_base)
clhint = 2;
/* Look in the "fragmentable" region first from clhint up */
clno = pc_find_free_cluster(pdr, clhint, pdr->free_contig_base);
/* Look in the "fragmentable" region up to clhint */
if (!clno)
clno = pc_find_free_cluster(pdr, 2, clhint);
/* Look in the contiguos region if the "fragmentable" region is full */
if (!clno)
clno = pc_find_free_cluster(pdr, pdr->free_contig_base, pdr->maxfindex);
if (!clno)
return(0);
/* Mark the cluster in use */
if (!pc_pfaxxterm(pdr, clno)) /* FAT32 */
return (0);
if (pdr->known_free_clusters)
{
#if (FAT32)
pdr->known_free_clusters -= (long)1;
#else
pdr->known_free_clusters -= (word)1;
#endif
}
return(clno);
}
#endif
#endif
RTFS_FILE(clgrow.c, pc_clgrow)
#ifndef __PCDISK__ /* This allows us to build the lib with subroutines split */
#include <pcdisk.h>
#endif
/****************************************************************************
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.
****************************************************************************/
#if (RTFS_WRITE)
#if (RTFS_SUBDIRS)
/* Note: The caller locks the fat before calling this routine */
CLUSTERTYPE pc_clgrow(DDRIVE *pdr, CLUSTERTYPE clno) /*__fn__*/
{
CLUSTERTYPE nxt;
CLUSTERTYPE nextcluster;
/* Make sure we are at the end of chain */
if (clno)
{
nextcluster = pc_clnext(pdr , clno);
while (nextcluster)
{
clno = nextcluster;
nextcluster = pc_clnext(pdr , clno);
}
}
/* Get a cluster, clno provides a hint for more efficient cluster
allocation */
nxt = pc_clalloc(pdr,clno);
if (!nxt)
return((CLUSTERTYPE) 0);
/* Attach it to the current cluster if not at the begining of the chain */
if (clno)
if (!pc_pfaxx(pdr, clno, nxt))
return((CLUSTERTYPE) 0);
return(nxt);
}
#endif
#endif
RTFS_FILE(clnext.c, pc_clnext)
#ifndef __PCDISK__ /* This allows us to build the lib with subroutines split */
#include <pcdisk.h>
#endif
/***************************************************************************
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 0 on end of chain.
****************************************************************************/
/* Return the next cluster in a chain or ZERO */
CLUSTERTYPE pc_clnext(DDRIVE *pdr, CLUSTERTYPE clno) /*__fn__ - FAT32*/
{
CLUSTERTYPE nxt; /*FAT32*/
#if (FAT32)
dword _Oxffffffful;
#endif
/* Get the value at clno. return 0 on any io errors */
if (! pc_faxx(pdr,clno,&nxt) )
return (0);
if (pdr->fasize == 3) /* 3 nibble ? */
{
if ( (0xff7 < nxt) && (nxt <= 0xfff) )
nxt = 0; /* end of chain */
}
else
#if (FAT32)
if (pdr->fasize == 8)
{
_Oxffffffful = 0x0ffffffful;
nxt &= _Oxffffffful;
if ( nxt == 0x0ffffffful )
nxt = 0; /* end of chain */
}
else
#endif
{
#if (FAT32)
if ( (nxt >= (CLUSTERTYPE)0xfff7) && (nxt <= (CLUSTERTYPE)0xffff) )
#else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -