/* * fat_fat_operations.c * * General operations on File Allocation Table * * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia * Author: Eugeny S. Mints * * @(#) $Id$ */ #include #include #include #include #include #include #include #include #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)*/ if (next_cln == 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) ) { #if 0 /* * do not perform endian conversion explicitely, * because following code will enforce little * endian format implicitly! */ fat16_clv = CT_LE_W((((unsigned16)in_val) << FAT_FAT12_SHIFT)); #else fat16_clv = ((unsigned16)in_val) << FAT_FAT12_SHIFT; #endif *((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 { #if 0 /* * do not perform endian conversion explicitely, * because following code will enforce little * endian format implicitly! */ fat16_clv = CT_LE_W((((unsigned16)in_val) & FAT_FAT12_MASK)); #else fat16_clv = ((unsigned16)in_val) & FAT_FAT12_MASK; #endif *((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; }