diff options
Diffstat (limited to 'cpukit')
-rw-r--r-- | cpukit/libfs/Makefile.am | 1 | ||||
-rw-r--r-- | cpukit/libfs/src/dosfs/dosfs.h | 197 | ||||
-rw-r--r-- | cpukit/libfs/src/dosfs/msdos.h | 70 | ||||
-rw-r--r-- | cpukit/libfs/src/dosfs/msdos_conv.c | 619 | ||||
-rw-r--r-- | cpukit/libfs/src/dosfs/msdos_conv_default.c | 188 | ||||
-rw-r--r-- | cpukit/libfs/src/dosfs/msdos_create.c | 3 | ||||
-rw-r--r-- | cpukit/libfs/src/dosfs/msdos_dir.c | 191 | ||||
-rw-r--r-- | cpukit/libfs/src/dosfs/msdos_fsunmount.c | 4 | ||||
-rw-r--r-- | cpukit/libfs/src/dosfs/msdos_init.c | 35 | ||||
-rw-r--r-- | cpukit/libfs/src/dosfs/msdos_initsupp.c | 5 | ||||
-rw-r--r-- | cpukit/libfs/src/dosfs/msdos_misc.c | 1028 |
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. * |