Skip to content
Snippets Groups Projects
fs_fat32dirent.c 74.6 KiB
Newer Older
patacongo's avatar
patacongo committed
  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;

  /* 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;
              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;
  uint8_t  ch;
  int      ret;

  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;
#ifdef CONFIG_FAT_LFN
          dirinfo->fd_seq.ds_cluster = dirinfo->dir.fd_currcluster;
#endif

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

#ifdef CONFIG_FAT_LFN
          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.
 *
 ****************************************************************************/
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;
  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);
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++;

  /* 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;
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_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_putlfnspaces
 *
 * 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
 *   space characters into one chunk.
 *
 ****************************************************************************/

#ifdef CONFIG_FAT_LFN
static void fat_putlfnspaces(uint8_t *chunk, int nchunk)
{
  int i;

  /* Initialize unicode characters 1-nchunk */

  for (i = 0; i < nchunk; i++)
    {
patacongo's avatar
patacongo committed
      /* The write the unicode space character into the directory entry. */
      fat_putuint16((uint8_t *)chunk, (uint16_t)' ');
      chunk += sizeof(wchar_t);
    }
}
#endif

/****************************************************************************
 * Name: fat_putlfnchunk
 *
 * 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
 *   file name characters into one chunk.
 *
 ****************************************************************************/

#ifdef CONFIG_FAT_LFN
static void fat_putlfnchunk(uint8_t *chunk, const uint8_t *src, int nchunk)
{
  uint16_t wch;
  int i;

  /* Write bytes 1-nchunk */

  for (i = 0; i < nchunk; i++)
    {
      /* Get the next ascii character from the name substring and convert it
       * to unicode.  The upper byte should be zero and the lower should be
       * the ASCII code.  The write the unicode character into the directory
       * entry.
       */

      wch = (uint16_t)*src++;
      fat_putuint16(chunk, wch);
      chunk += sizeof(wchar_t);
    }
}
#endif

/****************************************************************************
 * Name: fat_putlfname
 *
 * Desciption:  Write the long filename into 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 int fat_putlfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
{
  uint16_t diroffset;
  uint8_t *direntry;
  uint8_t  nfullentries;
  uint8_t  nentries;
  uint8_t  remainder;
  uint8_t  offset;
  uint8_t  seqno;
  uint8_t  checksum;
  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);

  /* How many LFN directory entries do we need to write? */

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

  /* Create the short file name alias */

  ret = fat_createalias(dirinfo);
  if (ret < 0)
    {
      return ret;
    }

  /* Get the short file name checksum */

  checksum = fat_lfnchecksum(dirinfo->fd_name);

  /* Setup the starting sequence number */

  seqno = LDIR0_LAST | nentries;

  /* Set up the initial positional data */

  dirinfo->dir.fd_currcluster  = dirinfo->fd_seq.ds_lfncluster;
  dirinfo->dir.fd_currsector   = dirinfo->fd_seq.ds_lfnsector;
patacongo's avatar
patacongo committed
  dirinfo->dir.fd_index        = dirinfo->fd_seq.ds_lfnoffset / DIR_SIZE;

  /* Make sure that the sector containing the "last" long file name entry
   * is in the sector cache (it probably is not).
   */

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

  /* Now loop, writing each long file name entry */

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

patacongo's avatar
patacongo committed
      offset = (nentries - 1) * LDIR_MAXLFNCHARS;

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

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

      /* Is this the "last" LFN directory entry? */

      if ((seqno & LDIR0_LAST) != 0 && remainder != 0)
        {
          int nbytes;

          /* Initialize the "last" directory entry name to all spaces */

          fat_putlfnspaces(LDIR_PTRWCHAR1_5(direntry), 5);
          fat_putlfnspaces(LDIR_PTRWCHAR6_11(direntry), 6);
          fat_putlfnspaces(LDIR_PTRWCHAR12_13(direntry), 2);

          /* Store the tail portion of the long file name in directory entry */

          nbytes = MIN(5, remainder);
          fat_putlfnchunk(LDIR_PTRWCHAR1_5(direntry),
                          &dirinfo->fd_lfname[offset], nbytes);
          remainder -= nbytes;

          if (remainder > 0)
            {
patacongo's avatar
patacongo committed
              nbytes = MIN(6, remainder);
              fat_putlfnchunk(LDIR_PTRWCHAR6_11(direntry),
                              &dirinfo->fd_lfname[offset+5], nbytes);
              remainder -= nbytes;
            }

          if (remainder > 0)
            {
              nbytes = MIN(2, remainder);
              fat_putlfnchunk(LDIR_PTRWCHAR12_13(direntry),
                              &dirinfo->fd_lfname[offset+11], nbytes);
              remainder -= nbytes;
            }

          /* The remainder should now be zero */
          
          DEBUGASSERT(remainder == 0);
        }
      else
        {
          /* Store a portion long file name in this directory entry  */

          fat_putlfnchunk(LDIR_PTRWCHAR1_5(direntry),
                          &dirinfo->fd_lfname[offset], 5);
          fat_putlfnchunk(LDIR_PTRWCHAR6_11(direntry),
                          &dirinfo->fd_lfname[offset+5], 6);
          fat_putlfnchunk(LDIR_PTRWCHAR12_13(direntry),
                          &dirinfo->fd_lfname[offset+11], 2);
        }

      /* Write the remaining directory entries */

      LDIR_PUTSEQ(direntry, seqno);
      LDIR_PUTATTRIBUTES(direntry, LDDIR_LFNATTR);
      LDIR_PUTNTRES(direntry, 0);
      LDIR_PUTCHECKSUM(direntry, checksum);
      fs->fs_dirty = true;

      /* Read next directory entry */

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

      /* 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;
        }

      /* Decrement the number of entries and get the next sequence number. */

      if (--nentries <= 0)
        {
          /* We have written all of the long file name entries to the media
           * and we have the short file name entry in the cache.  We can
           * just return success.
           */

          return OK;
        }

      /* The sequence number is just the number of entries left to be
       * written.
       */

      seqno = nentries;
    }
}
#endif

/****************************************************************************
 * Name: fat_putsfdirentry
 *
 * Desciption: Write a short file name directory entry
 *
 * Assumption:  The directory sector is in the cache.  The caller will write
 *   sector information.
 *
 ****************************************************************************/

static int fat_putsfdirentry(struct fat_mountpt_s *fs,
                             struct fat_dirinfo_s *dirinfo,
                             uint8_t attributes, uint32_t fattime)
{
  uint8_t *direntry;

  /* Initialize the 32-byte directory entry */

  direntry = &fs->fs_buffer[dirinfo->fd_seq.ds_offset];
  memset(direntry, 0, DIR_SIZE);

  /* Directory name info */

  (void)fat_putsfname(fs, dirinfo);

  /* Set the attribute attribute, write time, creation time */

  DIR_PUTATTRIBUTES(direntry, attributes);

  /* Set the time information */

  DIR_PUTWRTTIME(direntry, fattime & 0xffff);
  DIR_PUTCRTIME(direntry, fattime & 0xffff);
  DIR_PUTWRTDATE(direntry, fattime >> 16);
  DIR_PUTCRDATE(direntry, fattime >> 16);

  fs->fs_dirty = true;
  return OK;
}

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

/****************************************************************************
 * Name: fat_finddirentry
 *
 * Desciption: Given a path to something that may or may not be in the file
 *   system, return the description of the directory entry of the requested