diff options
author | Joel Sherrill <joel.sherrill@OARcorp.com> | 2002-02-28 20:43:50 +0000 |
---|---|---|
committer | Joel Sherrill <joel.sherrill@OARcorp.com> | 2002-02-28 20:43:50 +0000 |
commit | f36a7bfcde756cae3b3e933f0e688137fb684be8 (patch) | |
tree | 0d1c325e5c12913674c0e1d6ddfd656fa8c0fcd8 /c/src/exec/libfs/src/dosfs/fat_file.c | |
parent | 2002-02-28 Joel Sherrill <joel@OARcorp.com> (diff) | |
download | rtems-f36a7bfcde756cae3b3e933f0e688137fb684be8.tar.bz2 |
2002-02-28 Victor V. Vengerov <vvv@oktet.ru>
* DOS filesystem including FAT12, FAT16, and FAT32 support submitted.
* src/dosfs, src/dosfs/Makefile.am, src/dosfs/stamp-h2.in,
src/dosfs/config.h.in, src/dosfs/dosfs.h, src/dosfs/fat.c,
src/dosfs/fat.h, src/dosfs/fat_fat_operations.c,
src/dosfs/fat_fat_operations.h, src/dosfs/fat_file.c,
src/dosfs/fat_file.h, src/dosfs/msdos.h, src/dosfs/msdos_create.c,
src/dosfs/msdos_dir.c, src/dosfs/msdos_eval.c, src/dosfs/msdos_file.c,
src/dosfs/msdos_free.c, src/dosfs/msdos_fsunmount.c,
src/dosfs/msdos_handlers_dir.c, src/dosfs/msdos_handlers_file.c,
src/dosfs/msdos_init.c, src/dosfs/msdos_initsupp.c,
src/dosfs/msdos_misc.c, src/dosfs/msdos_mknod.c,
src/dosfs/msdos_node_type.c, src/dosfs/.cvsignore: New files.
* configure.ac, src/Makefile.am, wrapup/Makefile.am: Modified to
reflect addition.
Diffstat (limited to 'c/src/exec/libfs/src/dosfs/fat_file.c')
-rw-r--r-- | c/src/exec/libfs/src/dosfs/fat_file.c | 979 |
1 files changed, 979 insertions, 0 deletions
diff --git a/c/src/exec/libfs/src/dosfs/fat_file.c b/c/src/exec/libfs/src/dosfs/fat_file.c new file mode 100644 index 0000000000..4fd8a5024f --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/fat_file.c @@ -0,0 +1,979 @@ +/* + * fat_file.c + * + * General operations on "fat-file" + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * @(#) $Id$ + * + */ + +#include <bsp.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdarg.h> +#include <errno.h> +#include <stdlib.h> +#include <assert.h> +#include <time.h> + +#include <rtems/libio_.h> + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +static inline void +_hash_insert(Chain_Control *hash, unsigned32 key1, unsigned32 key2, + fat_file_fd_t *el); + +static inline void +_hash_delete(Chain_Control *hash, unsigned32 key1, unsigned32 key2, + fat_file_fd_t *el); + +static inline int +_hash_search( + rtems_filesystem_mount_table_entry_t *mt_entry, + Chain_Control *hash, + unsigned32 key1, + unsigned32 key2, + void **ret +); + +static int +fat_file_lseek( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 file_cln, + unsigned32 *disk_cln +); + +/* fat_file_open -- + * Open fat-file. Two hash tables are accessed by key + * constructed from cluster num and offset of the node (i.e. + * files/directories are distinguished by location on the disk). + * First, hash table("vhash") consists of fat-file descriptors corresponded + * to "valid" files is accessed. Search is made by 2 fields equal to key + * constructed. If descriptor is found in the "vhash" - return it. + * Otherwise search is made in hash table("rhash") consits of fat-file + * descriptors corresponded to "removed-but-still-open" files with the + * same keys. + * If search failed, new fat-file descriptor is added to "vhash" + * with both key fields equal to constructed key. Otherwise new fat-file + * descriptor is added to "vhash" with first key field equal to key + * constructed and the second equal to an unique (unique among all values + * of second key fields) value. + * + * PARAMETERS: + * mt_entry - mount table entry + * cln - cluster num of the node + * ofs - offset of the node + * fat_fd - placeholder for returned fat-file descriptor + * + * RETURNS: + * RC_OK and pointer to opened descriptor on success, or -1 if error + * occured (errno set appropriately) + */ +int +fat_file_open( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + unsigned32 ofs, + fat_file_fd_t **fat_fd + ) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + fat_file_fd_t *lfat_fd = NULL; + unsigned32 key = 0; + + /* construct key */ + key = fat_construct_key(mt_entry, cln, ofs); + + /* access "valid" hash table */ + rc = _hash_search(mt_entry, fs_info->vhash, key, 0, (void **)&lfat_fd); + if ( rc == RC_OK ) + { + /* return pointer to fat_file_descriptor allocated before */ + (*fat_fd) = lfat_fd; + lfat_fd->links_num++; + return rc; + } + + /* access "removed-but-still-open" hash table */ + rc = _hash_search(mt_entry, fs_info->rhash, key, key, (void **)&lfat_fd); + + lfat_fd = (*fat_fd) = (fat_file_fd_t*)malloc(sizeof(fat_file_fd_t)); + if ( lfat_fd == NULL ) + set_errno_and_return_minus_one( ENOMEM ); + + lfat_fd->links_num = 1; + lfat_fd->flags &= ~FAT_FILE_REMOVED; + lfat_fd->map.last_cln = FAT_UNDEFINED_VALUE; + + if ( rc != RC_OK ) + lfat_fd->ino = key; + else + { + lfat_fd->ino = fat_get_unique_ino(mt_entry); + + if ( lfat_fd->ino == 0 ) + { + free((*fat_fd)); + /* + * XXX: kernel resource is unsufficient, but not the memory, + * but there is no suitable errno :( + */ + set_errno_and_return_minus_one( ENOMEM ); + } + } + _hash_insert(fs_info->vhash, key, lfat_fd->ino, lfat_fd); + + + /* + * other fields of fat-file descriptor will be initialized on upper + * level + */ + + return RC_OK; +} + + +/* fat_file_reopen -- + * Increment by 1 number of links + * + * PARAMETERS: + * fat_fd - fat-file descriptor + * + * RETURNS: + * RC_OK + */ +int +fat_file_reopen(fat_file_fd_t *fat_fd) +{ + fat_fd->links_num++; + return RC_OK; +} + +/* fat_file_close -- + * Close fat-file. If count of links to fat-file + * descriptor is greater than 1 (i.e. somebody esle holds pointer + * to this descriptor) just decrement it. Otherwise + * do the following. If this descriptor corresponded to removed fat-file + * then free clusters contained fat-file data, delete descriptor from + * "rhash" table and free memory allocated by descriptor. If descriptor + * correspondes to non-removed fat-file and 'ino' field has value from + * unique inode numbers pool then set count of links to descriptor to zero + * and leave it in hash, otherwise delete descriptor from "vhash" and free + * memory allocated by the descriptor + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * + * RETURNS: + * RC_OK, or -1 if error occured (errno set appropriately) + */ +int +fat_file_close( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd + ) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 key = 0; + + /* + * if links_num field of fat-file descriptor is greater than 1 + * decrement the count of links and return + */ + if (fat_fd->links_num > 1) + { + fat_fd->links_num--; + return rc; + } + + key = fat_construct_key(mt_entry, fat_fd->info_cln, fat_fd->info_ofs); + + if (fat_fd->flags & FAT_FILE_REMOVED) + { + rc = fat_file_truncate(mt_entry, fat_fd, 0); + if ( rc != RC_OK ) + return rc; + + _hash_delete(fs_info->rhash, key, fat_fd->ino, fat_fd); + + if ( fat_ino_is_unique(mt_entry, fat_fd->ino) ) + fat_free_unique_ino(mt_entry, fat_fd->ino); + + free(fat_fd); + } + else + { + if (fat_ino_is_unique(mt_entry, fat_fd->ino)) + { + fat_fd->links_num = 0; + } + else + { + _hash_delete(fs_info->vhash, key, fat_fd->ino, fat_fd); + free(fat_fd); + } + } + return rc; +} + +/* fat_file_read -- + * Read 'count' bytes from 'start' position from fat-file. This + * interface hides the architecture of fat-file, represents it as + * linear file + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * start - offset in fat-file (in bytes) to read from + * count - count of bytes to read + * buf - buffer provided by user + * + * RETURNS: + * the number of bytes read on success, or -1 if error occured (errno + * set appropriately) + */ +ssize_t +fat_file_read( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 start, + unsigned32 count, + char *buf + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 cmpltd = 0; + unsigned32 cur_cln = 0; + unsigned32 cl_start = 0; + unsigned32 save_cln = 0; + unsigned32 ofs = 0; + unsigned32 save_ofs; + unsigned32 sec = 0; + unsigned32 byte = 0; + unsigned32 c = 0; + + /* it couldn't be removed - otherwise cache update will be broken */ + if (count == 0) + return cmpltd; + + /* + * >= because start is offset and computed from 0 and file_size + * computed from 1 + */ + if ( start >= fat_fd->fat_file_size ) + return FAT_EOF; + + if ((count > fat_fd->fat_file_size) || + (start > fat_fd->fat_file_size - count)) + count = fat_fd->fat_file_size - start; + + if ((FAT_FD_OF_ROOT_DIR(fat_fd)) && + (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16))) + { + sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->cln); + sec += (start >> fs_info->vol.sec_log2); + byte = start & (fs_info->vol.bps - 1); + + ret = _fat_block_read(mt_entry, sec, byte, count, buf); + if ( ret < 0 ) + return -1; + + return ret; + } + + cl_start = start >> fs_info->vol.bpc_log2; + save_ofs = ofs = start & (fs_info->vol.bpc - 1); + + rc = fat_file_lseek(mt_entry, fat_fd, cl_start, &cur_cln); + if (rc != RC_OK) + return rc; + + while (count > 0) + { + c = MIN(count, (fs_info->vol.bpc - ofs)); + + sec = fat_cluster_num_to_sector_num(mt_entry, cur_cln); + sec += (ofs >> fs_info->vol.sec_log2); + byte = ofs & (fs_info->vol.bps - 1); + + ret = _fat_block_read(mt_entry, sec, byte, c, buf + cmpltd); + if ( ret < 0 ) + return -1; + + count -= c; + cmpltd += c; + save_cln = cur_cln; + rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln); + if ( rc != RC_OK ) + return rc; + + ofs = 0; + } + + /* update cache */ + /* XXX: check this - I'm not sure :( */ + fat_fd->map.file_cln = cl_start + + ((save_ofs + cmpltd - 1) >> fs_info->vol.bpc_log2); + fat_fd->map.disk_cln = save_cln; + + return cmpltd; +} + +/* fat_file_write -- + * Write 'count' bytes of data from user supplied buffer to fat-file + * starting at offset 'start'. This interface hides the architecture + * of fat-file, represents it as linear file + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * start - offset(in bytes) to write from + * count - count + * buf - buffer provided by user + * + * RETURNS: + * number of bytes actually written to the file on success, or -1 if + * error occured (errno set appropriately) + */ +ssize_t +fat_file_write( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 start, + unsigned32 count, + char *buf + ) +{ + int rc = 0; + ssize_t ret = 0; + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 cmpltd = 0; + unsigned32 cur_cln = 0; + unsigned32 save_cln; + unsigned32 cl_start = 0; + unsigned32 ofs = 0; + unsigned32 save_ofs; + unsigned32 sec = 0; + unsigned32 byte = 0; + unsigned32 c = 0; + + if ( count == 0 ) + return cmpltd; + + if ( start > fat_fd->fat_file_size ) + set_errno_and_return_minus_one( EIO ); + + if ((count > fat_fd->size_limit) || + (start > fat_fd->size_limit - count)) + set_errno_and_return_minus_one( EIO ); + + rc = fat_file_extend(mt_entry, fat_fd, start + count, &c); + if (rc != RC_OK) + return rc; + + /* + * check whether there was enough room on device to locate + * file of 'start + count' bytes + */ + if (c != (start + count)) + count = c - start; + + if ((FAT_FD_OF_ROOT_DIR(fat_fd)) && + (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16))) + { + sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->cln); + sec += (start >> fs_info->vol.sec_log2); + byte = start & (fs_info->vol.bps - 1); + + ret = _fat_block_write(mt_entry, sec, byte, count, buf); + if ( ret < 0 ) + return -1; + + return ret; + } + + cl_start = start >> fs_info->vol.bpc_log2; + save_ofs = ofs = start & (fs_info->vol.bpc - 1); + + rc = fat_file_lseek(mt_entry, fat_fd, cl_start, &cur_cln); + if (rc != RC_OK) + return rc; + + while (count > 0) + { + c = MIN(count, (fs_info->vol.bpc - ofs)); + + sec = fat_cluster_num_to_sector_num(mt_entry, cur_cln); + sec += (ofs >> fs_info->vol.sec_log2); + byte = ofs & (fs_info->vol.bps - 1); + + ret = _fat_block_write(mt_entry, sec, byte, c, buf + cmpltd); + if ( ret < 0 ) + return -1; + + count -= c; + cmpltd += c; + save_cln = cur_cln; + rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln); + if ( rc != RC_OK ) + return rc; + + ofs = 0; + } + + /* update cache */ + /* XXX: check this - I'm not sure :( */ + fat_fd->map.file_cln = cl_start + + ((save_ofs + cmpltd - 1) >> fs_info->vol.bpc_log2); + fat_fd->map.disk_cln = save_cln; + + return cmpltd; +} + +/* fat_file_extend -- + * Extend fat-file. If new length less than current fat-file size - + * do nothing. Otherwise calculate necessary count of clusters to add, + * allocate it and add new clusters chain to the end of + * existing clusters chain. + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * new_length - new length + * a_length - placeholder for result - actual new length of file + * + * RETURNS: + * RC_OK and new length of file on success, or -1 if error occured (errno + * set appropriately) + */ +int +fat_file_extend( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 new_length, + unsigned32 *a_length + ) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 chain = 0; + unsigned32 bytes2add = 0; + unsigned32 cls2add = 0; + unsigned32 old_last_cl; + unsigned32 last_cl = 0; + unsigned32 bytes_remain = 0; + unsigned32 cls_added; + + *a_length = new_length; + + if (new_length <= fat_fd->fat_file_size) + return RC_OK; + + if ((FAT_FD_OF_ROOT_DIR(fat_fd)) && + (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16))) + set_errno_and_return_minus_one( ENOSPC ); + + bytes_remain = (fs_info->vol.bpc - + (fat_fd->fat_file_size & (fs_info->vol.bpc - 1))) & + (fs_info->vol.bpc - 1); + + bytes2add = new_length - fat_fd->fat_file_size; + + if (bytes2add > bytes_remain) + bytes2add -= bytes_remain; + else + bytes2add = 0; + + /* + * if in last cluster allocated for the file there is enough room to + * handle extention (hence we don't need to add even one cluster to the + * file ) - return + */ + if (bytes2add == 0) + return RC_OK; + + cls2add = ((bytes2add - 1) >> fs_info->vol.bpc_log2) + 1; + + rc = fat_scan_fat_for_free_clusters(mt_entry, &chain, cls2add, + &cls_added, &last_cl); + + /* this means that low level I/O error occured */ + if (rc != RC_OK) + return rc; + + /* this means that no space left on device */ + if ((cls_added == 0) && (bytes_remain == 0)) + set_errno_and_return_minus_one(ENOSPC); + + /* check wether we satisfied request for 'cls2add' clusters */ + if (cls2add != cls_added) + *a_length = new_length - + ((cls2add - cls_added - 1) << fs_info->vol.bpc_log2) - + (bytes2add & (fs_info->vol.bpc - 1)); + + /* add new chain to the end of existed */ + if ( fat_fd->fat_file_size == 0 ) + { + fat_fd->map.disk_cln = fat_fd->cln = chain; + fat_fd->map.file_cln = 0; + } + else + { + if (fat_fd->map.last_cln != FAT_UNDEFINED_VALUE) + { + old_last_cl = fat_fd->map.last_cln; + } + else + { + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, + (fat_fd->fat_file_size - 1), &old_last_cl); + if ( rc != RC_OK ) + { + fat_free_fat_clusters_chain(mt_entry, chain); + return rc; + } + } + + rc = fat_set_fat_cluster(mt_entry, old_last_cl, chain); + if ( rc != RC_OK ) + { + fat_free_fat_clusters_chain(mt_entry, chain); + return rc; + } + fat_buf_release(fs_info); + } + + /* update number of the last cluster of the file if it changed */ + if (cls_added != 0) + { + fat_fd->map.last_cln = last_cl; + if (fat_fd->fat_file_type == FAT_DIRECTORY) + { + rc = fat_init_clusters_chain(mt_entry, chain); + if ( rc != RC_OK ) + { + fat_free_fat_clusters_chain(mt_entry, chain); + return rc; + } + } + } + + return RC_OK; +} + +/* fat_file_truncate -- + * Truncate fat-file. If new length greater than current fat-file size - + * do nothing. Otherwise find first cluster to free and free all clusters + * in the chain starting from this cluster. + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * new_length - new length + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +fat_file_truncate( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 new_length + ) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 cur_cln = 0; + unsigned32 cl_start = 0; + unsigned32 new_last_cln = FAT_UNDEFINED_VALUE; + + + if ( new_length >= fat_fd->fat_file_size ) + return rc; + + assert(fat_fd->fat_file_size); + + cl_start = (new_length + fs_info->vol.bpc - 1) >> fs_info->vol.bpc_log2; + + if ((cl_start << fs_info->vol.bpc_log2) >= fat_fd->fat_file_size) + return RC_OK; + + if (cl_start != 0) + { + rc = fat_file_lseek(mt_entry, fat_fd, cl_start - 1, &new_last_cln); + if (rc != RC_OK) + return rc; + + } + + rc = fat_file_lseek(mt_entry, fat_fd, cl_start, &cur_cln); + if (rc != RC_OK) + return rc; + + rc = fat_free_fat_clusters_chain(mt_entry, cur_cln); + if (rc != RC_OK) + return rc; + + if (cl_start != 0) + { + rc = fat_set_fat_cluster(mt_entry, new_last_cln, FAT_GENFAT_EOC); + if ( rc != RC_OK ) + return rc; + fat_fd->map.file_cln = cl_start - 1; + fat_fd->map.disk_cln = new_last_cln; + fat_fd->map.last_cln = new_last_cln; + } + return RC_OK; +} + +/* fat_file_ioctl -- + * F_CLU_NUM: + * make mapping between serial number of the cluster in fat-file and + * its real number on the volume + * + * PARAMETERS: + * fat_fd - fat-file descriptor + * mt_entry - mount table entry + * cmd - command + * ... + * + * RETURNS: + * RC_OK on success, or -1 if error occured and errno set appropriately + */ +int +fat_file_ioctl( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + int cmd, + ...) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 cur_cln = 0; + unsigned32 cl_start = 0; + unsigned32 pos = 0; + unsigned32 *ret; + va_list ap; + + va_start(ap, cmd); + + switch (cmd) + { + case F_CLU_NUM: + pos = va_arg(ap, int); + ret = va_arg(ap, int *); + + /* sanity check */ + if ( pos >= fat_fd->fat_file_size ) + set_errno_and_return_minus_one( EIO ); + + if ((FAT_FD_OF_ROOT_DIR(fat_fd)) && + (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16))) + { + /* cluster 0 (zero) reserved for root dir */ + *ret = 0; + return RC_OK; + } + + cl_start = pos >> fs_info->vol.bpc_log2; + + rc = fat_file_lseek(mt_entry, fat_fd, cl_start, &cur_cln); + if ( rc != RC_OK ) + return rc; + + *ret = cur_cln; + break; + + default: + errno = EINVAL; + rc = -1; + break; + } + return rc; +} + +/* fat_file_mark_removed -- + * Remove the fat-file descriptor from "valid" hash table, insert it + * into "removed-but-still-open" hash table and set up "removed" bit. + * + * PARAMETERS: + * fat_fd - fat-file descriptor + * mt_entry - mount table entry + * + * RETURNS: + * None + */ +void +fat_file_mark_removed( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 key = 0; + + key = fat_construct_key(mt_entry, fat_fd->info_cln, fat_fd->info_ofs); + + _hash_delete(fs_info->vhash, key, fat_fd->ino, fat_fd); + + _hash_insert(fs_info->rhash, key, fat_fd->ino, fat_fd); + + fat_fd->flags |= FAT_FILE_REMOVED; +} + +/* fat_file_datasync -- + * Synchronize fat-file - flush all buffered data to the media. + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * + * RETURNS: + * RC_OK on success, or -1 if error occured and errno set appropriately + */ +int +fat_file_datasync( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 cur_cln = fat_fd->cln; + bdbuf_buffer *block = NULL; + unsigned32 sec = 0; + unsigned32 i = 0; + + if (fat_fd->fat_file_size == 0) + return RC_OK; + + /* + * we can use only one bdbuf :( and we also know that cache is useless + * for sync operation, so don't use it + */ + rc = fat_buf_release(fs_info); + if (rc != RC_OK) + return rc; + + /* for each cluster of the file ... */ + while ((cur_cln & fs_info->vol.mask) != fs_info->vol.eoc_val) + { + sec = fat_cluster_num_to_sector_num(mt_entry, cur_cln); + /* for each sector in cluster ... */ + for ( i = 0; i < fs_info->vol.spc; i++ ) + { + /* ... sync it */ + sc = rtems_bdbuf_read(fs_info->vol.dev, (sec + i), &block); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one( EIO ); + + sc = rtems_bdbuf_sync(block); + if ( sc != RTEMS_SUCCESSFUL ) + set_errno_and_return_minus_one( EIO ); + } + + rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln); + if ( rc != RC_OK ) + return rc; + } + return rc; +} + +/* fat_file_size -- + * Calculate fat-file size - fat-file is nothing that clusters chain, so + * go through all clusters in the chain and count it. Only + * special case is root directory for FAT12/16 volumes. + * This function is used only for directories which are fat-files with + * non-zero length, hence 'fat_fd->cln' always contains valid data. + * Calculated size is stored in 'fat_file_size' field of fat-file + * descriptor. + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +fat_file_size( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd + ) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 cur_cln = fat_fd->cln; + unsigned32 save_cln = 0; + + /* Have we requested root dir size for FAT12/16? */ + if ((FAT_FD_OF_ROOT_DIR(fat_fd)) && + (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16))) + { + fat_fd->fat_file_size = fs_info->vol.rdir_size; + return rc; + } + + fat_fd->fat_file_size = 0; + + while ((cur_cln & fs_info->vol.mask) != fs_info->vol.eoc_val) + { + save_cln = cur_cln; + rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln); + if ( rc != RC_OK ) + return rc; + + fat_fd->fat_file_size += fs_info->vol.bpc; + } + fat_fd->map.last_cln = save_cln; + return rc; +} + +/* hash support routines */ + +/* _hash_insert -- + * Insert elemnt into hash based on key 'key1' + * + * PARAMETERS: + * hash - hash element will be inserted into + * key1 - key on which insertion is based on + * key2 - not used during insertion + * el - element to insert + * + * RETURNS: + * None + */ +static inline void +_hash_insert(Chain_Control *hash, unsigned32 key1, unsigned32 key2, + fat_file_fd_t *el) +{ + _Chain_Append((hash) + ((key1) % FAT_HASH_MODULE), &(el)->link); +} + + +/* _hash_delete -- + * Remove element from hash + * + * PARAMETERS: + * hash - hash element will be removed from + * key1 - not used + * key2 - not used + * el - element to delete + * + * RETURNS: + * None + */ +static inline void +_hash_delete(Chain_Control *hash, unsigned32 key1, unsigned32 key2, + fat_file_fd_t *el) +{ + _Chain_Extract(&(el)->link); +} + +/* _hash_search -- + * Search element in hash. If both keys match pointer to found element + * is returned + * + * PARAMETERS: + * mt_entry - mount table entry + * hash - hash element will be removed from + * key1 - search key + * key2 - search key + * ret - placeholder for result + * + * RETURNS: + * 0 and pointer to found element on success, -1 otherwise + */ +static inline int +_hash_search( + rtems_filesystem_mount_table_entry_t *mt_entry, + Chain_Control *hash, + unsigned32 key1, + unsigned32 key2, + void **ret + ) +{ + unsigned32 mod = (key1) % FAT_HASH_MODULE; + Chain_Node *the_node = ((Chain_Control *)((hash) + mod))->first; + + for ( ; !_Chain_Is_tail((hash) + mod, the_node) ; ) + { + fat_file_fd_t *ffd = (fat_file_fd_t *)the_node; + unsigned32 ck = + fat_construct_key(mt_entry, ffd->info_cln, ffd->info_ofs); + + if ( (key1) == ck) + { + if ( ((key2) == 0) || ((key2) == ffd->ino) ) + { + *ret = (void *)the_node; + return 0; + } + } + the_node = the_node->next; + } + return -1; +} + +static int +fat_file_lseek( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 file_cln, + unsigned32 *disk_cln + ) +{ + int rc = RC_OK; +/* + assert(fat_fd->fat_file_size); + */ + if (file_cln == fat_fd->map.file_cln) + *disk_cln = fat_fd->map.disk_cln; + else + { + unsigned32 cur_cln; + unsigned32 count; + unsigned32 i; + + if (file_cln > fat_fd->map.file_cln) + { + cur_cln = fat_fd->map.disk_cln; + count = file_cln - fat_fd->map.file_cln; + } + else + { + cur_cln = fat_fd->cln; + count = file_cln; + } + + /* skip over the clusters */ + for (i = 0; i < count; i++) + { + rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln); + if ( rc != RC_OK ) + return rc; + } + + /* update cache */ + fat_fd->map.file_cln = file_cln; + fat_fd->map.disk_cln = cur_cln; + + *disk_cln = cur_cln; + } + return RC_OK; +} |