summaryrefslogtreecommitdiffstats
path: root/c/src/exec/libfs/src/dosfs/fat_fat_operations.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/exec/libfs/src/dosfs/fat_fat_operations.c')
-rw-r--r--c/src/exec/libfs/src/dosfs/fat_fat_operations.c445
1 files changed, 445 insertions, 0 deletions
diff --git a/c/src/exec/libfs/src/dosfs/fat_fat_operations.c b/c/src/exec/libfs/src/dosfs/fat_fat_operations.c
new file mode 100644
index 0000000000..49b2ab70d3
--- /dev/null
+++ b/c/src/exec/libfs/src/dosfs/fat_fat_operations.c
@@ -0,0 +1,445 @@
+/*
+ * fat_fat_operations.c
+ *
+ * General operations on File Allocation Table
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <rtems/libio_.h>
+
+#include "fat.h"
+#include "fat_fat_operations.h"
+
+/* fat_scan_fat_for_free_clusters --
+ * Allocate chain of free clusters from Files Allocation Table
+ *
+ * PARAMETERS:
+ * mt_entry - mount table entry
+ * chain - the number of the first allocated cluster (first cluster
+ * in the chain)
+ * count - count of clusters to allocate (chain length)
+ *
+ * RETURNS:
+ * RC_OK on success, or error code if error occured (errno set
+ * appropriately)
+ *
+ *
+ */
+int
+fat_scan_fat_for_free_clusters(
+ rtems_filesystem_mount_table_entry_t *mt_entry,
+ unsigned32 *chain,
+ unsigned32 count,
+ unsigned32 *cls_added,
+ unsigned32 *last_cl
+ )
+{
+ int rc = RC_OK;
+ fat_fs_info_t *fs_info = mt_entry->fs_info;
+ unsigned32 cl4find = 2;
+ unsigned32 next_cln = 0;
+ unsigned32 save_cln = 0;
+ unsigned32 data_cls_val = fs_info->vol.data_cls + 2;
+ unsigned32 i = 2;
+
+ *cls_added = 0;
+
+ if (count == 0)
+ return rc;
+
+ if ((fs_info->vol.type & FAT_FAT32) &&
+ (fs_info->vol.next_cl != FAT_UNDEFINED_VALUE))
+ cl4find = fs_info->vol.next_cl;
+
+ /*
+ * fs_info->vol.data_cls is exactly the count of data clusters
+ * starting at cluster 2, so the maximum valid cluster number is
+ * (fs_info->vol.data_cls + 1)
+ */
+ while (i < data_cls_val)
+ {
+ rc = fat_get_fat_cluster(mt_entry, cl4find, &next_cln);
+ if ( rc != RC_OK )
+ {
+ if (*cls_added != 0)
+ fat_free_fat_clusters_chain(mt_entry, (*chain));
+ return rc;
+ }
+
+ if ((next_cln & fs_info->vol.mask) == FAT_GENFAT_FREE)
+ {
+ /*
+ * We are enforced to process allocation of the first free cluster
+ * by separate 'if' statement because otherwise undo function
+ * wouldn't work properly
+ */
+ if (*cls_added == 0)
+ {
+ *chain = cl4find;
+ rc = fat_set_fat_cluster(mt_entry, cl4find, FAT_GENFAT_EOC);
+ if ( rc != RC_OK )
+ {
+ /*
+ * this is the first cluster we tried to allocate so no
+ * cleanup activity needed
+ */
+ return rc;
+ }
+ }
+ else
+ {
+ /* set EOC value to new allocated cluster */
+ rc = fat_set_fat_cluster(mt_entry, cl4find, FAT_GENFAT_EOC);
+ if ( rc != RC_OK )
+ {
+ /* cleanup activity */
+ fat_free_fat_clusters_chain(mt_entry, (*chain));
+ return rc;
+ }
+
+ rc = fat_set_fat_cluster(mt_entry, save_cln, cl4find);
+ if ( rc != RC_OK )
+ {
+ /* cleanup activity */
+ fat_free_fat_clusters_chain(mt_entry, (*chain));
+ /* trying to save last allocated cluster for future use */
+ fat_set_fat_cluster(mt_entry, cl4find, FAT_GENFAT_FREE);
+ fat_buf_release(fs_info);
+ return rc;
+ }
+ }
+
+ save_cln = cl4find;
+ (*cls_added)++;
+
+ /* have we satisfied request ? */
+ if (*cls_added == count)
+ {
+ if (fs_info->vol.type & FAT_FAT32)
+ {
+ fs_info->vol.next_cl = save_cln;
+ if (fs_info->vol.free_cls != 0xFFFFFFFF)
+ fs_info->vol.free_cls -= (*cls_added);
+ }
+ *last_cl = save_cln;
+ fat_buf_release(fs_info);
+ return rc;
+ }
+ }
+ i++;
+ cl4find++;
+ if (cl4find >= data_cls_val)
+ cl4find = 2;
+ }
+
+ if (fs_info->vol.type & FAT_FAT32)
+ {
+ fs_info->vol.next_cl = save_cln;
+ if (fs_info->vol.free_cls != 0xFFFFFFFF)
+ fs_info->vol.free_cls -= (*cls_added);
+ }
+ *last_cl = save_cln;
+ fat_buf_release(fs_info);
+ return RC_OK;
+}
+
+/* fat_free_fat_clusters_chain --
+ * Free chain of clusters in Files Allocation Table.
+ *
+ * PARAMETERS:
+ * mt_entry - mount table entry
+ * chain - number of the first cluster in the chain
+ *
+ * RETURNS:
+ * RC_OK on success, or -1 if error occured (errno set appropriately)
+ */
+int
+fat_free_fat_clusters_chain(
+ rtems_filesystem_mount_table_entry_t *mt_entry,
+ unsigned32 chain
+ )
+{
+ int rc = RC_OK, rc1 = RC_OK;
+ fat_fs_info_t *fs_info = mt_entry->fs_info;
+ unsigned32 cur_cln = chain;
+ unsigned32 next_cln = 0;
+ unsigned32 freed_cls_cnt = 0;
+
+ while ((cur_cln & fs_info->vol.mask) != fs_info->vol.eoc_val)
+ {
+ rc = fat_get_fat_cluster(mt_entry, cur_cln, &next_cln);
+ if ( rc != RC_OK )
+ {
+ if ((fs_info->vol.type & FAT_FAT32) &&
+ (fs_info->vol.free_cls != FAT_UNDEFINED_VALUE))
+ fs_info->vol.free_cls += freed_cls_cnt;
+ fat_buf_release(fs_info);
+ return rc;
+ }
+
+ rc = fat_set_fat_cluster(mt_entry, cur_cln, FAT_GENFAT_FREE);
+ if ( rc != RC_OK )
+ rc1 = rc;
+
+ freed_cls_cnt++;
+ cur_cln = next_cln;
+ }
+
+ if (fs_info->vol.type & FAT_FAT32)
+ {
+ fs_info->vol.next_cl = chain;
+ if (fs_info->vol.free_cls != FAT_UNDEFINED_VALUE)
+ fs_info->vol.free_cls += freed_cls_cnt;
+ }
+
+ fat_buf_release(fs_info);
+ if (rc1 != RC_OK)
+ return rc1;
+
+ return RC_OK;
+}
+
+/* fat_get_fat_cluster --
+ * Fetches the contents of the cluster (link to next cluster in the chain)
+ * from Files Allocation Table.
+ *
+ * PARAMETERS:
+ * mt_entry - mount table entry
+ * cln - number of cluster to fetch the contents from
+ * ret_val - contents of the cluster 'cln' (link to next cluster in
+ * the chain)
+ *
+ * RETURNS:
+ * RC_OK on success, or -1 if error occured
+ * and errno set appropriately
+ */
+int
+fat_get_fat_cluster(
+ rtems_filesystem_mount_table_entry_t *mt_entry,
+ unsigned32 cln,
+ unsigned32 *ret_val
+ )
+{
+ int rc = RC_OK;
+ register fat_fs_info_t *fs_info = mt_entry->fs_info;
+ bdbuf_buffer *block0 = NULL;
+ unsigned32 sec = 0;
+ unsigned32 ofs = 0;
+
+ /* sanity check */
+ if ( (cln < 2) || (cln > (fs_info->vol.data_cls + 1)) )
+ set_errno_and_return_minus_one(EIO);
+
+ sec = (FAT_FAT_OFFSET(fs_info->vol.type, cln) >> fs_info->vol.sec_log2) +
+ fs_info->vol.afat_loc;
+ ofs = FAT_FAT_OFFSET(fs_info->vol.type, cln) & (fs_info->vol.bps - 1);
+
+ rc = fat_buf_access(fs_info, sec, FAT_OP_TYPE_READ, &block0);
+ if (rc != RC_OK)
+ return rc;
+
+ switch ( fs_info->vol.type )
+ {
+ case FAT_FAT12:
+ /*
+ * we are enforced in complex computations for FAT12 to escape CPU
+ * align problems for some architectures
+ */
+ *ret_val = (*((unsigned8 *)(block0->buffer + ofs)));
+ if ( ofs == (fs_info->vol.bps - 1) )
+ {
+ rc = fat_buf_access(fs_info, sec + 1, FAT_OP_TYPE_READ,
+ &block0);
+ if (rc != RC_OK)
+ return rc;
+
+ *ret_val |= (*((unsigned8 *)(block0->buffer)))<<8;
+ }
+ else
+ {
+ *ret_val |= (*((unsigned8 *)(block0->buffer + ofs + 1)))<<8;
+ }
+
+ if ( FAT_CLUSTER_IS_ODD(cln) )
+ *ret_val = (*ret_val) >> FAT12_SHIFT;
+ else
+ *ret_val = (*ret_val) & FAT_FAT12_MASK;
+
+ break;
+
+ case FAT_FAT16:
+ *ret_val = *((unsigned16 *)(block0->buffer + ofs));
+ *ret_val = CF_LE_W(*ret_val);
+ break;
+
+ case FAT_FAT32:
+ *ret_val = *((unsigned32 *)(block0->buffer + ofs));
+ *ret_val = CF_LE_L(*ret_val);
+ break;
+
+ default:
+ set_errno_and_return_minus_one(EIO);
+ break;
+ }
+
+ return RC_OK;
+}
+
+/* fat_set_fat_cluster --
+ * Set the contents of the cluster (link to next cluster in the chain)
+ * from Files Allocation Table.
+ *
+ * PARAMETERS:
+ * mt_entry - mount table entry
+ * cln - number of cluster to set contents to
+ * in_val - value to set
+ *
+ * RETURNS:
+ * RC_OK on success, or -1 if error occured
+ * and errno set appropriately
+ */
+int
+fat_set_fat_cluster(
+ rtems_filesystem_mount_table_entry_t *mt_entry,
+ unsigned32 cln,
+ unsigned32 in_val
+ )
+{
+ int rc = RC_OK;
+ fat_fs_info_t *fs_info = mt_entry->fs_info;
+ unsigned32 sec = 0;
+ unsigned32 ofs = 0;
+ unsigned16 fat16_clv = 0;
+ unsigned32 fat32_clv = 0;
+ bdbuf_buffer *block0 = NULL;
+
+ /* sanity check */
+ if ( (cln < 2) || (cln > (fs_info->vol.data_cls + 1)) )
+ set_errno_and_return_minus_one(EIO);
+
+ sec = (FAT_FAT_OFFSET(fs_info->vol.type, cln) >> fs_info->vol.sec_log2) +
+ fs_info->vol.afat_loc;
+ ofs = FAT_FAT_OFFSET(fs_info->vol.type, cln) & (fs_info->vol.bps - 1);
+
+ rc = fat_buf_access(fs_info, sec, FAT_OP_TYPE_READ, &block0);
+ if (rc != RC_OK)
+ return rc;
+
+ switch ( fs_info->vol.type )
+ {
+ case FAT_FAT12:
+ if ( FAT_CLUSTER_IS_ODD(cln) )
+ {
+ fat16_clv = CT_LE_W((((unsigned16)in_val) << FAT_FAT12_SHIFT));
+
+ *((unsigned8 *)(block0->buffer + ofs)) =
+ (*((unsigned8 *)(block0->buffer + ofs))) & 0x0F;
+
+ *((unsigned8 *)(block0->buffer + ofs)) =
+ (*((unsigned8 *)(block0->buffer + ofs))) |
+ (unsigned8)(fat16_clv & 0x00FF);
+
+ fat_buf_mark_modified(fs_info);
+
+ if ( ofs == (fs_info->vol.bps - 1) )
+ {
+ rc = fat_buf_access(fs_info, sec + 1, FAT_OP_TYPE_READ,
+ &block0);
+ if (rc != RC_OK)
+ return rc;
+
+ *((unsigned8 *)(block0->buffer)) &= 0x00;
+
+ *((unsigned8 *)(block0->buffer)) =
+ (*((unsigned8 *)(block0->buffer))) |
+ (unsigned8)((fat16_clv & 0xFF00)>>8);
+
+ fat_buf_mark_modified(fs_info);
+ }
+ else
+ {
+ *((unsigned8 *)(block0->buffer + ofs + 1)) &= 0x00;
+
+ *((unsigned8 *)(block0->buffer + ofs + 1)) =
+ (*((unsigned8 *)(block0->buffer + ofs + 1))) |
+ (unsigned8)((fat16_clv & 0xFF00)>>8);
+ }
+ }
+ else
+ {
+ fat16_clv = CT_LE_W((((unsigned16)in_val) & FAT_FAT12_MASK));
+
+ *((unsigned8 *)(block0->buffer + ofs)) &= 0x00;
+
+ *((unsigned8 *)(block0->buffer + ofs)) =
+ (*((unsigned8 *)(block0->buffer + ofs))) |
+ (unsigned8)(fat16_clv & 0x00FF);
+
+ fat_buf_mark_modified(fs_info);
+
+ if ( ofs == (fs_info->vol.bps - 1) )
+ {
+ rc = fat_buf_access(fs_info, sec + 1, FAT_OP_TYPE_READ,
+ &block0);
+ if (rc != RC_OK)
+ return rc;
+
+ *((unsigned8 *)(block0->buffer)) =
+ (*((unsigned8 *)(block0->buffer))) & 0xF0;
+
+ *((unsigned8 *)(block0->buffer)) =
+ (*((unsigned8 *)(block0->buffer))) |
+ (unsigned8)((fat16_clv & 0xFF00)>>8);
+
+ fat_buf_mark_modified(fs_info);
+ }
+ else
+ {
+ *((unsigned8 *)(block0->buffer + ofs + 1)) =
+ (*((unsigned8 *)(block0->buffer + ofs + 1))) & 0xF0;
+
+ *((unsigned8 *)(block0->buffer + ofs+1)) =
+ (*((unsigned8 *)(block0->buffer + ofs+1))) |
+ (unsigned8)((fat16_clv & 0xFF00)>>8);
+ }
+ }
+ break;
+
+ case FAT_FAT16:
+ *((unsigned16 *)(block0->buffer + ofs)) =
+ (unsigned16)(CT_LE_W(in_val));
+ fat_buf_mark_modified(fs_info);
+ break;
+
+ case FAT_FAT32:
+ fat32_clv = CT_LE_L((in_val & FAT_FAT32_MASK));
+
+ *((unsigned32 *)(block0->buffer + ofs)) =
+ (*((unsigned32 *)(block0->buffer + ofs))) & (CT_LE_L(0xF0000000));
+
+ *((unsigned32 *)(block0->buffer + ofs)) =
+ fat32_clv | (*((unsigned32 *)(block0->buffer + ofs)));
+
+ fat_buf_mark_modified(fs_info);
+ break;
+
+ default:
+ set_errno_and_return_minus_one(EIO);
+ break;
+
+ }
+
+ return RC_OK;
+}