Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:16

0001 /**
0002  * @file
0003  *
0004  * @ingroup DOSFS
0005  *
0006  * @brief Miscellaneous Routines Implementation for MSDOS FileSystem
0007  */
0008 
0009 /*
0010  *  Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
0011  *  Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
0012  *
0013  *  Modifications to support UTF-8 in the file system are
0014  *  Copyright (c) 2013 embedded brains GmbH & Co. KG
0015  *
0016  *  The license and distribution terms for this file may be
0017  *  found in the file LICENSE in this distribution or at
0018  *  http://www.rtems.org/license/LICENSE.
0019  */
0020 
0021 #define MSDOS_TRACE 1
0022 
0023 #ifdef HAVE_CONFIG_H
0024 #include "config.h"
0025 #endif
0026 
0027 #include <stdlib.h>
0028 #include <ctype.h>
0029 #include <sys/time.h>
0030 #include <unistd.h>
0031 #include <string.h>
0032 #include <assert.h>
0033 #include <errno.h>
0034 #include <inttypes.h>
0035 #include <rtems/libio_.h>
0036 
0037 #include "fat.h"
0038 #include "fat_fat_operations.h"
0039 #include "fat_file.h"
0040 
0041 #include "msdos.h"
0042 
0043 
0044 #include <stdio.h>
0045 
0046 #define MSDOS_LFN_ENTRY_SIZE \
0047   (MSDOS_LFN_LEN_PER_ENTRY * MSDOS_NAME_LFN_BYTES_PER_CHAR)
0048 
0049 #define MSDOS_LFN_ENTRY_SIZE_UTF8 \
0050   ((MSDOS_LFN_LEN_PER_ENTRY + 1 ) * MSDOS_NAME_LFN_BYTES_PER_CHAR \
0051     * MSDOS_NAME_MAX_UTF8_BYTES_PER_CHAR)
0052 
0053 /*
0054  * External strings. Saves space this way.
0055  */
0056 const char *const MSDOS_DOT_NAME    = ".          ";
0057 const char *const MSDOS_DOTDOT_NAME = "..         ";
0058 
0059 uint8_t
0060 msdos_lfn_checksum(const void *entry)
0061 {
0062     const uint8_t *name;
0063     uint8_t        cs;
0064     int            i;
0065 
0066     name = (const uint8_t *) MSDOS_DIR_NAME(entry);
0067     cs = 0;
0068 
0069     for (i = 0; i < MSDOS_SHORT_NAME_LEN; ++i) {
0070         cs = ((cs & 1) ? 0x80 : 0) + (cs >> 1) + name[i];
0071     }
0072 
0073     return cs;
0074 }
0075 
0076 /* msdos_is_valid_name_char --
0077  *     Routine to check the character in a file or directory name.
0078  *     The characters support in the short file name are letters,
0079  *     digits, or characters with code points values greater than
0080  *     127 (not sure what this last is) plus the following special
0081  *     characters "$%'-_@~`!(){}^#&". The must be uppercase.
0082  *
0083  *     The following 6 characters are allowed in a long names,
0084  *     " +,;=[]" including a space and lower case letters.
0085  *
0086  * PARAMETERS:
0087  *     ch        - character to check.
0088  *
0089  * RETURNS:
0090  *     MSDOS_NAME_INVALID - Not valid in a long or short name.
0091  *     MSDOS_NAME_SHORT   - Valid in a short name or long name.
0092  *     MSDOS_NAME_LONG    - Valid in a long name only.
0093  *
0094  */
0095 static msdos_name_type_t
0096 msdos_is_valid_name_char(const char ch)
0097 {
0098     if (strchr(" +,;=[]", ch) != NULL)
0099         return MSDOS_NAME_LONG;
0100 
0101     if ((ch == '.') || isalnum((unsigned char)ch) ||
0102         (strchr("$%'-_@~`!(){}^#&", ch) != NULL) || (unsigned char) ch > 127)
0103         return MSDOS_NAME_SHORT;
0104 
0105     return MSDOS_NAME_INVALID;
0106 }
0107 
0108 /* msdos_short_hex_number --
0109  *     Routine to set the hex number in the SFN.
0110  *
0111  * PARAMETERS:
0112  *     name      - name to change
0113  *     num       - number to set
0114  *
0115  * RETURNS:
0116  *     nothing
0117  *
0118  */
0119 static void
0120 msdos_short_name_hex(char* sfn, uint32_t num)
0121 {
0122     static const char* hex = "0123456789ABCDEF";
0123     char* c = MSDOS_DIR_NAME(sfn);
0124     int   i;
0125     for (i = 0; i < 2; i++, c++)
0126       if ((*c == ' ') || (*c == '.'))
0127         *c = '_';
0128     for (i = 0; i < 4; i++, c++)
0129       *c = hex[(num >> ((3 - i) * 4)) & 0xf];
0130     *c++ = '~';
0131     *c   = '1';
0132 }
0133 
0134 /* msdos_name_type --
0135  *     Routine the type of file name.
0136  *
0137  * PARAMETERS:
0138  *     name      - name to check
0139  *
0140  * RETURNS:
0141  *     true the name is long, else the name is short.
0142  *
0143  */
0144 #define MSDOS_NAME_TYPE_PRINT 0
0145 static msdos_name_type_t
0146 msdos_name_type(const char *name, int name_len)
0147 {
0148     bool lowercase = false;
0149     bool uppercase = false;
0150     int  dot_at = -1;
0151     int  count = 0;
0152 
0153     while (*name && (count < name_len))
0154     {
0155         bool is_dot = *name == '.';
0156         msdos_name_type_t type = msdos_is_valid_name_char(*name);
0157 
0158 #if MSDOS_NAME_TYPE_PRINT
0159         printf ("MSDOS_NAME_TYPE: c:%02x type:%d\n", *name, type);
0160 #endif
0161 
0162         if ((type == MSDOS_NAME_INVALID) || (type == MSDOS_NAME_LONG))
0163             return type;
0164 
0165         if (dot_at >= 0)
0166         {
0167             if (is_dot || ((count - dot_at) > 3))
0168             {
0169 #if MSDOS_NAME_TYPE_PRINT
0170                 printf ("MSDOS_NAME_TYPE: LONG[1]: is_dot:%d, at:%d cnt\n",
0171                         is_dot, dot_at, count);
0172 #endif
0173                 return MSDOS_NAME_LONG;
0174             }
0175         }
0176         else
0177         {
0178             if (count == 8 && !is_dot)
0179             {
0180 #if MSDOS_NAME_TYPE_PRINT
0181                 printf ("MSDOS_NAME_TYPE: LONG[2]: is_dot:%d, at:%d cnt\n",
0182                         is_dot, dot_at, count);
0183 #endif
0184                 return MSDOS_NAME_LONG;
0185             }
0186         }
0187 
0188         if (is_dot)
0189             dot_at = count;
0190         else if ((*name >= 'A') && (*name <= 'Z'))
0191             uppercase = true;
0192         else if ((*name >= 'a') && (*name <= 'z'))
0193             lowercase = true;
0194 
0195         count++;
0196         name++;
0197     }
0198 
0199     if (lowercase && uppercase)
0200     {
0201 #if MSDOS_NAME_TYPE_PRINT
0202         printf ("MSDOS_NAME_TYPE: LONG[3]\n");
0203 #endif
0204         return MSDOS_NAME_LONG;
0205     }
0206 
0207 #if MSDOS_NAME_TYPE_PRINT
0208     printf ("MSDOS_NAME_TYPE: SHORT[1]\n");
0209 #endif
0210     return MSDOS_NAME_SHORT;
0211 }
0212 
0213 /* msdos_long_to_short --
0214  *     Routine to creates a short name from a long. Start the end of the
0215  *
0216  * PARAMETERS:
0217  *     name      - name to check
0218  *
0219  * RETURNS:
0220  *     true the name is long, else the name is short.
0221  *
0222  */
0223 #define MSDOS_L2S_PRINT 0
0224 msdos_name_type_t
0225 msdos_long_to_short(rtems_dosfs_convert_control     *converter,
0226                     const char                      *lfn,
0227                     int                              lfn_len,
0228                     char                            *sfn,
0229                     int                              sfn_len)
0230 {
0231     msdos_name_type_t type;
0232     int               eno = 0;
0233     int               i;
0234     ssize_t           short_filename_length = sfn_len;
0235     void             *buffer = converter->buffer.data;
0236     size_t            codepage_name_len = converter->buffer.size;
0237 
0238     /*
0239      * Fill with spaces. This is how a short directory entry is padded.
0240      */
0241     memset (sfn, ' ', sfn_len);
0242 
0243     /*
0244      * Handle '.' and '..' specially.
0245      */
0246     if ((lfn[0] == '.') && (lfn_len == 1))
0247     {
0248         sfn[0] = '.';
0249 #if MSDOS_L2S_PRINT
0250         printf ("MSDOS_L2S: SHORT[1]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
0251 #endif
0252         return MSDOS_NAME_SHORT;
0253     }
0254 
0255     if ((lfn[0] == '.') && (lfn[1] == '.') && (lfn_len == 2))
0256     {
0257         sfn[0] = sfn[1] = '.';
0258 #if MSDOS_L2S_PRINT
0259         printf ("MSDOS_L2S: SHORT[2]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
0260 #endif
0261         return MSDOS_NAME_SHORT;
0262     }
0263 
0264     /*
0265      * Filenames with only blanks and dots are not allowed!
0266      */
0267     for (i = 0; i < lfn_len; i++)
0268         if ((lfn[i] != ' ') && (lfn[i] != '.'))
0269             break;
0270 
0271     if (i == lfn_len)
0272     {
0273 #if MSDOS_L2S_PRINT
0274         printf ("MSDOS_L2S: INVALID[1]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
0275 #endif
0276         return MSDOS_NAME_INVALID;
0277     }
0278 
0279     /*
0280      * Is this a short name ?
0281      */
0282 
0283     eno = (*converter->handler->utf8_to_codepage) (
0284         converter,
0285         (const uint8_t*)&lfn[0],
0286         lfn_len,
0287         buffer,
0288         &codepage_name_len);
0289     if (eno == EINVAL)
0290     {
0291         type = MSDOS_NAME_LONG;
0292     }
0293     else
0294     {
0295         type = msdos_name_type (
0296             buffer,
0297             codepage_name_len);
0298     }
0299 
0300     if (type != MSDOS_NAME_INVALID)
0301     {
0302         short_filename_length = msdos_filename_utf8_to_short_name_for_save (
0303             converter,
0304             (const uint8_t*)lfn,
0305             lfn_len,
0306             sfn,
0307             short_filename_length);
0308         if (short_filename_length < 0 ) {
0309             type = MSDOS_NAME_INVALID;
0310         }
0311 #if MSDOS_L2S_PRINT
0312         printf ("MSDOS_L2S: TYPE:%d lfn:'%s' SFN:'%s'\n", type, lfn, sfn);
0313 #endif
0314     }
0315     else
0316     {
0317 #if MSDOS_L2S_PRINT
0318         printf ("MSDOS_L2S: INVALID[2]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
0319 #endif
0320     }
0321 
0322     return type;
0323 }
0324 
0325 /* msdos_find_name --
0326  *     Find the node which correspondes to the name, open fat-file which
0327  *     correspondes to the found node and close fat-file which correspondes
0328  *     to the node we searched in.
0329  *
0330  * PARAMETERS:
0331  *     parent_loc - parent node description
0332  *     name       - name to find
0333  *
0334  * RETURNS:
0335  *     RC_OK and updated 'parent_loc' on success, or -1 if error
0336  *     occurred (errno set apropriately)
0337  *
0338  */
0339 int
0340 msdos_find_name(
0341     rtems_filesystem_location_info_t *parent_loc,
0342     const char                       *name,
0343     int                               name_len
0344     )
0345 {
0346     int                rc = RC_OK;
0347     msdos_fs_info_t   *fs_info = parent_loc->mt_entry->fs_info;
0348     fat_file_fd_t     *fat_fd = NULL;
0349     msdos_name_type_t  name_type;
0350     fat_dir_pos_t      dir_pos;
0351     unsigned short     time_val = 0;
0352     unsigned short     date = 0;
0353     char               node_entry[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
0354 
0355     memset(node_entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
0356 
0357     name_type = msdos_long_to_short (
0358         fs_info->converter,
0359         name,
0360         name_len,
0361         MSDOS_DIR_NAME(node_entry),
0362         MSDOS_NAME_MAX);
0363 
0364     /*
0365      * find the node which corresponds to the name in the directory pointed by
0366      * 'parent_loc'
0367      */
0368     rc = msdos_get_name_node(parent_loc, false, name, name_len, name_type,
0369                              &dir_pos, node_entry);
0370     if (rc != RC_OK)
0371         return rc;
0372 
0373     if (((*MSDOS_DIR_ATTR(node_entry)) & MSDOS_ATTR_VOLUME_ID) ||
0374         ((*MSDOS_DIR_ATTR(node_entry) & MSDOS_ATTR_LFN_MASK) == MSDOS_ATTR_LFN))
0375         return MSDOS_NAME_NOT_FOUND_ERR;
0376 
0377     /* open fat-file corresponded to the found node */
0378     rc = fat_file_open(&fs_info->fat, &dir_pos, &fat_fd);
0379     if (rc != RC_OK)
0380         return rc;
0381 
0382     fat_fd->dir_pos = dir_pos;
0383 
0384     /*
0385      * I don't like this if, but: we should do it, or should write new file
0386      * size and first cluster num to the disk after each write operation
0387      * (even if one byte is written  - that is TOO slow) because
0388      * otherwise real values of these fields stored in fat-file descriptor
0389      * may be accidentally rewritten with wrong values stored on the disk
0390      */
0391     if (fat_fd->links_num == 1)
0392     {
0393         fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(node_entry);
0394 
0395         time_val = *MSDOS_DIR_WRITE_TIME(node_entry);
0396         date = *MSDOS_DIR_WRITE_DATE(node_entry);
0397 
0398         fat_fd->mtime = msdos_date_dos2unix(CF_LE_W(date), CF_LE_W(time_val));
0399 
0400         time_val = *MSDOS_DIR_CRT_TIME(node_entry);
0401         date = *MSDOS_DIR_CRT_DATE(node_entry);
0402 
0403         fat_fd->ctime = msdos_date_dos2unix(CF_LE_W(date), CF_LE_W(time_val));
0404 
0405         if ((*MSDOS_DIR_ATTR(node_entry)) & MSDOS_ATTR_DIRECTORY)
0406         {
0407             fat_fd->fat_file_type = FAT_DIRECTORY;
0408             fat_fd->size_limit = MSDOS_MAX_DIR_LENGTH;
0409 
0410             rc = fat_file_size(&fs_info->fat, fat_fd);
0411             if (rc != RC_OK)
0412             {
0413                 fat_file_close(&fs_info->fat, fat_fd);
0414                 return rc;
0415             }
0416         }
0417         else
0418         {
0419             fat_fd->fat_file_size = CF_LE_L(*MSDOS_DIR_FILE_SIZE(node_entry));
0420             fat_fd->fat_file_type = FAT_FILE;
0421             fat_fd->size_limit = MSDOS_MAX_FILE_SIZE;
0422         }
0423 
0424         /* these data is not actual for zero-length fat-file */
0425         fat_fd->map.file_cln = 0;
0426         fat_fd->map.disk_cln = fat_fd->cln;
0427 
0428         if ((fat_fd->fat_file_size != 0) &&
0429             (fat_fd->fat_file_size <= fs_info->fat.vol.bpc))
0430         {
0431             fat_fd->map.last_cln = fat_fd->cln;
0432         }
0433         else
0434         {
0435             fat_fd->map.last_cln = FAT_UNDEFINED_VALUE;
0436         }
0437     }
0438 
0439     /* close fat-file corresponded to the node we searched in */
0440     rc = fat_file_close(&fs_info->fat, parent_loc->node_access);
0441     if (rc != RC_OK)
0442     {
0443         fat_file_close(&fs_info->fat, fat_fd);
0444         return rc;
0445     }
0446 
0447     /* update node_info_ptr field */
0448     parent_loc->node_access = fat_fd;
0449 
0450     return rc;
0451 }
0452 
0453 /* msdos_get_name_node --
0454  *     This routine is used in two ways: for a new node creation (a) or for
0455  *     search the node which correspondes to the name parameter (b).
0456  *     In case (a) 'name' should be set up to NULL and 'name_dir_entry' should
0457  *     point to initialized 32 bytes structure described a new node.
0458  *     In case (b) 'name' should contain a valid string.
0459  *
0460  *     (a): reading fat-file which correspondes to directory we are going to
0461  *          create node in. If free slot is found write contents of
0462  *          'name_dir_entry' into it. If reach end of fat-file and no free
0463  *          slot found, write 32 bytes to the end of fat-file.
0464  *
0465  *     (b): reading fat-file which correspondes to directory and trying to
0466  *          find slot with the name field == 'name' parameter
0467  *
0468  *
0469  * PARAMETERS:
0470  *     parent_loc     - node description to create node in or to find name in
0471  *     name           - NULL or name to find
0472  *     paux           - identify a node location on the disk -
0473  *                      cluster num and offset inside the cluster
0474  *     short_dir_entry - node to create/placeholder for found node (IN/OUT)
0475  *
0476  * RETURNS:
0477  *     RC_OK, filled aux_struct_ptr and name_dir_entry on success, or -1 if
0478  *     error occurred (errno set apropriately)
0479  *
0480  */
0481 int
0482 msdos_get_name_node(
0483     const rtems_filesystem_location_info_t *parent_loc,
0484     bool                                    create_node,
0485     const char                             *name,
0486     int                                     name_len,
0487     msdos_name_type_t                       name_type,
0488     fat_dir_pos_t                          *dir_pos,
0489     char                                   *name_dir_entry
0490     )
0491 {
0492     int              rc = RC_OK;
0493     fat_file_fd_t   *fat_fd = parent_loc->node_access;
0494     uint32_t         dotdot_cln = 0;
0495 
0496     /* find name in fat-file which corresponds to the directory */
0497     rc = msdos_find_name_in_fat_file(parent_loc->mt_entry, fat_fd,
0498                                      create_node, (const uint8_t*)name, name_len, name_type,
0499                                      dir_pos, name_dir_entry);
0500     if ((rc != RC_OK) && (rc != MSDOS_NAME_NOT_FOUND_ERR))
0501         return rc;
0502 
0503     if (!create_node)
0504     {
0505         /* if we search for valid name and name not found -> return */
0506         if (rc == MSDOS_NAME_NOT_FOUND_ERR)
0507             return rc;
0508 
0509         /*
0510          * if we have deal with ".." - it is a special case :(((
0511          *
0512          * Really, we should return cluster num and offset not of ".." slot, but
0513          * slot which correspondes to real directory name.
0514          */
0515         if (rc == RC_OK)
0516         {
0517             if (strncmp(name, "..", 2) == 0)
0518             {
0519                 dotdot_cln = MSDOS_EXTRACT_CLUSTER_NUM((name_dir_entry));
0520 
0521                 /* are we right under root dir ? */
0522                 if (dotdot_cln == 0)
0523                 {
0524                     /*
0525                      * we can relax about first_char field - it never should be
0526                      * used for root dir
0527                      */
0528                     fat_dir_pos_init(dir_pos);
0529                     dir_pos->sname.cln = FAT_ROOTDIR_CLUSTER_NUM;
0530                 }
0531                 else
0532                 {
0533                     rc =
0534                         msdos_get_dotdot_dir_info_cluster_num_and_offset(parent_loc->mt_entry,
0535                                                                          dotdot_cln,
0536                                                                          dir_pos,
0537                                                                          name_dir_entry);
0538                     if (rc != RC_OK)
0539                         return rc;
0540                 }
0541             }
0542         }
0543     }
0544     return rc;
0545 }
0546 
0547 /*
0548  * msdos_get_dotdot_dir_info_cluster_num_and_offset
0549  *
0550  * Unfortunately, in general, we cann't work here in fat-file ideologic
0551  * (open fat_file "..", get ".." and ".", open "..", find an entry ...)
0552  * because if we open
0553  * fat-file ".." it may happend that we have two different fat-file
0554  * descriptors ( for real name of directory and ".." name ) for a single
0555  * file  ( cluster num of both pointers to the same cluster )
0556  * But...we do it because we protected by semaphore
0557  *
0558  */
0559 
0560 /* msdos_get_dotdot_dir_info_cluster_num_and_offset --
0561  *     Get cluster num and offset not of ".." slot, but slot which correspondes
0562  *     to real directory name.
0563  *
0564  * PARAMETERS:
0565  *     mt_entry       - mount table entry
0566  *     cln            - data cluster num extracted drom ".." slot
0567  *     paux           - identify a node location on the disk -
0568  *                      number of cluster and offset inside the cluster
0569  *     dir_entry      - placeholder for found node
0570  *
0571  * RETURNS:
0572  *     RC_OK, filled 'paux' and 'dir_entry' on success, or -1 if error occurred
0573  *     (errno set apropriately)
0574  *
0575  */
0576 int
0577 msdos_get_dotdot_dir_info_cluster_num_and_offset(
0578     rtems_filesystem_mount_table_entry_t *mt_entry,
0579     uint32_t                              cln,
0580     fat_dir_pos_t                        *dir_pos,
0581     char                                 *dir_entry
0582     )
0583 {
0584     int              rc = RC_OK;
0585     msdos_fs_info_t *fs_info = mt_entry->fs_info;
0586     fat_file_fd_t   *fat_fd = NULL;
0587     char             dot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
0588     char             dotdot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
0589     uint32_t         cl4find = 0;
0590     rtems_dosfs_convert_control *converter = fs_info->converter;
0591 
0592     /*
0593      * open fat-file corresponded to ".."
0594      */
0595     rc = fat_file_open(&fs_info->fat, dir_pos, &fat_fd);
0596     if (rc != RC_OK)
0597         return rc;
0598 
0599     fat_fd->cln = cln;
0600     fat_fd->fat_file_type = FAT_DIRECTORY;
0601     fat_fd->size_limit = MSDOS_MAX_DIR_LENGTH;
0602 
0603     fat_fd->map.file_cln = 0;
0604     fat_fd->map.disk_cln = fat_fd->cln;
0605 
0606     rc = fat_file_size(&fs_info->fat, fat_fd);
0607     if (rc != RC_OK)
0608     {
0609         fat_file_close(&fs_info->fat, fat_fd);
0610         return rc;
0611     }
0612 
0613     /* find "." node in opened directory */
0614     memset(dot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
0615     msdos_long_to_short(
0616         converter,
0617         ".", 1, dot_node, MSDOS_SHORT_NAME_LEN);
0618     rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, false, (const uint8_t*)".", 1,
0619                                      MSDOS_NAME_SHORT, dir_pos, dot_node);
0620 
0621     if (rc != RC_OK)
0622     {
0623         fat_file_close(&fs_info->fat, fat_fd);
0624         return rc;
0625     }
0626 
0627     /* find ".." node in opened directory */
0628     memset(dotdot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
0629     msdos_long_to_short(
0630         converter,
0631         "..", 2, dotdot_node, MSDOS_SHORT_NAME_LEN);
0632     rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, false, (const uint8_t*)"..", 2,
0633                                      MSDOS_NAME_SHORT, dir_pos,
0634                                      dotdot_node);
0635 
0636     if (rc != RC_OK)
0637     {
0638         fat_file_close(&fs_info->fat, fat_fd);
0639         return rc;
0640     }
0641 
0642     cl4find = MSDOS_EXTRACT_CLUSTER_NUM(dot_node);
0643 
0644     /* close fat-file corresponded to ".." directory */
0645     rc = fat_file_close(&fs_info->fat, fat_fd);
0646     if ( rc != RC_OK )
0647         return rc;
0648 
0649     if ( (MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node)) == 0)
0650     {
0651         /*
0652          * we handle root dir for all FAT types in the same way with the
0653          * ordinary directories ( through fat_file_* calls )
0654          */
0655         fat_dir_pos_init(dir_pos);
0656         dir_pos->sname.cln = FAT_ROOTDIR_CLUSTER_NUM;
0657     }
0658 
0659     /* open fat-file corresponded to second ".." */
0660     rc = fat_file_open(&fs_info->fat, dir_pos, &fat_fd);
0661     if (rc != RC_OK)
0662         return rc;
0663 
0664     if ((MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node)) == 0)
0665         fat_fd->cln = fs_info->fat.vol.rdir_cl;
0666     else
0667         fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node);
0668 
0669     fat_fd->fat_file_type = FAT_DIRECTORY;
0670     fat_fd->size_limit = MSDOS_MAX_DIR_LENGTH;
0671 
0672     fat_fd->map.file_cln = 0;
0673     fat_fd->map.disk_cln = fat_fd->cln;
0674 
0675     rc = fat_file_size(&fs_info->fat, fat_fd);
0676     if (rc != RC_OK)
0677     {
0678         fat_file_close(&fs_info->fat, fat_fd);
0679         return rc;
0680     }
0681 
0682     /* in this directory find slot with specified cluster num */
0683     rc = msdos_find_node_by_cluster_num_in_fat_file(mt_entry, fat_fd, cl4find,
0684                                                     dir_pos, dir_entry);
0685     if (rc != RC_OK)
0686     {
0687         fat_file_close(&fs_info->fat, fat_fd);
0688         return rc;
0689     }
0690     rc = fat_file_close(&fs_info->fat, fat_fd);
0691     return rc;
0692 }
0693 
0694 
0695 /* fat_file_write_time_and_date --
0696  *     Write last write date and time for a file to the disk (to corresponded
0697  *     32bytes node)
0698  *
0699  * PARAMETERS:
0700  *     fs_info  - fat fs info
0701  *     fat_fd   - fat-file descriptor
0702  *
0703  * RETURNS:
0704  *     RC_OK on success, or -1 if error occurred (errno set apropriately).
0705  *
0706  */
0707 int
0708 fat_file_write_time_and_date(
0709     fat_fs_info_t                        *fs_info,
0710     fat_file_fd_t                        *fat_fd
0711     )
0712 {
0713     int              rc = RC_OK;
0714     ssize_t          ret;
0715     uint16_t         time_val;
0716     uint16_t         date;
0717     uint32_t         sec = 0;
0718     uint32_t         byte = 0;
0719 
0720     /*
0721      * calculate input for fat_sector_write: convert (cluster num, offset) to
0722      * (sector num, new offset)
0723      */
0724     sec = fat_cluster_num_to_sector_num(fs_info, fat_fd->dir_pos.sname.cln);
0725     sec += (fat_fd->dir_pos.sname.ofs >> fs_info->vol.sec_log2);
0726     /* byte points to start of 32bytes structure */
0727     byte = fat_fd->dir_pos.sname.ofs & (fs_info->vol.bps - 1);
0728 
0729     msdos_date_unix2dos(fat_fd->mtime, &date, &time_val);
0730 
0731     time_val = CT_LE_W(time_val);
0732     ret = fat_sector_write(fs_info, sec, byte + MSDOS_FILE_WTIME_OFFSET,
0733                            2, (char *)(&time_val));
0734     if ( ret < 0 )
0735         rc = -1;
0736 
0737     date = CT_LE_W(date);
0738     ret = fat_sector_write(fs_info, sec, byte + MSDOS_FILE_WDATE_OFFSET,
0739                            2, (char *)(&date));
0740     if ( ret < 0 )
0741         rc = -1;
0742 
0743     ret = fat_sector_write(fs_info, sec, byte + MSDOS_FILE_ADATE_OFFSET,
0744                            2, (char *)(&date));
0745     if ( ret < 0 )
0746         rc = -1;
0747 
0748     msdos_date_unix2dos(fat_fd->ctime, &date, &time_val);
0749 
0750     time_val = CT_LE_W(time_val);
0751     ret = fat_sector_write(fs_info, sec, byte + MSDOS_FILE_CTIME_OFFSET,
0752                            2, (char *)(&time_val));
0753     if ( ret < 0 )
0754         rc = -1;
0755 
0756     date = CT_LE_W(date);
0757     ret = fat_sector_write(fs_info, sec, byte + MSDOS_FILE_CDATE_OFFSET,
0758                            2, (char *)(&date));
0759     if ( ret < 0 )
0760         rc = -1;
0761 
0762     return rc;
0763 }
0764 
0765 /* fat_set_first_cluster_num --
0766  *     Write number of first cluster of the file to the disk (to corresponded
0767  *     32bytes slot)
0768  *
0769  * PARAMETERS:
0770  *     fs_info  - fat fs info
0771  *     fat_fd   - fat-file descriptor
0772  *
0773  * RETURNS:
0774  *     RC_OK on success, or -1 if error occurred
0775  *
0776  */
0777 int
0778 fat_file_write_first_cluster_num(
0779     fat_fs_info_t                        *fs_info,
0780     fat_file_fd_t                        *fat_fd
0781     )
0782 {
0783     ssize_t          ret1 = 0, ret2 = 0;
0784     uint32_t         new_cln = fat_fd->cln;
0785     uint16_t         le_cl_low = 0;
0786     uint16_t         le_cl_hi = 0;
0787     uint32_t         sec = 0;
0788     uint32_t         byte = 0;
0789 
0790     /*
0791      * calculate input for fat_sector_write: convert (cluster num, offset) to
0792      * (sector num, new offset)
0793      */
0794     sec = fat_cluster_num_to_sector_num(fs_info, fat_fd->dir_pos.sname.cln);
0795     sec += (fat_fd->dir_pos.sname.ofs >> fs_info->vol.sec_log2);
0796     /* byte from points to start of 32bytes structure */
0797     byte = fat_fd->dir_pos.sname.ofs & (fs_info->vol.bps - 1);
0798 
0799     le_cl_low = CT_LE_W((uint16_t  )(new_cln & 0x0000FFFF));
0800     ret1 = fat_sector_write(fs_info, sec,
0801                             byte + MSDOS_FIRST_CLUSTER_LOW_OFFSET, 2,
0802                             (char *)(&le_cl_low));
0803     le_cl_hi = CT_LE_W((uint16_t  )((new_cln & 0xFFFF0000) >> 16));
0804     ret2 = fat_sector_write(fs_info, sec,
0805                             byte + MSDOS_FIRST_CLUSTER_HI_OFFSET, 2,
0806                             (char *)(&le_cl_hi));
0807     if ( (ret1 < 0) || (ret2 < 0) )
0808         return -1;
0809 
0810     return RC_OK;
0811 }
0812 
0813 
0814 /* fat_set_file size --
0815  *     Write file size of the file to the disk (to corresponded 32bytes slot)
0816  *
0817  * PARAMETERS:
0818  *     fs_info  - fat fs info
0819  *     fat_fd   - fat-file descriptor
0820  *
0821  * RETURNS:
0822  *     RC_OK on success, or -1 if error occurred (errno set apropriately).
0823  *
0824  */
0825 int
0826 fat_file_write_file_size(
0827     fat_fs_info_t                        *fs_info,
0828     fat_file_fd_t                        *fat_fd
0829     )
0830 {
0831     ssize_t          ret = 0;
0832     uint32_t         le_new_length = 0;
0833     uint32_t         sec = 0;
0834     uint32_t         byte = 0;
0835 
0836     sec = fat_cluster_num_to_sector_num(fs_info, fat_fd->dir_pos.sname.cln);
0837     sec += (fat_fd->dir_pos.sname.ofs >> fs_info->vol.sec_log2);
0838     byte = (fat_fd->dir_pos.sname.ofs & (fs_info->vol.bps - 1));
0839 
0840     if (fat_fd->fat_file_type == FAT_DIRECTORY) {
0841       le_new_length = CT_LE_L(0);
0842     } else {
0843       le_new_length = CT_LE_L(fat_fd->fat_file_size);
0844     }
0845 
0846     ret = fat_sector_write(fs_info, sec, byte + MSDOS_FILE_SIZE_OFFSET, 4,
0847                            (char *)(&le_new_length));
0848     if ( ret < 0 )
0849         return -1;
0850 
0851     return RC_OK;
0852 }
0853 
0854 /*
0855  * We should not check whether this routine is called for root dir - it
0856  * never can happend
0857  */
0858 
0859 /* msdos_set_first_char4file_name --
0860  *     Write first character of the name of the file to the disk (to
0861  *     corresponded 32bytes slot)
0862  *
0863  * PARAMETERS:
0864  *     mt_entry - mount table entry
0865  *     cl       - number of cluster
0866  *     ofs      - offset inside cluster
0867  *     fchar    - character to set up
0868  *
0869  * RETURNS:
0870  *     RC_OK on success, or -1 if error occurred (errno set apropriately)
0871  *
0872  */
0873 int
0874 msdos_set_first_char4file_name(
0875     rtems_filesystem_mount_table_entry_t *mt_entry,
0876     fat_dir_pos_t                        *dir_pos,
0877     unsigned char                         fchar
0878     )
0879 {
0880     ssize_t          ret;
0881     msdos_fs_info_t *fs_info = mt_entry->fs_info;
0882     uint32_t         dir_block_size;
0883     fat_pos_t        start = dir_pos->lname;
0884     fat_pos_t        end = dir_pos->sname;
0885 
0886     if ((end.cln == fs_info->fat.vol.rdir_cl) &&
0887         (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
0888       dir_block_size = fs_info->fat.vol.rdir_size;
0889     else
0890       dir_block_size = fs_info->fat.vol.bpc;
0891 
0892     if (dir_pos->lname.cln == FAT_FILE_SHORT_NAME)
0893       start = dir_pos->sname;
0894 
0895     /*
0896      * We handle the changes directly due the way the short file
0897      * name code was written rather than use the fat_file_write
0898      * interface.
0899      */
0900     while (true)
0901     {
0902       uint32_t sec = (fat_cluster_num_to_sector_num(&fs_info->fat, start.cln) +
0903                       (start.ofs >> fs_info->fat.vol.sec_log2));
0904       uint32_t byte = (start.ofs & (fs_info->fat.vol.bps - 1));
0905 
0906       ret = fat_sector_write(&fs_info->fat, sec, byte + MSDOS_FILE_NAME_OFFSET,
0907                              1, &fchar);
0908       if (ret < 0)
0909         return -1;
0910 
0911       if ((start.cln == end.cln) && (start.ofs == end.ofs))
0912         break;
0913 
0914       start.ofs += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
0915       if (start.ofs >= dir_block_size)
0916       {
0917         int rc;
0918         if ((end.cln == fs_info->fat.vol.rdir_cl) &&
0919             (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
0920           break;
0921         rc = fat_get_fat_cluster(&fs_info->fat, start.cln, &start.cln);
0922         if ( rc != RC_OK )
0923           return rc;
0924         start.ofs = 0;
0925       }
0926     }
0927 
0928     return  RC_OK;
0929 }
0930 
0931 /* msdos_dir_is_empty --
0932  *     Check whether directory which correspondes to the fat-file descriptor is
0933  *     empty.
0934  *
0935  * PARAMETERS:
0936  *     mt_entry - mount table entry
0937  *     fat_fd   - fat-file descriptor
0938  *     ret_val  - placeholder for result
0939  *
0940  * RETURNS:
0941  *     RC_OK on success, or -1 if error occurred
0942  *
0943  */
0944 int
0945 msdos_dir_is_empty(
0946     rtems_filesystem_mount_table_entry_t *mt_entry,
0947     fat_file_fd_t                        *fat_fd,
0948     bool                                 *ret_val
0949     )
0950 {
0951     ssize_t          ret = 0;
0952     msdos_fs_info_t *fs_info = mt_entry->fs_info;
0953     uint32_t         j = 0, i = 0;
0954 
0955     /* dir is not empty */
0956     *ret_val = false;
0957 
0958     while ((ret = fat_file_read(&fs_info->fat, fat_fd, j * fs_info->fat.vol.bps,
0959                                   fs_info->fat.vol.bps,
0960                                   fs_info->cl_buf)) != FAT_EOF)
0961     {
0962         if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
0963             return -1;
0964 
0965         assert(ret == fs_info->fat.vol.bps);
0966 
0967         /* have to look at the DIR_NAME as "raw" 8-bit data */
0968         for (i = 0;
0969              i < fs_info->fat.vol.bps;
0970              i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
0971         {
0972             char* entry = (char*) fs_info->cl_buf + i;
0973 
0974             /*
0975              * If the entry is empty, a long file name entry, or '.' or '..'
0976              * then consider it as empty.
0977              *
0978              * Just ignore long file name entries. They must have a short entry to
0979              * be valid.
0980              */
0981             if (((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
0982                  MSDOS_THIS_DIR_ENTRY_EMPTY) ||
0983                 ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
0984                  MSDOS_ATTR_LFN) ||
0985                 (strncmp(MSDOS_DIR_NAME((entry)), MSDOS_DOT_NAME,
0986                          MSDOS_SHORT_NAME_LEN) == 0) ||
0987                 (strncmp(MSDOS_DIR_NAME((entry)),
0988                          MSDOS_DOTDOT_NAME,
0989                          MSDOS_SHORT_NAME_LEN) == 0))
0990                 continue;
0991 
0992             /*
0993              * Nothing more to look at.
0994              */
0995             if ((*MSDOS_DIR_NAME(entry)) ==
0996                 MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
0997             {
0998                 *ret_val = true;
0999                 return RC_OK;
1000             }
1001 
1002             /*
1003              * Short file name entries mean not empty.
1004              */
1005             return RC_OK;
1006         }
1007         j++;
1008     }
1009     *ret_val = true;
1010     return RC_OK;
1011 }
1012 
1013 #define MSDOS_FIND_PRINT 0
1014 static int
1015 msdos_on_entry_found (
1016     msdos_fs_info_t                      *fs_info,
1017     fat_file_fd_t                        *fat_fd,
1018     const uint32_t                        bts2rd,
1019     char                                 *name_dir_entry,
1020     char                                 *entry,
1021     fat_dir_pos_t                        *dir_pos,
1022     uint32_t                              dir_offset,
1023     const uint32_t                        dir_entry,
1024     const fat_pos_t                      *lfn_start
1025 )
1026 {
1027     int rc = RC_OK;
1028 #if MSDOS_FIND_PRINT
1029     printf ("MSFS:[9.3] SNF found\n");
1030 #endif
1031     /*
1032      * We get the entry we looked for - fill the position
1033      * structure and the 32 bytes of the short entry
1034      */
1035     rc = fat_file_ioctl(&fs_info->fat,
1036                         fat_fd,
1037                         F_CLU_NUM,
1038                         dir_offset * bts2rd,
1039                         &dir_pos->sname.cln);
1040     if (rc == RC_OK) {
1041         dir_pos->sname.ofs = dir_entry;
1042 
1043         if (lfn_start->cln != FAT_FILE_SHORT_NAME)
1044         {
1045             rc = fat_file_ioctl (&fs_info->fat,
1046                                  fat_fd,
1047                                  F_CLU_NUM,
1048                                  lfn_start->cln * bts2rd,
1049                                  &lfn_start->cln);
1050         }
1051         if ( rc == RC_OK ) {
1052             dir_pos->lname.cln = lfn_start->cln;
1053             dir_pos->lname.ofs = lfn_start->ofs;
1054 
1055             memcpy(name_dir_entry, entry,
1056                    MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
1057         }
1058     }
1059 
1060     return rc;
1061 }
1062 
1063 ssize_t
1064 msdos_get_utf16_string_from_long_entry (
1065     const char                 *entry,
1066     uint16_t                   *entry_string_buf,
1067     const size_t                buf_size,
1068     bool                        is_first_entry
1069 )
1070 {
1071     ssize_t chars_in_entry;
1072 
1073     if (buf_size >= MSDOS_LFN_ENTRY_SIZE) {
1074         memcpy (&entry_string_buf[0],  &entry[1],  10 );
1075         memcpy (&entry_string_buf[5],  &entry[14], 12 );
1076         memcpy (&entry_string_buf[11], &entry[28],  4 );
1077 
1078         if (is_first_entry) {
1079             for (chars_in_entry = 0;
1080                  (   chars_in_entry < MSDOS_LFN_LEN_PER_ENTRY
1081                   && entry_string_buf[chars_in_entry] != 0x0000);
1082                   ++chars_in_entry) {
1083                 ;
1084             }
1085         }
1086         else
1087             chars_in_entry = MSDOS_LFN_LEN_PER_ENTRY;
1088 
1089         return chars_in_entry * MSDOS_NAME_LFN_BYTES_PER_CHAR;
1090     }
1091     else
1092         return ENOMEM;
1093 }
1094 
1095 /*  msdos_format_dirent_with_dot --
1096  *      This routine convert a (short) MSDOS filename as present on disk
1097  *      (fixed 8+3 characters, filled with blanks, without separator dot)
1098  *      to a "normal" format, with between 0 and 8 name chars,
1099  *      a separating dot and up to 3 extension characters
1100  *   Rules to work:
1101  *      - copy any (0-8) "name" part characters that are non-blank
1102  *      - if an extension exists, append a dot
1103  *      - copy any (0-3) non-blank extension characters
1104  *      - append a '\0' (dont count it for the rturn code
1105  *
1106  * PARAMETERS:
1107  *     dst: pointer to destination char array (must be big enough)
1108  *     src: pointer to source characters
1109  *
1110  *
1111  * RETURNS:
1112  *     the number of bytes (without trailing '\0'(written to destination
1113  */
1114 ssize_t
1115 msdos_format_dirent_with_dot(char *dst,const char *src)
1116 {
1117   ssize_t len;
1118   int i;
1119   const char *src_tmp;
1120 
1121   /*
1122    * find last non-blank character of base name
1123    */
1124   for (i = MSDOS_SHORT_BASE_LEN, src_tmp = src + MSDOS_SHORT_BASE_LEN - 1;
1125        i > 0 && *src_tmp == ' ';
1126        --i,--src_tmp)
1127     {};
1128   /*
1129    * copy base name to destination
1130    */
1131   src_tmp = src;
1132   len = i;
1133   while (i-- > 0) {
1134     *dst++ = tolower((unsigned char)(*src_tmp++));
1135   }
1136   /*
1137    * find last non-blank character of extension
1138    */
1139   for (i = MSDOS_SHORT_EXT_LEN,
1140         src_tmp = src + MSDOS_SHORT_BASE_LEN+MSDOS_SHORT_EXT_LEN-1;
1141        i > 0 && *src_tmp == ' ';
1142        --i, --src_tmp)
1143     {};
1144   /*
1145    * extension is not empty
1146    */
1147   if (i > 0) {
1148     *dst++ = '.'; /* append dot */
1149     ++len;        /* dot */
1150     src_tmp = src + MSDOS_SHORT_BASE_LEN;
1151     while (i-- > 0) {
1152       *dst++ = tolower((unsigned char)(*src_tmp++));
1153       ++len;
1154     }
1155   }
1156   *dst = '\0'; /* terminate string */
1157 
1158   return len;
1159 }
1160 
1161 static ssize_t
1162 msdos_long_entry_to_utf8_name (
1163     rtems_dosfs_convert_control *converter,
1164     const char                  *entry,
1165     const bool                   is_first_entry,
1166     uint8_t                     *entry_utf8_buf,
1167     const size_t                 buf_size)
1168 {
1169     ssize_t      retval         = 0;
1170     int          eno            = 0;
1171     size_t       bytes_in_utf8  = buf_size;
1172     size_t       bytes_in_buf;
1173     uint16_t     entry_string[MSDOS_LFN_LEN_PER_ENTRY];
1174 
1175     retval = msdos_get_utf16_string_from_long_entry (
1176         entry,
1177         entry_string,
1178         sizeof (entry_string),
1179         is_first_entry
1180     );
1181 
1182     if (retval >= 0) {
1183         bytes_in_buf = retval;
1184         eno = (*converter->handler->utf16_to_utf8) (
1185             converter,
1186             &entry_string[0],
1187             bytes_in_buf,
1188             &entry_utf8_buf[0],
1189             &bytes_in_utf8);
1190         if ( eno == 0 ) {
1191             retval = bytes_in_utf8;
1192         }
1193     }
1194 
1195     if (eno != 0) {
1196         retval = -1;
1197         errno  = eno;
1198     }
1199 
1200     return retval;
1201 }
1202 
1203 static ssize_t msdos_short_entry_to_utf8_name (
1204   rtems_dosfs_convert_control     *converter,
1205   const char                      *entry,
1206   uint8_t                         *buf,
1207   const size_t                     buf_size)
1208 {
1209   char         char_buf[MSDOS_NAME_MAX_WITH_DOT];
1210   int          eno             = 0;
1211   size_t       bytes_converted = buf_size;
1212   ssize_t      bytes_written   = msdos_format_dirent_with_dot(char_buf, entry);
1213 
1214   if (bytes_written > 0) {
1215     if (char_buf[0] == 0x05)
1216       char_buf[0] = 0xE5;
1217 
1218     eno = (*converter->handler->codepage_to_utf8) (
1219         converter,
1220         char_buf,
1221         bytes_written,
1222         buf,
1223         &bytes_converted);
1224     if (eno == 0)
1225         bytes_written = bytes_converted;
1226   } else {
1227       eno = EINVAL;
1228   }
1229 
1230   if (eno != 0) {
1231       bytes_written = -1;
1232       errno         = eno;
1233   }
1234 
1235   return bytes_written;
1236 }
1237 
1238 static ssize_t
1239 msdos_compare_entry_against_filename (
1240     rtems_dosfs_convert_control *converter,
1241     const uint8_t               *entry,
1242     const size_t                 entry_size,
1243     const uint8_t               *filename,
1244     const size_t                 name_len_remaining,
1245     bool                        *is_matching)
1246 {
1247   ssize_t      size_remaining = name_len_remaining;
1248   int          eno            = 0;
1249   uint8_t      entry_normalized[MSDOS_LFN_ENTRY_SIZE_UTF8];
1250   size_t       bytes_in_entry_normalized = sizeof ( entry_normalized );
1251 
1252   eno = (*converter->handler->utf8_normalize_and_fold) (
1253       converter,
1254       &entry[0],
1255       entry_size,
1256       &entry_normalized[0],
1257       &bytes_in_entry_normalized);
1258   if (eno == 0) {
1259 #if MSDOS_FIND_PRINT > 1
1260         printf ( "MSFS:[6] entry_normalized:%s"
1261                  "name:%s\n",
1262                  entry,
1263                  filename );
1264 #endif
1265         if (bytes_in_entry_normalized <= size_remaining) {
1266             size_remaining = size_remaining - bytes_in_entry_normalized;
1267             if (0 == memcmp ( &entry_normalized[0],
1268                               &filename[size_remaining],
1269                               bytes_in_entry_normalized)) {
1270                 *is_matching = true;
1271             } else {
1272                 *is_matching   = false;
1273                 size_remaining = name_len_remaining;
1274             }
1275 
1276         }
1277         else {
1278           *is_matching = false;
1279         }
1280     }
1281 
1282     if (eno != 0) {
1283       size_remaining = -1;
1284       errno          = eno;
1285     }
1286 
1287     return size_remaining;
1288 }
1289 
1290 static void
1291 msdos_prepare_for_next_entry(
1292     fat_pos_t *lfn_start,
1293     bool      *entry_matched,
1294     ssize_t   *name_len_remaining,
1295     size_t     name_len_for_compare)
1296 {
1297     lfn_start->cln = FAT_FILE_SHORT_NAME;
1298     *entry_matched = false;
1299     *name_len_remaining = name_len_for_compare;
1300 }
1301 
1302 static int
1303 msdos_find_file_in_directory (
1304     const uint8_t                        *filename_converted,
1305     const size_t                          name_len_for_compare,
1306     const msdos_name_type_t               name_type,
1307     msdos_fs_info_t                      *fs_info,
1308     fat_file_fd_t                        *fat_fd,
1309     const uint32_t                        bts2rd,
1310     const bool                            create_node,
1311     const unsigned int                    lfn_entries,
1312     char                                 *name_dir_entry,
1313     fat_dir_pos_t                        *dir_pos,
1314     uint32_t                             *empty_file_offset,
1315     uint32_t                             *empty_entry_count)
1316 {
1317     int               rc                = RC_OK;
1318     ssize_t           bytes_read;
1319     uint32_t          dir_entry;
1320     fat_pos_t         lfn_start;
1321     uint8_t           lfn_checksum      = 0;
1322     bool              entry_matched;
1323     bool              empty_space_found = false;
1324     uint32_t          entries_per_block = bts2rd / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
1325     int               lfn_entry         = 0;
1326     uint8_t           entry_utf8_normalized[MSDOS_LFN_ENTRY_SIZE_UTF8];
1327     size_t            bytes_in_entry;
1328     bool              filename_matched  = false;
1329     ssize_t           name_len_remaining;
1330     rtems_dosfs_convert_control *converter = fs_info->converter;
1331     uint32_t          dir_offset = 0;
1332 
1333     /*
1334      * Scan the directory seeing if the file is present. While
1335      * doing this see if a suitable location can be found to
1336      * create the entry if the name is not found.
1337      */
1338 
1339     msdos_prepare_for_next_entry(&lfn_start, &entry_matched,
1340                                  &name_len_remaining,
1341                                  name_len_for_compare);
1342 
1343     while (   (bytes_read = fat_file_read (&fs_info->fat, fat_fd, (dir_offset * bts2rd),
1344                                              bts2rd, fs_info->cl_buf)) != FAT_EOF
1345            && rc == RC_OK)
1346     {
1347         bool remainder_empty = false;
1348 #if MSDOS_FIND_PRINT
1349         printf ("MSFS:[2] dir_offset:%li\n", dir_offset);
1350 #endif
1351 
1352         if (bytes_read < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
1353             rtems_set_errno_and_return_minus_one(EIO);
1354 
1355         assert(bytes_read == bts2rd);
1356 
1357         /* have to look at the DIR_NAME as "raw" 8-bit data */
1358         for (dir_entry = 0;
1359              dir_entry < bts2rd && rc == RC_OK && (! filename_matched);
1360              dir_entry += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
1361         {
1362             char* entry = (char*) fs_info->cl_buf + dir_entry;
1363 
1364             /*
1365              * See if the entry is empty or the remainder of the directory is
1366              * empty ? Localize to make the code read better.
1367              */
1368             bool entry_empty = (*MSDOS_DIR_ENTRY_TYPE(entry) ==
1369                                 MSDOS_THIS_DIR_ENTRY_EMPTY);
1370             remainder_empty = (*MSDOS_DIR_ENTRY_TYPE(entry) ==
1371                                MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY);
1372 #if MSDOS_FIND_PRINT
1373             printf ("MSFS:[3] re:%i ee:%i do:%li de:%li(%ld)\n",
1374                     remainder_empty, entry_empty, dir_offset,
1375                     dir_entry, (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE));
1376 #endif
1377             /*
1378              * Remember where the we are, ie the start, so we can come back
1379              * to here and write the long file name if this is the start of
1380              * a series of empty entries. If empty_entry_count is 0 then
1381              * we are currently not inside an empty series of entries. It
1382              * is a count of empty entries.
1383              */
1384             if (*empty_entry_count == 0)
1385             {
1386                 *empty_file_offset = dir_offset * bts2rd + dir_entry;
1387             }
1388 
1389             if (remainder_empty)
1390             {
1391 #if MSDOS_FIND_PRINT
1392                 printf ("MSFS:[3.1] cn:%i esf:%i\n", create_node, empty_space_found);
1393 #endif
1394                 /*
1395                  * If just looking and there is no more entries in the
1396                  * directory - return name-not-found
1397                  */
1398                 if (!create_node)
1399                     rc = MSDOS_NAME_NOT_FOUND_ERR;
1400 
1401                 /*
1402                  * Lets go and write the directory entries. If we have not found
1403                  * any available space add the remaining number of entries to any that
1404                  * we may have already found that are just before this entry. If more
1405                  * are needed FAT_EOF is returned by the read and we extend the file.
1406                  */
1407                 if (   !empty_space_found
1408                     && rc == RC_OK )
1409                 {
1410                     *empty_entry_count +=
1411                     entries_per_block - (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
1412                     empty_space_found = true;
1413 #if MSDOS_FIND_PRINT
1414                     printf ( "MSFS:[3.2] esf:%i esc%"PRIu32"\n", empty_space_found, *empty_entry_count );
1415 #endif
1416                 }
1417                 break;
1418             }
1419             else if (entry_empty)
1420             {
1421                 if (create_node)
1422                 {
1423                     /*
1424                      * Remainder is not empty so is this entry empty ?
1425                      */
1426                     (*empty_entry_count)++;
1427 
1428                     if (*empty_entry_count == (lfn_entries + 1))
1429                         empty_space_found = true;
1430                 }
1431 #if MSDOS_FIND_PRINT
1432                 printf ("MSFS:[4.1] esc:%li esf:%i\n",
1433                         *empty_entry_count, empty_space_found);
1434 #endif
1435                 msdos_prepare_for_next_entry(&lfn_start, &entry_matched,
1436                                              &name_len_remaining,
1437                                              name_len_for_compare);
1438             }
1439             else
1440             {
1441                 /*
1442                  * A valid entry so handle it.
1443                  *
1444                  * If empty space has not been found we need to start the
1445                  * count again.
1446                  */
1447                 if (create_node && !empty_space_found)
1448                 {
1449                     *empty_file_offset = 0;
1450                     *empty_entry_count = 0;
1451                 }
1452 
1453                 /*
1454                  * Check the attribute to see if the entry is for a long
1455                  * file name.
1456                  */
1457                 if ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
1458                     MSDOS_ATTR_LFN)
1459                 {
1460                     bool is_first_lfn_entry =
1461                         (lfn_start.cln == FAT_FILE_SHORT_NAME);
1462 
1463 /*                    int   o;*/
1464 #if MSDOS_FIND_PRINT
1465                     printf ("MSFS:[4.2] lfn:%c entry:%i checksum:%i\n",
1466                             lfn_start.cln == FAT_FILE_SHORT_NAME ? 'f' : 't',
1467                             *MSDOS_DIR_ENTRY_TYPE(entry) & MSDOS_LAST_LONG_ENTRY_MASK,
1468                             *MSDOS_DIR_LFN_CHECKSUM(entry));
1469 #endif
1470                     /*
1471                      * If we are not already processing a LFN see if this is
1472                      * the first entry of a LFN ?
1473                      */
1474                     if (is_first_lfn_entry)
1475                     {
1476                         entry_matched = false;
1477 
1478                         /*
1479                          * The first entry must have the last long entry
1480                          * flag set.
1481                          */
1482                         if ((*MSDOS_DIR_ENTRY_TYPE(entry) &
1483                              MSDOS_LAST_LONG_ENTRY) == 0)
1484                             continue;
1485 
1486                         lfn_start.cln = dir_offset;
1487                         lfn_start.ofs = dir_entry;
1488                         lfn_entry = (*MSDOS_DIR_ENTRY_TYPE(entry)
1489                             & MSDOS_LAST_LONG_ENTRY_MASK);
1490                         lfn_checksum = *MSDOS_DIR_LFN_CHECKSUM(entry);
1491 
1492 #if MSDOS_FIND_PRINT
1493                         printf ("MSFS:[4.3] lfn_checksum:%i\n",
1494                                 lfn_checksum);
1495 #endif
1496                     }
1497 
1498                     /*
1499                      * If the entry number or the check sum do not match
1500                      * forget this series of long directory entries. These
1501                      * could be orphaned entries depending on the history
1502                      * of the disk.
1503                      */
1504                     if ((lfn_entry != (*MSDOS_DIR_ENTRY_TYPE(entry) &
1505                                        MSDOS_LAST_LONG_ENTRY_MASK)) ||
1506                         (lfn_checksum != *MSDOS_DIR_LFN_CHECKSUM(entry)))
1507                     {
1508 #if MSDOS_FIND_PRINT
1509                         printf ("MSFS:[4.4] no match\n");
1510 #endif
1511                         msdos_prepare_for_next_entry(&lfn_start,
1512                                                      &entry_matched,
1513                                                      &name_len_remaining,
1514                                                      name_len_for_compare);
1515                         continue;
1516                     }
1517 #if MSDOS_FIND_PRINT
1518                     printf ("MSFS:[5] lfne:%i\n", lfn_entry);
1519 #endif
1520                     lfn_entry--;
1521 
1522                     bytes_in_entry = msdos_long_entry_to_utf8_name (
1523                         converter,
1524                         entry,
1525                         is_first_lfn_entry,
1526                         &entry_utf8_normalized[0],
1527                         sizeof (entry_utf8_normalized));
1528                     if (bytes_in_entry > 0) {
1529                         name_len_remaining = msdos_compare_entry_against_filename (
1530                             converter,
1531                             &entry_utf8_normalized[0],
1532                             bytes_in_entry,
1533                             &filename_converted[0],
1534                             name_len_remaining,
1535                             &entry_matched);
1536 
1537                         if (name_len_remaining < 0 || !entry_matched) {
1538                             msdos_prepare_for_next_entry(&lfn_start,
1539                                                          &entry_matched,
1540                                                          &name_len_remaining,
1541                                                          name_len_for_compare);
1542                         }
1543                     } else {
1544                         msdos_prepare_for_next_entry(&lfn_start,
1545                                                      &entry_matched,
1546                                                      &name_len_remaining,
1547                                                      name_len_for_compare);
1548                     }
1549                 }
1550                 else
1551                 {
1552 #if MSDOS_FIND_PRINT
1553                     printf ("MSFS:[9.1] SFN entry, entry_matched:%i\n", entry_matched);
1554 #endif
1555                     /*
1556                      * SFN entry found.
1557                      *
1558                      * If a LFN has been found and it matched check the
1559                      * entries have all been found and the checksum is
1560                      * correct. If this is the case return the short file
1561                      * name entry.
1562                      */
1563                     if (entry_matched)
1564                     {
1565                         if (lfn_entry ||
1566                             name_len_remaining > 0 ||
1567                             lfn_checksum != msdos_lfn_checksum(entry)) {
1568                             msdos_prepare_for_next_entry(&lfn_start,
1569                                                          &entry_matched,
1570                                                          &name_len_remaining,
1571                                                          name_len_for_compare);
1572                         } else if (name_len_remaining == 0) {
1573                             filename_matched = true;
1574                             rc = msdos_on_entry_found (
1575                                 fs_info,
1576                                 fat_fd,
1577                                 bts2rd,
1578                                 name_dir_entry,
1579                                 entry,
1580                                 dir_pos,
1581                                 dir_offset,
1582                                 dir_entry,
1583                                 &lfn_start
1584                             );
1585                         }
1586 
1587 #if MSDOS_FIND_PRINT
1588                         printf ("MSFS:[9.2] checksum, entry_matched:%i, lfn_entry:%i, lfn_checksum:%02x/%02x\n",
1589                                 entry_matched, lfn_entry, lfn_checksum, msdos_lfn_checksum(entry));
1590 #endif
1591                     } else if ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_VOLUME_ID)
1592                                == 0) {
1593                         bytes_in_entry = MSDOS_SHORT_NAME_LEN + 1;
1594                         bytes_in_entry = msdos_short_entry_to_utf8_name (
1595                             converter,
1596                             MSDOS_DIR_NAME (entry),
1597                             &entry_utf8_normalized[0],
1598                             bytes_in_entry);
1599                         if (bytes_in_entry > 0) {
1600                             name_len_remaining = msdos_compare_entry_against_filename (
1601                                 converter,
1602                                 &entry_utf8_normalized[0],
1603                                 bytes_in_entry,
1604                                 &filename_converted[0],
1605                                 name_len_for_compare,
1606                                 &entry_matched);
1607                             if (entry_matched && name_len_remaining == 0) {
1608                                 filename_matched = true;
1609                                 rc = msdos_on_entry_found (
1610                                     fs_info,
1611                                     fat_fd,
1612                                     bts2rd,
1613                                     name_dir_entry,
1614                                     entry,
1615                                     dir_pos,
1616                                     dir_offset,
1617                                     dir_entry,
1618                                     &lfn_start
1619                                 );
1620                             }
1621                             if (rc == RC_OK && !filename_matched) {
1622                                 msdos_prepare_for_next_entry(&lfn_start,
1623                                                              &entry_matched,
1624                                                              &name_len_remaining,
1625                                                              name_len_for_compare);
1626                             }
1627                         } else {
1628                           msdos_prepare_for_next_entry(&lfn_start,
1629                                                        &entry_matched,
1630                                                        &name_len_remaining,
1631                                                        name_len_for_compare);
1632                         }
1633                     }
1634                 }
1635             }
1636         }
1637 
1638         if (filename_matched || remainder_empty)
1639             break;
1640 
1641         dir_offset++;
1642     }
1643     if ( ! filename_matched ) {
1644         /*
1645          * If we are not to create the entry return a not found error.
1646          */
1647         if (!create_node)
1648             rc = MSDOS_NAME_NOT_FOUND_ERR;
1649 
1650 #if MSDOS_FIND_PRINT
1651         printf ( "MSFS:[8.1] WRITE do:%"PRIu32" esc:%"PRIu32" efo:%"PRIu32"\n",
1652                  dir_offset, *empty_entry_count, *empty_file_offset );
1653 #endif
1654     }
1655 
1656     return rc;
1657 }
1658 
1659 static int
1660 msdos_get_pos(
1661     msdos_fs_info_t *fs_info,
1662     fat_file_fd_t   *fat_fd,
1663     uint32_t         bts2rd,
1664     uint32_t         file_offset,
1665     fat_pos_t       *pos
1666 )
1667 {
1668     pos->ofs = file_offset & (bts2rd - 1);
1669     return fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM,
1670                           file_offset, &pos->cln);
1671 }
1672 
1673 static int
1674 msdos_add_file (
1675     const char                           *name_converted,
1676     const msdos_name_type_t               name_type,
1677     msdos_fs_info_t                      *fs_info,
1678     fat_file_fd_t                        *fat_fd,
1679     const uint32_t                        bts2rd,
1680     const unsigned int                    lfn_entries,
1681     const char                           *name_dir_entry,
1682     fat_dir_pos_t                        *dir_pos,
1683     uint32_t                              empty_file_offset,
1684     const uint32_t                        empty_entry_count
1685 )
1686 {
1687     int              ret;
1688     ssize_t          bytes_written;
1689     uint8_t          lfn_checksum;
1690     int              lfn_entry;
1691     uint8_t         *entry;
1692     uint32_t         short_file_offset;
1693     uint32_t         length;
1694 
1695     /*
1696      * If there is not enough space available then extend the file.
1697      */
1698     if (empty_entry_count < lfn_entries + 1)
1699     {
1700         uint32_t unused;
1701 
1702         empty_file_offset = fat_fd->fat_file_size -
1703             empty_entry_count * MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
1704 
1705         ret = fat_file_extend(&fs_info->fat,
1706                               fat_fd,
1707                               true,
1708                               fat_fd->fat_file_size + fs_info->fat.vol.bpc,
1709                               &unused);
1710         if (ret != RC_OK)
1711             return ret;
1712     }
1713 
1714     if (name_type == MSDOS_NAME_LONG)
1715     {
1716         uint32_t slot;
1717 
1718         /*
1719          * If a long file name calculate the checksum of the short file name
1720          * data to place in each long file name entry. First set the short
1721          * file name to the slot of the SFN entry. This will mean no clashes
1722          * in this directory.
1723          */
1724         slot = (empty_file_offset /
1725             MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + lfn_entries + 1;
1726         msdos_short_name_hex(MSDOS_DIR_NAME(name_dir_entry), slot);
1727 
1728         lfn_checksum = msdos_lfn_checksum(name_dir_entry);
1729 
1730         short_file_offset = empty_file_offset + lfn_entries
1731             * MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
1732 
1733         /* Get position of first long file name entry */
1734         ret = msdos_get_pos(fs_info, fat_fd, bts2rd, empty_file_offset,
1735                             &dir_pos->lname);
1736         if (ret != RC_OK)
1737             return ret;
1738     } else {
1739         lfn_checksum = 0;
1740         short_file_offset = empty_file_offset;
1741         dir_pos->lname.cln = FAT_FILE_SHORT_NAME;
1742         dir_pos->lname.ofs = FAT_FILE_SHORT_NAME;
1743     }
1744 
1745     /* Get position of short file name entry */
1746     ret = msdos_get_pos(fs_info, fat_fd, bts2rd, short_file_offset,
1747                         &dir_pos->sname);
1748     if (ret != RC_OK)
1749         return ret;
1750 
1751     /*
1752      * Handle the entry writes.
1753      */
1754     entry = fs_info->cl_buf;
1755 
1756 #if MSDOS_FIND_PRINT
1757     printf ("MSFS:[9] read_cluster:%d efo:%ld ese:%ld\n",
1758             read_cluster, empty_file_offset, empty_space_entry);
1759 #endif
1760 
1761     /* Long file name entries */
1762     for (lfn_entry = 0; lfn_entry < lfn_entries; ++lfn_entry) {
1763         uint8_t       *p;
1764         const uint8_t *n;
1765         int            i;
1766         uint8_t        fill = 0;
1767 
1768         /*
1769          * Clear the entry before loading the data.
1770          */
1771         memset (entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
1772 
1773         *MSDOS_DIR_LFN_CHECKSUM(entry) = lfn_checksum;
1774 
1775         p = entry + 1;
1776         n = (const uint8_t *) name_converted +
1777             (lfn_entries - lfn_entry - 1) * MSDOS_LFN_ENTRY_SIZE;
1778 
1779 #if MSDOS_FIND_PRINT
1780         printf ("MSFS:[11] ");
1781 #endif
1782         for (i = 0; i < MSDOS_LFN_LEN_PER_ENTRY; ++i)
1783         {
1784             if (*n != 0 || *(n + 1) != 0)
1785             {
1786                 *p = *n;
1787                 *(p + 1) = *(n + 1);
1788                 n += MSDOS_NAME_LFN_BYTES_PER_CHAR;
1789             }
1790             else
1791             {
1792                 p [0] = fill;
1793                 p [1] = fill;
1794                 fill = 0xff;
1795             }
1796 #if MSDOS_FIND_PRINT
1797             printf ( "'%c''%c'", *p, *(p+1) );
1798 #endif
1799 
1800             switch (i)
1801             {
1802                 case 4:
1803                     p += 5;
1804                     break;
1805                 case 10:
1806                     p += 4;
1807                     break;
1808                 default:
1809                     p += 2;
1810                     break;
1811             }
1812         }
1813 #if MSDOS_FIND_PRINT
1814         printf ( "\n" );
1815 #endif
1816         *MSDOS_DIR_ENTRY_TYPE(entry) = lfn_entries - lfn_entry;
1817         if (lfn_entry == 0)
1818             *MSDOS_DIR_ENTRY_TYPE(entry) |= MSDOS_LAST_LONG_ENTRY;
1819         *MSDOS_DIR_ATTR(entry) |= MSDOS_ATTR_LFN;
1820 
1821         entry += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
1822     }
1823 
1824     /* Short file name entry */
1825     memcpy(entry, name_dir_entry, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
1826 
1827     length = (lfn_entries + 1) * MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
1828     bytes_written = fat_file_write(&fs_info->fat, fat_fd,
1829                                    empty_file_offset,
1830                                    length, fs_info->cl_buf);
1831     if (bytes_written == (ssize_t) length)
1832         return 0;
1833     else if (bytes_written == -1)
1834         return -1;
1835     else
1836         rtems_set_errno_and_return_minus_one(EIO);
1837 }
1838 
1839 int
1840 msdos_find_name_in_fat_file (
1841     rtems_filesystem_mount_table_entry_t *mt_entry,
1842     fat_file_fd_t                        *fat_fd,
1843     bool                                  create_node,
1844     const uint8_t                        *name_utf8,
1845     int                                   name_utf8_len,
1846     msdos_name_type_t                     name_type,
1847     fat_dir_pos_t                        *dir_pos,
1848     char                                 *name_dir_entry)
1849 {
1850     int                                retval                     = 0;
1851     msdos_fs_info_t                   *fs_info                    = mt_entry->fs_info;
1852     ssize_t                            name_len_for_save;
1853     ssize_t                            name_len_for_compare;
1854     uint32_t                           bts2rd                     = 0;
1855     uint32_t                           empty_file_offset          = 0;
1856     uint32_t                           empty_entry_count          = 0;
1857     unsigned int                       lfn_entries;
1858     rtems_dosfs_convert_control       *converter = fs_info->converter;
1859     void                              *buffer = converter->buffer.data;
1860     size_t                             buffer_size = converter->buffer.size;
1861 
1862     assert(name_utf8_len > 0);
1863 
1864     fat_dir_pos_init(dir_pos);
1865 
1866 
1867 
1868     if (FAT_FD_OF_ROOT_DIR(fat_fd) &&
1869         (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
1870         bts2rd = fat_fd->fat_file_size;
1871     else
1872         bts2rd = fs_info->fat.vol.bpc;
1873 
1874     switch ( name_type ) {
1875         case MSDOS_NAME_SHORT:
1876             name_len_for_compare = msdos_filename_utf8_to_short_name_for_compare (
1877                 converter,
1878                 name_utf8,
1879                 name_utf8_len,
1880                 buffer,
1881                 MSDOS_SHORT_NAME_LEN);
1882             if (name_len_for_compare > 0) {
1883                 lfn_entries = 0;
1884             }
1885             else
1886                 retval = -1;
1887         break;
1888         case MSDOS_NAME_LONG:
1889             name_len_for_save = msdos_filename_utf8_to_long_name_for_save (
1890                 converter,
1891                 name_utf8,
1892                 name_utf8_len,
1893                 buffer,
1894                 buffer_size);
1895             if (name_len_for_save > 0) {
1896                 lfn_entries = (name_len_for_save + MSDOS_LFN_ENTRY_SIZE - 1)
1897                     / MSDOS_LFN_ENTRY_SIZE;
1898                 name_len_for_compare = msdos_filename_utf8_to_long_name_for_compare (
1899                   converter,
1900                   name_utf8,
1901                   name_utf8_len,
1902                   buffer,
1903                   buffer_size);
1904                 if (0 >= name_len_for_compare) {
1905                     retval = -1;
1906                 }
1907             }
1908             else
1909                 retval = -1;
1910         break;
1911         default:
1912             errno = EINVAL;
1913             retval = -1;
1914         break;
1915     }
1916     if (retval == RC_OK) {
1917       /* See if the file/directory does already exist */
1918       retval = msdos_find_file_in_directory (
1919           buffer,
1920           name_len_for_compare,
1921           name_type,
1922           fs_info,
1923           fat_fd,
1924           bts2rd,
1925           create_node,
1926           lfn_entries,
1927           name_dir_entry,
1928           dir_pos,
1929           &empty_file_offset,
1930           &empty_entry_count);
1931     }
1932     /* Create a non-existing file/directory if requested */
1933     if (   retval == RC_OK
1934         && create_node) {
1935         switch (name_type) {
1936           case MSDOS_NAME_SHORT:
1937               name_len_for_save = msdos_filename_utf8_to_short_name_for_save (
1938                   converter,
1939                   name_utf8,
1940                   name_utf8_len,
1941                   buffer,
1942                   MSDOS_SHORT_NAME_LEN);
1943               if (name_len_for_save > 0 ) {
1944                   lfn_entries = 0;
1945               }
1946               else
1947                   retval = -1;
1948           break;
1949           case MSDOS_NAME_LONG:
1950               name_len_for_save = msdos_filename_utf8_to_long_name_for_save (
1951                   converter,
1952                   name_utf8,
1953                   name_utf8_len,
1954                   buffer,
1955                   buffer_size);
1956               if (name_len_for_save > 0) {
1957                   lfn_entries = (name_len_for_save + MSDOS_LFN_ENTRY_SIZE - 1)
1958                     / MSDOS_LFN_ENTRY_SIZE;
1959               }
1960               else
1961                   retval = -1;
1962           break;
1963           default:
1964               errno = EINVAL;
1965               retval = -1;
1966           break;
1967         }
1968 
1969         if (retval == RC_OK)
1970             retval = msdos_add_file (
1971                 buffer,
1972                 name_type,
1973                 fs_info,
1974                 fat_fd,
1975                 bts2rd,
1976                 lfn_entries,
1977                 name_dir_entry,
1978                 dir_pos,
1979                 empty_file_offset,
1980                 empty_entry_count
1981             );
1982     }
1983 
1984     return retval;
1985 }
1986 
1987 
1988 /* msdos_find_node_by_cluster_num_in_fat_file --
1989  *     Find node with specified number of cluster in fat-file.
1990  *
1991  * Note, not updated in the LFN change because it is only used
1992  *       for . and .. entries and these are always short.
1993  *
1994  * PARAMETERS:
1995  *     mt_entry  - mount table entry
1996  *     fat_fd    - fat-file descriptor
1997  *     cl4find   - number of cluster to find
1998  *     paux      - identify a node location on the disk -
1999  *                 cluster num and offset inside the cluster
2000  *     dir_entry - placeholder for found node
2001  *
2002  * RETURNS:
2003  *     RC_OK on success, or error code if error occurred
2004  *
2005  */
2006 int msdos_find_node_by_cluster_num_in_fat_file(
2007     rtems_filesystem_mount_table_entry_t *mt_entry,
2008     fat_file_fd_t                        *fat_fd,
2009     uint32_t                              cl4find,
2010     fat_dir_pos_t                        *dir_pos,
2011     char                                 *dir_entry
2012     )
2013 {
2014     int              rc = RC_OK;
2015     ssize_t          ret = 0;
2016     msdos_fs_info_t *fs_info = mt_entry->fs_info;
2017     uint32_t         bts2rd = 0;
2018     uint32_t         i = 0, j = 0;
2019 
2020     if (FAT_FD_OF_ROOT_DIR(fat_fd) &&
2021        (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
2022         bts2rd = fat_fd->fat_file_size;
2023     else
2024         bts2rd = fs_info->fat.vol.bpc;
2025 
2026     while ((ret = fat_file_read(&fs_info->fat, fat_fd, j * bts2rd, bts2rd,
2027                                   fs_info->cl_buf)) != FAT_EOF)
2028     {
2029         if ( ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE )
2030             rtems_set_errno_and_return_minus_one( EIO );
2031 
2032         assert(ret == bts2rd);
2033 
2034         for (i = 0; i < bts2rd; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
2035         {
2036             char* entry = (char*) fs_info->cl_buf + i;
2037 
2038             /* if this and all rest entries are empty - return not-found */
2039             if ((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
2040                 MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
2041                 return MSDOS_NAME_NOT_FOUND_ERR;
2042 
2043             /* if this entry is empty - skip it */
2044             if ((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
2045                 MSDOS_THIS_DIR_ENTRY_EMPTY)
2046                 continue;
2047 
2048             /* if get a non-empty entry - compare clusters num */
2049             if (MSDOS_EXTRACT_CLUSTER_NUM(entry) == cl4find)
2050             {
2051                 /* on success fill aux structure and copy all 32 bytes */
2052                 rc = fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM, j * bts2rd,
2053                                     &dir_pos->sname.cln);
2054                 if (rc != RC_OK)
2055                     return rc;
2056 
2057                 dir_pos->sname.ofs = i;
2058                 dir_pos->lname.cln = FAT_FILE_SHORT_NAME;
2059                 dir_pos->lname.ofs = FAT_FILE_SHORT_NAME;
2060 
2061                 memcpy(dir_entry, entry,
2062                        MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
2063                 return RC_OK;
2064             }
2065         }
2066         j++;
2067     }
2068     return MSDOS_NAME_NOT_FOUND_ERR;
2069 }
2070 
2071 int
2072 msdos_sync(rtems_libio_t *iop)
2073 {
2074     int                rc = RC_OK;
2075     msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info;
2076 
2077     msdos_fs_lock(fs_info);
2078 
2079     rc = fat_sync(&fs_info->fat);
2080 
2081     msdos_fs_unlock(fs_info);
2082     return rc;
2083 }