📄 fatfs.c
字号:
/* 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; /* avoid caching trouble */ DeleteBlockInBufferCache(currentblock, currentblock + sectors_to_xfer - 1, fnp->f_dpb->dpb_unit, mode); if (dskxfer(fnp->f_dpb->dpb_unit, currentblock, (VOID FAR *) buffer, sectors_to_xfer, mode == XFR_READ ? DSKREAD : DSKWRITE)) { fnp->f_offset = startoffset; save_far_f_node(fnp); return DE_ACCESS; } goto update_pointers; } /* normal read: just the old, buffer = sector based read */ normal_xfer:#ifdef DSK_DEBUG printf("r/w %d links; dir offset %d, cluster %d, mode %x\n", fnp->f_count, fnp->f_diroff, fnp->f_cluster, mode);#endif /* Get the block we need from cache */ bp = getblock(currentblock /*clus2phys(fnp->f_cluster, fnp->f_dpb) + fnp->f_sector */ , fnp->f_dpb->dpb_unit); #ifdef DISPLAY_GETBLOCK printf("DATA (rwblock)\n");#endif if (bp == NULL) /* (struct buffer *)0 --> DS:0 !! */ { save_far_f_node(fnp); return ret_cnt; } /* transfer a block */ /* Transfer size as either a full block size, or the */ /* requested transfer size, whichever is smaller. */ /* Then compare to what is left, since we can transfer */ /* a maximum of what is left. */ xfr_cnt = min(to_xfer, secsize - boff); if (!(fnp->f_flags & F_DDIR) && mode == XFR_READ) xfr_cnt = (UWORD) min(xfr_cnt, fnp->f_dir.dir_size - fnp->f_offset); /* transfer a block */ /* Transfer size as either a full block size, or the */ /* requested transfer size, whichever is smaller. */ /* Then compare to what is left, since we can transfer */ /* a maximum of what is left. */ if (mode == XFR_WRITE) { fmemcpy(&bp->b_buffer[boff], buffer, xfr_cnt); bp->b_flag |= BFR_DIRTY | BFR_VALID; } else { fmemcpy(buffer, &bp->b_buffer[boff], xfr_cnt); } /* complete buffer transferred ? probably not reused later */ if (xfr_cnt == sizeof(bp->b_buffer) || (mode == XFR_READ && fnp->f_offset + xfr_cnt == fnp->f_dir.dir_size)) { bp->b_flag |= BFR_UNCACHE; } /* update pointers and counters */ fnp->f_offset += xfr_cnt;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -