📄 fatfs.c
字号:
}
/* Description.
* Finds the cluster which contains byte at the fnp->f_offset offset and
* stores its number to the fnp->f_cluster. The search begins from the start of
* a file or a directory depending whether fnp->f_ddir is FALSE or TRUE
* and continues through the FAT chain until the target cluster is found.
* The mode can have only XFR_READ or XFR_WRITE values.
* In the XFR_WRITE mode map_cluster extends the FAT chain by creating
* new clusters upon necessity.
* Return value.
* DE_HNDLDSKFULL - [XFR_WRITE mode only] unable to find free cluster
* for extending the FAT chain, the disk is full.
* The fnode is released from memory.
* DE_SEEK - [XFR_READ mode only] byte at f_offset lies outside of
* the FAT chain. The fnode is not released.
* Notes.
* If we are moving forward, then use the relative cluster number offset
* that we are at now (f_cluster_offset) to start, instead of starting
* at the beginning. */
COUNT map_cluster(REG f_node_ptr fnp, COUNT mode)
{
CLUSTER relcluster, cluster;
#ifdef DISPLAY_GETBLOCK
printf("map_cluster: current %lu, offset %lu, diff=%lu ",
(ULONG)fnp->f_cluster_offset, fnp->f_offset,
fnp->f_offset - fnp->f_cluster_offset);
#endif
if (fnp->f_cluster == FREE)
{
/* If this is a read but the file still has zero bytes return */
/* immediately.... */
if (mode == XFR_READ)
return DE_SEEK;
/* If someone did a seek, but no writes have occured, we will */
/* need to initialize the fnode. */
/* (mode == XFR_WRITE) */
/* If there are no more free fat entries, then we are full! */
cluster = extend(fnp);
if (cluster == LONG_LAST_CLUSTER)
{
return DE_HNDLDSKFULL;
}
fnp->f_cluster = cluster;
}
relcluster = (CLUSTER)((fnp->f_offset / fnp->f_dpb->dpb_secsize) >>
fnp->f_dpb->dpb_shftcnt);
if (relcluster < fnp->f_cluster_offset)
{
/* Set internal index and cluster size. */
fnp->f_cluster = (fnp->f_flags & F_DDIR) ? fnp->f_dirstart :
getdstart(fnp->f_dpb, &fnp->f_dir);
fnp->f_cluster_offset = 0;
}
/* Now begin the linear search. The relative cluster is */
/* maintained as part of the set of physical indices. It is */
/* also the highest order index and is mapped directly into */
/* physical cluster. Our search is performed by pacing an index */
/* up to the relative cluster position where the index falls */
/* within the cluster. */
while (fnp->f_cluster_offset != relcluster)
{
/* get next cluster in the chain */
cluster = next_cluster(fnp->f_dpb, fnp->f_cluster);
if (cluster == 1)
return DE_SEEK;
/* If this is a read and the next is a LAST_CLUSTER, */
/* then we are going to read past EOF, return zero read */
/* or expand the list if we're going to write and have run into */
/* the last cluster marker. */
if (cluster == LONG_LAST_CLUSTER)
{
if (mode == XFR_READ)
return DE_SEEK;
/* mode == XFR_WRITE */
cluster = extend(fnp);
if (cluster == LONG_LAST_CLUSTER)
return DE_HNDLDSKFULL;
}
fnp->f_cluster = cluster;
fnp->f_cluster_offset++;
}
#ifdef DISPLAY_GETBLOCK
printf("done.\n");
#endif
return SUCCESS;
}
/* extends a file from f_dir.dir_size to f_offset */
/* Proper OS's write zeros in between, but DOS just adds */
/* garbage sectors, and lets the caller do the zero filling */
/* if you prefer you can have this enabled using */
/* #define WRITEZEROS 1 */
/* but because we want to be compatible, we don't do this by */
/* default */
STATIC COUNT dos_extend(f_node_ptr fnp)
{
#ifdef WRITEZEROS
struct buffer FAR *bp;
UCOUNT xfr_cnt = 0;
/* The variable secsize will be used later. */
UWORD secsize = fnp->f_dpb->dpb_secsize;
ULONG count;
unsigned sector, boff;
#endif
if (fnp->f_offset <= fnp->f_dir.dir_size)
return SUCCESS;
#ifdef WRITEZEROS
count = fnp->f_offset - fnp->f_dir.dir_size;
fnp->f_offset = fnp->f_dir.dir_size;
while (count > 0)
#endif
{
if (map_cluster(fnp, XFR_WRITE) != SUCCESS)
return DE_HNDLDSKFULL;
#ifdef WRITEZEROS
/* Compute the block within the cluster and the offset */
/* within the block. */
sector = (UBYTE)(fnp->f_offset / secsize) & fnp->f_dpb->dpb_clsmask;
boff = (UWORD)(fnp->f_offset % secsize);
#ifdef DSK_DEBUG
printf("write %d links; dir offset %ld, cluster %d\n",
fnp->f_count, fnp->f_diroff, fnp->f_cluster);
#endif
xfr_cnt = count < (ULONG) secsize - boff ?
(UWORD) count : secsize - boff;
/* get a buffer to store the block in */
if ((boff == 0) && (xfr_cnt == secsize))
{
bp = getblockOver(clus2phys(fnp->f_cluster, fnp->f_dpb) +
sector, fnp->f_dpb->dpb_unit);
}
else
{
bp = getblock(clus2phys(fnp->f_cluster, fnp->f_dpb) + sector,
fnp->f_dpb->dpb_unit);
}
if (bp == NULL)
{
return DE_BLKINVLD;
}
/* set a block to zero */
fmemset((BYTE FAR *) & bp->b_buffer[boff], 0, xfr_cnt);
bp->b_flag |= BFR_DIRTY | BFR_VALID;
if (xfr_cnt == sizeof(bp->b_buffer)) /* probably not used later */
{
bp->b_flag |= BFR_UNCACHE;
}
/* update pointers and counters */
count -= xfr_cnt;
fnp->f_offset += xfr_cnt;
#endif
fnp->f_dir.dir_size = fnp->f_offset;
merge_file_changes(fnp, FALSE); /* /// Added - Ron Cemer */
}
return SUCCESS;
}
/*
comments read optimization for large reads: read total clusters in one piece
running a program like
while (1) {
read(fd, header, sizeof(header)); // small read
read(fd, buffer, header.size); // where size is large, up to 63K
// with average ~32K
}
FreeDOS 2025 is really slow.
on a P200 with modern 30GB harddisk, doing above for a 14.5 MB file
MSDOS 6.22 clustersize 8K ~2.5 sec (accumulates over clusters, reads for 63 sectors seen),
IBM PCDOS 7.0 8K ~4.3
IBM PCDOS 7.0 16K ~2.8
FreeDOS ke2025 ~17.5
with the read optimization (ke2025a),
clustersize 8K ~6.5 sec
clustersize 16K ~4.2 sec
it was verified with IBM feature tool,
that the drive read ahead cache (says it) is on. still this huge difference ;-)
it's coded pretty conservative to avoid all special cases,
so it shouldn't break anything :-)
possible further optimization:
collect read across clusters (if file is not fragmented).
MSDOS does this (as readcounts up to 63 sectors where seen)
specially important for diskettes, where clustersize is 1 sector
the same should be done for writes as well
the time to compile the complete kernel (on some P200) is
reduced from 67 to 56 seconds - in an otherwise identical configuration.
it's not clear if this improvement shows up elsewhere, but it shouldn't harm either
TE 10/18/01 14:00
collect read across clusters (if file is not fragmented) done.
seems still to work :-))
no large performance gains visible, but should now work _much_
better for the people, that complain about slow floppy access
the
fnp->f_offset +to_xfer < fnp->f_dir.dir_size && avoid EOF problems
condition can probably _carefully_ be dropped
TE 10/18/01 19:00
*/
/* Read/write block from disk */
/* checking for valid access was already done by the functions in
dosfns.c */
long rwblock(COUNT fd, VOID FAR * buffer, UCOUNT count, int mode)
{
REG f_node_ptr fnp;
REG struct buffer FAR *bp;
UCOUNT xfr_cnt = 0;
UCOUNT ret_cnt = 0;
unsigned secsize;
unsigned to_xfer = count;
ULONG currentblock;
#if 0 /*DSK_DEBUG*/
if (bDumpRdWrParms)
{
printf("rwblock:fd %02x buffer %04x:%04x count %x\n",
fd, FP_SEG(buffer), FP_OFF(buffer), count);
}
#endif
/* Translate the fd into an fnode pointer, since all internal */
/* operations are achieved through fnodes. */
fnp = xlt_fd(fd);
/* If the fd was invalid because it was out of range or the */
/* requested file was not open, tell the caller and exit */
/* note: an invalid fd is indicated by a 0 return */
if (fnp == (f_node_ptr) 0)
{
return 0;
}
if (mode==XFR_WRITE)
{
fnp->f_dir.dir_attrib |= D_ARCHIVE;
fnp->f_flags |= F_DMOD; /* mark file as modified */
fnp->f_flags &= ~F_DDATE; /* set date not valid any more */
if (dos_extend(fnp) != SUCCESS)
{
save_far_f_node(fnp);
return 0;
}
}
/* Test that we are really about to do a data transfer. If the */
/* count is zero and the mode is XFR_READ, just exit. (Any */
/* read with a count of zero is a nop). */
/* */
/* A write (mode is XFR_WRITE) is a special case. It sets the */
/* file length to the current length (truncates it). */
/* */
/* NOTE: doing this up front saves a lot of headaches later. */
if (count == 0)
{
/* NOTE: doing this up front made a lot of headaches later :-( TE */
/* FAT allocation has to be extended if necessary TE */
/* Now done in dos_extend BO */
/* remove all the following allocated clusters in shrink_file */
if (mode == XFR_WRITE)
{
fnp->f_dir.dir_size = fnp->f_offset;
shrink_file(fnp);
}
save_far_f_node(fnp);
return 0;
}
/* The variable secsize will be used later. */
secsize = fnp->f_dpb->dpb_secsize;
/* Adjust the far pointer from user space to supervisor space */
buffer = adjust_far(buffer);
/* Do the data transfer. Use block transfer methods so that we */
/* can utilize memory management in future DOS-C versions. */
while (ret_cnt < count)
{
unsigned sector, boff;
/* Do an EOF test and return whatever was transferred */
/* but only for regular files. */
if (mode == XFR_READ && !(fnp->f_flags & F_DDIR) && (fnp->f_offset >= fnp->f_dir.dir_size))
{
save_far_f_node(fnp);
return ret_cnt;
}
/* Position the file to the fnode's pointer position. This is */
/* done by updating the fnode's cluster, block (sector) and */
/* byte offset so that read or write becomes a simple data move */
/* into or out of the block data buffer. */
/* The more difficult scenario is the (more common) */
/* file offset case. Here, we need to take the fnode's */
/* offset pointer (f_offset) and translate it into a */
/* relative cluster position, cluster block (sector) */
/* offset (sector) and byte offset (boff). Once we */
/* have this information, we need to translate the */
/* relative cluster position into an absolute cluster */
/* position (f_cluster). This is unfortunate because it */
/* requires a linear search through the file's FAT */
/* entries. It made sense when DOS was originally */
/* designed as a simple floppy disk operating system */
/* where the FAT was contained in core, but now */
/* requires a search through the FAT blocks. */
/* */
/* The algorithm in this function takes advantage of */
/* the blockio block buffering scheme to simplify the */
/* task. */
#ifdef DISPLAY_GETBLOCK
printf("rwblock: ");
#endif
if (map_cluster(fnp, mode) != SUCCESS)
{
save_far_f_node(fnp);
return ret_cnt;
}
if (mode == XFR_WRITE)
{
merge_file_changes(fnp, FALSE); /* /// Added - Ron Cemer */
}
/* Compute the block within the cluster and the offset */
/* within the block. */
sector = (UBYTE)(fnp->f_offset / secsize) & fnp->f_dpb->dpb_clsmask;
boff = (UWORD)(fnp->f_offset % secsize);
currentblock = clus2phys(fnp->f_cluster, fnp->f_dpb) + sector;
/* see comments above */
if (!(fnp->f_flags & F_DDIR) && /* don't experiment with directories yet */
boff == 0) /* complete sectors only */
{
static ULONG startoffset;
UCOUNT sectors_to_xfer, sectors_wanted;
startoffset = fnp->f_offset;
sectors_wanted = to_xfer;
/* avoid EOF problems */
if (mode == XFR_READ && to_xfer > fnp->f_dir.dir_size - fnp->f_offset)
sectors_wanted = (UCOUNT)(fnp->f_dir.dir_size - fnp->f_offset);
sectors_wanted /= secsize;
if (sectors_wanted == 0)
goto normal_xfer;
sectors_to_xfer = fnp->f_dpb->dpb_clsmask + 1 - sector;
sectors_to_xfer = min(sectors_to_xfer, sectors_wanted);
fnp->f_offset += sectors_to_xfer * secsize;
while (sectors_to_xfer < sectors_wanted)
{
if (map_cluster(fnp, mode) != SUCCESS)
break;
if (clus2phys(fnp->f_cluster, fnp->f_dpb) !=
currentblock + sectors_to_xfer)
break;
sectors_to_xfer += fnp->f_dpb->dpb_clsmask + 1;
sectors_to_xfer = min(sectors_to_xfer, sectors_wanted);
fnp->f_offset = startoffset + sectors_to_xfer * secsize;
}
xfr_cnt = sectors_to_xfer * secsize;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -