summaryrefslogtreecommitdiffstats
path: root/cpukit/libfs/src/dosfs/msdos_misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libfs/src/dosfs/msdos_misc.c')
-rw-r--r--cpukit/libfs/src/dosfs/msdos_misc.c1731
1 files changed, 1731 insertions, 0 deletions
diff --git a/cpukit/libfs/src/dosfs/msdos_misc.c b/cpukit/libfs/src/dosfs/msdos_misc.c
new file mode 100644
index 0000000000..d49b89048b
--- /dev/null
+++ b/cpukit/libfs/src/dosfs/msdos_misc.c
@@ -0,0 +1,1731 @@
+/*
+ * Miscellaneous routines implementation for MSDOS filesystem
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
+ *
+ * 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.
+ *
+ * @(#) $Id$
+ */
+
+#define MSDOS_TRACE 1
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <rtems/libio_.h>
+
+#include "fat.h"
+#include "fat_fat_operations.h"
+#include "fat_file.h"
+
+#include "msdos.h"
+
+
+#include <stdio.h>
+
+/*
+ * External strings. Saves spave this way.
+ */
+const char *const MSDOS_DOT_NAME = ". ";
+const char *const MSDOS_DOTDOT_NAME = ".. ";
+
+/* msdos_is_valid_name_char --
+ * Routine to check the character in a file or directory name.
+ * The characters support in the short file name are letters,
+ * digits, or characters with code points values greater than
+ * 127 (not sure what this last is) plus the following special
+ * characters "$%'-_@~`!(){}^#&". The must be uppercase.
+ *
+ * The following 6 characters are allowed in a long names,
+ * " +,;=[]" including a space and lower case letters.
+ *
+ * PARAMETERS:
+ * ch - character to check.
+ *
+ * RETURNS:
+ * MSDOS_NAME_INVALID - Not valid in a long or short name.
+ * MSDOS_NAME_SHORT - Valid in a short name or long name.
+ * MSDOS_NAME_LONG - Valid in a long name only.
+ *
+ */
+static msdos_name_type_t
+msdos_is_valid_name_char(const char ch)
+{
+ if (strchr(" +,;=[]", ch) != NULL)
+ return MSDOS_NAME_LONG;
+
+ if ((ch == '.') || isalnum((unsigned char)ch) ||
+ (strchr("$%'-_@~`!(){}^#&", ch) != NULL))
+ return MSDOS_NAME_SHORT;
+
+ return MSDOS_NAME_INVALID;
+}
+
+/* msdos_short_hex_number --
+ * Routine to set the hex number in the SFN.
+ *
+ * PARAMETERS:
+ * name - name to change
+ * num - number to set
+ *
+ * RETURNS:
+ * nothing
+ *
+ */
+static void
+msdos_short_name_hex(char* sfn, int num)
+{
+ static const char* hex = "0123456789ABCDEF";
+ char* c = MSDOS_DIR_NAME(sfn);
+ int i;
+ for (i = 0; i < 2; i++, c++)
+ if ((*c == ' ') || (*c == '.'))
+ *c = '_';
+ for (i = 0; i < 4; i++, c++)
+ *c = hex[(num >> ((3 - i) * 4)) & 0xf];
+ *c++ = '~';
+ *c++ = '1';
+}
+
+/* msdos_name_type --
+ * Routine the type of file name.
+ *
+ * PARAMETERS:
+ * name - name to check
+ *
+ * RETURNS:
+ * true the name is long, else the name is short.
+ *
+ */
+#define MSDOS_NAME_TYPE_PRINT 0
+static msdos_name_type_t
+msdos_name_type(const char *name, int name_len)
+{
+ bool lowercase = false;
+ bool uppercase = false;
+ int dot_at = -1;
+ int count = 0;
+
+ while (*name && (count < name_len))
+ {
+ bool is_dot = *name == '.';
+ msdos_name_type_t type = msdos_is_valid_name_char(*name);
+
+#if MSDOS_NAME_TYPE_PRINT
+ printf ("MSDOS_NAME_TYPE: c:%02x type:%d\n", *name, type);
+#endif
+
+ if ((type == MSDOS_NAME_INVALID) || (type == MSDOS_NAME_LONG))
+ return type;
+
+ if (dot_at >= 0)
+ {
+ if (is_dot || ((count - dot_at) > 3))
+ {
+#if MSDOS_NAME_TYPE_PRINT
+ printf ("MSDOS_NAME_TYPE: LONG[1]: is_dot:%d, at:%d cnt\n",
+ is_dot, dot_at, count);
+#endif
+ return MSDOS_NAME_LONG;
+ }
+ }
+ else
+ {
+ if (count == 8 && !is_dot)
+ {
+#if MSDOS_NAME_TYPE_PRINT
+ printf ("MSDOS_NAME_TYPE: LONG[2]: is_dot:%d, at:%d cnt\n",
+ is_dot, dot_at, count);
+#endif
+ return MSDOS_NAME_LONG;
+ }
+ }
+
+ if (is_dot)
+ dot_at = count;
+ else if ((*name >= 'A') && (*name <= 'Z'))
+ uppercase = true;
+ else if ((*name >= 'a') && (*name <= 'z'))
+ lowercase = true;
+
+ count++;
+ name++;
+ }
+
+ if (lowercase && uppercase)
+ {
+#if MSDOS_NAME_TYPE_PRINT
+ printf ("MSDOS_NAME_TYPE: LONG[3]\n");
+#endif
+ return MSDOS_NAME_LONG;
+ }
+
+#if MSDOS_NAME_TYPE_PRINT
+ printf ("MSDOS_NAME_TYPE: SHORT[1]\n");
+#endif
+ return MSDOS_NAME_SHORT;
+}
+
+/* msdos_long_to_short --
+ * Routine to creates a short name from a long. Start the end of the
+ *
+ * PARAMETERS:
+ * name - name to check
+ *
+ * RETURNS:
+ * true the name is long, else the name is short.
+ *
+ */
+#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_name_type_t type;
+ int i;
+
+ /*
+ * Fill with spaces. This is how a short directory entry is padded.
+ */
+ memset (sfn, ' ', sfn_len);
+
+ /*
+ * Handle '.' and '..' specially.
+ */
+ if ((lfn[0] == '.') && (lfn_len == 1))
+ {
+ sfn[0] = '.';
+#if MSDOS_L2S_PRINT
+ printf ("MSDOS_L2S: SHORT[1]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
+#endif
+ return MSDOS_NAME_SHORT;
+ }
+
+ if ((lfn[0] == '.') && (lfn[1] == '.') && (lfn_len == 2))
+ {
+ sfn[0] = sfn[1] = '.';
+#if MSDOS_L2S_PRINT
+ printf ("MSDOS_L2S: SHORT[2]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
+#endif
+ return MSDOS_NAME_SHORT;
+ }
+
+ /*
+ * Filenames with only blanks and dots are not allowed!
+ */
+ for (i = 0; i < lfn_len; i++)
+ if ((lfn[i] != ' ') && (lfn[i] != '.'))
+ break;
+
+ if (i == lfn_len)
+ {
+#if MSDOS_L2S_PRINT
+ printf ("MSDOS_L2S: INVALID[1]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
+#endif
+ return MSDOS_NAME_INVALID;
+ }
+
+ /*
+ * Is this a short name ?
+ */
+
+ type = msdos_name_type (lfn, lfn_len);
+
+ if (type == MSDOS_NAME_INVALID)
+ {
+#if MSDOS_L2S_PRINT
+ printf ("MSDOS_L2S: INVALID[2]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
+#endif
+ return MSDOS_NAME_INVALID;
+ }
+
+ msdos_filename_unix2dos (lfn, lfn_len, sfn);
+
+#if MSDOS_L2S_PRINT
+ printf ("MSDOS_L2S: TYPE:%d lfn:'%s' SFN:'%s'\n", type, lfn, sfn);
+#endif
+ return type;
+}
+
+/* msdos_get_token --
+ * Routine to get a token (name or separator) from the path.
+ *
+ * PARAMETERS:
+ * path - path to get token from
+ * ret_token - returned token
+ * token_len - length of returned token
+ *
+ * RETURNS:
+ * token type, token and token length
+ *
+ */
+msdos_token_types_t
+msdos_get_token(const char *path,
+ int pathlen,
+ const char **ret_token,
+ int *ret_token_len)
+{
+ msdos_token_types_t type = MSDOS_NAME;
+ int i = 0;
+
+ *ret_token = NULL;
+ *ret_token_len = 0;
+
+ if (pathlen == 0)
+ return MSDOS_NO_MORE_PATH;
+
+ /*
+ * Check for a separator.
+ */
+ while (!msdos_is_separator(path[i]) && (i < pathlen))
+ {
+ if ( !msdos_is_valid_name_char(path[i]) )
+ return MSDOS_INVALID_TOKEN;
+ ++i;
+ if ( i == MSDOS_NAME_MAX_LFN_WITH_DOT )
+ return MSDOS_INVALID_TOKEN;
+ }
+
+ *ret_token = path;
+
+ /*
+ * If it is just a separator then it is the current dir.
+ */
+ if ( i == 0 )
+ {
+ if ( (*path != '\0') && pathlen )
+ {
+ i++;
+ type = MSDOS_CURRENT_DIR;
+ }
+ else
+ type = MSDOS_NO_MORE_PATH;
+ }
+
+ /*
+ * Set the token and token_len to the token start and length.
+ */
+ *ret_token_len = i;
+
+ /*
+ * If we copied something that was not a seperator see if
+ * it was a special name.
+ */
+ if ( type == MSDOS_NAME )
+ {
+ if ((i == 2) && ((*ret_token)[0] == '.') && ((*ret_token)[1] == '.'))
+ {
+ type = MSDOS_UP_DIR;
+ return type;
+ }
+
+ if ((i == 1) && ((*ret_token)[0] == '.'))
+ {
+ type = MSDOS_CURRENT_DIR;
+ return type;
+ }
+ }
+
+ return type;
+}
+
+
+/* msdos_find_name --
+ * Find the node which correspondes to the name, open fat-file which
+ * correspondes to the found node and close fat-file which correspondes
+ * to the node we searched in.
+ *
+ * PARAMETERS:
+ * parent_loc - parent node description
+ * name - name to find
+ *
+ * RETURNS:
+ * RC_OK and updated 'parent_loc' on success, or -1 if error
+ * occured (errno set apropriately)
+ *
+ */
+int
+msdos_find_name(
+ rtems_filesystem_location_info_t *parent_loc,
+ const char *name,
+ int name_len
+ )
+{
+ int rc = RC_OK;
+ msdos_fs_info_t *fs_info = parent_loc->mt_entry->fs_info;
+ fat_file_fd_t *fat_fd = NULL;
+ msdos_name_type_t name_type;
+ fat_dir_pos_t dir_pos;
+ unsigned short time_val = 0;
+ unsigned short date = 0;
+ char node_entry[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
+
+ 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);
+
+ /*
+ * find the node which correspondes to the name in the directory pointed by
+ * 'parent_loc'
+ */
+ rc = msdos_get_name_node(parent_loc, false, name, name_len, name_type,
+ &dir_pos, node_entry);
+ if (rc != RC_OK)
+ return rc;
+
+ if (((*MSDOS_DIR_ATTR(node_entry)) & MSDOS_ATTR_VOLUME_ID) ||
+ ((*MSDOS_DIR_ATTR(node_entry) & MSDOS_ATTR_LFN_MASK) == MSDOS_ATTR_LFN))
+ return MSDOS_NAME_NOT_FOUND_ERR;
+
+ /* open fat-file corresponded to the found node */
+ rc = fat_file_open(parent_loc->mt_entry, &dir_pos, &fat_fd);
+ if (rc != RC_OK)
+ return rc;
+
+ fat_fd->dir_pos = dir_pos;
+
+ /*
+ * I don't like this if, but: we should do it, or should write new file
+ * size and first cluster num to the disk after each write operation
+ * (even if one byte is written - that is TOO slow) because
+ * otherwise real values of these fields stored in fat-file descriptor
+ * may be accidentally rewritten with wrong values stored on the disk
+ */
+ if (fat_fd->links_num == 1)
+ {
+ fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(node_entry);
+
+ time_val = *MSDOS_DIR_WRITE_TIME(node_entry);
+ date = *MSDOS_DIR_WRITE_DATE(node_entry);
+
+ fat_fd->mtime = msdos_date_dos2unix(CF_LE_W(date), CF_LE_W(time_val));
+
+ if ((*MSDOS_DIR_ATTR(node_entry)) & MSDOS_ATTR_DIRECTORY)
+ {
+ fat_fd->fat_file_type = FAT_DIRECTORY;
+ fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT;
+
+ rc = fat_file_size(parent_loc->mt_entry, fat_fd);
+ if (rc != RC_OK)
+ {
+ fat_file_close(parent_loc->mt_entry, fat_fd);
+ return rc;
+ }
+ }
+ else
+ {
+ fat_fd->fat_file_size = CF_LE_L(*MSDOS_DIR_FILE_SIZE(node_entry));
+ fat_fd->fat_file_type = FAT_FILE;
+ fat_fd->size_limit = MSDOS_MAX_FILE_SIZE;
+ }
+
+ /* these data is not actual for zero-length fat-file */
+ fat_fd->map.file_cln = 0;
+ fat_fd->map.disk_cln = fat_fd->cln;
+
+ if ((fat_fd->fat_file_size != 0) &&
+ (fat_fd->fat_file_size <= fs_info->fat.vol.bpc))
+ {
+ fat_fd->map.last_cln = fat_fd->cln;
+ }
+ else
+ {
+ fat_fd->map.last_cln = FAT_UNDEFINED_VALUE;
+ }
+ }
+
+ /* close fat-file corresponded to the node we searched in */
+ rc = fat_file_close(parent_loc->mt_entry, parent_loc->node_access);
+ if (rc != RC_OK)
+ {
+ fat_file_close(parent_loc->mt_entry, fat_fd);
+ return rc;
+ }
+
+ /* update node_info_ptr field */
+ parent_loc->node_access = fat_fd;
+
+ return rc;
+}
+
+/* msdos_get_name_node --
+ * This routine is used in two ways: for a new node creation (a) or for
+ * search the node which correspondes to the name parameter (b).
+ * In case (a) 'name' should be set up to NULL and 'name_dir_entry' should
+ * point to initialized 32 bytes structure described a new node.
+ * In case (b) 'name' should contain a valid string.
+ *
+ * (a): reading fat-file which correspondes to directory we are going to
+ * create node in. If free slot is found write contents of
+ * 'name_dir_entry' into it. If reach end of fat-file and no free
+ * slot found, write 32 bytes to the end of fat-file.
+ *
+ * (b): reading fat-file which correspondes to directory and trying to
+ * find slot with the name field == 'name' parameter
+ *
+ *
+ * PARAMETERS:
+ * parent_loc - node description to create node in or to find name in
+ * name - NULL or name to find
+ * paux - identify a node location on the disk -
+ * cluster num and offset inside the cluster
+ * short_dir_entry - node to create/placeholder for found node (IN/OUT)
+ *
+ * RETURNS:
+ * RC_OK, filled aux_struct_ptr and name_dir_entry on success, or -1 if
+ * error occured (errno set apropriately)
+ *
+ */
+int
+msdos_get_name_node(
+ rtems_filesystem_location_info_t *parent_loc,
+ 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
+ )
+{
+ int rc = RC_OK;
+ fat_file_fd_t *fat_fd = parent_loc->node_access;
+ uint32_t dotdot_cln = 0;
+
+ /* 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,
+ dir_pos, name_dir_entry);
+ if ((rc != RC_OK) && (rc != MSDOS_NAME_NOT_FOUND_ERR))
+ return rc;
+
+ if (!create_node)
+ {
+ /* if we search for valid name and name not found -> return */
+ if (rc == MSDOS_NAME_NOT_FOUND_ERR)
+ return rc;
+
+ /*
+ * if we have deal with ".." - it is a special case :(((
+ *
+ * Really, we should return cluster num and offset not of ".." slot, but
+ * slot which correspondes to real directory name.
+ */
+ if (rc == RC_OK)
+ {
+ if (strncmp(name, "..", 2) == 0)
+ {
+ dotdot_cln = MSDOS_EXTRACT_CLUSTER_NUM((name_dir_entry));
+
+ /* are we right under root dir ? */
+ if (dotdot_cln == 0)
+ {
+ /*
+ * we can relax about first_char field - it never should be
+ * used for root dir
+ */
+ fat_dir_pos_init(dir_pos);
+ dir_pos->sname.cln = FAT_ROOTDIR_CLUSTER_NUM;
+ }
+ else
+ {
+ rc =
+ msdos_get_dotdot_dir_info_cluster_num_and_offset(parent_loc->mt_entry,
+ dotdot_cln,
+ dir_pos,
+ name_dir_entry);
+ if (rc != RC_OK)
+ return rc;
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+ * msdos_get_dotdot_dir_info_cluster_num_and_offset
+ *
+ * Unfortunately, in general, we cann't work here in fat-file ideologic
+ * (open fat_file "..", get ".." and ".", open "..", find an entry ...)
+ * because if we open
+ * fat-file ".." it may happend that we have two different fat-file
+ * descriptors ( for real name of directory and ".." name ) for a single
+ * file ( cluster num of both pointers to the same cluster )
+ * But...we do it because we protected by semaphore
+ *
+ */
+
+/* msdos_get_dotdot_dir_info_cluster_num_and_offset --
+ * Get cluster num and offset not of ".." slot, but slot which correspondes
+ * to real directory name.
+ *
+ * PARAMETERS:
+ * mt_entry - mount table entry
+ * cln - data cluster num extracted drom ".." slot
+ * paux - identify a node location on the disk -
+ * number of cluster and offset inside the cluster
+ * dir_entry - placeholder for found node
+ *
+ * RETURNS:
+ * RC_OK, filled 'paux' and 'dir_entry' on success, or -1 if error occured
+ * (errno set apropriately)
+ *
+ */
+int
+msdos_get_dotdot_dir_info_cluster_num_and_offset(
+ rtems_filesystem_mount_table_entry_t *mt_entry,
+ uint32_t cln,
+ fat_dir_pos_t *dir_pos,
+ char *dir_entry
+ )
+{
+ int rc = RC_OK;
+ msdos_fs_info_t *fs_info = mt_entry->fs_info;
+ fat_file_fd_t *fat_fd = NULL;
+ char dot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
+ char dotdot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
+ uint32_t cl4find = 0;
+
+ /*
+ * open fat-file corresponded to ".."
+ */
+ rc = fat_file_open(mt_entry, dir_pos, &fat_fd);
+ if (rc != RC_OK)
+ return rc;
+
+ fat_fd->cln = cln;
+ fat_fd->fat_file_type = FAT_DIRECTORY;
+ fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT;
+
+ fat_fd->map.file_cln = 0;
+ fat_fd->map.disk_cln = fat_fd->cln;
+
+ rc = fat_file_size(mt_entry, fat_fd);
+ if (rc != RC_OK)
+ {
+ fat_file_close(mt_entry, fat_fd);
+ return rc;
+ }
+
+ /* 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_NAME_SHORT, dir_pos, dot_node);
+
+ if (rc != RC_OK)
+ {
+ fat_file_close(mt_entry, fat_fd);
+ return rc;
+ }
+
+ /* 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_NAME_SHORT, dir_pos,
+ dotdot_node);
+
+ if (rc != RC_OK)
+ {
+ fat_file_close(mt_entry, fat_fd);
+ return rc;
+ }
+
+ cl4find = MSDOS_EXTRACT_CLUSTER_NUM(dot_node);
+
+ /* close fat-file corresponded to ".." directory */
+ rc = fat_file_close(mt_entry, fat_fd);
+ if ( rc != RC_OK )
+ return rc;
+
+ if ( (MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node)) == 0)
+ {
+ /*
+ * we handle root dir for all FAT types in the same way with the
+ * ordinary directories ( through fat_file_* calls )
+ */
+ fat_dir_pos_init(dir_pos);
+ dir_pos->sname.cln = FAT_ROOTDIR_CLUSTER_NUM;
+ }
+
+ /* open fat-file corresponded to second ".." */
+ rc = fat_file_open(mt_entry, dir_pos, &fat_fd);
+ if (rc != RC_OK)
+ return rc;
+
+ if ((MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node)) == 0)
+ fat_fd->cln = fs_info->fat.vol.rdir_cl;
+ else
+ fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node);
+
+ fat_fd->fat_file_type = FAT_DIRECTORY;
+ fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT;
+
+ fat_fd->map.file_cln = 0;
+ fat_fd->map.disk_cln = fat_fd->cln;
+
+ rc = fat_file_size(mt_entry, fat_fd);
+ if (rc != RC_OK)
+ {
+ fat_file_close(mt_entry, fat_fd);
+ return rc;
+ }
+
+ /* in this directory find slot with specified cluster num */
+ rc = msdos_find_node_by_cluster_num_in_fat_file(mt_entry, fat_fd, cl4find,
+ dir_pos, dir_entry);
+ if (rc != RC_OK)
+ {
+ fat_file_close(mt_entry, fat_fd);
+ return rc;
+ }
+ rc = fat_file_close(mt_entry, fat_fd);
+ return rc;
+}
+
+
+/* msdos_set_dir_wrt_time_and_date --
+ * Write last write date and time for a file to the disk (to corresponded
+ * 32bytes node)
+ *
+ * PARAMETERS:
+ * mt_entry - mount table entry
+ * fat_fd - fat-file descriptor
+ *
+ * RETURNS:
+ * RC_OK on success, or -1 if error occured (errno set apropriately).
+ *
+ */
+int
+msdos_set_dir_wrt_time_and_date(
+ rtems_filesystem_mount_table_entry_t *mt_entry,
+ fat_file_fd_t *fat_fd
+ )
+{
+ ssize_t ret1 = 0, ret2 = 0, ret3 = 0;
+ msdos_fs_info_t *fs_info = mt_entry->fs_info;
+ uint16_t time_val;
+ uint16_t date;
+ uint32_t sec = 0;
+ uint32_t byte = 0;
+
+ msdos_date_unix2dos(fat_fd->mtime, &date, &time_val);
+
+ /*
+ * calculate input for _fat_block_write: convert (cluster num, offset) to
+ * (sector num, new offset)
+ */
+ sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->dir_pos.sname.cln);
+ sec += (fat_fd->dir_pos.sname.ofs >> fs_info->fat.vol.sec_log2);
+ /* byte points to start of 32bytes structure */
+ byte = fat_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1);
+
+ time_val = CT_LE_W(time_val);
+ ret1 = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_WTIME_OFFSET,
+ 2, (char *)(&time_val));
+ date = CT_LE_W(date);
+ ret2 = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_WDATE_OFFSET,
+ 2, (char *)(&date));
+ ret3 = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_ADATE_OFFSET,
+ 2, (char *)(&date));
+
+ if ( (ret1 < 0) || (ret2 < 0) || (ret3 < 0) )
+ return -1;
+
+ return RC_OK;
+}
+
+/* msdos_set_first_cluster_num --
+ * Write number of first cluster of the file to the disk (to corresponded
+ * 32bytes slot)
+ *
+ * PARAMETERS:
+ * mt_entry - mount table entry
+ * fat_fd - fat-file descriptor
+ *
+ * RETURNS:
+ * RC_OK on success, or -1 if error occured
+ *
+ */
+int
+msdos_set_first_cluster_num(
+ rtems_filesystem_mount_table_entry_t *mt_entry,
+ fat_file_fd_t *fat_fd
+ )
+{
+ ssize_t ret1 = 0, ret2 = 0;
+ msdos_fs_info_t *fs_info = mt_entry->fs_info;
+ uint32_t new_cln = fat_fd->cln;
+ uint16_t le_cl_low = 0;
+ uint16_t le_cl_hi = 0;
+ uint32_t sec = 0;
+ uint32_t byte = 0;
+
+ /*
+ * calculate input for _fat_block_write: convert (cluster num, offset) to
+ * (sector num, new offset)
+ */
+ sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->dir_pos.sname.cln);
+ sec += (fat_fd->dir_pos.sname.ofs >> fs_info->fat.vol.sec_log2);
+ /* byte from points to start of 32bytes structure */
+ byte = fat_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1);
+
+ le_cl_low = CT_LE_W((uint16_t )(new_cln & 0x0000FFFF));
+ ret1 = _fat_block_write(mt_entry, sec,
+ byte + MSDOS_FIRST_CLUSTER_LOW_OFFSET, 2,
+ (char *)(&le_cl_low));
+ le_cl_hi = CT_LE_W((uint16_t )((new_cln & 0xFFFF0000) >> 16));
+ ret2 = _fat_block_write(mt_entry, sec,
+ byte + MSDOS_FIRST_CLUSTER_HI_OFFSET, 2,
+ (char *)(&le_cl_hi));
+ if ( (ret1 < 0) || (ret2 < 0) )
+ return -1;
+
+ return RC_OK;
+}
+
+
+/* msdos_set_file size --
+ * Write file size of the file to the disk (to corresponded 32bytes slot)
+ *
+ * PARAMETERS:
+ * mt_entry - mount table entry
+ * fat_fd - fat-file descriptor
+ *
+ * RETURNS:
+ * RC_OK on success, or -1 if error occured (errno set apropriately).
+ *
+ */
+int
+msdos_set_file_size(
+ rtems_filesystem_mount_table_entry_t *mt_entry,
+ fat_file_fd_t *fat_fd
+ )
+{
+ ssize_t ret = 0;
+ msdos_fs_info_t *fs_info = mt_entry->fs_info;
+ uint32_t le_new_length = 0;
+ uint32_t sec = 0;
+ uint32_t byte = 0;
+
+ sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->dir_pos.sname.cln);
+ sec += (fat_fd->dir_pos.sname.ofs >> fs_info->fat.vol.sec_log2);
+ byte = (fat_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1));
+
+ le_new_length = CT_LE_L((fat_fd->fat_file_size));
+ ret = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_SIZE_OFFSET, 4,
+ (char *)(&le_new_length));
+ if ( ret < 0 )
+ return -1;
+
+ return RC_OK;
+}
+
+/*
+ * We should not check whether this routine is called for root dir - it
+ * never can happend
+ */
+
+/* msdos_set_first_char4file_name --
+ * Write first character of the name of the file to the disk (to
+ * corresponded 32bytes slot)
+ *
+ * PARAMETERS:
+ * mt_entry - mount table entry
+ * cl - number of cluster
+ * ofs - offset inside cluster
+ * fchar - character to set up
+ *
+ * RETURNS:
+ * RC_OK on success, or -1 if error occured (errno set apropriately)
+ *
+ */
+int
+msdos_set_first_char4file_name(
+ rtems_filesystem_mount_table_entry_t *mt_entry,
+ fat_dir_pos_t *dir_pos,
+ unsigned char fchar
+ )
+{
+ ssize_t ret;
+ msdos_fs_info_t *fs_info = mt_entry->fs_info;
+ uint32_t dir_block_size;
+ fat_pos_t start = dir_pos->lname;
+ fat_pos_t end = dir_pos->sname;
+
+ if ((end.cln == fs_info->fat.vol.rdir_cl) &&
+ (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
+ dir_block_size = fs_info->fat.vol.rdir_size;
+ else
+ dir_block_size = fs_info->fat.vol.bpc;
+
+ if (dir_pos->lname.cln == FAT_FILE_SHORT_NAME)
+ start = dir_pos->sname;
+
+ /*
+ * We handle the changes directly due the way the short file
+ * name code was written rather than use the fat_file_write
+ * interface.
+ */
+ while (true)
+ {
+ uint32_t sec = (fat_cluster_num_to_sector_num(mt_entry, start.cln) +
+ (start.ofs >> fs_info->fat.vol.sec_log2));
+ uint32_t byte = (start.ofs & (fs_info->fat.vol.bps - 1));;
+
+ ret = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_NAME_OFFSET, 1,
+ &fchar);
+ if (ret < 0)
+ return -1;
+
+ if ((start.cln == end.cln) && (start.ofs == end.ofs))
+ break;
+
+ start.ofs += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
+ if (start.ofs >= dir_block_size)
+ {
+ int rc;
+ if ((end.cln == fs_info->fat.vol.rdir_cl) &&
+ (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
+ break;
+ rc = fat_get_fat_cluster(mt_entry, start.cln, &start.cln);
+ if ( rc != RC_OK )
+ return rc;
+ start.ofs = 0;
+ }
+ }
+
+ return RC_OK;
+}
+
+/* msdos_dir_is_empty --
+ * Check whether directory which correspondes to the fat-file descriptor is
+ * empty.
+ *
+ * PARAMETERS:
+ * mt_entry - mount table entry
+ * fat_fd - fat-file descriptor
+ * ret_val - placeholder for result
+ *
+ * RETURNS:
+ * RC_OK on success, or -1 if error occured
+ *
+ */
+int
+msdos_dir_is_empty(
+ rtems_filesystem_mount_table_entry_t *mt_entry,
+ fat_file_fd_t *fat_fd,
+ bool *ret_val
+ )
+{
+ ssize_t ret = 0;
+ msdos_fs_info_t *fs_info = mt_entry->fs_info;
+ uint32_t j = 0, i = 0;
+
+ /* dir is not empty */
+ *ret_val = false;
+
+ while ((ret = fat_file_read(mt_entry, fat_fd, j * fs_info->fat.vol.bps,
+ fs_info->fat.vol.bps,
+ fs_info->cl_buf)) != FAT_EOF)
+ {
+ if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
+ return -1;
+
+ assert(ret == fs_info->fat.vol.bps);
+
+ /* have to look at the DIR_NAME as "raw" 8-bit data */
+ for (i = 0;
+ i < fs_info->fat.vol.bps;
+ i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
+ {
+ char* entry = (char*) fs_info->cl_buf + i;
+
+ /*
+ * If the entry is empty, a long file name entry, or '.' or '..'
+ * then consider it as empty.
+ *
+ * Just ignore long file name entries. They must have a short entry to
+ * be valid.
+ */
+ if (((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
+ MSDOS_THIS_DIR_ENTRY_EMPTY) ||
+ ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
+ MSDOS_ATTR_LFN) ||
+ (strncmp(MSDOS_DIR_NAME((entry)), MSDOS_DOT_NAME,
+ MSDOS_SHORT_NAME_LEN) == 0) ||
+ (strncmp(MSDOS_DIR_NAME((entry)),
+ MSDOS_DOTDOT_NAME,
+ MSDOS_SHORT_NAME_LEN) == 0))
+ continue;
+
+ /*
+ * Nothing more to look at.
+ */
+ if ((*MSDOS_DIR_NAME(entry)) ==
+ MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
+ {
+ *ret_val = true;
+ return RC_OK;
+ }
+
+ /*
+ * Short file name entries mean not empty.
+ */
+ return RC_OK;
+ }
+ j++;
+ }
+ *ret_val = true;
+ 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.
+ *
+ * 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
+ *
+ * RETURNS:
+ * RC_OK on success, or error code if error occured (errno set
+ * appropriately)
+ *
+ */
+#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 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);
+
+ fat_dir_pos_init(dir_pos);
+
+ lfn_start.cln = lfn_start.ofs = FAT_FILE_SHORT_NAME;
+
+ /*
+ * 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 (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;
+
+ entries_per_block = bts2rd / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
+
+#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);
+#endif
+ /*
+ * 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(mt_entry, fat_fd, (dir_offset * bts2rd),
+ bts2rd, fs_info->cl_buf)) != FAT_EOF)
+ {
+ bool remainder_empty = false;
+#if MSDOS_FIND_PRINT
+ printf ("MSFS:[2] dir_offset:%li\n", dir_offset);
+#endif
+
+ if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
+ rtems_set_errno_and_return_minus_one(EIO);
+
+ assert(ret == bts2rd);
+
+ /* have to look at the DIR_NAME as "raw" 8-bit data */
+ for (dir_entry = 0;
+ dir_entry < bts2rd;
+ 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.
+ */
+ bool entry_empty = (*MSDOS_DIR_ENTRY_TYPE(entry) ==
+ MSDOS_THIS_DIR_ENTRY_EMPTY);
+ remainder_empty = (*MSDOS_DIR_ENTRY_TYPE(entry) ==
+ 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,
+ dir_entry, (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE));
+#endif
+ /*
+ * Remember where the we are, ie the start, so we can come back
+ * to here and write the long file name if this is the start of
+ * a series of empty entries. If empty_space_count is 0 then
+ * we are currently not inside an empty series of entries. It
+ * is a count of empty entries.
+ */
+ if (empty_space_count == 0)
+ {
+ empty_space_entry = dir_entry;
+ empty_space_offset = dir_offset;
+ }
+
+ if (remainder_empty)
+ {
+#if MSDOS_FIND_PRINT
+ printf ("MSFS:[3.1] cn:%i esf:%i\n", create_node, empty_space_found);
+#endif
+ /*
+ * If just looking and there is no more entries in the
+ * directory - return name-not-found
+ */
+ if (!create_node)
+ return MSDOS_NAME_NOT_FOUND_ERR;
+
+ /*
+ * Lets go and write the directory entries. If we have not found
+ * any available space add the remaining number of entries to any that
+ * 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)
+ {
+ empty_space_count +=
+ entries_per_block - (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
+ empty_space_found = true;
+#if MSDOS_FIND_PRINT
+ printf ("MSFS:[3.2] esf:%i esc%i\n", empty_space_found, empty_space_count);
+#endif
+ }
+ break;
+ }
+ else if (entry_empty)
+ {
+ if (create_node)
+ {
+ /*
+ * Remainder is not empty so is this entry empty ?
+ */
+ empty_space_count++;
+
+ if (empty_space_count == (lfn_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);
+#endif
+ }
+ else
+ {
+ /*
+ * A valid entry so handle it.
+ *
+ * If empty space has not been found we need to start the
+ * count again.
+ */
+ if (create_node && !empty_space_found)
+ {
+ empty_space_entry = 0;
+ empty_space_count = 0;
+ }
+
+ /*
+ * Check the attribute to see if the entry is for a long
+ * file name.
+ */
+ if ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
+ MSDOS_ATTR_LFN)
+ {
+ char* p;
+ int o;
+ int i;
+#if MSDOS_FIND_PRINT
+ printf ("MSFS:[4.2] lfn:%c entry:%i checksum:%i\n",
+ lfn_start.cln == FAT_FILE_SHORT_NAME ? 'f' : 't',
+ *MSDOS_DIR_ENTRY_TYPE(entry) & MSDOS_LAST_LONG_ENTRY_MASK,
+ *MSDOS_DIR_LFN_CHECKSUM(entry));
+#endif
+ /*
+ * If we are not already processing a LFN see if this is
+ * the first entry of a LFN ?
+ */
+ if (lfn_start.cln == FAT_FILE_SHORT_NAME)
+ {
+ lfn_matched = false;
+
+ /*
+ * The first entry must have the last long entry
+ * flag set.
+ */
+ if ((*MSDOS_DIR_ENTRY_TYPE(entry) &
+ MSDOS_LAST_LONG_ENTRY) == 0)
+ continue;
+
+ /*
+ * Does the number of entries in the LFN directory
+ * entry match the number we expect for this
+ * file name. Note we do not know the number of
+ * characters in the entry so this is check further
+ * on when the characters are checked.
+ */
+ if (lfn_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.ofs = dir_entry;
+ lfn_entry = lfn_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));
+#endif
+ }
+
+ /*
+ * If the entry number or the check sum do not match
+ * forget this series of long directory entries. These
+ * could be orphaned entries depending on the history
+ * of the disk.
+ */
+ if ((lfn_entry != (*MSDOS_DIR_ENTRY_TYPE(entry) &
+ MSDOS_LAST_LONG_ENTRY_MASK)) ||
+ (lfn_checksum != *MSDOS_DIR_LFN_CHECKSUM(entry)))
+ {
+#if MSDOS_FIND_PRINT
+ printf ("MSFS:[4.4] no match\n");
+#endif
+ 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;
+ }
+
+ if (((o + i) >= name_len) || (*p != name[o + i]))
+ {
+ lfn_start.cln = FAT_FILE_SHORT_NAME;
+ break;
+ }
+
+ switch (i)
+ {
+ case 4:
+ p += 5;
+ break;
+ case 10:
+ p += 4;
+ break;
+ default:
+ p += 2;
+ break;
+ }
+ }
+
+ 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);
+#endif
+ /*
+ * SFN entry found.
+ *
+ * If a LFN has been found and it matched check the
+ * entries have all been found and the checksum is
+ * correct. If this is the case return the short file
+ * name entry.
+ */
+ if (lfn_matched)
+ {
+ uint8_t cs = 0;
+ uint8_t* p = (uint8_t*) MSDOS_DIR_NAME(entry);
+ int i;
+
+ for (i = 0; i < MSDOS_SHORT_NAME_LEN; i++, p++)
+ 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
+ }
+
+ /*
+ * 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");
+#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(mt_entry, 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(mt_entry, fat_fd, F_CLU_NUM,
+ lfn_start.cln * bts2rd,
+ &lfn_start.cln);
+ if (rc != RC_OK)
+ return rc;
+ }
+
+ 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)
+ break;
+
+ 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 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);
+#endif
+
+ /*
+ * If a long file name calculate the checksum of the short file name
+ * data to place in each long file name entry. First set the short
+ * 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_short_name_hex(MSDOS_DIR_NAME(name_dir_entry), slot);
+ }
+
+ if (lfn_entries)
+ {
+ uint8_t* p = (uint8_t*) MSDOS_DIR_NAME(name_dir_entry);
+ int i;
+ for (i = 0; i < 11; i++, p++)
+ lfn_checksum =
+ ((lfn_checksum & 1) ? 0x80 : 0) + (lfn_checksum >> 1) + *p;
+ }
+
+ /*
+ * If there is no space available then extend the file. The
+ * empty_space_count is a count of empty entries in the currently
+ * read cluster so if 0 there is no space. Note, dir_offset will
+ * be at the next cluster so we can just make empty_space_offset
+ * that value.
+ */
+ if (empty_space_count == 0)
+ {
+ read_cluster = true;
+ empty_space_offset = dir_offset;
+ empty_space_entry = 0;
+ }
+
+ /*
+ * Have we read past the empty block ? If so go back and read it again.
+ */
+ if (dir_offset != empty_space_offset)
+ read_cluster = true;
+
+ /*
+ * Handle the entry writes.
+ */
+ lfn_start.cln = lfn_start.ofs = FAT_FILE_SHORT_NAME;
+ lfn_entry = 0;
+
+#if MSDOS_FIND_PRINT
+ printf ("MSFS:[9] read_cluster:%d eso:%ld ese:%ld\n",
+ read_cluster, empty_space_offset, empty_space_entry);
+#endif
+
+ /*
+ * The one more is the short entry.
+ */
+ while (lfn_entry < (lfn_entries + 1))
+ {
+ int length = 0;
+
+ if (read_cluster)
+ {
+ uint32_t new_length;
+#if MSDOS_FIND_PRINT
+ printf ("MSFS:[9.1] eso:%li\n", empty_space_offset);
+#endif
+ ret = fat_file_read(mt_entry, 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 MSDOS_FIND_PRINT
+ printf ("MSFS:[9.2] extending file:%li\n", empty_space_offset);
+#endif
+ ret = fat_file_extend (mt_entry, fat_fd, empty_space_offset * bts2rd,
+ &new_length);
+
+ if (ret != RC_OK)
+ return ret;
+
+#if MSDOS_FIND_PRINT
+ printf ("MSFS:[9.3] extended: %d <-> %d\n", new_length, empty_space_offset * bts2rd);
+#endif
+ if (new_length != (empty_space_offset * bts2rd))
+ rtems_set_errno_and_return_minus_one(EIO);
+
+ memset(fs_info->cl_buf, 0, bts2rd);
+
+ ret = fat_file_write(mt_entry, fat_fd,
+ empty_space_offset * bts2rd,
+ bts2rd, fs_info->cl_buf);
+#if MSDOS_FIND_PRINT
+ 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 MSDOS_FIND_PRINT
+ printf ("MSFS:[10] eso:%li\n", empty_space_offset);
+#endif
+
+ for (dir_entry = empty_space_entry;
+ dir_entry < bts2rd;
+ dir_entry += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
+ {
+ char* entry = (char*) fs_info->cl_buf + dir_entry;
+ char* p;
+ const char* n;
+ int i;
+ char fill = 0;
+
+ length += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
+ lfn_entry++;
+
+#if MSDOS_FIND_PRINT
+ printf ("MSFS:[10] de:%li(%li) length:%i lfn_entry:%i\n",
+ dir_entry, (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE),
+ length, lfn_entry);
+#endif
+ /*
+ * Time to write the short file name entry.
+ */
+ if (lfn_entry == (lfn_entries + 1))
+ {
+ /* get current cluster number */
+ int rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM,
+ empty_space_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(mt_entry, fat_fd, F_CLU_NUM,
+ lfn_start.cln * bts2rd,
+ &lfn_start.cln);
+ if (rc != RC_OK)
+ return rc;
+ }
+
+ dir_pos->lname.cln = lfn_start.cln;
+ dir_pos->lname.ofs = lfn_start.ofs;
+
+ /* write new node entry */
+ memcpy (entry, (uint8_t *) name_dir_entry,
+ MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
+ break;
+ }
+
+ /*
+ * This is a long file name and we need to write
+ * a long file name entry. See if this is the
+ * first entry written and if so remember the
+ * the location of the long file name.
+ */
+ if (lfn_start.cln == FAT_FILE_SHORT_NAME)
+ {
+ lfn_start.cln = empty_space_offset;
+ lfn_start.ofs = dir_entry;
+ }
+
+ /*
+ * Clear the entry before loading the data.
+ */
+ memset (entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
+
+ *MSDOS_DIR_LFN_CHECKSUM(entry) = lfn_checksum;
+
+ p = entry + 1;
+ n = name + (lfn_entries - lfn_entry) * MSDOS_LFN_LEN_PER_ENTRY;
+
+ for (i = 0; i < MSDOS_LFN_LEN_PER_ENTRY; i++)
+ {
+ if (*n != 0)
+ {
+ *p = *n;
+ n++;
+ }
+ else
+ {
+ p [0] = fill;
+ p [1] = fill;
+ fill = 0xff;
+ }
+
+ switch (i)
+ {
+ case 4:
+ p += 5;
+ break;
+ case 10:
+ p += 4;
+ break;
+ default:
+ p += 2;
+ break;
+ }
+ }
+
+ *MSDOS_DIR_ENTRY_TYPE(entry) = (lfn_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(mt_entry, 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)
+ rtems_set_errno_and_return_minus_one(EIO);
+
+ empty_space_offset++;
+ empty_space_entry = 0;
+ read_cluster = true;
+ }
+
+ return 0;
+}
+
+/* msdos_find_node_by_cluster_num_in_fat_file --
+ * Find node with specified number of cluster in fat-file.
+ *
+ * Note, not updated in the LFN change because it is only used
+ * for . and .. entries and these are always short.
+ *
+ * PARAMETERS:
+ * mt_entry - mount table entry
+ * fat_fd - fat-file descriptor
+ * cl4find - number of cluster to find
+ * paux - identify a node location on the disk -
+ * cluster num and offset inside the cluster
+ * dir_entry - placeholder for found node
+ *
+ * RETURNS:
+ * RC_OK on success, or error code if error occured
+ *
+ */
+int msdos_find_node_by_cluster_num_in_fat_file(
+ rtems_filesystem_mount_table_entry_t *mt_entry,
+ fat_file_fd_t *fat_fd,
+ uint32_t cl4find,
+ fat_dir_pos_t *dir_pos,
+ char *dir_entry
+ )
+{
+ int rc = RC_OK;
+ ssize_t ret = 0;
+ msdos_fs_info_t *fs_info = mt_entry->fs_info;
+ uint32_t bts2rd = 0;
+ uint32_t i = 0, j = 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;
+
+ while ((ret = fat_file_read(mt_entry, fat_fd, j * bts2rd, bts2rd,
+ fs_info->cl_buf)) != FAT_EOF)
+ {
+ if ( ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE )
+ rtems_set_errno_and_return_minus_one( EIO );
+
+ assert(ret == bts2rd);
+
+ for (i = 0; i < bts2rd; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
+ {
+ char* entry = (char*) fs_info->cl_buf + i;
+
+ /* if this and all rest entries are empty - return not-found */
+ if ((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
+ MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
+ return MSDOS_NAME_NOT_FOUND_ERR;
+
+ /* if this entry is empty - skip it */
+ if ((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
+ MSDOS_THIS_DIR_ENTRY_EMPTY)
+ continue;
+
+ /* if get a non-empty entry - compare clusters num */
+ if (MSDOS_EXTRACT_CLUSTER_NUM(entry) == cl4find)
+ {
+ /* on success fill aux structure and copy all 32 bytes */
+ rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, j * bts2rd,
+ &dir_pos->sname.cln);
+ if (rc != RC_OK)
+ return rc;
+
+ dir_pos->sname.ofs = i;
+ dir_pos->lname.cln = FAT_FILE_SHORT_NAME;
+ dir_pos->lname.ofs = FAT_FILE_SHORT_NAME;
+
+ memcpy(dir_entry, entry,
+ MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
+ return RC_OK;
+ }
+ }
+ j++;
+ }
+ return MSDOS_NAME_NOT_FOUND_ERR;
+}