Skip to content
Snippets Groups Projects
fs_fat32util.c 66.6 KiB
Newer Older
patacongo's avatar
patacongo committed
/****************************************************************************
 * fs_fat32util.c
 *
 *   Copyright (C) 2007, 2008 Gregory Nutt. All rights reserved.
patacongo's avatar
patacongo committed
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
patacongo's avatar
patacongo committed
 * References:
 *   Microsoft FAT documentation
 *   Some good ideas were leveraged from the FAT implementation:
 *     'Copyright (C) 2007, ChaN, all right reserved.'
patacongo's avatar
patacongo committed
 *     which has an unrestricted license.
 *
patacongo's avatar
patacongo committed
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
patacongo's avatar
patacongo committed
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <semaphore.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>

#include <nuttx/fs.h>
#include <nuttx/fat.h>
patacongo's avatar
patacongo committed

#include "fs_internal.h"
#include "fs_fat32.h"

/****************************************************************************
 * Definitions
 ****************************************************************************/

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/****************************************************************************
 * Private Variables
 ****************************************************************************/

/****************************************************************************
 * Public Variables
 ****************************************************************************/

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
patacongo's avatar
patacongo committed
 * Name: fat_path2dirname
patacongo's avatar
patacongo committed
 *
patacongo's avatar
patacongo committed
 * Desciption:  Convert a user filename into a properly formatted FAT
 *   (short) filname as it would appear in a directory entry.  Here are the
 *    rules for the 11 byte name in the directory:
patacongo's avatar
patacongo committed
 *
patacongo's avatar
patacongo committed
 *   The first byte:
 *   - 0xe5 = The directory is free
 *   - 0x00 = This directory and all following directories are free
 *   - 0x05 = Really 0xe5
 *   - 0x20 = May NOT be ' '
patacongo's avatar
patacongo committed
 *
patacongo's avatar
patacongo committed
 *   Any bytes
 *     0x00-0x1f = (except for 0x00 and 0x05 in the first byte)
 *     0x22      = '"'
 *     0x2a-0x2c = '*', '+', ','
 *     0x2e-0x2f = '.', '/'
 *     0x3a-0x3f = ':', ';', '<', '=', '>', '?'
 *     0x5b-0x5d = '[', '\\', ;]'
 *     0x7c      = '|'
 *
 *   Upper case characters are not allowed in directory names (without some
 *   poorly documented operatgions on the NTRes directory byte).  Lower case
 *   codes may represent different characters in other character sets ("DOS
 *   code pages".  The logic below does not, at present, support any other
 *   character sets.
patacongo's avatar
patacongo committed
 *
 ****************************************************************************/

patacongo's avatar
patacongo committed
static inline int fat_path2dirname(const char **path, struct fat_dirinfo_s *dirinfo,
                                   char *terminator)
patacongo's avatar
patacongo committed
{
patacongo's avatar
patacongo committed
#ifdef CONFIG_FAT_LCNAMES
    unsigned int ntlcenable = FATNTRES_LCNAME | FATNTRES_LCEXT;
    unsigned int ntlcfound  = 0;
#endif
    const char *node = *path;
    int endndx;
    ubyte ch;
    int ndx = 0;
patacongo's avatar
patacongo committed
    /* Initialized the name with all spaces */
patacongo's avatar
patacongo committed
    memset(dirinfo->fd_name, ' ', 8+3);
 
    /* Loop until the name is successfully parsed or an error occurs */
patacongo's avatar
patacongo committed
    endndx  = 8;
    for (;;)
      {
        /* Get the next byte from the path */
patacongo's avatar
patacongo committed
        ch = *node++;
patacongo's avatar
patacongo committed
        /* Check if this the last byte in this node of the name */
patacongo's avatar
patacongo committed
        if ((ch == '\0' || ch == '/') && ndx != 0 )
          {
            /* Return the accumulated NT flags and the terminating character */
#ifdef CONFIG_FAT_LCNAMES
            dirinfo->fd_ntflags = ntlcfound & ntlcenable;
#endif
            *terminator = ch;
            *path       = node;
            return OK;
          }
patacongo's avatar
patacongo committed
        /* Accept only the printable character set.  Note the first byte
         * of the name could be 0x05 meaning that is it 0xe5, but this is
         * not a printable character in this character in either case.
         */
patacongo's avatar
patacongo committed
        else if (!isgraph(ch))
          {
            goto errout;
          }
patacongo's avatar
patacongo committed
        /* Check for transition from name to extension */
patacongo's avatar
patacongo committed
        else if (ch == '.')
          {
            /* Starting the extension */
patacongo's avatar
patacongo committed
            ndx    = 8;
            endndx = 11;
            continue;
          }
patacongo's avatar
patacongo committed
        /* Reject printable characters forbidden by FAT */
patacongo's avatar
patacongo committed
        else if (ch == '"'  ||  (ch >= '*' && ch <= ',') ||
                 ch == '.'  ||   ch == '/'               ||
                (ch >= ':'  &&   ch <= '?')              ||
                (ch >= '['  &&   ch <= ']')              ||
                (ch == '|'))
          {
            goto errout;
          }
patacongo's avatar
patacongo committed
        /* Check for upper case charaters */
patacongo's avatar
patacongo committed
#ifdef CONFIG_FAT_LCNAMES
        else if (isupper(ch))
          {
            /* Some or all of the characters in the name or extension
             * are upper case. Force all of the characters to be interpreted
             * as upper case.
             */

              if ( endndx == 8)
patacongo's avatar
patacongo committed
                {
patacongo's avatar
patacongo committed
                  /* Clear lower case name bit in mask*/
                  ntlcenable &= FATNTRES_LCNAME;
patacongo's avatar
patacongo committed
                }
patacongo's avatar
patacongo committed
              else
patacongo's avatar
patacongo committed
                {
patacongo's avatar
patacongo committed
                  /* Clear lower case extension in mask */
                  ntlcenable &= FATNTRES_LCNAME;
patacongo's avatar
patacongo committed
                }
patacongo's avatar
patacongo committed
          }
#endif
patacongo's avatar
patacongo committed
        /* Check for lower case characters */
patacongo's avatar
patacongo committed
        else if (islower(ch))
          {
            /* Convert the character to upper case */
patacongo's avatar
patacongo committed
            ch = toupper(ch);
patacongo's avatar
patacongo committed

            /* Some or all of the characters in the name or extension
             * are lower case.  They can be interpreted as lower case if
             * only if all of the characters in the name or extension are
             * lower case.
             */

#ifdef CONFIG_FAT_LCNAMES
            if ( endndx == 8)
              {
                /* Set lower case name bit */
                ntlcfound |= FATNTRES_LCNAME;
              }
            else
              {
                /* Set lower case extension bit */
                ntlcfound |= FATNTRES_LCNAME;
              }
#endif
          }

        /* Check if the file name exceeds the size permitted (without
         * long file name support
         */

        if (ndx >= endndx)
          {
            goto errout;
          }

        /* Save next character in the accumulated name */

        dirinfo->fd_name[ndx++] = ch;
      }

 errout:
    return -EINVAL;
}

/****************************************************************************
 * Name: fat_checkfsinfo
 *
 * Desciption: Read the FAT32 FSINFO sector
 *
 ****************************************************************************/

static int fat_checkfsinfo(struct fat_mountpt_s *fs)
{
  /* Verify that this is, indeed, an FSINFO sector */

  if (FSI_GETLEADSIG(fs->fs_buffer) == 0x41615252  &&
      FSI_GETSTRUCTSIG(fs->fs_buffer) == 0x61417272 &&
      FSI_GETTRAILSIG(fs->fs_buffer) == 0xaa550000)
    {
      fs->fs_fsinextfree  = FSI_GETFREECOUNT(fs->fs_buffer);
      fs->fs_fsifreecount = FSI_GETNXTFREE(fs->fs_buffer);
      return OK;
    }
  return -ENODEV;
}

/****************************************************************************
 * Name: fat_checkbootrecord
 *
 * Desciption: Read a sector and verify that it is a a FAT boot record.
 *
 ****************************************************************************/

static int fat_checkbootrecord(struct fat_mountpt_s *fs)
{
  uint32  ndatasectors;
  uint32  fatsize;
  uint16  rootdirsectors = 0;
  boolean notfat32 = FALSE;

  /* Verify the MBR signature at offset 510 in the sector (true even
   * if the sector size is greater than 512.  All FAT file systems have
   * this signature. On a FAT32 volume, the RootEntCount , FatSz16, and
   * FatSz32 values should always be zero.  The FAT sector size should
   * match the reported hardware sector size.
   */

  if (MBR_GETSIGNATURE(fs->fs_buffer) != 0xaa55 ||
      MBR_GETBYTESPERSEC(fs->fs_buffer) != fs->fs_hwsectorsize)
    {
      return -ENODEV;
    }

  /* Verify the FAT32 file system type. The determination of the file
   * system type is based on the number of clusters on the volume:  FAT12
   * volume has < 4085 cluseter, a FAT16 volume has fewer than 65,525
   * clusters, and any larger is FAT32.
   *
   * Get the number of 32-bit directory entries in root directory (zero
   * for FAT32.
   */

  fs->fs_rootentcnt = MBR_GETROOTENTCNT(fs->fs_buffer);
  if (fs->fs_rootentcnt != 0)
  {
      notfat32       = TRUE; /* Must be zero for FAT32 */
      rootdirsectors = (32 * fs->fs_rootentcnt  + fs->fs_hwsectorsize - 1) / fs->fs_hwsectorsize;
  }

  /* Determine the number of sectors in a FAT. */

  fs->fs_fatsize = MBR_GETFATSZ16(fs->fs_buffer); /* Should be zero */
  if (fs->fs_fatsize)
    {
      notfat32 = TRUE; /* Must be zero for FAT32 */
    }
  else
    {
      fs->fs_fatsize = MBR_GETFATSZ32(fs->fs_buffer);
    }

  if (!fs->fs_fatsize || fs->fs_fatsize >= fs->fs_hwnsectors)
    {
      return -ENODEV;
    }

  /* Get the total number of sectors on the volume. */

  fs->fs_fattotsec = MBR_GETTOTSEC16(fs->fs_buffer); /* Should be zero */
  if (fs->fs_fattotsec)
    {
      notfat32 = TRUE; /* Must be zero for FAT32 */
    }
  else
    {
      fs->fs_fattotsec = MBR_GETTOTSEC32(fs->fs_buffer);
    }

  if (!fs->fs_fattotsec || fs->fs_fattotsec > fs->fs_hwnsectors)
    {
      return -ENODEV;
    }

  /* Get the total number of reserved sectors */

  fs->fs_fatresvdseccount = MBR_GETRESVDSECCOUNT(fs->fs_buffer);
  if (fs->fs_fatresvdseccount > fs->fs_hwnsectors)
    {
      return -ENODEV;
    }

  /* Get the number of FATs. This is probably two but could have other values */

  fs->fs_fatnumfats = MBR_GETNUMFATS(fs->fs_buffer);
  fatsize = fs->fs_fatnumfats * fs->fs_fatsize;

  /* Get the total number of data sectors */

  ndatasectors = fs->fs_fattotsec - fs->fs_fatresvdseccount - fatsize - rootdirsectors;
  if (ndatasectors > fs->fs_hwnsectors)
    {
      return -ENODEV;
    }

  /* Get the sectors per cluster */

  fs->fs_fatsecperclus = MBR_GETSECPERCLUS(fs->fs_buffer);

  /* Calculate the number of clusters */

  fs->fs_nclusters = ndatasectors / fs->fs_fatsecperclus;

  /* Finally, the test: */

  if (fs->fs_nclusters < 4085)
    {
      fs->fs_fsinfo = 0;
      fs->fs_type   = FSTYPE_FAT12;
    }
  else if (fs->fs_nclusters < 65525)
    {
      fs->fs_fsinfo = 0;
      fs->fs_type   = FSTYPE_FAT16;
    }
  else if (!notfat32)
    {
      fs->fs_fsinfo   = fs->fs_fatbase + MBR_GETFSINFO(fs->fs_buffer);
      fs->fs_type     = FSTYPE_FAT32;
    }
  else
    {
      return -ENODEV;
    }

  /* We have what appears to be a valid FAT filesystem! Save a few more things
   * from the boot record that we will need later.
   */

  fs->fs_fatbase     += fs->fs_fatresvdseccount;

  if (fs->fs_type == FSTYPE_FAT32)
    {
      fs->fs_rootbase = MBR_GETROOTCLUS(fs->fs_buffer);
    }
  else
    {
      fs->fs_rootbase = fs->fs_fatbase + fatsize; 
    }

  fs->fs_database     = fs->fs_fatbase + fatsize + fs->fs_rootentcnt / DIRSEC_NDIRS(fs);
  fs->fs_fsifreecount = 0xffffffff;

  return OK;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: fat_getuint16
 ****************************************************************************/

uint16 fat_getuint16(ubyte *ptr)
{
#ifdef CONFIG_ENDIAN_BIG
patacongo's avatar
patacongo committed
  /* The bytes always have to be swapped if the target is big-endian */

  return ((uint16)ptr[0] << 8) | ptr[1];
#else
  /* Byte-by-byte transfer is still necessary if the address is un-aligned */

  return ((uint16)ptr[1] << 8) | ptr[0];
#endif
}

/****************************************************************************
 * Name: fat_getuint32
 ****************************************************************************/

uint32 fat_getuint32(ubyte *ptr)
{
#ifdef CONFIG_ENDIAN_BIG
patacongo's avatar
patacongo committed
  /* The bytes always have to be swapped if the target is big-endian */

  return ((uint32)fat_getuint16(&ptr[0]) << 16) | fat_getuint16(&ptr[2]);
#else
  /* Byte-by-byte transfer is still necessary if the address is un-aligned */

  return ((uint32)fat_getuint16(&ptr[2]) << 16) | fat_getuint16(&ptr[0]);
#endif
}

/****************************************************************************
 * Name: fat_putuint16
 ****************************************************************************/

void fat_putuint16(ubyte *ptr, uint16 value16)
{
  ubyte *val = (ubyte*)&value16;
#ifdef CONFIG_ENDIAN_BIG
patacongo's avatar
patacongo committed
  /* The bytes always have to be swapped if the target is big-endian */

  ptr[0] = val[1];
  ptr[1] = val[0];
#else
  /* Byte-by-byte transfer is still necessary if the address is un-aligned */

  ptr[0] = val[0];
  ptr[1] = val[1];
#endif
}

/****************************************************************************
 * Name: fat_putuint32
 ****************************************************************************/

void fat_putuint32(ubyte *ptr, uint32 value32)
{
  uint16 *val = (uint16*)&value32;
#ifdef CONFIG_ENDIAN_BIG
patacongo's avatar
patacongo committed
  /* The bytes always have to be swapped if the target is big-endian */

  fat_putuint16(&ptr[0], val[2]);
  fat_putuint16(&ptr[2], val[0]);
#else
  /* Byte-by-byte transfer is still necessary if the address is un-aligned */

patacongo's avatar
patacongo committed
  fat_putuint16(&ptr[0], val[0]);
  fat_putuint16(&ptr[2], val[2]);
#endif
}

/****************************************************************************
 * Name: fat_semtake
 ****************************************************************************/

void fat_semtake(struct fat_mountpt_s *fs)
{
  /* Take the semaphore (perhaps waiting) */

  while (sem_wait(&fs->fs_sem) != 0)
    {
      /* The only case that an error should occur here is if
       * the wait was awakened by a signal.
       */

      ASSERT(*get_errno_ptr() == EINTR);
    }
}

/****************************************************************************
 * Name: fat_semgive
 ****************************************************************************/

void fat_semgive(struct fat_mountpt_s *fs)
{
   sem_post(&fs->fs_sem);
}

/****************************************************************************
patacongo's avatar
patacongo committed
 * Name: fat_systime2fattime
 *
 * Desciption: Get the system time convertto a time and and date suitble
 * for writing into the FAT FS.
 *
 *    TIME in LS 16-bits:
 *      Bits 0:4   = 2 second count (0-29 representing 0-58 seconds)
 *      Bits 5-10  = minutes (0-59)
 *      Bits 11-15 = hours (0-23)
 *    DATE in MS 16-bits
 *      Bits 0:4   = Day of month (0-31)
 *      Bits 5:8   = Month of year (1-12)
 *      Bits 9:15  = Year from 1980 (0-127 representing 1980-2107)
 *
patacongo's avatar
patacongo committed
 ****************************************************************************/

uint32 fat_systime2fattime(void)
{
#warning "Time not implemented"
    return 0;
}

/****************************************************************************
 * Name: fat_fattime2systime
 *
 * Desciption: Convert FAT data and time to a system time_t
 *
 *    16-bit FAT time:
 *      Bits 0:4   = 2 second count (0-29 representing 0-58 seconds)
 *      Bits 5-10  = minutes (0-59)
 *      Bits 11-15 = hours (0-23)
 *    16-bit FAT date:
 *      Bits 0:4   = Day of month (0-31)
 *      Bits 5:8   = Month of year (1-12)
 *      Bits 9:15  = Year from 1980 (0-127 representing 1980-2107)
 *
 ****************************************************************************/

patacongo's avatar
patacongo committed
time_t fat_fattime2systime(uint16 fattime, uint16 fatdate)
{
#warning "Time not implemented"
    return 0;
}

patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: fat_mount
 *
 * Desciption: This function is called only when the mountpoint is first
 *   established.  It initializes the mountpoint structure and verifies
 *   that a valid FAT32 filesystem is provided by the block driver.
 *
 *   The caller should hold the mountpoint semaphore
 *
 ****************************************************************************/

int fat_mount(struct fat_mountpt_s *fs, boolean writeable)
{
  FAR struct inode *inode;
  struct geometry geo;
  int ret;

  /* Assume that the mount is successful */

  fs->fs_mounted = TRUE;

  /* Check if there is media available */

  inode = fs->fs_blkdriver;
  if (!inode || !inode->u.i_bops || !inode->u.i_bops->geometry ||
      inode->u.i_bops->geometry(inode, &geo) != OK || !geo.geo_available)
    {
      ret = -ENODEV;
      goto errout;
    }

  /* Make sure that that the media is write-able (if write access is needed) */

  if (writeable && !geo.geo_writeenabled)
    {
      ret = -EACCES;
      goto errout;
    }

  /* Save the hardware geometry */

  fs->fs_hwsectorsize = geo.geo_sectorsize;
  fs->fs_hwnsectors   = geo.geo_nsectors;

  /* Allocate a buffer to hold one hardware sector */

  fs->fs_buffer = (ubyte*)malloc(fs->fs_hwsectorsize);
  if (!fs->fs_buffer)
    {
      ret = -ENOMEM;
      goto errout;
    }

  /* Search FAT boot record on the drive.  First check at sector zero.  This
   * could be either the boot record or a partition that refers to the boot
   * record.
   *
   * First read sector zero.  This will be the first access to the drive and a
   * likely failure point.
   */

  fs->fs_fatbase = 0;
  ret = fat_hwread(fs, fs->fs_buffer, 0, 1);
  if (ret < 0)
    {
      goto errout_with_buffer;
    }

  if (fat_checkbootrecord(fs) != OK)
    {
      /* The contents of sector 0 is not a boot record.  It could be a
       * partition, however.  Assume it is a partition and get the offset
       * into the partition table.  This table is at offset MBR_TABLE and is
       * indexed by 16x the partition number.  Here we support only
       * parition 0.
       */

      ubyte *partition = &fs->fs_buffer[MBR_TABLE + 0];

      /* Check if the partition exists and, if so, get the bootsector for that
       * partition and see if we can find the boot record there.
       */
 
      if (partition[4])
        {
          /* There appears to be a partition, get the sector number of the
           * partition (LBA)
           */

          fs->fs_fatbase = MBR_GETPARTSECTOR(&partition[8]);

          /* Read the new candidate boot sector */

          ret = fat_hwread(fs, fs->fs_buffer, fs->fs_fatbase, 1);
          if (ret < 0)
          {
              goto errout_with_buffer;
          }

          /* Check if this is a boot record */

          if (fat_checkbootrecord(fs) != OK)
            {
              goto errout_with_buffer;
            }
        }
    }

  /* We have what appears to be a valid FAT filesystem! Now read the
   * FSINFO sector (FAT32 only)
   */

  if (fs->fs_type == FSTYPE_FAT32)
  {
      ret = fat_checkfsinfo(fs);
      if (ret != OK)
      {
          goto errout_with_buffer;
      }
  }

  /* We did it! */

  fdbg("FAT%d:\n", fs->fs_type == 0 ? 12 : fs->fs_type == 1  ? 16 : 32);
  fdbg("\tHW  sector size:     %d\n", fs->fs_hwsectorsize);
  fdbg("\t    sectors:         %d\n", fs->fs_hwnsectors);
  fdbg("\tFAT reserved:        %d\n", fs->fs_fatresvdseccount);
  fdbg("\t    sectors:         %d\n", fs->fs_fattotsec);
  fdbg("\t    start sector:    %d\n", fs->fs_fatbase);
  fdbg("\t    root sector:     %d\n", fs->fs_rootbase);
  fdbg("\t    root entries:    %d\n", fs->fs_rootentcnt);
  fdbg("\t    data sector:     %d\n", fs->fs_database);
  fdbg("\t    FSINFO sector:   %d\n", fs->fs_fsinfo);
  fdbg("\t    Num FATs:        %d\n", fs->fs_fatnumfats);
  fdbg("\t    FAT size:        %d\n", fs->fs_fatsize);
  fdbg("\t    sectors/cluster: %d\n", fs->fs_fatsecperclus);
  fdbg("\t    max clusters:    %d\n", fs->fs_nclusters);
  fdbg("\tFSI free count       %d\n", fs->fs_fsifreecount);
  fdbg("\t    next free        %d\n", fs->fs_fsinextfree);
patacongo's avatar
patacongo committed

  return OK;

 errout_with_buffer:
  free(fs->fs_buffer);
  fs->fs_buffer = 0;
 errout:
  fs->fs_mounted = FALSE;
  return ret;
}

/****************************************************************************
 * Name: fat_checkmount
 *
 * Desciption: Check if the mountpoint is still valid.
 *
 *   The caller should hold the mountpoint semaphore
 *
 ****************************************************************************/

int fat_checkmount(struct fat_mountpt_s *fs)
{
  /* If the fs_mounted flag is FALSE, then we have already handled the loss
   * of the mount.
   */

  if (fs && fs->fs_mounted)
    {
      struct fat_file_s *file;

      /* We still think the mount is healthy.  Check an see if this is
       * still the case
       */

      if (fs->fs_blkdriver)
        {
          struct inode *inode = fs->fs_blkdriver;
          if (inode && inode->u.i_bops && inode->u.i_bops->geometry)
            {
              struct geometry geo;
              int errcode = inode->u.i_bops->geometry(inode, &geo);
              if (errcode == OK && geo.geo_available && !geo.geo_mediachanged)
                {
                  return OK;
                }
            }
        }

      /* If we get here, the mount is NOT healthy */

      fs->fs_mounted = FALSE;

      /* Make sure that this is flagged in every opened file */

      for (file = fs->fs_head; file; file = file->ff_next)
        {
          file->ff_open = FALSE;
        }
    }
  return -ENODEV;
}

/****************************************************************************
 * Name: fat_hwread
 *
 * Desciption: Read the specified sector into the sector buffer
 *
 ****************************************************************************/

int fat_hwread(struct fat_mountpt_s *fs, ubyte *buffer,  size_t sector,
               unsigned int nsectors)
{
  int ret = -ENODEV;
  if (fs && fs->fs_blkdriver )
    {
      struct inode *inode = fs->fs_blkdriver;
      if (inode && inode->u.i_bops && inode->u.i_bops->read)
        {
          ssize_t nSectorsRead = inode->u.i_bops->read(inode, buffer,
                                                       sector, nsectors);
          if (nSectorsRead == nsectors)
            {
              ret = OK;
            }
          else if (nSectorsRead < 0)
            {
              ret = nSectorsRead;
            }
        }
    }
  return ret;
patacongo's avatar
patacongo committed
}

/****************************************************************************
patacongo's avatar
patacongo committed
 * Name: fat_hwwrite
 *
 * Desciption: Write the sector buffer to the specified sector
 *
patacongo's avatar
patacongo committed
 ****************************************************************************/

patacongo's avatar
patacongo committed
int fat_hwwrite(struct fat_mountpt_s *fs, ubyte *buffer, size_t sector,
                unsigned int nsectors)
patacongo's avatar
patacongo committed
{
patacongo's avatar
patacongo committed
  int ret = -ENODEV;
  if (fs && fs->fs_blkdriver )
patacongo's avatar
patacongo committed
    {
patacongo's avatar
patacongo committed
      struct inode *inode = fs->fs_blkdriver;
      if (inode && inode->u.i_bops && inode->u.i_bops->write)
        {
          ssize_t nSectorsWritten =
              inode->u.i_bops->write(inode, buffer, sector, nsectors);
patacongo's avatar
patacongo committed
          if (nSectorsWritten == nsectors)
            {
              ret = OK;
            }
          else if (nSectorsWritten < 0)
            {
              ret = nSectorsWritten;
            }
        }
patacongo's avatar
patacongo committed
    }
patacongo's avatar
patacongo committed
  return ret;
patacongo's avatar
patacongo committed
}

/****************************************************************************
patacongo's avatar
patacongo committed
 * Name: fat_cluster2sector
 *
 * Desciption: Convert a cluster number to a start sector number
 *
patacongo's avatar
patacongo committed
 ****************************************************************************/

patacongo's avatar
patacongo committed
ssize_t fat_cluster2sector(struct fat_mountpt_s *fs,  uint32 cluster )
patacongo's avatar
patacongo committed
{
patacongo's avatar
patacongo committed
  cluster -= 2;
  if (cluster >= fs->fs_nclusters - 2)
    {
       return -EINVAL;
    }
  return cluster * fs->fs_fatsecperclus + fs->fs_database;
patacongo's avatar
patacongo committed
}

/****************************************************************************
patacongo's avatar
patacongo committed
 * Name: fat_getcluster
patacongo's avatar
patacongo committed
 *
patacongo's avatar
patacongo committed
 * Desciption: Get the cluster start sector into the FAT.
 *
patacongo's avatar
patacongo committed
 * Return:  <0: error, 0:cluster unassigned, >=0: start sector of cluster
patacongo's avatar
patacongo committed
 *
 ****************************************************************************/

patacongo's avatar
patacongo committed
ssize_t fat_getcluster(struct fat_mountpt_s *fs, uint32 clusterno)
patacongo's avatar
patacongo committed
{
patacongo's avatar
patacongo committed
  /* Verify that the cluster number is within range */
patacongo's avatar
patacongo committed
  if (clusterno >= 2 && clusterno < fs->fs_nclusters)
patacongo's avatar
patacongo committed
    {
patacongo's avatar
patacongo committed
      /* Okay.. Read the next cluster from the FAT.  The way we will do
       * this depends on the type of FAT filesystm we are dealing with.
       */
patacongo's avatar
patacongo committed
      switch (fs->fs_type)
patacongo's avatar
patacongo committed
        {
patacongo's avatar
patacongo committed
          case FSTYPE_FAT12 :
patacongo's avatar
patacongo committed
            {
patacongo's avatar
patacongo committed
              size_t       fatsector;
              unsigned int fatoffset;
              unsigned int startsector;
              unsigned int fatindex;
patacongo's avatar
patacongo committed
              /* FAT12 is more complex because it has 12-bits (1.5 bytes)
               * per FAT entry. Get the offset to the first byte:
               */
patacongo's avatar
patacongo committed
              fatoffset = (clusterno * 3) / 2;
              fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
patacongo's avatar
patacongo committed
              /* Read the sector at this offset */
patacongo's avatar
patacongo committed
              if (fat_fscacheread(fs, fatsector) < 0)
patacongo's avatar
patacongo committed
                {
patacongo's avatar
patacongo committed
                  /* Read error */
                  break;
patacongo's avatar
patacongo committed
                }
patacongo's avatar
patacongo committed
              /* Get the first, LS byte of the cluster from the FAT */
patacongo's avatar
patacongo committed
              fatindex    = fatoffset & SEC_NDXMASK(fs);
              startsector = fs->fs_buffer[fatindex];
patacongo's avatar
patacongo committed
              /* With FAT12, the second byte of the cluster number may lie in
               * a different sector than the first byte.
               */
patacongo's avatar
patacongo committed
              fatindex++;
              if (fatindex >= fs->fs_hwsectorsize)
patacongo's avatar
patacongo committed
                {
patacongo's avatar
patacongo committed
                  fatsector++;
                  fatindex = 0;
patacongo's avatar
patacongo committed
                  if (fat_fscacheread(fs, fatsector) < 0)
patacongo's avatar
patacongo committed
                    {
patacongo's avatar
patacongo committed
                      /* Read error */
                      break;
patacongo's avatar
patacongo committed
              /* Get the second, MS byte of the cluster for 16-bits.  The
               * does not depend on the endian-ness of the target, but only
               * on the fact that the byte stream is little-endian.
               */
patacongo's avatar
patacongo committed
              startsector |= (unsigned int)fs->fs_buffer[fatindex] << 8;
patacongo's avatar
patacongo committed
              /* Now, pick out the correct 12 bit cluster start sector value */
patacongo's avatar
patacongo committed
              if ((clusterno & 1) != 0)
patacongo's avatar
patacongo committed
                {
patacongo's avatar
patacongo committed
                  /* Odd.. take the MS 12-bits */
                  startsector >>= 4;
patacongo's avatar
patacongo committed
                }
patacongo's avatar
patacongo committed
              else
patacongo's avatar
patacongo committed
                {
patacongo's avatar
patacongo committed
                  /* Even.. take the LS 12-bits */
                  startsector &= 0x0fff;
patacongo's avatar
patacongo committed
                }
patacongo's avatar
patacongo committed
              return startsector;
patacongo's avatar
patacongo committed
          case FSTYPE_FAT16 :
patacongo's avatar
patacongo committed
            {
patacongo's avatar
patacongo committed
              unsigned int fatoffset = 2 * clusterno;
              size_t       fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
              unsigned int fatindex  = fatoffset & SEC_NDXMASK(fs);
patacongo's avatar
patacongo committed
              if (fat_fscacheread(fs, fatsector) < 0)
patacongo's avatar
patacongo committed
                {
patacongo's avatar
patacongo committed
                  /* Read error */
                  break;
patacongo's avatar
patacongo committed
                }
patacongo's avatar
patacongo committed
              return FAT_GETFAT16(fs->fs_buffer, fatindex);
patacongo's avatar
patacongo committed
          case FSTYPE_FAT32 :
patacongo's avatar
patacongo committed
            {
patacongo's avatar
patacongo committed
              unsigned int fatoffset = 4 * clusterno;
              size_t       fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
              unsigned int fatindex  = fatoffset & SEC_NDXMASK(fs);
patacongo's avatar
patacongo committed
              if (fat_fscacheread(fs, fatsector) < 0)
patacongo's avatar
patacongo committed
                {
patacongo's avatar
patacongo committed
                  /* Read error */
                  break;
patacongo's avatar
patacongo committed
                }
patacongo's avatar
patacongo committed
              return FAT_GETFAT16(fs->fs_buffer, fatindex) & 0x0fffffff;
patacongo's avatar
patacongo committed
            }
patacongo's avatar
patacongo committed
          default:
              break;
patacongo's avatar
patacongo committed
        }
patacongo's avatar
patacongo committed
  /* There is no cluster information, or an error occured */

  return (ssize_t)-EINVAL;
patacongo's avatar
patacongo committed
}
patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: fat_putcluster
 *
 * Desciption: Write a new cluster start sector into the FAT
 *
 ****************************************************************************/
patacongo's avatar
patacongo committed
int fat_putcluster(struct fat_mountpt_s *fs, uint32 clusterno, size_t startsector)
patacongo's avatar
patacongo committed
{
patacongo's avatar
patacongo committed
  /* Verify that the cluster number is within range.  Zero erases the cluster. */
patacongo's avatar
patacongo committed
  if (clusterno == 0 || (clusterno >= 2 && clusterno < fs->fs_nclusters))
    {
      /* Okay.. Write the next cluster into the FAT.  The way we will do
       * this depends on the type of FAT filesystm we are dealing with.
       */
patacongo's avatar
patacongo committed
      switch (fs->fs_type)
        {
          case FSTYPE_FAT12 :