summaryrefslogtreecommitdiffstats
path: root/cpukit/libfs/src/dosfs/msdos_dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libfs/src/dosfs/msdos_dir.c')
-rw-r--r--cpukit/libfs/src/dosfs/msdos_dir.c191
1 files changed, 74 insertions, 117 deletions
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;
+ }
}
}