summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cpukit/libfs/Makefile.am1
-rw-r--r--cpukit/libfs/src/dosfs/dosfs.h197
-rw-r--r--cpukit/libfs/src/dosfs/msdos.h70
-rw-r--r--cpukit/libfs/src/dosfs/msdos_conv.c619
-rw-r--r--cpukit/libfs/src/dosfs/msdos_conv_default.c188
-rw-r--r--cpukit/libfs/src/dosfs/msdos_create.c3
-rw-r--r--cpukit/libfs/src/dosfs/msdos_dir.c191
-rw-r--r--cpukit/libfs/src/dosfs/msdos_fsunmount.c4
-rw-r--r--cpukit/libfs/src/dosfs/msdos_init.c35
-rw-r--r--cpukit/libfs/src/dosfs/msdos_initsupp.c5
-rw-r--r--cpukit/libfs/src/dosfs/msdos_misc.c1028
11 files changed, 1781 insertions, 560 deletions
diff --git a/cpukit/libfs/Makefile.am b/cpukit/libfs/Makefile.am
index 0828da8515..e06c8bd381 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 ea650b2f7e..f1c3d87d51 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@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 Destroys 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 78eda9b226..6da47cc5e2 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@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 7549c42e56..7e688ef3ce 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 0000000000..baf83208f5
--- /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@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 886dd404cc..e836513df8 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 fd9ca40461..253203cc2d 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@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 90a8073bf8..57335e2969 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 e82e3f52d2..bcc5f9f278 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 08441869b4..02b1baf657 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 88f0b94bbd..56b58c85f7 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@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.
*