⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fatfs.c

📁 开源DOS的C代码源程序
💻 C
📖 第 1 页 / 共 5 页
字号:

}

/* 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 + -