Skip to content
Snippets Groups Projects
fs_fat32dirent.c 84.4 KiB
Newer Older
patacongo's avatar
patacongo committed
          return ret;
        }

      /* Get a pointer to the directory entry */

      diroffset = DIRSEC_BYTENDX(fs, dirinfo->dir.fd_index);
      direntry  = &fs->fs_buffer[diroffset];

      /* Check if we are at the end of the directory */

      if (direntry[DIR_NAME] == DIR0_ALLEMPTY)
        {
          return -ENOENT;
        }

      /* Check if we have found the directory entry that we are looking for */

      if (direntry[DIR_NAME] != DIR0_EMPTY &&
          !(DIR_GETATTRIBUTES(direntry) & FATATTR_VOLUMEID) &&
          !memcmp(&direntry[DIR_NAME], dirinfo->fd_name, DIR_MAXFNAME) )
        {
          /* Yes.. Return success */

          dirinfo->fd_seq.ds_sector      = fs->fs_currentsector;
          dirinfo->fd_seq.ds_offset      = diroffset;
patacongo's avatar
patacongo committed
#ifdef CONFIG_FAT_LFN
          dirinfo->fd_seq.ds_cluster     = dirinfo->dir.fd_currcluster;
          dirinfo->fd_seq.ds_startsector = startsector;
patacongo's avatar
patacongo committed

          /* Position the last long file name directory entry at the same
           * position.
           */

          dirinfo->fd_seq.ds_lfnsector   = dirinfo->fd_seq.ds_sector;
          dirinfo->fd_seq.ds_lfnoffset   = dirinfo->fd_seq.ds_offset;
          dirinfo->fd_seq.ds_lfncluster  = dirinfo->fd_seq.ds_cluster;
patacongo's avatar
patacongo committed
#endif
         return OK;
        }

      /* No... get the next directory index and try again */

      if (fat_nextdirentry(fs, &dirinfo->dir) != OK)
        {
          return -ENOENT;
        }
    }
}

/****************************************************************************
 * Name: fat_cmplfnchunk
 *
 * Desciption:  There are 13 characters per LFN entry, broken up into three
 *   chunks for characts 1-5, 6-11, and 12-13.  This function will perform
 *   the comparison of a single chunk.
 *
 ****************************************************************************/

#ifdef CONFIG_FAT_LFN
static bool fat_cmplfnchunk(uint8_t *chunk, const uint8_t *substr, int nchunk)
patacongo's avatar
patacongo committed
  wchar_t wch;
  uint8_t ch;
  int     i;
patacongo's avatar
patacongo committed

  /* Check bytes 1-nchunk */

  for (i = 0; i < nchunk; i++)
    {
      /* Get the next character from the name string (which might be the NUL
       * terminating character).
      if (*substr == '\0')
patacongo's avatar
patacongo committed
        }
      else
        {
          ch = *substr++;
patacongo's avatar
patacongo committed
      /* Get the next unicode character from the chunk.  We only handle
       * ASCII. For ASCII, the upper byte should be zero and the lower
       * should match the ASCII code.
      wch = (wchar_t)fat_getuint16((uint8_t*)chunk);
patacongo's avatar
patacongo committed
      if ((wch & 0xff) != (wchar_t)ch)
      /* The characters match.  If we just matched the NUL terminating
       * character, then the strings match and we are finished.
      if (ch == '\0')
        {
          return true;
        }

      /* Try the next character from the directory entry. */

      chunk += sizeof(wchar_t);
patacongo's avatar
patacongo committed
    }

  /* All of the characters in the chunk match.. Return success */

  return true;
}
#endif

/****************************************************************************
 * Name: fat_cmplfname
 *
 * Desciption: Given an LFN directory entry, compare a substring of the name
 *   to a portion in the directory entry.
 *
 ****************************************************************************/

#ifdef CONFIG_FAT_LFN
static bool fat_cmplfname(const uint8_t *direntry, const uint8_t *substr)
patacongo's avatar
patacongo committed
{
  uint8_t *chunk;
patacongo's avatar
patacongo committed
  int len;
  /* How much of string do we have to compare? (including the NUL
   * terminator).
   */
  len = strlen((char*)substr) + 1;
patacongo's avatar
patacongo committed

  /* Check bytes 1-5 */

  chunk = LDIR_PTRWCHAR1_5(direntry);
  match = fat_cmplfnchunk(chunk, substr, 5);
  if (match && len > 5)
      /* Check bytes 6-11 */
patacongo's avatar
patacongo committed
      chunk = LDIR_PTRWCHAR6_11(direntry);
      match = fat_cmplfnchunk(chunk, &substr[5], 6);
      if (match && len > 11)
          /* Check bytes 12-13 */
patacongo's avatar
patacongo committed
          chunk = LDIR_PTRWCHAR12_13(direntry);
          match = fat_cmplfnchunk(chunk, &substr[11], 2);
patacongo's avatar
patacongo committed
}
#endif

/****************************************************************************
 * Name: fat_findlfnentry
 *
 * Desciption: Find a sequence of long file name directory entries.
 *
 * NOTE: As a side effect, this function returns with the sector containing
 *   the short file name directory entry in the cache.
 *
patacongo's avatar
patacongo committed
 ****************************************************************************/

#ifdef CONFIG_FAT_LFN
static inline int fat_findlfnentry(struct fat_mountpt_s *fs,
                                   struct fat_dirinfo_s *dirinfo)
{
  uint16_t diroffset;
  uint8_t *direntry;
  uint8_t  lastseq;
  uint8_t  seqno;
  uint8_t  nfullentries;
  uint8_t  nentries;
  uint8_t  remainder;
  uint8_t  checksum = 0;
patacongo's avatar
patacongo committed
  int      offset;
  int      namelen;
  int      ret;

  /* Get the length of the long file name (size of the fd_lfname array is
   * LDIR_MAXFNAME+1 we do not have to check the length of the string).
  namelen = strlen((char*)dirinfo->fd_lfname);
  DEBUGASSERT(namelen <= LDIR_MAXFNAME+1);
patacongo's avatar
patacongo committed

  /* How many LFN directory entries are we expecting? */

  nfullentries = namelen / LDIR_MAXLFNCHARS;
  remainder    = namelen - nfullentries * LDIR_MAXLFNCHARS;
  nentries     = nfullentries;
  if (remainder > 0)
    {
      nentries++;
    }
  DEBUGASSERT(nentries > 0 && nentries <= LDIR_MAXLFNS);

  /* This is the first sequency number we are looking for, the sequence
   * number of the last LFN entry (remember that they appear in reverse
   * order.. from last to first).
   */
patacongo's avatar
patacongo committed
  lastseq = LDIR0_LAST | nentries;
patacongo's avatar
patacongo committed
  seqno   = lastseq;

  /* Save the starting sector of the directory.  This is needed later to
   * re-scan the directory, looking duplicate short alias names.
   */

  startsector   = dirinfo->dir.fd_currsector;

patacongo's avatar
patacongo committed
  /* Search, beginning with the current sector, for a directory entry this
   * the match shore name
   */

  for (;;)
    {
      /* Read the next sector into memory */

      ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector);
      if (ret < 0)
        {
patacongo's avatar
patacongo committed
      /* Get a pointer to the directory entry */

      diroffset = DIRSEC_BYTENDX(fs, dirinfo->dir.fd_index);
      direntry  = &fs->fs_buffer[diroffset];

      /* Check if we are at the end of the directory */

      if (direntry[DIR_NAME] == DIR0_ALLEMPTY)
        {
          return -ENOENT;
        }

      /* Is this an LFN entry?  Does it have the sequence number we are
       * looking for?
       */

      if (LDIR_GETATTRIBUTES(direntry) != LDDIR_LFNATTR ||
          LDIR_GETSEQ(direntry) != seqno)
patacongo's avatar
patacongo committed
        {
          /* No, restart the search at the next entry */

          seqno = lastseq;
patacongo's avatar
patacongo committed
        }

      /* Yes.. If this is not the "last" LFN entry, then the checksum must
       * also be the same.
       */

      if (seqno == lastseq)
        {
          /* Just save the checksum for subsequent checks */

          checksum = LDIR_GETCHECKSUM(direntry);
patacongo's avatar
patacongo committed
        }

      /* Not the first entry in the sequence.  Does the checksum match the
       * previous sequences?
       */

      else if (checksum != LDIR_GETCHECKSUM(direntry))
patacongo's avatar
patacongo committed
        {
          /* No, restart the search at the next entry */

          seqno = lastseq;
patacongo's avatar
patacongo committed
        }

      /* Check if the name substring in this LFN matches the corresponding
       * substring of the name we are looking for.
patacongo's avatar
patacongo committed
      offset = ((seqno & LDIR0_SEQ_MASK) - 1) * LDIR_MAXLFNCHARS;
      if (fat_cmplfname(direntry, &dirinfo->fd_lfname[offset]))
patacongo's avatar
patacongo committed
        {
          /* Yes.. it matches.  Check the sequence number.  Is this the
           * "last" LFN entry (i.e., the one that appears first)?
           */

          if (seqno == lastseq)
            {
              /* Yes.. Save information about this LFN entry position */

              dirinfo->fd_seq.ds_lfnsector   = fs->fs_currentsector;
              dirinfo->fd_seq.ds_lfnoffset   = diroffset;
              dirinfo->fd_seq.ds_lfncluster  = dirinfo->dir.fd_currcluster;
              dirinfo->fd_seq.ds_startsector = startsector;
patacongo's avatar
patacongo committed
              seqno &= LDIR0_SEQ_MASK;
            }

          /* Is this the first sequence number (i.e., the LFN entry that
           * will appear last)?
           */

          if (seqno == 1)
            {
              /* We have found all of the LFN entries.  The next directory
               * entry should be the one containing the short file name
               * alias and all of the meat about the file or directory.
               */

              if (fat_nextdirentry(fs, &dirinfo->dir) != OK)
                {
                  return -ENOENT;
                }

patacongo's avatar
patacongo committed
              /* Make sure that the directory entry is in the sector cache */

              ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector);
              if (ret < 0)
                {
                  return ret;
                }

              /* Get a pointer to the directory entry */

              diroffset = DIRSEC_BYTENDX(fs, dirinfo->dir.fd_index);
              direntry  = &fs->fs_buffer[diroffset];
patacongo's avatar
patacongo committed
              /* Verify the checksum */

              if (fat_lfnchecksum(&direntry[DIR_NAME]) == checksum)
                {
                  /* Success! Save the position of the directory entry and
                   * return success.
                   */
patacongo's avatar
patacongo committed
                  dirinfo->fd_seq.ds_sector  = fs->fs_currentsector;
                  dirinfo->fd_seq.ds_offset  = diroffset;
                  dirinfo->fd_seq.ds_cluster = dirinfo->dir.fd_currcluster;
                  return OK;
patacongo's avatar
patacongo committed
                }

              /* Bad news.. reset and continue with this entry (which is
               * probably not an LFN entry unless the file systen is
               * seriously corrupted.
               */

              seqno = lastseq;
              continue;
            }

          /* No.. there are more LFN entries to go.  Decrement the sequence
           * number and check the next directory entry.
           */
patacongo's avatar
patacongo committed
          seqno--;
        }
      else
        {
          /* No.. the names do not match.  Restart the search at the next
           * entry.
           */
patacongo's avatar
patacongo committed
          seqno = lastseq;
        }

      /* Continue at the next directory entry */
 
next_entry:
      if (fat_nextdirentry(fs, &dirinfo->dir) != OK)
        {
          return -ENOENT;
        }
patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: fat_allocatesfnentry
 *
 * Desciption: Find a free directory entry for a short file name entry.
 *
 ****************************************************************************/
patacongo's avatar
patacongo committed
static inline int fat_allocatesfnentry(struct fat_mountpt_s *fs,
                                       struct fat_dirinfo_s *dirinfo)
{
  uint16_t diroffset;
  uint8_t *direntry;
#ifdef CONFIG_FAT_LFN
  off_t    startsector;
#endif
patacongo's avatar
patacongo committed
  uint8_t  ch;
  int      ret;

  /* Save the sector number of the first sector of the directory.  We don't
   * really need this for short file name entries; this is just done for
   * consistency with the long file name logic.
   */

#ifdef CONFIG_FAT_LFN
  startsector = dirinfo->dir.fd_currsector;
#endif

  /* Then search for a free short file name directory entry */

patacongo's avatar
patacongo committed
  for (;;)
    {
      /* Read the directory sector into fs_buffer */

      ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector);
      if (ret < 0)
        {
          /* Make sure that the return value is NOT -ENOSPC */

          return -EIO;
        }

      /* Get a pointer to the entry at fd_index */

      diroffset = (dirinfo->dir.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
      direntry  = &fs->fs_buffer[diroffset];

      /* Check if this directory entry is empty */

      ch = direntry[DIR_NAME];
      if (ch == DIR0_ALLEMPTY || ch == DIR0_EMPTY)
        {
          /* It is empty -- we have found a directory entry */

          dirinfo->fd_seq.ds_sector       = fs->fs_currentsector;
          dirinfo->fd_seq.ds_offset       = diroffset;
patacongo's avatar
patacongo committed
#ifdef CONFIG_FAT_LFN
          dirinfo->fd_seq.ds_cluster      = dirinfo->dir.fd_currcluster;
          dirinfo->fd_seq.ds_startsector  = startsector;
patacongo's avatar
patacongo committed

          /* Set the "last" long file name offset to the same entry */

          dirinfo->fd_seq.ds_lfnsector    = dirinfo->fd_seq.ds_sector;
          dirinfo->fd_seq.ds_lfnoffset    = dirinfo->fd_seq.ds_offset;
          dirinfo->fd_seq.ds_lfncluster   = dirinfo->fd_seq.ds_cluster;
#endif
patacongo's avatar
patacongo committed
          return OK;
        }

      /* It is not empty try the next one */

      ret = fat_nextdirentry(fs, &dirinfo->dir);
      if (ret < 0)
        {
          /* This will return -ENOSPC if we have examined all of the
           * directory entries without finding a free entry.
           */

          return ret;
        }
    }
}

/****************************************************************************
patacongo's avatar
patacongo committed
 * Name: fat_allocatelfnentry
patacongo's avatar
patacongo committed
 * Desciption: Find a sequence of free directory entries for a several long
 *   and one short file name entry.
 * On entry, dirinfo.dir refers to the first interesting entry the directory.
 *
 ****************************************************************************/
patacongo's avatar
patacongo committed
#ifdef CONFIG_FAT_LFN
static inline int fat_allocatelfnentry(struct fat_mountpt_s *fs,
                                       struct fat_dirinfo_s *dirinfo)
{
  uint16_t diroffset;
  uint8_t *direntry;
patacongo's avatar
patacongo committed
  uint8_t  nentries;
  uint8_t  remainder;
  uint8_t  needed;
patacongo's avatar
patacongo committed
  int      namelen;
  int      ret;

  /* Get the length of the long file name (size of the fd_lfname array is
   * LDIR_MAXFNAME+1 we do not have to check the length of the string).
  namelen = strlen((char *)dirinfo->fd_lfname);
  DEBUGASSERT(namelen <= LDIR_MAXFNAME+1);
patacongo's avatar
patacongo committed

  /* How many LFN directory entries are we expecting? */

  nentries   = namelen / LDIR_MAXLFNCHARS;
  remainder  = namelen - nentries * LDIR_MAXLFNCHARS;
patacongo's avatar
patacongo committed
  if (remainder > 0)
    {
      nentries++;
    }
  DEBUGASSERT(nentries > 0 && nentries <= LDIR_MAXLFNS);

  /* Plus another for short file name entry that follows the sequence of LFN
   * entries.
   */

  nentries++;

  /* Save the sector number of the first sector of the directory.  We will
   * need this later for re-scanning the directory to verify that a FAT file
   * name is unique.
   */

  startsector = dirinfo->dir.fd_currsector;

patacongo's avatar
patacongo committed
  /* Now, search the directory looking for a sequence for free entries that
   * long.
   */

  needed = nentries;
  for (;;)
    {
      /* Read the directory sector into fs_buffer */

      ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector);
      if (ret < 0)
        {
          /* Make sure that the return value is NOT -ENOSPC */

          return -EIO;
        }

      /* Get a pointer to the entry at fd_index */

      diroffset = (dirinfo->dir.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
      direntry  = &fs->fs_buffer[diroffset];

      /* Check if this directory entry is empty */

patacongo's avatar
patacongo committed
      ch = LDIR_GETSEQ(direntry);
patacongo's avatar
patacongo committed
      if (ch == DIR0_ALLEMPTY || ch == DIR0_EMPTY)
        {
          /* It is empty -- we have found a directory entry.  Is this the
           * "last" LFN entry (i.e., the one that occurs first)?
           */

          if (needed == nentries)
            {
              /* Yes.. remember the position of this entry */

              dirinfo->fd_seq.ds_lfnsector    = fs->fs_currentsector;
              dirinfo->fd_seq.ds_lfnoffset    = diroffset;
              dirinfo->fd_seq.ds_lfncluster   = dirinfo->dir.fd_currcluster;
              dirinfo->fd_seq.ds_startsector  = startsector;
patacongo's avatar
patacongo committed
            }
patacongo's avatar
patacongo committed

          /* Is this last entry we need (i.e., the entry for the short
           * file name entry)?
           */
           
          if (needed <= 1)
            {
              /* Yes.. remember the position of this entry and return
               * success.
               */

              dirinfo->fd_seq.ds_sector  = fs->fs_currentsector;
              dirinfo->fd_seq.ds_offset  = diroffset;
              dirinfo->fd_seq.ds_cluster = dirinfo->dir.fd_currcluster;
              return OK;
            }

          /* Otherwise, just decrement the number of directory entries
           * needed and continue looking.
           */

          needed--;
        }

      /* The directory entry is not available */

      else
        {
          /* Reset the search and continue looking */

          needed = nentries;
        }

      /* Try the next directory entry */

      ret = fat_nextdirentry(fs, &dirinfo->dir);
      if (ret < 0)
        {
          /* This will return -ENOSPC if we have examined all of the
           * directory entries without finding a free entry.
           */

          return ret;
        }
    }
}
#endif

/****************************************************************************
 * Name: fat_getsfname
 * Desciption:  Get the 8.3 filename from a directory entry.  On entry, the
 *  short file name entry is already in the cache.
 *
 ****************************************************************************/

static inline int fat_getsfname(uint8_t *direntry, char *buffer,
                                unsigned int buflen)
#ifdef CONFIG_FAT_LCNAMES
    uint8_t ntflags;
#endif
    int  ch;
    int  ndx;
    /* Check if we will be doing upper to lower case conversions */
#ifdef CONFIG_FAT_LCNAMES
    ntflags = DIR_GETNTRES(direntry);
#endif
    /* Reserve a byte for the NUL terminator */
    /* Get the 8-byte filename */
    for (ndx = 0; ndx < 8 && buflen > 0; ndx++)
      {
        /* Get the next filename character from the directory entry */
        ch = direntry[ndx];
        /* Any space (or ndx==8) terminates the filename */
        /* In this version, we never write 0xe5 in the directory filenames
         * (because we do not handle any character sets where 0xe5 is valid
         * in a filaname), but we could encounted this in a filesystem
         * written by some other system
         */
        if (ndx == 0 && ch == DIR0_E5)
          {
            ch = 0xe5;
          }
        /* Check if we should perform upper to lower case conversion
         * of the (whole) filename.
         */
#ifdef CONFIG_FAT_LCNAMES
        if (ntflags & FATNTRES_LCNAME && isupper(ch))
          {
            ch = tolower(ch);
          }
#endif
        /* Copy the next character into the filename */
        *buffer++ = ch;
        buflen--;
      }
    /* Check if there is an extension */
    if (direntry[8] != ' ' && buflen > 0)
      {
        /* Yes, output the dot before the extension */
        *buffer++ = '.';
        buflen--;
        /* Then output the (up to) 3 character extension */
        for (ndx = 8; ndx < 11 && buflen > 0; ndx++)
          {
            /* Get the next extensions character from the directory entry */
            ch = direntry[DIR_NAME + ndx];
            /* Any space (or ndx==11) terminates the extension */
            /* Check if we should perform upper to lower case conversion
             * of the (whole) filename.
             */
#ifdef CONFIG_FAT_LCNAMES
            if (ntflags & FATNTRES_LCEXT && isupper(ch))
              {
                ch = tolower(ch);
              }
#endif
        /* Copy the next character into the filename */
    /* Put a null terminator at the end of the filename.  We don't have to
     * check if there is room because we reserved a byte for the NUL
     * terminator at the beginning of this function.
     */
    *buffer = '\0';
    return OK;
}

/****************************************************************************
 * Name: fat_getlfnchunk
 * Desciption:  There are 13 characters per LFN entry, broken up into three
 *   chunks for characts 1-5, 6-11, and 12-13.  This function will get the
 *   file name characters from one chunk.
 *
 ****************************************************************************/

#ifdef CONFIG_FAT_LFN
static void fat_getlfnchunk(uint8_t *chunk, uint8_t *dest, int nchunk)
  /* Copy bytes 1-nchunk */
  for (i = 0; i < nchunk; i++)
      /* Get the next unicode character from the chunk.  We only handle ASCII.
       * For ASCII, the upper byte should be zero and the lower should match
       * the ASCII code.
       */

      wch = (wchar_t)fat_getuint16(chunk);
      *dest++ = (uint8_t)(wch & 0xff);
      chunk += sizeof(wchar_t);
    }
}
#endif

/****************************************************************************
 * Name: fat_getlfname
 *
 * Desciption:  Get the long filename from a sequence of directory entries.
 *   On entry, the "last" long file name entry is in the cache.  Returns with
 *   the short file name entry in the cache.
 *
 ****************************************************************************/

#ifdef CONFIG_FAT_LFN
static inline int fat_getlfname(struct fat_mountpt_s *fs, struct fs_dirent_s *dir)
{
  uint8_t  lfname[LDIR_MAXLFNCHARS];
  uint16_t diroffset;
  uint8_t *direntry;
  uint8_t  seqno;
  uint8_t  rawseq;
  uint8_t  offset;
  uint8_t  checksum;
  int      nsrc;
  int      i;

  /* Get a reference to the current directory entry */

  diroffset = (dir->u.fat.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
  direntry  = &fs->fs_buffer[diroffset];

  /* Get the starting sequence number */

  seqno = LDIR_GETSEQ(direntry);
  DEBUGASSERT((seqno & LDIR0_LAST) != 0);

  /* Sanity check */

  rawseq = (seqno & LDIR0_SEQ_MASK);
  if (rawseq < 1 || rawseq > LDIR_MAXLFNS)
    {
      return -EINVAL;
    }

  /* Save the checksum value */

  checksum = LDIR_GETCHECKSUM(direntry);

  /* Loop until the whole file name has been transferred */

  for (;;)
    {
      /* Get the string offset associated with the "last" entry. */

      offset = (rawseq - 1) * LDIR_MAXLFNCHARS;

      /* Will any of this file name fit into the destination buffer? */

      if (offset < NAME_MAX)
        {
          /* Yes.. extract and convert the unicode name */

          fat_getlfnchunk(LDIR_PTRWCHAR1_5(direntry), lfname, 5);
          fat_getlfnchunk(LDIR_PTRWCHAR6_11(direntry), &lfname[5], 6);
          fat_getlfnchunk(LDIR_PTRWCHAR12_13(direntry), &lfname[11], 2);

          /* Ignore trailing spaces on the "last" directory entry.  The
           * number of characters avaiable is LDIR_MAXLFNCHARS or that
           * minus the number of trailing spaces on the "last" directory
           * entry.
           */

          nsrc = LDIR_MAXLFNCHARS;
          if ((seqno & LDIR0_LAST) != 0)
            {
              /* Reduce the number of characters by the number of trailing
               * spaces.
               */

              for (; nsrc > 0 && lfname[nsrc-1] == ' '; nsrc--);

              /* Further reduce the length so that it fits in the destination
               * buffer.
               */

              if (offset + nsrc > NAME_MAX)
                {
                  nsrc = NAME_MAX - offset;
                }

              /* Add a null terminator to the destination string (the actual
               * length of the destination buffer is NAME_MAX+1, so the NUL
               * terminator will fit).
               */

              dir->fd_dir.d_name[offset+nsrc] = '\0';
            }

          /* Then transfer the characters */

          for (i = 0; i < nsrc && offset+i < NAME_MAX; i++)
            {
              dir->fd_dir.d_name[offset+i] = lfname[i];
      if (fat_nextdirentry(fs, &dir->u.fat) != OK)
        {
          return -ENOENT;
        }

      /* Make sure that the directory sector into the sector cache */

      ret = fat_fscacheread(fs, dir->u.fat.fd_currsector);
      if (ret < 0)
        {
          return ret;
        }

      /* Get a reference to the current directory entry */

      diroffset = (dir->u.fat.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
      direntry  = &fs->fs_buffer[diroffset];

      /* Get the next expected sequence number. */

      seqno = --rawseq;
      if (seqno < 1)
        {
          /* We just completed processing the "first" long file name entry
           * and we just read the short file name entry.  Verify that the
           * checksum of the short file name matches the checksum that we
           * found in the long file name entries.
           */

          if (fat_lfnchecksum(direntry) == checksum)
            {
              /* Yes.. return success! */

              return OK;
            }

          /* No, the checksum is bad. */

          return -EINVAL;
        }

      /* Verify the next long file name entry. Is this an LFN entry?  Does it
       * have the sequence number we are looking for?  Does the checksum
       * match the previous entries?
       */

      if (LDIR_GETATTRIBUTES(direntry) != LDDIR_LFNATTR ||
          LDIR_GETSEQ(direntry)        != seqno ||
          LDIR_GETCHECKSUM(direntry)   != checksum)
        {
          return -EINVAL;
        }
    }
}
#endif

/****************************************************************************
 * Name: fat_dirverify
 *
 * Desciption:
 *   Verify that every entry preceding this one is marked with something
 *   other than DIR0_ALLEMPTY.  This is necessary only in the root directory
 *   of freshly formatted volumes.  In that case, all entries are set to
 *   zero.
 *
 *   This function also assures that the sector containing the entry is in
 *   the sector cache.
 *
 ****************************************************************************/

static int fat_dirverify(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo,
                         uint16_t offset)
{
  uint8_t *direntry;
  uint16_t i;
  int ret;
 
  /* Make sure that the sector containing the directory entry is in the sector
  * cache.
   */

  ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector);
  if (ret < 0)
    {
      return ret;
    }

  /* Check every entry preceding this one */

  for (i = 0; i < offset; i += DIR_SIZE)
    {
      /* Is the rest of the directory marked empty? */

      direntry = &fs->fs_buffer[i];
      if (direntry[DIR_NAME] == DIR0_ALLEMPTY)
        {
          /* Then mark the just the entry as empty */

          fs->fs_dirty       = true;
          direntry[DIR_NAME] = DIR0_EMPTY;
        }
    }

  return OK;
}

/****************************************************************************
 * Name: fat_putsfname
 *
 * Desciption: Write the short directory entry name.
 *
 * Assumption:  The directory sector is in the cache.
 *
 ****************************************************************************/

static int fat_putsfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
{
  uint8_t *direntry = &fs->fs_buffer[dirinfo->fd_seq.ds_offset];
 
  memcpy(&direntry[DIR_NAME], dirinfo->fd_name, DIR_MAXFNAME);
#ifdef CONFIG_FAT_LCNAMES
  DIR_PUTNTRES(direntry, dirinfo->fd_ntflags);
#else
  DIR_PUTNTRES(direntry, 0);
#endif
  fs->fs_dirty = true;
  return OK;
}

/****************************************************************************
 * Name: fat_initlfname
 *
 * Desciption:  There are 13 characters per LFN entry, broken up into three
 *   chunks for characts 1-5, 6-11, and 12-13.  This function will put the
 *   0xffff characters into one chunk.
 *
 ****************************************************************************/

#ifdef CONFIG_FAT_LFN
static void fat_initlfname(uint8_t *chunk, int nchunk)