[PATCH 1/2] dosfs: Interface changes for supporting UTF-8 file names

Ralf Kirchner ralf.kirchner at embedded-brains.de
Fri Feb 1 09:41:58 UTC 2013


---
 cpukit/libfs/Makefile.am                           |    3 +-
 cpukit/libfs/src/dosfs/dosfs.h                     |  183 +++-
 cpukit/libfs/src/dosfs/msdos.h                     |   62 +-
 cpukit/libfs/src/dosfs/msdos_conv.c                |  581 ++++++++++---
 cpukit/libfs/src/dosfs/msdos_create.c              |    3 +-
 cpukit/libfs/src/dosfs/msdos_dir.c                 |  113 +--
 cpukit/libfs/src/dosfs/msdos_fsunmount.c           |    1 +
 cpukit/libfs/src/dosfs/msdos_init.c                |  194 ++++-
 cpukit/libfs/src/dosfs/msdos_initsupp.c            |    5 +-
 cpukit/libfs/src/dosfs/msdos_misc.c                |  910 ++++++++++++++------
 .../src/dosfs/msdos_string_handling_no_convert.c   |  166 ++++
 11 Dateien geändert, 1707 Zeilen hinzugefügt(+), 514 Zeilen entfernt(-)
 create mode 100644 cpukit/libfs/src/dosfs/msdos_string_handling_no_convert.c

diff --git a/cpukit/libfs/Makefile.am b/cpukit/libfs/Makefile.am
index 7535b5a..be58301 100644
--- a/cpukit/libfs/Makefile.am
+++ b/cpukit/libfs/Makefile.am
@@ -82,7 +82,8 @@ libdosfs_a_SOURCES += src/dosfs/msdos_create.c src/dosfs/msdos_dir.c \
     src/dosfs/msdos_mknod.c src/dosfs/msdos_node_type.c \
     src/dosfs/msdos_rmnod.c \
     src/dosfs/msdos_conv.c src/dosfs/msdos.h src/dosfs/msdos_format.c \
-    src/dosfs/dosfs.h src/dosfs/msdos_rename.c
+    src/dosfs/dosfs.h src/dosfs/msdos_rename.c \
+    src/dosfs/msdos_string_handling_no_convert.c
 endif
 
 # RFS
diff --git a/cpukit/libfs/src/dosfs/dosfs.h b/cpukit/libfs/src/dosfs/dosfs.h
index 211fc2d..190a21f 100644
--- a/cpukit/libfs/src/dosfs/dosfs.h
+++ b/cpukit/libfs/src/dosfs/dosfs.h
@@ -25,8 +25,189 @@
 extern "C" {
 #endif
 
+typedef struct {
+  uint8_t *file_name_scratch_buf;
+  void    *optional_args;
+} rtems_dosfs_string_context;
+
+/**
+ * @brief Converts from UTF-8 into a specific code page
+ * @param[in/out] string_arg An optional argument
+ * @param[in] str A well-formed UTF-8 string to be converted.
+ * @param[in] strsize The size of the string in bytes
+ * (inludes '\0' if any).
+ * @param[out] buffer The address the converted string will get copied to.
+ * @param[in/out] bufsize The size of the buffer in bytes respectively the
+ * number of bytes written to the buffer.
+ * @retval Pointer to the resulting string if successful. NULL and errno
+ * set if not successful.
+ */
+typedef char* (*rtems_dosfs_utf8_to_codepage) (
+  void* string_arg, const uint8_t *str, size_t strlen, char *buffer, size_t *bufsize);
+
+/**
+ * @brief Converts from a specific code page into UTF-8
+ * @param[in/out] string_arg An optional argument.
+ * @param[in] str A well-formed string in code page format.
+ * @param[in] strsize The size of the string in bytes
+ * (inludes '\0' if any).
+ * @param[out] buffer The address the converted string will get copied to.
+ * @param[in/out] bufsize The size of the buffer in bytes respectively the
+ * number of bytes written to the buffer.
+ * @retval Pointer to the resulting string if successful. NULL and errno
+ * set if not successful.
+ */
+typedef uint8_t* (*rtems_dosfs_codepage_to_utf8) (
+  void* string_arg, const char *str, size_t strlen, uint8_t *buffer, size_t *bufsize);
+
+/**
+ * @brief Converts from UTF-8 to UTF-16
+ * @param[in/out] string_arg An optional argument.
+ * @param[in] str A well-formed UTF-8 string to be converted.
+ * @param[in] strsize The size of the string in bytes
+ * (inludes '\0' if any).
+ * @param[out] buffer The address the converted string will get copied to
+ * @param[in/out] bufsize The size of the buffer in bytes respectively the
+ * number of bytes written to the buffer.
+ * @retval Pointer to the resulting string if successful. NULL and errno
+ * set if not successful.
+ */
+typedef uint16_t* (*rtems_dosfs_utf8_to_utf16) (
+  void* string_arg, const uint8_t *str, size_t strsize, uint16_t *buffer, size_t *bufsize);
+
+/**
+ * @brief Converts from UTF-16 to UTF-8.
+ * @param[in/out] string_arg An optional argument.
+ * @param[in] str A well-formed UTF-16 string to be converted.
+ * @param[in] strsize The size of the string in bytes
+ * (inludes '\0' if any).
+ * @param[out] buffer The address the converted string will get copied to.
+ * @param[in/out] bufsize The size of the buffer in bytes respectively the
+ * number of bytes written to the buffer
+ * @retval Pointer to the resulting string if successful. NULL and errno
+ * set if not successful.
+ */
+typedef uint8_t* (*rtems_dosfs_utf16_to_utf8) (
+  void* string_arg, const uint16_t *str, size_t strsize, uint8_t *buffer, size_t *bufsize);
+
+/* @brief Converts from UTF-8 to Normalized Form Canonical Decomposition.
+ *
+ *  Does canonical decomposition of the UTF-8 string and in addition
+ *  also converts upper case alphabetic characters to lower case characters
+ *
+ *  @param[in/out] string_arg An optional argument to the conversion method.
+ *  @param[in] str A well-formed UTF-8 string.
+ *  @param[in] strsize The size of the string in UTF-8 characters.
+ *  @param[out] buffer The address the converted string will get copied to.
+ *  @param[in/out] bufsize The size of the buffer in bytes respectively the
+ *   number of bytes written to the buffer.
+ *  @retval A pointer to the resulting string if successful. NULL and errno
+ *  set if not successful.
+ */
+typedef uint8_t* (*rtems_dosfs_utf8_normalize_and_fold) (
+  void* string_arg, const uint8_t* str, size_t strsize, uint8_t* buffer, size_t *bufsize);
+
+/**
+ * @brief FAT file system string handling.
+ */
+typedef struct {
+  /* @brief Converts from UTF-8 into a specific code page. */
+  rtems_dosfs_utf8_to_codepage        utf8_to_codepage;
+  /* @brief Converts from specific code page into UTF-8. */
+  rtems_dosfs_codepage_to_utf8        codepage_to_utf8;
+  /* @brief Converts from UTF-8 into UTF-16. */
+  rtems_dosfs_utf8_to_utf16           utf8_to_utf16;
+  /* @brief Converts from UTF-16 into UTF-8. */
+  rtems_dosfs_utf16_to_utf8           utf16_to_utf8;
+  /* @brief Normalizes and folds a UTF-8 string. */
+  rtems_dosfs_utf8_normalize_and_fold utf8_normalize_and_fold;
+} rtems_dosfs_string_handling;
+
+char* msdos_utf8_to_codepage_no_convert (
+  void* string_arg, const uint8_t *str, const size_t strsize, char *buffer, size_t *bufsize);
+
+uint8_t* msdos_codepage_to_utf8_no_convert (
+  void* string_arg, const char *str, const size_t strsize, uint8_t *buffer, size_t *bufsize);
+
+uint16_t* msdos_utf8_to_utf16_no_convert (
+  void* string_arg, const uint8_t *str, const size_t strsize, uint16_t *buffer, size_t *bufsize);
+
+uint8_t* msdos_utf16_to_utf8_no_convert (
+  void* string_arg, const uint16_t *str, const size_t strsize, uint8_t *buffer, size_t *buffsize);
+
+uint8_t* msdos_utf8_normalize_and_fold (
+  void* string_arg, const uint8_t* str, const size_t strsize, uint8_t* buffer, size_t *bufsize);
+
+/* forward declaration */
+typedef struct rtems_dosfs_string_options_s rtems_dosfs_string_options;
+
+/* @brief Cleanup handler for string options */
+typedef void (*rtems_dosfs_string_options_cleanup) (
+  rtems_dosfs_string_options* string_opts);
+
+/**
+ * @brief FAT file system mount options.
+ */
+typedef struct  rtems_dosfs_string_options_s {
+  /* @brief struct of pointers to string handling methods to be applied for
+   * file/directory names */
+  rtems_dosfs_string_handling         string_handling;
+
+  /* @brief Context for string handling*/
+  rtems_dosfs_string_context          string_handling_ctx;
+
+  /* @brief Cleanup handler for string options */
+  rtems_dosfs_string_options_cleanup  cleanup_handler;
+} rtems_dosfs_string_options;
+
+typedef struct {
+  rtems_dosfs_string_options    *string_opts;
+} rtems_dosfs_mount_options;
+
+/**
+ * @brief Initializes mount options.
+ *
+ * @param[in/out] mount_options The mount options to be initialized.
+ * @param[in] string_handling_arg A pointer to optional string handling
+ * argument.
+ * @param[in] utf8_to_codepage A function pointer to conversion method from
+ * UTF-8 to a code page.
+ * @param[in] codepage_to_utf8 A function pointer to conversion method from a
+ * code page to UTF-8.
+ * @param[in] utf8_to_utf16 A function pointer to conversion method from
+ * UTF-8 to UTF-16.
+ * @param[in] utf16_to_utf8 A function pointer to conversion method from
+ * UTF-16 to UTF-8.
+ * @param[in] utf8_normalize_and_fold A function pointer to conversion method
+ * which normalizes and folds a UTF-8 string.
+ *
+ * @retval 0 Successful operation.
+ * @retval -1 An error occurred.  The @c errno indicates the error.
+ * @see rtems_dosfs_initialize().
+ */
+int
+rtems_dosfs_mount_options_init (
+  rtems_dosfs_mount_options          *mount_options,
+  void                               *string_handling_arg,
+  rtems_dosfs_utf8_to_codepage        utf8_to_codepage,
+  rtems_dosfs_codepage_to_utf8        codepage_to_utf8,
+  rtems_dosfs_utf8_to_utf16           utf8_to_utf16,
+  rtems_dosfs_utf16_to_utf8           utf16_to_utf8,
+  rtems_dosfs_utf8_normalize_and_fold utf8_normalize_and_fold
+);
+
+/**
+ * @brief Initializes a FAT file system.
+ *
+ * @param[in] mt_entry The entry in the mount table.
+ * @param[in] mount_opts NULL or pointer to a rtems_dosfs_mount_options.
+ *
+ * @retval 0 Successful operation.
+ * @retval -1 An error occurred.  The @c errno indicates the error.
+ * @see rtems_dosfs_mount_options_init().
+ */
 int rtems_dosfs_initialize(rtems_filesystem_mount_table_entry_t *mt_entry,
-                           const void                           *data);
+                           const void                           *mount_opts);
 
 /**
  * @defgroup rtems_msdos_format DOSFS Support
diff --git a/cpukit/libfs/src/dosfs/msdos.h b/cpukit/libfs/src/dosfs/msdos.h
index 9465d26..2a41b47 100644
--- a/cpukit/libfs/src/dosfs/msdos.h
+++ b/cpukit/libfs/src/dosfs/msdos.h
@@ -20,6 +20,7 @@
 
 #include <rtems.h>
 #include <rtems/libio_.h>
+#include <rtems/dosfs.h>
 
 #include "fat.h"
 #include "fat_file.h"
@@ -67,6 +68,7 @@ typedef struct msdos_fs_info_s
                                                             * just placeholder
                                                             * for anything
                                                             */
+    rtems_dosfs_string_options       string_opts;         /* string handling options passed to mount() */
 } msdos_fs_info_t;
 
 /* a set of routines that handle the nodes which are directories */
@@ -183,6 +185,8 @@ typedef rtems_filesystem_node_types_t msdos_node_type_t;
 /*
  *  Macros for names parsing and formatting
  */
+#define MSDOS_NAME_MAX_UTF8_BYTES_PER_CHAR  4
+#define MSDOS_NAME_MIN_UTF8_BYTES_PER_CHAR  1
 
 #define MSDOS_SHORT_BASE_LEN             8  /* 8 characters */
 #define MSDOS_SHORT_EXT_LEN              3  /* 3 characters */
@@ -190,9 +194,18 @@ typedef rtems_filesystem_node_types_t msdos_node_type_t;
                                           MSDOS_SHORT_EXT_LEN) /* 11 chars */
 #define MSDOS_NAME_MAX_LNF_LEN           (255)
 #define MSDOS_NAME_MAX                   MSDOS_SHORT_NAME_LEN
+#define MSDOS_NAME_MAX_UTF8_SFN_BYTES    (MSDOS_NAME_MAX *\
+                                         MSDOS_NAME_MAX_UTF8_BYTES_PER_CHAR)
 #define MSDOS_NAME_MAX_WITH_DOT          (MSDOS_NAME_MAX + 1)
 #define MSDOS_NAME_MAX_LFN_WITH_DOT      (260)
 
+#define MSDOS_NAME_LFN_BYTES_PER_CHAR    (2)
+#define MSDOS_NAME_MAX_LFN_BYTES         (MSDOS_NAME_MAX_LFN_WITH_DOT *\
+                                          MSDOS_NAME_LFN_BYTES_PER_CHAR)
+#define MSDOS_NAME_MAX_UTF8_LFN_BYTES    (MSDOS_NAME_MAX_LFN_WITH_DOT *\
+                                          MSDOS_NAME_MAX_UTF8_BYTES_PER_CHAR)
+#define MSDOS_ENTRY_LFN_UTF8_BYTES       (MSDOS_LFN_LEN_PER_ENTRY *\
+                                          MSDOS_NAME_MAX_UTF8_BYTES_PER_CHAR)
 
 extern const char *const MSDOS_DOT_NAME;    /* ".", padded to MSDOS_NAME chars */
 extern const char *const MSDOS_DOTDOT_NAME; /* ".", padded to MSDOS_NAME chars */
@@ -213,6 +226,14 @@ typedef enum msdos_token_types_e
     MSDOS_INVALID_TOKEN
 } msdos_token_types_t;
 
+typedef enum
+{
+  MSDOS_STRING_TYPE_CODEPAGE,
+  MSDOS_STRING_TYPE_UTF8,
+
+  MSDOS_STRING_TYPE_NUMBER_OF
+} msdos_string_types;
+
 /* Others macros */
 #define MSDOS_RES_NT_VALUE     0x00
 #define MSDOS_INIT_DIR_SIZE    0x00
@@ -306,7 +327,8 @@ int msdos_initialize_support(
   rtems_filesystem_mount_table_entry_t    *temp_mt_entry,
   const rtems_filesystem_operations_table *op_table,
   const rtems_filesystem_file_handlers_r  *file_handlers,
-  const rtems_filesystem_file_handlers_r  *directory_handlers
+  const rtems_filesystem_file_handlers_r  *directory_handlers,
+  rtems_dosfs_string_options              *string_options
 );
 
 int msdos_file_close(rtems_libio_t *iop /* IN  */);
@@ -384,10 +406,42 @@ int msdos_get_name_node(
 
 int msdos_dir_info_remove(rtems_filesystem_location_info_t *pathloc);
 
-msdos_name_type_t msdos_long_to_short(const char *lfn, int lfn_len,
+msdos_name_type_t msdos_long_to_short(rtems_dosfs_string_options *string_opts,
+                                      const char *lfn, int lfn_len,
                                       char* sfn, int sfn_len);
 
-int msdos_filename_unix2dos(const char *un, int unlen, char *dn);
+int
+msdos_filename_utf8_to_short_name (
+    rtems_dosfs_string_options      *string_opts,
+    const uint8_t                   *utf8_name,
+    const size_t                     utf8_name_size,
+    void                            *short_name,
+    const size_t                     short_name_size,
+    const msdos_string_types         short_name_string_type);
+
+ssize_t
+msdos_filename_utf8_to_long_name_for_compare (
+    rtems_dosfs_string_options      *string_opts,
+    const uint8_t                   *utf8_name,
+    const size_t                     utf8_name_size,
+    uint8_t                         *long_name,
+    const size_t                     long_name_size);
+
+ssize_t
+msdos_filename_utf8_to_long_name_for_save (
+    rtems_dosfs_string_options      *string_opts,
+    const uint8_t                   *utf8_name,
+    const size_t                     utf8_name_size,
+    uint16_t                        *long_name,
+    const size_t                     long_name_size);
+
+ssize_t
+msdos_get_utf16_string_from_long_entry (
+  const char                 *entry,
+  uint16_t                   *entry_string_buf,
+  const size_t                buf_size,
+  bool                        is_first_entry
+);
 
 void msdos_date_unix2dos(
   unsigned int tsp, uint16_t *ddp,
@@ -427,7 +481,7 @@ int msdos_find_name_in_fat_file(
     rtems_filesystem_mount_table_entry_t *mt_entry,
     fat_file_fd_t                        *fat_fd,
     bool                                  create_node,
-    const char                           *name,
+    const uint8_t                        *name_utf8,
     int                                   name_len,
     msdos_name_type_t                     name_type,
     fat_dir_pos_t                        *dir_pos,
diff --git a/cpukit/libfs/src/dosfs/msdos_conv.c b/cpukit/libfs/src/dosfs/msdos_conv.c
index 7549c42..73f95f0 100644
--- a/cpukit/libfs/src/dosfs/msdos_conv.c
+++ b/cpukit/libfs/src/dosfs/msdos_conv.c
@@ -28,12 +28,29 @@
 #include "config.h"
 #endif
 
+#include <ctype.h>
 #include <rtems.h>
 #include "msdos.h"
 
 /* #define SECONDSPERDAY (24 * 60 * 60) */
 #define SECONDSPERDAY ((uint32_t) 86400)
 
+#define UTF8_MAX_CHAR_SIZE    4
+#define UTF8_NULL             0x00
+#define UTF8_NULL_SIZE        1
+#define UTF8_BLANK            0x20
+#define UTF8_BLANK_SIZE       1
+#define UTF8_FULL_STOP        0x2e
+#define UTF8_FULL_STOP_SIZE   1
+
+#define UTF16_MAX_CHAR_SIZE   4
+#define UTF16_NULL            0x0000
+#define UTF16_NULL_SIZE       2
+#define UTF16_BLANK           0x0020
+#define UTF16_BLANK_SIZE      2
+#define UTF16_FULL_STOP       0x002e
+#define UTF16_FULL_STOP_SIZE  2
+
 /*
  * Days in each month in a regular year.
  */
@@ -174,150 +191,450 @@ msdos_date_dos2unix(unsigned int dd, unsigned int dt)
 	return seconds + lastseconds;
 }
 
-static const uint8_t msdos_map[] = {
+
+static const uint8_t codepage_valid_char_map[] = {
     0,    0,    0,    0,    0,    0,    0,    0,    /* 00-07 */
     0,    0,    0,    0,    0,    0,    0,    0,    /* 08-0f */
     0,    0,    0,    0,    0,    0,    0,    0,    /* 10-17 */
     0,    0,    0,    0,    0,    0,    0,    0,    /* 18-1f */
-    0,    '!',  0,    '#',  '$',  '%',  '&',  '\'', /* 20-27 */
-    '(',  ')',  0,    '+',  0,    '-',  0,    0,    /* 28-2f */
-    '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',  /* 30-37 */
-    '8',  '9',  0,    0,    0,    0,    0,    0,    /* 38-3f */
-    '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',  /* 40-47 */
-    'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',  /* 48-4f */
-    'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  /* 50-57 */
-    'X',  'Y',  'Z',  0,    0,    0,    '^',  '_',  /* 58-5f */
-    '`',  'A',  'B',  'C',  'D',  'E',  'F',  'G',  /* 60-67 */
-    'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',  /* 68-6f */
-    'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  /* 70-77 */
-    'X',  'Y',  'Z',  '{',  0,    '}',  '~',  0,    /* 78-7f */
-    0,    0,    0,    0,    0,    0,    0,    0,    /* 80-87 */
-    0,    0,    0,    0,    0,    0,    0,    0,    /* 88-8f */
-    0,    0,    0,    0,    0,    0,    0,    0,    /* 90-97 */
-    0,    0,    0,    0,    0,    0,    0,    0,    /* 98-9f */
-    0,    0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* a0-a7 */
-    0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* a8-af */
-    0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* b0-b7 */
-    0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* b8-bf */
-    0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* c0-c7 */
-    0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* c8-cf */
-    0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, /* d0-d7 */
-    0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, /* d8-df */
-    0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* e0-e7 */
-    0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* e8-ef */
-    0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0xf6, /* f0-f7 */
-    0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0x98, /* f8-ff */
-#if OLD_TABLE
-/* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* 20 */ 0x00, 0x21, 0x00, 0x23, 0x24, 0x25, 0x26, 0x27, /*  !"#$%&' */
-/* 28 */ 0x28, 0x29, 0x00, 0x00, 0x00, 0x2D, 0x2E, 0x00, /* ()*+,-./ */
-/* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 01234567 */
-/* 38 */ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 89:;<=>? */
-/* 40 */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* @ABCDEFG */
-/* 48 */ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* HIJKLMNO */
-/* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* PQRSTUVW */
-/* 58 */ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x00, 0x5E, 0x5F, /* XYZ[\]^_ */
-/* 60 */ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* `abcdefg */
-/* 68 */ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* hijklmno */
-/* 70 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* pqrstuvw */
-/* 78 */ 0x58, 0x59, 0x5A, 0x5B, 0x7C, 0x00, 0x7E, 0x00, /* xyz{|}~  */
-/* 80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* 88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* 98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* A0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* A8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* B0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* B8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* C0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* C8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* D0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* D8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* E0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* E8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* F0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* F8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-#endif
+    0x20, 0x21, 0,    0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */
+    0x28, 0x29, 0,    0,    0,    0x2d,  0,    0,   /* 28-2f */
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */
+    0x38, 0x39, 0,    0,    0,    0,    0,    0,    /* 38-3f */
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48-4f */
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */
+    0x58, 0x59, 0x5a, 0,    0,    0,    0x5e, 0x5f, /* 58-5f */
+    0x60, 0,    0,    0,    0,    0,    0,    0,    /* 60-67 */
+    0,    0,    0,    0,    0,    0,    0,    0,    /* 68-6f */
+    0,    0,    0,    0,    0,    0,    0,    0,    /* 70-77 */
+    0,    0,    0,    0x7b, 0,    0x7d, 0x73, 0,    /* 78-7f */
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 80-87 */
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 88-8f */
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 90-97 */
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 98-9f */
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a0-a7 */
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* a8-af */
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b0-b7 */
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* b8-bf */
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* c0-c7 */
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* c8-cf */
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* d0-d7 */
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* d8-df */
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e0-e7 */
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* e8-ef */
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f0-f7 */
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff  /* f8-ff */
 };
-/*
- * Convert a unix filename to a DOS filename. Return -1 if wrong name is
- * supplied.
- */
-int
-msdos_filename_unix2dos(const char *un, int unlen, char *dn)
+
+typedef uint16_t (*msdos_get_valid_character_from_utf16) (
+  const uint16_t character);
+
+
+static uint16_t
+msdos_get_valid_utf16_filename_character (const uint16_t utf16_character)
 {
-	int i;
-	uint8_t c;
+  uint16_t retval   = 0x0000;
+  uint16_t tmp_char = 0x0000;
 
-	/*
-	 * Fill the dos filename string with blanks. These are DOS's pad
-	 * characters.
-	 */
-	for (i = 0; i <= 10; i++)
-		dn[i] = ' ';
+  if ( utf16_character <= 0x00ff ) {
+    switch (utf16_character)
+    {
+      case 0x002b: /* '+' */
+      case 0x002c: /* ',' */
+      case 0x002e: /* '.' */
+      case 0x003b: /* ';' */
+      case 0x003d: /* '=' */
+      case 0x005b: /* '[' */
+      case 0x005d: /* ']' */
+      case 0x0061: /* 'a' */
+      case 0x0062: /* 'b' */
+      case 0x0063: /* 'c' */
+      case 0x0064: /* 'd' */
+      case 0x0065: /* 'e' */
+      case 0x0066: /* 'f' */
+      case 0x0067: /* 'g' */
+      case 0x0068: /* 'h' */
+      case 0x0069: /* 'i' */
+      case 0x006a: /* 'j' */
+      case 0x006b: /* 'k' */
+      case 0x006c: /* 'l' */
+      case 0x006d: /* 'm' */
+      case 0x006e: /* 'n' */
+      case 0x006f: /* 'o' */
+      case 0x0070: /* 'p' */
+      case 0x0071: /* 'q' */
+      case 0x0072: /* 'r' */
+      case 0x0073: /* 's' */
+      case 0x0074: /* 't' */
+      case 0x0075: /* 'u' */
+      case 0x0076: /* 'v' */
+      case 0x0077: /* 'w' */
+      case 0x0078: /* 'x' */
+      case 0x0079: /* 'y' */
+      case 0x007a: /* 'z' */
+        retval = utf16_character;
+      break;
+      default:
+        tmp_char = codepage_valid_char_map[utf16_character];
+        retval   = 0x00ff & tmp_char;
+      break;
+    }
+  }
+  else
+    retval = utf16_character;
+
+  return retval;
+}
+
+typedef char (*msdos_get_valid_character_from_utf8) (
+  const uint8_t character);
+
+static char
+msdos_get_valid_codepage_filename_character (const uint8_t character)
+{
+  return codepage_valid_char_map[(unsigned int)character];
+}
+
+static char
+msdos_get_valid_utf8_filename_character_from_utf8 (const uint8_t character)
+{
+  return (char)character;
+}
+
+ssize_t
+msdos_filename_utf8_to_long_name_for_compare (
+    rtems_dosfs_string_options      *string_opts,
+    const uint8_t                   *utf8_name,
+    const size_t                     utf8_name_size,
+    uint8_t                         *long_name,
+    const size_t                     long_name_size)
+  {
+    ssize_t        returned_size = 0;
+    int            eno           = 0;
+    const uint8_t *name_ptr      = utf8_name;
+    size_t         name_size     = utf8_name_size;
+    size_t         dest_size     = long_name_size;
+    int            i;
+
+    /*
+     * Fill the UTF-8 filename string with NULLs. These are DOS's pad
+     * characters for long file names.
+     */
+    for (i = 0; i < long_name_size; ++i)
+      long_name[i] = UTF16_NULL;
+    /*
+     * The filenames "." and ".." are handled specially, since they
+     * don't follow dos filename rules.
+     */
+    if (    name_ptr[0] == UTF8_FULL_STOP
+         && name_size   == UTF8_FULL_STOP_SIZE) {
+      long_name[0] = UTF8_FULL_STOP;
+      returned_size    = UTF8_FULL_STOP_SIZE;
+    }
+    else if (    name_ptr[0] == UTF8_FULL_STOP
+              && name_ptr[1] == UTF8_FULL_STOP
+              && name_size   == ( 2 * UTF8_FULL_STOP_SIZE ) ) {
+      long_name[0] = UTF8_FULL_STOP;
+      long_name[1] = UTF8_FULL_STOP;
+      returned_size    = 2 * UTF8_FULL_STOP_SIZE;
+    }
+    else {
+      /*
+       * Remove any dots from the end of a file name.
+       */
+      for ( i = name_size - 1 - UTF8_FULL_STOP_SIZE;
+              name_size >= UTF8_FULL_STOP_SIZE
+           && utf8_name[i] == UTF8_FULL_STOP;) {
+        name_size -= UTF8_FULL_STOP_SIZE;
+        i         -= UTF8_FULL_STOP_SIZE;
+      }
+      if (NULL != string_opts->string_handling.utf8_normalize_and_fold (
+             string_opts->string_handling_ctx.optional_args,
+             utf8_name,
+             name_size,
+             long_name,
+             &dest_size) ) {
+        returned_size = (ssize_t)dest_size;
+      }
+      else
+        eno = EINVAL;
+    }
+
+    if ( eno != 0 ) {
+      errno = eno;
+      return -1;
+    }
+
+    return returned_size;
+  }
+
+ssize_t
+msdos_filename_utf8_to_long_name_for_save (
+    rtems_dosfs_string_options      *string_opts,
+    const uint8_t                   *utf8_name,
+    const size_t                     utf8_name_size,
+    uint16_t                        *long_name,
+    const size_t                     long_name_size)
+  {
+    ssize_t   returned_size = 0;
+    int       eno           = 0;
+    size_t    name_size     = utf8_name_size;
+    size_t    name_size_tmp = long_name_size / MSDOS_NAME_LFN_BYTES_PER_CHAR;
+    int       i;
+    uint16_t  c;
+
+    /*
+     * Fill the UTF-16 filename string with NULLs. These are DOS's pad
+     * characters for long file names.
+     */
+    for (i = 0; i < name_size_tmp; ++i)
+      long_name[i] = UTF16_NULL;
+
+    name_size_tmp = long_name_size;
+    /*
+     * The filenames "." and ".." are handled specially, since they
+     * don't follow dos filename rules.
+     */
+    if (    utf8_name[0] == UTF8_FULL_STOP
+         && name_size   == UTF8_FULL_STOP_SIZE) {
+      long_name[0]  = UTF16_FULL_STOP;
+      returned_size = UTF16_FULL_STOP_SIZE;
+    }
+    else if (    utf8_name[0] == UTF8_FULL_STOP
+              && utf8_name[1] == UTF8_FULL_STOP
+              && name_size   == ( 2 * UTF8_FULL_STOP_SIZE ) ) {
+      long_name[0]  = UTF16_FULL_STOP;
+      long_name[1]  = UTF16_FULL_STOP;
+      returned_size = 2 * UTF16_FULL_STOP_SIZE;
+    }
+    else {
+      /*
+       * Remove any dots from the end of a file name.
+       */
+      for ( i = name_size - 1 - UTF8_FULL_STOP_SIZE;
+              name_size >= UTF8_FULL_STOP_SIZE
+           && utf8_name[i] == UTF8_FULL_STOP;) {
+        name_size -= UTF8_FULL_STOP_SIZE;
+        i         -= UTF8_FULL_STOP_SIZE;
+      }
+      /*
+       * Finally convert from UTF-8 to UTF-16
+       */
+      if (NULL != string_opts->string_handling.utf8_to_utf16 (
+        string_opts->string_handling_ctx.optional_args,
+        utf8_name,
+        name_size,
+        &long_name[0],
+        &name_size_tmp) ) {
+        name_size = name_size_tmp;
+      }
+      else
+        eno = EINVAL;
+
+      if ( eno == 0 )
+      {
+        /*
+         * Validate the characters and assign them to the UTF-16 file name
+         */
+        for ( i = 0;
+                 name_size
+              && (c = msdos_get_valid_utf16_filename_character ( long_name[i]) );
+              ++i ) {
+          long_name[i]   = c;
+          returned_size += MSDOS_NAME_LFN_BYTES_PER_CHAR;
+          name_size     -= MSDOS_NAME_LFN_BYTES_PER_CHAR;
+        }
+        if ( name_size == UTF16_NULL_SIZE && c == UTF16_NULL ) {
+          long_name[i]   = c;
+          returned_size += MSDOS_NAME_LFN_BYTES_PER_CHAR;
+          name_size     -= MSDOS_NAME_LFN_BYTES_PER_CHAR;
+        }
+        else if ( name_size != 0 )
+          eno = EINVAL;
+      }
+    }
+
+    if ( eno != 0 ) {
+      errno = eno;
+      return -1;
+    }
+
+    return returned_size;
+  }
+
+int
+msdos_filename_utf8_to_short_name (
+    rtems_dosfs_string_options      *string_opts,
+    const uint8_t                   *utf8_name,
+    const size_t                     utf8_name_size,
+    void                            *short_name,
+    const size_t                     short_name_size,
+    const msdos_string_types         short_name_string_type)
+{
+  const size_t                          CP_NAME_TMP_BUF_SIZE          = MSDOS_SHORT_NAME_LEN +1;
+  int                                   returned_size                 = 0;
+  int                                   eno                           = 0;
+  unsigned int                          i;
+  char                                  c;
+  char                                  name_to_format_buf[CP_NAME_TMP_BUF_SIZE];
+  const uint8_t                        *name_ptr                = utf8_name;
+  size_t                                name_size               = utf8_name_size;
+  size_t                                name_size_tmp           = MSDOS_NAME_MAX_UTF8_LFN_BYTES;
+  msdos_get_valid_character_from_utf8   get_valid_character     = NULL;
+  char                                 *dest_ptr                = (char*)short_name;
+  char                                 *cp_name_ptr             = NULL;
 
-	/*
-	 * The filenames "." and ".." are handled specially, since they
-	 * don't follow dos filename rules.
-	 */
-	if (un[0] == '.' && unlen == 1) {
-		dn[0] = '.';
-		return 0;
-	}
-	if (un[0] == '.' && un[1] == '.' && unlen == 2) {
-		dn[0] = '.';
-		dn[1] = '.';
-		return 0;
-	}
 
   /*
-   * Remove any dots from the start of a file name.
+   * The filenames "." and ".." are handled specially, since they
+   * don't follow dos filename rules.
    */
-	while (unlen && (*un == '.')) {
-		un++;
-		unlen--;
-	}
+  if (    name_ptr[0] == UTF8_FULL_STOP
+       && name_size   == UTF8_FULL_STOP_SIZE) {
+    dest_ptr[0]    = '.';
+    returned_size = sizeof ( '.' );
+  }
+  else if (    name_ptr[0] == UTF8_FULL_STOP
+            && name_ptr[1] == UTF8_FULL_STOP
+            && name_size   == ( 2 * UTF8_FULL_STOP_SIZE ) ) {
+    dest_ptr[0]    = '.';
+    dest_ptr[1]    = '.';
+    returned_size = 2 * sizeof ( '.' );
+  }
+  else {
+    /*
+     * Remove any dots from the start of a file name.
+     */
+    while (   name_size >= UTF8_FULL_STOP_SIZE
+           && *name_ptr == UTF8_FULL_STOP) {
+      name_ptr  += UTF8_FULL_STOP_SIZE;
+      name_size -= UTF8_FULL_STOP_SIZE;
+    }
 
-	/*
-	 * Copy the unix filename into the dos filename string upto the end
-	 * of string, a '.', or 8 characters. Whichever happens first stops
-	 * us. This forms the name portion of the dos filename. Fold to
-	 * upper case.
-	 */
-	for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) {
-    if (msdos_map[c] == 0)
-      break;
-		dn[i] = msdos_map[c];
-		un++;
-		unlen--;
-	}
+    /*
+     * Normalize the name and convert to lower case
+     */
+    if (NULL != string_opts->string_handling.utf8_normalize_and_fold (
+      string_opts->string_handling_ctx.optional_args,
+      name_ptr,
+      name_size,
+      &string_opts->string_handling_ctx.file_name_scratch_buf[0]/*&utf8_name_tmp[0]*/,
+      &name_size_tmp) ) {
+      name_ptr  = &string_opts->string_handling_ctx.file_name_scratch_buf[0]/*&utf8_name_tmp[0]*/;
+      name_size = name_size_tmp;
+      switch (short_name_string_type)
+      {
+        case MSDOS_STRING_TYPE_CODEPAGE:
+          /*
+           * Finally convert from UTF-8 to codepage
+           */
+          name_size_tmp = sizeof ( name_to_format_buf );
+          cp_name_ptr   = string_opts->string_handling.utf8_to_codepage (
+            string_opts->string_handling_ctx.optional_args,
+            name_ptr,
+            name_size,
+            &name_to_format_buf[0],
+            &name_size_tmp);
+          if ( cp_name_ptr == NULL && name_size_tmp == sizeof ( name_to_format_buf ) ) {
+            cp_name_ptr = &name_to_format_buf[0];
+            errno = 0;
+          }
+          if ( NULL != cp_name_ptr ) {
+            name_ptr            = ( typeof( name_ptr ) )(&name_to_format_buf[0]);
+            name_size           = name_size_tmp;
+            get_valid_character = msdos_get_valid_codepage_filename_character;
+            for (i = 0; i < name_size; ++i)
+              name_to_format_buf[i] = toupper ( (int)(name_to_format_buf[i]) );
+          }
+          else
+            eno = EINVAL;
+        break;
+        case MSDOS_STRING_TYPE_UTF8:
+          get_valid_character = msdos_get_valid_utf8_filename_character_from_utf8;
+        break;
+        case MSDOS_STRING_TYPE_NUMBER_OF:
+        default:
+          eno = EINVAL;
+        break;
+      }
+      if ( eno == 0 ) {
+        /*
+         * Validate the characters and assign them to the codepage file name
+         */
+        if ( name_size > 0 ) {
+          /*
+           * The first character needs some special treatment
+           */
+          if ( 0x20 == *name_ptr )
+            eno = EINVAL;
+          else if ( 0xE5 == *name_ptr )
+            dest_ptr[0] = 0x05;
+          else if (0 != (c = get_valid_character( *name_ptr ) ) )
+            dest_ptr[0] = c;
+          else
+            eno = EINVAL;
+          if ( eno == 0 ) {
+            ++name_ptr;
+            ++returned_size;
+            --name_size;
+            /*
+             * Validate and assign all other characters of the name part
+             */
+            for (i = 1; i <= 7 && name_size && (c = get_valid_character ( *name_ptr ) ) && c != '.'; ++i) {
+              dest_ptr[i] = c;
+              ++name_ptr;
+              ++returned_size;
+              --name_size;
+            }
+            if ( c == 0 && name_size > 0)
+              eno = EINVAL;
+            /*
+             * Strip any further characters up to a '.' or the end of the
+             * string.
+             */
+            while (name_size && (c = get_valid_character ( *name_ptr ) ) ) {
+              name_ptr++;
+              name_size--;
+              /* Make sure we've skipped over the dot before stopping. */
+              if (c == '.')
+                break;
+            }
+            if ( c == 0 && name_size > 0)
+              eno = EINVAL;
+            /*
+             * Copy in the extension part of the name, if any.
+             */
+            for (i = 8; i <= 10 && name_size && (c = get_valid_character ( *name_ptr) ); i++) {
+              dest_ptr[i] = c;
+              ++name_ptr;
+              ++returned_size;
+              name_size--;
+            }
+            if ( c == 0 && name_size > 0)
+              eno = EINVAL;
+            /*
+             * Fill up with blanks. These are DOS's pad characters.
+             */
+            if ( eno == 0 ) {
+              for ( i = returned_size; i < short_name_size; ++i ) {
+                dest_ptr[i] = ' ';
+                ++returned_size;
+              }
+            }
+          }
+        }
+      }
+    }
+    else
+      eno = errno;
 
-	/*
-	 * Strip any further characters up to a '.' or the end of the
-	 * string.
-	 */
-	while (unlen && (c = *un)) {
-		un++;
-		unlen--;
-		/* Make sure we've skipped over the dot before stopping. */
-		if (c == '.')
-			break;
-	}
+  }
 
-	/*
-	 * Copy in the extension part of the name, if any. Force to upper
-	 * case. Note that the extension is allowed to contain '.'s.
-	 * Filenames in this form are probably inaccessable under dos.
-	 */
-	for (i = 8; i <= 10 && unlen && (c = *un); i++) {
-    if (msdos_map[c] == 0)
-      break;
-    dn[i] = msdos_map[c];
-		un++;
-		unlen--;
-	}
-	return 0;
+  if ( eno != 0 ) {
+    errno = eno;
+    return -1;
+  }
+
+  return returned_size;
 }
diff --git a/cpukit/libfs/src/dosfs/msdos_create.c b/cpukit/libfs/src/dosfs/msdos_create.c
index 886dd40..2e813b7 100644
--- a/cpukit/libfs/src/dosfs/msdos_create.c
+++ b/cpukit/libfs/src/dosfs/msdos_create.c
@@ -90,7 +90,8 @@ msdos_creat_node(const rtems_filesystem_location_info_t  *parent_loc,
         rtems_set_errno_and_return_minus_one(ENAMETOOLONG);
     }
 
-    name_type = msdos_long_to_short (name, name_len,
+    name_type = msdos_long_to_short (&fs_info->string_opts,
+                                     name, name_len,
                                      MSDOS_DIR_NAME(short_node),
                                      MSDOS_NAME_MAX);
     if (name_type == MSDOS_NAME_INVALID) {
diff --git a/cpukit/libfs/src/dosfs/msdos_dir.c b/cpukit/libfs/src/dosfs/msdos_dir.c
index fd9ca40..f3dc42a 100644
--- a/cpukit/libfs/src/dosfs/msdos_dir.c
+++ b/cpukit/libfs/src/dosfs/msdos_dir.c
@@ -24,6 +24,7 @@
 #include <rtems/libio_.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <assert.h>
 
 #include <dirent.h>
 
@@ -130,9 +131,13 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
     int                rc = RC_OK;
     rtems_status_code  sc = RTEMS_SUCCESSFUL;
     msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info;
+    const rtems_dosfs_string_handling *string_handlers = &fs_info->string_opts.string_handling;
     fat_file_fd_t     *fat_fd = iop->pathinfo.node_access;
     fat_file_fd_t     *tmp_fat_fd = NULL;
     struct dirent      tmp_dirent;
+    size_t             tmp_lfn_len;
+    uint16_t          *lfn_buf = (uint16_t*)(&fs_info->string_opts.string_handling_ctx.file_name_scratch_buf[0]);
+    char              *sfn_buf = (char*)(&fs_info->string_opts.string_handling_ctx.file_name_scratch_buf[0]);
     uint32_t           start = 0;
     ssize_t            ret = 0;
     uint32_t           cmpltd = 0;
@@ -142,6 +147,8 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
     uint32_t           lfn_start = FAT_FILE_SHORT_NAME;
     uint8_t            lfn_checksum = 0;
     int                lfn_entries = 0;
+    size_t             string_size = sizeof(tmp_dirent.d_name);
+    bool               is_first_entry;
 
     /*
      * cast start and count - protect against using sizes that are not exact
@@ -167,7 +174,7 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
     if (sc != RTEMS_SUCCESSFUL)
         rtems_set_errno_and_return_minus_one(EIO);
 
-    while (count > 0)
+    while (count > 0 && cmpltd >= 0)
     {
         /*
          * fat-file is already opened by open call, so read it
@@ -183,7 +190,7 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
             rtems_set_errno_and_return_minus_one(EIO);
         }
 
-        for (i = 0; i < ret; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
+        for (i = 0; i < ret && cmpltd >= 0; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
         {
             char* entry = (char*) fs_info->cl_buf + i;
 
@@ -213,15 +220,14 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
             if ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
                 MSDOS_ATTR_LFN)
             {
-                int   o;
-                char* p;
-                int   q;
+                int offset_lfn;
 
                 /*
                  * Is this is the first entry of a LFN ?
                  */
                 if (lfn_start == FAT_FILE_SHORT_NAME)
                 {
+                    is_first_entry = true;
                     /*
                      * The first entry must have the last long entry flag set.
                      */
@@ -241,9 +247,12 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
                      */
                     lfn_entries = (*MSDOS_DIR_ENTRY_TYPE(entry) &
                                    MSDOS_LAST_LONG_ENTRY_MASK);
+                    tmp_lfn_len = 0;
                     lfn_checksum = *MSDOS_DIR_LFN_CHECKSUM(entry);
                     memset (tmp_dirent.d_name, 0, sizeof(tmp_dirent.d_name));
                 }
+                else
+                    is_first_entry = false;
 
                 /*
                  * If the entry number or the check sum do not match
@@ -262,7 +271,9 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
                 /*
                  * Extract the file name into the directory entry. The data is
                  * stored in UNICODE characters (16bit). No translation is
-                 * currently supported.
+                 * done for the possibly partial entry.
+                 * Once all entries have been assembled to a UTF-16 file name,
+                 * this file name will get converted to UTF-8.
                  *
                  * The DOS maximum length is 255 characters without the
                  * trailing nul character. We need to range check the length to
@@ -270,32 +281,13 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
                  */
 
                 lfn_entries--;
-                p = entry + 1;
-                o = lfn_entries * MSDOS_LFN_LEN_PER_ENTRY;
-
-                for (q = 0; q < MSDOS_LFN_LEN_PER_ENTRY; q++)
-                {
-                    if (o >= (sizeof(tmp_dirent.d_name) - 1))
-                        break;
-
-                    tmp_dirent.d_name[o++] = *p;
-
-                    if (*p == '\0')
-                        break;
-
-                    switch (q)
-                    {
-                        case 4:
-                            p += 5;
-                            break;
-                        case 10:
-                            p += 4;
-                            break;
-                        default:
-                            p += 2;
-                            break;
-                    }
-                }
+                offset_lfn = lfn_entries * MSDOS_LFN_LEN_PER_ENTRY;
+                tmp_lfn_len += msdos_get_utf16_string_from_long_entry (
+                  entry,
+                  &lfn_buf[offset_lfn],
+                  MSDOS_NAME_MAX_LFN_BYTES - offset_lfn,
+                  is_first_entry
+                );
             }
             else
             {
@@ -344,9 +336,10 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
                 tmp_dirent.d_ino = tmp_fat_fd->ino;
 
                 /*
-                 * If a long file name check if the correct number of
-                 * entries have been found and if the checksum is correct.
-                 * If not return the short file name.
+                 * If a long file name check if the correct number of entries
+                 * have been found and if the checksum is correct and if it is
+                 * convertable to utf8 string.  If not return the short file
+                 * name.
                  */
                 if (lfn_start != FAT_FILE_SHORT_NAME)
                 {
@@ -359,6 +352,18 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
 
                     if (lfn_entries || (lfn_checksum != cs))
                         lfn_start = FAT_FILE_SHORT_NAME;
+
+                    if (NULL != string_handlers->utf16_to_utf8(
+                                  fs_info->string_opts.string_handling_ctx.optional_args,
+                                  lfn_buf,
+                                  tmp_lfn_len,
+                                  (uint8_t*)(&tmp_dirent.d_name[0]),
+                                  &string_size)) {
+                      tmp_dirent.d_namlen = string_size;
+                    }
+                    else
+                      lfn_start = FAT_FILE_SHORT_NAME;
+
                 }
 
                 if (lfn_start == FAT_FILE_SHORT_NAME)
@@ -368,25 +373,33 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
                      * to 0..8 + 1dot + 0..3 format
                      */
                     tmp_dirent.d_namlen = msdos_format_dirent_with_dot(
-                        tmp_dirent.d_name, entry); /* src text */
-                }
-                else
-                {
-                    tmp_dirent.d_namlen = strlen(tmp_dirent.d_name);
+                        sfn_buf, entry); /* src text */
+                    if (NULL != fs_info->string_opts.string_handling.codepage_to_utf8 (
+                        fs_info->string_opts.string_handling_ctx.optional_args,
+                        sfn_buf,
+                        tmp_dirent.d_namlen,
+                        (uint8_t*)(&tmp_dirent.d_name[0]),
+                        &string_size ) ) {
+                        tmp_dirent.d_namlen = string_size;
+                    }
+                    else
+                        cmpltd = -1;
                 }
 
-                memcpy(buffer + cmpltd, &tmp_dirent, sizeof(struct dirent));
+                if ( cmpltd >= 0 ) {
+                    memcpy(buffer + cmpltd, &tmp_dirent, sizeof(struct dirent));
 
-                iop->offset = iop->offset + sizeof(struct dirent);
-                cmpltd += (sizeof(struct dirent));
-                count -= (sizeof(struct dirent));
+                    iop->offset = iop->offset + sizeof(struct dirent);
+                    cmpltd += (sizeof(struct dirent));
+                    count -= (sizeof(struct dirent));
 
-                /* inode number extracted, close fat-file */
-                rc = fat_file_close(&fs_info->fat, tmp_fat_fd);
-                if (rc != RC_OK)
-                {
-                    rtems_semaphore_release(fs_info->vol_sema);
-                    return rc;
+                    /* inode number extracted, close fat-file */
+                    rc = fat_file_close(&fs_info->fat, tmp_fat_fd);
+                    if (rc != RC_OK)
+                    {
+                        rtems_semaphore_release(fs_info->vol_sema);
+                        return rc;
+                    }
                 }
             }
 
diff --git a/cpukit/libfs/src/dosfs/msdos_fsunmount.c b/cpukit/libfs/src/dosfs/msdos_fsunmount.c
index 90a8073..f8fd52f 100644
--- a/cpukit/libfs/src/dosfs/msdos_fsunmount.c
+++ b/cpukit/libfs/src/dosfs/msdos_fsunmount.c
@@ -54,6 +54,7 @@ msdos_shut_down(rtems_filesystem_mount_table_entry_t *temp_mt_entry)
     fat_shutdown_drive(&fs_info->fat);
 
     rtems_semaphore_delete(fs_info->vol_sema);
+    fs_info->string_opts.cleanup_handler ( &fs_info->string_opts );
     free(fs_info->cl_buf);
     free(temp_mt_entry->fs_info);
 }
diff --git a/cpukit/libfs/src/dosfs/msdos_init.c b/cpukit/libfs/src/dosfs/msdos_init.c
index eb46141..96ea7fc 100644
--- a/cpukit/libfs/src/dosfs/msdos_init.c
+++ b/cpukit/libfs/src/dosfs/msdos_init.c
@@ -10,7 +10,7 @@
  *  Author: Eugeny S. Mints <Eugeny.Mints at oktet.ru>
  *
  *  Modifications to support reference counting in the file system are
- *  Copyright (c) 2012 embedded brains GmbH.
+ *  Copyright (c) 2012, 2013 embedded brains GmbH.
  *
  *  The license and distribution terms for this file may be
  *  found in the file LICENSE in this distribution or at
@@ -21,61 +21,142 @@
 #include "config.h"
 #endif
 
+#include <assert.h>
 #include <rtems/libio_.h>
 #include "dosfs.h"
 #include "msdos.h"
 
-static int msdos_clone_node_info(rtems_filesystem_location_info_t *loc)
+static int msdos_clone_node_info( rtems_filesystem_location_info_t *loc )
 {
-    fat_file_fd_t *fat_fd = loc->node_access;
+  fat_file_fd_t *fat_fd = loc->node_access;
 
-    return fat_file_reopen(fat_fd);
+
+  return fat_file_reopen( fat_fd );
 }
 
-const rtems_filesystem_operations_table  msdos_ops = {
-  .lock_h         =  msdos_lock,
-  .unlock_h       =  msdos_unlock,
-  .eval_path_h    =  msdos_eval_path,
-  .link_h         =  rtems_filesystem_default_link,
+const rtems_filesystem_operations_table msdos_ops = {
+  .lock_h            = msdos_lock,
+  .unlock_h          = msdos_unlock,
+  .eval_path_h       = msdos_eval_path,
+  .link_h            = rtems_filesystem_default_link,
   .are_nodes_equal_h = rtems_filesystem_default_are_nodes_equal,
-  .node_type_h    =  msdos_node_type,
-  .mknod_h        =  msdos_mknod,
-  .rmnod_h        =  msdos_rmnod,
-  .fchmod_h       =  rtems_filesystem_default_fchmod,
-  .chown_h        =  rtems_filesystem_default_chown,
-  .clonenod_h     =  msdos_clone_node_info,
-  .freenod_h      =  msdos_free_node_info,
-  .mount_h        =  rtems_filesystem_default_mount,
-  .fsmount_me_h   =  rtems_dosfs_initialize,
-  .unmount_h      =  rtems_filesystem_default_unmount,
-  .fsunmount_me_h =  msdos_shut_down,
-  .utime_h        =  rtems_filesystem_default_utime,
-  .symlink_h      =  rtems_filesystem_default_symlink,
-  .readlink_h     =  rtems_filesystem_default_readlink,
-  .rename_h       =  msdos_rename,
-  .statvfs_h      =  rtems_filesystem_default_statvfs
+  .node_type_h       = msdos_node_type,
+  .mknod_h           = msdos_mknod,
+  .rmnod_h           = msdos_rmnod,
+  .fchmod_h          = rtems_filesystem_default_fchmod,
+  .chown_h           = rtems_filesystem_default_chown,
+  .clonenod_h        = msdos_clone_node_info,
+  .freenod_h         = msdos_free_node_info,
+  .mount_h           = rtems_filesystem_default_mount,
+  .fsmount_me_h      = rtems_dosfs_initialize,
+  .unmount_h         = rtems_filesystem_default_unmount,
+  .fsunmount_me_h    = msdos_shut_down,
+  .utime_h           = rtems_filesystem_default_utime,
+  .symlink_h         = rtems_filesystem_default_symlink,
+  .readlink_h        = rtems_filesystem_default_readlink,
+  .rename_h          = msdos_rename,
+  .statvfs_h         = rtems_filesystem_default_statvfs
 };
 
-void msdos_lock(const rtems_filesystem_mount_table_entry_t *mt_entry)
+void msdos_lock( const rtems_filesystem_mount_table_entry_t *mt_entry )
 {
-  msdos_fs_info_t *fs_info = mt_entry->fs_info;
-  rtems_status_code sc = rtems_semaphore_obtain(
+  msdos_fs_info_t  *fs_info = mt_entry->fs_info;
+  rtems_status_code sc      = rtems_semaphore_obtain(
     fs_info->vol_sema,
     RTEMS_WAIT,
     RTEMS_NO_TIMEOUT
-  );
-  if (sc != RTEMS_SUCCESSFUL) {
-    rtems_fatal_error_occurred(0xdeadbeef);
+    );
+
+
+  if ( sc != RTEMS_SUCCESSFUL ) {
+    rtems_fatal_error_occurred( 0xdeadbeef );
+  }
+}
+
+void msdos_unlock( const rtems_filesystem_mount_table_entry_t *mt_entry )
+{
+  msdos_fs_info_t  *fs_info = mt_entry->fs_info;
+  rtems_status_code sc      = rtems_semaphore_release( fs_info->vol_sema );
+
+
+  if ( sc != RTEMS_SUCCESSFUL ) {
+    rtems_fatal_error_occurred( 0xdeadbeef );
   }
 }
 
-void msdos_unlock(const rtems_filesystem_mount_table_entry_t *mt_entry)
+static void msdos_string_options_cleanup(
+  rtems_dosfs_string_options *string_opts )
 {
-  msdos_fs_info_t *fs_info = mt_entry->fs_info;
-  rtems_status_code sc = rtems_semaphore_release(fs_info->vol_sema);
-  if (sc != RTEMS_SUCCESSFUL) {
-    rtems_fatal_error_occurred(0xdeadbeef);
+  if ( string_opts != NULL ) {
+    if ( string_opts->string_handling_ctx.file_name_scratch_buf != NULL ) {
+      free( string_opts->string_handling_ctx.file_name_scratch_buf );
+      string_opts->string_handling_ctx.file_name_scratch_buf = NULL;
+    }
+  }
+}
+
+static int rtems_dosfs_string_options_init(
+  rtems_dosfs_string_options               *opts,
+  void                                     *string_handling_arg,
+  const rtems_dosfs_utf8_to_codepage        utf8_to_codepage,
+  const rtems_dosfs_codepage_to_utf8        codepage_to_utf8,
+  const rtems_dosfs_utf8_to_utf16           utf8_to_utf16,
+  const rtems_dosfs_utf16_to_utf8           utf16_to_utf8,
+  const rtems_dosfs_utf8_normalize_and_fold utf8_normalize_and_fold )
+{
+  int eno = 0;
+
+
+  assert( opts != NULL );
+  assert( utf8_to_codepage != NULL );
+  assert( codepage_to_utf8 != NULL );
+  assert( utf8_to_utf16 != NULL );
+  assert( utf16_to_utf8 != NULL );
+  assert( utf8_normalize_and_fold != NULL );
+
+  opts->string_handling_ctx.file_name_scratch_buf = calloc(
+    MSDOS_NAME_MAX_UTF8_LFN_BYTES, sizeof( uint8_t ) );
+
+  if ( opts->string_handling_ctx.file_name_scratch_buf != NULL ) {
+    opts->string_handling_ctx.optional_args       = NULL;
+    opts->cleanup_handler                         =
+      (rtems_dosfs_string_options_cleanup) msdos_string_options_cleanup;
+    opts->string_handling.utf8_to_codepage        = utf8_to_codepage;
+    opts->string_handling.codepage_to_utf8        = codepage_to_utf8;
+    opts->string_handling.utf8_to_utf16           = utf8_to_utf16;
+    opts->string_handling.utf16_to_utf8           = utf16_to_utf8;
+    opts->string_handling.utf8_normalize_and_fold = utf8_normalize_and_fold;
+    opts->string_handling_ctx.optional_args       = string_handling_arg;
+  } else
+    eno = ENOMEM;
+
+  if ( eno != 0 ) {
+    errno = eno;
+    return -1;
   }
+
+  return 0;
+}
+
+int rtems_dosfs_mount_options_init(
+  rtems_dosfs_mount_options          *mount_options,
+  void                               *string_handling_arg,
+  rtems_dosfs_utf8_to_codepage        utf8_to_codepage,
+  rtems_dosfs_codepage_to_utf8        codepage_to_utf8,
+  rtems_dosfs_utf8_to_utf16           utf8_to_utf16,
+  rtems_dosfs_utf16_to_utf8           utf16_to_utf8,
+  rtems_dosfs_utf8_normalize_and_fold utf8_normalize_and_fold )
+{
+  assert( mount_options != NULL );
+
+  return rtems_dosfs_string_options_init(
+    mount_options->string_opts,
+    string_handling_arg,
+    utf8_to_codepage,
+    codepage_to_utf8,
+    utf8_to_utf16,
+    utf16_to_utf8,
+    utf8_normalize_and_fold );
 }
 
 /* msdos_initialize --
@@ -89,14 +170,37 @@ void msdos_unlock(const rtems_filesystem_mount_table_entry_t *mt_entry)
  *     RC_OK on success, or -1 if error occured (errno set apropriately).
  *
  */
-int rtems_dosfs_initialize(rtems_filesystem_mount_table_entry_t *mt_entry,
-                           const void                           *data)
+int rtems_dosfs_initialize( rtems_filesystem_mount_table_entry_t *mt_entry,
+  const void                                                     *mount_options )
 {
-    int rc;
+  int                        rc;
+  rtems_dosfs_string_options string_opts;
 
-    rc = msdos_initialize_support(mt_entry,
-                                  &msdos_ops,
-                                  &msdos_file_handlers,
-                                  &msdos_dir_handlers);
-    return rc;
-}
+
+  if ( NULL == mount_options ) {
+    rc = rtems_dosfs_string_options_init(
+      &string_opts,
+      NULL,
+      msdos_utf8_to_codepage_no_convert,
+      msdos_codepage_to_utf8_no_convert,
+      msdos_utf8_to_utf16_no_convert,
+      msdos_utf16_to_utf8_no_convert,
+      msdos_utf8_normalize_and_fold );
+
+    if ( rc == 0 ) {
+      rc = msdos_initialize_support( mt_entry,
+                                     &msdos_ops,
+                                     &msdos_file_handlers,
+                                     &msdos_dir_handlers,
+                                     &string_opts );
+    }
+  } else {
+    rc = msdos_initialize_support( mt_entry,
+                                   &msdos_ops,
+                                   &msdos_file_handlers,
+                                   &msdos_dir_handlers,
+                                   ( (rtems_dosfs_mount_options *) mount_options )->string_opts );
+  }
+
+  return rc;
+}
\ No newline at end of file
diff --git a/cpukit/libfs/src/dosfs/msdos_initsupp.c b/cpukit/libfs/src/dosfs/msdos_initsupp.c
index 0844186..b03cc42 100644
--- a/cpukit/libfs/src/dosfs/msdos_initsupp.c
+++ b/cpukit/libfs/src/dosfs/msdos_initsupp.c
@@ -52,7 +52,8 @@ msdos_initialize_support(
     rtems_filesystem_mount_table_entry_t    *temp_mt_entry,
     const rtems_filesystem_operations_table *op_table,
     const rtems_filesystem_file_handlers_r  *file_handlers,
-    const rtems_filesystem_file_handlers_r  *directory_handlers
+    const rtems_filesystem_file_handlers_r  *directory_handlers,
+    rtems_dosfs_string_options              *string_opts
     )
 {
     int                rc = RC_OK;
@@ -68,6 +69,8 @@ msdos_initialize_support(
 
     temp_mt_entry->fs_info = fs_info;
 
+    fs_info->string_opts = *string_opts;
+
     rc = fat_init_volume_info(&fs_info->fat, temp_mt_entry->dev);
     if (rc != RC_OK)
     {
diff --git a/cpukit/libfs/src/dosfs/msdos_misc.c b/cpukit/libfs/src/dosfs/msdos_misc.c
index a1979b1..bb6cd70 100644
--- a/cpukit/libfs/src/dosfs/msdos_misc.c
+++ b/cpukit/libfs/src/dosfs/msdos_misc.c
@@ -26,6 +26,8 @@
 #include <unistd.h>
 #include <string.h>
 #include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
 #include <rtems/libio_.h>
 
 #include "fat.h"
@@ -69,7 +71,7 @@ msdos_is_valid_name_char(const char ch)
         return MSDOS_NAME_LONG;
 
     if ((ch == '.') || isalnum((unsigned char)ch) ||
-        (strchr("$%'-_@~`!(){}^#&", ch) != NULL))
+        (strchr("$%'-_@~`!(){}^#&", ch) != NULL) || (unsigned char) ch > 127)
         return MSDOS_NAME_SHORT;
 
     return MSDOS_NAME_INVALID;
@@ -192,10 +194,15 @@ msdos_name_type(const char *name, int name_len)
  */
 #define MSDOS_L2S_PRINT 0
 msdos_name_type_t
-msdos_long_to_short(const char *lfn, int lfn_len, char* sfn, int sfn_len)
+msdos_long_to_short(rtems_dosfs_string_options      *string_opts,
+                    const char                      *lfn,
+                    int                              lfn_len,
+                    char                            *sfn,
+                    int                              sfn_len)
 {
     msdos_name_type_t type;
     int               i;
+    ssize_t           short_filename_length = sfn_len;
 
     /*
      * Fill with spaces. This is how a short directory entry is padded.
@@ -252,7 +259,16 @@ msdos_long_to_short(const char *lfn, int lfn_len, char* sfn, int sfn_len)
         return MSDOS_NAME_INVALID;
     }
 
-    msdos_filename_unix2dos (lfn, lfn_len, sfn);
+    short_filename_length = msdos_filename_utf8_to_short_name (
+        string_opts,
+        (const uint8_t*)lfn,
+        lfn_len,
+        sfn,
+        short_filename_length,
+        MSDOS_STRING_TYPE_CODEPAGE);
+    if (short_filename_length < 0 ) {
+        type = MSDOS_NAME_INVALID;
+    }
 
 #if MSDOS_L2S_PRINT
     printf ("MSDOS_L2S: TYPE:%d lfn:'%s' SFN:'%s'\n", type, lfn, sfn);
@@ -292,13 +308,15 @@ msdos_find_name(
 
     memset(node_entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
 
-    name_type = msdos_long_to_short (name,
-                                     name_len,
-                                     MSDOS_DIR_NAME(node_entry),
-                                     MSDOS_NAME_MAX);
+    name_type = msdos_long_to_short (
+        &fs_info->string_opts,
+        name,
+        name_len,
+        MSDOS_DIR_NAME(node_entry),
+        MSDOS_NAME_MAX);
 
     /*
-     * find the node which correspondes to the name in the directory pointed by
+     * find the node which corresponds to the name in the directory pointed by
      * 'parent_loc'
      */
     rc = msdos_get_name_node(parent_loc, false, name, name_len, name_type,
@@ -426,7 +444,7 @@ msdos_get_name_node(
 
     /* find name in fat-file which corresponds to the directory */
     rc = msdos_find_name_in_fat_file(parent_loc->mt_entry, fat_fd,
-                                     create_node, name, name_len, name_type,
+                                     create_node, (const uint8_t*)name, name_len, name_type,
                                      dir_pos, name_dir_entry);
     if ((rc != RC_OK) && (rc != MSDOS_NAME_NOT_FOUND_ERR))
         return rc;
@@ -542,8 +560,10 @@ msdos_get_dotdot_dir_info_cluster_num_and_offset(
 
     /* find "." node in opened directory */
     memset(dot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
-    msdos_long_to_short(".", 1, dot_node, MSDOS_SHORT_NAME_LEN);
-    rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, false, ".", 1,
+    msdos_long_to_short(
+        &fs_info->string_opts,
+        ".", 1, dot_node, MSDOS_SHORT_NAME_LEN);
+    rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, false, (const uint8_t*)".", 1,
                                      MSDOS_NAME_SHORT, dir_pos, dot_node);
 
     if (rc != RC_OK)
@@ -554,8 +574,10 @@ msdos_get_dotdot_dir_info_cluster_num_and_offset(
 
     /* find ".." node in opened directory */
     memset(dotdot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
-    msdos_long_to_short("..", 2, dotdot_node, MSDOS_SHORT_NAME_LEN);
-    rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, false, "..", 2,
+    msdos_long_to_short(
+        &fs_info->string_opts,
+        "..", 2, dotdot_node, MSDOS_SHORT_NAME_LEN);
+    rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, false, (const uint8_t*)"..", 2,
                                      MSDOS_NAME_SHORT, dir_pos,
                                      dotdot_node);
 
@@ -914,118 +936,270 @@ msdos_dir_is_empty(
     return RC_OK;
 }
 
-/* msdos_create_name_in_fat_file --
- *     This routine creates an entry in the fat file for the file name
- *     provided by the user. The directory entry passed is the short
- *     file name and is added as it. If the file name is long a long
- *     file name set of entries is added.
- *
- *     Scan the directory for the file and if not found add the new entry.
- *     When scanning remember the offset in the file where the directory
- *     entry can be added.
- *
- * PARAMETERS:
- *     mt_entry       - mount table entry
- *     fat_fd         - fat-file descriptor
- *     name           - NULL or name to find
- *     paux           - identify a node location on the disk -
- *                      number of cluster and offset inside the cluster
- *     name_dir_entry - node to create/placeholder for found node
- *
- * RETURNS:
- *     RC_OK on success, or error code if error occured (errno set
- *     appropriately)
- *
- */
 #define MSDOS_FIND_PRINT 0
-int msdos_find_name_in_fat_file(
-    rtems_filesystem_mount_table_entry_t *mt_entry,
+static int
+msdos_on_entry_found (
+    msdos_fs_info_t                      *fs_info,
     fat_file_fd_t                        *fat_fd,
-    bool                                  create_node,
-    const char                           *name,
-    int                                   name_len,
-    msdos_name_type_t                     name_type,
+    const uint32_t                        bts2rd,
+    char                                 *name_dir_entry,
+    char                                 *entry,
     fat_dir_pos_t                        *dir_pos,
-    char                                 *name_dir_entry
-                                )
+    uint32_t                             *dir_offset,
+    const uint32_t                        dir_entry,
+    const fat_pos_t                      *lfn_start
+)
 {
-    ssize_t          ret = 0;
-    msdos_fs_info_t *fs_info = mt_entry->fs_info;
-    uint32_t         dir_offset = 0;
-    uint32_t         dir_entry = 0;
-    uint32_t         bts2rd = 0;
-    fat_pos_t        lfn_start;
-    bool             lfn_matched = false;
-    uint8_t          lfn_checksum = 0;
-    int              lfn_entries;
-    int              lfn_entry = 0;
-    uint32_t         empty_space_offset = 0;
-    uint32_t         empty_space_entry = 0;
-    uint32_t         empty_space_count = 0;
-    bool             empty_space_found = false;
-    uint32_t         entries_per_block;
-    bool             read_cluster = false;
-
-    assert(name_len > 0);
+    int rc = RC_OK;
+#if MSDOS_FIND_PRINT
+    printf ("MSFS:[9.3] SNF found\n");
+#endif
+    /*
+     * We get the entry we looked for - fill the position
+     * structure and the 32 bytes of the short entry
+     */
+    rc = fat_file_ioctl(&fs_info->fat,
+                        fat_fd,
+                        F_CLU_NUM,
+                        *dir_offset * bts2rd,
+                        &dir_pos->sname.cln);
+    if (rc == RC_OK) {
+        dir_pos->sname.ofs = dir_entry;
+
+        if (lfn_start->cln != FAT_FILE_SHORT_NAME)
+        {
+            rc = fat_file_ioctl (&fs_info->fat,
+                                 fat_fd,
+                                 F_CLU_NUM,
+                                 lfn_start->cln * bts2rd,
+                                 &lfn_start->cln);
+        }
+        if ( rc == RC_OK ) {
+            dir_pos->lname.cln = lfn_start->cln;
+            dir_pos->lname.ofs = lfn_start->ofs;
 
-    fat_dir_pos_init(dir_pos);
+            memcpy(name_dir_entry, entry,
+                   MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
+        }
+    }
 
-    lfn_start.cln = lfn_start.ofs = FAT_FILE_SHORT_NAME;
+    return rc;
+}
 
-    /*
-     * Set the number of short entries needed to store the LFN. If the name
-     * is short still check for possible long entries with the short name.
-     *
-     * In PR1491 we need to have a LFN for a short file name entry. To
-     * test this make this test always fail, ie add "0 &&".
-     */
-    if (create_node && (name_type == MSDOS_NAME_SHORT))
-      lfn_entries = 0;
-    else
-      lfn_entries =
-        ((name_len - 1) + MSDOS_LFN_LEN_PER_ENTRY) / MSDOS_LFN_LEN_PER_ENTRY;
+ssize_t
+msdos_get_utf16_string_from_long_entry (
+    const char                 *entry,
+    uint16_t                   *entry_string_buf,
+    const size_t                buf_size,
+    bool                        is_first_entry
+)
+{
+    ssize_t chars_in_entry;
+
+    if (buf_size >= MSDOS_LFN_LEN_PER_ENTRY * MSDOS_NAME_LFN_BYTES_PER_CHAR) {
+        memcpy (&entry_string_buf[0],  &entry[1],  10 );
+        memcpy (&entry_string_buf[5],  &entry[14], 12 );
+        memcpy (&entry_string_buf[11], &entry[28],  4 );
+
+        if (is_first_entry) {
+            for (chars_in_entry = 0;
+                 (    entry_string_buf[chars_in_entry] != 0x0000
+                  && chars_in_entry < MSDOS_LFN_LEN_PER_ENTRY );
+                  ++chars_in_entry) {
+                ;
+            }
+        }
+        else
+            chars_in_entry = MSDOS_LFN_LEN_PER_ENTRY;
 
-    if (FAT_FD_OF_ROOT_DIR(fat_fd) &&
-        (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
-        bts2rd = fat_fd->fat_file_size;
+        return chars_in_entry * MSDOS_NAME_LFN_BYTES_PER_CHAR;
+    }
     else
-        bts2rd = fs_info->fat.vol.bpc;
+        return ENOMEM;
+}
 
-    entries_per_block = bts2rd / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
+static ssize_t
+msdos_get_long_name_entry_as_utf8 (
+    rtems_dosfs_string_options *string_opts,
+    const char                 *entry,
+    const bool                  is_first_entry,
+    uint8_t                    *entry_utf8_buf,
+    const size_t                buf_size)
+{
+    ssize_t      retval       = 0;
+    size_t       bytes_in_buf = buf_size;
+    uint16_t     entry_string[MSDOS_LFN_LEN_PER_ENTRY];
+
+    retval = msdos_get_utf16_string_from_long_entry (
+        entry,
+        entry_string,
+        sizeof (entry_string),
+        is_first_entry
+    );
+
+    if (retval >= 0) {
+        bytes_in_buf = retval;
+        if (NULL != string_opts->string_handling.utf16_to_utf8 (
+            string_opts->string_handling_ctx.optional_args,
+            &entry_string[0],
+            bytes_in_buf,
+            &entry_utf8_buf[0],
+            &bytes_in_buf ) ) {
+            retval = bytes_in_buf;
+        }
+        else
+            retval = -1;
+    }
 
-#if MSDOS_FIND_PRINT
-    printf ("MSFS:[1] nt:%d, cn:%i ebp:%li bts2rd:%li lfne:%d nl:%i n:%s\n",
-            name_type, create_node, entries_per_block, bts2rd,
-            lfn_entries, name_len, name);
+    return retval;
+}
+
+static int
+msdos_compare_entry_against_long_filename (
+    rtems_dosfs_string_options *string_opts,
+    const char                 *entry,
+    const uint8_t              *filename,
+    const size_t                filename_size,
+    const bool                  is_first_entry,
+    bool                       *is_matching)
+{
+    int          retval = 0;
+    ssize_t      bytes_read;
+    size_t       bytes_in_entry;
+    uint8_t      entry_utf8           [MSDOS_ENTRY_LFN_UTF8_BYTES];
+    uint8_t      entry_utf8_normalized[( MSDOS_LFN_LEN_PER_ENTRY + 1 ) * MSDOS_NAME_MAX_UTF8_BYTES_PER_CHAR];
+    size_t       bytes_in_entry_normalized = sizeof ( entry_utf8_normalized );
+
+    bytes_read = msdos_get_long_name_entry_as_utf8 (
+        string_opts,
+        entry,
+        is_first_entry,
+        &entry_utf8[0],
+        sizeof (entry_utf8));
+    if (bytes_read >= 0) {
+        bytes_in_entry = bytes_read;
+        if (bytes_in_entry > filename_size)
+            *is_matching = false;
+        else {
+          if (NULL != string_opts->string_handling.utf8_normalize_and_fold (
+              string_opts->string_handling_ctx.optional_args,
+              &entry_utf8[0],
+              bytes_in_entry,
+              &entry_utf8_normalized[0],
+              &bytes_in_entry_normalized)) {
+#if MSDOS_FIND_PRINT > 1
+                printf ( "MSFS:[6] entry_utf8_normalized:%s"
+                         "name:%s\n",
+                         entry_utf8_normalized,
+                         filename );
 #endif
+              if (   is_first_entry
+                  && bytes_in_entry_normalized != filename_size)
+                  *is_matching = false;
+              else if (0 == memcmp ( &entry_utf8_normalized[0],
+                                      &filename[0],
+                                      bytes_in_entry_normalized)) {
+                  *is_matching = true;
+              }
+              else
+                  *is_matching = false;
+          }
+          else
+              retval = -1;
+        }
+    }
+    else
+        retval = -1;
+
+    return retval;
+}
+
+
+static ssize_t
+msdos_file_name_to_short_entry_string (
+    const uint8_t *filename,
+    const size_t   filename_size,
+    uint8_t       *entry_buf,
+    const size_t   entry_buf_size)
+{
+    size_t number_of_bytes = MIN (filename_size, MSDOS_SHORT_NAME_LEN);
+
+    assert (entry_buf != NULL);
+    assert (entry_buf_size >= MSDOS_SHORT_NAME_LEN);
+
+    memcpy (entry_buf, filename, number_of_bytes);
+    if (number_of_bytes < MSDOS_SHORT_NAME_LEN)
+      memset (&entry_buf[number_of_bytes], ' ', MSDOS_SHORT_NAME_LEN - number_of_bytes);
+
+    return MSDOS_SHORT_NAME_LEN;
+}
+
+static int
+msdos_find_file_in_directory (
+    const uint8_t                        *filename_converted,
+    const size_t                          filename_converted_size,
+    const msdos_name_type_t               name_type,
+    msdos_fs_info_t                      *fs_info,
+    fat_file_fd_t                        *fat_fd,
+    const uint32_t                        bts2rd,
+    const bool                            create_node,
+    const unsigned int                    fat_entries,
+    char                                 *name_dir_entry,
+    fat_dir_pos_t                        *dir_pos,
+    uint32_t                             *dir_offset,
+    uint32_t                             *empty_space_offset,
+    uint32_t                             *empty_space_entry,
+    uint32_t                             *empty_space_count)
+{
+    int               rc                = RC_OK;
+    ssize_t           bytes_read;
+    uint32_t          dir_entry;
+    fat_pos_t         lfn_start;
+    uint8_t           lfn_checksum      = 0;
+    bool              lfn_matched       = false;
+    bool              empty_space_found = false;
+    uint32_t          entries_per_block = bts2rd / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
+    int               lfn_entry         = 0;
+    uint8_t           entry_utf8           [MSDOS_ENTRY_LFN_UTF8_BYTES];
+    uint8_t           entry_utf8_normalized[MSDOS_ENTRY_LFN_UTF8_BYTES];
+    uint8_t          *filename_entry_format = &entry_utf8[0];
+    size_t            filename_entry_format_size;
+    size_t            bytes_in_entry;
+    size_t            bytes_in_entry_normalized;
+    bool              filename_matched  = false;
+
     /*
      * Scan the directory seeing if the file is present. While
      * doing this see if a suitable location can be found to
      * create the entry if the name is not found.
      */
-    while ((ret = fat_file_read(&fs_info->fat, fat_fd, (dir_offset * bts2rd),
-                                bts2rd, fs_info->cl_buf)) != FAT_EOF)
+
+    lfn_start.cln = lfn_start.ofs = FAT_FILE_SHORT_NAME;
+
+    while (   (bytes_read = fat_file_read (&fs_info->fat, fat_fd, (*dir_offset * bts2rd),
+                                             bts2rd, fs_info->cl_buf)) != FAT_EOF
+           && rc == RC_OK)
     {
         bool remainder_empty = false;
 #if MSDOS_FIND_PRINT
-        printf ("MSFS:[2] dir_offset:%li\n", dir_offset);
+        printf ("MSFS:[2] dir_offset:%li\n", *dir_offset);
 #endif
 
-        if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
+        if (bytes_read < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
             rtems_set_errno_and_return_minus_one(EIO);
 
-        assert(ret == bts2rd);
+        assert(bytes_read == bts2rd);
 
         /* have to look at the DIR_NAME as "raw" 8-bit data */
         for (dir_entry = 0;
-             dir_entry < bts2rd;
+             dir_entry < bts2rd && rc == RC_OK && (! filename_matched);
              dir_entry += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
         {
             char* entry = (char*) fs_info->cl_buf + dir_entry;
 
             /*
              * See if the entry is empty or the remainder of the directory is
-             * empty ? Localise to make the code read better.
+             * empty ? Localize to make the code read better.
              */
             bool entry_empty = (*MSDOS_DIR_ENTRY_TYPE(entry) ==
                                 MSDOS_THIS_DIR_ENTRY_EMPTY);
@@ -1033,7 +1207,7 @@ int msdos_find_name_in_fat_file(
                                MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY);
 #if MSDOS_FIND_PRINT
             printf ("MSFS:[3] re:%i ee:%i do:%li de:%li(%ld)\n",
-                    remainder_empty, entry_empty, dir_offset,
+                    remainder_empty, entry_empty, *dir_offset,
                     dir_entry, (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE));
 #endif
             /*
@@ -1043,10 +1217,10 @@ int msdos_find_name_in_fat_file(
              * we are currently not inside an empty series of entries. It
              * is a count of empty entries.
              */
-            if (empty_space_count == 0)
+            if (*empty_space_count == 0)
             {
-                empty_space_entry = dir_entry;
-                empty_space_offset = dir_offset;
+                *empty_space_entry = dir_entry;
+                *empty_space_offset = *dir_offset;
             }
 
             if (remainder_empty)
@@ -1059,7 +1233,7 @@ int msdos_find_name_in_fat_file(
                  * directory - return name-not-found
                  */
                 if (!create_node)
-                    return MSDOS_NAME_NOT_FOUND_ERR;
+                    rc = MSDOS_NAME_NOT_FOUND_ERR;
 
                 /*
                  * Lets go and write the directory entries. If we have not found
@@ -1067,13 +1241,14 @@ int msdos_find_name_in_fat_file(
                  * we may have already found that are just before this entry. If more
                  * are needed FAT_EOF is returned by the read and we extend the file.
                  */
-                if (!empty_space_found)
+                if (   !empty_space_found
+                    && rc == RC_OK )
                 {
-                  empty_space_count +=
+                    *empty_space_count +=
                     entries_per_block - (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
-                  empty_space_found = true;
+                    empty_space_found = true;
 #if MSDOS_FIND_PRINT
-                  printf ("MSFS:[3.2] esf:%i esc%i\n", empty_space_found, empty_space_count);
+                    printf ( "MSFS:[3.2] esf:%i esc%"PRIu32"\n", empty_space_found, *empty_space_count );
 #endif
                 }
                 break;
@@ -1082,17 +1257,17 @@ int msdos_find_name_in_fat_file(
             {
                 if (create_node)
                 {
-                  /*
-                   * Remainder is not empty so is this entry empty ?
-                   */
-                  empty_space_count++;
+                    /*
+                     * Remainder is not empty so is this entry empty ?
+                     */
+                    (*empty_space_count)++;
 
-                  if (empty_space_count == (lfn_entries + 1))
-                    empty_space_found = true;
+                    if (*empty_space_count == (fat_entries + 1))
+                        empty_space_found = true;
                 }
 #if MSDOS_FIND_PRINT
                 printf ("MSFS:[4.1] esc:%li esf:%i\n",
-                        empty_space_count, empty_space_found);
+                        *empty_space_count, empty_space_found);
 #endif
             }
             else
@@ -1105,8 +1280,8 @@ int msdos_find_name_in_fat_file(
                  */
                 if (create_node && !empty_space_found)
                 {
-                    empty_space_entry = 0;
-                    empty_space_count = 0;
+                    *empty_space_entry = 0;
+                    *empty_space_count = 0;
                 }
 
                 /*
@@ -1116,9 +1291,7 @@ int msdos_find_name_in_fat_file(
                 if ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
                     MSDOS_ATTR_LFN)
                 {
-                    char* p;
                     int   o;
-                    int   i;
 #if MSDOS_FIND_PRINT
                     printf ("MSFS:[4.2] lfn:%c entry:%i checksum:%i\n",
                             lfn_start.cln == FAT_FILE_SHORT_NAME ? 'f' : 't',
@@ -1148,21 +1321,21 @@ int msdos_find_name_in_fat_file(
                          * characters in the entry so this is check further
                          * on when the characters are checked.
                          */
-                        if (lfn_entries != (*MSDOS_DIR_ENTRY_TYPE(entry) &
+                        if (fat_entries != (*MSDOS_DIR_ENTRY_TYPE(entry) &
                                             MSDOS_LAST_LONG_ENTRY_MASK))
                             continue;
 
                         /*
                          * Get the checksum of the short entry.
                          */
-                        lfn_start.cln = dir_offset;
+                        lfn_start.cln = *dir_offset;
                         lfn_start.ofs = dir_entry;
-                        lfn_entry = lfn_entries;
+                        lfn_entry = fat_entries;
                         lfn_checksum = *MSDOS_DIR_LFN_CHECKSUM(entry);
 
 #if MSDOS_FIND_PRINT
                         printf ("MSFS:[4.3] lfn_checksum:%i\n",
-                                *MSDOS_DIR_LFN_CHECKSUM(entry));
+                                lfn_checksum);
 #endif
                     }
 
@@ -1182,60 +1355,25 @@ int msdos_find_name_in_fat_file(
                         lfn_start.cln = FAT_FILE_SHORT_NAME;
                         continue;
                     }
-
-                    lfn_entry--;
-                    o = lfn_entry * MSDOS_LFN_LEN_PER_ENTRY;
-                    p = entry + 1;
-
 #if MSDOS_FIND_PRINT
                     printf ("MSFS:[5] lfne:%i\n", lfn_entry);
 #endif
-                    for (i = 0; i < MSDOS_LFN_LEN_PER_ENTRY; i++)
-                    {
-#if MSDOS_FIND_PRINT > 1
-                        printf ("MSFS:[6] o:%i i:%i *p:%c(%02x) name[o + i]:%c(%02x)\n",
-                                o, i, *p, *p, name[o + i], name[o + i]);
-#endif
-                        if (*p == '\0')
-                        {
-                            /*
-                             * If this is the first entry, ie the last part of the
-                             * long file name and the length does not match then
-                             * the file names do not match.
-                             */
-                            if (((lfn_entry + 1) == lfn_entries) &&
-                                ((o + i) != name_len))
-                                lfn_start.cln = FAT_FILE_SHORT_NAME;
-                            break;
-                        }
+                    lfn_entry--;
+                    o = lfn_entry * MSDOS_LFN_LEN_PER_ENTRY;
 
-                        if (((o + i) >= name_len) || (*p != name[o + i]))
-                        {
-                            lfn_start.cln = FAT_FILE_SHORT_NAME;
-                            break;
-                        }
+                    rc = msdos_compare_entry_against_long_filename (
+                        &fs_info->string_opts,
+                        entry,
+                        &filename_converted[o],
+                        filename_converted_size - o,
+                        (lfn_entry + 1) == fat_entries,
+                        &lfn_matched);
 
-                        switch (i)
-                        {
-                            case 4:
-                                p += 5;
-                                break;
-                            case 10:
-                                p += 4;
-                                break;
-                            default:
-                                p += 2;
-                                break;
-                        }
+                    if (rc == 0
+                        && (! lfn_matched)) {
+                        lfn_start.cln = FAT_FILE_SHORT_NAME;
                     }
-
-                    lfn_matched = ((lfn_entry == 0) &&
-                                   (lfn_start.cln != FAT_FILE_SHORT_NAME));
-
-#if MSDOS_FIND_PRINT
-                    printf ("MSFS:[8.1] lfn_matched:%i\n", lfn_matched);
-#endif
-                }
+                  }
                 else
                 {
 #if MSDOS_FIND_PRINT
@@ -1260,80 +1398,133 @@ int msdos_find_name_in_fat_file(
 
                         if (lfn_entry || (lfn_checksum != cs))
                             lfn_matched = false;
+                        else {
+                            filename_matched = true;
+                            rc = msdos_on_entry_found (
+                                fs_info,
+                                fat_fd,
+                                bts2rd,
+                                name_dir_entry,
+                                entry,
+                                dir_pos,
+                                dir_offset,
+                                dir_entry,
+                                &lfn_start
+                            );
+                        }
+
 #if MSDOS_FIND_PRINT
                         printf ("MSFS:[9.2] checksum, lfn_matched:%i, lfn_entry:%i, lfn_checksum:%02x/%02x\n",
                                 lfn_matched, lfn_entry, lfn_checksum, cs);
 #endif
-                    }
-
-                    /*
-                     * If the long file names matched or the file name is
-                     * short and they match then we have the entry. We will not
-                     * match a long file name against a short file name because
-                     * a long file name that generates a matching short file
-                     * name is not a long file name.
-                     */
-                    if (lfn_matched ||
-                        ((name_type == MSDOS_NAME_SHORT) &&
-                         (lfn_start.cln == FAT_FILE_SHORT_NAME) &&
-                         (memcmp(MSDOS_DIR_NAME(entry),
-                                 MSDOS_DIR_NAME(name_dir_entry),
-                                 MSDOS_SHORT_NAME_LEN) == 0)))
-                    {
-#if MSDOS_FIND_PRINT
-                        printf ("MSFS:[9.3] SNF found\n");
-#endif
-                        /*
-                         * We get the entry we looked for - fill the position
-                         * structure and the 32 bytes of the short entry
-                         */
-                        int rc = fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM,
-                                                dir_offset * bts2rd,
-                                                &dir_pos->sname.cln);
-                        if (rc != RC_OK)
-                            return rc;
-
-                        dir_pos->sname.ofs = dir_entry;
-
-                        if (lfn_start.cln != FAT_FILE_SHORT_NAME)
-                        {
-                          rc = fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM,
-                                              lfn_start.cln * bts2rd,
-                                              &lfn_start.cln);
-                          if (rc != RC_OK)
-                            return rc;
+                    } else {
+                        bytes_in_entry = MSDOS_SHORT_NAME_LEN;
+                        bytes_in_entry_normalized = sizeof (entry_utf8_normalized);
+                        if (NULL != fs_info->string_opts.string_handling.codepage_to_utf8 (
+                            fs_info->string_opts.string_handling_ctx.optional_args,
+                            MSDOS_DIR_NAME (entry),
+                            (size_t)MSDOS_SHORT_NAME_LEN,
+                            &entry_utf8[0],
+                            &bytes_in_entry)) {
+                            if (NULL != fs_info->string_opts.string_handling.utf8_normalize_and_fold (
+                                fs_info->string_opts.string_handling_ctx.optional_args,
+                                &entry_utf8[0],
+                                bytes_in_entry,
+                                &entry_utf8_normalized[0],
+                                &bytes_in_entry_normalized) ) {
+                                    filename_entry_format_size = msdos_file_name_to_short_entry_string (
+                                        filename_converted,
+                                        filename_converted_size,
+                                        filename_entry_format,
+                                        MSDOS_ENTRY_LFN_UTF8_BYTES);
+                                    /*
+                                     * If the long file names matched or the file name is
+                                     * short and they match then we have the entry. We will not
+                                     * match a long file name against a short file name because
+                                     * a long file name that generates a matching short file
+                                     * name is not a long file name.
+                                     */
+                                    if (   bytes_in_entry_normalized ==
+                                           filename_entry_format_size
+                                        && lfn_start.cln == FAT_FILE_SHORT_NAME
+                                        && (0 == memcmp (entry_utf8_normalized,
+                                                         filename_entry_format,
+                                                         bytes_in_entry_normalized))) {
+                                        filename_matched = true;
+                                        rc = msdos_on_entry_found (
+                                            fs_info,
+                                            fat_fd,
+                                            bts2rd,
+                                            name_dir_entry,
+                                            entry,
+                                            dir_pos,
+                                            dir_offset,
+                                            dir_entry,
+                                            &lfn_start
+                                        );
+                                    }
+                                    if (rc == RC_OK && (! filename_matched)) {
+                                        lfn_start.cln = FAT_FILE_SHORT_NAME;
+                                        lfn_matched   = false;
+                                    }
+                            }
+                            else
+                              rc = -1;
                         }
-
-                        dir_pos->lname.cln = lfn_start.cln;
-                        dir_pos->lname.ofs = lfn_start.ofs;
-
-                        memcpy(name_dir_entry, entry,
-                               MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
-                        return RC_OK;
+                        else
+                          rc = -1;
                     }
-
-                    lfn_start.cln = FAT_FILE_SHORT_NAME;
-                    lfn_matched = false;
                 }
             }
         }
 
-        if (remainder_empty)
+        if (filename_matched || remainder_empty)
             break;
 
-        dir_offset++;
+        (*dir_offset)++;
     }
-
-    /*
-     * If we are not to create the entry return a not found error.
-     */
-    if (!create_node)
-      return MSDOS_NAME_NOT_FOUND_ERR;
+    if ( ! filename_matched ) {
+        /*
+         * If we are not to create the entry return a not found error.
+         */
+        if (!create_node)
+            rc = MSDOS_NAME_NOT_FOUND_ERR;
 
 #if MSDOS_FIND_PRINT
-    printf ("MSFS:[8.1] WRITE do:%ld esc:%ld eso:%ld ese:%ld\n",
-            dir_offset, empty_space_count, empty_space_offset, empty_space_entry);
+        printf ( "MSFS:[8.1] WRITE do:%"PRIu32" esc:%"PRIu32" eso:%"PRIu32" ese:%"PRIu32"\n",
+                 *dir_offset, *empty_space_count, *empty_space_offset, *empty_space_entry );
 #endif
+    }
+
+    return rc;
+}
+
+static int
+msdos_add_file (
+    const char                           *name_converted,
+    const msdos_name_type_t               name_type,
+    msdos_fs_info_t                      *fs_info,
+    fat_file_fd_t                        *fat_fd,
+    const uint32_t                        bts2rd,
+    const unsigned int                    fat_entries,
+    const char                           *name_dir_entry,
+    fat_dir_pos_t                        *dir_pos,
+    const uint32_t                        dir_offset,
+    const uint32_t                        empty_space_offset_param,
+    const uint32_t                        empty_space_entry_param,
+    const uint32_t                        empty_space_count
+
+)
+{
+    int              ret                = 0;
+    ssize_t          bytes_written      = 0;
+    uint8_t          lfn_checksum       = 0;
+    uint32_t         empty_space_offset = empty_space_offset_param;
+    uint32_t         empty_space_entry  = empty_space_entry_param;
+    bool             read_cluster       = false;
+    int              lfn_entry          = 0;
+    fat_pos_t        lfn_start;
+    uint32_t         dir_entry;
 
     /*
      * If a long file name calculate the checksum of the short file name
@@ -1341,15 +1532,14 @@ int msdos_find_name_in_fat_file(
      * file name to the slot of the SFN entry. This will mean no clashes
      * in this directory.
      */
-    lfn_checksum = 0;
     if (name_type == MSDOS_NAME_LONG)
     {
         int      slot = (((empty_space_offset * bts2rd) + empty_space_entry) /
-                         MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + lfn_entries + 1;
+                        MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + fat_entries + 1;
         msdos_short_name_hex(MSDOS_DIR_NAME(name_dir_entry), slot);
     }
 
-    if (lfn_entries)
+    if (fat_entries)
     {
         uint8_t* p = (uint8_t*) MSDOS_DIR_NAME(name_dir_entry);
         int      i;
@@ -1392,53 +1582,53 @@ int msdos_find_name_in_fat_file(
     /*
      * The one more is the short entry.
      */
-    while (lfn_entry < (lfn_entries + 1))
+    while (lfn_entry < (fat_entries + 1))
     {
         int length = 0;
 
         if (read_cluster)
         {
-          uint32_t new_length;
+            uint32_t new_length;
 #if MSDOS_FIND_PRINT
-          printf ("MSFS:[9.1] eso:%li\n", empty_space_offset);
+            printf ("MSFS:[9.1] eso:%li\n", empty_space_offset);
 #endif
-          ret = fat_file_read(&fs_info->fat, fat_fd,
-                              (empty_space_offset * bts2rd), bts2rd,
-                              fs_info->cl_buf);
+            ret = fat_file_read(&fs_info->fat, fat_fd,
+                                (empty_space_offset * bts2rd), bts2rd,
+                                fs_info->cl_buf);
 
-          if (ret != bts2rd)
-          {
-            if (ret != FAT_EOF)
-              rtems_set_errno_and_return_minus_one(EIO);
+            if (ret != bts2rd)
+            {
+                if (ret != FAT_EOF)
+                    rtems_set_errno_and_return_minus_one(EIO);
 
 #if MSDOS_FIND_PRINT
-            printf ("MSFS:[9.2] extending file:%li\n", empty_space_offset);
+                printf ("MSFS:[9.2] extending file:%li\n", empty_space_offset);
 #endif
-            ret = fat_file_extend (&fs_info->fat, fat_fd, false,
-                                   empty_space_offset * bts2rd, &new_length);
+                ret = fat_file_extend (&fs_info->fat, fat_fd, false,
+                                       empty_space_offset * bts2rd, &new_length);
 
-            if (ret != RC_OK)
-              return ret;
+                if (ret != RC_OK)
+                    return ret;
 
 #if MSDOS_FIND_PRINT
-            printf ("MSFS:[9.3] extended: %d <-> %d\n", new_length, empty_space_offset * bts2rd);
+                printf ("MSFS:[9.3] extended: %"PRIu32" <-> %"PRIu32"\n", new_length, empty_space_offset * bts2rd);
 #endif
-            if (new_length != (empty_space_offset * bts2rd))
-              rtems_set_errno_and_return_minus_one(EIO);
+                if (new_length != (empty_space_offset * bts2rd))
+                    rtems_set_errno_and_return_minus_one(EIO);
 
-            memset(fs_info->cl_buf, 0, bts2rd);
+                memset(fs_info->cl_buf, 0, bts2rd);
 
-            ret = fat_file_write(&fs_info->fat, fat_fd,
-                                 empty_space_offset * bts2rd,
-                                 bts2rd, fs_info->cl_buf);
+                bytes_written = fat_file_write(&fs_info->fat, fat_fd,
+                                               empty_space_offset * bts2rd,
+                                               bts2rd, fs_info->cl_buf);
 #if MSDOS_FIND_PRINT
-            printf ("MSFS:[9.4] clear write: %d\n", ret);
+                printf ("MSFS:[9.4] clear write: %d\n", ret);
 #endif
-            if (ret == -1)
-              return ret;
-            else if (ret != bts2rd)
-              rtems_set_errno_and_return_minus_one(EIO);
-          }
+                if (bytes_written == -1)
+                    return -1;
+                else if (bytes_written != bts2rd)
+                    rtems_set_errno_and_return_minus_one(EIO);
+            }
         }
 
 #if MSDOS_FIND_PRINT
@@ -1466,24 +1656,24 @@ int msdos_find_name_in_fat_file(
             /*
              * Time to write the short file name entry.
              */
-            if (lfn_entry == (lfn_entries + 1))
+            if (lfn_entry == (fat_entries + 1))
             {
                 /* get current cluster number */
-                int rc = fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM,
-                                        empty_space_offset * bts2rd,
-                                        &dir_pos->sname.cln);
-                if (rc != RC_OK)
-                  return rc;
+                ret = fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM,
+                                    empty_space_offset * bts2rd,
+                                    &dir_pos->sname.cln);
+                if (ret != RC_OK)
+                    return ret;
 
                 dir_pos->sname.ofs = dir_entry;
 
                 if (lfn_start.cln != FAT_FILE_SHORT_NAME)
                 {
-                  rc = fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM,
-                                      lfn_start.cln * bts2rd,
-                                      &lfn_start.cln);
-                  if (rc != RC_OK)
-                    return rc;
+                    ret = fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM,
+                                        lfn_start.cln * bts2rd,
+                                        &lfn_start.cln);
+                    if (ret != RC_OK)
+                        return ret;
                 }
 
                 dir_pos->lname.cln = lfn_start.cln;
@@ -1503,8 +1693,8 @@ int msdos_find_name_in_fat_file(
              */
             if (lfn_start.cln == FAT_FILE_SHORT_NAME)
             {
-              lfn_start.cln = empty_space_offset;
-              lfn_start.ofs = dir_entry;
+                lfn_start.cln = empty_space_offset;
+                lfn_start.ofs = dir_entry;
             }
 
             /*
@@ -1515,14 +1705,17 @@ int msdos_find_name_in_fat_file(
             *MSDOS_DIR_LFN_CHECKSUM(entry) = lfn_checksum;
 
             p = entry + 1;
-            n = name + (lfn_entries - lfn_entry) * MSDOS_LFN_LEN_PER_ENTRY;
+            n = name_converted + (fat_entries - lfn_entry) * MSDOS_LFN_LEN_PER_ENTRY * MSDOS_NAME_LFN_BYTES_PER_CHAR;
 
-            for (i = 0; i < MSDOS_LFN_LEN_PER_ENTRY; i++)
+#if MSDOS_FIND_PRINT
+            printf ("MSFS:[11] ");
+#endif
+            for (i = 0; i < MSDOS_LFN_LEN_PER_ENTRY; ++i)
             {
-                if (*n != 0)
+                if (!(*n == 0 && *(n+1) == 0))
                 {
                     *p = *n;
-                    n++;
+                    *(p+1) = *(n+1);
                 }
                 else
                 {
@@ -1530,6 +1723,10 @@ int msdos_find_name_in_fat_file(
                     p [1] = fill;
                     fill = 0xff;
                 }
+                n += MSDOS_NAME_LFN_BYTES_PER_CHAR;
+#if MSDOS_FIND_PRINT
+                printf ( "'%c''%c'", *p, *(p+1) );
+#endif
 
                 switch (i)
                 {
@@ -1544,29 +1741,184 @@ int msdos_find_name_in_fat_file(
                         break;
                 }
             }
-
-            *MSDOS_DIR_ENTRY_TYPE(entry) = (lfn_entries - lfn_entry) + 1;
+#if MSDOS_FIND_PRINT
+            printf ( "\n" );
+#endif
+            *MSDOS_DIR_ENTRY_TYPE(entry) = (fat_entries - lfn_entry) + 1;
             if (lfn_entry == 1)
                 *MSDOS_DIR_ENTRY_TYPE(entry) |= MSDOS_LAST_LONG_ENTRY;
             *MSDOS_DIR_ATTR(entry) |= MSDOS_ATTR_LFN;
         }
 
-        ret = fat_file_write(&fs_info->fat, fat_fd,
-                             (empty_space_offset * bts2rd) + empty_space_entry,
-                             length, fs_info->cl_buf + empty_space_entry);
-        if (ret == -1)
-            return ret;
-        else if (ret != length)
+        bytes_written = fat_file_write(&fs_info->fat, fat_fd,
+                                       (empty_space_offset * bts2rd) + empty_space_entry,
+                                       length, fs_info->cl_buf + empty_space_entry);
+        if (bytes_written == -1)
+            return -1;
+        else if (bytes_written != length)
             rtems_set_errno_and_return_minus_one(EIO);
 
         empty_space_offset++;
         empty_space_entry = 0;
         read_cluster = true;
     }
+    return ret;
+}
+
+int
+msdos_find_name_in_fat_file (
+    rtems_filesystem_mount_table_entry_t *mt_entry,
+    fat_file_fd_t                        *fat_fd,
+    bool                                  create_node,
+    const uint8_t                        *name_utf8,
+    int                                   name_utf8_len,
+    msdos_name_type_t                     name_type,
+    fat_dir_pos_t                        *dir_pos,
+    char                                 *name_dir_entry)
+{
+    int                                retval                     = 0;
+    msdos_fs_info_t                   *fs_info                    = mt_entry->fs_info;
+    size_t                             name_converted_len;
+    uint32_t                           bts2rd                     = 0;
+    uint32_t                           empty_space_offset         = 0;
+    uint32_t                           empty_space_entry          = 0;
+    uint32_t                           empty_space_count          = 0;
+    uint32_t                           dir_offset                 = 0;
+    unsigned int                       fat_entries;
+
+    assert(name_utf8_len > 0);
+
+    fat_dir_pos_init(dir_pos);
+
+
+
+    if (FAT_FD_OF_ROOT_DIR(fat_fd) &&
+        (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
+        bts2rd = fat_fd->fat_file_size;
+    else
+        bts2rd = fs_info->fat.vol.bpc;
+
+    switch ( name_type ) {
+        case MSDOS_NAME_SHORT:
+            memset (&fs_info->string_opts.string_handling_ctx.file_name_scratch_buf[0], 0,
+                    MSDOS_SHORT_NAME_LEN);
+            name_converted_len = msdos_filename_utf8_to_short_name (
+                &fs_info->string_opts,
+                name_utf8,
+                name_utf8_len,
+                (char*)&fs_info->string_opts.string_handling_ctx.file_name_scratch_buf[0],
+                MSDOS_SHORT_NAME_LEN,
+                MSDOS_STRING_TYPE_UTF8);
+            if (name_converted_len > 0) {
+                fat_entries = 0;
+            }
+            else
+                retval = -1;
+        break;
+        case MSDOS_NAME_LONG:
+            memset (&fs_info->string_opts.string_handling_ctx.file_name_scratch_buf[0], 0,
+                    MSDOS_NAME_MAX_LFN_BYTES);
+            name_converted_len = msdos_filename_utf8_to_long_name_for_compare (
+                &fs_info->string_opts,
+                name_utf8,
+                name_utf8_len/* +1*/,
+                &fs_info->string_opts.string_handling_ctx.file_name_scratch_buf[0],
+                MSDOS_NAME_MAX_LFN_BYTES);
+            if (name_converted_len > 0) {
+                fat_entries = (name_converted_len -1
+                               + MSDOS_LFN_LEN_PER_ENTRY) / MSDOS_LFN_LEN_PER_ENTRY;
+            }
+            else
+                retval = -1;
+        break;
+        case MSDOS_NAME_INVALID:
+        default:
+            errno = EINVAL;
+            retval = -1;
+        break;
+    }
+    /* See if the file/directory does already exist */
+    retval = msdos_find_file_in_directory (
+        &fs_info->string_opts.string_handling_ctx.file_name_scratch_buf[0],
+        name_converted_len,
+        name_type,
+        fs_info,
+        fat_fd,
+        bts2rd,
+        create_node,
+        fat_entries,
+        name_dir_entry,
+        dir_pos,
+        &dir_offset,
+        &empty_space_offset,
+        &empty_space_entry,
+        &empty_space_count);
+
+    /* Create a non-existing file/directory if requested */
+    if (   retval == RC_OK
+        && create_node) {
+        switch (name_type) {
+          case MSDOS_NAME_SHORT:
+              memset (&fs_info->string_opts.string_handling_ctx.file_name_scratch_buf[0], 0,
+                      MSDOS_SHORT_NAME_LEN);
+              name_converted_len = msdos_filename_utf8_to_short_name (
+                  &fs_info->string_opts,
+                  name_utf8,
+                  name_utf8_len,
+                  (char*)&fs_info->string_opts.string_handling_ctx.file_name_scratch_buf[0],
+                  MSDOS_SHORT_NAME_LEN,
+                  MSDOS_STRING_TYPE_CODEPAGE);
+              if (name_converted_len > 0 ) {
+                  fat_entries = 0;
+              }
+              else
+                  retval = -1;
+          break;
+          case MSDOS_NAME_LONG:
+              memset (&fs_info->string_opts.string_handling_ctx.file_name_scratch_buf[0], 0,
+                      MSDOS_NAME_MAX_LFN_BYTES);
+              name_converted_len = msdos_filename_utf8_to_long_name_for_save (
+                  &fs_info->string_opts,
+                  name_utf8,
+                  name_utf8_len,
+                  (uint16_t*)&fs_info->string_opts.string_handling_ctx.file_name_scratch_buf[0],
+                  MIN (MSDOS_NAME_MAX_LFN_BYTES,
+                       name_utf8_len * MSDOS_NAME_LFN_BYTES_PER_CHAR));
+              if (name_converted_len > 0) {
+                  name_converted_len = name_converted_len / MSDOS_NAME_LFN_BYTES_PER_CHAR;
+                  fat_entries = (name_converted_len -1
+                                 + MSDOS_LFN_LEN_PER_ENTRY) / MSDOS_LFN_LEN_PER_ENTRY;
+
+              }
+              else
+                  retval = -1;
+          break;
+          case MSDOS_NAME_INVALID:
+          default:
+              errno = EINVAL;
+              retval = -1;
+          break;
+        }
+        retval = msdos_add_file (
+            (const char*)&fs_info->string_opts.string_handling_ctx.file_name_scratch_buf[0],
+            name_type,
+            fs_info,
+            fat_fd,
+            bts2rd,
+            fat_entries,
+            name_dir_entry,
+            dir_pos,
+            dir_offset,
+            empty_space_offset,
+            empty_space_entry,
+            empty_space_count
+        );
+    }
 
-    return 0;
+    return retval;
 }
 
+
 /* msdos_find_node_by_cluster_num_in_fat_file --
  *     Find node with specified number of cluster in fat-file.
  *
diff --git a/cpukit/libfs/src/dosfs/msdos_string_handling_no_convert.c b/cpukit/libfs/src/dosfs/msdos_string_handling_no_convert.c
new file mode 100644
index 0000000..60e8fc7
--- /dev/null
+++ b/cpukit/libfs/src/dosfs/msdos_string_handling_no_convert.c
@@ -0,0 +1,166 @@
+/**
+ * @file msdos_string_handling_no_convert.c
+ *
+ * @ingroup libfs
+ *
+ * @brief Default- mostly stubbed implementations of string conversion methods
+ */
+
+/*
+ * Copyright (c) 2012, 2013 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Dornierstr. 4
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <rtems/dosfs.h>
+#include "fat.h"
+
+char *msdos_utf8_to_codepage_no_convert(
+  void          *string_arg,
+  const uint8_t *str,
+  const size_t   strsize,
+  char          *buffer,
+  size_t        *bufsize )
+{
+  char  *retval        = NULL;
+  size_t bytes_to_copy = MIN( strsize, *bufsize );
+
+
+  (void) string_arg;
+
+  if ( str != NULL
+       && buffer != NULL ) {
+    memcpy( buffer, str, bytes_to_copy );
+
+    *bufsize = bytes_to_copy;
+    retval   = buffer;
+  } else
+    errno = EINVAL;
+
+  return retval;
+}
+
+uint8_t *msdos_codepage_to_utf8_no_convert(
+  void        *string_arg,
+  const char  *str,
+  const size_t strsize,
+  uint8_t     *buffer,
+  size_t      *bufsize )
+{
+  uint8_t *retval        = NULL;
+  size_t   bytes_to_copy = MIN( strsize, *bufsize );
+
+
+  (void) string_arg;
+
+  if ( str != NULL
+       && buffer != NULL ) {
+    memcpy( buffer, str, bytes_to_copy );
+
+    *bufsize = bytes_to_copy;
+    retval   = buffer;
+  } else
+    errno = EINVAL;
+
+  return retval;
+}
+
+uint16_t *msdos_utf8_to_utf16_no_convert(
+  void          *string_arg,
+  const uint8_t *str,
+  const size_t   strsize,
+  uint16_t      *buffer,
+  size_t        *bufsize )
+{
+  uint16_t    *retval        = NULL;
+  size_t       bytes_to_copy = MIN( strsize, *bufsize );
+  unsigned int index;
+  uint16_t     utf16_char;
+
+
+  (void) string_arg;
+
+  if ( str != NULL
+       && buffer != NULL ) {
+    for ( index = 0; index < bytes_to_copy; ++index ) {
+      utf16_char    = str[index] << 8;
+      buffer[index] = 0xff00 & utf16_char;
+    }
+
+    *bufsize = bytes_to_copy * 2;
+    retval   = buffer;
+  } else
+    errno = EINVAL;
+
+  return retval;
+}
+
+uint8_t *msdos_utf16_to_utf8_no_convert(
+  void           *string_arg,
+  const uint16_t *str,
+  const size_t    strsize,
+  uint8_t        *buffer,
+  size_t         *bufsize )
+{
+  uint8_t     *retval        = NULL;
+  size_t       bytes_to_copy = MIN( strsize / 2, *bufsize );
+  unsigned int index;
+
+
+  (void) string_arg;
+
+  if ( str != NULL
+       && buffer != NULL ) {
+    for ( index = 0; index < bytes_to_copy; ++index ) {
+      buffer[index] = ( 0xff00 & str[index] ) >> 8;
+    }
+
+    *bufsize = bytes_to_copy;
+    retval   = buffer;
+  } else
+    errno = EINVAL;
+
+  return retval;
+}
+
+uint8_t *msdos_utf8_normalize_and_fold(
+  void          *string_arg,
+  const uint8_t *str,
+  const size_t   strsize,
+  uint8_t       *buffer,
+  size_t        *bufsize )
+{
+  uint8_t     *retval        = NULL;
+  size_t       bytes_to_copy = MIN( strsize, *bufsize );
+  unsigned int i;
+
+
+  (void) string_arg;
+
+  if ( str != NULL
+       && buffer != NULL ) {
+    for ( i = 0; i < bytes_to_copy; ++i ) {
+      if ( isalpha( str[i] ) )
+        buffer[i] = tolower( str[i] );
+      else
+        buffer[i] = str[i];
+    }
+
+    *bufsize = bytes_to_copy;
+    retval   = buffer;
+  } else
+    errno = EINVAL;
+
+  return retval;
+}
-- 
1.7.10.4




More information about the devel mailing list