[PATCH 1/9] dosfs: UTF-8 Support: UI, backwards compatibility
Ralf Kirchner
ralf.kirchner at embedded-brains.de
Fri May 31 13:42:17 UTC 2013
User interface and backwards compatibility for UTF-8 support in the FAT
file system. Purpose of UTF-8 support is to permit file names and
directory names with characters from all kinds of languages (Czech,
Chinese, Arabian, Hebrew, Korean, ...). This commit does not yet
support multibyte characters. It only contains the user interface and
the backwards compatibility.
---
cpukit/libfs/Makefile.am | 1 +
cpukit/libfs/src/dosfs/dosfs.h | 197 ++++-
cpukit/libfs/src/dosfs/msdos.h | 70 +-
cpukit/libfs/src/dosfs/msdos_conv.c | 619 ++++++++++++----
cpukit/libfs/src/dosfs/msdos_conv_default.c | 188 +++++
cpukit/libfs/src/dosfs/msdos_create.c | 3 +-
cpukit/libfs/src/dosfs/msdos_dir.c | 191 ++---
cpukit/libfs/src/dosfs/msdos_fsunmount.c | 4 +-
cpukit/libfs/src/dosfs/msdos_init.c | 35 +-
cpukit/libfs/src/dosfs/msdos_initsupp.c | 5 +-
cpukit/libfs/src/dosfs/msdos_misc.c | 1028 +++++++++++++++++++--------
11 Dateien geändert, 1781 Zeilen hinzugefügt(+), 560 Zeilen entfernt(-)
create mode 100644 cpukit/libfs/src/dosfs/msdos_conv_default.c
diff --git a/cpukit/libfs/Makefile.am b/cpukit/libfs/Makefile.am
index 0828da8..e06c8bd 100644
--- a/cpukit/libfs/Makefile.am
+++ b/cpukit/libfs/Makefile.am
@@ -81,6 +81,7 @@ libdosfs_a_SOURCES += src/dosfs/msdos_create.c src/dosfs/msdos_dir.c \
src/dosfs/msdos_initsupp.c src/dosfs/msdos_misc.c \
src/dosfs/msdos_mknod.c src/dosfs/msdos_node_type.c \
src/dosfs/msdos_rmnod.c src/dosfs/msdos_statvfs.c \
+ src/dosfs/msdos_conv_default.c \
src/dosfs/msdos_conv.c src/dosfs/msdos.h src/dosfs/msdos_format.c \
src/dosfs/dosfs.h src/dosfs/msdos_rename.c
endif
diff --git a/cpukit/libfs/src/dosfs/dosfs.h b/cpukit/libfs/src/dosfs/dosfs.h
index ea650b2..c7b27df 100644
--- a/cpukit/libfs/src/dosfs/dosfs.h
+++ b/cpukit/libfs/src/dosfs/dosfs.h
@@ -1,15 +1,18 @@
/**
- * @file rtems/dosfs.h
+ * @file
*
- * @brief Application Interface to MSDOS Filesystem
+ * @brief Application Interface to FAT Filesystem
*
- * @ingroup rtems_msdos_format
+ * @ingroup DOSFS
*/
/*
* Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
* Author: Eugeny S. Mints <Eugeny.Mints at oktet.ru>
*
+ * Modifications to support UTF-8 in the file system are
+ * Copyright (c) 2013 embedded brains GmbH.
+ *
* 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.
@@ -25,6 +28,170 @@
extern "C" {
#endif
+typedef struct rtems_dosfs_convert_control rtems_dosfs_convert_control;
+
+/**
+ * @brief Converts from UTF-8 into a specific code page.
+ *
+ * @param[in/out] self The convert control.
+ * @param[in] src A well-formed UTF-8 string to be converted.
+ * @param[in] src_size The size of the string in bytes (inludes '\0' if any).
+ * @param[out] dst The address the converted string will get copied to.
+ * @param[in/out] dst_size The size of the buffer in bytes respectively the
+ * number of bytes written to the buffer.
+ *
+ * @retval 0 Successful operation.
+ * @retval EINVAL Conversion was successful, but is not reversible.
+ * @retval ENOMEM Conversion failed (possibly due to insufficient buffer size).
+ */
+typedef int (*rtems_dosfs_utf8_to_codepage)(
+ rtems_dosfs_convert_control *self,
+ const uint8_t *src,
+ size_t src_size,
+ char *dst,
+ size_t *dst_size
+);
+
+/**
+ * @brief Converts from a specific code page into UTF-8
+ *
+ * @param[in/out] self The convert control.
+ * @param[in] src A well-formed string in code page format.
+ * @param[in] src_size The size of the string in bytes (inludes '\0' if any).
+ * @param[out] dst The address the converted string will get copied to.
+ * @param[in/out] dst_size The size of the buffer in bytes respectively the
+ * number of bytes written to the buffer.
+ *
+ * @retval 0 Successful operation.
+ * @retval EINVAL Conversion was successful, but is not reversible.
+ * @retval ENOMEM Conversion failed (possibly due to insufficient buffer size).
+ */
+typedef int (*rtems_dosfs_codepage_to_utf8)(
+ rtems_dosfs_convert_control *self,
+ const char *src,
+ size_t src_size,
+ uint8_t *dst,
+ size_t *dst_size
+);
+
+/**
+ * @brief Converts from UTF-8 to UTF-16
+ *
+ * @param[in/out] self The convert control.
+ * @param[in] src A well-formed UTF-8 string to be converted.
+ * @param[in] src_size The size of the string in bytes (inludes '\0' if any).
+ * @param[out] dst The address the converted string will get copied to
+ * @param[in/out] dst_size The size of the buffer in bytes respectively the
+ * number of bytes written to the buffer.
+ *
+ * @retval 0 Successful operation.
+ * @retval EINVAL Conversion was successful, but is not reversible.
+ * @retval ENOMEM Conversion failed (possibly due to insufficient buffer size).
+ */
+typedef int (*rtems_dosfs_utf8_to_utf16)(
+ rtems_dosfs_convert_control *self,
+ const uint8_t *src,
+ size_t src_size,
+ uint16_t *dst,
+ size_t *dst_size
+);
+
+/**
+ * @brief Converts from UTF-16 to UTF-8.
+ *
+ * @param[in/out] self The convert control.
+ * @param[in] src A well-formed UTF-16 string to be converted.
+ * @param[in] src_size The size of the string in bytes (inludes '\0' if any).
+ * @param[out] dst The address the converted string will get copied to.
+ * @param[in/out] dst_size The size of the buffer in bytes respectively the
+ * number of bytes written to the buffer
+ *
+ * @retval 0 Successful operation.
+ * @retval EINVAL Conversion was successful, but is not reversible.
+ * @retval ENOMEM Conversion failed (possibly due to insufficient buffer size).
+ */
+typedef int (*rtems_dosfs_utf16_to_utf8)(
+ rtems_dosfs_convert_control *self,
+ const uint16_t *src,
+ size_t src_size,
+ uint8_t *dst,
+ size_t *dst_size
+);
+
+/**
+ * @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] self The convert control.
+ * @param[in] src A well-formed UTF-8 string to be normalized and fold.
+ * @param[in] src_size The size of the string in bytes (inludes '\0' if any).
+ * @param[out] dst The address the normalized and fold string will get
+ * copied to.
+ * @param[in/out] dst_size The size of the buffer in bytes respectively the
+ * number of bytes written to the buffer.
+ *
+ * @retval 0 Successful operation.
+ * @retval EINVAL Conversion failed.
+ * @retval ENOMEM Conversion failed (possibly due to insufficient buffer size).
+ * @retval EOVERFLOW Conversion failed.
+ * @retval ENOENT Conversion failed.
+ */
+typedef int (*rtems_dosfs_utf8_normalize_and_fold)(
+ rtems_dosfs_convert_control *self,
+ const uint8_t *src,
+ size_t src_size,
+ uint8_t *dst,
+ size_t *dst_size
+);
+
+/**
+ * @brief Destorys a convert control structure.
+ *
+ * @param[in/out] self The convert control for destruction.
+ */
+typedef void (*rtems_dosfs_convert_destroy)(
+ rtems_dosfs_convert_control *self
+);
+
+/**
+ * @brief FAT filesystem convert handler.
+ */
+typedef struct {
+ 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;
+ rtems_dosfs_convert_destroy destroy;
+} rtems_dosfs_convert_handler;
+
+typedef struct {
+ void *data;
+ size_t size;
+} rtems_dosfs_buffer;
+
+/**
+ * @brief FAT filesystem convert control.
+ *
+ * Short file names are stored in the code page format. Long file names are
+ * stored as little-endian UTF-16. The convert control determines the format
+ * conversions to and from the POSIX file name strings.
+ */
+struct rtems_dosfs_convert_control {
+ const rtems_dosfs_convert_handler *handler;
+ rtems_dosfs_buffer buffer;
+};
+
+/**
+ * @defgroup DOSFS FAT Filesystem Support
+ *
+ * @ingroup FileSystemTypesAndMount
+ *
+ * @{
+ */
+
/**
* @brief Semaphore count per FAT filesystem instance.
*
@@ -32,16 +199,27 @@ extern "C" {
*/
#define RTEMS_DOSFS_SEMAPHORES_PER_INSTANCE 1
-int rtems_dosfs_initialize(rtems_filesystem_mount_table_entry_t *mt_entry,
- const void *data);
+/**
+ * @brief FAT filesystem mount options.
+ */
+typedef struct {
+ /**
+ * @brief Converter implementation for new filesystem instance.
+ *
+ * @see rtems_dosfs_create_default_converter().
+ */
+ rtems_dosfs_convert_control *converter;
+} rtems_dosfs_mount_options;
/**
- * @defgroup rtems_msdos_format DOSFS Support
+ * @brief Allocates and initializes a default converter.
*
- * @ingroup FileSystemTypesAndMount
+ * @retval NULL Something failed.
+ * @retval other Pointer to initialized converter.
*
+ * @see rtems_dosfs_mount_options and mount().
*/
-/**@{**/
+rtems_dosfs_convert_control *rtems_dosfs_create_default_converter(void);
#define MSDOS_FMT_INFO_LEVEL_NONE (0)
#define MSDOS_FMT_INFO_LEVEL_INFO (1)
@@ -131,6 +309,9 @@ int msdos_format (
/** @} */
+int rtems_dosfs_initialize(rtems_filesystem_mount_table_entry_t *mt_entry,
+ const void *data);
+
#ifdef __cplusplus
}
#endif
diff --git a/cpukit/libfs/src/dosfs/msdos.h b/cpukit/libfs/src/dosfs/msdos.h
index 78eda9b..6da47cc 100644
--- a/cpukit/libfs/src/dosfs/msdos.h
+++ b/cpukit/libfs/src/dosfs/msdos.h
@@ -10,6 +10,9 @@
* Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
* Author: Eugeny S. Mints <Eugeny.Mints at oktet.ru>
*
+ * Modifications to support UTF-8 in the file system are
+ * Copyright (c) 2013 embedded brains GmbH.
+ *
* 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.
@@ -20,6 +23,7 @@
#include <rtems.h>
#include <rtems/libio_.h>
+#include <rtems/dosfs.h>
#include "fat.h"
#include "fat_file.h"
@@ -67,6 +71,8 @@ typedef struct msdos_fs_info_s
* just placeholder
* for anything
*/
+
+ rtems_dosfs_convert_control *converter;
} msdos_fs_info_t;
/* a set of routines that handle the nodes which are directories */
@@ -183,6 +189,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 +198,20 @@ 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_SFN_MAX_WITH_DOT_UTF8_BYTES (MSDOS_NAME_MAX_WITH_DOT *\
+ MSDOS_NAME_MAX_UTF8_BYTES_PER_CHAR)
#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 */
@@ -309,7 +328,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_convert_control *converter
);
int msdos_file_close(rtems_libio_t *iop /* IN */);
@@ -387,10 +407,52 @@ 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,
+ssize_t
+msdos_format_dirent_with_dot(char *dst,const char *src);
+
+msdos_name_type_t msdos_long_to_short(rtems_dosfs_convert_control *converter,
+ const char *lfn, int lfn_len,
char* sfn, int sfn_len);
-int msdos_filename_unix2dos(const char *un, int unlen, char *dn);
+ssize_t
+msdos_filename_utf8_to_short_name_for_compare (
+ rtems_dosfs_convert_control *converter,
+ const uint8_t *utf8_name,
+ const size_t utf8_name_size,
+ void *short_name,
+ const size_t short_name_size);
+
+ssize_t
+msdos_filename_utf8_to_short_name_for_save (
+ rtems_dosfs_convert_control *converter,
+ const uint8_t *utf8_name,
+ const size_t utf8_name_size,
+ void *short_name,
+ const size_t short_name_size);
+
+ssize_t
+msdos_filename_utf8_to_long_name_for_compare (
+ rtems_dosfs_convert_control *converter,
+ 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_convert_control *converter,
+ 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,
@@ -430,7 +492,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..7e688ef 100644
--- a/cpukit/libfs/src/dosfs/msdos_conv.c
+++ b/cpukit/libfs/src/dosfs/msdos_conv.c
@@ -22,18 +22,38 @@
* $NetBSD: msdosfs_conv.c,v 1.10 1994/12/27 18:36:24 mycroft Exp $
*
* October 1992
+ *
+ * Modifications to support UTF-8 in the file system are
+ * Copyright (c) 2013 embedded brains GmbH.
*/
#if HAVE_CONFIG_H
#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 CT_LE_W( 0x0000 )
+#define UTF16_NULL_SIZE 2
+#define UTF16_BLANK CT_LE_W( 0x0020 )
+#define UTF16_BLANK_SIZE 2
+#define UTF16_FULL_STOP CT_LE_W( 0x002e )
+#define UTF16_FULL_STOP_SIZE 2
+
/*
* Days in each month in a regular year.
*/
@@ -174,150 +194,485 @@ 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, 0x7e, 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)
+
+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 char_num = CF_LE_W( utf16_character );
- /*
- * Fill the dos filename string with blanks. These are DOS's pad
- * characters.
- */
- for (i = 0; i <= 10; i++)
- dn[i] = ' ';
+ if ( char_num <= 0x00ff ) {
+ switch ( char_num )
+ {
+ 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 = char_num;
+ break;
+ default:
+ retval = codepage_valid_char_map[char_num];
+ break;
+ }
+ }
+ else
+ retval = char_num;
- /*
- * 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;
- }
+ return CT_LE_W( retval );
+}
+
+static char
+msdos_get_valid_codepage_filename_character (const uint8_t character)
+{
+ return codepage_valid_char_map[(unsigned int)character];
+}
+
+static ssize_t
+msdos_filename_process_dot_names (const uint8_t *src_name,
+ const size_t src_size,
+ uint8_t *dest_name,
+ const size_t dest_size)
+{
+ ssize_t returned_size = 0;
+ int eno = 0;
+ /*
+ * The filenames "." and ".." are handled specially, since they
+ * don't follow dos filename rules.
+ */
+ if ( src_name[0] == UTF8_FULL_STOP
+ && src_size == UTF8_FULL_STOP_SIZE) {
+ if (dest_size >= UTF8_FULL_STOP_SIZE) {
+ dest_name[0] = UTF8_FULL_STOP;
+ returned_size = UTF8_FULL_STOP_SIZE;
+ }
+ else
+ eno = ENAMETOOLONG;
+ }
+ else if ( eno == 0
+ && src_name[0] == UTF8_FULL_STOP
+ && src_name[1] == UTF8_FULL_STOP
+ && src_size == ( 2 * UTF8_FULL_STOP_SIZE ) ) {
+ if (dest_size >= 2 * UTF8_FULL_STOP_SIZE) {
+ dest_name[0] = UTF8_FULL_STOP;
+ dest_name[1] = UTF8_FULL_STOP;
+ returned_size = 2 * UTF8_FULL_STOP_SIZE;
+ }
+ else
+ eno = ENAMETOOLONG;
+ }
+
+ if (eno != 0) {
+ errno = eno;
+ returned_size = -1;
+ }
+
+ return returned_size;
+}
+
+static ssize_t
+msdos_filename_delete_trailing_dots (const uint8_t *filename_utf8,
+ const size_t filename_size)
+{
+ ssize_t size_returned = filename_size;
+ unsigned int i;
/*
- * Remove any dots from the start of a file name.
+ * Remove any dots from the end of a file name.
*/
- while (unlen && (*un == '.')) {
- un++;
- unlen--;
- }
+ for ( i = size_returned - UTF8_FULL_STOP_SIZE;
+ size_returned >= UTF8_FULL_STOP_SIZE
+ && filename_utf8[i] == UTF8_FULL_STOP;) {
+ size_returned -= UTF8_FULL_STOP_SIZE;
+ i -= 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--;
- }
+ return size_returned;
+}
- /*
- * 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;
- }
+ssize_t
+msdos_filename_utf8_to_long_name_for_compare (
+ rtems_dosfs_convert_control *converter,
+ 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;
+ size_t name_size;
+ size_t dest_size = long_name_size;
- /*
- * 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;
+ returned_size = msdos_filename_process_dot_names (
+ utf8_name,
+ utf8_name_size,
+ long_name,
+ long_name_size);
+
+ if (returned_size == 0) {
+ name_size = msdos_filename_delete_trailing_dots (
+ &utf8_name[0],
+ utf8_name_size);
+ if (name_size > 0) {
+ eno = (*converter->handler->utf8_normalize_and_fold) (
+ converter,
+ utf8_name,
+ name_size,
+ long_name,
+ &dest_size);
+ if (eno == 0) {
+ returned_size = (ssize_t)dest_size;
+ }
+ } else {
+ eno = EINVAL;
+ }
+ }
+
+ if ( eno != 0 ) {
+ errno = eno;
+ returned_size = -1;
+ }
+
+ return returned_size;
+ }
+
+ssize_t
+msdos_filename_utf8_to_long_name_for_save (
+ rtems_dosfs_convert_control *converter,
+ 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;
+ unsigned int chars_written;
+
+ name_size_tmp = long_name_size;
+ name_size = msdos_filename_delete_trailing_dots (
+ &utf8_name[0],
+ utf8_name_size);
+ if (name_size > 0) {
+ /*
+ * Finally convert from UTF-8 to UTF-16
+ */
+ eno = (*converter->handler->utf8_to_utf16) (
+ converter,
+ utf8_name,
+ name_size,
+ &long_name[0],
+ &name_size_tmp);
+ if (eno == 0) {
+ if (name_size_tmp <= (MSDOS_NAME_MAX_LNF_LEN * MSDOS_NAME_LFN_BYTES_PER_CHAR))
+ name_size = name_size_tmp;
+ else
+ eno = ENAMETOOLONG;
+ }
+
+ 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;
+ chars_written = returned_size / MSDOS_NAME_LFN_BYTES_PER_CHAR;
+ if ( long_name [chars_written - 1] != UTF16_NULL
+ && (returned_size + UTF16_NULL_SIZE ) <= long_name_size ) {
+ long_name[chars_written] = UTF16_NULL;
+ }
+ }
+ }
+ else
+ eno = EINVAL;
+
+ if ( eno != 0 ) {
+ errno = eno;
+ returned_size = -1;
+ }
+
+ return returned_size;
+ }
+
+/*
+ * Remove any dots from the start of a file name.
+ */
+static void msdos_filename_remove_prepended_dots (const uint8_t **name_utf8,
+ size_t *name_size)
+{
+ while ( *name_size >= UTF8_FULL_STOP_SIZE
+ && **name_utf8 == UTF8_FULL_STOP) {
+ *name_utf8 += UTF8_FULL_STOP_SIZE;
+ *name_size -= UTF8_FULL_STOP_SIZE;
+ }
+}
+
+ssize_t
+msdos_filename_utf8_to_short_name_for_compare (
+ rtems_dosfs_convert_control *converter,
+ const uint8_t *utf8_name,
+ const size_t utf8_name_size,
+ void *short_name,
+ const size_t short_name_size)
+{
+ ssize_t returned_size = 0;
+ int eno = 0;
+ const uint8_t *name_ptr = utf8_name;
+ char *dest_ptr = (char*)short_name;
+ size_t name_size = utf8_name_size;
+ uint8_t name_normalized_buf[(MSDOS_SHORT_NAME_LEN +1) * MSDOS_NAME_MAX_UTF8_BYTES_PER_CHAR];
+ size_t name_size_tmp = sizeof(name_normalized_buf);
+
+ returned_size = msdos_filename_process_dot_names (
+ utf8_name,
+ utf8_name_size,
+ short_name,
+ short_name_size);
+
+ if (returned_size == 0) {
+ msdos_filename_remove_prepended_dots (&name_ptr,
+ &name_size);
+ if (name_size > 0) {
+ /*
+ * Normalize the name and convert to lower case
+ */
+ eno = (*converter->handler->utf8_normalize_and_fold) (
+ converter,
+ name_ptr,
+ name_size,
+ &name_normalized_buf[0],
+ &name_size_tmp);
+ name_ptr = &name_normalized_buf[0];
+ name_size = name_size_tmp;
+ if ( eno == ENOMEM ) {
+ eno = 0;
+ }
+ if ( eno == 0 ) {
+ memcpy (&dest_ptr[0], &name_ptr[0], name_size);
+ returned_size = name_size;
+ }
+ } else
+ eno = EINVAL;
+ }
+
+ if ( eno != 0 ) {
+ errno = eno;
+ returned_size = -1;
+ }
+
+ return returned_size;
+}
+
+ssize_t
+msdos_filename_utf8_to_short_name_for_save (
+ rtems_dosfs_convert_control *converter,
+ const uint8_t *utf8_name,
+ const size_t utf8_name_size,
+ void *short_name,
+ const size_t short_name_size)
+{
+ ssize_t returned_size = 0;
+ int eno = 0;
+ const uint8_t *name_ptr = utf8_name;
+ size_t name_size = utf8_name_size;
+ char *dest_ptr = (char*)short_name;
+ unsigned int i;
+ char c;
+ size_t name_size_tmp;
+ char name_to_format_buf[MSDOS_SHORT_NAME_LEN +1];
+
+ returned_size = msdos_filename_process_dot_names (
+ utf8_name,
+ utf8_name_size,
+ short_name,
+ short_name_size);
+
+ if (returned_size == 0) {
+ msdos_filename_remove_prepended_dots (&name_ptr,
+ &name_size);
+
+ if (name_size > 0) {
+ /*
+ * Finally convert from UTF-8 to codepage
+ */
+ name_size_tmp = sizeof ( name_to_format_buf );
+ eno = (*converter->handler->utf8_to_codepage) (
+ converter,
+ name_ptr,
+ name_size,
+ &name_to_format_buf[0],
+ &name_size_tmp);
+ if ( eno != 0 ) {
+ /* The UTF-8 name my well be long name, for which we now want to
+ * generate the corresponding short name. Under these circumstances
+ * eno != 0 likely simply means that the UTF-8 name is longer than 11 characters
+ * or that it contains unicode characters which can not be converted to the code page
+ * in a reversible way. Non-reversible characters will be represented by question mark
+ * characters. Later in this method they will get replaced by underline characters.
+ */
+ eno = 0;
+ }
+ name_ptr = (const uint8_t *)(&name_to_format_buf[0]);
+ name_size = name_size_tmp;
+ for (i = 0; i < name_size; ++i)
+ name_to_format_buf[i] = toupper ( (unsigned char)(name_to_format_buf[i]) );
+ /*
+ * 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 )
+ dest_ptr[0] = '_';
+ else if ( 0xE5 == *name_ptr )
+ dest_ptr[0] = 0x05;
+ else if (0 != (c = msdos_get_valid_codepage_filename_character( *name_ptr ) ) )
+ dest_ptr[0] = c;
+ else
+ dest_ptr[0] = '_';
+ ++name_ptr;
+ ++returned_size;
+ --name_size;
+ /*
+ * Validate and assign all other characters of the name part
+ */
+ for (i = 1; i <= 7 && name_size && *name_ptr != '.'; ++i) {
+ c = msdos_get_valid_codepage_filename_character ( *name_ptr );
+ if (c != 0)
+ dest_ptr[i] = c;
+ else
+ dest_ptr[i] = '_';
+ ++name_ptr;
+ ++returned_size;
+ --name_size;
+ }
+ /*
+ * Strip any further characters up to a '.' or the end of the
+ * string.
+ */
+ if ( *name_ptr == '.' ) {
+ ++name_ptr;
+ --name_size;
+ }
+
+ for (; i < 8; ++i) {
+ dest_ptr[i] = ' ';
+ ++returned_size;
+ }
+
+ /*
+ * Copy in the extension part of the name, if any.
+ */
+ for (; i <= 10 && name_size ; i++) {
+ c = msdos_get_valid_codepage_filename_character ( *name_ptr);
+ if (c != 0)
+ dest_ptr[i] = c;
+ else
+ dest_ptr[i] = '_';
+ ++name_ptr;
+ ++returned_size;
+ name_size--;
+ }
+ /*
+ * Fill up with blanks. These are DOS's pad characters.
+ */
+ for ( ; i < short_name_size; ++i ) {
+ dest_ptr[i] = ' ';
+ ++returned_size;
+ }
+ }
+ }
+ else
+ eno = EINVAL;
+ }
+
+ if ( eno != 0 ) {
+ errno = eno;
+ return -1;
+ }
+
+ return returned_size;
}
+
+
diff --git a/cpukit/libfs/src/dosfs/msdos_conv_default.c b/cpukit/libfs/src/dosfs/msdos_conv_default.c
new file mode 100644
index 0000000..baf8320
--- /dev/null
+++ b/cpukit/libfs/src/dosfs/msdos_conv_default.c
@@ -0,0 +1,188 @@
+/**
+ * @file
+ *
+ * @ingroup DOSFS
+ *
+ * @brief Default Converter
+ */
+
+/*
+ * Copyright (c) 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 <assert.h>
+#include <rtems/endian.h>
+#include <rtems/dosfs.h>
+#include "fat.h"
+#include "msdos.h"
+
+static int msdos_default_utf8_to_codepage(
+ rtems_dosfs_convert_control *super,
+ const uint8_t *src,
+ const size_t src_size,
+ char *dst,
+ size_t *dst_size
+)
+{
+ int eno = 0;
+ size_t bytes_to_copy = MIN( src_size, *dst_size );
+
+ (void) super;
+
+ *dst_size = bytes_to_copy;
+
+ memcpy( dst, src, bytes_to_copy );
+
+ return eno;
+}
+
+static int msdos_default_codepage_to_utf8(
+ rtems_dosfs_convert_control *super,
+ const char *src,
+ const size_t src_size,
+ uint8_t *dst,
+ size_t *dst_size
+)
+{
+ int eno = 0;
+ size_t bytes_to_copy = MIN( src_size, *dst_size );
+
+ (void) super;
+
+ *dst_size = bytes_to_copy;
+
+ memcpy( dst, src, bytes_to_copy );
+
+ return eno;
+}
+
+static int msdos_default_utf8_to_utf16(
+ rtems_dosfs_convert_control *super,
+ const uint8_t *src,
+ const size_t src_size,
+ uint16_t *dst,
+ size_t *dst_size
+)
+{
+ int eno = 0;
+ size_t bytes_to_copy = MIN( src_size, *dst_size / 2);
+ size_t i;
+
+ (void) super;
+
+ *dst_size = 2 * bytes_to_copy;
+
+ for ( i = 0; eno == 0 && i < bytes_to_copy; ++i ) {
+ uint16_t utf16_native = src[i];
+
+ if ( utf16_native <= 127 ) {
+ dst[i] = CT_LE_W( utf16_native );
+ } else {
+ eno = EINVAL;
+ }
+ }
+
+ return eno;
+}
+
+static int msdos_default_utf16_to_utf8(
+ rtems_dosfs_convert_control *super,
+ const uint16_t *src,
+ const size_t src_size,
+ uint8_t *dst,
+ size_t *dst_size
+)
+{
+ int eno = 0;
+ size_t bytes_to_copy = MIN( src_size / 2, *dst_size );
+ size_t i;
+
+ (void) super;
+
+ *dst_size = bytes_to_copy;
+
+ for ( i = 0; eno == 0 && i < bytes_to_copy; ++i ) {
+ uint16_t utf16_le = src[i];
+ uint16_t utf16_native = CF_LE_W( utf16_le );
+
+ if ( utf16_native <= 127 ) {
+ dst[i] = (uint8_t) utf16_native;
+ } else {
+ eno = EINVAL;
+ }
+ }
+
+ return eno;
+}
+
+static int msdos_default_normalize_and_fold(
+ rtems_dosfs_convert_control *super,
+ const uint8_t *src,
+ const size_t src_size,
+ uint8_t *dst,
+ size_t *dst_size
+)
+{
+ int eno = 0;
+ size_t bytes_to_copy = MIN( src_size, *dst_size );
+ size_t i;
+
+ (void) super;
+
+ *dst_size = bytes_to_copy;
+
+ for ( i = 0; i < bytes_to_copy; ++i ) {
+ dst[i] = tolower( src[i] );
+ }
+
+ return eno;
+}
+
+static void msdos_default_destroy(
+ rtems_dosfs_convert_control *super
+)
+{
+ free( super );
+}
+
+static const rtems_dosfs_convert_handler msdos_default_convert_handler = {
+ .utf8_to_codepage = msdos_default_utf8_to_codepage,
+ .codepage_to_utf8 = msdos_default_codepage_to_utf8,
+ .utf8_to_utf16 = msdos_default_utf8_to_utf16,
+ .utf16_to_utf8 = msdos_default_utf16_to_utf8,
+ .utf8_normalize_and_fold = msdos_default_normalize_and_fold,
+ .destroy = msdos_default_destroy
+};
+
+typedef struct {
+ rtems_dosfs_convert_control super;
+ uint8_t buffer[MSDOS_NAME_MAX_LFN_BYTES];
+} msdos_default_convert_control;
+
+rtems_dosfs_convert_control *rtems_dosfs_create_default_converter(void)
+{
+ msdos_default_convert_control *self = malloc( sizeof( *self ) );
+
+ if ( self != NULL ) {
+ rtems_dosfs_convert_control *super = &self->super;
+
+ super->handler = &msdos_default_convert_handler;
+ super->buffer.data = &self->buffer;
+ super->buffer.size = sizeof( self->buffer );
+ }
+
+ return &self->super;
+}
diff --git a/cpukit/libfs/src/dosfs/msdos_create.c b/cpukit/libfs/src/dosfs/msdos_create.c
index 886dd40..e836513 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->converter,
+ 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..253203c 100644
--- a/cpukit/libfs/src/dosfs/msdos_dir.c
+++ b/cpukit/libfs/src/dosfs/msdos_dir.c
@@ -9,6 +9,9 @@
* Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
* Author: Eugeny S. Mints <Eugeny.Mints at oktet.ru>
*
+ * Modifications to support UTF-8 in the file system are
+ * Copyright (c) 2013 embedded brains GmbH.
+ *
* 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.
@@ -33,74 +36,7 @@
#include "msdos.h"
-/* msdos_format_dirent_with_dot --
- * This routine convert a (short) MSDOS filename as present on disk
- * (fixed 8+3 characters, filled with blanks, without separator dot)
- * to a "normal" format, with between 0 and 8 name chars,
- * a separating dot and up to 3 extension characters
- * Rules to work:
- * - copy any (0-8) "name" part characters that are non-blank
- * - if an extension exists, append a dot
- * - copy any (0-3) non-blank extension characters
- * - append a '\0' (dont count it for the rturn code
- *
- * PARAMETERS:
- * dst: pointer to destination char array (must be big enough)
- * src: pointer to source characters
- *
- *
- * RETURNS:
- * the number of bytes (without trailing '\0'(written to destination
- */
-static ssize_t
-msdos_format_dirent_with_dot(char *dst,const char *src)
-{
- ssize_t len;
- int i;
- const char *src_tmp;
-
- /*
- * find last non-blank character of base name
- */
- for ((i = MSDOS_SHORT_BASE_LEN ,
- src_tmp = src + MSDOS_SHORT_BASE_LEN-1);
- ((i > 0) &&
- (*src_tmp == ' '));
- i--,src_tmp--)
- {};
- /*
- * copy base name to destination
- */
- src_tmp = src;
- len = i;
- while (i-- > 0) {
- *dst++ = tolower((unsigned char)(*src_tmp++));
- }
- /*
- * find last non-blank character of extension
- */
- for ((i = MSDOS_SHORT_EXT_LEN ,
- src_tmp = src + MSDOS_SHORT_BASE_LEN+MSDOS_SHORT_EXT_LEN-1);
- ((i > 0) &&
- (*src_tmp == ' '));
- i--,src_tmp--)
- {};
- /*
- * extension is not empty
- */
- if (i > 0) {
- *dst++ = '.'; /* append dot */
- len += i + 1; /* extension + dot */
- src_tmp = src + MSDOS_SHORT_BASE_LEN;
- while (i-- > 0) {
- *dst++ = tolower((unsigned char)(*src_tmp++));
- len++;
- }
- }
- *dst = '\0'; /* terminate string */
- return len;
-}
/* msdos_dir_read --
* This routine will read the next directory entry based on the directory
@@ -128,11 +64,18 @@ ssize_t
msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
{
int rc = RC_OK;
+ int eno = 0;
rtems_status_code sc = RTEMS_SUCCESSFUL;
msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info;
+ rtems_dosfs_convert_control *converter = fs_info->converter;
+ const rtems_dosfs_convert_handler *convert_handler = converter->handler;
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 = 0;
+ uint16_t *lfn_buf = converter->buffer.data;
+ char *sfn_buf = converter->buffer.data;
+ const size_t buf_size = converter->buffer.size;
uint32_t start = 0;
ssize_t ret = 0;
uint32_t cmpltd = 0;
@@ -142,6 +85,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 +112,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 +128,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 +158,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 +185,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 +209,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 +219,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],
+ buf_size - offset_lfn,
+ is_first_entry
+ );
}
else
{
@@ -344,9 +274,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 +290,20 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
if (lfn_entries || (lfn_checksum != cs))
lfn_start = FAT_FILE_SHORT_NAME;
+
+ eno = (*convert_handler->utf16_to_utf8) (
+ converter,
+ lfn_buf,
+ tmp_lfn_len,
+ (uint8_t*)(&tmp_dirent.d_name[0]),
+ &string_size);
+ if (eno == 0) {
+ tmp_dirent.d_namlen = string_size;
+ tmp_dirent.d_name[tmp_dirent.d_namlen] = '\0';
+ }
+ else {
+ lfn_start = FAT_FILE_SHORT_NAME;
+ }
}
if (lfn_start == FAT_FILE_SHORT_NAME)
@@ -368,25 +313,37 @@ 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 */
+ eno = (*convert_handler->codepage_to_utf8) (
+ converter,
+ sfn_buf,
+ tmp_dirent.d_namlen,
+ (uint8_t*)(&tmp_dirent.d_name[0]),
+ &string_size);
+ if ( 0 == eno ) {
+ tmp_dirent.d_namlen = string_size;
+ tmp_dirent.d_name[tmp_dirent.d_namlen] = '\0';
+ }
+ else {
+ cmpltd = -1;
+ errno = eno;
+ }
}
- 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..57335e2 100644
--- a/cpukit/libfs/src/dosfs/msdos_fsunmount.c
+++ b/cpukit/libfs/src/dosfs/msdos_fsunmount.c
@@ -47,13 +47,15 @@ msdos_shut_down(rtems_filesystem_mount_table_entry_t *temp_mt_entry)
{
msdos_fs_info_t *fs_info = temp_mt_entry->fs_info;
fat_file_fd_t *fat_fd = temp_mt_entry->mt_fs_root->location.node_access;
+ rtems_dosfs_convert_control *converter = fs_info->converter;
- /* close fat-file which correspondes to root directory */
+ /* close fat-file which corresponds to root directory */
fat_file_close(&fs_info->fat, fat_fd);
fat_shutdown_drive(&fs_info->fat);
rtems_semaphore_delete(fs_info->vol_sema);
+ (*converter->handler->destroy)( converter );
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 e82e3f5..bcc5f9f 100644
--- a/cpukit/libfs/src/dosfs/msdos_init.c
+++ b/cpukit/libfs/src/dosfs/msdos_init.c
@@ -12,6 +12,9 @@
* Modifications to support reference counting in the file system are
* Copyright (c) 2012 embedded brains GmbH.
*
+ * Modifications to support UTF-8 in the file system are
+ * Copyright (c) 2013 embedded brains GmbH.
+ *
* 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.
@@ -89,14 +92,32 @@ 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 *data
+)
{
- int rc;
+ int rc = 0;
+ const rtems_dosfs_mount_options *mount_options = data;
+ rtems_dosfs_convert_control *converter;
+
+
+ if (mount_options == NULL || mount_options->converter == NULL) {
+ converter = rtems_dosfs_create_default_converter();
+ } else {
+ converter = mount_options->converter;
+ }
+
+ if (converter != NULL) {
+ rc = msdos_initialize_support(mt_entry,
+ &msdos_ops,
+ &msdos_file_handlers,
+ &msdos_dir_handlers,
+ converter);
+ } else {
+ errno = ENOMEM;
+ rc = -1;
+ }
- rc = msdos_initialize_support(mt_entry,
- &msdos_ops,
- &msdos_file_handlers,
- &msdos_dir_handlers);
return rc;
}
diff --git a/cpukit/libfs/src/dosfs/msdos_initsupp.c b/cpukit/libfs/src/dosfs/msdos_initsupp.c
index 0844186..02b1baf 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_convert_control *converter
)
{
int rc = RC_OK;
@@ -68,6 +69,8 @@ msdos_initialize_support(
temp_mt_entry->fs_info = fs_info;
+ fs_info->converter = converter;
+
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 88f0b94..56b58c8 100644
--- a/cpukit/libfs/src/dosfs/msdos_misc.c
+++ b/cpukit/libfs/src/dosfs/msdos_misc.c
@@ -9,6 +9,9 @@
* Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
* Author: Eugeny S. Mints <Eugeny.Mints at oktet.ru>
*
+ * Modifications to support UTF-8 in the file system are
+ * Copyright (c) 2013 embedded brains GmbH.
+ *
* 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.
@@ -26,6 +29,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 +74,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 +197,18 @@ 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_convert_control *converter,
+ const char *lfn,
+ int lfn_len,
+ char *sfn,
+ int sfn_len)
{
msdos_name_type_t type;
+ int eno = 0;
int i;
+ ssize_t short_filename_length = sfn_len;
+ void *buffer = converter->buffer.data;
+ size_t codepage_name_len = converter->buffer.size;
/*
* Fill with spaces. This is how a short directory entry is padded.
@@ -242,21 +255,46 @@ msdos_long_to_short(const char *lfn, int lfn_len, char* sfn, int sfn_len)
* Is this a short name ?
*/
- type = msdos_name_type (lfn, lfn_len);
+ eno = (*converter->handler->utf8_to_codepage) (
+ converter,
+ (const uint8_t*)&lfn[0],
+ lfn_len,
+ buffer,
+ &codepage_name_len);
+ if (eno == EINVAL)
+ {
+ eno = 0;
+ type = MSDOS_NAME_LONG;
+ }
+ else
+ {
+ type = msdos_name_type (
+ buffer,
+ codepage_name_len);
+ }
- if (type == MSDOS_NAME_INVALID)
+ if (type != MSDOS_NAME_INVALID)
{
+ short_filename_length = msdos_filename_utf8_to_short_name_for_save (
+ converter,
+ (const uint8_t*)lfn,
+ lfn_len,
+ sfn,
+ short_filename_length);
+ if (short_filename_length < 0 ) {
+ type = MSDOS_NAME_INVALID;
+ }
#if MSDOS_L2S_PRINT
- printf ("MSDOS_L2S: INVALID[2]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
+ printf ("MSDOS_L2S: TYPE:%d lfn:'%s' SFN:'%s'\n", type, lfn, sfn);
#endif
- return MSDOS_NAME_INVALID;
}
-
- msdos_filename_unix2dos (lfn, lfn_len, sfn);
-
+ else
+ {
#if MSDOS_L2S_PRINT
- printf ("MSDOS_L2S: TYPE:%d lfn:'%s' SFN:'%s'\n", type, lfn, sfn);
+ printf ("MSDOS_L2S: INVALID[2]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
#endif
+ }
+
return type;
}
@@ -292,13 +330,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->converter,
+ 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 +466,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;
@@ -518,6 +558,7 @@ msdos_get_dotdot_dir_info_cluster_num_and_offset(
char dot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
char dotdot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
uint32_t cl4find = 0;
+ rtems_dosfs_convert_control *converter = fs_info->converter;
/*
* open fat-file corresponded to ".."
@@ -542,8 +583,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(
+ converter,
+ ".", 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 +597,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(
+ converter,
+ "..", 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 +959,348 @@ 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.
+#define MSDOS_FIND_PRINT 0
+static int
+msdos_on_entry_found (
+ msdos_fs_info_t *fs_info,
+ fat_file_fd_t *fat_fd,
+ const uint32_t bts2rd,
+ char *name_dir_entry,
+ char *entry,
+ fat_dir_pos_t *dir_pos,
+ uint32_t *dir_offset,
+ const uint32_t dir_entry,
+ const fat_pos_t *lfn_start
+)
+{
+ 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;
+
+ memcpy(name_dir_entry, entry,
+ MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
+ }
+ }
+
+ return rc;
+}
+
+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;
+
+ return chars_in_entry * MSDOS_NAME_LFN_BYTES_PER_CHAR;
+ }
+ else
+ return ENOMEM;
+}
+
+/* msdos_format_dirent_with_dot --
+ * This routine convert a (short) MSDOS filename as present on disk
+ * (fixed 8+3 characters, filled with blanks, without separator dot)
+ * to a "normal" format, with between 0 and 8 name chars,
+ * a separating dot and up to 3 extension characters
+ * Rules to work:
+ * - copy any (0-8) "name" part characters that are non-blank
+ * - if an extension exists, append a dot
+ * - copy any (0-3) non-blank extension characters
+ * - append a '\0' (dont count it for the rturn code
*
* 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
+ * dst: pointer to destination char array (must be big enough)
+ * src: pointer to source characters
*
- * RETURNS:
- * RC_OK on success, or error code if error occured (errno set
- * appropriately)
*
+ * RETURNS:
+ * the number of bytes (without trailing '\0'(written to destination
*/
-#define MSDOS_FIND_PRINT 0
-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,
- int name_len,
- msdos_name_type_t name_type,
- fat_dir_pos_t *dir_pos,
- char *name_dir_entry
- )
+ssize_t
+msdos_format_dirent_with_dot(char *dst,const char *src)
{
- 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);
+ ssize_t len;
+ int i;
+ const char *src_tmp;
+
+ /*
+ * find last non-blank character of base name
+ */
+ for (i = MSDOS_SHORT_BASE_LEN, src_tmp = src + MSDOS_SHORT_BASE_LEN - 1;
+ i > 0 && *src_tmp == ' ';
+ --i,--src_tmp)
+ {};
+ /*
+ * copy base name to destination
+ */
+ src_tmp = src;
+ len = i;
+ while (i-- > 0) {
+ *dst++ = tolower((unsigned char)(*src_tmp++));
+ }
+ /*
+ * find last non-blank character of extension
+ */
+ for (i = MSDOS_SHORT_EXT_LEN,
+ src_tmp = src + MSDOS_SHORT_BASE_LEN+MSDOS_SHORT_EXT_LEN-1;
+ i > 0 && *src_tmp == ' ';
+ --i, --src_tmp)
+ {};
+ /*
+ * extension is not empty
+ */
+ if (i > 0) {
+ *dst++ = '.'; /* append dot */
+ ++len; /* dot */
+ src_tmp = src + MSDOS_SHORT_BASE_LEN;
+ while (i-- > 0) {
+ *dst++ = tolower((unsigned char)(*src_tmp++));
+ ++len;
+ }
+ }
+ *dst = '\0'; /* terminate string */
- fat_dir_pos_init(dir_pos);
+ return len;
+}
- lfn_start.cln = lfn_start.ofs = FAT_FILE_SHORT_NAME;
+static ssize_t
+msdos_long_entry_to_utf8_name (
+ rtems_dosfs_convert_control *converter,
+ const char *entry,
+ const bool is_first_entry,
+ uint8_t *entry_utf8_buf,
+ const size_t buf_size)
+{
+ ssize_t retval = 0;
+ int eno = 0;
+ size_t bytes_in_utf8 = buf_size;
+ size_t bytes_in_buf;
+ 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;
+ eno = (*converter->handler->utf16_to_utf8) (
+ converter,
+ &entry_string[0],
+ bytes_in_buf,
+ &entry_utf8_buf[0],
+ &bytes_in_utf8);
+ if ( eno == 0 ) {
+ retval = bytes_in_utf8;
+ }
+ }
- /*
- * 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;
+ if (eno != 0) {
+ retval = -1;
+ errno = eno;
+ }
- 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;
+ return retval;
+}
- entries_per_block = bts2rd / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
+static ssize_t msdos_short_entry_to_utf8_name (
+ rtems_dosfs_convert_control *converter,
+ const char *entry,
+ uint8_t *buf,
+ const size_t buf_size)
+{
+ char char_buf[MSDOS_NAME_MAX_WITH_DOT];
+ int eno = 0;
+ size_t bytes_converted = buf_size;
+ ssize_t bytes_written = msdos_format_dirent_with_dot(char_buf, entry);
+
+ if (bytes_written > 0) {
+ if (char_buf[0] == 0x05)
+ char_buf[0] = 0xE5;
+
+ eno = (*converter->handler->codepage_to_utf8) (
+ converter,
+ char_buf,
+ bytes_written,
+ buf,
+ &bytes_converted);
+ if (eno == 0)
+ bytes_written = bytes_converted;
+ } else {
+ eno = EINVAL;
+ }
+
+ if (eno != 0) {
+ bytes_written = -1;
+ errno = eno;
+ }
+
+ return bytes_written;
+}
-#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);
+static ssize_t
+msdos_compare_entry_against_filename (
+ rtems_dosfs_convert_control *converter,
+ const uint8_t *entry,
+ const size_t entry_size,
+ const uint8_t *filename,
+ const size_t filename_size_remaining,
+ bool *is_matching)
+{
+ ssize_t size_remaining = filename_size_remaining;
+ int eno = 0;
+ uint8_t entry_normalized[( MSDOS_LFN_LEN_PER_ENTRY + 1 ) * MSDOS_NAME_LFN_BYTES_PER_CHAR * MSDOS_NAME_MAX_UTF8_BYTES_PER_CHAR];
+ size_t bytes_in_entry_normalized = sizeof ( entry_normalized );
+
+ eno = (*converter->handler->utf8_normalize_and_fold) (
+ converter,
+ &entry[0],
+ entry_size,
+ &entry_normalized[0],
+ &bytes_in_entry_normalized);
+ if (eno == 0) {
+#if MSDOS_FIND_PRINT > 1
+ printf ( "MSFS:[6] entry_normalized:%s"
+ "name:%s\n",
+ entry,
+ filename );
#endif
+ if (bytes_in_entry_normalized <= size_remaining) {
+ size_remaining = size_remaining - bytes_in_entry_normalized;
+ if (0 == memcmp ( &entry_normalized[0],
+ &filename[size_remaining],
+ bytes_in_entry_normalized)) {
+ *is_matching = true;
+ } else {
+ *is_matching = false;
+ size_remaining = filename_size_remaining;
+ }
+
+ }
+ else {
+ *is_matching = false;
+ }
+ }
+
+ if (eno != 0) {
+ size_remaining = -1;
+ errno = eno;
+ }
+
+ return size_remaining;
+}
+
+static int
+msdos_find_file_in_directory (
+ const uint8_t *filename_converted,
+ const size_t name_len_for_compare,
+ const size_t name_len_for_save,
+ 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 entry_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_normalized[(MSDOS_LFN_LEN_PER_ENTRY + 1 ) * MSDOS_NAME_LFN_BYTES_PER_CHAR * MSDOS_NAME_MAX_UTF8_BYTES_PER_CHAR/*MSDOS_ENTRY_LFN_UTF8_BYTES*/];
+ size_t bytes_in_entry;
+ bool filename_matched = false;
+ ssize_t filename_size_remaining = name_len_for_compare;
+ rtems_dosfs_convert_control *converter = fs_info->converter;
+
/*
* 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 +1308,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 +1318,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 +1334,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 +1342,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 +1358,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 +1381,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 +1392,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;
+/* int o;*/
#if MSDOS_FIND_PRINT
printf ("MSFS:[4.2] lfn:%c entry:%i checksum:%i\n",
lfn_start.cln == FAT_FILE_SHORT_NAME ? 'f' : 't',
@@ -1131,7 +1405,7 @@ int msdos_find_name_in_fat_file(
*/
if (lfn_start.cln == FAT_FILE_SHORT_NAME)
{
- lfn_matched = false;
+ entry_matched = false;
/*
* The first entry must have the last long entry
@@ -1148,21 +1422,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,64 +1456,40 @@ 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--;
- if (((o + i) >= name_len) || (*p != name[o + i]))
- {
+ bytes_in_entry = msdos_long_entry_to_utf8_name (
+ converter,
+ entry,
+ (lfn_entry + 1) == fat_entries,
+ &entry_utf8_normalized[0],
+ sizeof (entry_utf8_normalized));
+ if (bytes_in_entry > 0) {
+ filename_size_remaining = msdos_compare_entry_against_filename (
+ converter,
+ &entry_utf8_normalized[0],
+ bytes_in_entry,
+ &filename_converted[0],
+ filename_size_remaining,
+ &entry_matched);
+
+ if (filename_size_remaining < 0
+ || (! entry_matched)) {
+ filename_size_remaining = name_len_for_compare;
lfn_start.cln = FAT_FILE_SHORT_NAME;
- break;
- }
-
- switch (i)
- {
- case 4:
- p += 5;
- break;
- case 10:
- p += 4;
- break;
- default:
- p += 2;
- break;
}
+ } else {
+ lfn_start.cln = FAT_FILE_SHORT_NAME;
+ entry_matched = false;
}
-
- 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
- printf ("MSFS:[9.1] SFN entry, lfn_matched:%i\n", lfn_matched);
+ printf ("MSFS:[9.1] SFN entry, entry_matched:%i\n", entry_matched);
#endif
/*
* SFN entry found.
@@ -1249,7 +1499,7 @@ int msdos_find_name_in_fat_file(
* correct. If this is the case return the short file
* name entry.
*/
- if (lfn_matched)
+ if (entry_matched)
{
uint8_t cs = 0;
uint8_t* p = (uint8_t*) MSDOS_DIR_NAME(entry);
@@ -1259,81 +1509,117 @@ int msdos_find_name_in_fat_file(
cs = ((cs & 1) ? 0x80 : 0) + (cs >> 1) + *p;
if (lfn_entry || (lfn_checksum != cs))
- lfn_matched = false;
-#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
- }
+ entry_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 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");
+ printf ("MSFS:[9.2] checksum, entry_matched:%i, lfn_entry:%i, lfn_checksum:%02x/%02x\n",
+ entry_matched, lfn_entry, lfn_checksum, cs);
#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 + 1;
+ bytes_in_entry = msdos_short_entry_to_utf8_name (
+ converter,
+ MSDOS_DIR_NAME (entry),
+ &entry_utf8_normalized[0],
+ bytes_in_entry);
+ if (bytes_in_entry > 0) {
+ filename_size_remaining = msdos_compare_entry_against_filename (
+ converter,
+ &entry_utf8_normalized[0],
+ bytes_in_entry,
+ &filename_converted[0],
+ name_len_for_compare,
+ &entry_matched);
+ if (entry_matched && filename_size_remaining == 0) {
+ 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;
+ entry_matched = false;
+ filename_size_remaining = name_len_for_compare;
+ }
+ } else {
+ lfn_start.cln = FAT_FILE_SHORT_NAME;
+ entry_matched = false;
+ filename_size_remaining = name_len_for_compare;
}
-
- 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;
}
-
- 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 +1627,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 +1677,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 +1751,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 +1788,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 +1800,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 +1818,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 +1836,187 @@ 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;
+ ssize_t name_len_for_save;
+ ssize_t name_len_for_compare;
+ 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;
+ rtems_dosfs_convert_control *converter = fs_info->converter;
+ void *buffer = converter->buffer.data;
+ size_t buffer_size = converter->buffer.size;
+
+ assert(name_utf8_len > 0);
+
+ fat_dir_pos_init(dir_pos);
+
- return 0;
+
+ 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:
+ name_len_for_compare = msdos_filename_utf8_to_short_name_for_compare (
+ converter,
+ name_utf8,
+ name_utf8_len,
+ buffer,
+ MSDOS_SHORT_NAME_LEN);
+ if (name_len_for_compare > 0) {
+ fat_entries = 0;
+ }
+ else
+ retval = -1;
+ break;
+ case MSDOS_NAME_LONG:
+ name_len_for_save = msdos_filename_utf8_to_long_name_for_save (
+ converter,
+ name_utf8,
+ name_utf8_len,
+ buffer,
+ buffer_size);
+ if (name_len_for_save > 0) {
+ fat_entries = (name_len_for_save -1
+ + (MSDOS_LFN_LEN_PER_ENTRY * MSDOS_NAME_LFN_BYTES_PER_CHAR)) / (MSDOS_LFN_LEN_PER_ENTRY * MSDOS_NAME_LFN_BYTES_PER_CHAR);
+ name_len_for_compare = msdos_filename_utf8_to_long_name_for_compare (
+ converter,
+ name_utf8,
+ name_utf8_len,
+ buffer,
+ buffer_size);
+ if (0 >= name_len_for_compare) {
+ retval = -1;
+ }
+ }
+ else
+ retval = -1;
+ break;
+ case MSDOS_NAME_INVALID:
+ default:
+ errno = EINVAL;
+ retval = -1;
+ break;
+ }
+ if (retval == RC_OK) {
+ /* See if the file/directory does already exist */
+ retval = msdos_find_file_in_directory (
+ buffer,
+ name_len_for_compare,
+ name_len_for_save,
+ 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:
+ name_len_for_save = msdos_filename_utf8_to_short_name_for_save (
+ converter,
+ name_utf8,
+ name_utf8_len,
+ buffer,
+ MSDOS_SHORT_NAME_LEN);
+ if (name_len_for_save > 0 ) {
+ fat_entries = 0;
+ }
+ else
+ retval = -1;
+ break;
+ case MSDOS_NAME_LONG:
+ name_len_for_save = msdos_filename_utf8_to_long_name_for_save (
+ converter,
+ name_utf8,
+ name_utf8_len,
+ buffer,
+ buffer_size);
+ if (name_len_for_save > 0) {
+ fat_entries = (name_len_for_save -1
+ + (MSDOS_LFN_LEN_PER_ENTRY * MSDOS_NAME_LFN_BYTES_PER_CHAR)) / (MSDOS_LFN_LEN_PER_ENTRY * MSDOS_NAME_LFN_BYTES_PER_CHAR);
+
+ }
+ else
+ retval = -1;
+ break;
+ case MSDOS_NAME_INVALID:
+ default:
+ errno = EINVAL;
+ retval = -1;
+ break;
+ }
+ retval = msdos_add_file (
+ buffer,
+ 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 retval;
}
+
/* msdos_find_node_by_cluster_num_in_fat_file --
* Find node with specified number of cluster in fat-file.
*
--
1.7.10.4
More information about the devel
mailing list