diff options
Diffstat (limited to 'cpukit/libfs/src/dosfs/msdos_misc.c')
-rw-r--r-- | cpukit/libfs/src/dosfs/msdos_misc.c | 1028 |
1 files changed, 739 insertions, 289 deletions
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. * |