From f36a7bfcde756cae3b3e933f0e688137fb684be8 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Thu, 28 Feb 2002 20:43:50 +0000 Subject: 2002-02-28 Victor V. Vengerov * 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. --- c/src/exec/libfs/ChangeLog | 17 + c/src/exec/libfs/configure.ac | 3 + c/src/exec/libfs/src/Makefile.am | 2 +- c/src/exec/libfs/src/dosfs/.cvsignore | 6 + c/src/exec/libfs/src/dosfs/Makefile.am | 80 ++ c/src/exec/libfs/src/dosfs/dosfs.h | 31 + c/src/exec/libfs/src/dosfs/fat.c | 695 ++++++++++++++ c/src/exec/libfs/src/dosfs/fat.h | 489 ++++++++++ c/src/exec/libfs/src/dosfs/fat_fat_operations.c | 445 +++++++++ c/src/exec/libfs/src/dosfs/fat_fat_operations.h | 58 ++ c/src/exec/libfs/src/dosfs/fat_file.c | 979 +++++++++++++++++++ c/src/exec/libfs/src/dosfs/fat_file.h | 195 ++++ c/src/exec/libfs/src/dosfs/msdos.h | 408 ++++++++ c/src/exec/libfs/src/dosfs/msdos_create.c | 208 +++++ c/src/exec/libfs/src/dosfs/msdos_dir.c | 483 ++++++++++ c/src/exec/libfs/src/dosfs/msdos_eval.c | 435 +++++++++ c/src/exec/libfs/src/dosfs/msdos_file.c | 485 ++++++++++ c/src/exec/libfs/src/dosfs/msdos_free.c | 56 ++ c/src/exec/libfs/src/dosfs/msdos_fsunmount.c | 71 ++ c/src/exec/libfs/src/dosfs/msdos_handlers_dir.c | 36 + c/src/exec/libfs/src/dosfs/msdos_handlers_file.c | 36 + c/src/exec/libfs/src/dosfs/msdos_init.c | 60 ++ c/src/exec/libfs/src/dosfs/msdos_initsupp.c | 149 +++ c/src/exec/libfs/src/dosfs/msdos_misc.c | 1087 ++++++++++++++++++++++ c/src/exec/libfs/src/dosfs/msdos_mknod.c | 90 ++ c/src/exec/libfs/src/dosfs/msdos_node_type.c | 58 ++ c/src/exec/libfs/wrapup/Makefile.am | 4 +- c/src/libfs/ChangeLog | 17 + c/src/libfs/configure.ac | 3 + c/src/libfs/src/Makefile.am | 2 +- c/src/libfs/src/dosfs/.cvsignore | 6 + c/src/libfs/src/dosfs/Makefile.am | 80 ++ c/src/libfs/src/dosfs/config.h.in | 1 + c/src/libfs/src/dosfs/dosfs.h | 31 + c/src/libfs/src/dosfs/fat.c | 695 ++++++++++++++ c/src/libfs/src/dosfs/fat.h | 489 ++++++++++ c/src/libfs/src/dosfs/fat_fat_operations.c | 445 +++++++++ c/src/libfs/src/dosfs/fat_fat_operations.h | 58 ++ c/src/libfs/src/dosfs/fat_file.c | 979 +++++++++++++++++++ c/src/libfs/src/dosfs/fat_file.h | 195 ++++ c/src/libfs/src/dosfs/msdos.h | 408 ++++++++ c/src/libfs/src/dosfs/msdos_create.c | 208 +++++ c/src/libfs/src/dosfs/msdos_dir.c | 483 ++++++++++ c/src/libfs/src/dosfs/msdos_eval.c | 435 +++++++++ c/src/libfs/src/dosfs/msdos_file.c | 485 ++++++++++ c/src/libfs/src/dosfs/msdos_free.c | 56 ++ c/src/libfs/src/dosfs/msdos_fsunmount.c | 71 ++ c/src/libfs/src/dosfs/msdos_handlers_dir.c | 36 + c/src/libfs/src/dosfs/msdos_handlers_file.c | 36 + c/src/libfs/src/dosfs/msdos_init.c | 60 ++ c/src/libfs/src/dosfs/msdos_initsupp.c | 149 +++ c/src/libfs/src/dosfs/msdos_misc.c | 1087 ++++++++++++++++++++++ c/src/libfs/src/dosfs/msdos_mknod.c | 90 ++ c/src/libfs/src/dosfs/msdos_node_type.c | 58 ++ c/src/libfs/src/dosfs/stamp-h2.in | 0 c/src/libfs/wrapup/Makefile.am | 4 +- cpukit/libfs/ChangeLog | 17 + cpukit/libfs/configure.ac | 3 + cpukit/libfs/src/Makefile.am | 2 +- cpukit/libfs/src/dosfs/.cvsignore | 6 + cpukit/libfs/src/dosfs/Makefile.am | 80 ++ cpukit/libfs/src/dosfs/dosfs.h | 31 + cpukit/libfs/src/dosfs/fat.c | 695 ++++++++++++++ cpukit/libfs/src/dosfs/fat.h | 489 ++++++++++ cpukit/libfs/src/dosfs/fat_fat_operations.c | 445 +++++++++ cpukit/libfs/src/dosfs/fat_fat_operations.h | 58 ++ cpukit/libfs/src/dosfs/fat_file.c | 979 +++++++++++++++++++ cpukit/libfs/src/dosfs/fat_file.h | 195 ++++ cpukit/libfs/src/dosfs/msdos.h | 408 ++++++++ cpukit/libfs/src/dosfs/msdos_create.c | 208 +++++ cpukit/libfs/src/dosfs/msdos_dir.c | 483 ++++++++++ cpukit/libfs/src/dosfs/msdos_eval.c | 435 +++++++++ cpukit/libfs/src/dosfs/msdos_file.c | 485 ++++++++++ cpukit/libfs/src/dosfs/msdos_free.c | 56 ++ cpukit/libfs/src/dosfs/msdos_fsunmount.c | 71 ++ cpukit/libfs/src/dosfs/msdos_handlers_dir.c | 36 + cpukit/libfs/src/dosfs/msdos_handlers_file.c | 36 + cpukit/libfs/src/dosfs/msdos_init.c | 60 ++ cpukit/libfs/src/dosfs/msdos_initsupp.c | 149 +++ cpukit/libfs/src/dosfs/msdos_misc.c | 1087 ++++++++++++++++++++++ cpukit/libfs/src/dosfs/msdos_mknod.c | 90 ++ cpukit/libfs/src/dosfs/msdos_node_type.c | 58 ++ cpukit/libfs/wrapup/Makefile.am | 4 +- 83 files changed, 19993 insertions(+), 6 deletions(-) create mode 100644 c/src/exec/libfs/src/dosfs/.cvsignore create mode 100644 c/src/exec/libfs/src/dosfs/Makefile.am create mode 100644 c/src/exec/libfs/src/dosfs/dosfs.h create mode 100644 c/src/exec/libfs/src/dosfs/fat.c create mode 100644 c/src/exec/libfs/src/dosfs/fat.h create mode 100644 c/src/exec/libfs/src/dosfs/fat_fat_operations.c create mode 100644 c/src/exec/libfs/src/dosfs/fat_fat_operations.h create mode 100644 c/src/exec/libfs/src/dosfs/fat_file.c create mode 100644 c/src/exec/libfs/src/dosfs/fat_file.h create mode 100644 c/src/exec/libfs/src/dosfs/msdos.h create mode 100644 c/src/exec/libfs/src/dosfs/msdos_create.c create mode 100644 c/src/exec/libfs/src/dosfs/msdos_dir.c create mode 100644 c/src/exec/libfs/src/dosfs/msdos_eval.c create mode 100644 c/src/exec/libfs/src/dosfs/msdos_file.c create mode 100644 c/src/exec/libfs/src/dosfs/msdos_free.c create mode 100644 c/src/exec/libfs/src/dosfs/msdos_fsunmount.c create mode 100644 c/src/exec/libfs/src/dosfs/msdos_handlers_dir.c create mode 100644 c/src/exec/libfs/src/dosfs/msdos_handlers_file.c create mode 100644 c/src/exec/libfs/src/dosfs/msdos_init.c create mode 100644 c/src/exec/libfs/src/dosfs/msdos_initsupp.c create mode 100644 c/src/exec/libfs/src/dosfs/msdos_misc.c create mode 100644 c/src/exec/libfs/src/dosfs/msdos_mknod.c create mode 100644 c/src/exec/libfs/src/dosfs/msdos_node_type.c create mode 100644 c/src/libfs/src/dosfs/.cvsignore create mode 100644 c/src/libfs/src/dosfs/Makefile.am create mode 100644 c/src/libfs/src/dosfs/config.h.in create mode 100644 c/src/libfs/src/dosfs/dosfs.h create mode 100644 c/src/libfs/src/dosfs/fat.c create mode 100644 c/src/libfs/src/dosfs/fat.h create mode 100644 c/src/libfs/src/dosfs/fat_fat_operations.c create mode 100644 c/src/libfs/src/dosfs/fat_fat_operations.h create mode 100644 c/src/libfs/src/dosfs/fat_file.c create mode 100644 c/src/libfs/src/dosfs/fat_file.h create mode 100644 c/src/libfs/src/dosfs/msdos.h create mode 100644 c/src/libfs/src/dosfs/msdos_create.c create mode 100644 c/src/libfs/src/dosfs/msdos_dir.c create mode 100644 c/src/libfs/src/dosfs/msdos_eval.c create mode 100644 c/src/libfs/src/dosfs/msdos_file.c create mode 100644 c/src/libfs/src/dosfs/msdos_free.c create mode 100644 c/src/libfs/src/dosfs/msdos_fsunmount.c create mode 100644 c/src/libfs/src/dosfs/msdos_handlers_dir.c create mode 100644 c/src/libfs/src/dosfs/msdos_handlers_file.c create mode 100644 c/src/libfs/src/dosfs/msdos_init.c create mode 100644 c/src/libfs/src/dosfs/msdos_initsupp.c create mode 100644 c/src/libfs/src/dosfs/msdos_misc.c create mode 100644 c/src/libfs/src/dosfs/msdos_mknod.c create mode 100644 c/src/libfs/src/dosfs/msdos_node_type.c create mode 100644 c/src/libfs/src/dosfs/stamp-h2.in create mode 100644 cpukit/libfs/src/dosfs/.cvsignore create mode 100644 cpukit/libfs/src/dosfs/Makefile.am create mode 100644 cpukit/libfs/src/dosfs/dosfs.h create mode 100644 cpukit/libfs/src/dosfs/fat.c create mode 100644 cpukit/libfs/src/dosfs/fat.h create mode 100644 cpukit/libfs/src/dosfs/fat_fat_operations.c create mode 100644 cpukit/libfs/src/dosfs/fat_fat_operations.h create mode 100644 cpukit/libfs/src/dosfs/fat_file.c create mode 100644 cpukit/libfs/src/dosfs/fat_file.h create mode 100644 cpukit/libfs/src/dosfs/msdos.h create mode 100644 cpukit/libfs/src/dosfs/msdos_create.c create mode 100644 cpukit/libfs/src/dosfs/msdos_dir.c create mode 100644 cpukit/libfs/src/dosfs/msdos_eval.c create mode 100644 cpukit/libfs/src/dosfs/msdos_file.c create mode 100644 cpukit/libfs/src/dosfs/msdos_free.c create mode 100644 cpukit/libfs/src/dosfs/msdos_fsunmount.c create mode 100644 cpukit/libfs/src/dosfs/msdos_handlers_dir.c create mode 100644 cpukit/libfs/src/dosfs/msdos_handlers_file.c create mode 100644 cpukit/libfs/src/dosfs/msdos_init.c create mode 100644 cpukit/libfs/src/dosfs/msdos_initsupp.c create mode 100644 cpukit/libfs/src/dosfs/msdos_misc.c create mode 100644 cpukit/libfs/src/dosfs/msdos_mknod.c create mode 100644 cpukit/libfs/src/dosfs/msdos_node_type.c diff --git a/c/src/exec/libfs/ChangeLog b/c/src/exec/libfs/ChangeLog index b4dd8e9f61..4166f2e369 100644 --- a/c/src/exec/libfs/ChangeLog +++ b/c/src/exec/libfs/ChangeLog @@ -1,3 +1,20 @@ +2002-02-28 Victor V. Vengerov + + * 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. + 2002-01-07 Ralf Corsepius * src/imfs/imfs_load_tar.c: Add include . diff --git a/c/src/exec/libfs/configure.ac b/c/src/exec/libfs/configure.ac index 4e2adeb1b5..7f36fac28e 100644 --- a/c/src/exec/libfs/configure.ac +++ b/c/src/exec/libfs/configure.ac @@ -5,6 +5,7 @@ AC_PREREQ(2.52) AC_INIT AC_CONFIG_SRCDIR([src/imfs/imfs.h]) +AC_CONFIG_SRCDIR([src/dosfs/dosfs.h]) RTEMS_TOP(../../..) AC_CONFIG_AUX_DIR(../../..) @@ -27,11 +28,13 @@ RTEMS_CANONICALIZE_TOOLS AM_CONDITIONAL(UNIX,test x"$RTEMS_CPU" = x"unix") AM_CONFIG_HEADER(src/imfs/config.h) +AM_CONFIG_HEADER(src/dosfs/config.h) # Explicitly list all Makefiles here AC_CONFIG_FILES([Makefile src/Makefile src/imfs/Makefile +src/dosfs/Makefile wrapup/Makefile ]) AC_OUTPUT diff --git a/c/src/exec/libfs/src/Makefile.am b/c/src/exec/libfs/src/Makefile.am index 126a226126..fcd82899b4 100644 --- a/c/src/exec/libfs/src/Makefile.am +++ b/c/src/exec/libfs/src/Makefile.am @@ -4,7 +4,7 @@ AUTOMAKE_OPTIONS = foreign 1.4 -SUBDIRS = imfs +SUBDIRS = imfs dosfs include $(top_srcdir)/../../../automake/subdirs.am include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/exec/libfs/src/dosfs/.cvsignore b/c/src/exec/libfs/src/dosfs/.cvsignore new file mode 100644 index 0000000000..7bb609bf24 --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/.cvsignore @@ -0,0 +1,6 @@ +Makefile +Makefile.in +config.h +config.h.in +stamp-h +stamp-h.in diff --git a/c/src/exec/libfs/src/dosfs/Makefile.am b/c/src/exec/libfs/src/dosfs/Makefile.am new file mode 100644 index 0000000000..44c9f4cd2f --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/Makefile.am @@ -0,0 +1,80 @@ +## +## $Id$ +## + +AUTOMAKE_OPTIONS = foreign 1.4 + +INCLUDES = -I. + +LIBNAME = libdosfs +LIB = ${ARCH}/${LIBNAME}.a + +FATFS_C_FILES = fat.c fat_fat_operations.c fat_file.c + +DOSFS_C_FILES = msdos_create.c msdos_dir.c msdos_eval.c msdos_file.c \ + msdos_free.c msdos_fsunmount.c msdos_handlers_dir.c \ + msdos_handlers_file.c msdos_init.c msdos_initsupp.c \ + msdos_misc.c msdos_mknod.c msdos_node_type.c + + +UNIX_C_FILES = msdos_unixstub.c + +EMBEDDED_C_FILES = $(FATFS_C_FILES) $(DOSFS_C_FILES) + +COMMON_C_FILES = + +if UNIX +C_FILES = $(COMMON_C_FILES) $(UNIX_C_FILES) +else +C_FILES = $(COMMON_C_FILES) $(EMBEDDED_C_FILES) +endif +C_O_FILES = $(C_FILES:%.c=${ARCH}/%.o) + +include_HEADERS = fat.h fat_fat_operations.h \ + fat_file.h msdos.h dosfs.h +SYS_H_FILES = +RTEMS_H_FILES = +noinst_HEADERS = + +include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg +include $(top_srcdir)/../../../automake/compile.am +include $(top_srcdir)/../../../automake/lib.am + +PREINSTALL_FILES = $(PROJECT_INCLUDE) $(PROJECT_INCLUDE)/rtems \ + $(PROJECT_INCLUDE)/sys $(include_HEADERS:%=$(PROJECT_INCLUDE)/%) \ + $(RTEMS_H_FILES:%=$(PROJECT_INCLUDE)/rtems/%) \ + $(SYS_H_FILES:%=$(PROJECT_INCLUDE)/sys/%) + +$(PROJECT_INCLUDE): + @$(mkinstalldirs) $@ +$(PROJECT_INCLUDE)/rtems: + @$(mkinstalldirs) $@ +$(PROJECT_INCLUDE)/sys: + @$(mkinstalldirs) $@ + +$(PROJECT_INCLUDE)/%.h: %.h + $(INSTALL_DATA) $< $@ +$(PROJECT_INCLUDE)/rtems/%.h: %.h + $(INSTALL_DATA) $< $@ +$(PROJECT_INCLUDE)/sys/%.h: %.h + $(INSTALL_DATA) $< $@ + +OBJS = $(C_O_FILES) + +# +# Add local stuff here using += +# + +AM_CFLAGS += $(LIBC_DEFINES) + +all-local: ${ARCH} $(LIB) + +$(LIB): ${OBJS} + $(make-library) + +DOC_FILES = + +EXTRA_DIST = $(DOC_FILES) $(COMMON_C_FILES) $(EMBEDDED_C_FILES) \ + $(UNIX_C_FILES) $(RTEMS_H_FILES) $(SYS_H_FILES) + +include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/exec/libfs/src/dosfs/dosfs.h b/c/src/exec/libfs/src/dosfs/dosfs.h new file mode 100644 index 0000000000..4cea929d4c --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/dosfs.h @@ -0,0 +1,31 @@ +/* + * dosfs.h + * + * Application interface to MSDOS filesystem. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_DOSFS_H__ +#define __DOSFS_DOSFS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +extern rtems_filesystem_operations_table msdos_ops; + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_DOSFS_H__ */ diff --git a/c/src/exec/libfs/src/dosfs/fat.c b/c/src/exec/libfs/src/dosfs/fat.c new file mode 100644 index 0000000000..852c104781 --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/fat.c @@ -0,0 +1,695 @@ +/* + * fat.c + * + * Low-level operations on a volume with FAT filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * @(#) $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" + +/* _fat_block_read -- + * This function reads 'count' bytes from device filesystem is mounted on, + * starts at 'start+offset' position where 'start' computed in sectors + * and 'offset' is offset inside sector (reading may cross sectors + * boundary; in this case assumed we want to read sequential sector(s)) + * + * PARAMETERS: + * mt_entry - mount table entry + * start - sector num to start read from + * offset - offset inside sector 'start' + * count - count of bytes to read + * buff - buffer provided by user + * + * RETURNS: + * bytes read on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +_fat_block_read( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start, + unsigned32 offset, + unsigned32 count, + void *buff + ) +{ + int rc = RC_OK; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + ssize_t cmpltd = 0; + unsigned32 blk = start; + unsigned32 ofs = offset; + bdbuf_buffer *block = NULL; + unsigned32 c = 0; + + while (count > 0) + { + rc = fat_buf_access(fs_info, blk, FAT_OP_TYPE_READ, &block); + if (rc != RC_OK) + return rc; + + c = MIN(count, (fs_info->vol.bps - ofs)); + memcpy((buff + cmpltd), (block->buffer + ofs), c); + + count -= c; + cmpltd += c; + blk++; + ofs = 0; + } + return cmpltd; +} + +/* _fat_block_write -- + * This function write 'count' bytes to device filesystem is mounted on, + * starts at 'start+offset' position where 'start' computed in sectors + * and 'offset' is offset inside sector (writing may cross sectors + * boundary; in this case assumed we want to write sequential sector(s)) + * + * PARAMETERS: + * mt_entry - mount table entry + * start - sector num to start read from + * offset - offset inside sector 'start' + * count - count of bytes to write + * buff - buffer provided by user + * + * RETURNS: + * bytes written on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +_fat_block_write( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start, + unsigned32 offset, + unsigned32 count, + const void *buff) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + ssize_t cmpltd = 0; + unsigned32 blk = start; + unsigned32 ofs = offset; + bdbuf_buffer *block = NULL; + unsigned32 c = 0; + + while(count > 0) + { + c = MIN(count, (fs_info->vol.bps - ofs)); + + if (c == fs_info->vol.bps) + rc = fat_buf_access(fs_info, blk, FAT_OP_TYPE_GET, &block); + else + rc = fat_buf_access(fs_info, blk, FAT_OP_TYPE_READ, &block); + if (rc != RC_OK) + return rc; + + memcpy((block->buffer + ofs), (buff + cmpltd), c); + + fat_buf_mark_modified(fs_info); + + count -= c; + cmpltd +=c; + blk++; + ofs = 0; + } + return cmpltd; +} + + + + +/* fat_cluster_read -- + * wrapper for reading a whole cluster at once + * + * PARAMETERS: + * mt_entry - mount table entry + * cln - number of cluster to read + * buff - buffer provided by user + * + * RETURNS: + * bytes read on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +fat_cluster_read( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + void *buff + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 fsec = 0; + + fsec = fat_cluster_num_to_sector_num(mt_entry, cln); + + return _fat_block_read(mt_entry, fsec, 0, + fs_info->vol.spc << fs_info->vol.sec_log2, buff); +} + +/* fat_cluster_write -- + * wrapper for writting a whole cluster at once + * + * PARAMETERS: + * mt_entry - mount table entry + * cln - number of cluster to write + * buff - buffer provided by user + * + * RETURNS: + * bytes written on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +fat_cluster_write( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + const void *buff + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 fsec = 0; + + fsec = fat_cluster_num_to_sector_num(mt_entry, cln); + + return _fat_block_write(mt_entry, fsec, 0, + fs_info->vol.spc << fs_info->vol.sec_log2, buff); +} + +/* fat_init_volume_info -- + * Get inforamtion about volume on which filesystem is mounted on + * + * PARAMETERS: + * mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured + * and errno set appropriately + */ +int +fat_init_volume_info(rtems_filesystem_mount_table_entry_t *mt_entry) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + register fat_vol_t *vol = &fs_info->vol; + unsigned32 data_secs = 0; + char boot_rec[FAT_MAX_BPB_SIZE]; + char fs_info_sector[FAT_USEFUL_INFO_SIZE]; + ssize_t ret = 0; + int fd; + struct stat stat_buf; + int i = 0; + + rc = stat(mt_entry->dev, &stat_buf); + if (rc == -1) + return rc; + + /* rtmes feature: no block devices, all are character devices */ + if (!S_ISCHR(stat_buf.st_mode)) + set_errno_and_return_minus_one(ENOTBLK); + + /* check that device is registred as block device and lock it */ + vol->dd = rtems_disk_lookup(stat_buf.st_dev); + if (vol->dd == NULL) + set_errno_and_return_minus_one(ENOTBLK); + + vol->dev = stat_buf.st_dev; + + fd = open(mt_entry->dev, O_RDONLY); + if (fd == -1) + { + rtems_disk_release(vol->dd); + return -1; + } + + ret = read(fd, (void *)boot_rec, FAT_MAX_BPB_SIZE); + if ( ret != FAT_MAX_BPB_SIZE ) + { + close(fd); + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( EIO ); + } + close(fd); + + vol->bps = FAT_BR_BYTES_PER_SECTOR(boot_rec); + + if ( (vol->bps != 512) && + (vol->bps != 1024) && + (vol->bps != 2048) && + (vol->bps != 4096)) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( EINVAL ); + } + + for (vol->sec_mul = 0, i = (vol->bps >> FAT_SECTOR512_BITS); (i & 1) == 0; + i >>= 1, vol->sec_mul++); + for (vol->sec_log2 = 0, i = vol->bps; (i & 1) == 0; + i >>= 1, vol->sec_log2++); + + vol->spc = FAT_BR_SECTORS_PER_CLUSTER(boot_rec); + for (vol->spc_log2 = 0, i = vol->spc; (i & 1) == 0; + i >>= 1, vol->spc_log2++); + + /* + * According to M$ White Paper "bytes per cluster" value + * greater than 32K is invalid + */ + if ((vol->bpc = vol->bps << vol->spc_log2) > MS_BYTES_PER_CLUSTER_LIMIT) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one(EINVAL); + } + + for (vol->bpc_log2 = 0, i = vol->bpc; (i & 1) == 0; + i >>= 1, vol->bpc_log2++); + + vol->fats = FAT_BR_FAT_NUM(boot_rec); + vol->fat_loc = FAT_BR_RESERVED_SECTORS_NUM(boot_rec); + + vol->rdir_entrs = FAT_BR_FILES_PER_ROOT_DIR(boot_rec); + + /* calculate the count of sectors occupied by the root directory */ + vol->rdir_secs = ((vol->rdir_entrs * FAT_DIRENTRY_SIZE) + (vol->bps - 1)) / + vol->bps; + + vol->rdir_size = vol->rdir_secs << vol->sec_log2; + + if ( (FAT_BR_SECTORS_PER_FAT(boot_rec)) != 0) + vol->fat_length = FAT_BR_SECTORS_PER_FAT(boot_rec); + else + vol->fat_length = FAT_BR_SECTORS_PER_FAT32(boot_rec); + + vol->data_fsec = vol->fat_loc + vol->fats * vol->fat_length + + vol->rdir_secs; + + /* for FAT12/16 root dir starts at(sector) */ + vol->rdir_loc = vol->fat_loc + vol->fats * vol->fat_length; + + if ( (FAT_BR_TOTAL_SECTORS_NUM16(boot_rec)) != 0) + vol->tot_secs = FAT_BR_TOTAL_SECTORS_NUM16(boot_rec); + else + vol->tot_secs = FAT_BR_TOTAL_SECTORS_NUM32(boot_rec); + + data_secs = vol->tot_secs - vol->data_fsec; + + vol->data_cls = data_secs / vol->spc; + + /* determine FAT type at least */ + if ( vol->data_cls < FAT_FAT12_MAX_CLN) + { + vol->type = FAT_FAT12; + vol->mask = FAT_FAT12_MASK; + vol->eoc_val = FAT_FAT12_EOC; + } + else + { + if ( vol->data_cls < FAT_FAT16_MAX_CLN) + { + vol->type = FAT_FAT16; + vol->mask = FAT_FAT16_MASK; + vol->eoc_val = FAT_FAT16_EOC; + } + else + { + vol->type = FAT_FAT32; + vol->mask = FAT_FAT32_MASK; + vol->eoc_val = FAT_FAT32_EOC; + } + } + + if (vol->type == FAT_FAT32) + { + vol->rdir_cl = FAT_BR_FAT32_ROOT_CLUSTER(boot_rec); + + vol->mirror = FAT_BR_EXT_FLAGS(boot_rec) & FAT_BR_EXT_FLAGS_MIRROR; + if (vol->mirror) + vol->afat = FAT_BR_EXT_FLAGS(boot_rec) & FAT_BR_EXT_FLAGS_FAT_NUM; + else + vol->afat = 0; + + vol->info_sec = FAT_BR_FAT32_FS_INFO_SECTOR(boot_rec); + if( vol->info_sec == 0 ) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( EINVAL ); + } + else + { + ret = _fat_block_read(mt_entry, vol->info_sec , 0, + FAT_FSI_LEADSIG_SIZE, fs_info_sector); + if ( ret < 0 ) + { + rtems_disk_release(vol->dd); + return -1; + } + + if (FAT_FSINFO_LEAD_SIGNATURE(fs_info_sector) != + FAT_FSINFO_LEAD_SIGNATURE_VALUE) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( EINVAL ); + } + else + { + ret = _fat_block_read(mt_entry, vol->info_sec , FAT_FSI_INFO, + FAT_USEFUL_INFO_SIZE, fs_info_sector); + if ( ret < 0 ) + { + rtems_disk_release(vol->dd); + return -1; + } + + vol->free_cls = FAT_FSINFO_FREE_CLUSTER_COUNT(fs_info_sector); + vol->next_cl = FAT_FSINFO_NEXT_FREE_CLUSTER(fs_info_sector); + rc = fat_fat32_update_fsinfo_sector(mt_entry, 0xFFFFFFFF, + 0xFFFFFFFF); + if ( rc != RC_OK ) + { + rtems_disk_release(vol->dd); + return rc; + } + } + } + } + else + { + vol->rdir_cl = 0; + vol->mirror = 0; + vol->afat = 0; + vol->free_cls = 0xFFFFFFFF; + vol->next_cl = 0xFFFFFFFF; + } + vol->afat_loc = vol->fat_loc + vol->fat_length * vol->afat; + + /* set up collection of fat-files fd */ + fs_info->vhash = calloc(FAT_HASH_SIZE, sizeof(Chain_Control)); + if ( fs_info->vhash == NULL ) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( ENOMEM ); + } + + for (i = 0; i < FAT_HASH_SIZE; i++) + _Chain_Initialize_empty(fs_info->vhash + i); + + fs_info->rhash = calloc(FAT_HASH_SIZE, sizeof(Chain_Control)); + if ( fs_info->rhash == NULL ) + { + rtems_disk_release(vol->dd); + free(fs_info->vhash); + set_errno_and_return_minus_one( ENOMEM ); + } + for (i = 0; i < FAT_HASH_SIZE; i++) + _Chain_Initialize_empty(fs_info->rhash + i); + + fs_info->uino_pool_size = FAT_UINO_POOL_INIT_SIZE; + fs_info->uino_base = (vol->tot_secs << vol->sec_mul) << 4; + fs_info->index = 0; + fs_info->uino = (char *)calloc(fs_info->uino_pool_size, sizeof(char)); + if ( fs_info->uino == NULL ) + { + rtems_disk_release(vol->dd); + free(fs_info->vhash); + free(fs_info->rhash); + set_errno_and_return_minus_one( ENOMEM ); + } + fs_info->sec_buf = (char *)calloc(vol->bps, sizeof(char)); + if (fs_info->sec_buf == NULL) + { + rtems_disk_release(vol->dd); + free(fs_info->vhash); + free(fs_info->rhash); + free(fs_info->uino); + set_errno_and_return_minus_one( ENOMEM ); + } + + return RC_OK; +} + +/* fat_shutdown_drive -- + * Free all allocated resources and synchronize all necessary data + * + * PARAMETERS: + * mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured + * and errno set appropriately + */ +int +fat_shutdown_drive(rtems_filesystem_mount_table_entry_t *mt_entry) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + int i = 0; + + if (fs_info->vol.type & FAT_FAT32) + { + rc = fat_fat32_update_fsinfo_sector(mt_entry, fs_info->vol.free_cls, + fs_info->vol.next_cl); + if ( rc != RC_OK ) + rc = -1; + } + + fat_buf_release(fs_info); + + if (rtems_bdbuf_syncdev(fs_info->vol.dev) != RTEMS_SUCCESSFUL) + rc = -1; + + for (i = 0; i < FAT_HASH_SIZE; i++) + { + Chain_Node *node = NULL; + Chain_Control *the_chain = fs_info->vhash + i; + + while ( (node = _Chain_Get(the_chain)) != NULL ) + free(node); + } + + for (i = 0; i < FAT_HASH_SIZE; i++) + { + Chain_Node *node = NULL; + Chain_Control *the_chain = fs_info->rhash + i; + + while ( (node = _Chain_Get(the_chain)) != NULL ) + free(node); + } + + free(fs_info->vhash); + free(fs_info->rhash); + + free(fs_info->uino); + free(fs_info->sec_buf); + rtems_disk_release(fs_info->vol.dd); + + if (rc) + errno = EIO; + return rc; +} + +/* fat_init_clusters_chain -- + * Zeroing contents of all clusters in the chain + * + * PARAMETERS: + * mt_entry - mount table entry + * start_cluster_num - num of first cluster in the chain + * + * RETURNS: + * RC_OK on success, or -1 if error occured + * and errno set appropriately + */ +int +fat_init_clusters_chain( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start_cln + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 cur_cln = start_cln; + char *buf; + + buf = calloc(fs_info->vol.bpc, sizeof(char)); + if ( buf == NULL ) + set_errno_and_return_minus_one( EIO ); + + while ((cur_cln & fs_info->vol.mask) != fs_info->vol.eoc_val) + { + ret = fat_cluster_write(mt_entry, cur_cln, buf); + if ( ret == -1 ) + { + free(buf); + return -1; + } + + rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln); + if ( rc != RC_OK ) + { + free(buf); + return rc; + } + + } + free(buf); + return rc; +} + +#define FAT_UNIQ_INO_BASE 0x0FFFFF00 + +#define FAT_UNIQ_INO_IS_BUSY(index, arr) \ + (((arr)[((index)>>3)]>>((index) & (8-1))) & 0x01) + +#define FAT_SET_UNIQ_INO_BUSY(index, arr) \ + ((arr)[((index)>>3)] |= (0x01<<((index) & (8-1)))) + +#define FAT_SET_UNIQ_INO_FREE(index, arr) \ + ((arr)[((index)>>3)] &= (~(0x01<<((index) & (8-1))))) + +/* fat_get_unique_ino -- + * Allocate unique ino from unique ino pool + * + * PARAMETERS: + * mt_entry - mount table entry + * + * RETURNS: + * unique inode number on success, or 0 if there is no free unique inode + * number in the pool + * + * ATTENTION: + * 0 means FAILED !!! + * + */ +unsigned32 +fat_get_unique_ino(rtems_filesystem_mount_table_entry_t *mt_entry) +{ + register fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 j = 0; + rtems_boolean resrc_unsuff = FALSE; + + while (!resrc_unsuff) + { + for (j = 0; j < fs_info->uino_pool_size; j++) + { + if (!FAT_UNIQ_INO_IS_BUSY(fs_info->index, fs_info->uino)) + { + FAT_SET_UNIQ_INO_BUSY(fs_info->index, fs_info->uino); + return (fs_info->uino_base + fs_info->index); + } + fs_info->index++; + if (fs_info->index >= fs_info->uino_pool_size) + fs_info->index = 0; + } + + if ((fs_info->uino_pool_size << 1) < (0x0FFFFFFF - fs_info->uino_base)) + { + fs_info->uino_pool_size <<= 1; + fs_info->uino = realloc(fs_info->uino, fs_info->uino_pool_size); + if (fs_info->uino != NULL) + fs_info->index = fs_info->uino_pool_size; + else + resrc_unsuff = TRUE; + } + else + resrc_unsuff = TRUE; + } + return 0; +} + +/* fat_free_unique_ino -- + * Return unique ino to unique ino pool + * + * PARAMETERS: + * mt_entry - mount table entry + * ino - inode number to free + * + * RETURNS: + * None + */ +void +fat_free_unique_ino( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 ino + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + + FAT_SET_UNIQ_INO_FREE((ino - fs_info->uino_base), fs_info->uino); +} + +/* fat_ino_is_unique -- + * Test whether ino is from unique ino pool + * + * PARAMETERS: + * mt_entry - mount table entry + * ino - ino to be tested + * + * RETURNS: + * TRUE if ino is allocated from unique ino pool, FALSE otherwise + */ +inline rtems_boolean +fat_ino_is_unique( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 ino + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + + return (ino >= fs_info->uino_base); +} + +/* fat_fat32_update_fsinfo_sector -- + * Synchronize fsinfo sector for FAT32 volumes + * + * PARAMETERS: + * mt_entry - mount table entry + * free_count - count of free clusters + * next_free - the next free cluster num + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +fat_fat32_update_fsinfo_sector( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 free_count, + unsigned32 next_free + ) +{ + ssize_t ret1 = 0, ret2 = 0; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 le_free_count = 0; + unsigned32 le_next_free = 0; + + le_free_count = CT_LE_L(free_count); + le_next_free = CT_LE_L(next_free); + + ret1 = _fat_block_write(mt_entry, + fs_info->vol.info_sec, + FAT_FSINFO_FREE_CLUSTER_COUNT_OFFSET, + 4, + (char *)(&le_free_count)); + + ret2 = _fat_block_write(mt_entry, + fs_info->vol.info_sec, + FAT_FSINFO_NEXT_FREE_CLUSTER_OFFSET, + 4, + (char *)(&le_next_free)); + + if ( (ret1 < 0) || (ret2 < 0) ) + return -1; + + return RC_OK; +} + \ No newline at end of file diff --git a/c/src/exec/libfs/src/dosfs/fat.h b/c/src/exec/libfs/src/dosfs/fat.h new file mode 100644 index 0000000000..04a1f0f662 --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/fat.h @@ -0,0 +1,489 @@ +/* + * fat.h + * + * Constants/data structures/prototypes for low-level operations on a volume + * with FAT filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#ifndef __DOSFS_FAT_H__ +#define __DOSFS_FAT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* XXX: temporary hack :(( */ +#ifndef set_errno_and_return_minus_one +#define set_errno_and_return_minus_one rtems_set_errno_and_return_minus_one +#endif /* set_errno_and_return_minus_one */ + +#include +#include +#include + +#define DBG1(x) x +#define DBG2(x) x + +#ifndef RC_OK +#define RC_OK 0x00000000 +#endif + +/* + * Remember that all FAT file system on disk data structure is + * "little endian"! + * (derived from linux) + */ +/* + * Conversion from and to little-endian byte order. (no-op on i386/i486) + * + * Naming: Ca_b_c, where a: F = from, T = to, b: LE = little-endian, + * BE = big-endian, c: W = word (16 bits), L = longword (32 bits) + */ + +#if (CPU_BIG_ENDIAN == TRUE) +# define CF_LE_W(v) CPU_swap_u16(v) +# define CF_LE_L(v) CPU_swap_u32(v) +# define CT_LE_W(v) CPU_swap_u16(v) +# define CT_LE_L(v) CPU_swap_u32(v) +#else +# define CF_LE_W(v) (v) +# define CF_LE_L(v) (v) +# define CT_LE_W(v) (v) +# define CT_LE_L(v) (v) +#endif + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#define FAT_HASH_SIZE 2 +#define FAT_HASH_MODULE FAT_HASH_SIZE + + +#define FAT_SECTOR512_SIZE 512 /* sector size (bytes) */ +#define FAT_SECTOR512_BITS 9 /* log2(SECTOR_SIZE) */ + +/* maximum + 1 number of clusters for FAT12 */ +#define FAT_FAT12_MAX_CLN 4085 + +/* maximum + 1 number of clusters for FAT16 */ +#define FAT_FAT16_MAX_CLN 65525 + +#define FAT_FAT12 0x01 +#define FAT_FAT16 0x02 +#define FAT_FAT32 0x04 + +#define FAT_UNDEFINED_VALUE 0xFFFFFFFF + +#define FAT_FAT12_EOC 0x0FFF +#define FAT_FAT16_EOC 0xFFFF +#define FAT_FAT32_EOC 0x0FFFFFFF + +#define FAT_FAT12_FREE 0x0000 +#define FAT_FAT16_FREE 0x0000 +#define FAT_FAT32_FREE 0x00000000 + +#define FAT_GENFAT_EOC 0xFFFFFFFF +#define FAT_GENFAT_FREE 0x00000000 + +#define FAT_FAT12_SHIFT 0x04 + +#define FAT_FAT12_MASK 0x00000FFF +#define FAT_FAT16_MASK 0x0000FFFF +#define FAT_FAT32_MASK 0x0FFFFFFF + +#define FAT_MAX_BPB_SIZE 90 + +/* size of useful information in FSInfo sector */ +#define FAT_USEFUL_INFO_SIZE 12 + +#define FAT_VAL8(x, ofs) (unsigned8)(*((unsigned8 *)(x) + (ofs))) + +#define FAT_VAL16(x, ofs) \ + (unsigned16)( (*((unsigned8 *)(x) + (ofs))) | \ + ((*((unsigned8 *)(x) + (ofs) + 1)) << 8) ) + +#define FAT_VAL32(x, ofs) \ + (unsigned32)( (*((unsigned8 *)(x) + (ofs))) | \ + ((*((unsigned8 *)(x) + (ofs) + 1)) << 8) | \ + ((*((unsigned8 *)(x) + (ofs) + 2)) << 16) | \ + ((*((unsigned8 *)(x) + (ofs) + 3)) << 24) ) + +/* macros to access boot sector fields */ +#define FAT_BR_BYTES_PER_SECTOR(x) FAT_VAL16(x, 11) +#define FAT_BR_SECTORS_PER_CLUSTER(x) FAT_VAL8(x, 13) +#define FAT_BR_RESERVED_SECTORS_NUM(x) FAT_VAL16(x, 14) +#define FAT_BR_FAT_NUM(x) FAT_VAL8(x, 16) +#define FAT_BR_FILES_PER_ROOT_DIR(x) FAT_VAL16(x, 17) +#define FAT_BR_TOTAL_SECTORS_NUM16(x) FAT_VAL16(x, 19) +#define FAT_BR_MEDIA(x) FAT_VAL8(x, 21) +#define FAT_BR_SECTORS_PER_FAT(x) FAT_VAL16(x, 22) +#define FAT_BR_TOTAL_SECTORS_NUM32(x) FAT_VAL32(x, 32) +#define FAT_BR_SECTORS_PER_FAT32(x) FAT_VAL32(x, 36) +#define FAT_BR_EXT_FLAGS(x) FAT_VAL16(x, 40) +#define FAT_BR_FAT32_ROOT_CLUSTER(x) FAT_VAL32(x, 44) +#define FAT_BR_FAT32_FS_INFO_SECTOR(x) FAT_VAL16(x, 48) +#define FAT_FSINFO_LEAD_SIGNATURE(x) FAT_VAL32(x, 0) +/* + * I read FSInfo sector from offset 484 to access the information, so offsets + * of these fields a relative + */ +#define FAT_FSINFO_FREE_CLUSTER_COUNT(x) FAT_VAL32(x, 4) +#define FAT_FSINFO_NEXT_FREE_CLUSTER(x) FAT_VAL32(x, 8) + +#define FAT_FSINFO_FREE_CLUSTER_COUNT_OFFSET 488 + +#define FAT_FSINFO_NEXT_FREE_CLUSTER_OFFSET 492 + +#define FAT_RSRVD_CLN 0x02 + +#define FAT_FSINFO_LEAD_SIGNATURE_VALUE 0x41615252 + +#define FAT_FSI_LEADSIG_SIZE 0x04 + +#define FAT_FSI_INFO 484 + +#define MS_BYTES_PER_CLUSTER_LIMIT 0x8000 /* 32K */ + +#define FAT_BR_EXT_FLAGS_MIRROR 0x0080 + +#define FAT_BR_EXT_FLAGS_FAT_NUM 0x000F + + +#define FAT_DIRENTRY_SIZE 32 + +#define FAT_DIRENTRIES_PER_SEC512 16 + +/* + * Volume descriptor + * Description of the volume the FAT filesystem is located on - generally + * the fields of the structure corresponde to Boot Sector and BPB Srtucture + * (see M$ White Paper) fields + */ +typedef struct fat_vol_s +{ + unsigned16 bps; /* bytes per sector */ + unsigned8 sec_log2; /* log2 of bps */ + unsigned8 sec_mul; /* log2 of 512bts sectors number per sector */ + unsigned8 spc; /* sectors per cluster */ + unsigned8 spc_log2; /* log2 of spc */ + unsigned16 bpc; /* bytes per cluster */ + unsigned8 bpc_log2; /* log2 of bytes per cluster */ + unsigned8 fats; /* number of FATs */ + unsigned8 type; /* FAT type */ + unsigned32 mask; + unsigned32 eoc_val; + unsigned16 fat_loc; /* FAT start */ + unsigned32 fat_length; /* sectors per FAT */ + unsigned32 rdir_loc; /* root directory start */ + unsigned16 rdir_entrs; /* files per root directory */ + unsigned32 rdir_secs; /* sectors per root directory */ + unsigned32 rdir_size; /* root directory size in bytes */ + unsigned32 tot_secs; /* total count of sectors */ + unsigned32 data_fsec; /* first data sector */ + unsigned32 data_cls; /* count of data clusters */ + unsigned32 rdir_cl; /* first cluster of the root directory */ + unsigned16 info_sec; /* FSInfo Sector Structure location */ + unsigned32 free_cls; /* last known free clusters count */ + unsigned32 next_cl; /* next free cluster number */ + unsigned8 mirror; /* mirroring enabla/disable */ + unsigned32 afat_loc; /* active FAT location */ + unsigned8 afat; /* the number of active FAT */ + dev_t dev; /* device ID */ + disk_device *dd; /* disk device (see libblock) */ + void *private_data; /* reserved */ +} fat_vol_t; + + +typedef struct fat_cache_s +{ + unsigned32 blk_num; + rtems_boolean modified; + unsigned8 state; + bdbuf_buffer *buf; +} fat_cache_t; + +/* + * This structure identifies the instance of the filesystem on the FAT + * ("fat-file") level. + */ +typedef struct fat_fs_info_s +{ + fat_vol_t vol; /* volume descriptor */ + Chain_Control *vhash; /* "vhash" of fat-file descriptors */ + Chain_Control *rhash; /* "rhash" of fat-file descriptors */ + char *uino; /* array of unique ino numbers */ + unsigned32 index; + unsigned32 uino_pool_size; /* size */ + unsigned32 uino_base; + fat_cache_t c; /* cache */ + unsigned8 *sec_buf; /* just placeholder for anything */ +} fat_fs_info_t; + +/* + * if the name we looking for is file we store not only first data cluster + * number, but and cluster number and offset for directory entry for this + * name + */ +typedef struct fat_auxiliary_s +{ + unsigned32 cln; + unsigned32 ofs; +} fat_auxiliary_t; + +#define FAT_FAT_OFFSET(fat_type, cln) \ + ((fat_type) & FAT_FAT12 ? ((cln) + ((cln) >> 1)) : \ + (fat_type) & FAT_FAT16 ? ((cln) << 1) : \ + ((cln) << 2)) + +#define FAT_CLUSTER_IS_ODD(n) ((n) & 0x0001) + +#define FAT12_SHIFT 0x4 /* half of a byte */ + +/* initial size of array of unique ino */ +#define FAT_UINO_POOL_INIT_SIZE 0x100 + +/* cache support */ +#define FAT_CACHE_EMPTY 0x0 +#define FAT_CACHE_ACTUAL 0x1 + +#define FAT_OP_TYPE_READ 0x1 +#define FAT_OP_TYPE_GET 0x2 + +static inline unsigned32 +fat_cluster_num_to_sector_num( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln + ) +{ + register fat_fs_info_t *fs_info = mt_entry->fs_info; + + if ( (cln == 0) && (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)) ) + return fs_info->vol.rdir_loc; + + return (((cln - FAT_RSRVD_CLN) << fs_info->vol.spc_log2) + + fs_info->vol.data_fsec); +} + +static inline unsigned32 +fat_cluster_num_to_sector512_num( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + + if (cln == 1) + return 1; + + return (fat_cluster_num_to_sector_num(mt_entry, cln) << + fs_info->vol.sec_mul); +} + +static inline int +fat_buf_access(fat_fs_info_t *fs_info, unsigned32 blk, int op_type, + bdbuf_buffer **buf) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + unsigned8 i; + rtems_boolean sec_of_fat; + + + if (fs_info->c.state == FAT_CACHE_EMPTY) + { + if (op_type == FAT_OP_TYPE_READ) + sc = rtems_bdbuf_read(fs_info->vol.dev, blk, &fs_info->c.buf); + else + sc = rtems_bdbuf_get(fs_info->vol.dev, blk, &fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + fs_info->c.blk_num = blk; + fs_info->c.state = FAT_CACHE_ACTUAL; + } + + sec_of_fat = ((fs_info->c.blk_num >= fs_info->vol.fat_loc) && + (fs_info->c.blk_num < fs_info->vol.rdir_loc)); + + if (fs_info->c.blk_num != blk) + { + if (fs_info->c.modified) + { + if (sec_of_fat && !fs_info->vol.mirror) + memcpy(fs_info->sec_buf, fs_info->c.buf->buffer, + fs_info->vol.bps); + + sc = rtems_bdbuf_release_modified(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + fs_info->c.modified = 0; + + if (sec_of_fat && !fs_info->vol.mirror) + { + bdbuf_buffer *b; + + for (i = 1; i < fs_info->vol.fats; i++) + { + sc = rtems_bdbuf_get(fs_info->vol.dev, + fs_info->c.blk_num + + fs_info->vol.fat_length * i, + &b); + if ( sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(ENOMEM); + memcpy(b->buffer, fs_info->sec_buf, fs_info->vol.bps); + sc = rtems_bdbuf_release_modified(b); + if ( sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(ENOMEM); + } + } + } + else + { + sc = rtems_bdbuf_release(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + } + if (op_type == FAT_OP_TYPE_READ) + sc = rtems_bdbuf_read(fs_info->vol.dev, blk, &fs_info->c.buf); + else + sc = rtems_bdbuf_get(fs_info->vol.dev, blk, &fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + fs_info->c.blk_num = blk; + } + *buf = fs_info->c.buf; + return RC_OK; +} + + +static inline int +fat_buf_release(fat_fs_info_t *fs_info) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + unsigned8 i; + rtems_boolean sec_of_fat; + + if (fs_info->c.state == FAT_CACHE_EMPTY) + return RC_OK; + + sec_of_fat = ((fs_info->c.blk_num >= fs_info->vol.fat_loc) && + (fs_info->c.blk_num < fs_info->vol.rdir_loc)); + + if (fs_info->c.modified) + { + if (sec_of_fat && !fs_info->vol.mirror) + memcpy(fs_info->sec_buf, fs_info->c.buf->buffer, fs_info->vol.bps); + + sc = rtems_bdbuf_release_modified(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + fs_info->c.modified = 0; + + if (sec_of_fat && !fs_info->vol.mirror) + { + bdbuf_buffer *b; + + for (i = 1; i < fs_info->vol.fats; i++) + { + sc = rtems_bdbuf_get(fs_info->vol.dev, + fs_info->c.blk_num + + fs_info->vol.fat_length * i, + &b); + if ( sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(ENOMEM); + memcpy(b->buffer, fs_info->sec_buf, fs_info->vol.bps); + sc = rtems_bdbuf_release_modified(b); + if ( sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(ENOMEM); + } + } + } + else + { + sc = rtems_bdbuf_release(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + } + fs_info->c.state = FAT_CACHE_EMPTY; + return RC_OK; +} + +static inline void +fat_buf_mark_modified(fat_fs_info_t *fs_info) +{ + fs_info->c.modified = TRUE; +} + + + +ssize_t +_fat_block_read(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start, + unsigned32 offset, + unsigned32 count, + void *buff); + +ssize_t +_fat_block_write(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start, + unsigned32 offset, + unsigned32 count, + const void *buff); + +ssize_t +fat_cluster_read(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + void *buff); + +ssize_t +fat_cluster_write(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + const void *buff); + +int +fat_init_volume_info(rtems_filesystem_mount_table_entry_t *mt_entry); + +int +fat_init_clusters_chain(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start_cln); + +unsigned32 +fat_cluster_num_to_sector_num(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln); + +int +fat_shutdown_drive(rtems_filesystem_mount_table_entry_t *mt_entry); + + +unsigned32 +fat_get_unique_ino(rtems_filesystem_mount_table_entry_t *mt_entry); + +rtems_boolean +fat_ino_is_unique(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 ino); + +void +fat_free_unique_ino(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 ino); + +int +fat_fat32_update_fsinfo_sector( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 free_count, + unsigned32 next_free + ); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_FAT_H__ */ 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 + * + * @(#) $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) + { + /* + * 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; +} diff --git a/c/src/exec/libfs/src/dosfs/fat_fat_operations.h b/c/src/exec/libfs/src/dosfs/fat_fat_operations.h new file mode 100644 index 0000000000..59b6a84018 --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/fat_fat_operations.h @@ -0,0 +1,58 @@ +/* + * fat_fat_operations.h + * + * Constants/data structures/prototypes for operations on Files Allocation + * Table + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_FAT_FAT_OPERATIONS_H__ +#define __DOSFS_FAT_FAT_OPERATIONS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include "fat.h" + +int +fat_get_fat_cluster(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + unsigned32 *ret_val); + +int +fat_set_fat_cluster(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + unsigned32 in_val); + +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 +fat_free_fat_clusters_chain( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 chain +); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_FAT_FAT_OPERATIONS_H__ */ 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 + * + * @(#) $Id$ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; +} diff --git a/c/src/exec/libfs/src/dosfs/fat_file.h b/c/src/exec/libfs/src/dosfs/fat_file.h new file mode 100644 index 0000000000..2821a27cf7 --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/fat_file.h @@ -0,0 +1,195 @@ +/* + * fat_file.h + * + * Constants/data structures/prototypes for operations on "fat-file" + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_FAT_FILE_H__ +#define __DOSFS_FAT_FILE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +/* "fat-file" representation + * + * the idea is: fat-file is nothing but a cluster chain, any open fat-file is + * represented in system by fat-file descriptor and has well-known + * file interface: + * + * fat_file_open() + * fat_file_close() + * fat_file_read() + * fat_file_write() + * + * Such interface hides the architecture of fat-file and represents it like + * linear file + */ + +typedef rtems_filesystem_node_types_t fat_file_type_t; + +#define FAT_DIRECTORY RTEMS_FILESYSTEM_DIRECTORY +#define FAT_FILE RTEMS_FILESYSTEM_MEMORY_FILE + +typedef struct fat_file_map_s +{ + unsigned32 file_cln; + unsigned32 disk_cln; + unsigned32 last_cln; +} fat_file_map_t; +/* + * descriptor of a fat-file + * + * To each particular clusters chain + */ +typedef struct fat_file_fd_s +{ + Chain_Node link; /* + * fat-file descriptors organized into hash; + * collision lists are handled via link + * field + */ + unsigned32 links_num; /* + * the number of fat_file_open call on + * this fat-file + */ + unsigned32 ino; /* inode, file serial number :)))) */ + fat_file_type_t fat_file_type; + unsigned32 size_limit; + unsigned32 fat_file_size; /* length */ + unsigned32 info_cln; + unsigned32 cln; + unsigned16 info_ofs; + unsigned char first_char; + unsigned8 flags; + fat_file_map_t map; + time_t mtime; + +} fat_file_fd_t; + + +#define FAT_FILE_REMOVED 0x01 + +#define FAT_FILE_IS_REMOVED(p)\ + (((p)->flags & FAT_FILE_REMOVED) ? 1 : 0) + +/* ioctl macros */ +#define F_CLU_NUM 0x01 + +/* + * Each file and directory on a MSDOS volume is unique identified by it + * location, i.e. location of it 32 Bytes Directory Entry Structure. We can + * distinguish them by cluster number it locates on and offset inside this + * cluster. But root directory on any volumes (FAT12/16/32) has no 32 Bytes + * Directory Entry Structure corresponded to it. So we assume 32 Bytes + * Directory Entry Structure of root directory locates at cluster 1 (invalid + * cluaster number) and offset 0 + */ +#define FAT_ROOTDIR_CLUSTER_NUM 0x01 + +#define FAT_FD_OF_ROOT_DIR(fat_fd) \ + ((fat_fd->info_cln == FAT_ROOTDIR_CLUSTER_NUM ) && \ + (fat_fd->info_ofs == 0)) + +#define FAT_EOF 0x00 + +/* fat_construct_key -- + * Construct key for hash access: convert (cluster num, offset) to + * (sector512 num, new offset) and than construct key as + * key = (sector512 num) << 4 | (new offset) + * + * PARAMETERS: + * cl - cluster number + * ofs - offset inside cluster 'cl' + * mt_entry - mount table entry + * + * RETURNS: + * constructed key + */ +static inline unsigned32 +fat_construct_key( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cl, + unsigned32 ofs) +{ + return ( ((fat_cluster_num_to_sector512_num(mt_entry, cl) + + (ofs >> FAT_SECTOR512_BITS)) << 4) + + ((ofs >> 5) & (FAT_DIRENTRIES_PER_SEC512 - 1)) ); +} + +/* Prototypes for "fat-file" operations */ +int +fat_file_open(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + unsigned32 ofs, + fat_file_fd_t **fat_fd); + +int +fat_file_reopen(fat_file_fd_t *fat_fd); + +int +fat_file_close(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +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); + +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 +fat_file_extend(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 new_length, + unsigned32 *a_length); + +int +fat_file_truncate(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 new_length); + +int +fat_file_datasync(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + + +int +fat_file_ioctl(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + int cmd, + ...); + +int +fat_file_size(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +void +fat_file_mark_removed(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_FAT_FILE_H__ */ diff --git a/c/src/exec/libfs/src/dosfs/msdos.h b/c/src/exec/libfs/src/dosfs/msdos.h new file mode 100644 index 0000000000..a9216b1ed3 --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos.h @@ -0,0 +1,408 @@ +/* + * msdos.h + * + * The MSDOS filesystem constants/data structures/prototypes + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_MSDOS_H__ +#define __DOSFS_MSDOS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "fat.h" +#include "fat_file.h" + +#ifndef RC_OK +#define RC_OK 0x00000000 +#endif + +#define MSDOS_NAME_NOT_FOUND_ERR 0xDD000001 + +/* + * This structure identifies the instance of the filesystem on the MSDOS + * level. + */ +typedef struct msdos_fs_info_s +{ + fat_fs_info_t fat; /* + * volume + * description + */ + rtems_filesystem_file_handlers_r *directory_handlers; /* + * a set of routines + * that handles the + * nodes of directory + * type + */ + rtems_filesystem_file_handlers_r *file_handlers; /* + * a set of routines + * that handles the + * nodes of file + * type + */ + rtems_id vol_sema; /* + * semaphore + * associated with + * the volume + */ + unsigned8 *cl_buf; /* + * just placeholder + * for anything + */ +} msdos_fs_info_t; + +/* a set of routines that handle the nodes which are directories */ +extern rtems_filesystem_file_handlers_r msdos_dir_handlers; + +/* a set of routines that handle the nodes which are files */ +extern rtems_filesystem_file_handlers_r msdos_file_handlers; + +/* Volume semaphore timeout value */ +#define MSDOS_VOLUME_SEMAPHORE_TIMEOUT 100 + +/* Node types */ +#define MSDOS_DIRECTORY RTEMS_FILESYSTEM_DIRECTORY +#define MSDOS_REGULAR_FILE RTEMS_FILESYSTEM_MEMORY_FILE + +typedef rtems_filesystem_node_types_t msdos_node_type_t; + +/* + * Macros for fetching fields from 32 bytes long FAT Directory Entry + * Structure (see M$ White Paper) + */ +#define MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE 32 /* 32 bytes */ + +#define MSDOS_DIR_NAME(x) (unsigned8 *)((x) + 0) +#define MSDOS_DIR_ATTR(x) (unsigned8 *)((x) + 11) +#define MSDOS_DIR_NT_RES(x) (unsigned8 *)((x) + 12) +#define MSDOS_DIR_CRT_TIME_TENTH(x) (unsigned8 *)((x) + 13) +#define MSDOS_DIR_CRT_TIME(x) (unsigned16 *)((x) + 14) +#define MSDOS_DIR_CRT_DATE(x) (unsigned16 *)((x) + 16) +#define MSDOS_DIR_LAST_ACCESS_DATE(x) (unsigned16 *)((x) + 18) +#define MSDOS_DIR_FIRST_CLUSTER_HI(x) (unsigned16 *)((x) + 20) +#define MSDOS_DIR_WRITE_TIME(x) (unsigned16 *)((x) + 22) +#define MSDOS_DIR_WRITE_DATE(x) (unsigned16 *)((x) + 24) +#define MSDOS_DIR_FIRST_CLUSTER_LOW(x) (unsigned16 *)((x) + 26) +#define MSDOS_DIR_FILE_SIZE(x) (unsigned32 *)((x) + 28) + +#define MSDOS_EXTRACT_CLUSTER_NUM(p) \ + (unsigned32)( (CF_LE_W(*MSDOS_DIR_FIRST_CLUSTER_LOW(p))) | \ + ((CF_LE_W((*MSDOS_DIR_FIRST_CLUSTER_HI(p))))<<16) ) + +/* + * Fields offset in 32 bytes long FAT Directory Entry + * Structure (see M$ White Paper) + */ +#define MSDOS_FILE_SIZE_OFFSET 28 +#define MSDOS_FILE_NAME_OFFSET 0 +#define MSDOS_FIRST_CLUSTER_HI_OFFSET 20 +#define MSDOS_FIRST_CLUSTER_LOW_OFFSET 26 +#define MSDOS_FILE_WDATE_OFFSET 24 +#define MSDOS_FILE_WTIME_OFFSET 22 + +/* + * Possible values of DIR_Attr field of 32 bytes long FAT Directory Entry + * Structure (see M$ White Paper) + */ +#define MSDOS_ATTR_READ_ONLY 0x01 +#define MSDOS_ATTR_HIDDEN 0x02 +#define MSDOS_ATTR_SYSTEM 0x04 +#define MSDOS_ATTR_VOLUME_ID 0x08 +#define MSDOS_ATTR_DIRECTORY 0x10 +#define MSDOS_ATTR_ARCHIVE 0x20 + +/* + * Possible values of DIR_Name[0] field of 32 bytes long FAT Directory Entry + * Structure (see M$ White Paper) + */ +#define MSDOS_THIS_DIR_ENTRY_EMPTY 0xE5 +#define MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY 0x00 + + +/* + * Macros for names parsing and formatting + */ +#define msdos_is_valid_name_char(_ch) (1) +#define msdos_is_separator(_ch) rtems_filesystem_is_separator(_ch) + +#define MSDOS_SHORT_NAME_LEN 11 /* 11 characters */ +#define MSDOS_NAME_MAX MSDOS_SHORT_NAME_LEN +#define MSDOS_NAME_MAX_WITH_DOT (MSDOS_NAME_MAX + 1) + +#define MSDOS_DOT_NAME ". " /* ".", padded to MSDOS_NAME chars */ +#define MSDOS_DOTDOT_NAME ".. " /* "..", padded to MSDOS_NAME chars */ + +typedef enum msdos_token_types_e +{ + MSDOS_NO_MORE_PATH, + MSDOS_CURRENT_DIR, + MSDOS_UP_DIR, + MSDOS_NAME, + MSDOS_INVALID_TOKEN +} msdos_token_types_t; + +/* Others macros */ +#define MSDOS_RES_NT_VALUE 0x00 +#define MSDOS_INIT_DIR_SIZE 0x00 + +/* "dot" entry offset in a directory */ +#define MSDOS_DOT_DIR_ENTRY_OFFSET 0x00 /* first entry in directory */ + +/* "dotdot" entry offset in a directory */ +#define MSDOS_DOTDOT_DIR_ENTRY_OFFSET 0x20 /* second entry in directory */ + +/* 'p' should be char* */ +#define DOT_NODE_P(p) ((char *)(p)) +#define DOTDOT_NODE_P(p) ((char *)((p) + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)) + +/* Size limits for files and directories (see M$ White Paper) */ +#define MSDOS_MAX_DIR_LENGHT 0x200000 /* 2,097,152 bytes */ +#define MSDOS_MAX_FILE_SIZE 0xFFFFFFFF /* 4 Gb */ + +/* + * The number of 32 bytes long FAT Directory Entry + * Structures per 512 bytes sector + */ +#define MSDOS_DPS512_NUM 16 + +/* Prototypes */ +int +msdos_initialize(rtems_filesystem_mount_table_entry_t *temp_mt_entry); + +int +msdos_shut_down(rtems_filesystem_mount_table_entry_t *temp_mt_entry); + +int +msdos_eval_path(const char *pathname, /* IN */ + int flags, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */); + +int +msdos_eval4make(const char *path, /* IN */ + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + const char **name /* OUT */); + +int +msdos_unlink(rtems_filesystem_location_info_t *pathloc /* IN */); + +int +msdos_free_node_info(rtems_filesystem_location_info_t *pathloc /* IN */); + +rtems_filesystem_node_types_t +msdos_node_type(rtems_filesystem_location_info_t *pathloc); + +int +msdos_mknod(const char *path, /* IN */ + mode_t mode, /* IN */ + dev_t dev, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */); + +int +msdos_utime(rtems_filesystem_location_info_t *pathloc, /* IN */ + time_t actime, /* IN */ + time_t modtime /* IN */); + +int +msdos_initialize_support( + rtems_filesystem_mount_table_entry_t *temp_mt_entry, + rtems_filesystem_operations_table *op_table, + rtems_filesystem_file_handlers_r *file_handlers, + rtems_filesystem_file_handlers_r *directory_handlers +); + +int +msdos_file_open( + rtems_libio_t *iop, /* IN */ + const char *pathname, /* IN */ + unsigned32 flag, /* IN */ + unsigned32 mode /* IN */ +); + +int +msdos_file_close(rtems_libio_t *iop /* IN */); + +ssize_t +msdos_file_read( + rtems_libio_t *iop, /* IN */ + void *buffer, /* IN */ + unsigned32 count /* IN */ +); + +ssize_t +msdos_file_write( + rtems_libio_t *iop, /* IN */ + const void *buffer, /* IN */ + unsigned32 count /* IN */ +); + +int +msdos_file_lseek( + rtems_libio_t *iop, /* IN */ + off_t offset, /* IN */ + int whence /* IN */ +); + +int +msdos_file_stat(rtems_filesystem_location_info_t *loc, /* IN */ + struct stat *buf /* OUT */); + +int +msdos_file_ftruncate( + rtems_libio_t *iop, /* IN */ + off_t length /* IN */ +); + +int +msdos_file_sync(rtems_libio_t *iop); + +int +msdos_file_datasync(rtems_libio_t *iop); + +int +msdos_file_ioctl( + rtems_libio_t *iop, /* IN */ + unsigned32 command, /* IN */ + void *buffer /* IN */ +); + +int +msdos_file_rmnod(rtems_filesystem_location_info_t *pathloc /* IN */); + +int +msdos_dir_open( + rtems_libio_t *iop, /* IN */ + const char *pathname, /* IN */ + unsigned32 flag, /* IN */ + unsigned32 mode /* IN */ +); + +int +msdos_dir_close(rtems_libio_t *iop /* IN */); + +ssize_t +msdos_dir_read( + rtems_libio_t *iop, /* IN */ + void *buffer, /* IN */ + unsigned32 count /* IN */ +); + +int +msdos_dir_lseek( + rtems_libio_t *iop, /* IN */ + off_t offset, /* IN */ + int whence /* IN */ +); + +int +msdos_dir_rmnod(rtems_filesystem_location_info_t *pathloc /* IN */); + +int +msdos_dir_sync(rtems_libio_t *iop); + +int +msdos_dir_stat( + rtems_filesystem_location_info_t *loc, /* IN */ + struct stat *buf /* OUT */ +); + +int +msdos_creat_node(rtems_filesystem_location_info_t *parent_loc, + msdos_node_type_t type, + char *name, + mode_t mode); + +/* Misc prototypes */ +msdos_token_types_t msdos_get_token(const char *path, + char *token, + int *token_len); + +int +msdos_find_name(rtems_filesystem_location_info_t *parent_loc, + char *name); + +int +msdos_get_name_node(rtems_filesystem_location_info_t *parent_loc, + char *name, + fat_auxiliary_t *paux, + char *name_dir_entry); + +int +msdos_dir_info_remove(rtems_filesystem_location_info_t *pathloc); + +void +msdos_date_unix2dos(int unix_date, + unsigned short *time_val, + unsigned short *date); + +unsigned int +msdos_date_dos2unix(unsigned short time_val, unsigned short date); + +int +msdos_set_first_cluster_num(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +int +msdos_set_file_size(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +int +msdos_set_first_char4file_name(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cl, + unsigned32 ofs, + unsigned char first_char); + +int +msdos_set_dir_wrt_time_and_date( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd +); + + +int +msdos_dir_is_empty(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + rtems_boolean *ret_val); + +int +msdos_find_name_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + char *name, + fat_auxiliary_t *paux, + char *name_dir_entry); + +int +msdos_find_node_by_cluster_num_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 cl4find, + fat_auxiliary_t *paux, + char *dir_entry +); + +int +msdos_get_dotdot_dir_info_cluster_num_and_offset( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + fat_auxiliary_t *paux, + char *dir_entry +); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_MSDOS_H__ */ diff --git a/c/src/exec/libfs/src/dosfs/msdos_create.c b/c/src/exec/libfs/src/dosfs/msdos_create.c new file mode 100644 index 0000000000..4b4c7001ca --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_create.c @@ -0,0 +1,208 @@ +/* + * Routine to create a new MSDOS filesystem node + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + * + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_creat_node -- + * Create a new node. If a new node is file, FAT 32 Bytes Directory + * Entry Structure (see M$ White Paper) is initialized, free space is + * found in parent directory and structure is written to the disk. + * In case of directory, all above steps present and also new cluster + * is allocated for a new directory and dot and dotdot nodes are created + * in alloceted cluster. + * + * PARAMETERS: + * parent_loc - parent (directory we are going to create node in) + * type - new node type (file or directory) + * name - new node name + * mode - mode + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately). + * + */ +int +msdos_creat_node( + rtems_filesystem_location_info_t *parent_loc, + msdos_node_type_t type, + char *name, + mode_t mode + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = parent_loc->mt_entry->fs_info; + fat_file_fd_t *parent_fat_fd = parent_loc->node_access; + fat_file_fd_t *fat_fd = NULL; + time_t time_ret = 0; + unsigned16 time_val = 0; + unsigned16 date = 0; + fat_auxiliary_t aux; + unsigned char new_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + unsigned char dot_dotdot[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2]; + + memset(new_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memset(dot_dotdot, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2); + + /* set up name */ + strncpy(MSDOS_DIR_NAME(new_node), name, MSDOS_NAME_MAX); + + /* fill reserved field */ + *MSDOS_DIR_NT_RES(new_node) = MSDOS_RES_NT_VALUE; + + /* set up last write date and time */ + time_ret = time(NULL); + if ( time_ret == -1 ) + return -1; + + msdos_date_unix2dos(time_ret, &time_val, &date); + *MSDOS_DIR_WRITE_TIME(new_node) = CT_LE_W(time_val); + *MSDOS_DIR_WRITE_DATE(new_node) = CT_LE_W(date); + + /* initialize directory/file size */ + *MSDOS_DIR_FILE_SIZE(new_node) = MSDOS_INIT_DIR_SIZE; + + if (type == MSDOS_DIRECTORY) + *MSDOS_DIR_ATTR(new_node) |= MSDOS_ATTR_DIRECTORY; + else + *MSDOS_DIR_ATTR(new_node) |= MSDOS_ATTR_ARCHIVE; + + /* + * find free space in the parent directory and write new initialized + * FAT 32 Bytes Directory Entry Structure (see M$ White Paper) + * to the disk + */ + rc = msdos_get_name_node(parent_loc, NULL, &aux, new_node); + if ( rc != RC_OK ) + return rc; + + /* + * if we create a new file we are done, if directory there are more steps + * to do + */ + if (type == MSDOS_DIRECTORY) + { + /* open new directory as fat-file */ + rc = fat_file_open(parent_loc->mt_entry, aux.cln, aux.ofs, &fat_fd); + if (rc != RC_OK) + goto err; + + /* + * we opened fat-file for node we just created, so initialize fat-file + * descritor + */ + fat_fd->info_cln = aux.cln; + fat_fd->info_ofs = aux.ofs; + fat_fd->fat_file_size = 0; + fat_fd->fat_file_type = FAT_DIRECTORY; + fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT; + + /* + * dot and dotdot entries are identical to new node except the + * names + */ + memcpy(DOT_NODE_P(dot_dotdot), new_node, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memcpy(DOTDOT_NODE_P(dot_dotdot), new_node, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memcpy(MSDOS_DIR_NAME(DOT_NODE_P(dot_dotdot)), MSDOS_DOT_NAME, + MSDOS_NAME_MAX); + memcpy(MSDOS_DIR_NAME(DOTDOT_NODE_P(dot_dotdot)), MSDOS_DOTDOT_NAME, + MSDOS_NAME_MAX); + + /* set up cluster num for dotdot entry */ + /* + * here we can ommit FAT32 condition because for all FAT types dirs + * right under root dir should contain 0 in dotdot entry but for + * FAT12/16 parent_fat_fd->cluster_num always contains such value + */ + if ((FAT_FD_OF_ROOT_DIR(parent_fat_fd)) && + (fs_info->fat.vol.type & FAT_FAT32)) + { + *MSDOS_DIR_FIRST_CLUSTER_LOW(DOTDOT_NODE_P(dot_dotdot)) = 0x0000; + *MSDOS_DIR_FIRST_CLUSTER_HI(DOTDOT_NODE_P(dot_dotdot)) = 0x0000; + } + else + { + *MSDOS_DIR_FIRST_CLUSTER_LOW(DOTDOT_NODE_P(dot_dotdot)) = + CT_LE_W((unsigned16)((parent_fat_fd->cln) & 0x0000FFFF)); + *MSDOS_DIR_FIRST_CLUSTER_HI(DOTDOT_NODE_P(dot_dotdot)) = + CT_LE_W((unsigned16)(((parent_fat_fd->cln) & 0xFFFF0000)>>16)); + } + + /* + * write dot and dotdot entries to new fat-file: currently fat-file + * correspondes to a new node is zero length, so it will be extended + * by one cluster and entries will be written + */ + ret = fat_file_write(parent_loc->mt_entry, fat_fd, 0, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2, + dot_dotdot); + if (ret < 0) + { + rc = -1; + goto error; + } + + /* increment fat-file size by cluster size */ + fat_fd->fat_file_size += fs_info->fat.vol.bpc; + + /* set up cluster num for dot entry */ + *MSDOS_DIR_FIRST_CLUSTER_LOW(DOT_NODE_P(dot_dotdot)) = + CT_LE_W((unsigned16)((fat_fd->cln) & 0x0000FFFF)); + *MSDOS_DIR_FIRST_CLUSTER_HI(DOT_NODE_P(dot_dotdot)) = + CT_LE_W((unsigned16)(((fat_fd->cln) & 0xFFFF0000) >> 16)); + + /* rewrite dot entry */ + ret = fat_file_write(parent_loc->mt_entry, fat_fd, 0, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE, + DOT_NODE_P(dot_dotdot)); + if (ret < 0) + { + rc = -1; + goto error; + } + + /* write first cluster num of a new directory to disk */ + rc = msdos_set_first_cluster_num(parent_loc->mt_entry, fat_fd); + if (rc != RC_OK) + goto error; + + fat_file_close(parent_loc->mt_entry, fat_fd); + } + return RC_OK; + +error: + fat_file_close(parent_loc->mt_entry, fat_fd); + +err: + /* mark 32bytes structure on the disk as free */ + msdos_set_first_char4file_name(parent_loc->mt_entry, aux.cln, aux.ofs, + 0xE5); + return rc; +} diff --git a/c/src/exec/libfs/src/dosfs/msdos_dir.c b/c/src/exec/libfs/src/dosfs/msdos_dir.c new file mode 100644 index 0000000000..93449cd2fb --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_dir.c @@ -0,0 +1,483 @@ +/* + * MSDOS directory handlers implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_dir_open -- + * Open fat-file which correspondes to the directory being opened and + * set offset field of file control block to zero. + * + * PARAMETERS: + * iop - file control block + * pathname - name + * flag - flags + * mode - mode + * + * RETURNS: + * RC_OK, if directory opened successfully, or -1 if error occured (errno + * set apropriately) + */ +int +msdos_dir_open(rtems_libio_t *iop, const char *pathname, unsigned32 flag, + unsigned32 mode) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one( EIO ); + + rc = fat_file_reopen(fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + iop->offset = 0; + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_dir_close -- + * Close fat-file which correspondes to the directory being closed + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK, if directory closed successfully, or -1 if error occured (errno + * set apropriately. + */ +int +msdos_dir_close(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one( EIO ); + + rc = fat_file_close(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_dir_read -- + * This routine will read the next directory entry based on the directory + * offset. The offset should be equal to -n- time the size of an + * individual dirent structure. If n is not an integer multiple of the + * sizeof a dirent structure, an integer division will be performed to + * determine directory entry that will be returned in the buffer. Count + * should reflect -m- times the sizeof dirent bytes to be placed in the + * buffer. + * If there are not -m- dirent elements from the current directory + * position to the end of the exisiting file, the remaining entries will + * be placed in the buffer and the returned value will be equal to + * -m actual- times the size of a directory entry. + * + * PARAMETERS: + * iop - file control block + * buffer - buffer provided by user + * count - count of bytes to read + * + * RETURNS: + * the number of bytes read on success, or -1 if error occured (errno + * set apropriately). + */ +ssize_t +msdos_dir_read(rtems_libio_t *iop, void *buffer, unsigned32 count) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + fat_file_fd_t *tmp_fat_fd = NULL; + struct dirent tmp_dirent; + unsigned32 start = 0; + ssize_t ret = 0; + unsigned32 cmpltd = 0; + unsigned32 j = 0, i = 0; + unsigned32 bts2rd = 0; + unsigned32 cur_cln = 0; + + /* + * cast start and count - protect against using sizes that are not exact + * multiples of the -dirent- size. These could result in unexpected + * results + */ + start = iop->offset / sizeof(struct dirent); + count = (count / sizeof(struct dirent)) * sizeof(struct dirent); + + /* + * optimization: we know that root directory for FAT12/16 volumes is + * sequential set of sectors and any cluster is sequential set of sectors + * too, so read such set of sectors is quick operation for low-level IO + * layer. + */ + bts2rd = (FAT_FD_OF_ROOT_DIR(fat_fd) && + (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16))) ? + fat_fd->fat_file_size : + fs_info->fat.vol.bpc; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + while (count > 0) + { + /* + * fat-file is already opened by open call, so read it + * Always read directory fat-file from the beggining because of MSDOS + * directories feature :( - we should count elements currently + * present in the directory because there may be holes :) + */ + ret = fat_file_read(iop->pathinfo.mt_entry, fat_fd, (j * bts2rd), + bts2rd, fs_info->cl_buf); + if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + rtems_semaphore_release(fs_info->vol_sema); + set_errno_and_return_minus_one(EIO); + } + + for (i = 0; i < ret; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) + { + rtems_semaphore_release(fs_info->vol_sema); + return cmpltd; + } + + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_EMPTY) + continue; + + /* + * skip active entries until get the entry to start from + */ + if (start) + { + start--; + continue; + } + + /* + * Move the entry to the return buffer + * + * unfortunately there is no method to extract ino except to + * open fat-file descriptor :( ... so, open it + */ + + /* get number of cluster we are working with */ + rc = fat_file_ioctl(iop->pathinfo.mt_entry, fat_fd, F_CLU_NUM, + j * bts2rd, &cur_cln); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + rc = fat_file_open(iop->pathinfo.mt_entry, cur_cln, i, + &tmp_fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + tmp_fat_fd->info_cln = cur_cln; + tmp_fat_fd->info_ofs = i; + + /* fill in dirent structure */ + /* XXX: from what and in what d_off should be computed ?! */ + tmp_dirent.d_off = start + cmpltd; + tmp_dirent.d_reclen = sizeof(struct dirent); + tmp_dirent.d_ino = tmp_fat_fd->ino; + tmp_dirent.d_namlen = MSDOS_SHORT_NAME_LEN; + memcpy(tmp_dirent.d_name, MSDOS_DIR_NAME((fs_info->cl_buf + i)), + MSDOS_SHORT_NAME_LEN); + + /* d_name is null-terminated */ + tmp_dirent.d_name[MSDOS_SHORT_NAME_LEN] = 0; + memcpy(buffer + cmpltd, &tmp_dirent, sizeof(struct dirent)); + + iop->offset = iop->offset + sizeof(struct dirent); + cmpltd += (sizeof(struct dirent)); + count -= (sizeof(struct dirent)); + + /* inode number extracted, close fat-file */ + rc = fat_file_close(iop->pathinfo.mt_entry, tmp_fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + if (count <= 0) + break; + } + j++; + } + + rtems_semaphore_release(fs_info->vol_sema); + return cmpltd; +} + +/* msdos_dir_write -- + * no write for directory + */ + +/* msdos_dir_lseek -- + * + * This routine will behave in one of three ways based on the state of + * argument whence. Based on the state of its value the offset argument will + * be interpreted using one of the following methods: + * + * SEEK_SET - offset is the absolute byte offset from the start of the + * logical start of the dirent sequence that represents the + * directory + * SEEK_CUR - offset is used as the relative byte offset from the current + * directory position index held in the iop structure + * SEEK_END - N/A --> This will cause an assert. + * + * PARAMETERS: + * iop - file control block + * offset - offset + * whence - predefine directive + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno + * set apropriately). + */ +int +msdos_dir_lseek(rtems_libio_t *iop, off_t offset, int whence) +{ + switch (whence) + { + case SEEK_SET: + case SEEK_CUR: + break; + /* + * Movement past the end of the directory via lseek is not a + * permitted operation + */ + case SEEK_END: + default: + set_errno_and_return_minus_one( EINVAL ); + break; + } + return RC_OK; +} + +/* msdos_dir_stat -- + * + * This routine will obtain the following information concerning the current + * directory: + * st_dev device id + * st_ino node serial number :) + * st_mode mode extracted from the node + * st_size total size in bytes + * st_blksize blocksize for filesystem I/O + * st_blocks number of blocks allocated + * stat_mtime time of last modification + * + * PARAMETERS: + * loc - this directory + * buf - stat buffer provided by user + * + * RETURNS: + * RC_OK and filled stat buffer on success, or -1 if error occured (errno + * set apropriately). + */ +int +msdos_dir_stat( + rtems_filesystem_location_info_t *loc, + struct stat *buf + ) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = loc->node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + buf->st_dev = fs_info->fat.vol.dev; + buf->st_ino = fat_fd->ino; + buf->st_mode = S_IFDIR; + buf->st_rdev = 0ll; + buf->st_size = fat_fd->fat_file_size; + buf->st_blocks = fat_fd->fat_file_size >> FAT_SECTOR512_BITS; + buf->st_blksize = fs_info->fat.vol.bps; + buf->st_mtime = fat_fd->mtime; + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_dir_truncate -- + * No truncate for directory. + * + * PARAMETERS: + * + * RETURNS: + * + */ + +/* msdos_dir_sync -- + * The following routine does a syncronization on a MSDOS directory node. + * DIR_WrtTime, DIR_WrtDate and DIR_fileSize fields of 32 Bytes Directory + * Entry Structure(see M$ White Paper) should not be updated for + * directories, so only call to corresponding fat-file routine. + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + */ +int +msdos_dir_sync(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + fat_file_fd_t *fat_fd = iop->file_info; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_datasync(iop->pathinfo.mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} + +/* msdos_dir_rmnod -- + * Remove directory node. + * + * Check that this directory node is not opened as fat-file, is empty and + * not filesystem root node. If all this conditions met then delete. + * + * PARAMETERS: + * pathloc - node description + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + */ +int +msdos_dir_rmnod(rtems_filesystem_location_info_t *pathloc) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = pathloc->node_access; + rtems_boolean is_empty = FALSE; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* + * We deny attemp to delete open directory (if directory is current + * directory we assume it is open one) + */ + if (fat_fd->links_num > 1) + { + rtems_semaphore_release(fs_info->vol_sema); + set_errno_and_return_minus_one(EBUSY); + } + + /* + * You cannot remove a node that still has children + */ + rc = msdos_dir_is_empty(pathloc->mt_entry, fat_fd, &is_empty); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + if (!is_empty) + { + rtems_semaphore_release(fs_info->vol_sema); + set_errno_and_return_minus_one(ENOTEMPTY); + } + + /* + * You cannot remove the file system root node. + */ + if (pathloc->mt_entry->mt_fs_root.node_access == pathloc->node_access) + { + rtems_semaphore_release(fs_info->vol_sema); + set_errno_and_return_minus_one(EBUSY); + } + + /* + * You cannot remove a mountpoint. + * not used - mount() not implemenetd yet. + */ + + /* mark file removed */ + rc = msdos_set_first_char4file_name(pathloc->mt_entry, fat_fd->info_cln, + fat_fd->info_ofs, + MSDOS_THIS_DIR_ENTRY_EMPTY); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + fat_file_mark_removed(pathloc->mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/c/src/exec/libfs/src/dosfs/msdos_eval.c b/c/src/exec/libfs/src/dosfs/msdos_eval.c new file mode 100644 index 0000000000..3eba0e63e8 --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_eval.c @@ -0,0 +1,435 @@ +/* + * MSDOS evaluation routines + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_set_handlers -- + * Set handlers for the node with specified type(i.e. handlers for file + * or directory). + * + * PARAMETERS: + * loc - node description + * + * RETURNS: + * None + */ +static void +msdos_set_handlers(rtems_filesystem_location_info_t *loc) +{ + msdos_fs_info_t *fs_info = loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = loc->node_access; + + if (fat_fd->fat_file_type == FAT_DIRECTORY) + loc->handlers = fs_info->directory_handlers; + else + loc->handlers = fs_info->file_handlers; +} + +/* msdos_eval_path -- + * + * The following routine evaluate path for a node that wishes to be + * accessed. Structure 'pathloc' is returned with a pointer to the + * node to be accessed. + * + * PARAMETERS: + * pathname - path for evaluation + * flags - flags + * pathloc - node description (IN/OUT) + * + * RETURNS: + * RC_OK and filled pathloc on success, or -1 if error occured + * (errno set appropriately) + * + */ +int +msdos_eval_path( + const char *pathname, + int flags, + rtems_filesystem_location_info_t *pathloc + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + rtems_filesystem_location_info_t newloc; + int i = 0; + int len = 0; + msdos_token_types_t type = MSDOS_CURRENT_DIR; + char token[MSDOS_NAME_MAX + 1]; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + if (!pathloc->node_access) + { + errno = ENOENT; + rc = -1; + goto err; + } + + fat_fd = pathloc->node_access; + + rc = fat_file_reopen(fat_fd); + if (rc != RC_OK) + goto err; + + while ((type != MSDOS_NO_MORE_PATH) && (type != MSDOS_INVALID_TOKEN)) + { + type = msdos_get_token(&pathname[i], token, &len); + i += len; + + fat_fd = pathloc->node_access; + + switch (type) + { + case MSDOS_UP_DIR: + /* + * Only a directory can be decended into. + */ + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + /* + * Am I at the root of this mounted filesystem? + */ + if (pathloc->node_access == + pathloc->mt_entry->mt_fs_root.node_access) + { + /* + * Am I at the root of all filesystems? + * XXX: MSDOS is not supposed to be base fs. + */ + if (pathloc->node_access == + rtems_filesystem_root.node_access) + { + break; /* Throw out the .. in this case */ + } + else + { + newloc = pathloc->mt_entry->mt_point_node; + *pathloc = newloc; + + rc = fat_file_close(pathloc->mt_entry, fat_fd); + if (rc != RC_OK) + goto err; + + rtems_semaphore_release(fs_info->vol_sema); + return (*pathloc->ops->evalpath_h)(&(pathname[i-len]), + flags, pathloc); + } + } + else + { + rc = msdos_find_name(pathloc, token); + if (rc != RC_OK) + { + if (rc == MSDOS_NAME_NOT_FOUND_ERR) + { + errno = ENOENT; + rc = -1; + } + goto error; + } + } + break; + + case MSDOS_NAME: + /* + * Only a directory can be decended into. + */ + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + /* + * Otherwise find the token name in the present location and + * set the node access to the point we have found. + */ + rc = msdos_find_name(pathloc, token); + if (rc != RC_OK) + { + if (rc == MSDOS_NAME_NOT_FOUND_ERR) + { + errno = ENOENT; + rc = -1; + } + goto error; + } + break; + + case MSDOS_NO_MORE_PATH: + case MSDOS_CURRENT_DIR: + break; + + case MSDOS_INVALID_TOKEN: + errno = ENAMETOOLONG; + rc = -1; + goto error; + break; + + } + } + + /* + * Always return the root node. + * + * If we are at a node that is a mount point. Set loc to the + * new fs root node and let let the mounted filesystem set the handlers. + * + * NOTE: The behavior of stat() on a mount point appears to be + * questionable. + * NOTE: MSDOS filesystem currently doesn't support mount functionality -> + * action not implemented + */ + fat_fd = pathloc->node_access; + + msdos_set_handlers(pathloc); + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; + +error: + fat_file_close(pathloc->mt_entry, fat_fd); + +err: + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} + +/* msdos_eval4make -- + * The following routine evaluate path for a new node to be created. + * 'pathloc' is returned with a pointer to the parent of the new node. + * 'name' is returned with a pointer to the first character in the + * new node name. The parent node is verified to be a directory. + * + * PARAMETERS: + * path - path for evaluation + * pathloc - IN/OUT (start point for evaluation/parent directory for + * creation) + * name - new node name + * + * RETURNS: + * RC_OK, filled pathloc for parent directory and name of new node on + * success, or -1 if error occured (errno set appropriately) + */ +int +msdos_eval4make( + const char *path, + rtems_filesystem_location_info_t *pathloc, + const char **name + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + rtems_filesystem_location_info_t newloc; + msdos_token_types_t type; + int i = 0; + int len; + char token[ MSDOS_NAME_MAX + 1 ]; + rtems_boolean done = 0; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + if (!pathloc->node_access) + { + errno = ENOENT; + rc = -1; + goto err; + } + + fat_fd = pathloc->node_access; + + rc = fat_file_reopen(fat_fd); + if (rc != RC_OK) + goto err; + + while (!done) + { + type = msdos_get_token(&path[i], token, &len); + i += len; + fat_fd = pathloc->node_access; + + switch (type) + { + case MSDOS_UP_DIR: + /* + * Only a directory can be decended into. + */ + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + /* + * Am I at the root of this mounted filesystem? + */ + if (pathloc->node_access == + pathloc->mt_entry->mt_fs_root.node_access) + { + /* + * Am I at the root of all filesystems? + * XXX: MSDOS is not supposed to be base fs. + */ + if (pathloc->node_access == + rtems_filesystem_root.node_access) + { + break; /* Throw out the .. in this case */ + } + else + { + newloc = pathloc->mt_entry->mt_point_node; + *pathloc = newloc; + + rc = fat_file_close(pathloc->mt_entry, fat_fd); + if (rc != RC_OK) + goto err; + + rtems_semaphore_release(fs_info->vol_sema); + return (*pathloc->ops->evalformake_h)(&path[i-len], + pathloc, name); + } + } + else + { + rc = msdos_find_name(pathloc, token); + if (rc != RC_OK) + { + if (rc == MSDOS_NAME_NOT_FOUND_ERR) + { + errno = ENOENT; + rc = -1; + } + goto error; + } + } + break; + + case MSDOS_NAME: + /* + * Only a directory can be decended into. + */ + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + /* + * Otherwise find the token name in the present location and + * set the node access to the point we have found. + */ + rc = msdos_find_name(pathloc, token); + if (rc) + { + if (rc != MSDOS_NAME_NOT_FOUND_ERR) + { + errno = ENOENT; + rc = -1; + goto error; + } + else + done = TRUE; + } + break; + + case MSDOS_NO_MORE_PATH: + errno = EEXIST; + rc = -1; + goto error; + break; + + case MSDOS_CURRENT_DIR: + break; + + case MSDOS_INVALID_TOKEN: + errno = ENAMETOOLONG; + rc = -1; + goto error; + break; + + } + } + + *name = &path[i - len]; + + /* + * We have evaluated the path as far as we can. + * Verify there is not any invalid stuff at the end of the name. + */ + for( ; path[i] != '\0'; i++) + { + if (!msdos_is_separator(path[i])) + { + errno = ENOENT; + rc = -1; + goto error; + } + } + + fat_fd = pathloc->node_access; + + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + msdos_set_handlers(pathloc); + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; + +error: + fat_file_close(pathloc->mt_entry, fat_fd); + +err: + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/c/src/exec/libfs/src/dosfs/msdos_file.c b/c/src/exec/libfs/src/dosfs/msdos_file.c new file mode 100644 index 0000000000..da36827338 --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_file.c @@ -0,0 +1,485 @@ +/* + * MSDOS file handlers implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_file_open -- + * Open fat-file which correspondes to the file + * + * PARAMETERS: + * iop - file control block + * pathname - name + * flag - flags + * mode - mode + * + * RETURNS: + * RC_OK, if file opened successfully, or -1 if error occured + * and errno set appropriately + */ +int +msdos_file_open(rtems_libio_t *iop, const char *pathname, unsigned32 flag, + unsigned32 mode) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_reopen(fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + if (iop->flags & LIBIO_FLAGS_APPEND) + iop->offset = fat_fd->fat_file_size; + + iop->size = fat_fd->fat_file_size; + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_file_close -- + * Close fat-file which correspondes to the file. If fat-file descriptor + * which correspondes to the file is not marked "removed", synchronize + * size, first cluster number, write time and date fields of the file. + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK, if file closed successfully, or -1 if error occured (errno set + * appropriately) + */ +int +msdos_file_close(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* + * if fat-file descriptor is not marked as "removed", synchronize + * size, first cluster number, write time and date fields of the file + */ + if (!FAT_FILE_IS_REMOVED(fat_fd)) + { + rc = msdos_set_first_cluster_num(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + rc = msdos_set_file_size(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + rc = msdos_set_dir_wrt_time_and_date(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + } + + rc = fat_file_close(iop->pathinfo.mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} + +/* msdos_file_read -- + * This routine read from file pointed to by file control block into + * the specified data buffer provided by user + * + * PARAMETERS: + * iop - file control block + * buffer - buffer provided by user + * count - the number of bytes to read + * + * RETURNS: + * the number of bytes read on success, or -1 if error occured (errno set + * appropriately) + */ +ssize_t +msdos_file_read(rtems_libio_t *iop, void *buffer, unsigned32 count) +{ + ssize_t ret = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + ret = fat_file_read(iop->pathinfo.mt_entry, fat_fd, iop->offset, count, + buffer); + + rtems_semaphore_release(fs_info->vol_sema); + return ret; +} + +/* msdos_file_write -- + * This routine writes the specified data buffer into the file pointed to + * by file control block. + * + * PARAMETERS: + * iop - file control block + * buffer - data to write + * count - count of bytes to write + * + * RETURNS: + * the number of bytes written on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +msdos_file_write(rtems_libio_t *iop,const void *buffer, unsigned32 count) +{ + ssize_t ret = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + ret = fat_file_write(iop->pathinfo.mt_entry, fat_fd, iop->offset, count, + buffer); + if (ret < 0) + { + rtems_semaphore_release(fs_info->vol_sema); + return -1; + } + + /* + * update file size in both fat-file descriptor and file control block if + * file was extended + */ + if (iop->offset + ret > fat_fd->fat_file_size) + fat_fd->fat_file_size = iop->offset + ret; + + iop->size = fat_fd->fat_file_size; + + rtems_semaphore_release(fs_info->vol_sema); + return ret; +} + +/* msdos_file_lseek -- + * Process lseek call to the file: extend file if lseek is up to the end + * of the file. + * + * PARAMETERS: + * iop - file control block + * offset - new offset + * whence - predefine directive + * + * RETURNS: + * new offset on success, or -1 if error occured (errno set + * appropriately). + */ +int +msdos_file_lseek(rtems_libio_t *iop, off_t offset, int whence) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + unsigned32 real_size = 0; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_extend(iop->pathinfo.mt_entry, fat_fd, iop->offset, + &real_size); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + if (real_size > fat_fd->fat_file_size) + fat_fd->fat_file_size = iop->offset = real_size; + + iop->size = fat_fd->fat_file_size; + + rtems_semaphore_release(fs_info->vol_sema); + return iop->offset; +} + +/* msdos_file_stat -- + * + * PARAMETERS: + * loc - node description + * buf - stat buffer provided by user + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_file_stat( + rtems_filesystem_location_info_t *loc, + struct stat *buf + ) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = loc->node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + buf->st_dev = fs_info->fat.vol.dev; + buf->st_ino = fat_fd->ino; + buf->st_mode = S_IFREG; + buf->st_rdev = 0ll; + buf->st_size = fat_fd->fat_file_size; + buf->st_blocks = fat_fd->fat_file_size >> FAT_SECTOR512_BITS; + buf->st_blksize = fs_info->fat.vol.bps; + buf->st_mtime = fat_fd->mtime; + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_file_ftruncate -- + * Truncate the file (if new length is greater then current do nothing). + * + * PARAMETERS: + * iop - file control block + * length - new length + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately). + */ +int +msdos_file_ftruncate(rtems_libio_t *iop, off_t length) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + if (length >= fat_fd->fat_file_size) + return RC_OK; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_truncate(iop->pathinfo.mt_entry, fat_fd, length); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + /* + * fat_file_truncate do nothing if new length >= fat-file size, so update + * file size only if length < fat-file size + */ + if (length < fat_fd->fat_file_size) + iop->size = fat_fd->fat_file_size = length; + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_file_sync -- + * Synchronize file - synchronize file data and if file is not removed + * synchronize file metadata. + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_file_sync(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + fat_file_fd_t *fat_fd = iop->file_info; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* synchronize file data */ + rc = fat_file_datasync(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + /* + * if fat-file descriptor is not marked "removed" - synchronize file + * metadata + */ + if (!FAT_FILE_IS_REMOVED(fat_fd)) + { + rc = msdos_set_first_cluster_num(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + rc = msdos_set_file_size(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + rc = msdos_set_dir_wrt_time_and_date(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + } + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_file_datasync -- + * Synchronize file - synchronize only file data (metadata is letf intact). + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_file_datasync(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + fat_file_fd_t *fat_fd = iop->file_info; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* synchronize file data */ + rc = fat_file_datasync(iop->pathinfo.mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + + +/* msdos_file_ioctl -- + * + * + * PARAMETERS: + * iop - file control block + * ... + * + * RETURNS: + * + */ +int +msdos_file_ioctl(rtems_libio_t *iop,unsigned32 command, void *buffer) +{ + int rc = RC_OK; + + return rc; +} + +/* msdos_file_rmnod -- + * Remove node associated with a file - set up first name character to + * predefined value(and write it to the disk), and mark fat-file which + * correspondes to the file as "removed" + * + * PARAMETERS: + * pathloc - node description + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_file_rmnod(rtems_filesystem_location_info_t *pathloc) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = pathloc->node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* mark file removed */ + rc = msdos_set_first_char4file_name(pathloc->mt_entry, fat_fd->info_cln, + fat_fd->info_ofs, + MSDOS_THIS_DIR_ENTRY_EMPTY); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + fat_file_mark_removed(pathloc->mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} diff --git a/c/src/exec/libfs/src/dosfs/msdos_free.c b/c/src/exec/libfs/src/dosfs/msdos_free.c new file mode 100644 index 0000000000..c0d5938dbb --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_free.c @@ -0,0 +1,56 @@ +/* + * Free node handler implementation for the filesystem + * operations table. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_free_node_info -- + * Call fat-file close routine. + * + * PARAMETERS: + * pathloc - node description + * + * RETURNS: + * RC_OK on success, or -1 code if error occured + * + */ +int +msdos_free_node_info(rtems_filesystem_location_info_t *pathloc) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_close(pathloc->mt_entry, pathloc->node_access); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/c/src/exec/libfs/src/dosfs/msdos_fsunmount.c b/c/src/exec/libfs/src/dosfs/msdos_fsunmount.c new file mode 100644 index 0000000000..9072a2fad5 --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_fsunmount.c @@ -0,0 +1,71 @@ +/* + * MSDOS shut down handler implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_shut_down -- + * Shut down MSDOS filesystem - free all allocated resources (don't + * return if deallocation of some resource failed - free as much as + * possible). + * + * PARAMETERS: + * temp_mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + * + */ +int +msdos_shut_down(rtems_filesystem_mount_table_entry_t *temp_mt_entry) +{ + int rc = RC_OK; + msdos_fs_info_t *fs_info = temp_mt_entry->fs_info; + fat_file_fd_t *fat_fd = temp_mt_entry->mt_fs_root.node_access; + + /* close fat-file which correspondes to root directory */ + if (fat_file_close(temp_mt_entry, fat_fd) != RC_OK) + { + /* no return - try to free as much as possible */ + rc = -1; + } + + if (fat_shutdown_drive(temp_mt_entry) != RC_OK) + { + /* no return - try to free as much as possible */ + rc = -1; + } + + rtems_semaphore_delete(fs_info->vol_sema); + free(fs_info->cl_buf); + free(temp_mt_entry->fs_info); + + return rc; +} diff --git a/c/src/exec/libfs/src/dosfs/msdos_handlers_dir.c b/c/src/exec/libfs/src/dosfs/msdos_handlers_dir.c new file mode 100644 index 0000000000..e14d892add --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_handlers_dir.c @@ -0,0 +1,36 @@ +/* + * Directory Handlers Table for MSDOS filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "msdos.h" + +rtems_filesystem_file_handlers_r msdos_dir_handlers = { + msdos_dir_open, + msdos_dir_close, + msdos_dir_read, + NULL, /* msdos_dir_write */ + NULL, /* msdos_dir_ioctl */ + msdos_dir_lseek, + msdos_dir_stat, + NULL, + NULL, /* msdos_dir_ftruncate */ + NULL, + msdos_dir_sync, + msdos_dir_sync, + NULL, /* msdos_dir_fcntl */ + msdos_dir_rmnod +}; diff --git a/c/src/exec/libfs/src/dosfs/msdos_handlers_file.c b/c/src/exec/libfs/src/dosfs/msdos_handlers_file.c new file mode 100644 index 0000000000..ae627066de --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_handlers_file.c @@ -0,0 +1,36 @@ +/* + * File Operations Table for MSDOS filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "msdos.h" + +rtems_filesystem_file_handlers_r msdos_file_handlers = { + msdos_file_open, + msdos_file_close, + msdos_file_read, + msdos_file_write, + msdos_file_ioctl, + msdos_file_lseek, + msdos_file_stat, + NULL, + msdos_file_ftruncate, + NULL, + msdos_file_sync, + msdos_file_datasync, + NULL, /* msdos_file_fcntl */ + msdos_file_rmnod +}; diff --git a/c/src/exec/libfs/src/dosfs/msdos_init.c b/c/src/exec/libfs/src/dosfs/msdos_init.c new file mode 100644 index 0000000000..2d5bf6c9e0 --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_init.c @@ -0,0 +1,60 @@ +/* + * Init routine for MSDOS + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "msdos.h" + +rtems_filesystem_operations_table msdos_ops = { + msdos_eval_path, + msdos_eval4make, + NULL, /* msdos_link */ + msdos_file_rmnod, + msdos_node_type, + msdos_mknod, + NULL, /* msdos_chown */ + msdos_free_node_info, + NULL, + msdos_initialize, + NULL, + msdos_shut_down, /* msdos_shut_down */ + NULL, /* msdos_utime */ + NULL, + NULL, + NULL +}; + +/* msdos_initialize -- + * MSDOS filesystem initialization + * + * PARAMETERS: + * temp_mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + * + */ +int +msdos_initialize(rtems_filesystem_mount_table_entry_t *temp_mt_entry) +{ + int rc = RC_OK; + + rc = msdos_initialize_support(temp_mt_entry, + &msdos_ops, + &msdos_file_handlers, + &msdos_dir_handlers); + return rc; +} diff --git a/c/src/exec/libfs/src/dosfs/msdos_initsupp.c b/c/src/exec/libfs/src/dosfs/msdos_initsupp.c new file mode 100644 index 0000000000..eee8a6f9b2 --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_initsupp.c @@ -0,0 +1,149 @@ +/* + * MSDOS Initialization support routine implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_initialize_support -- + * MSDOS filesystem initialization + * + * PARAMETERS: + * temp_mt_entry - mount table entry + * op_table - filesystem operations table + * file_handlers - file operations table + * directory_handlers - directory operations table + * + * RETURNS: + * RC_OK and filled temp_mt_entry on success, or -1 if error occured + * (errno set apropriately) + * + */ +int +msdos_initialize_support( + rtems_filesystem_mount_table_entry_t *temp_mt_entry, + rtems_filesystem_operations_table *op_table, + rtems_filesystem_file_handlers_r *file_handlers, + rtems_filesystem_file_handlers_r *directory_handlers + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = NULL; + fat_file_fd_t *fat_fd = NULL; + unsigned32 cl_buf_size; + + fs_info = (msdos_fs_info_t *)calloc(1, sizeof(msdos_fs_info_t)); + if (!fs_info) + set_errno_and_return_minus_one(ENOMEM); + + temp_mt_entry->fs_info = fs_info; + + rc = fat_init_volume_info(temp_mt_entry); + if (rc != RC_OK) + { + free(fs_info); + return rc; + } + + fs_info->file_handlers = file_handlers; + fs_info->directory_handlers = directory_handlers; + + /* + * open fat-file which correspondes to root directory + * (so inode number 0x00000010 is always used for root directory) + */ + rc = fat_file_open(temp_mt_entry, FAT_ROOTDIR_CLUSTER_NUM, 0, &fat_fd); + if (rc != RC_OK) + { + fat_shutdown_drive(temp_mt_entry); + free(fs_info); + return rc; + } + + /* again: unfortunately "fat-file" is just almost fat file :( */ + fat_fd->fat_file_type = FAT_DIRECTORY; + fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT; + fat_fd->info_cln = FAT_ROOTDIR_CLUSTER_NUM; + fat_fd->info_ofs = 0; + fat_fd->cln = fs_info->fat.vol.rdir_cl; + + fat_fd->map.file_cln = 0; + fat_fd->map.disk_cln = fat_fd->cln; + + /* if we have FAT12/16 */ + if ( fat_fd->cln == 0 ) + { + fat_fd->fat_file_size = fs_info->fat.vol.rdir_size; + cl_buf_size = (fs_info->fat.vol.bpc > fs_info->fat.vol.rdir_size) ? + fs_info->fat.vol.bpc : + fs_info->fat.vol.rdir_size; + } + else + { + rc = fat_file_size(temp_mt_entry, fat_fd); + if ( rc != RC_OK ) + { + fat_file_close(temp_mt_entry, fat_fd); + fat_shutdown_drive(temp_mt_entry); + free(fs_info); + return rc; + } + cl_buf_size = fs_info->fat.vol.bpc; + } + + fs_info->cl_buf = (char *)calloc(cl_buf_size, sizeof(char)); + if (fs_info->cl_buf == NULL) + { + fat_file_close(temp_mt_entry, fat_fd); + fat_shutdown_drive(temp_mt_entry); + free(fs_info); + set_errno_and_return_minus_one(ENOMEM); + } + + sc = rtems_semaphore_create(3, + 1, + RTEMS_BINARY_SEMAPHORE | RTEMS_FIFO, + RTEMS_INHERIT_PRIORITY, + &fs_info->vol_sema); + if (sc != RTEMS_SUCCESSFUL) + { + fat_file_close(temp_mt_entry, fat_fd); + fat_shutdown_drive(temp_mt_entry); + free(fs_info->cl_buf); + free(fs_info); + set_errno_and_return_minus_one( EIO ); + } + + temp_mt_entry->mt_fs_root.node_access = fat_fd; + temp_mt_entry->mt_fs_root.handlers = directory_handlers; + temp_mt_entry->mt_fs_root.ops = op_table; + + return rc; +} diff --git a/c/src/exec/libfs/src/dosfs/msdos_misc.c b/c/src/exec/libfs/src/dosfs/msdos_misc.c new file mode 100644 index 0000000000..fe2779f7a8 --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_misc.c @@ -0,0 +1,1087 @@ +/* + * Miscellaneous routines implementation for MSDOS filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* This copied from Linux */ +static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 }; + /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ + +#undef CONFIG_ATARI + +/* MS-DOS "device special files" */ +static const char *reserved_names[] = { +#ifndef CONFIG_ATARI /* GEMDOS is less stupid */ + "CON ","PRN ","NUL ","AUX ", + "LPT1 ","LPT2 ","LPT3 ","LPT4 ", + "COM1 ","COM2 ","COM3 ","COM4 ", +#endif + NULL }; + +static char bad_chars[] = "*?<>|\""; +#ifdef CONFIG_ATARI +/* GEMDOS is less restrictive */ +static char bad_if_strict[] = " "; +#else +static char bad_if_strict[] = "+=,; "; +#endif + +/* The following three functions copied from Linux */ +/* + * Formats an MS-DOS file name. Rejects invalid names + * + * conv is relaxed/normal/strict, name is proposed name, + * len is the length of the proposed name, res is the result name, + * dotsOK is if hidden files get dots. + */ +int +msdos_format_name(char conv, const char *name, int len, char *res, + char dotsOK) +{ + char *walk; + const char **reserved; + unsigned char c; + int space; + if (name[0] == '.') { /* dotfile because . and .. already done */ + if (!dotsOK) return -EINVAL; + /* Get rid of dot - test for it elsewhere */ + name++; len--; + } +#ifndef CONFIG_ATARI + space = 1; /* disallow names that _really_ start with a dot */ +#else + space = 0; /* GEMDOS does not care */ +#endif + c = 0; + for (walk = res; len && walk-res < 8; walk++) { + c = *name++; + len--; + if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; + if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL; + if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL; + if (c < ' ' || c == ':' || c == '\\') return -EINVAL; +/* 0xE5 is legal as a first character, but we must substitute 0x05 */ +/* because 0xE5 marks deleted files. Yes, DOS really does this. */ +/* It seems that Microsoft hacked DOS to support non-US characters */ +/* after the 0xE5 character was already in use to mark deleted files. */ + if((res==walk) && (c==0xE5)) c=0x05; + if (c == '.') break; + space = (c == ' '); + *walk = (c >= 'a' && c <= 'z') ? c-32 : c; + } + if (space) return -EINVAL; + if (conv == 's' && len && c != '.') { + c = *name++; + len--; + if (c != '.') return -EINVAL; + } + while (c != '.' && len--) c = *name++; + if (c == '.') { + while (walk-res < 8) *walk++ = ' '; + while (len > 0 && walk-res < MSDOS_NAME_MAX) { + c = *name++; + len--; + if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; + if (conv == 's' && strchr(bad_if_strict,c)) + return -EINVAL; + if (c < ' ' || c == ':' || c == '\\') + return -EINVAL; + if (c == '.') { + if (conv == 's') + return -EINVAL; + break; + } + if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL; + space = c == ' '; + *walk++ = c >= 'a' && c <= 'z' ? c-32 : c; + } + if (space) return -EINVAL; + if (conv == 's' && len) return -EINVAL; + } + while (walk-res < MSDOS_NAME_MAX) *walk++ = ' '; + for (reserved = reserved_names; *reserved; reserved++) + if (!strncmp(res,*reserved,8)) return -EINVAL; + return 0; +} + +/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70) */ +unsigned int +msdos_date_dos2unix(unsigned short time_val,unsigned short date) +{ + int month,year,secs; + + month = ((date >> 5) & 15)-1; + year = date >> 9; + secs = (time_val & 31)*2+60*((time_val >> 5) & 63)+ + (time_val >> 11)*3600+86400* + ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && + month < 2 ? 1 : 0)+3653); + /* days since 1.1.70 plus 80's leap day */ + + return secs; +} + + +/* Convert linear UNIX date to a MS-DOS time/date pair */ +void msdos_date_unix2dos(int unix_date, + unsigned short *time_val, + unsigned short *date) +{ + int day,year,nl_day,month; + + *time_val = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+ + (((unix_date/3600) % 24) << 11); + day = unix_date/86400-3652; + year = day/365; + if ((year+3)/4+365*year > day) year--; + day -= (year+3)/4+365*year; + if (day == 59 && !(year & 3)) { + nl_day = day; + month = 2; + } + else { + nl_day = (year & 3) || day <= 59 ? day : day-1; + for (month = 0; month < 12; month++) + if (day_n[month] > nl_day) break; + } + *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9); +} + + +/* 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, char *ret_token, int *token_len) +{ + int rc = RC_OK; + register int i = 0; + msdos_token_types_t type = MSDOS_NAME; + char token[MSDOS_NAME_MAX_WITH_DOT+1]; + register char c; + + /* + * Copy a name into token. (Remember NULL is a token.) + */ + c = path[i]; + while ( (!msdos_is_separator(c)) && (i <= MSDOS_NAME_MAX_WITH_DOT) ) + { + token[i] = c; + if ( i == MSDOS_NAME_MAX_WITH_DOT ) + return MSDOS_INVALID_TOKEN; + if ( !msdos_is_valid_name_char(c) ) + return MSDOS_INVALID_TOKEN; + c = path [++i]; + } + + /* + * Copy a seperator into token. + */ + if ( i == 0 ) + { + token[i] = c; + if ( token[i] != '\0' ) + { + i++; + type = MSDOS_CURRENT_DIR; + } + else + type = MSDOS_NO_MORE_PATH; + } + else if (token[ i-1 ] != '\0') + token[i] = '\0'; + + /* + * Set token_len to the number of characters copied. + */ + *token_len = i; + + /* + * If we copied something that was not a seperator see if + * it was a special name. + */ + if ( type == MSDOS_NAME ) + { + if ( strcmp( token, "..") == 0 ) + { + strcpy(ret_token, MSDOS_DOTDOT_NAME); + type = MSDOS_UP_DIR; + return type; + } + + if ( strcmp( token, "." ) == 0 ) + { + strcpy(ret_token, MSDOS_DOT_NAME); + type = MSDOS_CURRENT_DIR; + return type; + } + + rc = msdos_format_name('r', token, *token_len, ret_token, 0); + if ( rc != RC_OK ) + return MSDOS_INVALID_TOKEN; + } + ret_token[MSDOS_NAME_MAX] = '\0'; + 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, + char *name + ) +{ + int rc = RC_OK; + msdos_fs_info_t *fs_info = parent_loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + fat_auxiliary_t aux; + unsigned short time_val = 0; + unsigned short date = 0; + unsigned char node_entry[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + + memset(node_entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + + /* + * find the node which correspondes to the name in the directory pointed by + * 'parent_loc' + */ + rc = msdos_get_name_node(parent_loc, name, &aux, node_entry); + if (rc != RC_OK) + return rc; + + /* open fat-file corresponded to the found node */ + rc = fat_file_open(parent_loc->mt_entry, aux.cln, aux.ofs, &fat_fd); + if (rc != RC_OK) + return rc; + + /* + * 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 non-optimize) because + * otherwise real values of these fields stored in fat-file descriptor + * may be accidentely rewritten with wrong values stored on the disk + */ + if (fat_fd->links_num == 1) + { + fat_fd->info_cln = aux.cln; + fat_fd->info_ofs = aux.ofs; + fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(node_entry); + fat_fd->first_char = *MSDOS_DIR_NAME(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(time_val), CF_LE_W(date)); + + 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 mode 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 + * name_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, + char *name, + fat_auxiliary_t *paux, + char *name_dir_entry + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = parent_loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = parent_loc->node_access; + unsigned32 dotdot_cln = 0; + + /* find name in fat-file which correspondes to the directory */ + rc = msdos_find_name_in_fat_file(parent_loc->mt_entry, fat_fd, name, paux, + name_dir_entry); + if ((rc != RC_OK) && (rc != MSDOS_NAME_NOT_FOUND_ERR)) + return rc; + + /* if we search for valid name and name not found -> return */ + if ((rc == MSDOS_NAME_NOT_FOUND_ERR) && (name != NULL)) + return rc; + + /* + * if we try to create new entry and the directory is not big enough + * currently - try to enlarge directory + */ + if ((rc == MSDOS_NAME_NOT_FOUND_ERR) && (name == NULL)) + { + ret = fat_file_write(parent_loc->mt_entry, fat_fd, + fat_fd->fat_file_size, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE, + name_dir_entry); + if (ret == -1) + return -1; + + /* on success directory is enlarged by a new cluster */ + fat_fd->fat_file_size += fs_info->fat.vol.bpc; + + /* get cluster num where a new node located */ + rc = fat_file_ioctl(parent_loc->mt_entry, fat_fd, F_CLU_NUM, + fat_fd->fat_file_size - 1, &paux->cln); + + if (rc != RC_OK) + return rc; + + /* + * if new cluster allocated succesfully then new node is at very + * beginning of the cluster (offset is computed in bytes) + */ + paux->ofs = 0; + return RC_OK; + } + + /* + * 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) && (name != NULL)) + { + if (strncmp(name, MSDOS_DOTDOT_NAME, MSDOS_SHORT_NAME_LEN) == 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 + */ + paux->cln = FAT_ROOTDIR_CLUSTER_NUM; + paux->ofs = 0; + } + else + { + rc = msdos_get_dotdot_dir_info_cluster_num_and_offset( + parent_loc->mt_entry, + dotdot_cln, + paux, + 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, + unsigned32 cln, + fat_auxiliary_t *paux, + char *dir_entry + ) +{ + int rc = RC_OK; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + unsigned char dot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + unsigned char dotdot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + unsigned char cur_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + unsigned32 cl4find = 0; + + memset(dot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memset(dotdot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memset(cur_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + + /* + * open fat-file corresponded to ".." + */ + rc = fat_file_open(mt_entry, paux->cln, paux->ofs, &fat_fd); + if (rc != RC_OK) + return rc; + + fat_fd->info_cln = paux->cln; + fat_fd->info_ofs = paux->ofs; + 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 */ + rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, MSDOS_DOT_NAME, paux, + dot_node); + + if (rc != RC_OK) + { + fat_file_close(mt_entry, fat_fd); + return rc; + } + + /* find ".." node in opened directory */ + rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, MSDOS_DOTDOT_NAME, paux, + 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 ) + */ + paux->cln = FAT_ROOTDIR_CLUSTER_NUM; + paux->ofs = 0; + } + + /* open fat-file corresponded to second ".." */ + rc = fat_file_open(mt_entry, paux->cln, paux->ofs, &fat_fd); + if (rc != RC_OK) + return rc; + + fat_fd->info_cln = paux->cln; + fat_fd->info_ofs = paux->ofs; + + 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, + paux, 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; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned short time_val; + unsigned short date; + unsigned32 sec = 0; + unsigned32 byte = 0; + + msdos_date_unix2dos(fat_fd->mtime, &time_val, &date); + + /* + * 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->info_cln); + sec += (fat_fd->info_ofs >> fs_info->fat.vol.sec_log2); + /* byte points to start of 32bytes structure */ + byte = fat_fd->info_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)); + + if ( (ret1 < 0) || (ret2 < 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; + unsigned32 new_cln = fat_fd->cln; + unsigned16 le_cl_low = 0; + unsigned16 le_cl_hi = 0; + unsigned32 sec = 0; + unsigned32 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->info_cln); + sec += (fat_fd->info_ofs >> fs_info->fat.vol.sec_log2); + /* byte from points to start of 32bytes structure */ + byte = fat_fd->info_ofs & (fs_info->fat.vol.bps - 1); + + le_cl_low = CT_LE_W((unsigned16)(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((unsigned16)((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; + unsigned32 le_new_length = 0; + unsigned32 sec = 0; + unsigned32 byte = 0; + + sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->info_cln); + sec += (fat_fd->info_ofs >> fs_info->fat.vol.sec_log2); + byte = (fat_fd->info_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, + unsigned32 cl, + unsigned32 ofs, + unsigned char fchar + ) +{ + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 sec = 0; + unsigned32 byte = 0; + + sec = fat_cluster_num_to_sector_num(mt_entry, cl); + sec += (ofs >> fs_info->fat.vol.sec_log2); + byte = (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; + + 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, + rtems_boolean *ret_val + ) +{ + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 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); + + for (i = 0; + i < fs_info->fat.vol.bps; + i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + if (((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_EMPTY) || + (strncmp(MSDOS_DIR_NAME((fs_info->cl_buf + i)), MSDOS_DOT_NAME, + MSDOS_SHORT_NAME_LEN) == 0) || + (strncmp(MSDOS_DIR_NAME((fs_info->cl_buf + i)), + MSDOS_DOTDOT_NAME, + MSDOS_SHORT_NAME_LEN) == 0)) + continue; + + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) + { + *ret_val = TRUE; + return RC_OK; + } + return RC_OK; + } + j++; + } + *ret_val = TRUE; + return RC_OK; +} + + +/* msdos_find_name_in_fat_file -- + * This routine is used in two ways: for a new mode 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 corresponded to directory we are going to create + * node in. If found free slot write contents of name_dir_entry into + * it. + * + * (b): reading fat-file corresponded to directory and trying to find slot + * with the name field == name parameter + * + * 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) + * + */ +int +msdos_find_name_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + char *name, + fat_auxiliary_t *paux, + char *name_dir_entry + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 i = 0, j = 0; + unsigned32 bts2rd = 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) + set_errno_and_return_minus_one(EIO); + + assert(ret == bts2rd); + + for (i = 0; i < bts2rd; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + /* is the entry empty ? */ + if (((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) || + ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_EMPTY)) + { + /* whether we are looking for an empty entry */ + if (name == NULL) + { + /* get current cluster number */ + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, + j * bts2rd, &paux->cln); + if (rc != RC_OK) + return rc; + + /* offset is computed in bytes */ + paux->ofs = i; + + /* write new node entry */ + ret = fat_file_write(mt_entry, fat_fd, j * bts2rd + i, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE, + name_dir_entry); + if (ret != MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + return -1; + + /* + * we don't update fat_file_size here - it should not + * increase + */ + return RC_OK; + } + + /* + * if name != NULL and there is no more entries in the + * directory - return name-not-found + */ + if (((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)) + return MSDOS_NAME_NOT_FOUND_ERR; + } + else + { + /* entry not empty and name != NULL -> compare names */ + if (name != NULL) + { + if (strncmp(MSDOS_DIR_NAME((fs_info->cl_buf + i)), name, + MSDOS_SHORT_NAME_LEN) == 0) + { + /* + * we get the entry we looked for - fill auxiliary + * structure and copy all 32 bytes of the entry + */ + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, + j * bts2rd, &paux->cln); + if (rc != RC_OK) + return rc; + + /* offset is computed in bytes */ + paux->ofs = i; + memcpy(name_dir_entry,(fs_info->cl_buf + i), + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + return RC_OK; + } + } + } + } + j++; + } + return MSDOS_NAME_NOT_FOUND_ERR; +} + +/* msdos_find_node_by_cluster_num_in_fat_file -- + * Find node with specified number of cluster in fat-file. + * + * 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, + unsigned32 cl4find, + fat_auxiliary_t *paux, + char *dir_entry + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 bts2rd = 0; + unsigned32 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 ) + set_errno_and_return_minus_one( EIO ); + + assert(ret == bts2rd); + + for (i = 0; i < bts2rd; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + /* if this and all rest entries are empty - return not-found */ + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) + return MSDOS_NAME_NOT_FOUND_ERR; + + /* if this entry is empty - skip it */ + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_EMPTY) + continue; + + /* if get a non-empty entry - compare clusters num */ + if (MSDOS_EXTRACT_CLUSTER_NUM((fs_info->cl_buf + i)) == cl4find) + { + /* on success fill aux structure and copy all 32 bytes */ + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, j * bts2rd, + &paux->cln); + if (rc != RC_OK) + return rc; + + paux->ofs = i; + memcpy(dir_entry, fs_info->cl_buf + i, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + return RC_OK; + } + } + j++; + } + return MSDOS_NAME_NOT_FOUND_ERR; +} diff --git a/c/src/exec/libfs/src/dosfs/msdos_mknod.c b/c/src/exec/libfs/src/dosfs/msdos_mknod.c new file mode 100644 index 0000000000..5e32dbf3bf --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_mknod.c @@ -0,0 +1,90 @@ +/* + * Routine for node creation in MSDOS filesystem. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_mknod -- + * The following function checks spelling and formats name for a new node, + * determines type of the node to be created and creates it. + * + * PARAMETERS: + * token - non-formatted name of a new node + * mode - node type + * dev - dev + * pathloc - parent directory description + * + * RETURNS: + * RC_OK on succes, or -1 if error occured and set errno + * + */ +int +msdos_mknod( + const char *token, + mode_t mode, + dev_t dev, + rtems_filesystem_location_info_t *pathloc + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + msdos_token_types_t type = 0; + char new_name[ MSDOS_NAME_MAX + 1 ]; + int len; + + /* check spelling and format new node name */ + msdos_get_token(token, new_name, &len); + + /* + * Figure out what type of msdos node this is. + */ + if (S_ISDIR(mode)) + { + type = MSDOS_DIRECTORY; + } + else if (S_ISREG(mode)) + { + type = MSDOS_REGULAR_FILE; + } + else + set_errno_and_return_minus_one(EINVAL); + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* Create an MSDOS node */ + rc = msdos_creat_node(pathloc, type, new_name, mode); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/c/src/exec/libfs/src/dosfs/msdos_node_type.c b/c/src/exec/libfs/src/dosfs/msdos_node_type.c new file mode 100644 index 0000000000..517dabda3f --- /dev/null +++ b/c/src/exec/libfs/src/dosfs/msdos_node_type.c @@ -0,0 +1,58 @@ +/* + * The following returns the type of node that the loc refers to. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_node_type -- + * Determine type of the node that the pathloc refers to. + * + * PARAMETERS: + * pathloc - node description + * + * RETURNS: + * node type + * + */ +rtems_filesystem_node_types_t +msdos_node_type(rtems_filesystem_location_info_t *pathloc) +{ + fat_file_fd_t *fat_fd; + + /* + * we don't need to obtain the volume semaphore here because node_type_h + * call always follows evalpath_h call(hence link increment occured) and + * hence node_access memory can't be freed during processing node_type_h + * call + */ + fat_fd = pathloc->node_access; + + return fat_fd->fat_file_type; +} diff --git a/c/src/exec/libfs/wrapup/Makefile.am b/c/src/exec/libfs/wrapup/Makefile.am index 5dfd76e83d..13406dcb5f 100644 --- a/c/src/exec/libfs/wrapup/Makefile.am +++ b/c/src/exec/libfs/wrapup/Makefile.am @@ -13,7 +13,9 @@ include $(top_srcdir)/../../../automake/lib.am IMFSLIB = ../src/imfs/$(ARCH)/libimfs.a -TMP_LIBS = $(IMFSLIB) +DOSFSLIB = ../src/dosfs/$(ARCH)/libdosfs.a + +TMP_LIBS = $(IMFSLIB) $(DOSFSLIB) $(PROJECT_RELEASE)/lib/$(LIBNAME)$(LIB_VARIANT).a: $(LIB) $(INSTALL_DATA) $< $@ diff --git a/c/src/libfs/ChangeLog b/c/src/libfs/ChangeLog index b4dd8e9f61..4166f2e369 100644 --- a/c/src/libfs/ChangeLog +++ b/c/src/libfs/ChangeLog @@ -1,3 +1,20 @@ +2002-02-28 Victor V. Vengerov + + * 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. + 2002-01-07 Ralf Corsepius * src/imfs/imfs_load_tar.c: Add include . diff --git a/c/src/libfs/configure.ac b/c/src/libfs/configure.ac index 4e2adeb1b5..7f36fac28e 100644 --- a/c/src/libfs/configure.ac +++ b/c/src/libfs/configure.ac @@ -5,6 +5,7 @@ AC_PREREQ(2.52) AC_INIT AC_CONFIG_SRCDIR([src/imfs/imfs.h]) +AC_CONFIG_SRCDIR([src/dosfs/dosfs.h]) RTEMS_TOP(../../..) AC_CONFIG_AUX_DIR(../../..) @@ -27,11 +28,13 @@ RTEMS_CANONICALIZE_TOOLS AM_CONDITIONAL(UNIX,test x"$RTEMS_CPU" = x"unix") AM_CONFIG_HEADER(src/imfs/config.h) +AM_CONFIG_HEADER(src/dosfs/config.h) # Explicitly list all Makefiles here AC_CONFIG_FILES([Makefile src/Makefile src/imfs/Makefile +src/dosfs/Makefile wrapup/Makefile ]) AC_OUTPUT diff --git a/c/src/libfs/src/Makefile.am b/c/src/libfs/src/Makefile.am index 126a226126..fcd82899b4 100644 --- a/c/src/libfs/src/Makefile.am +++ b/c/src/libfs/src/Makefile.am @@ -4,7 +4,7 @@ AUTOMAKE_OPTIONS = foreign 1.4 -SUBDIRS = imfs +SUBDIRS = imfs dosfs include $(top_srcdir)/../../../automake/subdirs.am include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/libfs/src/dosfs/.cvsignore b/c/src/libfs/src/dosfs/.cvsignore new file mode 100644 index 0000000000..7bb609bf24 --- /dev/null +++ b/c/src/libfs/src/dosfs/.cvsignore @@ -0,0 +1,6 @@ +Makefile +Makefile.in +config.h +config.h.in +stamp-h +stamp-h.in diff --git a/c/src/libfs/src/dosfs/Makefile.am b/c/src/libfs/src/dosfs/Makefile.am new file mode 100644 index 0000000000..44c9f4cd2f --- /dev/null +++ b/c/src/libfs/src/dosfs/Makefile.am @@ -0,0 +1,80 @@ +## +## $Id$ +## + +AUTOMAKE_OPTIONS = foreign 1.4 + +INCLUDES = -I. + +LIBNAME = libdosfs +LIB = ${ARCH}/${LIBNAME}.a + +FATFS_C_FILES = fat.c fat_fat_operations.c fat_file.c + +DOSFS_C_FILES = msdos_create.c msdos_dir.c msdos_eval.c msdos_file.c \ + msdos_free.c msdos_fsunmount.c msdos_handlers_dir.c \ + msdos_handlers_file.c msdos_init.c msdos_initsupp.c \ + msdos_misc.c msdos_mknod.c msdos_node_type.c + + +UNIX_C_FILES = msdos_unixstub.c + +EMBEDDED_C_FILES = $(FATFS_C_FILES) $(DOSFS_C_FILES) + +COMMON_C_FILES = + +if UNIX +C_FILES = $(COMMON_C_FILES) $(UNIX_C_FILES) +else +C_FILES = $(COMMON_C_FILES) $(EMBEDDED_C_FILES) +endif +C_O_FILES = $(C_FILES:%.c=${ARCH}/%.o) + +include_HEADERS = fat.h fat_fat_operations.h \ + fat_file.h msdos.h dosfs.h +SYS_H_FILES = +RTEMS_H_FILES = +noinst_HEADERS = + +include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg +include $(top_srcdir)/../../../automake/compile.am +include $(top_srcdir)/../../../automake/lib.am + +PREINSTALL_FILES = $(PROJECT_INCLUDE) $(PROJECT_INCLUDE)/rtems \ + $(PROJECT_INCLUDE)/sys $(include_HEADERS:%=$(PROJECT_INCLUDE)/%) \ + $(RTEMS_H_FILES:%=$(PROJECT_INCLUDE)/rtems/%) \ + $(SYS_H_FILES:%=$(PROJECT_INCLUDE)/sys/%) + +$(PROJECT_INCLUDE): + @$(mkinstalldirs) $@ +$(PROJECT_INCLUDE)/rtems: + @$(mkinstalldirs) $@ +$(PROJECT_INCLUDE)/sys: + @$(mkinstalldirs) $@ + +$(PROJECT_INCLUDE)/%.h: %.h + $(INSTALL_DATA) $< $@ +$(PROJECT_INCLUDE)/rtems/%.h: %.h + $(INSTALL_DATA) $< $@ +$(PROJECT_INCLUDE)/sys/%.h: %.h + $(INSTALL_DATA) $< $@ + +OBJS = $(C_O_FILES) + +# +# Add local stuff here using += +# + +AM_CFLAGS += $(LIBC_DEFINES) + +all-local: ${ARCH} $(LIB) + +$(LIB): ${OBJS} + $(make-library) + +DOC_FILES = + +EXTRA_DIST = $(DOC_FILES) $(COMMON_C_FILES) $(EMBEDDED_C_FILES) \ + $(UNIX_C_FILES) $(RTEMS_H_FILES) $(SYS_H_FILES) + +include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/libfs/src/dosfs/config.h.in b/c/src/libfs/src/dosfs/config.h.in new file mode 100644 index 0000000000..489947fd57 --- /dev/null +++ b/c/src/libfs/src/dosfs/config.h.in @@ -0,0 +1 @@ +/* src/dosfs/config.h.in. Generated automatically from configure.in by autoheader. */ diff --git a/c/src/libfs/src/dosfs/dosfs.h b/c/src/libfs/src/dosfs/dosfs.h new file mode 100644 index 0000000000..4cea929d4c --- /dev/null +++ b/c/src/libfs/src/dosfs/dosfs.h @@ -0,0 +1,31 @@ +/* + * dosfs.h + * + * Application interface to MSDOS filesystem. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_DOSFS_H__ +#define __DOSFS_DOSFS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +extern rtems_filesystem_operations_table msdos_ops; + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_DOSFS_H__ */ diff --git a/c/src/libfs/src/dosfs/fat.c b/c/src/libfs/src/dosfs/fat.c new file mode 100644 index 0000000000..852c104781 --- /dev/null +++ b/c/src/libfs/src/dosfs/fat.c @@ -0,0 +1,695 @@ +/* + * fat.c + * + * Low-level operations on a volume with FAT filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * @(#) $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" + +/* _fat_block_read -- + * This function reads 'count' bytes from device filesystem is mounted on, + * starts at 'start+offset' position where 'start' computed in sectors + * and 'offset' is offset inside sector (reading may cross sectors + * boundary; in this case assumed we want to read sequential sector(s)) + * + * PARAMETERS: + * mt_entry - mount table entry + * start - sector num to start read from + * offset - offset inside sector 'start' + * count - count of bytes to read + * buff - buffer provided by user + * + * RETURNS: + * bytes read on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +_fat_block_read( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start, + unsigned32 offset, + unsigned32 count, + void *buff + ) +{ + int rc = RC_OK; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + ssize_t cmpltd = 0; + unsigned32 blk = start; + unsigned32 ofs = offset; + bdbuf_buffer *block = NULL; + unsigned32 c = 0; + + while (count > 0) + { + rc = fat_buf_access(fs_info, blk, FAT_OP_TYPE_READ, &block); + if (rc != RC_OK) + return rc; + + c = MIN(count, (fs_info->vol.bps - ofs)); + memcpy((buff + cmpltd), (block->buffer + ofs), c); + + count -= c; + cmpltd += c; + blk++; + ofs = 0; + } + return cmpltd; +} + +/* _fat_block_write -- + * This function write 'count' bytes to device filesystem is mounted on, + * starts at 'start+offset' position where 'start' computed in sectors + * and 'offset' is offset inside sector (writing may cross sectors + * boundary; in this case assumed we want to write sequential sector(s)) + * + * PARAMETERS: + * mt_entry - mount table entry + * start - sector num to start read from + * offset - offset inside sector 'start' + * count - count of bytes to write + * buff - buffer provided by user + * + * RETURNS: + * bytes written on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +_fat_block_write( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start, + unsigned32 offset, + unsigned32 count, + const void *buff) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + ssize_t cmpltd = 0; + unsigned32 blk = start; + unsigned32 ofs = offset; + bdbuf_buffer *block = NULL; + unsigned32 c = 0; + + while(count > 0) + { + c = MIN(count, (fs_info->vol.bps - ofs)); + + if (c == fs_info->vol.bps) + rc = fat_buf_access(fs_info, blk, FAT_OP_TYPE_GET, &block); + else + rc = fat_buf_access(fs_info, blk, FAT_OP_TYPE_READ, &block); + if (rc != RC_OK) + return rc; + + memcpy((block->buffer + ofs), (buff + cmpltd), c); + + fat_buf_mark_modified(fs_info); + + count -= c; + cmpltd +=c; + blk++; + ofs = 0; + } + return cmpltd; +} + + + + +/* fat_cluster_read -- + * wrapper for reading a whole cluster at once + * + * PARAMETERS: + * mt_entry - mount table entry + * cln - number of cluster to read + * buff - buffer provided by user + * + * RETURNS: + * bytes read on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +fat_cluster_read( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + void *buff + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 fsec = 0; + + fsec = fat_cluster_num_to_sector_num(mt_entry, cln); + + return _fat_block_read(mt_entry, fsec, 0, + fs_info->vol.spc << fs_info->vol.sec_log2, buff); +} + +/* fat_cluster_write -- + * wrapper for writting a whole cluster at once + * + * PARAMETERS: + * mt_entry - mount table entry + * cln - number of cluster to write + * buff - buffer provided by user + * + * RETURNS: + * bytes written on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +fat_cluster_write( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + const void *buff + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 fsec = 0; + + fsec = fat_cluster_num_to_sector_num(mt_entry, cln); + + return _fat_block_write(mt_entry, fsec, 0, + fs_info->vol.spc << fs_info->vol.sec_log2, buff); +} + +/* fat_init_volume_info -- + * Get inforamtion about volume on which filesystem is mounted on + * + * PARAMETERS: + * mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured + * and errno set appropriately + */ +int +fat_init_volume_info(rtems_filesystem_mount_table_entry_t *mt_entry) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + register fat_vol_t *vol = &fs_info->vol; + unsigned32 data_secs = 0; + char boot_rec[FAT_MAX_BPB_SIZE]; + char fs_info_sector[FAT_USEFUL_INFO_SIZE]; + ssize_t ret = 0; + int fd; + struct stat stat_buf; + int i = 0; + + rc = stat(mt_entry->dev, &stat_buf); + if (rc == -1) + return rc; + + /* rtmes feature: no block devices, all are character devices */ + if (!S_ISCHR(stat_buf.st_mode)) + set_errno_and_return_minus_one(ENOTBLK); + + /* check that device is registred as block device and lock it */ + vol->dd = rtems_disk_lookup(stat_buf.st_dev); + if (vol->dd == NULL) + set_errno_and_return_minus_one(ENOTBLK); + + vol->dev = stat_buf.st_dev; + + fd = open(mt_entry->dev, O_RDONLY); + if (fd == -1) + { + rtems_disk_release(vol->dd); + return -1; + } + + ret = read(fd, (void *)boot_rec, FAT_MAX_BPB_SIZE); + if ( ret != FAT_MAX_BPB_SIZE ) + { + close(fd); + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( EIO ); + } + close(fd); + + vol->bps = FAT_BR_BYTES_PER_SECTOR(boot_rec); + + if ( (vol->bps != 512) && + (vol->bps != 1024) && + (vol->bps != 2048) && + (vol->bps != 4096)) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( EINVAL ); + } + + for (vol->sec_mul = 0, i = (vol->bps >> FAT_SECTOR512_BITS); (i & 1) == 0; + i >>= 1, vol->sec_mul++); + for (vol->sec_log2 = 0, i = vol->bps; (i & 1) == 0; + i >>= 1, vol->sec_log2++); + + vol->spc = FAT_BR_SECTORS_PER_CLUSTER(boot_rec); + for (vol->spc_log2 = 0, i = vol->spc; (i & 1) == 0; + i >>= 1, vol->spc_log2++); + + /* + * According to M$ White Paper "bytes per cluster" value + * greater than 32K is invalid + */ + if ((vol->bpc = vol->bps << vol->spc_log2) > MS_BYTES_PER_CLUSTER_LIMIT) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one(EINVAL); + } + + for (vol->bpc_log2 = 0, i = vol->bpc; (i & 1) == 0; + i >>= 1, vol->bpc_log2++); + + vol->fats = FAT_BR_FAT_NUM(boot_rec); + vol->fat_loc = FAT_BR_RESERVED_SECTORS_NUM(boot_rec); + + vol->rdir_entrs = FAT_BR_FILES_PER_ROOT_DIR(boot_rec); + + /* calculate the count of sectors occupied by the root directory */ + vol->rdir_secs = ((vol->rdir_entrs * FAT_DIRENTRY_SIZE) + (vol->bps - 1)) / + vol->bps; + + vol->rdir_size = vol->rdir_secs << vol->sec_log2; + + if ( (FAT_BR_SECTORS_PER_FAT(boot_rec)) != 0) + vol->fat_length = FAT_BR_SECTORS_PER_FAT(boot_rec); + else + vol->fat_length = FAT_BR_SECTORS_PER_FAT32(boot_rec); + + vol->data_fsec = vol->fat_loc + vol->fats * vol->fat_length + + vol->rdir_secs; + + /* for FAT12/16 root dir starts at(sector) */ + vol->rdir_loc = vol->fat_loc + vol->fats * vol->fat_length; + + if ( (FAT_BR_TOTAL_SECTORS_NUM16(boot_rec)) != 0) + vol->tot_secs = FAT_BR_TOTAL_SECTORS_NUM16(boot_rec); + else + vol->tot_secs = FAT_BR_TOTAL_SECTORS_NUM32(boot_rec); + + data_secs = vol->tot_secs - vol->data_fsec; + + vol->data_cls = data_secs / vol->spc; + + /* determine FAT type at least */ + if ( vol->data_cls < FAT_FAT12_MAX_CLN) + { + vol->type = FAT_FAT12; + vol->mask = FAT_FAT12_MASK; + vol->eoc_val = FAT_FAT12_EOC; + } + else + { + if ( vol->data_cls < FAT_FAT16_MAX_CLN) + { + vol->type = FAT_FAT16; + vol->mask = FAT_FAT16_MASK; + vol->eoc_val = FAT_FAT16_EOC; + } + else + { + vol->type = FAT_FAT32; + vol->mask = FAT_FAT32_MASK; + vol->eoc_val = FAT_FAT32_EOC; + } + } + + if (vol->type == FAT_FAT32) + { + vol->rdir_cl = FAT_BR_FAT32_ROOT_CLUSTER(boot_rec); + + vol->mirror = FAT_BR_EXT_FLAGS(boot_rec) & FAT_BR_EXT_FLAGS_MIRROR; + if (vol->mirror) + vol->afat = FAT_BR_EXT_FLAGS(boot_rec) & FAT_BR_EXT_FLAGS_FAT_NUM; + else + vol->afat = 0; + + vol->info_sec = FAT_BR_FAT32_FS_INFO_SECTOR(boot_rec); + if( vol->info_sec == 0 ) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( EINVAL ); + } + else + { + ret = _fat_block_read(mt_entry, vol->info_sec , 0, + FAT_FSI_LEADSIG_SIZE, fs_info_sector); + if ( ret < 0 ) + { + rtems_disk_release(vol->dd); + return -1; + } + + if (FAT_FSINFO_LEAD_SIGNATURE(fs_info_sector) != + FAT_FSINFO_LEAD_SIGNATURE_VALUE) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( EINVAL ); + } + else + { + ret = _fat_block_read(mt_entry, vol->info_sec , FAT_FSI_INFO, + FAT_USEFUL_INFO_SIZE, fs_info_sector); + if ( ret < 0 ) + { + rtems_disk_release(vol->dd); + return -1; + } + + vol->free_cls = FAT_FSINFO_FREE_CLUSTER_COUNT(fs_info_sector); + vol->next_cl = FAT_FSINFO_NEXT_FREE_CLUSTER(fs_info_sector); + rc = fat_fat32_update_fsinfo_sector(mt_entry, 0xFFFFFFFF, + 0xFFFFFFFF); + if ( rc != RC_OK ) + { + rtems_disk_release(vol->dd); + return rc; + } + } + } + } + else + { + vol->rdir_cl = 0; + vol->mirror = 0; + vol->afat = 0; + vol->free_cls = 0xFFFFFFFF; + vol->next_cl = 0xFFFFFFFF; + } + vol->afat_loc = vol->fat_loc + vol->fat_length * vol->afat; + + /* set up collection of fat-files fd */ + fs_info->vhash = calloc(FAT_HASH_SIZE, sizeof(Chain_Control)); + if ( fs_info->vhash == NULL ) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( ENOMEM ); + } + + for (i = 0; i < FAT_HASH_SIZE; i++) + _Chain_Initialize_empty(fs_info->vhash + i); + + fs_info->rhash = calloc(FAT_HASH_SIZE, sizeof(Chain_Control)); + if ( fs_info->rhash == NULL ) + { + rtems_disk_release(vol->dd); + free(fs_info->vhash); + set_errno_and_return_minus_one( ENOMEM ); + } + for (i = 0; i < FAT_HASH_SIZE; i++) + _Chain_Initialize_empty(fs_info->rhash + i); + + fs_info->uino_pool_size = FAT_UINO_POOL_INIT_SIZE; + fs_info->uino_base = (vol->tot_secs << vol->sec_mul) << 4; + fs_info->index = 0; + fs_info->uino = (char *)calloc(fs_info->uino_pool_size, sizeof(char)); + if ( fs_info->uino == NULL ) + { + rtems_disk_release(vol->dd); + free(fs_info->vhash); + free(fs_info->rhash); + set_errno_and_return_minus_one( ENOMEM ); + } + fs_info->sec_buf = (char *)calloc(vol->bps, sizeof(char)); + if (fs_info->sec_buf == NULL) + { + rtems_disk_release(vol->dd); + free(fs_info->vhash); + free(fs_info->rhash); + free(fs_info->uino); + set_errno_and_return_minus_one( ENOMEM ); + } + + return RC_OK; +} + +/* fat_shutdown_drive -- + * Free all allocated resources and synchronize all necessary data + * + * PARAMETERS: + * mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured + * and errno set appropriately + */ +int +fat_shutdown_drive(rtems_filesystem_mount_table_entry_t *mt_entry) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + int i = 0; + + if (fs_info->vol.type & FAT_FAT32) + { + rc = fat_fat32_update_fsinfo_sector(mt_entry, fs_info->vol.free_cls, + fs_info->vol.next_cl); + if ( rc != RC_OK ) + rc = -1; + } + + fat_buf_release(fs_info); + + if (rtems_bdbuf_syncdev(fs_info->vol.dev) != RTEMS_SUCCESSFUL) + rc = -1; + + for (i = 0; i < FAT_HASH_SIZE; i++) + { + Chain_Node *node = NULL; + Chain_Control *the_chain = fs_info->vhash + i; + + while ( (node = _Chain_Get(the_chain)) != NULL ) + free(node); + } + + for (i = 0; i < FAT_HASH_SIZE; i++) + { + Chain_Node *node = NULL; + Chain_Control *the_chain = fs_info->rhash + i; + + while ( (node = _Chain_Get(the_chain)) != NULL ) + free(node); + } + + free(fs_info->vhash); + free(fs_info->rhash); + + free(fs_info->uino); + free(fs_info->sec_buf); + rtems_disk_release(fs_info->vol.dd); + + if (rc) + errno = EIO; + return rc; +} + +/* fat_init_clusters_chain -- + * Zeroing contents of all clusters in the chain + * + * PARAMETERS: + * mt_entry - mount table entry + * start_cluster_num - num of first cluster in the chain + * + * RETURNS: + * RC_OK on success, or -1 if error occured + * and errno set appropriately + */ +int +fat_init_clusters_chain( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start_cln + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 cur_cln = start_cln; + char *buf; + + buf = calloc(fs_info->vol.bpc, sizeof(char)); + if ( buf == NULL ) + set_errno_and_return_minus_one( EIO ); + + while ((cur_cln & fs_info->vol.mask) != fs_info->vol.eoc_val) + { + ret = fat_cluster_write(mt_entry, cur_cln, buf); + if ( ret == -1 ) + { + free(buf); + return -1; + } + + rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln); + if ( rc != RC_OK ) + { + free(buf); + return rc; + } + + } + free(buf); + return rc; +} + +#define FAT_UNIQ_INO_BASE 0x0FFFFF00 + +#define FAT_UNIQ_INO_IS_BUSY(index, arr) \ + (((arr)[((index)>>3)]>>((index) & (8-1))) & 0x01) + +#define FAT_SET_UNIQ_INO_BUSY(index, arr) \ + ((arr)[((index)>>3)] |= (0x01<<((index) & (8-1)))) + +#define FAT_SET_UNIQ_INO_FREE(index, arr) \ + ((arr)[((index)>>3)] &= (~(0x01<<((index) & (8-1))))) + +/* fat_get_unique_ino -- + * Allocate unique ino from unique ino pool + * + * PARAMETERS: + * mt_entry - mount table entry + * + * RETURNS: + * unique inode number on success, or 0 if there is no free unique inode + * number in the pool + * + * ATTENTION: + * 0 means FAILED !!! + * + */ +unsigned32 +fat_get_unique_ino(rtems_filesystem_mount_table_entry_t *mt_entry) +{ + register fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 j = 0; + rtems_boolean resrc_unsuff = FALSE; + + while (!resrc_unsuff) + { + for (j = 0; j < fs_info->uino_pool_size; j++) + { + if (!FAT_UNIQ_INO_IS_BUSY(fs_info->index, fs_info->uino)) + { + FAT_SET_UNIQ_INO_BUSY(fs_info->index, fs_info->uino); + return (fs_info->uino_base + fs_info->index); + } + fs_info->index++; + if (fs_info->index >= fs_info->uino_pool_size) + fs_info->index = 0; + } + + if ((fs_info->uino_pool_size << 1) < (0x0FFFFFFF - fs_info->uino_base)) + { + fs_info->uino_pool_size <<= 1; + fs_info->uino = realloc(fs_info->uino, fs_info->uino_pool_size); + if (fs_info->uino != NULL) + fs_info->index = fs_info->uino_pool_size; + else + resrc_unsuff = TRUE; + } + else + resrc_unsuff = TRUE; + } + return 0; +} + +/* fat_free_unique_ino -- + * Return unique ino to unique ino pool + * + * PARAMETERS: + * mt_entry - mount table entry + * ino - inode number to free + * + * RETURNS: + * None + */ +void +fat_free_unique_ino( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 ino + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + + FAT_SET_UNIQ_INO_FREE((ino - fs_info->uino_base), fs_info->uino); +} + +/* fat_ino_is_unique -- + * Test whether ino is from unique ino pool + * + * PARAMETERS: + * mt_entry - mount table entry + * ino - ino to be tested + * + * RETURNS: + * TRUE if ino is allocated from unique ino pool, FALSE otherwise + */ +inline rtems_boolean +fat_ino_is_unique( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 ino + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + + return (ino >= fs_info->uino_base); +} + +/* fat_fat32_update_fsinfo_sector -- + * Synchronize fsinfo sector for FAT32 volumes + * + * PARAMETERS: + * mt_entry - mount table entry + * free_count - count of free clusters + * next_free - the next free cluster num + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +fat_fat32_update_fsinfo_sector( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 free_count, + unsigned32 next_free + ) +{ + ssize_t ret1 = 0, ret2 = 0; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 le_free_count = 0; + unsigned32 le_next_free = 0; + + le_free_count = CT_LE_L(free_count); + le_next_free = CT_LE_L(next_free); + + ret1 = _fat_block_write(mt_entry, + fs_info->vol.info_sec, + FAT_FSINFO_FREE_CLUSTER_COUNT_OFFSET, + 4, + (char *)(&le_free_count)); + + ret2 = _fat_block_write(mt_entry, + fs_info->vol.info_sec, + FAT_FSINFO_NEXT_FREE_CLUSTER_OFFSET, + 4, + (char *)(&le_next_free)); + + if ( (ret1 < 0) || (ret2 < 0) ) + return -1; + + return RC_OK; +} + \ No newline at end of file diff --git a/c/src/libfs/src/dosfs/fat.h b/c/src/libfs/src/dosfs/fat.h new file mode 100644 index 0000000000..04a1f0f662 --- /dev/null +++ b/c/src/libfs/src/dosfs/fat.h @@ -0,0 +1,489 @@ +/* + * fat.h + * + * Constants/data structures/prototypes for low-level operations on a volume + * with FAT filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#ifndef __DOSFS_FAT_H__ +#define __DOSFS_FAT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* XXX: temporary hack :(( */ +#ifndef set_errno_and_return_minus_one +#define set_errno_and_return_minus_one rtems_set_errno_and_return_minus_one +#endif /* set_errno_and_return_minus_one */ + +#include +#include +#include + +#define DBG1(x) x +#define DBG2(x) x + +#ifndef RC_OK +#define RC_OK 0x00000000 +#endif + +/* + * Remember that all FAT file system on disk data structure is + * "little endian"! + * (derived from linux) + */ +/* + * Conversion from and to little-endian byte order. (no-op on i386/i486) + * + * Naming: Ca_b_c, where a: F = from, T = to, b: LE = little-endian, + * BE = big-endian, c: W = word (16 bits), L = longword (32 bits) + */ + +#if (CPU_BIG_ENDIAN == TRUE) +# define CF_LE_W(v) CPU_swap_u16(v) +# define CF_LE_L(v) CPU_swap_u32(v) +# define CT_LE_W(v) CPU_swap_u16(v) +# define CT_LE_L(v) CPU_swap_u32(v) +#else +# define CF_LE_W(v) (v) +# define CF_LE_L(v) (v) +# define CT_LE_W(v) (v) +# define CT_LE_L(v) (v) +#endif + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#define FAT_HASH_SIZE 2 +#define FAT_HASH_MODULE FAT_HASH_SIZE + + +#define FAT_SECTOR512_SIZE 512 /* sector size (bytes) */ +#define FAT_SECTOR512_BITS 9 /* log2(SECTOR_SIZE) */ + +/* maximum + 1 number of clusters for FAT12 */ +#define FAT_FAT12_MAX_CLN 4085 + +/* maximum + 1 number of clusters for FAT16 */ +#define FAT_FAT16_MAX_CLN 65525 + +#define FAT_FAT12 0x01 +#define FAT_FAT16 0x02 +#define FAT_FAT32 0x04 + +#define FAT_UNDEFINED_VALUE 0xFFFFFFFF + +#define FAT_FAT12_EOC 0x0FFF +#define FAT_FAT16_EOC 0xFFFF +#define FAT_FAT32_EOC 0x0FFFFFFF + +#define FAT_FAT12_FREE 0x0000 +#define FAT_FAT16_FREE 0x0000 +#define FAT_FAT32_FREE 0x00000000 + +#define FAT_GENFAT_EOC 0xFFFFFFFF +#define FAT_GENFAT_FREE 0x00000000 + +#define FAT_FAT12_SHIFT 0x04 + +#define FAT_FAT12_MASK 0x00000FFF +#define FAT_FAT16_MASK 0x0000FFFF +#define FAT_FAT32_MASK 0x0FFFFFFF + +#define FAT_MAX_BPB_SIZE 90 + +/* size of useful information in FSInfo sector */ +#define FAT_USEFUL_INFO_SIZE 12 + +#define FAT_VAL8(x, ofs) (unsigned8)(*((unsigned8 *)(x) + (ofs))) + +#define FAT_VAL16(x, ofs) \ + (unsigned16)( (*((unsigned8 *)(x) + (ofs))) | \ + ((*((unsigned8 *)(x) + (ofs) + 1)) << 8) ) + +#define FAT_VAL32(x, ofs) \ + (unsigned32)( (*((unsigned8 *)(x) + (ofs))) | \ + ((*((unsigned8 *)(x) + (ofs) + 1)) << 8) | \ + ((*((unsigned8 *)(x) + (ofs) + 2)) << 16) | \ + ((*((unsigned8 *)(x) + (ofs) + 3)) << 24) ) + +/* macros to access boot sector fields */ +#define FAT_BR_BYTES_PER_SECTOR(x) FAT_VAL16(x, 11) +#define FAT_BR_SECTORS_PER_CLUSTER(x) FAT_VAL8(x, 13) +#define FAT_BR_RESERVED_SECTORS_NUM(x) FAT_VAL16(x, 14) +#define FAT_BR_FAT_NUM(x) FAT_VAL8(x, 16) +#define FAT_BR_FILES_PER_ROOT_DIR(x) FAT_VAL16(x, 17) +#define FAT_BR_TOTAL_SECTORS_NUM16(x) FAT_VAL16(x, 19) +#define FAT_BR_MEDIA(x) FAT_VAL8(x, 21) +#define FAT_BR_SECTORS_PER_FAT(x) FAT_VAL16(x, 22) +#define FAT_BR_TOTAL_SECTORS_NUM32(x) FAT_VAL32(x, 32) +#define FAT_BR_SECTORS_PER_FAT32(x) FAT_VAL32(x, 36) +#define FAT_BR_EXT_FLAGS(x) FAT_VAL16(x, 40) +#define FAT_BR_FAT32_ROOT_CLUSTER(x) FAT_VAL32(x, 44) +#define FAT_BR_FAT32_FS_INFO_SECTOR(x) FAT_VAL16(x, 48) +#define FAT_FSINFO_LEAD_SIGNATURE(x) FAT_VAL32(x, 0) +/* + * I read FSInfo sector from offset 484 to access the information, so offsets + * of these fields a relative + */ +#define FAT_FSINFO_FREE_CLUSTER_COUNT(x) FAT_VAL32(x, 4) +#define FAT_FSINFO_NEXT_FREE_CLUSTER(x) FAT_VAL32(x, 8) + +#define FAT_FSINFO_FREE_CLUSTER_COUNT_OFFSET 488 + +#define FAT_FSINFO_NEXT_FREE_CLUSTER_OFFSET 492 + +#define FAT_RSRVD_CLN 0x02 + +#define FAT_FSINFO_LEAD_SIGNATURE_VALUE 0x41615252 + +#define FAT_FSI_LEADSIG_SIZE 0x04 + +#define FAT_FSI_INFO 484 + +#define MS_BYTES_PER_CLUSTER_LIMIT 0x8000 /* 32K */ + +#define FAT_BR_EXT_FLAGS_MIRROR 0x0080 + +#define FAT_BR_EXT_FLAGS_FAT_NUM 0x000F + + +#define FAT_DIRENTRY_SIZE 32 + +#define FAT_DIRENTRIES_PER_SEC512 16 + +/* + * Volume descriptor + * Description of the volume the FAT filesystem is located on - generally + * the fields of the structure corresponde to Boot Sector and BPB Srtucture + * (see M$ White Paper) fields + */ +typedef struct fat_vol_s +{ + unsigned16 bps; /* bytes per sector */ + unsigned8 sec_log2; /* log2 of bps */ + unsigned8 sec_mul; /* log2 of 512bts sectors number per sector */ + unsigned8 spc; /* sectors per cluster */ + unsigned8 spc_log2; /* log2 of spc */ + unsigned16 bpc; /* bytes per cluster */ + unsigned8 bpc_log2; /* log2 of bytes per cluster */ + unsigned8 fats; /* number of FATs */ + unsigned8 type; /* FAT type */ + unsigned32 mask; + unsigned32 eoc_val; + unsigned16 fat_loc; /* FAT start */ + unsigned32 fat_length; /* sectors per FAT */ + unsigned32 rdir_loc; /* root directory start */ + unsigned16 rdir_entrs; /* files per root directory */ + unsigned32 rdir_secs; /* sectors per root directory */ + unsigned32 rdir_size; /* root directory size in bytes */ + unsigned32 tot_secs; /* total count of sectors */ + unsigned32 data_fsec; /* first data sector */ + unsigned32 data_cls; /* count of data clusters */ + unsigned32 rdir_cl; /* first cluster of the root directory */ + unsigned16 info_sec; /* FSInfo Sector Structure location */ + unsigned32 free_cls; /* last known free clusters count */ + unsigned32 next_cl; /* next free cluster number */ + unsigned8 mirror; /* mirroring enabla/disable */ + unsigned32 afat_loc; /* active FAT location */ + unsigned8 afat; /* the number of active FAT */ + dev_t dev; /* device ID */ + disk_device *dd; /* disk device (see libblock) */ + void *private_data; /* reserved */ +} fat_vol_t; + + +typedef struct fat_cache_s +{ + unsigned32 blk_num; + rtems_boolean modified; + unsigned8 state; + bdbuf_buffer *buf; +} fat_cache_t; + +/* + * This structure identifies the instance of the filesystem on the FAT + * ("fat-file") level. + */ +typedef struct fat_fs_info_s +{ + fat_vol_t vol; /* volume descriptor */ + Chain_Control *vhash; /* "vhash" of fat-file descriptors */ + Chain_Control *rhash; /* "rhash" of fat-file descriptors */ + char *uino; /* array of unique ino numbers */ + unsigned32 index; + unsigned32 uino_pool_size; /* size */ + unsigned32 uino_base; + fat_cache_t c; /* cache */ + unsigned8 *sec_buf; /* just placeholder for anything */ +} fat_fs_info_t; + +/* + * if the name we looking for is file we store not only first data cluster + * number, but and cluster number and offset for directory entry for this + * name + */ +typedef struct fat_auxiliary_s +{ + unsigned32 cln; + unsigned32 ofs; +} fat_auxiliary_t; + +#define FAT_FAT_OFFSET(fat_type, cln) \ + ((fat_type) & FAT_FAT12 ? ((cln) + ((cln) >> 1)) : \ + (fat_type) & FAT_FAT16 ? ((cln) << 1) : \ + ((cln) << 2)) + +#define FAT_CLUSTER_IS_ODD(n) ((n) & 0x0001) + +#define FAT12_SHIFT 0x4 /* half of a byte */ + +/* initial size of array of unique ino */ +#define FAT_UINO_POOL_INIT_SIZE 0x100 + +/* cache support */ +#define FAT_CACHE_EMPTY 0x0 +#define FAT_CACHE_ACTUAL 0x1 + +#define FAT_OP_TYPE_READ 0x1 +#define FAT_OP_TYPE_GET 0x2 + +static inline unsigned32 +fat_cluster_num_to_sector_num( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln + ) +{ + register fat_fs_info_t *fs_info = mt_entry->fs_info; + + if ( (cln == 0) && (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)) ) + return fs_info->vol.rdir_loc; + + return (((cln - FAT_RSRVD_CLN) << fs_info->vol.spc_log2) + + fs_info->vol.data_fsec); +} + +static inline unsigned32 +fat_cluster_num_to_sector512_num( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + + if (cln == 1) + return 1; + + return (fat_cluster_num_to_sector_num(mt_entry, cln) << + fs_info->vol.sec_mul); +} + +static inline int +fat_buf_access(fat_fs_info_t *fs_info, unsigned32 blk, int op_type, + bdbuf_buffer **buf) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + unsigned8 i; + rtems_boolean sec_of_fat; + + + if (fs_info->c.state == FAT_CACHE_EMPTY) + { + if (op_type == FAT_OP_TYPE_READ) + sc = rtems_bdbuf_read(fs_info->vol.dev, blk, &fs_info->c.buf); + else + sc = rtems_bdbuf_get(fs_info->vol.dev, blk, &fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + fs_info->c.blk_num = blk; + fs_info->c.state = FAT_CACHE_ACTUAL; + } + + sec_of_fat = ((fs_info->c.blk_num >= fs_info->vol.fat_loc) && + (fs_info->c.blk_num < fs_info->vol.rdir_loc)); + + if (fs_info->c.blk_num != blk) + { + if (fs_info->c.modified) + { + if (sec_of_fat && !fs_info->vol.mirror) + memcpy(fs_info->sec_buf, fs_info->c.buf->buffer, + fs_info->vol.bps); + + sc = rtems_bdbuf_release_modified(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + fs_info->c.modified = 0; + + if (sec_of_fat && !fs_info->vol.mirror) + { + bdbuf_buffer *b; + + for (i = 1; i < fs_info->vol.fats; i++) + { + sc = rtems_bdbuf_get(fs_info->vol.dev, + fs_info->c.blk_num + + fs_info->vol.fat_length * i, + &b); + if ( sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(ENOMEM); + memcpy(b->buffer, fs_info->sec_buf, fs_info->vol.bps); + sc = rtems_bdbuf_release_modified(b); + if ( sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(ENOMEM); + } + } + } + else + { + sc = rtems_bdbuf_release(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + } + if (op_type == FAT_OP_TYPE_READ) + sc = rtems_bdbuf_read(fs_info->vol.dev, blk, &fs_info->c.buf); + else + sc = rtems_bdbuf_get(fs_info->vol.dev, blk, &fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + fs_info->c.blk_num = blk; + } + *buf = fs_info->c.buf; + return RC_OK; +} + + +static inline int +fat_buf_release(fat_fs_info_t *fs_info) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + unsigned8 i; + rtems_boolean sec_of_fat; + + if (fs_info->c.state == FAT_CACHE_EMPTY) + return RC_OK; + + sec_of_fat = ((fs_info->c.blk_num >= fs_info->vol.fat_loc) && + (fs_info->c.blk_num < fs_info->vol.rdir_loc)); + + if (fs_info->c.modified) + { + if (sec_of_fat && !fs_info->vol.mirror) + memcpy(fs_info->sec_buf, fs_info->c.buf->buffer, fs_info->vol.bps); + + sc = rtems_bdbuf_release_modified(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + fs_info->c.modified = 0; + + if (sec_of_fat && !fs_info->vol.mirror) + { + bdbuf_buffer *b; + + for (i = 1; i < fs_info->vol.fats; i++) + { + sc = rtems_bdbuf_get(fs_info->vol.dev, + fs_info->c.blk_num + + fs_info->vol.fat_length * i, + &b); + if ( sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(ENOMEM); + memcpy(b->buffer, fs_info->sec_buf, fs_info->vol.bps); + sc = rtems_bdbuf_release_modified(b); + if ( sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(ENOMEM); + } + } + } + else + { + sc = rtems_bdbuf_release(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + } + fs_info->c.state = FAT_CACHE_EMPTY; + return RC_OK; +} + +static inline void +fat_buf_mark_modified(fat_fs_info_t *fs_info) +{ + fs_info->c.modified = TRUE; +} + + + +ssize_t +_fat_block_read(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start, + unsigned32 offset, + unsigned32 count, + void *buff); + +ssize_t +_fat_block_write(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start, + unsigned32 offset, + unsigned32 count, + const void *buff); + +ssize_t +fat_cluster_read(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + void *buff); + +ssize_t +fat_cluster_write(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + const void *buff); + +int +fat_init_volume_info(rtems_filesystem_mount_table_entry_t *mt_entry); + +int +fat_init_clusters_chain(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start_cln); + +unsigned32 +fat_cluster_num_to_sector_num(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln); + +int +fat_shutdown_drive(rtems_filesystem_mount_table_entry_t *mt_entry); + + +unsigned32 +fat_get_unique_ino(rtems_filesystem_mount_table_entry_t *mt_entry); + +rtems_boolean +fat_ino_is_unique(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 ino); + +void +fat_free_unique_ino(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 ino); + +int +fat_fat32_update_fsinfo_sector( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 free_count, + unsigned32 next_free + ); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_FAT_H__ */ diff --git a/c/src/libfs/src/dosfs/fat_fat_operations.c b/c/src/libfs/src/dosfs/fat_fat_operations.c new file mode 100644 index 0000000000..49b2ab70d3 --- /dev/null +++ b/c/src/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 + * + * @(#) $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) + { + /* + * 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; +} diff --git a/c/src/libfs/src/dosfs/fat_fat_operations.h b/c/src/libfs/src/dosfs/fat_fat_operations.h new file mode 100644 index 0000000000..59b6a84018 --- /dev/null +++ b/c/src/libfs/src/dosfs/fat_fat_operations.h @@ -0,0 +1,58 @@ +/* + * fat_fat_operations.h + * + * Constants/data structures/prototypes for operations on Files Allocation + * Table + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_FAT_FAT_OPERATIONS_H__ +#define __DOSFS_FAT_FAT_OPERATIONS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include "fat.h" + +int +fat_get_fat_cluster(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + unsigned32 *ret_val); + +int +fat_set_fat_cluster(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + unsigned32 in_val); + +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 +fat_free_fat_clusters_chain( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 chain +); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_FAT_FAT_OPERATIONS_H__ */ diff --git a/c/src/libfs/src/dosfs/fat_file.c b/c/src/libfs/src/dosfs/fat_file.c new file mode 100644 index 0000000000..4fd8a5024f --- /dev/null +++ b/c/src/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 + * + * @(#) $Id$ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; +} diff --git a/c/src/libfs/src/dosfs/fat_file.h b/c/src/libfs/src/dosfs/fat_file.h new file mode 100644 index 0000000000..2821a27cf7 --- /dev/null +++ b/c/src/libfs/src/dosfs/fat_file.h @@ -0,0 +1,195 @@ +/* + * fat_file.h + * + * Constants/data structures/prototypes for operations on "fat-file" + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_FAT_FILE_H__ +#define __DOSFS_FAT_FILE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +/* "fat-file" representation + * + * the idea is: fat-file is nothing but a cluster chain, any open fat-file is + * represented in system by fat-file descriptor and has well-known + * file interface: + * + * fat_file_open() + * fat_file_close() + * fat_file_read() + * fat_file_write() + * + * Such interface hides the architecture of fat-file and represents it like + * linear file + */ + +typedef rtems_filesystem_node_types_t fat_file_type_t; + +#define FAT_DIRECTORY RTEMS_FILESYSTEM_DIRECTORY +#define FAT_FILE RTEMS_FILESYSTEM_MEMORY_FILE + +typedef struct fat_file_map_s +{ + unsigned32 file_cln; + unsigned32 disk_cln; + unsigned32 last_cln; +} fat_file_map_t; +/* + * descriptor of a fat-file + * + * To each particular clusters chain + */ +typedef struct fat_file_fd_s +{ + Chain_Node link; /* + * fat-file descriptors organized into hash; + * collision lists are handled via link + * field + */ + unsigned32 links_num; /* + * the number of fat_file_open call on + * this fat-file + */ + unsigned32 ino; /* inode, file serial number :)))) */ + fat_file_type_t fat_file_type; + unsigned32 size_limit; + unsigned32 fat_file_size; /* length */ + unsigned32 info_cln; + unsigned32 cln; + unsigned16 info_ofs; + unsigned char first_char; + unsigned8 flags; + fat_file_map_t map; + time_t mtime; + +} fat_file_fd_t; + + +#define FAT_FILE_REMOVED 0x01 + +#define FAT_FILE_IS_REMOVED(p)\ + (((p)->flags & FAT_FILE_REMOVED) ? 1 : 0) + +/* ioctl macros */ +#define F_CLU_NUM 0x01 + +/* + * Each file and directory on a MSDOS volume is unique identified by it + * location, i.e. location of it 32 Bytes Directory Entry Structure. We can + * distinguish them by cluster number it locates on and offset inside this + * cluster. But root directory on any volumes (FAT12/16/32) has no 32 Bytes + * Directory Entry Structure corresponded to it. So we assume 32 Bytes + * Directory Entry Structure of root directory locates at cluster 1 (invalid + * cluaster number) and offset 0 + */ +#define FAT_ROOTDIR_CLUSTER_NUM 0x01 + +#define FAT_FD_OF_ROOT_DIR(fat_fd) \ + ((fat_fd->info_cln == FAT_ROOTDIR_CLUSTER_NUM ) && \ + (fat_fd->info_ofs == 0)) + +#define FAT_EOF 0x00 + +/* fat_construct_key -- + * Construct key for hash access: convert (cluster num, offset) to + * (sector512 num, new offset) and than construct key as + * key = (sector512 num) << 4 | (new offset) + * + * PARAMETERS: + * cl - cluster number + * ofs - offset inside cluster 'cl' + * mt_entry - mount table entry + * + * RETURNS: + * constructed key + */ +static inline unsigned32 +fat_construct_key( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cl, + unsigned32 ofs) +{ + return ( ((fat_cluster_num_to_sector512_num(mt_entry, cl) + + (ofs >> FAT_SECTOR512_BITS)) << 4) + + ((ofs >> 5) & (FAT_DIRENTRIES_PER_SEC512 - 1)) ); +} + +/* Prototypes for "fat-file" operations */ +int +fat_file_open(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + unsigned32 ofs, + fat_file_fd_t **fat_fd); + +int +fat_file_reopen(fat_file_fd_t *fat_fd); + +int +fat_file_close(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +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); + +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 +fat_file_extend(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 new_length, + unsigned32 *a_length); + +int +fat_file_truncate(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 new_length); + +int +fat_file_datasync(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + + +int +fat_file_ioctl(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + int cmd, + ...); + +int +fat_file_size(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +void +fat_file_mark_removed(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_FAT_FILE_H__ */ diff --git a/c/src/libfs/src/dosfs/msdos.h b/c/src/libfs/src/dosfs/msdos.h new file mode 100644 index 0000000000..a9216b1ed3 --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos.h @@ -0,0 +1,408 @@ +/* + * msdos.h + * + * The MSDOS filesystem constants/data structures/prototypes + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_MSDOS_H__ +#define __DOSFS_MSDOS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "fat.h" +#include "fat_file.h" + +#ifndef RC_OK +#define RC_OK 0x00000000 +#endif + +#define MSDOS_NAME_NOT_FOUND_ERR 0xDD000001 + +/* + * This structure identifies the instance of the filesystem on the MSDOS + * level. + */ +typedef struct msdos_fs_info_s +{ + fat_fs_info_t fat; /* + * volume + * description + */ + rtems_filesystem_file_handlers_r *directory_handlers; /* + * a set of routines + * that handles the + * nodes of directory + * type + */ + rtems_filesystem_file_handlers_r *file_handlers; /* + * a set of routines + * that handles the + * nodes of file + * type + */ + rtems_id vol_sema; /* + * semaphore + * associated with + * the volume + */ + unsigned8 *cl_buf; /* + * just placeholder + * for anything + */ +} msdos_fs_info_t; + +/* a set of routines that handle the nodes which are directories */ +extern rtems_filesystem_file_handlers_r msdos_dir_handlers; + +/* a set of routines that handle the nodes which are files */ +extern rtems_filesystem_file_handlers_r msdos_file_handlers; + +/* Volume semaphore timeout value */ +#define MSDOS_VOLUME_SEMAPHORE_TIMEOUT 100 + +/* Node types */ +#define MSDOS_DIRECTORY RTEMS_FILESYSTEM_DIRECTORY +#define MSDOS_REGULAR_FILE RTEMS_FILESYSTEM_MEMORY_FILE + +typedef rtems_filesystem_node_types_t msdos_node_type_t; + +/* + * Macros for fetching fields from 32 bytes long FAT Directory Entry + * Structure (see M$ White Paper) + */ +#define MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE 32 /* 32 bytes */ + +#define MSDOS_DIR_NAME(x) (unsigned8 *)((x) + 0) +#define MSDOS_DIR_ATTR(x) (unsigned8 *)((x) + 11) +#define MSDOS_DIR_NT_RES(x) (unsigned8 *)((x) + 12) +#define MSDOS_DIR_CRT_TIME_TENTH(x) (unsigned8 *)((x) + 13) +#define MSDOS_DIR_CRT_TIME(x) (unsigned16 *)((x) + 14) +#define MSDOS_DIR_CRT_DATE(x) (unsigned16 *)((x) + 16) +#define MSDOS_DIR_LAST_ACCESS_DATE(x) (unsigned16 *)((x) + 18) +#define MSDOS_DIR_FIRST_CLUSTER_HI(x) (unsigned16 *)((x) + 20) +#define MSDOS_DIR_WRITE_TIME(x) (unsigned16 *)((x) + 22) +#define MSDOS_DIR_WRITE_DATE(x) (unsigned16 *)((x) + 24) +#define MSDOS_DIR_FIRST_CLUSTER_LOW(x) (unsigned16 *)((x) + 26) +#define MSDOS_DIR_FILE_SIZE(x) (unsigned32 *)((x) + 28) + +#define MSDOS_EXTRACT_CLUSTER_NUM(p) \ + (unsigned32)( (CF_LE_W(*MSDOS_DIR_FIRST_CLUSTER_LOW(p))) | \ + ((CF_LE_W((*MSDOS_DIR_FIRST_CLUSTER_HI(p))))<<16) ) + +/* + * Fields offset in 32 bytes long FAT Directory Entry + * Structure (see M$ White Paper) + */ +#define MSDOS_FILE_SIZE_OFFSET 28 +#define MSDOS_FILE_NAME_OFFSET 0 +#define MSDOS_FIRST_CLUSTER_HI_OFFSET 20 +#define MSDOS_FIRST_CLUSTER_LOW_OFFSET 26 +#define MSDOS_FILE_WDATE_OFFSET 24 +#define MSDOS_FILE_WTIME_OFFSET 22 + +/* + * Possible values of DIR_Attr field of 32 bytes long FAT Directory Entry + * Structure (see M$ White Paper) + */ +#define MSDOS_ATTR_READ_ONLY 0x01 +#define MSDOS_ATTR_HIDDEN 0x02 +#define MSDOS_ATTR_SYSTEM 0x04 +#define MSDOS_ATTR_VOLUME_ID 0x08 +#define MSDOS_ATTR_DIRECTORY 0x10 +#define MSDOS_ATTR_ARCHIVE 0x20 + +/* + * Possible values of DIR_Name[0] field of 32 bytes long FAT Directory Entry + * Structure (see M$ White Paper) + */ +#define MSDOS_THIS_DIR_ENTRY_EMPTY 0xE5 +#define MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY 0x00 + + +/* + * Macros for names parsing and formatting + */ +#define msdos_is_valid_name_char(_ch) (1) +#define msdos_is_separator(_ch) rtems_filesystem_is_separator(_ch) + +#define MSDOS_SHORT_NAME_LEN 11 /* 11 characters */ +#define MSDOS_NAME_MAX MSDOS_SHORT_NAME_LEN +#define MSDOS_NAME_MAX_WITH_DOT (MSDOS_NAME_MAX + 1) + +#define MSDOS_DOT_NAME ". " /* ".", padded to MSDOS_NAME chars */ +#define MSDOS_DOTDOT_NAME ".. " /* "..", padded to MSDOS_NAME chars */ + +typedef enum msdos_token_types_e +{ + MSDOS_NO_MORE_PATH, + MSDOS_CURRENT_DIR, + MSDOS_UP_DIR, + MSDOS_NAME, + MSDOS_INVALID_TOKEN +} msdos_token_types_t; + +/* Others macros */ +#define MSDOS_RES_NT_VALUE 0x00 +#define MSDOS_INIT_DIR_SIZE 0x00 + +/* "dot" entry offset in a directory */ +#define MSDOS_DOT_DIR_ENTRY_OFFSET 0x00 /* first entry in directory */ + +/* "dotdot" entry offset in a directory */ +#define MSDOS_DOTDOT_DIR_ENTRY_OFFSET 0x20 /* second entry in directory */ + +/* 'p' should be char* */ +#define DOT_NODE_P(p) ((char *)(p)) +#define DOTDOT_NODE_P(p) ((char *)((p) + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)) + +/* Size limits for files and directories (see M$ White Paper) */ +#define MSDOS_MAX_DIR_LENGHT 0x200000 /* 2,097,152 bytes */ +#define MSDOS_MAX_FILE_SIZE 0xFFFFFFFF /* 4 Gb */ + +/* + * The number of 32 bytes long FAT Directory Entry + * Structures per 512 bytes sector + */ +#define MSDOS_DPS512_NUM 16 + +/* Prototypes */ +int +msdos_initialize(rtems_filesystem_mount_table_entry_t *temp_mt_entry); + +int +msdos_shut_down(rtems_filesystem_mount_table_entry_t *temp_mt_entry); + +int +msdos_eval_path(const char *pathname, /* IN */ + int flags, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */); + +int +msdos_eval4make(const char *path, /* IN */ + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + const char **name /* OUT */); + +int +msdos_unlink(rtems_filesystem_location_info_t *pathloc /* IN */); + +int +msdos_free_node_info(rtems_filesystem_location_info_t *pathloc /* IN */); + +rtems_filesystem_node_types_t +msdos_node_type(rtems_filesystem_location_info_t *pathloc); + +int +msdos_mknod(const char *path, /* IN */ + mode_t mode, /* IN */ + dev_t dev, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */); + +int +msdos_utime(rtems_filesystem_location_info_t *pathloc, /* IN */ + time_t actime, /* IN */ + time_t modtime /* IN */); + +int +msdos_initialize_support( + rtems_filesystem_mount_table_entry_t *temp_mt_entry, + rtems_filesystem_operations_table *op_table, + rtems_filesystem_file_handlers_r *file_handlers, + rtems_filesystem_file_handlers_r *directory_handlers +); + +int +msdos_file_open( + rtems_libio_t *iop, /* IN */ + const char *pathname, /* IN */ + unsigned32 flag, /* IN */ + unsigned32 mode /* IN */ +); + +int +msdos_file_close(rtems_libio_t *iop /* IN */); + +ssize_t +msdos_file_read( + rtems_libio_t *iop, /* IN */ + void *buffer, /* IN */ + unsigned32 count /* IN */ +); + +ssize_t +msdos_file_write( + rtems_libio_t *iop, /* IN */ + const void *buffer, /* IN */ + unsigned32 count /* IN */ +); + +int +msdos_file_lseek( + rtems_libio_t *iop, /* IN */ + off_t offset, /* IN */ + int whence /* IN */ +); + +int +msdos_file_stat(rtems_filesystem_location_info_t *loc, /* IN */ + struct stat *buf /* OUT */); + +int +msdos_file_ftruncate( + rtems_libio_t *iop, /* IN */ + off_t length /* IN */ +); + +int +msdos_file_sync(rtems_libio_t *iop); + +int +msdos_file_datasync(rtems_libio_t *iop); + +int +msdos_file_ioctl( + rtems_libio_t *iop, /* IN */ + unsigned32 command, /* IN */ + void *buffer /* IN */ +); + +int +msdos_file_rmnod(rtems_filesystem_location_info_t *pathloc /* IN */); + +int +msdos_dir_open( + rtems_libio_t *iop, /* IN */ + const char *pathname, /* IN */ + unsigned32 flag, /* IN */ + unsigned32 mode /* IN */ +); + +int +msdos_dir_close(rtems_libio_t *iop /* IN */); + +ssize_t +msdos_dir_read( + rtems_libio_t *iop, /* IN */ + void *buffer, /* IN */ + unsigned32 count /* IN */ +); + +int +msdos_dir_lseek( + rtems_libio_t *iop, /* IN */ + off_t offset, /* IN */ + int whence /* IN */ +); + +int +msdos_dir_rmnod(rtems_filesystem_location_info_t *pathloc /* IN */); + +int +msdos_dir_sync(rtems_libio_t *iop); + +int +msdos_dir_stat( + rtems_filesystem_location_info_t *loc, /* IN */ + struct stat *buf /* OUT */ +); + +int +msdos_creat_node(rtems_filesystem_location_info_t *parent_loc, + msdos_node_type_t type, + char *name, + mode_t mode); + +/* Misc prototypes */ +msdos_token_types_t msdos_get_token(const char *path, + char *token, + int *token_len); + +int +msdos_find_name(rtems_filesystem_location_info_t *parent_loc, + char *name); + +int +msdos_get_name_node(rtems_filesystem_location_info_t *parent_loc, + char *name, + fat_auxiliary_t *paux, + char *name_dir_entry); + +int +msdos_dir_info_remove(rtems_filesystem_location_info_t *pathloc); + +void +msdos_date_unix2dos(int unix_date, + unsigned short *time_val, + unsigned short *date); + +unsigned int +msdos_date_dos2unix(unsigned short time_val, unsigned short date); + +int +msdos_set_first_cluster_num(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +int +msdos_set_file_size(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +int +msdos_set_first_char4file_name(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cl, + unsigned32 ofs, + unsigned char first_char); + +int +msdos_set_dir_wrt_time_and_date( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd +); + + +int +msdos_dir_is_empty(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + rtems_boolean *ret_val); + +int +msdos_find_name_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + char *name, + fat_auxiliary_t *paux, + char *name_dir_entry); + +int +msdos_find_node_by_cluster_num_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 cl4find, + fat_auxiliary_t *paux, + char *dir_entry +); + +int +msdos_get_dotdot_dir_info_cluster_num_and_offset( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + fat_auxiliary_t *paux, + char *dir_entry +); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_MSDOS_H__ */ diff --git a/c/src/libfs/src/dosfs/msdos_create.c b/c/src/libfs/src/dosfs/msdos_create.c new file mode 100644 index 0000000000..4b4c7001ca --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_create.c @@ -0,0 +1,208 @@ +/* + * Routine to create a new MSDOS filesystem node + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + * + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_creat_node -- + * Create a new node. If a new node is file, FAT 32 Bytes Directory + * Entry Structure (see M$ White Paper) is initialized, free space is + * found in parent directory and structure is written to the disk. + * In case of directory, all above steps present and also new cluster + * is allocated for a new directory and dot and dotdot nodes are created + * in alloceted cluster. + * + * PARAMETERS: + * parent_loc - parent (directory we are going to create node in) + * type - new node type (file or directory) + * name - new node name + * mode - mode + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately). + * + */ +int +msdos_creat_node( + rtems_filesystem_location_info_t *parent_loc, + msdos_node_type_t type, + char *name, + mode_t mode + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = parent_loc->mt_entry->fs_info; + fat_file_fd_t *parent_fat_fd = parent_loc->node_access; + fat_file_fd_t *fat_fd = NULL; + time_t time_ret = 0; + unsigned16 time_val = 0; + unsigned16 date = 0; + fat_auxiliary_t aux; + unsigned char new_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + unsigned char dot_dotdot[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2]; + + memset(new_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memset(dot_dotdot, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2); + + /* set up name */ + strncpy(MSDOS_DIR_NAME(new_node), name, MSDOS_NAME_MAX); + + /* fill reserved field */ + *MSDOS_DIR_NT_RES(new_node) = MSDOS_RES_NT_VALUE; + + /* set up last write date and time */ + time_ret = time(NULL); + if ( time_ret == -1 ) + return -1; + + msdos_date_unix2dos(time_ret, &time_val, &date); + *MSDOS_DIR_WRITE_TIME(new_node) = CT_LE_W(time_val); + *MSDOS_DIR_WRITE_DATE(new_node) = CT_LE_W(date); + + /* initialize directory/file size */ + *MSDOS_DIR_FILE_SIZE(new_node) = MSDOS_INIT_DIR_SIZE; + + if (type == MSDOS_DIRECTORY) + *MSDOS_DIR_ATTR(new_node) |= MSDOS_ATTR_DIRECTORY; + else + *MSDOS_DIR_ATTR(new_node) |= MSDOS_ATTR_ARCHIVE; + + /* + * find free space in the parent directory and write new initialized + * FAT 32 Bytes Directory Entry Structure (see M$ White Paper) + * to the disk + */ + rc = msdos_get_name_node(parent_loc, NULL, &aux, new_node); + if ( rc != RC_OK ) + return rc; + + /* + * if we create a new file we are done, if directory there are more steps + * to do + */ + if (type == MSDOS_DIRECTORY) + { + /* open new directory as fat-file */ + rc = fat_file_open(parent_loc->mt_entry, aux.cln, aux.ofs, &fat_fd); + if (rc != RC_OK) + goto err; + + /* + * we opened fat-file for node we just created, so initialize fat-file + * descritor + */ + fat_fd->info_cln = aux.cln; + fat_fd->info_ofs = aux.ofs; + fat_fd->fat_file_size = 0; + fat_fd->fat_file_type = FAT_DIRECTORY; + fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT; + + /* + * dot and dotdot entries are identical to new node except the + * names + */ + memcpy(DOT_NODE_P(dot_dotdot), new_node, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memcpy(DOTDOT_NODE_P(dot_dotdot), new_node, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memcpy(MSDOS_DIR_NAME(DOT_NODE_P(dot_dotdot)), MSDOS_DOT_NAME, + MSDOS_NAME_MAX); + memcpy(MSDOS_DIR_NAME(DOTDOT_NODE_P(dot_dotdot)), MSDOS_DOTDOT_NAME, + MSDOS_NAME_MAX); + + /* set up cluster num for dotdot entry */ + /* + * here we can ommit FAT32 condition because for all FAT types dirs + * right under root dir should contain 0 in dotdot entry but for + * FAT12/16 parent_fat_fd->cluster_num always contains such value + */ + if ((FAT_FD_OF_ROOT_DIR(parent_fat_fd)) && + (fs_info->fat.vol.type & FAT_FAT32)) + { + *MSDOS_DIR_FIRST_CLUSTER_LOW(DOTDOT_NODE_P(dot_dotdot)) = 0x0000; + *MSDOS_DIR_FIRST_CLUSTER_HI(DOTDOT_NODE_P(dot_dotdot)) = 0x0000; + } + else + { + *MSDOS_DIR_FIRST_CLUSTER_LOW(DOTDOT_NODE_P(dot_dotdot)) = + CT_LE_W((unsigned16)((parent_fat_fd->cln) & 0x0000FFFF)); + *MSDOS_DIR_FIRST_CLUSTER_HI(DOTDOT_NODE_P(dot_dotdot)) = + CT_LE_W((unsigned16)(((parent_fat_fd->cln) & 0xFFFF0000)>>16)); + } + + /* + * write dot and dotdot entries to new fat-file: currently fat-file + * correspondes to a new node is zero length, so it will be extended + * by one cluster and entries will be written + */ + ret = fat_file_write(parent_loc->mt_entry, fat_fd, 0, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2, + dot_dotdot); + if (ret < 0) + { + rc = -1; + goto error; + } + + /* increment fat-file size by cluster size */ + fat_fd->fat_file_size += fs_info->fat.vol.bpc; + + /* set up cluster num for dot entry */ + *MSDOS_DIR_FIRST_CLUSTER_LOW(DOT_NODE_P(dot_dotdot)) = + CT_LE_W((unsigned16)((fat_fd->cln) & 0x0000FFFF)); + *MSDOS_DIR_FIRST_CLUSTER_HI(DOT_NODE_P(dot_dotdot)) = + CT_LE_W((unsigned16)(((fat_fd->cln) & 0xFFFF0000) >> 16)); + + /* rewrite dot entry */ + ret = fat_file_write(parent_loc->mt_entry, fat_fd, 0, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE, + DOT_NODE_P(dot_dotdot)); + if (ret < 0) + { + rc = -1; + goto error; + } + + /* write first cluster num of a new directory to disk */ + rc = msdos_set_first_cluster_num(parent_loc->mt_entry, fat_fd); + if (rc != RC_OK) + goto error; + + fat_file_close(parent_loc->mt_entry, fat_fd); + } + return RC_OK; + +error: + fat_file_close(parent_loc->mt_entry, fat_fd); + +err: + /* mark 32bytes structure on the disk as free */ + msdos_set_first_char4file_name(parent_loc->mt_entry, aux.cln, aux.ofs, + 0xE5); + return rc; +} diff --git a/c/src/libfs/src/dosfs/msdos_dir.c b/c/src/libfs/src/dosfs/msdos_dir.c new file mode 100644 index 0000000000..93449cd2fb --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_dir.c @@ -0,0 +1,483 @@ +/* + * MSDOS directory handlers implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_dir_open -- + * Open fat-file which correspondes to the directory being opened and + * set offset field of file control block to zero. + * + * PARAMETERS: + * iop - file control block + * pathname - name + * flag - flags + * mode - mode + * + * RETURNS: + * RC_OK, if directory opened successfully, or -1 if error occured (errno + * set apropriately) + */ +int +msdos_dir_open(rtems_libio_t *iop, const char *pathname, unsigned32 flag, + unsigned32 mode) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one( EIO ); + + rc = fat_file_reopen(fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + iop->offset = 0; + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_dir_close -- + * Close fat-file which correspondes to the directory being closed + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK, if directory closed successfully, or -1 if error occured (errno + * set apropriately. + */ +int +msdos_dir_close(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one( EIO ); + + rc = fat_file_close(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_dir_read -- + * This routine will read the next directory entry based on the directory + * offset. The offset should be equal to -n- time the size of an + * individual dirent structure. If n is not an integer multiple of the + * sizeof a dirent structure, an integer division will be performed to + * determine directory entry that will be returned in the buffer. Count + * should reflect -m- times the sizeof dirent bytes to be placed in the + * buffer. + * If there are not -m- dirent elements from the current directory + * position to the end of the exisiting file, the remaining entries will + * be placed in the buffer and the returned value will be equal to + * -m actual- times the size of a directory entry. + * + * PARAMETERS: + * iop - file control block + * buffer - buffer provided by user + * count - count of bytes to read + * + * RETURNS: + * the number of bytes read on success, or -1 if error occured (errno + * set apropriately). + */ +ssize_t +msdos_dir_read(rtems_libio_t *iop, void *buffer, unsigned32 count) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + fat_file_fd_t *tmp_fat_fd = NULL; + struct dirent tmp_dirent; + unsigned32 start = 0; + ssize_t ret = 0; + unsigned32 cmpltd = 0; + unsigned32 j = 0, i = 0; + unsigned32 bts2rd = 0; + unsigned32 cur_cln = 0; + + /* + * cast start and count - protect against using sizes that are not exact + * multiples of the -dirent- size. These could result in unexpected + * results + */ + start = iop->offset / sizeof(struct dirent); + count = (count / sizeof(struct dirent)) * sizeof(struct dirent); + + /* + * optimization: we know that root directory for FAT12/16 volumes is + * sequential set of sectors and any cluster is sequential set of sectors + * too, so read such set of sectors is quick operation for low-level IO + * layer. + */ + bts2rd = (FAT_FD_OF_ROOT_DIR(fat_fd) && + (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16))) ? + fat_fd->fat_file_size : + fs_info->fat.vol.bpc; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + while (count > 0) + { + /* + * fat-file is already opened by open call, so read it + * Always read directory fat-file from the beggining because of MSDOS + * directories feature :( - we should count elements currently + * present in the directory because there may be holes :) + */ + ret = fat_file_read(iop->pathinfo.mt_entry, fat_fd, (j * bts2rd), + bts2rd, fs_info->cl_buf); + if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + rtems_semaphore_release(fs_info->vol_sema); + set_errno_and_return_minus_one(EIO); + } + + for (i = 0; i < ret; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) + { + rtems_semaphore_release(fs_info->vol_sema); + return cmpltd; + } + + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_EMPTY) + continue; + + /* + * skip active entries until get the entry to start from + */ + if (start) + { + start--; + continue; + } + + /* + * Move the entry to the return buffer + * + * unfortunately there is no method to extract ino except to + * open fat-file descriptor :( ... so, open it + */ + + /* get number of cluster we are working with */ + rc = fat_file_ioctl(iop->pathinfo.mt_entry, fat_fd, F_CLU_NUM, + j * bts2rd, &cur_cln); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + rc = fat_file_open(iop->pathinfo.mt_entry, cur_cln, i, + &tmp_fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + tmp_fat_fd->info_cln = cur_cln; + tmp_fat_fd->info_ofs = i; + + /* fill in dirent structure */ + /* XXX: from what and in what d_off should be computed ?! */ + tmp_dirent.d_off = start + cmpltd; + tmp_dirent.d_reclen = sizeof(struct dirent); + tmp_dirent.d_ino = tmp_fat_fd->ino; + tmp_dirent.d_namlen = MSDOS_SHORT_NAME_LEN; + memcpy(tmp_dirent.d_name, MSDOS_DIR_NAME((fs_info->cl_buf + i)), + MSDOS_SHORT_NAME_LEN); + + /* d_name is null-terminated */ + tmp_dirent.d_name[MSDOS_SHORT_NAME_LEN] = 0; + memcpy(buffer + cmpltd, &tmp_dirent, sizeof(struct dirent)); + + iop->offset = iop->offset + sizeof(struct dirent); + cmpltd += (sizeof(struct dirent)); + count -= (sizeof(struct dirent)); + + /* inode number extracted, close fat-file */ + rc = fat_file_close(iop->pathinfo.mt_entry, tmp_fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + if (count <= 0) + break; + } + j++; + } + + rtems_semaphore_release(fs_info->vol_sema); + return cmpltd; +} + +/* msdos_dir_write -- + * no write for directory + */ + +/* msdos_dir_lseek -- + * + * This routine will behave in one of three ways based on the state of + * argument whence. Based on the state of its value the offset argument will + * be interpreted using one of the following methods: + * + * SEEK_SET - offset is the absolute byte offset from the start of the + * logical start of the dirent sequence that represents the + * directory + * SEEK_CUR - offset is used as the relative byte offset from the current + * directory position index held in the iop structure + * SEEK_END - N/A --> This will cause an assert. + * + * PARAMETERS: + * iop - file control block + * offset - offset + * whence - predefine directive + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno + * set apropriately). + */ +int +msdos_dir_lseek(rtems_libio_t *iop, off_t offset, int whence) +{ + switch (whence) + { + case SEEK_SET: + case SEEK_CUR: + break; + /* + * Movement past the end of the directory via lseek is not a + * permitted operation + */ + case SEEK_END: + default: + set_errno_and_return_minus_one( EINVAL ); + break; + } + return RC_OK; +} + +/* msdos_dir_stat -- + * + * This routine will obtain the following information concerning the current + * directory: + * st_dev device id + * st_ino node serial number :) + * st_mode mode extracted from the node + * st_size total size in bytes + * st_blksize blocksize for filesystem I/O + * st_blocks number of blocks allocated + * stat_mtime time of last modification + * + * PARAMETERS: + * loc - this directory + * buf - stat buffer provided by user + * + * RETURNS: + * RC_OK and filled stat buffer on success, or -1 if error occured (errno + * set apropriately). + */ +int +msdos_dir_stat( + rtems_filesystem_location_info_t *loc, + struct stat *buf + ) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = loc->node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + buf->st_dev = fs_info->fat.vol.dev; + buf->st_ino = fat_fd->ino; + buf->st_mode = S_IFDIR; + buf->st_rdev = 0ll; + buf->st_size = fat_fd->fat_file_size; + buf->st_blocks = fat_fd->fat_file_size >> FAT_SECTOR512_BITS; + buf->st_blksize = fs_info->fat.vol.bps; + buf->st_mtime = fat_fd->mtime; + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_dir_truncate -- + * No truncate for directory. + * + * PARAMETERS: + * + * RETURNS: + * + */ + +/* msdos_dir_sync -- + * The following routine does a syncronization on a MSDOS directory node. + * DIR_WrtTime, DIR_WrtDate and DIR_fileSize fields of 32 Bytes Directory + * Entry Structure(see M$ White Paper) should not be updated for + * directories, so only call to corresponding fat-file routine. + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + */ +int +msdos_dir_sync(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + fat_file_fd_t *fat_fd = iop->file_info; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_datasync(iop->pathinfo.mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} + +/* msdos_dir_rmnod -- + * Remove directory node. + * + * Check that this directory node is not opened as fat-file, is empty and + * not filesystem root node. If all this conditions met then delete. + * + * PARAMETERS: + * pathloc - node description + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + */ +int +msdos_dir_rmnod(rtems_filesystem_location_info_t *pathloc) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = pathloc->node_access; + rtems_boolean is_empty = FALSE; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* + * We deny attemp to delete open directory (if directory is current + * directory we assume it is open one) + */ + if (fat_fd->links_num > 1) + { + rtems_semaphore_release(fs_info->vol_sema); + set_errno_and_return_minus_one(EBUSY); + } + + /* + * You cannot remove a node that still has children + */ + rc = msdos_dir_is_empty(pathloc->mt_entry, fat_fd, &is_empty); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + if (!is_empty) + { + rtems_semaphore_release(fs_info->vol_sema); + set_errno_and_return_minus_one(ENOTEMPTY); + } + + /* + * You cannot remove the file system root node. + */ + if (pathloc->mt_entry->mt_fs_root.node_access == pathloc->node_access) + { + rtems_semaphore_release(fs_info->vol_sema); + set_errno_and_return_minus_one(EBUSY); + } + + /* + * You cannot remove a mountpoint. + * not used - mount() not implemenetd yet. + */ + + /* mark file removed */ + rc = msdos_set_first_char4file_name(pathloc->mt_entry, fat_fd->info_cln, + fat_fd->info_ofs, + MSDOS_THIS_DIR_ENTRY_EMPTY); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + fat_file_mark_removed(pathloc->mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/c/src/libfs/src/dosfs/msdos_eval.c b/c/src/libfs/src/dosfs/msdos_eval.c new file mode 100644 index 0000000000..3eba0e63e8 --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_eval.c @@ -0,0 +1,435 @@ +/* + * MSDOS evaluation routines + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_set_handlers -- + * Set handlers for the node with specified type(i.e. handlers for file + * or directory). + * + * PARAMETERS: + * loc - node description + * + * RETURNS: + * None + */ +static void +msdos_set_handlers(rtems_filesystem_location_info_t *loc) +{ + msdos_fs_info_t *fs_info = loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = loc->node_access; + + if (fat_fd->fat_file_type == FAT_DIRECTORY) + loc->handlers = fs_info->directory_handlers; + else + loc->handlers = fs_info->file_handlers; +} + +/* msdos_eval_path -- + * + * The following routine evaluate path for a node that wishes to be + * accessed. Structure 'pathloc' is returned with a pointer to the + * node to be accessed. + * + * PARAMETERS: + * pathname - path for evaluation + * flags - flags + * pathloc - node description (IN/OUT) + * + * RETURNS: + * RC_OK and filled pathloc on success, or -1 if error occured + * (errno set appropriately) + * + */ +int +msdos_eval_path( + const char *pathname, + int flags, + rtems_filesystem_location_info_t *pathloc + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + rtems_filesystem_location_info_t newloc; + int i = 0; + int len = 0; + msdos_token_types_t type = MSDOS_CURRENT_DIR; + char token[MSDOS_NAME_MAX + 1]; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + if (!pathloc->node_access) + { + errno = ENOENT; + rc = -1; + goto err; + } + + fat_fd = pathloc->node_access; + + rc = fat_file_reopen(fat_fd); + if (rc != RC_OK) + goto err; + + while ((type != MSDOS_NO_MORE_PATH) && (type != MSDOS_INVALID_TOKEN)) + { + type = msdos_get_token(&pathname[i], token, &len); + i += len; + + fat_fd = pathloc->node_access; + + switch (type) + { + case MSDOS_UP_DIR: + /* + * Only a directory can be decended into. + */ + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + /* + * Am I at the root of this mounted filesystem? + */ + if (pathloc->node_access == + pathloc->mt_entry->mt_fs_root.node_access) + { + /* + * Am I at the root of all filesystems? + * XXX: MSDOS is not supposed to be base fs. + */ + if (pathloc->node_access == + rtems_filesystem_root.node_access) + { + break; /* Throw out the .. in this case */ + } + else + { + newloc = pathloc->mt_entry->mt_point_node; + *pathloc = newloc; + + rc = fat_file_close(pathloc->mt_entry, fat_fd); + if (rc != RC_OK) + goto err; + + rtems_semaphore_release(fs_info->vol_sema); + return (*pathloc->ops->evalpath_h)(&(pathname[i-len]), + flags, pathloc); + } + } + else + { + rc = msdos_find_name(pathloc, token); + if (rc != RC_OK) + { + if (rc == MSDOS_NAME_NOT_FOUND_ERR) + { + errno = ENOENT; + rc = -1; + } + goto error; + } + } + break; + + case MSDOS_NAME: + /* + * Only a directory can be decended into. + */ + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + /* + * Otherwise find the token name in the present location and + * set the node access to the point we have found. + */ + rc = msdos_find_name(pathloc, token); + if (rc != RC_OK) + { + if (rc == MSDOS_NAME_NOT_FOUND_ERR) + { + errno = ENOENT; + rc = -1; + } + goto error; + } + break; + + case MSDOS_NO_MORE_PATH: + case MSDOS_CURRENT_DIR: + break; + + case MSDOS_INVALID_TOKEN: + errno = ENAMETOOLONG; + rc = -1; + goto error; + break; + + } + } + + /* + * Always return the root node. + * + * If we are at a node that is a mount point. Set loc to the + * new fs root node and let let the mounted filesystem set the handlers. + * + * NOTE: The behavior of stat() on a mount point appears to be + * questionable. + * NOTE: MSDOS filesystem currently doesn't support mount functionality -> + * action not implemented + */ + fat_fd = pathloc->node_access; + + msdos_set_handlers(pathloc); + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; + +error: + fat_file_close(pathloc->mt_entry, fat_fd); + +err: + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} + +/* msdos_eval4make -- + * The following routine evaluate path for a new node to be created. + * 'pathloc' is returned with a pointer to the parent of the new node. + * 'name' is returned with a pointer to the first character in the + * new node name. The parent node is verified to be a directory. + * + * PARAMETERS: + * path - path for evaluation + * pathloc - IN/OUT (start point for evaluation/parent directory for + * creation) + * name - new node name + * + * RETURNS: + * RC_OK, filled pathloc for parent directory and name of new node on + * success, or -1 if error occured (errno set appropriately) + */ +int +msdos_eval4make( + const char *path, + rtems_filesystem_location_info_t *pathloc, + const char **name + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + rtems_filesystem_location_info_t newloc; + msdos_token_types_t type; + int i = 0; + int len; + char token[ MSDOS_NAME_MAX + 1 ]; + rtems_boolean done = 0; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + if (!pathloc->node_access) + { + errno = ENOENT; + rc = -1; + goto err; + } + + fat_fd = pathloc->node_access; + + rc = fat_file_reopen(fat_fd); + if (rc != RC_OK) + goto err; + + while (!done) + { + type = msdos_get_token(&path[i], token, &len); + i += len; + fat_fd = pathloc->node_access; + + switch (type) + { + case MSDOS_UP_DIR: + /* + * Only a directory can be decended into. + */ + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + /* + * Am I at the root of this mounted filesystem? + */ + if (pathloc->node_access == + pathloc->mt_entry->mt_fs_root.node_access) + { + /* + * Am I at the root of all filesystems? + * XXX: MSDOS is not supposed to be base fs. + */ + if (pathloc->node_access == + rtems_filesystem_root.node_access) + { + break; /* Throw out the .. in this case */ + } + else + { + newloc = pathloc->mt_entry->mt_point_node; + *pathloc = newloc; + + rc = fat_file_close(pathloc->mt_entry, fat_fd); + if (rc != RC_OK) + goto err; + + rtems_semaphore_release(fs_info->vol_sema); + return (*pathloc->ops->evalformake_h)(&path[i-len], + pathloc, name); + } + } + else + { + rc = msdos_find_name(pathloc, token); + if (rc != RC_OK) + { + if (rc == MSDOS_NAME_NOT_FOUND_ERR) + { + errno = ENOENT; + rc = -1; + } + goto error; + } + } + break; + + case MSDOS_NAME: + /* + * Only a directory can be decended into. + */ + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + /* + * Otherwise find the token name in the present location and + * set the node access to the point we have found. + */ + rc = msdos_find_name(pathloc, token); + if (rc) + { + if (rc != MSDOS_NAME_NOT_FOUND_ERR) + { + errno = ENOENT; + rc = -1; + goto error; + } + else + done = TRUE; + } + break; + + case MSDOS_NO_MORE_PATH: + errno = EEXIST; + rc = -1; + goto error; + break; + + case MSDOS_CURRENT_DIR: + break; + + case MSDOS_INVALID_TOKEN: + errno = ENAMETOOLONG; + rc = -1; + goto error; + break; + + } + } + + *name = &path[i - len]; + + /* + * We have evaluated the path as far as we can. + * Verify there is not any invalid stuff at the end of the name. + */ + for( ; path[i] != '\0'; i++) + { + if (!msdos_is_separator(path[i])) + { + errno = ENOENT; + rc = -1; + goto error; + } + } + + fat_fd = pathloc->node_access; + + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + msdos_set_handlers(pathloc); + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; + +error: + fat_file_close(pathloc->mt_entry, fat_fd); + +err: + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/c/src/libfs/src/dosfs/msdos_file.c b/c/src/libfs/src/dosfs/msdos_file.c new file mode 100644 index 0000000000..da36827338 --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_file.c @@ -0,0 +1,485 @@ +/* + * MSDOS file handlers implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_file_open -- + * Open fat-file which correspondes to the file + * + * PARAMETERS: + * iop - file control block + * pathname - name + * flag - flags + * mode - mode + * + * RETURNS: + * RC_OK, if file opened successfully, or -1 if error occured + * and errno set appropriately + */ +int +msdos_file_open(rtems_libio_t *iop, const char *pathname, unsigned32 flag, + unsigned32 mode) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_reopen(fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + if (iop->flags & LIBIO_FLAGS_APPEND) + iop->offset = fat_fd->fat_file_size; + + iop->size = fat_fd->fat_file_size; + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_file_close -- + * Close fat-file which correspondes to the file. If fat-file descriptor + * which correspondes to the file is not marked "removed", synchronize + * size, first cluster number, write time and date fields of the file. + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK, if file closed successfully, or -1 if error occured (errno set + * appropriately) + */ +int +msdos_file_close(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* + * if fat-file descriptor is not marked as "removed", synchronize + * size, first cluster number, write time and date fields of the file + */ + if (!FAT_FILE_IS_REMOVED(fat_fd)) + { + rc = msdos_set_first_cluster_num(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + rc = msdos_set_file_size(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + rc = msdos_set_dir_wrt_time_and_date(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + } + + rc = fat_file_close(iop->pathinfo.mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} + +/* msdos_file_read -- + * This routine read from file pointed to by file control block into + * the specified data buffer provided by user + * + * PARAMETERS: + * iop - file control block + * buffer - buffer provided by user + * count - the number of bytes to read + * + * RETURNS: + * the number of bytes read on success, or -1 if error occured (errno set + * appropriately) + */ +ssize_t +msdos_file_read(rtems_libio_t *iop, void *buffer, unsigned32 count) +{ + ssize_t ret = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + ret = fat_file_read(iop->pathinfo.mt_entry, fat_fd, iop->offset, count, + buffer); + + rtems_semaphore_release(fs_info->vol_sema); + return ret; +} + +/* msdos_file_write -- + * This routine writes the specified data buffer into the file pointed to + * by file control block. + * + * PARAMETERS: + * iop - file control block + * buffer - data to write + * count - count of bytes to write + * + * RETURNS: + * the number of bytes written on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +msdos_file_write(rtems_libio_t *iop,const void *buffer, unsigned32 count) +{ + ssize_t ret = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + ret = fat_file_write(iop->pathinfo.mt_entry, fat_fd, iop->offset, count, + buffer); + if (ret < 0) + { + rtems_semaphore_release(fs_info->vol_sema); + return -1; + } + + /* + * update file size in both fat-file descriptor and file control block if + * file was extended + */ + if (iop->offset + ret > fat_fd->fat_file_size) + fat_fd->fat_file_size = iop->offset + ret; + + iop->size = fat_fd->fat_file_size; + + rtems_semaphore_release(fs_info->vol_sema); + return ret; +} + +/* msdos_file_lseek -- + * Process lseek call to the file: extend file if lseek is up to the end + * of the file. + * + * PARAMETERS: + * iop - file control block + * offset - new offset + * whence - predefine directive + * + * RETURNS: + * new offset on success, or -1 if error occured (errno set + * appropriately). + */ +int +msdos_file_lseek(rtems_libio_t *iop, off_t offset, int whence) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + unsigned32 real_size = 0; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_extend(iop->pathinfo.mt_entry, fat_fd, iop->offset, + &real_size); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + if (real_size > fat_fd->fat_file_size) + fat_fd->fat_file_size = iop->offset = real_size; + + iop->size = fat_fd->fat_file_size; + + rtems_semaphore_release(fs_info->vol_sema); + return iop->offset; +} + +/* msdos_file_stat -- + * + * PARAMETERS: + * loc - node description + * buf - stat buffer provided by user + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_file_stat( + rtems_filesystem_location_info_t *loc, + struct stat *buf + ) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = loc->node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + buf->st_dev = fs_info->fat.vol.dev; + buf->st_ino = fat_fd->ino; + buf->st_mode = S_IFREG; + buf->st_rdev = 0ll; + buf->st_size = fat_fd->fat_file_size; + buf->st_blocks = fat_fd->fat_file_size >> FAT_SECTOR512_BITS; + buf->st_blksize = fs_info->fat.vol.bps; + buf->st_mtime = fat_fd->mtime; + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_file_ftruncate -- + * Truncate the file (if new length is greater then current do nothing). + * + * PARAMETERS: + * iop - file control block + * length - new length + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately). + */ +int +msdos_file_ftruncate(rtems_libio_t *iop, off_t length) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + if (length >= fat_fd->fat_file_size) + return RC_OK; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_truncate(iop->pathinfo.mt_entry, fat_fd, length); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + /* + * fat_file_truncate do nothing if new length >= fat-file size, so update + * file size only if length < fat-file size + */ + if (length < fat_fd->fat_file_size) + iop->size = fat_fd->fat_file_size = length; + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_file_sync -- + * Synchronize file - synchronize file data and if file is not removed + * synchronize file metadata. + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_file_sync(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + fat_file_fd_t *fat_fd = iop->file_info; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* synchronize file data */ + rc = fat_file_datasync(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + /* + * if fat-file descriptor is not marked "removed" - synchronize file + * metadata + */ + if (!FAT_FILE_IS_REMOVED(fat_fd)) + { + rc = msdos_set_first_cluster_num(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + rc = msdos_set_file_size(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + rc = msdos_set_dir_wrt_time_and_date(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + } + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_file_datasync -- + * Synchronize file - synchronize only file data (metadata is letf intact). + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_file_datasync(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + fat_file_fd_t *fat_fd = iop->file_info; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* synchronize file data */ + rc = fat_file_datasync(iop->pathinfo.mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + + +/* msdos_file_ioctl -- + * + * + * PARAMETERS: + * iop - file control block + * ... + * + * RETURNS: + * + */ +int +msdos_file_ioctl(rtems_libio_t *iop,unsigned32 command, void *buffer) +{ + int rc = RC_OK; + + return rc; +} + +/* msdos_file_rmnod -- + * Remove node associated with a file - set up first name character to + * predefined value(and write it to the disk), and mark fat-file which + * correspondes to the file as "removed" + * + * PARAMETERS: + * pathloc - node description + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_file_rmnod(rtems_filesystem_location_info_t *pathloc) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = pathloc->node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* mark file removed */ + rc = msdos_set_first_char4file_name(pathloc->mt_entry, fat_fd->info_cln, + fat_fd->info_ofs, + MSDOS_THIS_DIR_ENTRY_EMPTY); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + fat_file_mark_removed(pathloc->mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} diff --git a/c/src/libfs/src/dosfs/msdos_free.c b/c/src/libfs/src/dosfs/msdos_free.c new file mode 100644 index 0000000000..c0d5938dbb --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_free.c @@ -0,0 +1,56 @@ +/* + * Free node handler implementation for the filesystem + * operations table. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_free_node_info -- + * Call fat-file close routine. + * + * PARAMETERS: + * pathloc - node description + * + * RETURNS: + * RC_OK on success, or -1 code if error occured + * + */ +int +msdos_free_node_info(rtems_filesystem_location_info_t *pathloc) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_close(pathloc->mt_entry, pathloc->node_access); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/c/src/libfs/src/dosfs/msdos_fsunmount.c b/c/src/libfs/src/dosfs/msdos_fsunmount.c new file mode 100644 index 0000000000..9072a2fad5 --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_fsunmount.c @@ -0,0 +1,71 @@ +/* + * MSDOS shut down handler implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_shut_down -- + * Shut down MSDOS filesystem - free all allocated resources (don't + * return if deallocation of some resource failed - free as much as + * possible). + * + * PARAMETERS: + * temp_mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + * + */ +int +msdos_shut_down(rtems_filesystem_mount_table_entry_t *temp_mt_entry) +{ + int rc = RC_OK; + msdos_fs_info_t *fs_info = temp_mt_entry->fs_info; + fat_file_fd_t *fat_fd = temp_mt_entry->mt_fs_root.node_access; + + /* close fat-file which correspondes to root directory */ + if (fat_file_close(temp_mt_entry, fat_fd) != RC_OK) + { + /* no return - try to free as much as possible */ + rc = -1; + } + + if (fat_shutdown_drive(temp_mt_entry) != RC_OK) + { + /* no return - try to free as much as possible */ + rc = -1; + } + + rtems_semaphore_delete(fs_info->vol_sema); + free(fs_info->cl_buf); + free(temp_mt_entry->fs_info); + + return rc; +} diff --git a/c/src/libfs/src/dosfs/msdos_handlers_dir.c b/c/src/libfs/src/dosfs/msdos_handlers_dir.c new file mode 100644 index 0000000000..e14d892add --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_handlers_dir.c @@ -0,0 +1,36 @@ +/* + * Directory Handlers Table for MSDOS filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "msdos.h" + +rtems_filesystem_file_handlers_r msdos_dir_handlers = { + msdos_dir_open, + msdos_dir_close, + msdos_dir_read, + NULL, /* msdos_dir_write */ + NULL, /* msdos_dir_ioctl */ + msdos_dir_lseek, + msdos_dir_stat, + NULL, + NULL, /* msdos_dir_ftruncate */ + NULL, + msdos_dir_sync, + msdos_dir_sync, + NULL, /* msdos_dir_fcntl */ + msdos_dir_rmnod +}; diff --git a/c/src/libfs/src/dosfs/msdos_handlers_file.c b/c/src/libfs/src/dosfs/msdos_handlers_file.c new file mode 100644 index 0000000000..ae627066de --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_handlers_file.c @@ -0,0 +1,36 @@ +/* + * File Operations Table for MSDOS filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "msdos.h" + +rtems_filesystem_file_handlers_r msdos_file_handlers = { + msdos_file_open, + msdos_file_close, + msdos_file_read, + msdos_file_write, + msdos_file_ioctl, + msdos_file_lseek, + msdos_file_stat, + NULL, + msdos_file_ftruncate, + NULL, + msdos_file_sync, + msdos_file_datasync, + NULL, /* msdos_file_fcntl */ + msdos_file_rmnod +}; diff --git a/c/src/libfs/src/dosfs/msdos_init.c b/c/src/libfs/src/dosfs/msdos_init.c new file mode 100644 index 0000000000..2d5bf6c9e0 --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_init.c @@ -0,0 +1,60 @@ +/* + * Init routine for MSDOS + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "msdos.h" + +rtems_filesystem_operations_table msdos_ops = { + msdos_eval_path, + msdos_eval4make, + NULL, /* msdos_link */ + msdos_file_rmnod, + msdos_node_type, + msdos_mknod, + NULL, /* msdos_chown */ + msdos_free_node_info, + NULL, + msdos_initialize, + NULL, + msdos_shut_down, /* msdos_shut_down */ + NULL, /* msdos_utime */ + NULL, + NULL, + NULL +}; + +/* msdos_initialize -- + * MSDOS filesystem initialization + * + * PARAMETERS: + * temp_mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + * + */ +int +msdos_initialize(rtems_filesystem_mount_table_entry_t *temp_mt_entry) +{ + int rc = RC_OK; + + rc = msdos_initialize_support(temp_mt_entry, + &msdos_ops, + &msdos_file_handlers, + &msdos_dir_handlers); + return rc; +} diff --git a/c/src/libfs/src/dosfs/msdos_initsupp.c b/c/src/libfs/src/dosfs/msdos_initsupp.c new file mode 100644 index 0000000000..eee8a6f9b2 --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_initsupp.c @@ -0,0 +1,149 @@ +/* + * MSDOS Initialization support routine implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_initialize_support -- + * MSDOS filesystem initialization + * + * PARAMETERS: + * temp_mt_entry - mount table entry + * op_table - filesystem operations table + * file_handlers - file operations table + * directory_handlers - directory operations table + * + * RETURNS: + * RC_OK and filled temp_mt_entry on success, or -1 if error occured + * (errno set apropriately) + * + */ +int +msdos_initialize_support( + rtems_filesystem_mount_table_entry_t *temp_mt_entry, + rtems_filesystem_operations_table *op_table, + rtems_filesystem_file_handlers_r *file_handlers, + rtems_filesystem_file_handlers_r *directory_handlers + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = NULL; + fat_file_fd_t *fat_fd = NULL; + unsigned32 cl_buf_size; + + fs_info = (msdos_fs_info_t *)calloc(1, sizeof(msdos_fs_info_t)); + if (!fs_info) + set_errno_and_return_minus_one(ENOMEM); + + temp_mt_entry->fs_info = fs_info; + + rc = fat_init_volume_info(temp_mt_entry); + if (rc != RC_OK) + { + free(fs_info); + return rc; + } + + fs_info->file_handlers = file_handlers; + fs_info->directory_handlers = directory_handlers; + + /* + * open fat-file which correspondes to root directory + * (so inode number 0x00000010 is always used for root directory) + */ + rc = fat_file_open(temp_mt_entry, FAT_ROOTDIR_CLUSTER_NUM, 0, &fat_fd); + if (rc != RC_OK) + { + fat_shutdown_drive(temp_mt_entry); + free(fs_info); + return rc; + } + + /* again: unfortunately "fat-file" is just almost fat file :( */ + fat_fd->fat_file_type = FAT_DIRECTORY; + fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT; + fat_fd->info_cln = FAT_ROOTDIR_CLUSTER_NUM; + fat_fd->info_ofs = 0; + fat_fd->cln = fs_info->fat.vol.rdir_cl; + + fat_fd->map.file_cln = 0; + fat_fd->map.disk_cln = fat_fd->cln; + + /* if we have FAT12/16 */ + if ( fat_fd->cln == 0 ) + { + fat_fd->fat_file_size = fs_info->fat.vol.rdir_size; + cl_buf_size = (fs_info->fat.vol.bpc > fs_info->fat.vol.rdir_size) ? + fs_info->fat.vol.bpc : + fs_info->fat.vol.rdir_size; + } + else + { + rc = fat_file_size(temp_mt_entry, fat_fd); + if ( rc != RC_OK ) + { + fat_file_close(temp_mt_entry, fat_fd); + fat_shutdown_drive(temp_mt_entry); + free(fs_info); + return rc; + } + cl_buf_size = fs_info->fat.vol.bpc; + } + + fs_info->cl_buf = (char *)calloc(cl_buf_size, sizeof(char)); + if (fs_info->cl_buf == NULL) + { + fat_file_close(temp_mt_entry, fat_fd); + fat_shutdown_drive(temp_mt_entry); + free(fs_info); + set_errno_and_return_minus_one(ENOMEM); + } + + sc = rtems_semaphore_create(3, + 1, + RTEMS_BINARY_SEMAPHORE | RTEMS_FIFO, + RTEMS_INHERIT_PRIORITY, + &fs_info->vol_sema); + if (sc != RTEMS_SUCCESSFUL) + { + fat_file_close(temp_mt_entry, fat_fd); + fat_shutdown_drive(temp_mt_entry); + free(fs_info->cl_buf); + free(fs_info); + set_errno_and_return_minus_one( EIO ); + } + + temp_mt_entry->mt_fs_root.node_access = fat_fd; + temp_mt_entry->mt_fs_root.handlers = directory_handlers; + temp_mt_entry->mt_fs_root.ops = op_table; + + return rc; +} diff --git a/c/src/libfs/src/dosfs/msdos_misc.c b/c/src/libfs/src/dosfs/msdos_misc.c new file mode 100644 index 0000000000..fe2779f7a8 --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_misc.c @@ -0,0 +1,1087 @@ +/* + * Miscellaneous routines implementation for MSDOS filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* This copied from Linux */ +static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 }; + /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ + +#undef CONFIG_ATARI + +/* MS-DOS "device special files" */ +static const char *reserved_names[] = { +#ifndef CONFIG_ATARI /* GEMDOS is less stupid */ + "CON ","PRN ","NUL ","AUX ", + "LPT1 ","LPT2 ","LPT3 ","LPT4 ", + "COM1 ","COM2 ","COM3 ","COM4 ", +#endif + NULL }; + +static char bad_chars[] = "*?<>|\""; +#ifdef CONFIG_ATARI +/* GEMDOS is less restrictive */ +static char bad_if_strict[] = " "; +#else +static char bad_if_strict[] = "+=,; "; +#endif + +/* The following three functions copied from Linux */ +/* + * Formats an MS-DOS file name. Rejects invalid names + * + * conv is relaxed/normal/strict, name is proposed name, + * len is the length of the proposed name, res is the result name, + * dotsOK is if hidden files get dots. + */ +int +msdos_format_name(char conv, const char *name, int len, char *res, + char dotsOK) +{ + char *walk; + const char **reserved; + unsigned char c; + int space; + if (name[0] == '.') { /* dotfile because . and .. already done */ + if (!dotsOK) return -EINVAL; + /* Get rid of dot - test for it elsewhere */ + name++; len--; + } +#ifndef CONFIG_ATARI + space = 1; /* disallow names that _really_ start with a dot */ +#else + space = 0; /* GEMDOS does not care */ +#endif + c = 0; + for (walk = res; len && walk-res < 8; walk++) { + c = *name++; + len--; + if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; + if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL; + if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL; + if (c < ' ' || c == ':' || c == '\\') return -EINVAL; +/* 0xE5 is legal as a first character, but we must substitute 0x05 */ +/* because 0xE5 marks deleted files. Yes, DOS really does this. */ +/* It seems that Microsoft hacked DOS to support non-US characters */ +/* after the 0xE5 character was already in use to mark deleted files. */ + if((res==walk) && (c==0xE5)) c=0x05; + if (c == '.') break; + space = (c == ' '); + *walk = (c >= 'a' && c <= 'z') ? c-32 : c; + } + if (space) return -EINVAL; + if (conv == 's' && len && c != '.') { + c = *name++; + len--; + if (c != '.') return -EINVAL; + } + while (c != '.' && len--) c = *name++; + if (c == '.') { + while (walk-res < 8) *walk++ = ' '; + while (len > 0 && walk-res < MSDOS_NAME_MAX) { + c = *name++; + len--; + if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; + if (conv == 's' && strchr(bad_if_strict,c)) + return -EINVAL; + if (c < ' ' || c == ':' || c == '\\') + return -EINVAL; + if (c == '.') { + if (conv == 's') + return -EINVAL; + break; + } + if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL; + space = c == ' '; + *walk++ = c >= 'a' && c <= 'z' ? c-32 : c; + } + if (space) return -EINVAL; + if (conv == 's' && len) return -EINVAL; + } + while (walk-res < MSDOS_NAME_MAX) *walk++ = ' '; + for (reserved = reserved_names; *reserved; reserved++) + if (!strncmp(res,*reserved,8)) return -EINVAL; + return 0; +} + +/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70) */ +unsigned int +msdos_date_dos2unix(unsigned short time_val,unsigned short date) +{ + int month,year,secs; + + month = ((date >> 5) & 15)-1; + year = date >> 9; + secs = (time_val & 31)*2+60*((time_val >> 5) & 63)+ + (time_val >> 11)*3600+86400* + ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && + month < 2 ? 1 : 0)+3653); + /* days since 1.1.70 plus 80's leap day */ + + return secs; +} + + +/* Convert linear UNIX date to a MS-DOS time/date pair */ +void msdos_date_unix2dos(int unix_date, + unsigned short *time_val, + unsigned short *date) +{ + int day,year,nl_day,month; + + *time_val = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+ + (((unix_date/3600) % 24) << 11); + day = unix_date/86400-3652; + year = day/365; + if ((year+3)/4+365*year > day) year--; + day -= (year+3)/4+365*year; + if (day == 59 && !(year & 3)) { + nl_day = day; + month = 2; + } + else { + nl_day = (year & 3) || day <= 59 ? day : day-1; + for (month = 0; month < 12; month++) + if (day_n[month] > nl_day) break; + } + *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9); +} + + +/* 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, char *ret_token, int *token_len) +{ + int rc = RC_OK; + register int i = 0; + msdos_token_types_t type = MSDOS_NAME; + char token[MSDOS_NAME_MAX_WITH_DOT+1]; + register char c; + + /* + * Copy a name into token. (Remember NULL is a token.) + */ + c = path[i]; + while ( (!msdos_is_separator(c)) && (i <= MSDOS_NAME_MAX_WITH_DOT) ) + { + token[i] = c; + if ( i == MSDOS_NAME_MAX_WITH_DOT ) + return MSDOS_INVALID_TOKEN; + if ( !msdos_is_valid_name_char(c) ) + return MSDOS_INVALID_TOKEN; + c = path [++i]; + } + + /* + * Copy a seperator into token. + */ + if ( i == 0 ) + { + token[i] = c; + if ( token[i] != '\0' ) + { + i++; + type = MSDOS_CURRENT_DIR; + } + else + type = MSDOS_NO_MORE_PATH; + } + else if (token[ i-1 ] != '\0') + token[i] = '\0'; + + /* + * Set token_len to the number of characters copied. + */ + *token_len = i; + + /* + * If we copied something that was not a seperator see if + * it was a special name. + */ + if ( type == MSDOS_NAME ) + { + if ( strcmp( token, "..") == 0 ) + { + strcpy(ret_token, MSDOS_DOTDOT_NAME); + type = MSDOS_UP_DIR; + return type; + } + + if ( strcmp( token, "." ) == 0 ) + { + strcpy(ret_token, MSDOS_DOT_NAME); + type = MSDOS_CURRENT_DIR; + return type; + } + + rc = msdos_format_name('r', token, *token_len, ret_token, 0); + if ( rc != RC_OK ) + return MSDOS_INVALID_TOKEN; + } + ret_token[MSDOS_NAME_MAX] = '\0'; + 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, + char *name + ) +{ + int rc = RC_OK; + msdos_fs_info_t *fs_info = parent_loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + fat_auxiliary_t aux; + unsigned short time_val = 0; + unsigned short date = 0; + unsigned char node_entry[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + + memset(node_entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + + /* + * find the node which correspondes to the name in the directory pointed by + * 'parent_loc' + */ + rc = msdos_get_name_node(parent_loc, name, &aux, node_entry); + if (rc != RC_OK) + return rc; + + /* open fat-file corresponded to the found node */ + rc = fat_file_open(parent_loc->mt_entry, aux.cln, aux.ofs, &fat_fd); + if (rc != RC_OK) + return rc; + + /* + * 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 non-optimize) because + * otherwise real values of these fields stored in fat-file descriptor + * may be accidentely rewritten with wrong values stored on the disk + */ + if (fat_fd->links_num == 1) + { + fat_fd->info_cln = aux.cln; + fat_fd->info_ofs = aux.ofs; + fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(node_entry); + fat_fd->first_char = *MSDOS_DIR_NAME(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(time_val), CF_LE_W(date)); + + 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 mode 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 + * name_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, + char *name, + fat_auxiliary_t *paux, + char *name_dir_entry + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = parent_loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = parent_loc->node_access; + unsigned32 dotdot_cln = 0; + + /* find name in fat-file which correspondes to the directory */ + rc = msdos_find_name_in_fat_file(parent_loc->mt_entry, fat_fd, name, paux, + name_dir_entry); + if ((rc != RC_OK) && (rc != MSDOS_NAME_NOT_FOUND_ERR)) + return rc; + + /* if we search for valid name and name not found -> return */ + if ((rc == MSDOS_NAME_NOT_FOUND_ERR) && (name != NULL)) + return rc; + + /* + * if we try to create new entry and the directory is not big enough + * currently - try to enlarge directory + */ + if ((rc == MSDOS_NAME_NOT_FOUND_ERR) && (name == NULL)) + { + ret = fat_file_write(parent_loc->mt_entry, fat_fd, + fat_fd->fat_file_size, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE, + name_dir_entry); + if (ret == -1) + return -1; + + /* on success directory is enlarged by a new cluster */ + fat_fd->fat_file_size += fs_info->fat.vol.bpc; + + /* get cluster num where a new node located */ + rc = fat_file_ioctl(parent_loc->mt_entry, fat_fd, F_CLU_NUM, + fat_fd->fat_file_size - 1, &paux->cln); + + if (rc != RC_OK) + return rc; + + /* + * if new cluster allocated succesfully then new node is at very + * beginning of the cluster (offset is computed in bytes) + */ + paux->ofs = 0; + return RC_OK; + } + + /* + * 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) && (name != NULL)) + { + if (strncmp(name, MSDOS_DOTDOT_NAME, MSDOS_SHORT_NAME_LEN) == 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 + */ + paux->cln = FAT_ROOTDIR_CLUSTER_NUM; + paux->ofs = 0; + } + else + { + rc = msdos_get_dotdot_dir_info_cluster_num_and_offset( + parent_loc->mt_entry, + dotdot_cln, + paux, + 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, + unsigned32 cln, + fat_auxiliary_t *paux, + char *dir_entry + ) +{ + int rc = RC_OK; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + unsigned char dot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + unsigned char dotdot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + unsigned char cur_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + unsigned32 cl4find = 0; + + memset(dot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memset(dotdot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memset(cur_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + + /* + * open fat-file corresponded to ".." + */ + rc = fat_file_open(mt_entry, paux->cln, paux->ofs, &fat_fd); + if (rc != RC_OK) + return rc; + + fat_fd->info_cln = paux->cln; + fat_fd->info_ofs = paux->ofs; + 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 */ + rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, MSDOS_DOT_NAME, paux, + dot_node); + + if (rc != RC_OK) + { + fat_file_close(mt_entry, fat_fd); + return rc; + } + + /* find ".." node in opened directory */ + rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, MSDOS_DOTDOT_NAME, paux, + 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 ) + */ + paux->cln = FAT_ROOTDIR_CLUSTER_NUM; + paux->ofs = 0; + } + + /* open fat-file corresponded to second ".." */ + rc = fat_file_open(mt_entry, paux->cln, paux->ofs, &fat_fd); + if (rc != RC_OK) + return rc; + + fat_fd->info_cln = paux->cln; + fat_fd->info_ofs = paux->ofs; + + 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, + paux, 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; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned short time_val; + unsigned short date; + unsigned32 sec = 0; + unsigned32 byte = 0; + + msdos_date_unix2dos(fat_fd->mtime, &time_val, &date); + + /* + * 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->info_cln); + sec += (fat_fd->info_ofs >> fs_info->fat.vol.sec_log2); + /* byte points to start of 32bytes structure */ + byte = fat_fd->info_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)); + + if ( (ret1 < 0) || (ret2 < 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; + unsigned32 new_cln = fat_fd->cln; + unsigned16 le_cl_low = 0; + unsigned16 le_cl_hi = 0; + unsigned32 sec = 0; + unsigned32 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->info_cln); + sec += (fat_fd->info_ofs >> fs_info->fat.vol.sec_log2); + /* byte from points to start of 32bytes structure */ + byte = fat_fd->info_ofs & (fs_info->fat.vol.bps - 1); + + le_cl_low = CT_LE_W((unsigned16)(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((unsigned16)((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; + unsigned32 le_new_length = 0; + unsigned32 sec = 0; + unsigned32 byte = 0; + + sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->info_cln); + sec += (fat_fd->info_ofs >> fs_info->fat.vol.sec_log2); + byte = (fat_fd->info_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, + unsigned32 cl, + unsigned32 ofs, + unsigned char fchar + ) +{ + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 sec = 0; + unsigned32 byte = 0; + + sec = fat_cluster_num_to_sector_num(mt_entry, cl); + sec += (ofs >> fs_info->fat.vol.sec_log2); + byte = (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; + + 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, + rtems_boolean *ret_val + ) +{ + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 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); + + for (i = 0; + i < fs_info->fat.vol.bps; + i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + if (((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_EMPTY) || + (strncmp(MSDOS_DIR_NAME((fs_info->cl_buf + i)), MSDOS_DOT_NAME, + MSDOS_SHORT_NAME_LEN) == 0) || + (strncmp(MSDOS_DIR_NAME((fs_info->cl_buf + i)), + MSDOS_DOTDOT_NAME, + MSDOS_SHORT_NAME_LEN) == 0)) + continue; + + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) + { + *ret_val = TRUE; + return RC_OK; + } + return RC_OK; + } + j++; + } + *ret_val = TRUE; + return RC_OK; +} + + +/* msdos_find_name_in_fat_file -- + * This routine is used in two ways: for a new mode 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 corresponded to directory we are going to create + * node in. If found free slot write contents of name_dir_entry into + * it. + * + * (b): reading fat-file corresponded to directory and trying to find slot + * with the name field == name parameter + * + * 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) + * + */ +int +msdos_find_name_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + char *name, + fat_auxiliary_t *paux, + char *name_dir_entry + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 i = 0, j = 0; + unsigned32 bts2rd = 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) + set_errno_and_return_minus_one(EIO); + + assert(ret == bts2rd); + + for (i = 0; i < bts2rd; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + /* is the entry empty ? */ + if (((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) || + ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_EMPTY)) + { + /* whether we are looking for an empty entry */ + if (name == NULL) + { + /* get current cluster number */ + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, + j * bts2rd, &paux->cln); + if (rc != RC_OK) + return rc; + + /* offset is computed in bytes */ + paux->ofs = i; + + /* write new node entry */ + ret = fat_file_write(mt_entry, fat_fd, j * bts2rd + i, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE, + name_dir_entry); + if (ret != MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + return -1; + + /* + * we don't update fat_file_size here - it should not + * increase + */ + return RC_OK; + } + + /* + * if name != NULL and there is no more entries in the + * directory - return name-not-found + */ + if (((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)) + return MSDOS_NAME_NOT_FOUND_ERR; + } + else + { + /* entry not empty and name != NULL -> compare names */ + if (name != NULL) + { + if (strncmp(MSDOS_DIR_NAME((fs_info->cl_buf + i)), name, + MSDOS_SHORT_NAME_LEN) == 0) + { + /* + * we get the entry we looked for - fill auxiliary + * structure and copy all 32 bytes of the entry + */ + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, + j * bts2rd, &paux->cln); + if (rc != RC_OK) + return rc; + + /* offset is computed in bytes */ + paux->ofs = i; + memcpy(name_dir_entry,(fs_info->cl_buf + i), + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + return RC_OK; + } + } + } + } + j++; + } + return MSDOS_NAME_NOT_FOUND_ERR; +} + +/* msdos_find_node_by_cluster_num_in_fat_file -- + * Find node with specified number of cluster in fat-file. + * + * 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, + unsigned32 cl4find, + fat_auxiliary_t *paux, + char *dir_entry + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 bts2rd = 0; + unsigned32 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 ) + set_errno_and_return_minus_one( EIO ); + + assert(ret == bts2rd); + + for (i = 0; i < bts2rd; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + /* if this and all rest entries are empty - return not-found */ + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) + return MSDOS_NAME_NOT_FOUND_ERR; + + /* if this entry is empty - skip it */ + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_EMPTY) + continue; + + /* if get a non-empty entry - compare clusters num */ + if (MSDOS_EXTRACT_CLUSTER_NUM((fs_info->cl_buf + i)) == cl4find) + { + /* on success fill aux structure and copy all 32 bytes */ + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, j * bts2rd, + &paux->cln); + if (rc != RC_OK) + return rc; + + paux->ofs = i; + memcpy(dir_entry, fs_info->cl_buf + i, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + return RC_OK; + } + } + j++; + } + return MSDOS_NAME_NOT_FOUND_ERR; +} diff --git a/c/src/libfs/src/dosfs/msdos_mknod.c b/c/src/libfs/src/dosfs/msdos_mknod.c new file mode 100644 index 0000000000..5e32dbf3bf --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_mknod.c @@ -0,0 +1,90 @@ +/* + * Routine for node creation in MSDOS filesystem. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_mknod -- + * The following function checks spelling and formats name for a new node, + * determines type of the node to be created and creates it. + * + * PARAMETERS: + * token - non-formatted name of a new node + * mode - node type + * dev - dev + * pathloc - parent directory description + * + * RETURNS: + * RC_OK on succes, or -1 if error occured and set errno + * + */ +int +msdos_mknod( + const char *token, + mode_t mode, + dev_t dev, + rtems_filesystem_location_info_t *pathloc + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + msdos_token_types_t type = 0; + char new_name[ MSDOS_NAME_MAX + 1 ]; + int len; + + /* check spelling and format new node name */ + msdos_get_token(token, new_name, &len); + + /* + * Figure out what type of msdos node this is. + */ + if (S_ISDIR(mode)) + { + type = MSDOS_DIRECTORY; + } + else if (S_ISREG(mode)) + { + type = MSDOS_REGULAR_FILE; + } + else + set_errno_and_return_minus_one(EINVAL); + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* Create an MSDOS node */ + rc = msdos_creat_node(pathloc, type, new_name, mode); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/c/src/libfs/src/dosfs/msdos_node_type.c b/c/src/libfs/src/dosfs/msdos_node_type.c new file mode 100644 index 0000000000..517dabda3f --- /dev/null +++ b/c/src/libfs/src/dosfs/msdos_node_type.c @@ -0,0 +1,58 @@ +/* + * The following returns the type of node that the loc refers to. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_node_type -- + * Determine type of the node that the pathloc refers to. + * + * PARAMETERS: + * pathloc - node description + * + * RETURNS: + * node type + * + */ +rtems_filesystem_node_types_t +msdos_node_type(rtems_filesystem_location_info_t *pathloc) +{ + fat_file_fd_t *fat_fd; + + /* + * we don't need to obtain the volume semaphore here because node_type_h + * call always follows evalpath_h call(hence link increment occured) and + * hence node_access memory can't be freed during processing node_type_h + * call + */ + fat_fd = pathloc->node_access; + + return fat_fd->fat_file_type; +} diff --git a/c/src/libfs/src/dosfs/stamp-h2.in b/c/src/libfs/src/dosfs/stamp-h2.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/c/src/libfs/wrapup/Makefile.am b/c/src/libfs/wrapup/Makefile.am index 5dfd76e83d..13406dcb5f 100644 --- a/c/src/libfs/wrapup/Makefile.am +++ b/c/src/libfs/wrapup/Makefile.am @@ -13,7 +13,9 @@ include $(top_srcdir)/../../../automake/lib.am IMFSLIB = ../src/imfs/$(ARCH)/libimfs.a -TMP_LIBS = $(IMFSLIB) +DOSFSLIB = ../src/dosfs/$(ARCH)/libdosfs.a + +TMP_LIBS = $(IMFSLIB) $(DOSFSLIB) $(PROJECT_RELEASE)/lib/$(LIBNAME)$(LIB_VARIANT).a: $(LIB) $(INSTALL_DATA) $< $@ diff --git a/cpukit/libfs/ChangeLog b/cpukit/libfs/ChangeLog index b4dd8e9f61..4166f2e369 100644 --- a/cpukit/libfs/ChangeLog +++ b/cpukit/libfs/ChangeLog @@ -1,3 +1,20 @@ +2002-02-28 Victor V. Vengerov + + * 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. + 2002-01-07 Ralf Corsepius * src/imfs/imfs_load_tar.c: Add include . diff --git a/cpukit/libfs/configure.ac b/cpukit/libfs/configure.ac index 4e2adeb1b5..7f36fac28e 100644 --- a/cpukit/libfs/configure.ac +++ b/cpukit/libfs/configure.ac @@ -5,6 +5,7 @@ AC_PREREQ(2.52) AC_INIT AC_CONFIG_SRCDIR([src/imfs/imfs.h]) +AC_CONFIG_SRCDIR([src/dosfs/dosfs.h]) RTEMS_TOP(../../..) AC_CONFIG_AUX_DIR(../../..) @@ -27,11 +28,13 @@ RTEMS_CANONICALIZE_TOOLS AM_CONDITIONAL(UNIX,test x"$RTEMS_CPU" = x"unix") AM_CONFIG_HEADER(src/imfs/config.h) +AM_CONFIG_HEADER(src/dosfs/config.h) # Explicitly list all Makefiles here AC_CONFIG_FILES([Makefile src/Makefile src/imfs/Makefile +src/dosfs/Makefile wrapup/Makefile ]) AC_OUTPUT diff --git a/cpukit/libfs/src/Makefile.am b/cpukit/libfs/src/Makefile.am index 126a226126..fcd82899b4 100644 --- a/cpukit/libfs/src/Makefile.am +++ b/cpukit/libfs/src/Makefile.am @@ -4,7 +4,7 @@ AUTOMAKE_OPTIONS = foreign 1.4 -SUBDIRS = imfs +SUBDIRS = imfs dosfs include $(top_srcdir)/../../../automake/subdirs.am include $(top_srcdir)/../../../automake/local.am diff --git a/cpukit/libfs/src/dosfs/.cvsignore b/cpukit/libfs/src/dosfs/.cvsignore new file mode 100644 index 0000000000..7bb609bf24 --- /dev/null +++ b/cpukit/libfs/src/dosfs/.cvsignore @@ -0,0 +1,6 @@ +Makefile +Makefile.in +config.h +config.h.in +stamp-h +stamp-h.in diff --git a/cpukit/libfs/src/dosfs/Makefile.am b/cpukit/libfs/src/dosfs/Makefile.am new file mode 100644 index 0000000000..44c9f4cd2f --- /dev/null +++ b/cpukit/libfs/src/dosfs/Makefile.am @@ -0,0 +1,80 @@ +## +## $Id$ +## + +AUTOMAKE_OPTIONS = foreign 1.4 + +INCLUDES = -I. + +LIBNAME = libdosfs +LIB = ${ARCH}/${LIBNAME}.a + +FATFS_C_FILES = fat.c fat_fat_operations.c fat_file.c + +DOSFS_C_FILES = msdos_create.c msdos_dir.c msdos_eval.c msdos_file.c \ + msdos_free.c msdos_fsunmount.c msdos_handlers_dir.c \ + msdos_handlers_file.c msdos_init.c msdos_initsupp.c \ + msdos_misc.c msdos_mknod.c msdos_node_type.c + + +UNIX_C_FILES = msdos_unixstub.c + +EMBEDDED_C_FILES = $(FATFS_C_FILES) $(DOSFS_C_FILES) + +COMMON_C_FILES = + +if UNIX +C_FILES = $(COMMON_C_FILES) $(UNIX_C_FILES) +else +C_FILES = $(COMMON_C_FILES) $(EMBEDDED_C_FILES) +endif +C_O_FILES = $(C_FILES:%.c=${ARCH}/%.o) + +include_HEADERS = fat.h fat_fat_operations.h \ + fat_file.h msdos.h dosfs.h +SYS_H_FILES = +RTEMS_H_FILES = +noinst_HEADERS = + +include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg +include $(top_srcdir)/../../../automake/compile.am +include $(top_srcdir)/../../../automake/lib.am + +PREINSTALL_FILES = $(PROJECT_INCLUDE) $(PROJECT_INCLUDE)/rtems \ + $(PROJECT_INCLUDE)/sys $(include_HEADERS:%=$(PROJECT_INCLUDE)/%) \ + $(RTEMS_H_FILES:%=$(PROJECT_INCLUDE)/rtems/%) \ + $(SYS_H_FILES:%=$(PROJECT_INCLUDE)/sys/%) + +$(PROJECT_INCLUDE): + @$(mkinstalldirs) $@ +$(PROJECT_INCLUDE)/rtems: + @$(mkinstalldirs) $@ +$(PROJECT_INCLUDE)/sys: + @$(mkinstalldirs) $@ + +$(PROJECT_INCLUDE)/%.h: %.h + $(INSTALL_DATA) $< $@ +$(PROJECT_INCLUDE)/rtems/%.h: %.h + $(INSTALL_DATA) $< $@ +$(PROJECT_INCLUDE)/sys/%.h: %.h + $(INSTALL_DATA) $< $@ + +OBJS = $(C_O_FILES) + +# +# Add local stuff here using += +# + +AM_CFLAGS += $(LIBC_DEFINES) + +all-local: ${ARCH} $(LIB) + +$(LIB): ${OBJS} + $(make-library) + +DOC_FILES = + +EXTRA_DIST = $(DOC_FILES) $(COMMON_C_FILES) $(EMBEDDED_C_FILES) \ + $(UNIX_C_FILES) $(RTEMS_H_FILES) $(SYS_H_FILES) + +include $(top_srcdir)/../../../automake/local.am diff --git a/cpukit/libfs/src/dosfs/dosfs.h b/cpukit/libfs/src/dosfs/dosfs.h new file mode 100644 index 0000000000..4cea929d4c --- /dev/null +++ b/cpukit/libfs/src/dosfs/dosfs.h @@ -0,0 +1,31 @@ +/* + * dosfs.h + * + * Application interface to MSDOS filesystem. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_DOSFS_H__ +#define __DOSFS_DOSFS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +extern rtems_filesystem_operations_table msdos_ops; + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_DOSFS_H__ */ diff --git a/cpukit/libfs/src/dosfs/fat.c b/cpukit/libfs/src/dosfs/fat.c new file mode 100644 index 0000000000..852c104781 --- /dev/null +++ b/cpukit/libfs/src/dosfs/fat.c @@ -0,0 +1,695 @@ +/* + * fat.c + * + * Low-level operations on a volume with FAT filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * @(#) $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" + +/* _fat_block_read -- + * This function reads 'count' bytes from device filesystem is mounted on, + * starts at 'start+offset' position where 'start' computed in sectors + * and 'offset' is offset inside sector (reading may cross sectors + * boundary; in this case assumed we want to read sequential sector(s)) + * + * PARAMETERS: + * mt_entry - mount table entry + * start - sector num to start read from + * offset - offset inside sector 'start' + * count - count of bytes to read + * buff - buffer provided by user + * + * RETURNS: + * bytes read on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +_fat_block_read( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start, + unsigned32 offset, + unsigned32 count, + void *buff + ) +{ + int rc = RC_OK; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + ssize_t cmpltd = 0; + unsigned32 blk = start; + unsigned32 ofs = offset; + bdbuf_buffer *block = NULL; + unsigned32 c = 0; + + while (count > 0) + { + rc = fat_buf_access(fs_info, blk, FAT_OP_TYPE_READ, &block); + if (rc != RC_OK) + return rc; + + c = MIN(count, (fs_info->vol.bps - ofs)); + memcpy((buff + cmpltd), (block->buffer + ofs), c); + + count -= c; + cmpltd += c; + blk++; + ofs = 0; + } + return cmpltd; +} + +/* _fat_block_write -- + * This function write 'count' bytes to device filesystem is mounted on, + * starts at 'start+offset' position where 'start' computed in sectors + * and 'offset' is offset inside sector (writing may cross sectors + * boundary; in this case assumed we want to write sequential sector(s)) + * + * PARAMETERS: + * mt_entry - mount table entry + * start - sector num to start read from + * offset - offset inside sector 'start' + * count - count of bytes to write + * buff - buffer provided by user + * + * RETURNS: + * bytes written on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +_fat_block_write( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start, + unsigned32 offset, + unsigned32 count, + const void *buff) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + ssize_t cmpltd = 0; + unsigned32 blk = start; + unsigned32 ofs = offset; + bdbuf_buffer *block = NULL; + unsigned32 c = 0; + + while(count > 0) + { + c = MIN(count, (fs_info->vol.bps - ofs)); + + if (c == fs_info->vol.bps) + rc = fat_buf_access(fs_info, blk, FAT_OP_TYPE_GET, &block); + else + rc = fat_buf_access(fs_info, blk, FAT_OP_TYPE_READ, &block); + if (rc != RC_OK) + return rc; + + memcpy((block->buffer + ofs), (buff + cmpltd), c); + + fat_buf_mark_modified(fs_info); + + count -= c; + cmpltd +=c; + blk++; + ofs = 0; + } + return cmpltd; +} + + + + +/* fat_cluster_read -- + * wrapper for reading a whole cluster at once + * + * PARAMETERS: + * mt_entry - mount table entry + * cln - number of cluster to read + * buff - buffer provided by user + * + * RETURNS: + * bytes read on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +fat_cluster_read( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + void *buff + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 fsec = 0; + + fsec = fat_cluster_num_to_sector_num(mt_entry, cln); + + return _fat_block_read(mt_entry, fsec, 0, + fs_info->vol.spc << fs_info->vol.sec_log2, buff); +} + +/* fat_cluster_write -- + * wrapper for writting a whole cluster at once + * + * PARAMETERS: + * mt_entry - mount table entry + * cln - number of cluster to write + * buff - buffer provided by user + * + * RETURNS: + * bytes written on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +fat_cluster_write( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + const void *buff + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 fsec = 0; + + fsec = fat_cluster_num_to_sector_num(mt_entry, cln); + + return _fat_block_write(mt_entry, fsec, 0, + fs_info->vol.spc << fs_info->vol.sec_log2, buff); +} + +/* fat_init_volume_info -- + * Get inforamtion about volume on which filesystem is mounted on + * + * PARAMETERS: + * mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured + * and errno set appropriately + */ +int +fat_init_volume_info(rtems_filesystem_mount_table_entry_t *mt_entry) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + register fat_vol_t *vol = &fs_info->vol; + unsigned32 data_secs = 0; + char boot_rec[FAT_MAX_BPB_SIZE]; + char fs_info_sector[FAT_USEFUL_INFO_SIZE]; + ssize_t ret = 0; + int fd; + struct stat stat_buf; + int i = 0; + + rc = stat(mt_entry->dev, &stat_buf); + if (rc == -1) + return rc; + + /* rtmes feature: no block devices, all are character devices */ + if (!S_ISCHR(stat_buf.st_mode)) + set_errno_and_return_minus_one(ENOTBLK); + + /* check that device is registred as block device and lock it */ + vol->dd = rtems_disk_lookup(stat_buf.st_dev); + if (vol->dd == NULL) + set_errno_and_return_minus_one(ENOTBLK); + + vol->dev = stat_buf.st_dev; + + fd = open(mt_entry->dev, O_RDONLY); + if (fd == -1) + { + rtems_disk_release(vol->dd); + return -1; + } + + ret = read(fd, (void *)boot_rec, FAT_MAX_BPB_SIZE); + if ( ret != FAT_MAX_BPB_SIZE ) + { + close(fd); + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( EIO ); + } + close(fd); + + vol->bps = FAT_BR_BYTES_PER_SECTOR(boot_rec); + + if ( (vol->bps != 512) && + (vol->bps != 1024) && + (vol->bps != 2048) && + (vol->bps != 4096)) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( EINVAL ); + } + + for (vol->sec_mul = 0, i = (vol->bps >> FAT_SECTOR512_BITS); (i & 1) == 0; + i >>= 1, vol->sec_mul++); + for (vol->sec_log2 = 0, i = vol->bps; (i & 1) == 0; + i >>= 1, vol->sec_log2++); + + vol->spc = FAT_BR_SECTORS_PER_CLUSTER(boot_rec); + for (vol->spc_log2 = 0, i = vol->spc; (i & 1) == 0; + i >>= 1, vol->spc_log2++); + + /* + * According to M$ White Paper "bytes per cluster" value + * greater than 32K is invalid + */ + if ((vol->bpc = vol->bps << vol->spc_log2) > MS_BYTES_PER_CLUSTER_LIMIT) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one(EINVAL); + } + + for (vol->bpc_log2 = 0, i = vol->bpc; (i & 1) == 0; + i >>= 1, vol->bpc_log2++); + + vol->fats = FAT_BR_FAT_NUM(boot_rec); + vol->fat_loc = FAT_BR_RESERVED_SECTORS_NUM(boot_rec); + + vol->rdir_entrs = FAT_BR_FILES_PER_ROOT_DIR(boot_rec); + + /* calculate the count of sectors occupied by the root directory */ + vol->rdir_secs = ((vol->rdir_entrs * FAT_DIRENTRY_SIZE) + (vol->bps - 1)) / + vol->bps; + + vol->rdir_size = vol->rdir_secs << vol->sec_log2; + + if ( (FAT_BR_SECTORS_PER_FAT(boot_rec)) != 0) + vol->fat_length = FAT_BR_SECTORS_PER_FAT(boot_rec); + else + vol->fat_length = FAT_BR_SECTORS_PER_FAT32(boot_rec); + + vol->data_fsec = vol->fat_loc + vol->fats * vol->fat_length + + vol->rdir_secs; + + /* for FAT12/16 root dir starts at(sector) */ + vol->rdir_loc = vol->fat_loc + vol->fats * vol->fat_length; + + if ( (FAT_BR_TOTAL_SECTORS_NUM16(boot_rec)) != 0) + vol->tot_secs = FAT_BR_TOTAL_SECTORS_NUM16(boot_rec); + else + vol->tot_secs = FAT_BR_TOTAL_SECTORS_NUM32(boot_rec); + + data_secs = vol->tot_secs - vol->data_fsec; + + vol->data_cls = data_secs / vol->spc; + + /* determine FAT type at least */ + if ( vol->data_cls < FAT_FAT12_MAX_CLN) + { + vol->type = FAT_FAT12; + vol->mask = FAT_FAT12_MASK; + vol->eoc_val = FAT_FAT12_EOC; + } + else + { + if ( vol->data_cls < FAT_FAT16_MAX_CLN) + { + vol->type = FAT_FAT16; + vol->mask = FAT_FAT16_MASK; + vol->eoc_val = FAT_FAT16_EOC; + } + else + { + vol->type = FAT_FAT32; + vol->mask = FAT_FAT32_MASK; + vol->eoc_val = FAT_FAT32_EOC; + } + } + + if (vol->type == FAT_FAT32) + { + vol->rdir_cl = FAT_BR_FAT32_ROOT_CLUSTER(boot_rec); + + vol->mirror = FAT_BR_EXT_FLAGS(boot_rec) & FAT_BR_EXT_FLAGS_MIRROR; + if (vol->mirror) + vol->afat = FAT_BR_EXT_FLAGS(boot_rec) & FAT_BR_EXT_FLAGS_FAT_NUM; + else + vol->afat = 0; + + vol->info_sec = FAT_BR_FAT32_FS_INFO_SECTOR(boot_rec); + if( vol->info_sec == 0 ) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( EINVAL ); + } + else + { + ret = _fat_block_read(mt_entry, vol->info_sec , 0, + FAT_FSI_LEADSIG_SIZE, fs_info_sector); + if ( ret < 0 ) + { + rtems_disk_release(vol->dd); + return -1; + } + + if (FAT_FSINFO_LEAD_SIGNATURE(fs_info_sector) != + FAT_FSINFO_LEAD_SIGNATURE_VALUE) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( EINVAL ); + } + else + { + ret = _fat_block_read(mt_entry, vol->info_sec , FAT_FSI_INFO, + FAT_USEFUL_INFO_SIZE, fs_info_sector); + if ( ret < 0 ) + { + rtems_disk_release(vol->dd); + return -1; + } + + vol->free_cls = FAT_FSINFO_FREE_CLUSTER_COUNT(fs_info_sector); + vol->next_cl = FAT_FSINFO_NEXT_FREE_CLUSTER(fs_info_sector); + rc = fat_fat32_update_fsinfo_sector(mt_entry, 0xFFFFFFFF, + 0xFFFFFFFF); + if ( rc != RC_OK ) + { + rtems_disk_release(vol->dd); + return rc; + } + } + } + } + else + { + vol->rdir_cl = 0; + vol->mirror = 0; + vol->afat = 0; + vol->free_cls = 0xFFFFFFFF; + vol->next_cl = 0xFFFFFFFF; + } + vol->afat_loc = vol->fat_loc + vol->fat_length * vol->afat; + + /* set up collection of fat-files fd */ + fs_info->vhash = calloc(FAT_HASH_SIZE, sizeof(Chain_Control)); + if ( fs_info->vhash == NULL ) + { + rtems_disk_release(vol->dd); + set_errno_and_return_minus_one( ENOMEM ); + } + + for (i = 0; i < FAT_HASH_SIZE; i++) + _Chain_Initialize_empty(fs_info->vhash + i); + + fs_info->rhash = calloc(FAT_HASH_SIZE, sizeof(Chain_Control)); + if ( fs_info->rhash == NULL ) + { + rtems_disk_release(vol->dd); + free(fs_info->vhash); + set_errno_and_return_minus_one( ENOMEM ); + } + for (i = 0; i < FAT_HASH_SIZE; i++) + _Chain_Initialize_empty(fs_info->rhash + i); + + fs_info->uino_pool_size = FAT_UINO_POOL_INIT_SIZE; + fs_info->uino_base = (vol->tot_secs << vol->sec_mul) << 4; + fs_info->index = 0; + fs_info->uino = (char *)calloc(fs_info->uino_pool_size, sizeof(char)); + if ( fs_info->uino == NULL ) + { + rtems_disk_release(vol->dd); + free(fs_info->vhash); + free(fs_info->rhash); + set_errno_and_return_minus_one( ENOMEM ); + } + fs_info->sec_buf = (char *)calloc(vol->bps, sizeof(char)); + if (fs_info->sec_buf == NULL) + { + rtems_disk_release(vol->dd); + free(fs_info->vhash); + free(fs_info->rhash); + free(fs_info->uino); + set_errno_and_return_minus_one( ENOMEM ); + } + + return RC_OK; +} + +/* fat_shutdown_drive -- + * Free all allocated resources and synchronize all necessary data + * + * PARAMETERS: + * mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured + * and errno set appropriately + */ +int +fat_shutdown_drive(rtems_filesystem_mount_table_entry_t *mt_entry) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + int i = 0; + + if (fs_info->vol.type & FAT_FAT32) + { + rc = fat_fat32_update_fsinfo_sector(mt_entry, fs_info->vol.free_cls, + fs_info->vol.next_cl); + if ( rc != RC_OK ) + rc = -1; + } + + fat_buf_release(fs_info); + + if (rtems_bdbuf_syncdev(fs_info->vol.dev) != RTEMS_SUCCESSFUL) + rc = -1; + + for (i = 0; i < FAT_HASH_SIZE; i++) + { + Chain_Node *node = NULL; + Chain_Control *the_chain = fs_info->vhash + i; + + while ( (node = _Chain_Get(the_chain)) != NULL ) + free(node); + } + + for (i = 0; i < FAT_HASH_SIZE; i++) + { + Chain_Node *node = NULL; + Chain_Control *the_chain = fs_info->rhash + i; + + while ( (node = _Chain_Get(the_chain)) != NULL ) + free(node); + } + + free(fs_info->vhash); + free(fs_info->rhash); + + free(fs_info->uino); + free(fs_info->sec_buf); + rtems_disk_release(fs_info->vol.dd); + + if (rc) + errno = EIO; + return rc; +} + +/* fat_init_clusters_chain -- + * Zeroing contents of all clusters in the chain + * + * PARAMETERS: + * mt_entry - mount table entry + * start_cluster_num - num of first cluster in the chain + * + * RETURNS: + * RC_OK on success, or -1 if error occured + * and errno set appropriately + */ +int +fat_init_clusters_chain( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start_cln + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 cur_cln = start_cln; + char *buf; + + buf = calloc(fs_info->vol.bpc, sizeof(char)); + if ( buf == NULL ) + set_errno_and_return_minus_one( EIO ); + + while ((cur_cln & fs_info->vol.mask) != fs_info->vol.eoc_val) + { + ret = fat_cluster_write(mt_entry, cur_cln, buf); + if ( ret == -1 ) + { + free(buf); + return -1; + } + + rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln); + if ( rc != RC_OK ) + { + free(buf); + return rc; + } + + } + free(buf); + return rc; +} + +#define FAT_UNIQ_INO_BASE 0x0FFFFF00 + +#define FAT_UNIQ_INO_IS_BUSY(index, arr) \ + (((arr)[((index)>>3)]>>((index) & (8-1))) & 0x01) + +#define FAT_SET_UNIQ_INO_BUSY(index, arr) \ + ((arr)[((index)>>3)] |= (0x01<<((index) & (8-1)))) + +#define FAT_SET_UNIQ_INO_FREE(index, arr) \ + ((arr)[((index)>>3)] &= (~(0x01<<((index) & (8-1))))) + +/* fat_get_unique_ino -- + * Allocate unique ino from unique ino pool + * + * PARAMETERS: + * mt_entry - mount table entry + * + * RETURNS: + * unique inode number on success, or 0 if there is no free unique inode + * number in the pool + * + * ATTENTION: + * 0 means FAILED !!! + * + */ +unsigned32 +fat_get_unique_ino(rtems_filesystem_mount_table_entry_t *mt_entry) +{ + register fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 j = 0; + rtems_boolean resrc_unsuff = FALSE; + + while (!resrc_unsuff) + { + for (j = 0; j < fs_info->uino_pool_size; j++) + { + if (!FAT_UNIQ_INO_IS_BUSY(fs_info->index, fs_info->uino)) + { + FAT_SET_UNIQ_INO_BUSY(fs_info->index, fs_info->uino); + return (fs_info->uino_base + fs_info->index); + } + fs_info->index++; + if (fs_info->index >= fs_info->uino_pool_size) + fs_info->index = 0; + } + + if ((fs_info->uino_pool_size << 1) < (0x0FFFFFFF - fs_info->uino_base)) + { + fs_info->uino_pool_size <<= 1; + fs_info->uino = realloc(fs_info->uino, fs_info->uino_pool_size); + if (fs_info->uino != NULL) + fs_info->index = fs_info->uino_pool_size; + else + resrc_unsuff = TRUE; + } + else + resrc_unsuff = TRUE; + } + return 0; +} + +/* fat_free_unique_ino -- + * Return unique ino to unique ino pool + * + * PARAMETERS: + * mt_entry - mount table entry + * ino - inode number to free + * + * RETURNS: + * None + */ +void +fat_free_unique_ino( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 ino + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + + FAT_SET_UNIQ_INO_FREE((ino - fs_info->uino_base), fs_info->uino); +} + +/* fat_ino_is_unique -- + * Test whether ino is from unique ino pool + * + * PARAMETERS: + * mt_entry - mount table entry + * ino - ino to be tested + * + * RETURNS: + * TRUE if ino is allocated from unique ino pool, FALSE otherwise + */ +inline rtems_boolean +fat_ino_is_unique( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 ino + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + + return (ino >= fs_info->uino_base); +} + +/* fat_fat32_update_fsinfo_sector -- + * Synchronize fsinfo sector for FAT32 volumes + * + * PARAMETERS: + * mt_entry - mount table entry + * free_count - count of free clusters + * next_free - the next free cluster num + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +fat_fat32_update_fsinfo_sector( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 free_count, + unsigned32 next_free + ) +{ + ssize_t ret1 = 0, ret2 = 0; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 le_free_count = 0; + unsigned32 le_next_free = 0; + + le_free_count = CT_LE_L(free_count); + le_next_free = CT_LE_L(next_free); + + ret1 = _fat_block_write(mt_entry, + fs_info->vol.info_sec, + FAT_FSINFO_FREE_CLUSTER_COUNT_OFFSET, + 4, + (char *)(&le_free_count)); + + ret2 = _fat_block_write(mt_entry, + fs_info->vol.info_sec, + FAT_FSINFO_NEXT_FREE_CLUSTER_OFFSET, + 4, + (char *)(&le_next_free)); + + if ( (ret1 < 0) || (ret2 < 0) ) + return -1; + + return RC_OK; +} + \ No newline at end of file diff --git a/cpukit/libfs/src/dosfs/fat.h b/cpukit/libfs/src/dosfs/fat.h new file mode 100644 index 0000000000..04a1f0f662 --- /dev/null +++ b/cpukit/libfs/src/dosfs/fat.h @@ -0,0 +1,489 @@ +/* + * fat.h + * + * Constants/data structures/prototypes for low-level operations on a volume + * with FAT filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#ifndef __DOSFS_FAT_H__ +#define __DOSFS_FAT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* XXX: temporary hack :(( */ +#ifndef set_errno_and_return_minus_one +#define set_errno_and_return_minus_one rtems_set_errno_and_return_minus_one +#endif /* set_errno_and_return_minus_one */ + +#include +#include +#include + +#define DBG1(x) x +#define DBG2(x) x + +#ifndef RC_OK +#define RC_OK 0x00000000 +#endif + +/* + * Remember that all FAT file system on disk data structure is + * "little endian"! + * (derived from linux) + */ +/* + * Conversion from and to little-endian byte order. (no-op on i386/i486) + * + * Naming: Ca_b_c, where a: F = from, T = to, b: LE = little-endian, + * BE = big-endian, c: W = word (16 bits), L = longword (32 bits) + */ + +#if (CPU_BIG_ENDIAN == TRUE) +# define CF_LE_W(v) CPU_swap_u16(v) +# define CF_LE_L(v) CPU_swap_u32(v) +# define CT_LE_W(v) CPU_swap_u16(v) +# define CT_LE_L(v) CPU_swap_u32(v) +#else +# define CF_LE_W(v) (v) +# define CF_LE_L(v) (v) +# define CT_LE_W(v) (v) +# define CT_LE_L(v) (v) +#endif + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#define FAT_HASH_SIZE 2 +#define FAT_HASH_MODULE FAT_HASH_SIZE + + +#define FAT_SECTOR512_SIZE 512 /* sector size (bytes) */ +#define FAT_SECTOR512_BITS 9 /* log2(SECTOR_SIZE) */ + +/* maximum + 1 number of clusters for FAT12 */ +#define FAT_FAT12_MAX_CLN 4085 + +/* maximum + 1 number of clusters for FAT16 */ +#define FAT_FAT16_MAX_CLN 65525 + +#define FAT_FAT12 0x01 +#define FAT_FAT16 0x02 +#define FAT_FAT32 0x04 + +#define FAT_UNDEFINED_VALUE 0xFFFFFFFF + +#define FAT_FAT12_EOC 0x0FFF +#define FAT_FAT16_EOC 0xFFFF +#define FAT_FAT32_EOC 0x0FFFFFFF + +#define FAT_FAT12_FREE 0x0000 +#define FAT_FAT16_FREE 0x0000 +#define FAT_FAT32_FREE 0x00000000 + +#define FAT_GENFAT_EOC 0xFFFFFFFF +#define FAT_GENFAT_FREE 0x00000000 + +#define FAT_FAT12_SHIFT 0x04 + +#define FAT_FAT12_MASK 0x00000FFF +#define FAT_FAT16_MASK 0x0000FFFF +#define FAT_FAT32_MASK 0x0FFFFFFF + +#define FAT_MAX_BPB_SIZE 90 + +/* size of useful information in FSInfo sector */ +#define FAT_USEFUL_INFO_SIZE 12 + +#define FAT_VAL8(x, ofs) (unsigned8)(*((unsigned8 *)(x) + (ofs))) + +#define FAT_VAL16(x, ofs) \ + (unsigned16)( (*((unsigned8 *)(x) + (ofs))) | \ + ((*((unsigned8 *)(x) + (ofs) + 1)) << 8) ) + +#define FAT_VAL32(x, ofs) \ + (unsigned32)( (*((unsigned8 *)(x) + (ofs))) | \ + ((*((unsigned8 *)(x) + (ofs) + 1)) << 8) | \ + ((*((unsigned8 *)(x) + (ofs) + 2)) << 16) | \ + ((*((unsigned8 *)(x) + (ofs) + 3)) << 24) ) + +/* macros to access boot sector fields */ +#define FAT_BR_BYTES_PER_SECTOR(x) FAT_VAL16(x, 11) +#define FAT_BR_SECTORS_PER_CLUSTER(x) FAT_VAL8(x, 13) +#define FAT_BR_RESERVED_SECTORS_NUM(x) FAT_VAL16(x, 14) +#define FAT_BR_FAT_NUM(x) FAT_VAL8(x, 16) +#define FAT_BR_FILES_PER_ROOT_DIR(x) FAT_VAL16(x, 17) +#define FAT_BR_TOTAL_SECTORS_NUM16(x) FAT_VAL16(x, 19) +#define FAT_BR_MEDIA(x) FAT_VAL8(x, 21) +#define FAT_BR_SECTORS_PER_FAT(x) FAT_VAL16(x, 22) +#define FAT_BR_TOTAL_SECTORS_NUM32(x) FAT_VAL32(x, 32) +#define FAT_BR_SECTORS_PER_FAT32(x) FAT_VAL32(x, 36) +#define FAT_BR_EXT_FLAGS(x) FAT_VAL16(x, 40) +#define FAT_BR_FAT32_ROOT_CLUSTER(x) FAT_VAL32(x, 44) +#define FAT_BR_FAT32_FS_INFO_SECTOR(x) FAT_VAL16(x, 48) +#define FAT_FSINFO_LEAD_SIGNATURE(x) FAT_VAL32(x, 0) +/* + * I read FSInfo sector from offset 484 to access the information, so offsets + * of these fields a relative + */ +#define FAT_FSINFO_FREE_CLUSTER_COUNT(x) FAT_VAL32(x, 4) +#define FAT_FSINFO_NEXT_FREE_CLUSTER(x) FAT_VAL32(x, 8) + +#define FAT_FSINFO_FREE_CLUSTER_COUNT_OFFSET 488 + +#define FAT_FSINFO_NEXT_FREE_CLUSTER_OFFSET 492 + +#define FAT_RSRVD_CLN 0x02 + +#define FAT_FSINFO_LEAD_SIGNATURE_VALUE 0x41615252 + +#define FAT_FSI_LEADSIG_SIZE 0x04 + +#define FAT_FSI_INFO 484 + +#define MS_BYTES_PER_CLUSTER_LIMIT 0x8000 /* 32K */ + +#define FAT_BR_EXT_FLAGS_MIRROR 0x0080 + +#define FAT_BR_EXT_FLAGS_FAT_NUM 0x000F + + +#define FAT_DIRENTRY_SIZE 32 + +#define FAT_DIRENTRIES_PER_SEC512 16 + +/* + * Volume descriptor + * Description of the volume the FAT filesystem is located on - generally + * the fields of the structure corresponde to Boot Sector and BPB Srtucture + * (see M$ White Paper) fields + */ +typedef struct fat_vol_s +{ + unsigned16 bps; /* bytes per sector */ + unsigned8 sec_log2; /* log2 of bps */ + unsigned8 sec_mul; /* log2 of 512bts sectors number per sector */ + unsigned8 spc; /* sectors per cluster */ + unsigned8 spc_log2; /* log2 of spc */ + unsigned16 bpc; /* bytes per cluster */ + unsigned8 bpc_log2; /* log2 of bytes per cluster */ + unsigned8 fats; /* number of FATs */ + unsigned8 type; /* FAT type */ + unsigned32 mask; + unsigned32 eoc_val; + unsigned16 fat_loc; /* FAT start */ + unsigned32 fat_length; /* sectors per FAT */ + unsigned32 rdir_loc; /* root directory start */ + unsigned16 rdir_entrs; /* files per root directory */ + unsigned32 rdir_secs; /* sectors per root directory */ + unsigned32 rdir_size; /* root directory size in bytes */ + unsigned32 tot_secs; /* total count of sectors */ + unsigned32 data_fsec; /* first data sector */ + unsigned32 data_cls; /* count of data clusters */ + unsigned32 rdir_cl; /* first cluster of the root directory */ + unsigned16 info_sec; /* FSInfo Sector Structure location */ + unsigned32 free_cls; /* last known free clusters count */ + unsigned32 next_cl; /* next free cluster number */ + unsigned8 mirror; /* mirroring enabla/disable */ + unsigned32 afat_loc; /* active FAT location */ + unsigned8 afat; /* the number of active FAT */ + dev_t dev; /* device ID */ + disk_device *dd; /* disk device (see libblock) */ + void *private_data; /* reserved */ +} fat_vol_t; + + +typedef struct fat_cache_s +{ + unsigned32 blk_num; + rtems_boolean modified; + unsigned8 state; + bdbuf_buffer *buf; +} fat_cache_t; + +/* + * This structure identifies the instance of the filesystem on the FAT + * ("fat-file") level. + */ +typedef struct fat_fs_info_s +{ + fat_vol_t vol; /* volume descriptor */ + Chain_Control *vhash; /* "vhash" of fat-file descriptors */ + Chain_Control *rhash; /* "rhash" of fat-file descriptors */ + char *uino; /* array of unique ino numbers */ + unsigned32 index; + unsigned32 uino_pool_size; /* size */ + unsigned32 uino_base; + fat_cache_t c; /* cache */ + unsigned8 *sec_buf; /* just placeholder for anything */ +} fat_fs_info_t; + +/* + * if the name we looking for is file we store not only first data cluster + * number, but and cluster number and offset for directory entry for this + * name + */ +typedef struct fat_auxiliary_s +{ + unsigned32 cln; + unsigned32 ofs; +} fat_auxiliary_t; + +#define FAT_FAT_OFFSET(fat_type, cln) \ + ((fat_type) & FAT_FAT12 ? ((cln) + ((cln) >> 1)) : \ + (fat_type) & FAT_FAT16 ? ((cln) << 1) : \ + ((cln) << 2)) + +#define FAT_CLUSTER_IS_ODD(n) ((n) & 0x0001) + +#define FAT12_SHIFT 0x4 /* half of a byte */ + +/* initial size of array of unique ino */ +#define FAT_UINO_POOL_INIT_SIZE 0x100 + +/* cache support */ +#define FAT_CACHE_EMPTY 0x0 +#define FAT_CACHE_ACTUAL 0x1 + +#define FAT_OP_TYPE_READ 0x1 +#define FAT_OP_TYPE_GET 0x2 + +static inline unsigned32 +fat_cluster_num_to_sector_num( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln + ) +{ + register fat_fs_info_t *fs_info = mt_entry->fs_info; + + if ( (cln == 0) && (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)) ) + return fs_info->vol.rdir_loc; + + return (((cln - FAT_RSRVD_CLN) << fs_info->vol.spc_log2) + + fs_info->vol.data_fsec); +} + +static inline unsigned32 +fat_cluster_num_to_sector512_num( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + + if (cln == 1) + return 1; + + return (fat_cluster_num_to_sector_num(mt_entry, cln) << + fs_info->vol.sec_mul); +} + +static inline int +fat_buf_access(fat_fs_info_t *fs_info, unsigned32 blk, int op_type, + bdbuf_buffer **buf) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + unsigned8 i; + rtems_boolean sec_of_fat; + + + if (fs_info->c.state == FAT_CACHE_EMPTY) + { + if (op_type == FAT_OP_TYPE_READ) + sc = rtems_bdbuf_read(fs_info->vol.dev, blk, &fs_info->c.buf); + else + sc = rtems_bdbuf_get(fs_info->vol.dev, blk, &fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + fs_info->c.blk_num = blk; + fs_info->c.state = FAT_CACHE_ACTUAL; + } + + sec_of_fat = ((fs_info->c.blk_num >= fs_info->vol.fat_loc) && + (fs_info->c.blk_num < fs_info->vol.rdir_loc)); + + if (fs_info->c.blk_num != blk) + { + if (fs_info->c.modified) + { + if (sec_of_fat && !fs_info->vol.mirror) + memcpy(fs_info->sec_buf, fs_info->c.buf->buffer, + fs_info->vol.bps); + + sc = rtems_bdbuf_release_modified(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + fs_info->c.modified = 0; + + if (sec_of_fat && !fs_info->vol.mirror) + { + bdbuf_buffer *b; + + for (i = 1; i < fs_info->vol.fats; i++) + { + sc = rtems_bdbuf_get(fs_info->vol.dev, + fs_info->c.blk_num + + fs_info->vol.fat_length * i, + &b); + if ( sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(ENOMEM); + memcpy(b->buffer, fs_info->sec_buf, fs_info->vol.bps); + sc = rtems_bdbuf_release_modified(b); + if ( sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(ENOMEM); + } + } + } + else + { + sc = rtems_bdbuf_release(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + } + if (op_type == FAT_OP_TYPE_READ) + sc = rtems_bdbuf_read(fs_info->vol.dev, blk, &fs_info->c.buf); + else + sc = rtems_bdbuf_get(fs_info->vol.dev, blk, &fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + fs_info->c.blk_num = blk; + } + *buf = fs_info->c.buf; + return RC_OK; +} + + +static inline int +fat_buf_release(fat_fs_info_t *fs_info) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + unsigned8 i; + rtems_boolean sec_of_fat; + + if (fs_info->c.state == FAT_CACHE_EMPTY) + return RC_OK; + + sec_of_fat = ((fs_info->c.blk_num >= fs_info->vol.fat_loc) && + (fs_info->c.blk_num < fs_info->vol.rdir_loc)); + + if (fs_info->c.modified) + { + if (sec_of_fat && !fs_info->vol.mirror) + memcpy(fs_info->sec_buf, fs_info->c.buf->buffer, fs_info->vol.bps); + + sc = rtems_bdbuf_release_modified(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + fs_info->c.modified = 0; + + if (sec_of_fat && !fs_info->vol.mirror) + { + bdbuf_buffer *b; + + for (i = 1; i < fs_info->vol.fats; i++) + { + sc = rtems_bdbuf_get(fs_info->vol.dev, + fs_info->c.blk_num + + fs_info->vol.fat_length * i, + &b); + if ( sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(ENOMEM); + memcpy(b->buffer, fs_info->sec_buf, fs_info->vol.bps); + sc = rtems_bdbuf_release_modified(b); + if ( sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(ENOMEM); + } + } + } + else + { + sc = rtems_bdbuf_release(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + } + fs_info->c.state = FAT_CACHE_EMPTY; + return RC_OK; +} + +static inline void +fat_buf_mark_modified(fat_fs_info_t *fs_info) +{ + fs_info->c.modified = TRUE; +} + + + +ssize_t +_fat_block_read(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start, + unsigned32 offset, + unsigned32 count, + void *buff); + +ssize_t +_fat_block_write(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start, + unsigned32 offset, + unsigned32 count, + const void *buff); + +ssize_t +fat_cluster_read(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + void *buff); + +ssize_t +fat_cluster_write(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + const void *buff); + +int +fat_init_volume_info(rtems_filesystem_mount_table_entry_t *mt_entry); + +int +fat_init_clusters_chain(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 start_cln); + +unsigned32 +fat_cluster_num_to_sector_num(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln); + +int +fat_shutdown_drive(rtems_filesystem_mount_table_entry_t *mt_entry); + + +unsigned32 +fat_get_unique_ino(rtems_filesystem_mount_table_entry_t *mt_entry); + +rtems_boolean +fat_ino_is_unique(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 ino); + +void +fat_free_unique_ino(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 ino); + +int +fat_fat32_update_fsinfo_sector( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 free_count, + unsigned32 next_free + ); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_FAT_H__ */ diff --git a/cpukit/libfs/src/dosfs/fat_fat_operations.c b/cpukit/libfs/src/dosfs/fat_fat_operations.c new file mode 100644 index 0000000000..49b2ab70d3 --- /dev/null +++ b/cpukit/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 + * + * @(#) $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) + { + /* + * 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; +} diff --git a/cpukit/libfs/src/dosfs/fat_fat_operations.h b/cpukit/libfs/src/dosfs/fat_fat_operations.h new file mode 100644 index 0000000000..59b6a84018 --- /dev/null +++ b/cpukit/libfs/src/dosfs/fat_fat_operations.h @@ -0,0 +1,58 @@ +/* + * fat_fat_operations.h + * + * Constants/data structures/prototypes for operations on Files Allocation + * Table + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_FAT_FAT_OPERATIONS_H__ +#define __DOSFS_FAT_FAT_OPERATIONS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include "fat.h" + +int +fat_get_fat_cluster(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + unsigned32 *ret_val); + +int +fat_set_fat_cluster(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + unsigned32 in_val); + +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 +fat_free_fat_clusters_chain( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 chain +); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_FAT_FAT_OPERATIONS_H__ */ diff --git a/cpukit/libfs/src/dosfs/fat_file.c b/cpukit/libfs/src/dosfs/fat_file.c new file mode 100644 index 0000000000..4fd8a5024f --- /dev/null +++ b/cpukit/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 + * + * @(#) $Id$ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; +} diff --git a/cpukit/libfs/src/dosfs/fat_file.h b/cpukit/libfs/src/dosfs/fat_file.h new file mode 100644 index 0000000000..2821a27cf7 --- /dev/null +++ b/cpukit/libfs/src/dosfs/fat_file.h @@ -0,0 +1,195 @@ +/* + * fat_file.h + * + * Constants/data structures/prototypes for operations on "fat-file" + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_FAT_FILE_H__ +#define __DOSFS_FAT_FILE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +/* "fat-file" representation + * + * the idea is: fat-file is nothing but a cluster chain, any open fat-file is + * represented in system by fat-file descriptor and has well-known + * file interface: + * + * fat_file_open() + * fat_file_close() + * fat_file_read() + * fat_file_write() + * + * Such interface hides the architecture of fat-file and represents it like + * linear file + */ + +typedef rtems_filesystem_node_types_t fat_file_type_t; + +#define FAT_DIRECTORY RTEMS_FILESYSTEM_DIRECTORY +#define FAT_FILE RTEMS_FILESYSTEM_MEMORY_FILE + +typedef struct fat_file_map_s +{ + unsigned32 file_cln; + unsigned32 disk_cln; + unsigned32 last_cln; +} fat_file_map_t; +/* + * descriptor of a fat-file + * + * To each particular clusters chain + */ +typedef struct fat_file_fd_s +{ + Chain_Node link; /* + * fat-file descriptors organized into hash; + * collision lists are handled via link + * field + */ + unsigned32 links_num; /* + * the number of fat_file_open call on + * this fat-file + */ + unsigned32 ino; /* inode, file serial number :)))) */ + fat_file_type_t fat_file_type; + unsigned32 size_limit; + unsigned32 fat_file_size; /* length */ + unsigned32 info_cln; + unsigned32 cln; + unsigned16 info_ofs; + unsigned char first_char; + unsigned8 flags; + fat_file_map_t map; + time_t mtime; + +} fat_file_fd_t; + + +#define FAT_FILE_REMOVED 0x01 + +#define FAT_FILE_IS_REMOVED(p)\ + (((p)->flags & FAT_FILE_REMOVED) ? 1 : 0) + +/* ioctl macros */ +#define F_CLU_NUM 0x01 + +/* + * Each file and directory on a MSDOS volume is unique identified by it + * location, i.e. location of it 32 Bytes Directory Entry Structure. We can + * distinguish them by cluster number it locates on and offset inside this + * cluster. But root directory on any volumes (FAT12/16/32) has no 32 Bytes + * Directory Entry Structure corresponded to it. So we assume 32 Bytes + * Directory Entry Structure of root directory locates at cluster 1 (invalid + * cluaster number) and offset 0 + */ +#define FAT_ROOTDIR_CLUSTER_NUM 0x01 + +#define FAT_FD_OF_ROOT_DIR(fat_fd) \ + ((fat_fd->info_cln == FAT_ROOTDIR_CLUSTER_NUM ) && \ + (fat_fd->info_ofs == 0)) + +#define FAT_EOF 0x00 + +/* fat_construct_key -- + * Construct key for hash access: convert (cluster num, offset) to + * (sector512 num, new offset) and than construct key as + * key = (sector512 num) << 4 | (new offset) + * + * PARAMETERS: + * cl - cluster number + * ofs - offset inside cluster 'cl' + * mt_entry - mount table entry + * + * RETURNS: + * constructed key + */ +static inline unsigned32 +fat_construct_key( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cl, + unsigned32 ofs) +{ + return ( ((fat_cluster_num_to_sector512_num(mt_entry, cl) + + (ofs >> FAT_SECTOR512_BITS)) << 4) + + ((ofs >> 5) & (FAT_DIRENTRIES_PER_SEC512 - 1)) ); +} + +/* Prototypes for "fat-file" operations */ +int +fat_file_open(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + unsigned32 ofs, + fat_file_fd_t **fat_fd); + +int +fat_file_reopen(fat_file_fd_t *fat_fd); + +int +fat_file_close(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +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); + +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 +fat_file_extend(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 new_length, + unsigned32 *a_length); + +int +fat_file_truncate(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 new_length); + +int +fat_file_datasync(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + + +int +fat_file_ioctl(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + int cmd, + ...); + +int +fat_file_size(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +void +fat_file_mark_removed(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_FAT_FILE_H__ */ diff --git a/cpukit/libfs/src/dosfs/msdos.h b/cpukit/libfs/src/dosfs/msdos.h new file mode 100644 index 0000000000..a9216b1ed3 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos.h @@ -0,0 +1,408 @@ +/* + * msdos.h + * + * The MSDOS filesystem constants/data structures/prototypes + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_MSDOS_H__ +#define __DOSFS_MSDOS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "fat.h" +#include "fat_file.h" + +#ifndef RC_OK +#define RC_OK 0x00000000 +#endif + +#define MSDOS_NAME_NOT_FOUND_ERR 0xDD000001 + +/* + * This structure identifies the instance of the filesystem on the MSDOS + * level. + */ +typedef struct msdos_fs_info_s +{ + fat_fs_info_t fat; /* + * volume + * description + */ + rtems_filesystem_file_handlers_r *directory_handlers; /* + * a set of routines + * that handles the + * nodes of directory + * type + */ + rtems_filesystem_file_handlers_r *file_handlers; /* + * a set of routines + * that handles the + * nodes of file + * type + */ + rtems_id vol_sema; /* + * semaphore + * associated with + * the volume + */ + unsigned8 *cl_buf; /* + * just placeholder + * for anything + */ +} msdos_fs_info_t; + +/* a set of routines that handle the nodes which are directories */ +extern rtems_filesystem_file_handlers_r msdos_dir_handlers; + +/* a set of routines that handle the nodes which are files */ +extern rtems_filesystem_file_handlers_r msdos_file_handlers; + +/* Volume semaphore timeout value */ +#define MSDOS_VOLUME_SEMAPHORE_TIMEOUT 100 + +/* Node types */ +#define MSDOS_DIRECTORY RTEMS_FILESYSTEM_DIRECTORY +#define MSDOS_REGULAR_FILE RTEMS_FILESYSTEM_MEMORY_FILE + +typedef rtems_filesystem_node_types_t msdos_node_type_t; + +/* + * Macros for fetching fields from 32 bytes long FAT Directory Entry + * Structure (see M$ White Paper) + */ +#define MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE 32 /* 32 bytes */ + +#define MSDOS_DIR_NAME(x) (unsigned8 *)((x) + 0) +#define MSDOS_DIR_ATTR(x) (unsigned8 *)((x) + 11) +#define MSDOS_DIR_NT_RES(x) (unsigned8 *)((x) + 12) +#define MSDOS_DIR_CRT_TIME_TENTH(x) (unsigned8 *)((x) + 13) +#define MSDOS_DIR_CRT_TIME(x) (unsigned16 *)((x) + 14) +#define MSDOS_DIR_CRT_DATE(x) (unsigned16 *)((x) + 16) +#define MSDOS_DIR_LAST_ACCESS_DATE(x) (unsigned16 *)((x) + 18) +#define MSDOS_DIR_FIRST_CLUSTER_HI(x) (unsigned16 *)((x) + 20) +#define MSDOS_DIR_WRITE_TIME(x) (unsigned16 *)((x) + 22) +#define MSDOS_DIR_WRITE_DATE(x) (unsigned16 *)((x) + 24) +#define MSDOS_DIR_FIRST_CLUSTER_LOW(x) (unsigned16 *)((x) + 26) +#define MSDOS_DIR_FILE_SIZE(x) (unsigned32 *)((x) + 28) + +#define MSDOS_EXTRACT_CLUSTER_NUM(p) \ + (unsigned32)( (CF_LE_W(*MSDOS_DIR_FIRST_CLUSTER_LOW(p))) | \ + ((CF_LE_W((*MSDOS_DIR_FIRST_CLUSTER_HI(p))))<<16) ) + +/* + * Fields offset in 32 bytes long FAT Directory Entry + * Structure (see M$ White Paper) + */ +#define MSDOS_FILE_SIZE_OFFSET 28 +#define MSDOS_FILE_NAME_OFFSET 0 +#define MSDOS_FIRST_CLUSTER_HI_OFFSET 20 +#define MSDOS_FIRST_CLUSTER_LOW_OFFSET 26 +#define MSDOS_FILE_WDATE_OFFSET 24 +#define MSDOS_FILE_WTIME_OFFSET 22 + +/* + * Possible values of DIR_Attr field of 32 bytes long FAT Directory Entry + * Structure (see M$ White Paper) + */ +#define MSDOS_ATTR_READ_ONLY 0x01 +#define MSDOS_ATTR_HIDDEN 0x02 +#define MSDOS_ATTR_SYSTEM 0x04 +#define MSDOS_ATTR_VOLUME_ID 0x08 +#define MSDOS_ATTR_DIRECTORY 0x10 +#define MSDOS_ATTR_ARCHIVE 0x20 + +/* + * Possible values of DIR_Name[0] field of 32 bytes long FAT Directory Entry + * Structure (see M$ White Paper) + */ +#define MSDOS_THIS_DIR_ENTRY_EMPTY 0xE5 +#define MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY 0x00 + + +/* + * Macros for names parsing and formatting + */ +#define msdos_is_valid_name_char(_ch) (1) +#define msdos_is_separator(_ch) rtems_filesystem_is_separator(_ch) + +#define MSDOS_SHORT_NAME_LEN 11 /* 11 characters */ +#define MSDOS_NAME_MAX MSDOS_SHORT_NAME_LEN +#define MSDOS_NAME_MAX_WITH_DOT (MSDOS_NAME_MAX + 1) + +#define MSDOS_DOT_NAME ". " /* ".", padded to MSDOS_NAME chars */ +#define MSDOS_DOTDOT_NAME ".. " /* "..", padded to MSDOS_NAME chars */ + +typedef enum msdos_token_types_e +{ + MSDOS_NO_MORE_PATH, + MSDOS_CURRENT_DIR, + MSDOS_UP_DIR, + MSDOS_NAME, + MSDOS_INVALID_TOKEN +} msdos_token_types_t; + +/* Others macros */ +#define MSDOS_RES_NT_VALUE 0x00 +#define MSDOS_INIT_DIR_SIZE 0x00 + +/* "dot" entry offset in a directory */ +#define MSDOS_DOT_DIR_ENTRY_OFFSET 0x00 /* first entry in directory */ + +/* "dotdot" entry offset in a directory */ +#define MSDOS_DOTDOT_DIR_ENTRY_OFFSET 0x20 /* second entry in directory */ + +/* 'p' should be char* */ +#define DOT_NODE_P(p) ((char *)(p)) +#define DOTDOT_NODE_P(p) ((char *)((p) + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)) + +/* Size limits for files and directories (see M$ White Paper) */ +#define MSDOS_MAX_DIR_LENGHT 0x200000 /* 2,097,152 bytes */ +#define MSDOS_MAX_FILE_SIZE 0xFFFFFFFF /* 4 Gb */ + +/* + * The number of 32 bytes long FAT Directory Entry + * Structures per 512 bytes sector + */ +#define MSDOS_DPS512_NUM 16 + +/* Prototypes */ +int +msdos_initialize(rtems_filesystem_mount_table_entry_t *temp_mt_entry); + +int +msdos_shut_down(rtems_filesystem_mount_table_entry_t *temp_mt_entry); + +int +msdos_eval_path(const char *pathname, /* IN */ + int flags, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */); + +int +msdos_eval4make(const char *path, /* IN */ + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + const char **name /* OUT */); + +int +msdos_unlink(rtems_filesystem_location_info_t *pathloc /* IN */); + +int +msdos_free_node_info(rtems_filesystem_location_info_t *pathloc /* IN */); + +rtems_filesystem_node_types_t +msdos_node_type(rtems_filesystem_location_info_t *pathloc); + +int +msdos_mknod(const char *path, /* IN */ + mode_t mode, /* IN */ + dev_t dev, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */); + +int +msdos_utime(rtems_filesystem_location_info_t *pathloc, /* IN */ + time_t actime, /* IN */ + time_t modtime /* IN */); + +int +msdos_initialize_support( + rtems_filesystem_mount_table_entry_t *temp_mt_entry, + rtems_filesystem_operations_table *op_table, + rtems_filesystem_file_handlers_r *file_handlers, + rtems_filesystem_file_handlers_r *directory_handlers +); + +int +msdos_file_open( + rtems_libio_t *iop, /* IN */ + const char *pathname, /* IN */ + unsigned32 flag, /* IN */ + unsigned32 mode /* IN */ +); + +int +msdos_file_close(rtems_libio_t *iop /* IN */); + +ssize_t +msdos_file_read( + rtems_libio_t *iop, /* IN */ + void *buffer, /* IN */ + unsigned32 count /* IN */ +); + +ssize_t +msdos_file_write( + rtems_libio_t *iop, /* IN */ + const void *buffer, /* IN */ + unsigned32 count /* IN */ +); + +int +msdos_file_lseek( + rtems_libio_t *iop, /* IN */ + off_t offset, /* IN */ + int whence /* IN */ +); + +int +msdos_file_stat(rtems_filesystem_location_info_t *loc, /* IN */ + struct stat *buf /* OUT */); + +int +msdos_file_ftruncate( + rtems_libio_t *iop, /* IN */ + off_t length /* IN */ +); + +int +msdos_file_sync(rtems_libio_t *iop); + +int +msdos_file_datasync(rtems_libio_t *iop); + +int +msdos_file_ioctl( + rtems_libio_t *iop, /* IN */ + unsigned32 command, /* IN */ + void *buffer /* IN */ +); + +int +msdos_file_rmnod(rtems_filesystem_location_info_t *pathloc /* IN */); + +int +msdos_dir_open( + rtems_libio_t *iop, /* IN */ + const char *pathname, /* IN */ + unsigned32 flag, /* IN */ + unsigned32 mode /* IN */ +); + +int +msdos_dir_close(rtems_libio_t *iop /* IN */); + +ssize_t +msdos_dir_read( + rtems_libio_t *iop, /* IN */ + void *buffer, /* IN */ + unsigned32 count /* IN */ +); + +int +msdos_dir_lseek( + rtems_libio_t *iop, /* IN */ + off_t offset, /* IN */ + int whence /* IN */ +); + +int +msdos_dir_rmnod(rtems_filesystem_location_info_t *pathloc /* IN */); + +int +msdos_dir_sync(rtems_libio_t *iop); + +int +msdos_dir_stat( + rtems_filesystem_location_info_t *loc, /* IN */ + struct stat *buf /* OUT */ +); + +int +msdos_creat_node(rtems_filesystem_location_info_t *parent_loc, + msdos_node_type_t type, + char *name, + mode_t mode); + +/* Misc prototypes */ +msdos_token_types_t msdos_get_token(const char *path, + char *token, + int *token_len); + +int +msdos_find_name(rtems_filesystem_location_info_t *parent_loc, + char *name); + +int +msdos_get_name_node(rtems_filesystem_location_info_t *parent_loc, + char *name, + fat_auxiliary_t *paux, + char *name_dir_entry); + +int +msdos_dir_info_remove(rtems_filesystem_location_info_t *pathloc); + +void +msdos_date_unix2dos(int unix_date, + unsigned short *time_val, + unsigned short *date); + +unsigned int +msdos_date_dos2unix(unsigned short time_val, unsigned short date); + +int +msdos_set_first_cluster_num(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +int +msdos_set_file_size(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd); + +int +msdos_set_first_char4file_name(rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cl, + unsigned32 ofs, + unsigned char first_char); + +int +msdos_set_dir_wrt_time_and_date( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd +); + + +int +msdos_dir_is_empty(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + rtems_boolean *ret_val); + +int +msdos_find_name_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + char *name, + fat_auxiliary_t *paux, + char *name_dir_entry); + +int +msdos_find_node_by_cluster_num_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + unsigned32 cl4find, + fat_auxiliary_t *paux, + char *dir_entry +); + +int +msdos_get_dotdot_dir_info_cluster_num_and_offset( + rtems_filesystem_mount_table_entry_t *mt_entry, + unsigned32 cln, + fat_auxiliary_t *paux, + char *dir_entry +); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_MSDOS_H__ */ diff --git a/cpukit/libfs/src/dosfs/msdos_create.c b/cpukit/libfs/src/dosfs/msdos_create.c new file mode 100644 index 0000000000..4b4c7001ca --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_create.c @@ -0,0 +1,208 @@ +/* + * Routine to create a new MSDOS filesystem node + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + * + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_creat_node -- + * Create a new node. If a new node is file, FAT 32 Bytes Directory + * Entry Structure (see M$ White Paper) is initialized, free space is + * found in parent directory and structure is written to the disk. + * In case of directory, all above steps present and also new cluster + * is allocated for a new directory and dot and dotdot nodes are created + * in alloceted cluster. + * + * PARAMETERS: + * parent_loc - parent (directory we are going to create node in) + * type - new node type (file or directory) + * name - new node name + * mode - mode + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately). + * + */ +int +msdos_creat_node( + rtems_filesystem_location_info_t *parent_loc, + msdos_node_type_t type, + char *name, + mode_t mode + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = parent_loc->mt_entry->fs_info; + fat_file_fd_t *parent_fat_fd = parent_loc->node_access; + fat_file_fd_t *fat_fd = NULL; + time_t time_ret = 0; + unsigned16 time_val = 0; + unsigned16 date = 0; + fat_auxiliary_t aux; + unsigned char new_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + unsigned char dot_dotdot[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2]; + + memset(new_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memset(dot_dotdot, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2); + + /* set up name */ + strncpy(MSDOS_DIR_NAME(new_node), name, MSDOS_NAME_MAX); + + /* fill reserved field */ + *MSDOS_DIR_NT_RES(new_node) = MSDOS_RES_NT_VALUE; + + /* set up last write date and time */ + time_ret = time(NULL); + if ( time_ret == -1 ) + return -1; + + msdos_date_unix2dos(time_ret, &time_val, &date); + *MSDOS_DIR_WRITE_TIME(new_node) = CT_LE_W(time_val); + *MSDOS_DIR_WRITE_DATE(new_node) = CT_LE_W(date); + + /* initialize directory/file size */ + *MSDOS_DIR_FILE_SIZE(new_node) = MSDOS_INIT_DIR_SIZE; + + if (type == MSDOS_DIRECTORY) + *MSDOS_DIR_ATTR(new_node) |= MSDOS_ATTR_DIRECTORY; + else + *MSDOS_DIR_ATTR(new_node) |= MSDOS_ATTR_ARCHIVE; + + /* + * find free space in the parent directory and write new initialized + * FAT 32 Bytes Directory Entry Structure (see M$ White Paper) + * to the disk + */ + rc = msdos_get_name_node(parent_loc, NULL, &aux, new_node); + if ( rc != RC_OK ) + return rc; + + /* + * if we create a new file we are done, if directory there are more steps + * to do + */ + if (type == MSDOS_DIRECTORY) + { + /* open new directory as fat-file */ + rc = fat_file_open(parent_loc->mt_entry, aux.cln, aux.ofs, &fat_fd); + if (rc != RC_OK) + goto err; + + /* + * we opened fat-file for node we just created, so initialize fat-file + * descritor + */ + fat_fd->info_cln = aux.cln; + fat_fd->info_ofs = aux.ofs; + fat_fd->fat_file_size = 0; + fat_fd->fat_file_type = FAT_DIRECTORY; + fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT; + + /* + * dot and dotdot entries are identical to new node except the + * names + */ + memcpy(DOT_NODE_P(dot_dotdot), new_node, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memcpy(DOTDOT_NODE_P(dot_dotdot), new_node, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memcpy(MSDOS_DIR_NAME(DOT_NODE_P(dot_dotdot)), MSDOS_DOT_NAME, + MSDOS_NAME_MAX); + memcpy(MSDOS_DIR_NAME(DOTDOT_NODE_P(dot_dotdot)), MSDOS_DOTDOT_NAME, + MSDOS_NAME_MAX); + + /* set up cluster num for dotdot entry */ + /* + * here we can ommit FAT32 condition because for all FAT types dirs + * right under root dir should contain 0 in dotdot entry but for + * FAT12/16 parent_fat_fd->cluster_num always contains such value + */ + if ((FAT_FD_OF_ROOT_DIR(parent_fat_fd)) && + (fs_info->fat.vol.type & FAT_FAT32)) + { + *MSDOS_DIR_FIRST_CLUSTER_LOW(DOTDOT_NODE_P(dot_dotdot)) = 0x0000; + *MSDOS_DIR_FIRST_CLUSTER_HI(DOTDOT_NODE_P(dot_dotdot)) = 0x0000; + } + else + { + *MSDOS_DIR_FIRST_CLUSTER_LOW(DOTDOT_NODE_P(dot_dotdot)) = + CT_LE_W((unsigned16)((parent_fat_fd->cln) & 0x0000FFFF)); + *MSDOS_DIR_FIRST_CLUSTER_HI(DOTDOT_NODE_P(dot_dotdot)) = + CT_LE_W((unsigned16)(((parent_fat_fd->cln) & 0xFFFF0000)>>16)); + } + + /* + * write dot and dotdot entries to new fat-file: currently fat-file + * correspondes to a new node is zero length, so it will be extended + * by one cluster and entries will be written + */ + ret = fat_file_write(parent_loc->mt_entry, fat_fd, 0, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2, + dot_dotdot); + if (ret < 0) + { + rc = -1; + goto error; + } + + /* increment fat-file size by cluster size */ + fat_fd->fat_file_size += fs_info->fat.vol.bpc; + + /* set up cluster num for dot entry */ + *MSDOS_DIR_FIRST_CLUSTER_LOW(DOT_NODE_P(dot_dotdot)) = + CT_LE_W((unsigned16)((fat_fd->cln) & 0x0000FFFF)); + *MSDOS_DIR_FIRST_CLUSTER_HI(DOT_NODE_P(dot_dotdot)) = + CT_LE_W((unsigned16)(((fat_fd->cln) & 0xFFFF0000) >> 16)); + + /* rewrite dot entry */ + ret = fat_file_write(parent_loc->mt_entry, fat_fd, 0, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE, + DOT_NODE_P(dot_dotdot)); + if (ret < 0) + { + rc = -1; + goto error; + } + + /* write first cluster num of a new directory to disk */ + rc = msdos_set_first_cluster_num(parent_loc->mt_entry, fat_fd); + if (rc != RC_OK) + goto error; + + fat_file_close(parent_loc->mt_entry, fat_fd); + } + return RC_OK; + +error: + fat_file_close(parent_loc->mt_entry, fat_fd); + +err: + /* mark 32bytes structure on the disk as free */ + msdos_set_first_char4file_name(parent_loc->mt_entry, aux.cln, aux.ofs, + 0xE5); + return rc; +} diff --git a/cpukit/libfs/src/dosfs/msdos_dir.c b/cpukit/libfs/src/dosfs/msdos_dir.c new file mode 100644 index 0000000000..93449cd2fb --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_dir.c @@ -0,0 +1,483 @@ +/* + * MSDOS directory handlers implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_dir_open -- + * Open fat-file which correspondes to the directory being opened and + * set offset field of file control block to zero. + * + * PARAMETERS: + * iop - file control block + * pathname - name + * flag - flags + * mode - mode + * + * RETURNS: + * RC_OK, if directory opened successfully, or -1 if error occured (errno + * set apropriately) + */ +int +msdos_dir_open(rtems_libio_t *iop, const char *pathname, unsigned32 flag, + unsigned32 mode) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one( EIO ); + + rc = fat_file_reopen(fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + iop->offset = 0; + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_dir_close -- + * Close fat-file which correspondes to the directory being closed + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK, if directory closed successfully, or -1 if error occured (errno + * set apropriately. + */ +int +msdos_dir_close(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one( EIO ); + + rc = fat_file_close(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_dir_read -- + * This routine will read the next directory entry based on the directory + * offset. The offset should be equal to -n- time the size of an + * individual dirent structure. If n is not an integer multiple of the + * sizeof a dirent structure, an integer division will be performed to + * determine directory entry that will be returned in the buffer. Count + * should reflect -m- times the sizeof dirent bytes to be placed in the + * buffer. + * If there are not -m- dirent elements from the current directory + * position to the end of the exisiting file, the remaining entries will + * be placed in the buffer and the returned value will be equal to + * -m actual- times the size of a directory entry. + * + * PARAMETERS: + * iop - file control block + * buffer - buffer provided by user + * count - count of bytes to read + * + * RETURNS: + * the number of bytes read on success, or -1 if error occured (errno + * set apropriately). + */ +ssize_t +msdos_dir_read(rtems_libio_t *iop, void *buffer, unsigned32 count) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + fat_file_fd_t *tmp_fat_fd = NULL; + struct dirent tmp_dirent; + unsigned32 start = 0; + ssize_t ret = 0; + unsigned32 cmpltd = 0; + unsigned32 j = 0, i = 0; + unsigned32 bts2rd = 0; + unsigned32 cur_cln = 0; + + /* + * cast start and count - protect against using sizes that are not exact + * multiples of the -dirent- size. These could result in unexpected + * results + */ + start = iop->offset / sizeof(struct dirent); + count = (count / sizeof(struct dirent)) * sizeof(struct dirent); + + /* + * optimization: we know that root directory for FAT12/16 volumes is + * sequential set of sectors and any cluster is sequential set of sectors + * too, so read such set of sectors is quick operation for low-level IO + * layer. + */ + bts2rd = (FAT_FD_OF_ROOT_DIR(fat_fd) && + (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16))) ? + fat_fd->fat_file_size : + fs_info->fat.vol.bpc; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + while (count > 0) + { + /* + * fat-file is already opened by open call, so read it + * Always read directory fat-file from the beggining because of MSDOS + * directories feature :( - we should count elements currently + * present in the directory because there may be holes :) + */ + ret = fat_file_read(iop->pathinfo.mt_entry, fat_fd, (j * bts2rd), + bts2rd, fs_info->cl_buf); + if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + rtems_semaphore_release(fs_info->vol_sema); + set_errno_and_return_minus_one(EIO); + } + + for (i = 0; i < ret; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) + { + rtems_semaphore_release(fs_info->vol_sema); + return cmpltd; + } + + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_EMPTY) + continue; + + /* + * skip active entries until get the entry to start from + */ + if (start) + { + start--; + continue; + } + + /* + * Move the entry to the return buffer + * + * unfortunately there is no method to extract ino except to + * open fat-file descriptor :( ... so, open it + */ + + /* get number of cluster we are working with */ + rc = fat_file_ioctl(iop->pathinfo.mt_entry, fat_fd, F_CLU_NUM, + j * bts2rd, &cur_cln); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + rc = fat_file_open(iop->pathinfo.mt_entry, cur_cln, i, + &tmp_fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + tmp_fat_fd->info_cln = cur_cln; + tmp_fat_fd->info_ofs = i; + + /* fill in dirent structure */ + /* XXX: from what and in what d_off should be computed ?! */ + tmp_dirent.d_off = start + cmpltd; + tmp_dirent.d_reclen = sizeof(struct dirent); + tmp_dirent.d_ino = tmp_fat_fd->ino; + tmp_dirent.d_namlen = MSDOS_SHORT_NAME_LEN; + memcpy(tmp_dirent.d_name, MSDOS_DIR_NAME((fs_info->cl_buf + i)), + MSDOS_SHORT_NAME_LEN); + + /* d_name is null-terminated */ + tmp_dirent.d_name[MSDOS_SHORT_NAME_LEN] = 0; + memcpy(buffer + cmpltd, &tmp_dirent, sizeof(struct dirent)); + + iop->offset = iop->offset + sizeof(struct dirent); + cmpltd += (sizeof(struct dirent)); + count -= (sizeof(struct dirent)); + + /* inode number extracted, close fat-file */ + rc = fat_file_close(iop->pathinfo.mt_entry, tmp_fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + if (count <= 0) + break; + } + j++; + } + + rtems_semaphore_release(fs_info->vol_sema); + return cmpltd; +} + +/* msdos_dir_write -- + * no write for directory + */ + +/* msdos_dir_lseek -- + * + * This routine will behave in one of three ways based on the state of + * argument whence. Based on the state of its value the offset argument will + * be interpreted using one of the following methods: + * + * SEEK_SET - offset is the absolute byte offset from the start of the + * logical start of the dirent sequence that represents the + * directory + * SEEK_CUR - offset is used as the relative byte offset from the current + * directory position index held in the iop structure + * SEEK_END - N/A --> This will cause an assert. + * + * PARAMETERS: + * iop - file control block + * offset - offset + * whence - predefine directive + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno + * set apropriately). + */ +int +msdos_dir_lseek(rtems_libio_t *iop, off_t offset, int whence) +{ + switch (whence) + { + case SEEK_SET: + case SEEK_CUR: + break; + /* + * Movement past the end of the directory via lseek is not a + * permitted operation + */ + case SEEK_END: + default: + set_errno_and_return_minus_one( EINVAL ); + break; + } + return RC_OK; +} + +/* msdos_dir_stat -- + * + * This routine will obtain the following information concerning the current + * directory: + * st_dev device id + * st_ino node serial number :) + * st_mode mode extracted from the node + * st_size total size in bytes + * st_blksize blocksize for filesystem I/O + * st_blocks number of blocks allocated + * stat_mtime time of last modification + * + * PARAMETERS: + * loc - this directory + * buf - stat buffer provided by user + * + * RETURNS: + * RC_OK and filled stat buffer on success, or -1 if error occured (errno + * set apropriately). + */ +int +msdos_dir_stat( + rtems_filesystem_location_info_t *loc, + struct stat *buf + ) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = loc->node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + buf->st_dev = fs_info->fat.vol.dev; + buf->st_ino = fat_fd->ino; + buf->st_mode = S_IFDIR; + buf->st_rdev = 0ll; + buf->st_size = fat_fd->fat_file_size; + buf->st_blocks = fat_fd->fat_file_size >> FAT_SECTOR512_BITS; + buf->st_blksize = fs_info->fat.vol.bps; + buf->st_mtime = fat_fd->mtime; + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_dir_truncate -- + * No truncate for directory. + * + * PARAMETERS: + * + * RETURNS: + * + */ + +/* msdos_dir_sync -- + * The following routine does a syncronization on a MSDOS directory node. + * DIR_WrtTime, DIR_WrtDate and DIR_fileSize fields of 32 Bytes Directory + * Entry Structure(see M$ White Paper) should not be updated for + * directories, so only call to corresponding fat-file routine. + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + */ +int +msdos_dir_sync(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + fat_file_fd_t *fat_fd = iop->file_info; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_datasync(iop->pathinfo.mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} + +/* msdos_dir_rmnod -- + * Remove directory node. + * + * Check that this directory node is not opened as fat-file, is empty and + * not filesystem root node. If all this conditions met then delete. + * + * PARAMETERS: + * pathloc - node description + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + */ +int +msdos_dir_rmnod(rtems_filesystem_location_info_t *pathloc) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = pathloc->node_access; + rtems_boolean is_empty = FALSE; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* + * We deny attemp to delete open directory (if directory is current + * directory we assume it is open one) + */ + if (fat_fd->links_num > 1) + { + rtems_semaphore_release(fs_info->vol_sema); + set_errno_and_return_minus_one(EBUSY); + } + + /* + * You cannot remove a node that still has children + */ + rc = msdos_dir_is_empty(pathloc->mt_entry, fat_fd, &is_empty); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + if (!is_empty) + { + rtems_semaphore_release(fs_info->vol_sema); + set_errno_and_return_minus_one(ENOTEMPTY); + } + + /* + * You cannot remove the file system root node. + */ + if (pathloc->mt_entry->mt_fs_root.node_access == pathloc->node_access) + { + rtems_semaphore_release(fs_info->vol_sema); + set_errno_and_return_minus_one(EBUSY); + } + + /* + * You cannot remove a mountpoint. + * not used - mount() not implemenetd yet. + */ + + /* mark file removed */ + rc = msdos_set_first_char4file_name(pathloc->mt_entry, fat_fd->info_cln, + fat_fd->info_ofs, + MSDOS_THIS_DIR_ENTRY_EMPTY); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + fat_file_mark_removed(pathloc->mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/cpukit/libfs/src/dosfs/msdos_eval.c b/cpukit/libfs/src/dosfs/msdos_eval.c new file mode 100644 index 0000000000..3eba0e63e8 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_eval.c @@ -0,0 +1,435 @@ +/* + * MSDOS evaluation routines + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_set_handlers -- + * Set handlers for the node with specified type(i.e. handlers for file + * or directory). + * + * PARAMETERS: + * loc - node description + * + * RETURNS: + * None + */ +static void +msdos_set_handlers(rtems_filesystem_location_info_t *loc) +{ + msdos_fs_info_t *fs_info = loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = loc->node_access; + + if (fat_fd->fat_file_type == FAT_DIRECTORY) + loc->handlers = fs_info->directory_handlers; + else + loc->handlers = fs_info->file_handlers; +} + +/* msdos_eval_path -- + * + * The following routine evaluate path for a node that wishes to be + * accessed. Structure 'pathloc' is returned with a pointer to the + * node to be accessed. + * + * PARAMETERS: + * pathname - path for evaluation + * flags - flags + * pathloc - node description (IN/OUT) + * + * RETURNS: + * RC_OK and filled pathloc on success, or -1 if error occured + * (errno set appropriately) + * + */ +int +msdos_eval_path( + const char *pathname, + int flags, + rtems_filesystem_location_info_t *pathloc + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + rtems_filesystem_location_info_t newloc; + int i = 0; + int len = 0; + msdos_token_types_t type = MSDOS_CURRENT_DIR; + char token[MSDOS_NAME_MAX + 1]; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + if (!pathloc->node_access) + { + errno = ENOENT; + rc = -1; + goto err; + } + + fat_fd = pathloc->node_access; + + rc = fat_file_reopen(fat_fd); + if (rc != RC_OK) + goto err; + + while ((type != MSDOS_NO_MORE_PATH) && (type != MSDOS_INVALID_TOKEN)) + { + type = msdos_get_token(&pathname[i], token, &len); + i += len; + + fat_fd = pathloc->node_access; + + switch (type) + { + case MSDOS_UP_DIR: + /* + * Only a directory can be decended into. + */ + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + /* + * Am I at the root of this mounted filesystem? + */ + if (pathloc->node_access == + pathloc->mt_entry->mt_fs_root.node_access) + { + /* + * Am I at the root of all filesystems? + * XXX: MSDOS is not supposed to be base fs. + */ + if (pathloc->node_access == + rtems_filesystem_root.node_access) + { + break; /* Throw out the .. in this case */ + } + else + { + newloc = pathloc->mt_entry->mt_point_node; + *pathloc = newloc; + + rc = fat_file_close(pathloc->mt_entry, fat_fd); + if (rc != RC_OK) + goto err; + + rtems_semaphore_release(fs_info->vol_sema); + return (*pathloc->ops->evalpath_h)(&(pathname[i-len]), + flags, pathloc); + } + } + else + { + rc = msdos_find_name(pathloc, token); + if (rc != RC_OK) + { + if (rc == MSDOS_NAME_NOT_FOUND_ERR) + { + errno = ENOENT; + rc = -1; + } + goto error; + } + } + break; + + case MSDOS_NAME: + /* + * Only a directory can be decended into. + */ + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + /* + * Otherwise find the token name in the present location and + * set the node access to the point we have found. + */ + rc = msdos_find_name(pathloc, token); + if (rc != RC_OK) + { + if (rc == MSDOS_NAME_NOT_FOUND_ERR) + { + errno = ENOENT; + rc = -1; + } + goto error; + } + break; + + case MSDOS_NO_MORE_PATH: + case MSDOS_CURRENT_DIR: + break; + + case MSDOS_INVALID_TOKEN: + errno = ENAMETOOLONG; + rc = -1; + goto error; + break; + + } + } + + /* + * Always return the root node. + * + * If we are at a node that is a mount point. Set loc to the + * new fs root node and let let the mounted filesystem set the handlers. + * + * NOTE: The behavior of stat() on a mount point appears to be + * questionable. + * NOTE: MSDOS filesystem currently doesn't support mount functionality -> + * action not implemented + */ + fat_fd = pathloc->node_access; + + msdos_set_handlers(pathloc); + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; + +error: + fat_file_close(pathloc->mt_entry, fat_fd); + +err: + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} + +/* msdos_eval4make -- + * The following routine evaluate path for a new node to be created. + * 'pathloc' is returned with a pointer to the parent of the new node. + * 'name' is returned with a pointer to the first character in the + * new node name. The parent node is verified to be a directory. + * + * PARAMETERS: + * path - path for evaluation + * pathloc - IN/OUT (start point for evaluation/parent directory for + * creation) + * name - new node name + * + * RETURNS: + * RC_OK, filled pathloc for parent directory and name of new node on + * success, or -1 if error occured (errno set appropriately) + */ +int +msdos_eval4make( + const char *path, + rtems_filesystem_location_info_t *pathloc, + const char **name + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + rtems_filesystem_location_info_t newloc; + msdos_token_types_t type; + int i = 0; + int len; + char token[ MSDOS_NAME_MAX + 1 ]; + rtems_boolean done = 0; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + if (!pathloc->node_access) + { + errno = ENOENT; + rc = -1; + goto err; + } + + fat_fd = pathloc->node_access; + + rc = fat_file_reopen(fat_fd); + if (rc != RC_OK) + goto err; + + while (!done) + { + type = msdos_get_token(&path[i], token, &len); + i += len; + fat_fd = pathloc->node_access; + + switch (type) + { + case MSDOS_UP_DIR: + /* + * Only a directory can be decended into. + */ + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + /* + * Am I at the root of this mounted filesystem? + */ + if (pathloc->node_access == + pathloc->mt_entry->mt_fs_root.node_access) + { + /* + * Am I at the root of all filesystems? + * XXX: MSDOS is not supposed to be base fs. + */ + if (pathloc->node_access == + rtems_filesystem_root.node_access) + { + break; /* Throw out the .. in this case */ + } + else + { + newloc = pathloc->mt_entry->mt_point_node; + *pathloc = newloc; + + rc = fat_file_close(pathloc->mt_entry, fat_fd); + if (rc != RC_OK) + goto err; + + rtems_semaphore_release(fs_info->vol_sema); + return (*pathloc->ops->evalformake_h)(&path[i-len], + pathloc, name); + } + } + else + { + rc = msdos_find_name(pathloc, token); + if (rc != RC_OK) + { + if (rc == MSDOS_NAME_NOT_FOUND_ERR) + { + errno = ENOENT; + rc = -1; + } + goto error; + } + } + break; + + case MSDOS_NAME: + /* + * Only a directory can be decended into. + */ + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + /* + * Otherwise find the token name in the present location and + * set the node access to the point we have found. + */ + rc = msdos_find_name(pathloc, token); + if (rc) + { + if (rc != MSDOS_NAME_NOT_FOUND_ERR) + { + errno = ENOENT; + rc = -1; + goto error; + } + else + done = TRUE; + } + break; + + case MSDOS_NO_MORE_PATH: + errno = EEXIST; + rc = -1; + goto error; + break; + + case MSDOS_CURRENT_DIR: + break; + + case MSDOS_INVALID_TOKEN: + errno = ENAMETOOLONG; + rc = -1; + goto error; + break; + + } + } + + *name = &path[i - len]; + + /* + * We have evaluated the path as far as we can. + * Verify there is not any invalid stuff at the end of the name. + */ + for( ; path[i] != '\0'; i++) + { + if (!msdos_is_separator(path[i])) + { + errno = ENOENT; + rc = -1; + goto error; + } + } + + fat_fd = pathloc->node_access; + + if (fat_fd->fat_file_type != FAT_DIRECTORY) + { + errno = ENOTDIR; + rc = -1; + goto error; + } + + msdos_set_handlers(pathloc); + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; + +error: + fat_file_close(pathloc->mt_entry, fat_fd); + +err: + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/cpukit/libfs/src/dosfs/msdos_file.c b/cpukit/libfs/src/dosfs/msdos_file.c new file mode 100644 index 0000000000..da36827338 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_file.c @@ -0,0 +1,485 @@ +/* + * MSDOS file handlers implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_file_open -- + * Open fat-file which correspondes to the file + * + * PARAMETERS: + * iop - file control block + * pathname - name + * flag - flags + * mode - mode + * + * RETURNS: + * RC_OK, if file opened successfully, or -1 if error occured + * and errno set appropriately + */ +int +msdos_file_open(rtems_libio_t *iop, const char *pathname, unsigned32 flag, + unsigned32 mode) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_reopen(fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + if (iop->flags & LIBIO_FLAGS_APPEND) + iop->offset = fat_fd->fat_file_size; + + iop->size = fat_fd->fat_file_size; + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_file_close -- + * Close fat-file which correspondes to the file. If fat-file descriptor + * which correspondes to the file is not marked "removed", synchronize + * size, first cluster number, write time and date fields of the file. + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK, if file closed successfully, or -1 if error occured (errno set + * appropriately) + */ +int +msdos_file_close(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* + * if fat-file descriptor is not marked as "removed", synchronize + * size, first cluster number, write time and date fields of the file + */ + if (!FAT_FILE_IS_REMOVED(fat_fd)) + { + rc = msdos_set_first_cluster_num(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + rc = msdos_set_file_size(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + rc = msdos_set_dir_wrt_time_and_date(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + } + + rc = fat_file_close(iop->pathinfo.mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} + +/* msdos_file_read -- + * This routine read from file pointed to by file control block into + * the specified data buffer provided by user + * + * PARAMETERS: + * iop - file control block + * buffer - buffer provided by user + * count - the number of bytes to read + * + * RETURNS: + * the number of bytes read on success, or -1 if error occured (errno set + * appropriately) + */ +ssize_t +msdos_file_read(rtems_libio_t *iop, void *buffer, unsigned32 count) +{ + ssize_t ret = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + ret = fat_file_read(iop->pathinfo.mt_entry, fat_fd, iop->offset, count, + buffer); + + rtems_semaphore_release(fs_info->vol_sema); + return ret; +} + +/* msdos_file_write -- + * This routine writes the specified data buffer into the file pointed to + * by file control block. + * + * PARAMETERS: + * iop - file control block + * buffer - data to write + * count - count of bytes to write + * + * RETURNS: + * the number of bytes written on success, or -1 if error occured + * and errno set appropriately + */ +ssize_t +msdos_file_write(rtems_libio_t *iop,const void *buffer, unsigned32 count) +{ + ssize_t ret = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + ret = fat_file_write(iop->pathinfo.mt_entry, fat_fd, iop->offset, count, + buffer); + if (ret < 0) + { + rtems_semaphore_release(fs_info->vol_sema); + return -1; + } + + /* + * update file size in both fat-file descriptor and file control block if + * file was extended + */ + if (iop->offset + ret > fat_fd->fat_file_size) + fat_fd->fat_file_size = iop->offset + ret; + + iop->size = fat_fd->fat_file_size; + + rtems_semaphore_release(fs_info->vol_sema); + return ret; +} + +/* msdos_file_lseek -- + * Process lseek call to the file: extend file if lseek is up to the end + * of the file. + * + * PARAMETERS: + * iop - file control block + * offset - new offset + * whence - predefine directive + * + * RETURNS: + * new offset on success, or -1 if error occured (errno set + * appropriately). + */ +int +msdos_file_lseek(rtems_libio_t *iop, off_t offset, int whence) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + unsigned32 real_size = 0; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_extend(iop->pathinfo.mt_entry, fat_fd, iop->offset, + &real_size); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + if (real_size > fat_fd->fat_file_size) + fat_fd->fat_file_size = iop->offset = real_size; + + iop->size = fat_fd->fat_file_size; + + rtems_semaphore_release(fs_info->vol_sema); + return iop->offset; +} + +/* msdos_file_stat -- + * + * PARAMETERS: + * loc - node description + * buf - stat buffer provided by user + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_file_stat( + rtems_filesystem_location_info_t *loc, + struct stat *buf + ) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = loc->node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + buf->st_dev = fs_info->fat.vol.dev; + buf->st_ino = fat_fd->ino; + buf->st_mode = S_IFREG; + buf->st_rdev = 0ll; + buf->st_size = fat_fd->fat_file_size; + buf->st_blocks = fat_fd->fat_file_size >> FAT_SECTOR512_BITS; + buf->st_blksize = fs_info->fat.vol.bps; + buf->st_mtime = fat_fd->mtime; + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_file_ftruncate -- + * Truncate the file (if new length is greater then current do nothing). + * + * PARAMETERS: + * iop - file control block + * length - new length + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately). + */ +int +msdos_file_ftruncate(rtems_libio_t *iop, off_t length) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + fat_file_fd_t *fat_fd = iop->file_info; + + if (length >= fat_fd->fat_file_size) + return RC_OK; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_truncate(iop->pathinfo.mt_entry, fat_fd, length); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + /* + * fat_file_truncate do nothing if new length >= fat-file size, so update + * file size only if length < fat-file size + */ + if (length < fat_fd->fat_file_size) + iop->size = fat_fd->fat_file_size = length; + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_file_sync -- + * Synchronize file - synchronize file data and if file is not removed + * synchronize file metadata. + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_file_sync(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + fat_file_fd_t *fat_fd = iop->file_info; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* synchronize file data */ + rc = fat_file_datasync(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + /* + * if fat-file descriptor is not marked "removed" - synchronize file + * metadata + */ + if (!FAT_FILE_IS_REMOVED(fat_fd)) + { + rc = msdos_set_first_cluster_num(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + rc = msdos_set_file_size(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + rc = msdos_set_dir_wrt_time_and_date(iop->pathinfo.mt_entry, fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + } + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + +/* msdos_file_datasync -- + * Synchronize file - synchronize only file data (metadata is letf intact). + * + * PARAMETERS: + * iop - file control block + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_file_datasync(rtems_libio_t *iop) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + fat_file_fd_t *fat_fd = iop->file_info; + msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* synchronize file data */ + rc = fat_file_datasync(iop->pathinfo.mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} + + +/* msdos_file_ioctl -- + * + * + * PARAMETERS: + * iop - file control block + * ... + * + * RETURNS: + * + */ +int +msdos_file_ioctl(rtems_libio_t *iop,unsigned32 command, void *buffer) +{ + int rc = RC_OK; + + return rc; +} + +/* msdos_file_rmnod -- + * Remove node associated with a file - set up first name character to + * predefined value(and write it to the disk), and mark fat-file which + * correspondes to the file as "removed" + * + * PARAMETERS: + * pathloc - node description + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_file_rmnod(rtems_filesystem_location_info_t *pathloc) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = pathloc->node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* mark file removed */ + rc = msdos_set_first_char4file_name(pathloc->mt_entry, fat_fd->info_cln, + fat_fd->info_ofs, + MSDOS_THIS_DIR_ENTRY_EMPTY); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + fat_file_mark_removed(pathloc->mt_entry, fat_fd); + + rtems_semaphore_release(fs_info->vol_sema); + return RC_OK; +} diff --git a/cpukit/libfs/src/dosfs/msdos_free.c b/cpukit/libfs/src/dosfs/msdos_free.c new file mode 100644 index 0000000000..c0d5938dbb --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_free.c @@ -0,0 +1,56 @@ +/* + * Free node handler implementation for the filesystem + * operations table. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_free_node_info -- + * Call fat-file close routine. + * + * PARAMETERS: + * pathloc - node description + * + * RETURNS: + * RC_OK on success, or -1 code if error occured + * + */ +int +msdos_free_node_info(rtems_filesystem_location_info_t *pathloc) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + rc = fat_file_close(pathloc->mt_entry, pathloc->node_access); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/cpukit/libfs/src/dosfs/msdos_fsunmount.c b/cpukit/libfs/src/dosfs/msdos_fsunmount.c new file mode 100644 index 0000000000..9072a2fad5 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_fsunmount.c @@ -0,0 +1,71 @@ +/* + * MSDOS shut down handler implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_shut_down -- + * Shut down MSDOS filesystem - free all allocated resources (don't + * return if deallocation of some resource failed - free as much as + * possible). + * + * PARAMETERS: + * temp_mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + * + */ +int +msdos_shut_down(rtems_filesystem_mount_table_entry_t *temp_mt_entry) +{ + int rc = RC_OK; + msdos_fs_info_t *fs_info = temp_mt_entry->fs_info; + fat_file_fd_t *fat_fd = temp_mt_entry->mt_fs_root.node_access; + + /* close fat-file which correspondes to root directory */ + if (fat_file_close(temp_mt_entry, fat_fd) != RC_OK) + { + /* no return - try to free as much as possible */ + rc = -1; + } + + if (fat_shutdown_drive(temp_mt_entry) != RC_OK) + { + /* no return - try to free as much as possible */ + rc = -1; + } + + rtems_semaphore_delete(fs_info->vol_sema); + free(fs_info->cl_buf); + free(temp_mt_entry->fs_info); + + return rc; +} diff --git a/cpukit/libfs/src/dosfs/msdos_handlers_dir.c b/cpukit/libfs/src/dosfs/msdos_handlers_dir.c new file mode 100644 index 0000000000..e14d892add --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_handlers_dir.c @@ -0,0 +1,36 @@ +/* + * Directory Handlers Table for MSDOS filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "msdos.h" + +rtems_filesystem_file_handlers_r msdos_dir_handlers = { + msdos_dir_open, + msdos_dir_close, + msdos_dir_read, + NULL, /* msdos_dir_write */ + NULL, /* msdos_dir_ioctl */ + msdos_dir_lseek, + msdos_dir_stat, + NULL, + NULL, /* msdos_dir_ftruncate */ + NULL, + msdos_dir_sync, + msdos_dir_sync, + NULL, /* msdos_dir_fcntl */ + msdos_dir_rmnod +}; diff --git a/cpukit/libfs/src/dosfs/msdos_handlers_file.c b/cpukit/libfs/src/dosfs/msdos_handlers_file.c new file mode 100644 index 0000000000..ae627066de --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_handlers_file.c @@ -0,0 +1,36 @@ +/* + * File Operations Table for MSDOS filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "msdos.h" + +rtems_filesystem_file_handlers_r msdos_file_handlers = { + msdos_file_open, + msdos_file_close, + msdos_file_read, + msdos_file_write, + msdos_file_ioctl, + msdos_file_lseek, + msdos_file_stat, + NULL, + msdos_file_ftruncate, + NULL, + msdos_file_sync, + msdos_file_datasync, + NULL, /* msdos_file_fcntl */ + msdos_file_rmnod +}; diff --git a/cpukit/libfs/src/dosfs/msdos_init.c b/cpukit/libfs/src/dosfs/msdos_init.c new file mode 100644 index 0000000000..2d5bf6c9e0 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_init.c @@ -0,0 +1,60 @@ +/* + * Init routine for MSDOS + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "msdos.h" + +rtems_filesystem_operations_table msdos_ops = { + msdos_eval_path, + msdos_eval4make, + NULL, /* msdos_link */ + msdos_file_rmnod, + msdos_node_type, + msdos_mknod, + NULL, /* msdos_chown */ + msdos_free_node_info, + NULL, + msdos_initialize, + NULL, + msdos_shut_down, /* msdos_shut_down */ + NULL, /* msdos_utime */ + NULL, + NULL, + NULL +}; + +/* msdos_initialize -- + * MSDOS filesystem initialization + * + * PARAMETERS: + * temp_mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + * + */ +int +msdos_initialize(rtems_filesystem_mount_table_entry_t *temp_mt_entry) +{ + int rc = RC_OK; + + rc = msdos_initialize_support(temp_mt_entry, + &msdos_ops, + &msdos_file_handlers, + &msdos_dir_handlers); + return rc; +} diff --git a/cpukit/libfs/src/dosfs/msdos_initsupp.c b/cpukit/libfs/src/dosfs/msdos_initsupp.c new file mode 100644 index 0000000000..eee8a6f9b2 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_initsupp.c @@ -0,0 +1,149 @@ +/* + * MSDOS Initialization support routine implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_initialize_support -- + * MSDOS filesystem initialization + * + * PARAMETERS: + * temp_mt_entry - mount table entry + * op_table - filesystem operations table + * file_handlers - file operations table + * directory_handlers - directory operations table + * + * RETURNS: + * RC_OK and filled temp_mt_entry on success, or -1 if error occured + * (errno set apropriately) + * + */ +int +msdos_initialize_support( + rtems_filesystem_mount_table_entry_t *temp_mt_entry, + rtems_filesystem_operations_table *op_table, + rtems_filesystem_file_handlers_r *file_handlers, + rtems_filesystem_file_handlers_r *directory_handlers + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = NULL; + fat_file_fd_t *fat_fd = NULL; + unsigned32 cl_buf_size; + + fs_info = (msdos_fs_info_t *)calloc(1, sizeof(msdos_fs_info_t)); + if (!fs_info) + set_errno_and_return_minus_one(ENOMEM); + + temp_mt_entry->fs_info = fs_info; + + rc = fat_init_volume_info(temp_mt_entry); + if (rc != RC_OK) + { + free(fs_info); + return rc; + } + + fs_info->file_handlers = file_handlers; + fs_info->directory_handlers = directory_handlers; + + /* + * open fat-file which correspondes to root directory + * (so inode number 0x00000010 is always used for root directory) + */ + rc = fat_file_open(temp_mt_entry, FAT_ROOTDIR_CLUSTER_NUM, 0, &fat_fd); + if (rc != RC_OK) + { + fat_shutdown_drive(temp_mt_entry); + free(fs_info); + return rc; + } + + /* again: unfortunately "fat-file" is just almost fat file :( */ + fat_fd->fat_file_type = FAT_DIRECTORY; + fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT; + fat_fd->info_cln = FAT_ROOTDIR_CLUSTER_NUM; + fat_fd->info_ofs = 0; + fat_fd->cln = fs_info->fat.vol.rdir_cl; + + fat_fd->map.file_cln = 0; + fat_fd->map.disk_cln = fat_fd->cln; + + /* if we have FAT12/16 */ + if ( fat_fd->cln == 0 ) + { + fat_fd->fat_file_size = fs_info->fat.vol.rdir_size; + cl_buf_size = (fs_info->fat.vol.bpc > fs_info->fat.vol.rdir_size) ? + fs_info->fat.vol.bpc : + fs_info->fat.vol.rdir_size; + } + else + { + rc = fat_file_size(temp_mt_entry, fat_fd); + if ( rc != RC_OK ) + { + fat_file_close(temp_mt_entry, fat_fd); + fat_shutdown_drive(temp_mt_entry); + free(fs_info); + return rc; + } + cl_buf_size = fs_info->fat.vol.bpc; + } + + fs_info->cl_buf = (char *)calloc(cl_buf_size, sizeof(char)); + if (fs_info->cl_buf == NULL) + { + fat_file_close(temp_mt_entry, fat_fd); + fat_shutdown_drive(temp_mt_entry); + free(fs_info); + set_errno_and_return_minus_one(ENOMEM); + } + + sc = rtems_semaphore_create(3, + 1, + RTEMS_BINARY_SEMAPHORE | RTEMS_FIFO, + RTEMS_INHERIT_PRIORITY, + &fs_info->vol_sema); + if (sc != RTEMS_SUCCESSFUL) + { + fat_file_close(temp_mt_entry, fat_fd); + fat_shutdown_drive(temp_mt_entry); + free(fs_info->cl_buf); + free(fs_info); + set_errno_and_return_minus_one( EIO ); + } + + temp_mt_entry->mt_fs_root.node_access = fat_fd; + temp_mt_entry->mt_fs_root.handlers = directory_handlers; + temp_mt_entry->mt_fs_root.ops = op_table; + + return rc; +} diff --git a/cpukit/libfs/src/dosfs/msdos_misc.c b/cpukit/libfs/src/dosfs/msdos_misc.c new file mode 100644 index 0000000000..fe2779f7a8 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_misc.c @@ -0,0 +1,1087 @@ +/* + * Miscellaneous routines implementation for MSDOS filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* This copied from Linux */ +static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 }; + /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ + +#undef CONFIG_ATARI + +/* MS-DOS "device special files" */ +static const char *reserved_names[] = { +#ifndef CONFIG_ATARI /* GEMDOS is less stupid */ + "CON ","PRN ","NUL ","AUX ", + "LPT1 ","LPT2 ","LPT3 ","LPT4 ", + "COM1 ","COM2 ","COM3 ","COM4 ", +#endif + NULL }; + +static char bad_chars[] = "*?<>|\""; +#ifdef CONFIG_ATARI +/* GEMDOS is less restrictive */ +static char bad_if_strict[] = " "; +#else +static char bad_if_strict[] = "+=,; "; +#endif + +/* The following three functions copied from Linux */ +/* + * Formats an MS-DOS file name. Rejects invalid names + * + * conv is relaxed/normal/strict, name is proposed name, + * len is the length of the proposed name, res is the result name, + * dotsOK is if hidden files get dots. + */ +int +msdos_format_name(char conv, const char *name, int len, char *res, + char dotsOK) +{ + char *walk; + const char **reserved; + unsigned char c; + int space; + if (name[0] == '.') { /* dotfile because . and .. already done */ + if (!dotsOK) return -EINVAL; + /* Get rid of dot - test for it elsewhere */ + name++; len--; + } +#ifndef CONFIG_ATARI + space = 1; /* disallow names that _really_ start with a dot */ +#else + space = 0; /* GEMDOS does not care */ +#endif + c = 0; + for (walk = res; len && walk-res < 8; walk++) { + c = *name++; + len--; + if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; + if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL; + if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL; + if (c < ' ' || c == ':' || c == '\\') return -EINVAL; +/* 0xE5 is legal as a first character, but we must substitute 0x05 */ +/* because 0xE5 marks deleted files. Yes, DOS really does this. */ +/* It seems that Microsoft hacked DOS to support non-US characters */ +/* after the 0xE5 character was already in use to mark deleted files. */ + if((res==walk) && (c==0xE5)) c=0x05; + if (c == '.') break; + space = (c == ' '); + *walk = (c >= 'a' && c <= 'z') ? c-32 : c; + } + if (space) return -EINVAL; + if (conv == 's' && len && c != '.') { + c = *name++; + len--; + if (c != '.') return -EINVAL; + } + while (c != '.' && len--) c = *name++; + if (c == '.') { + while (walk-res < 8) *walk++ = ' '; + while (len > 0 && walk-res < MSDOS_NAME_MAX) { + c = *name++; + len--; + if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; + if (conv == 's' && strchr(bad_if_strict,c)) + return -EINVAL; + if (c < ' ' || c == ':' || c == '\\') + return -EINVAL; + if (c == '.') { + if (conv == 's') + return -EINVAL; + break; + } + if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL; + space = c == ' '; + *walk++ = c >= 'a' && c <= 'z' ? c-32 : c; + } + if (space) return -EINVAL; + if (conv == 's' && len) return -EINVAL; + } + while (walk-res < MSDOS_NAME_MAX) *walk++ = ' '; + for (reserved = reserved_names; *reserved; reserved++) + if (!strncmp(res,*reserved,8)) return -EINVAL; + return 0; +} + +/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70) */ +unsigned int +msdos_date_dos2unix(unsigned short time_val,unsigned short date) +{ + int month,year,secs; + + month = ((date >> 5) & 15)-1; + year = date >> 9; + secs = (time_val & 31)*2+60*((time_val >> 5) & 63)+ + (time_val >> 11)*3600+86400* + ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && + month < 2 ? 1 : 0)+3653); + /* days since 1.1.70 plus 80's leap day */ + + return secs; +} + + +/* Convert linear UNIX date to a MS-DOS time/date pair */ +void msdos_date_unix2dos(int unix_date, + unsigned short *time_val, + unsigned short *date) +{ + int day,year,nl_day,month; + + *time_val = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+ + (((unix_date/3600) % 24) << 11); + day = unix_date/86400-3652; + year = day/365; + if ((year+3)/4+365*year > day) year--; + day -= (year+3)/4+365*year; + if (day == 59 && !(year & 3)) { + nl_day = day; + month = 2; + } + else { + nl_day = (year & 3) || day <= 59 ? day : day-1; + for (month = 0; month < 12; month++) + if (day_n[month] > nl_day) break; + } + *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9); +} + + +/* 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, char *ret_token, int *token_len) +{ + int rc = RC_OK; + register int i = 0; + msdos_token_types_t type = MSDOS_NAME; + char token[MSDOS_NAME_MAX_WITH_DOT+1]; + register char c; + + /* + * Copy a name into token. (Remember NULL is a token.) + */ + c = path[i]; + while ( (!msdos_is_separator(c)) && (i <= MSDOS_NAME_MAX_WITH_DOT) ) + { + token[i] = c; + if ( i == MSDOS_NAME_MAX_WITH_DOT ) + return MSDOS_INVALID_TOKEN; + if ( !msdos_is_valid_name_char(c) ) + return MSDOS_INVALID_TOKEN; + c = path [++i]; + } + + /* + * Copy a seperator into token. + */ + if ( i == 0 ) + { + token[i] = c; + if ( token[i] != '\0' ) + { + i++; + type = MSDOS_CURRENT_DIR; + } + else + type = MSDOS_NO_MORE_PATH; + } + else if (token[ i-1 ] != '\0') + token[i] = '\0'; + + /* + * Set token_len to the number of characters copied. + */ + *token_len = i; + + /* + * If we copied something that was not a seperator see if + * it was a special name. + */ + if ( type == MSDOS_NAME ) + { + if ( strcmp( token, "..") == 0 ) + { + strcpy(ret_token, MSDOS_DOTDOT_NAME); + type = MSDOS_UP_DIR; + return type; + } + + if ( strcmp( token, "." ) == 0 ) + { + strcpy(ret_token, MSDOS_DOT_NAME); + type = MSDOS_CURRENT_DIR; + return type; + } + + rc = msdos_format_name('r', token, *token_len, ret_token, 0); + if ( rc != RC_OK ) + return MSDOS_INVALID_TOKEN; + } + ret_token[MSDOS_NAME_MAX] = '\0'; + 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, + char *name + ) +{ + int rc = RC_OK; + msdos_fs_info_t *fs_info = parent_loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + fat_auxiliary_t aux; + unsigned short time_val = 0; + unsigned short date = 0; + unsigned char node_entry[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + + memset(node_entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + + /* + * find the node which correspondes to the name in the directory pointed by + * 'parent_loc' + */ + rc = msdos_get_name_node(parent_loc, name, &aux, node_entry); + if (rc != RC_OK) + return rc; + + /* open fat-file corresponded to the found node */ + rc = fat_file_open(parent_loc->mt_entry, aux.cln, aux.ofs, &fat_fd); + if (rc != RC_OK) + return rc; + + /* + * 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 non-optimize) because + * otherwise real values of these fields stored in fat-file descriptor + * may be accidentely rewritten with wrong values stored on the disk + */ + if (fat_fd->links_num == 1) + { + fat_fd->info_cln = aux.cln; + fat_fd->info_ofs = aux.ofs; + fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(node_entry); + fat_fd->first_char = *MSDOS_DIR_NAME(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(time_val), CF_LE_W(date)); + + 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 mode 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 + * name_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, + char *name, + fat_auxiliary_t *paux, + char *name_dir_entry + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = parent_loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = parent_loc->node_access; + unsigned32 dotdot_cln = 0; + + /* find name in fat-file which correspondes to the directory */ + rc = msdos_find_name_in_fat_file(parent_loc->mt_entry, fat_fd, name, paux, + name_dir_entry); + if ((rc != RC_OK) && (rc != MSDOS_NAME_NOT_FOUND_ERR)) + return rc; + + /* if we search for valid name and name not found -> return */ + if ((rc == MSDOS_NAME_NOT_FOUND_ERR) && (name != NULL)) + return rc; + + /* + * if we try to create new entry and the directory is not big enough + * currently - try to enlarge directory + */ + if ((rc == MSDOS_NAME_NOT_FOUND_ERR) && (name == NULL)) + { + ret = fat_file_write(parent_loc->mt_entry, fat_fd, + fat_fd->fat_file_size, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE, + name_dir_entry); + if (ret == -1) + return -1; + + /* on success directory is enlarged by a new cluster */ + fat_fd->fat_file_size += fs_info->fat.vol.bpc; + + /* get cluster num where a new node located */ + rc = fat_file_ioctl(parent_loc->mt_entry, fat_fd, F_CLU_NUM, + fat_fd->fat_file_size - 1, &paux->cln); + + if (rc != RC_OK) + return rc; + + /* + * if new cluster allocated succesfully then new node is at very + * beginning of the cluster (offset is computed in bytes) + */ + paux->ofs = 0; + return RC_OK; + } + + /* + * 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) && (name != NULL)) + { + if (strncmp(name, MSDOS_DOTDOT_NAME, MSDOS_SHORT_NAME_LEN) == 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 + */ + paux->cln = FAT_ROOTDIR_CLUSTER_NUM; + paux->ofs = 0; + } + else + { + rc = msdos_get_dotdot_dir_info_cluster_num_and_offset( + parent_loc->mt_entry, + dotdot_cln, + paux, + 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, + unsigned32 cln, + fat_auxiliary_t *paux, + char *dir_entry + ) +{ + int rc = RC_OK; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + unsigned char dot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + unsigned char dotdot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + unsigned char cur_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + unsigned32 cl4find = 0; + + memset(dot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memset(dotdot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memset(cur_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + + /* + * open fat-file corresponded to ".." + */ + rc = fat_file_open(mt_entry, paux->cln, paux->ofs, &fat_fd); + if (rc != RC_OK) + return rc; + + fat_fd->info_cln = paux->cln; + fat_fd->info_ofs = paux->ofs; + 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 */ + rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, MSDOS_DOT_NAME, paux, + dot_node); + + if (rc != RC_OK) + { + fat_file_close(mt_entry, fat_fd); + return rc; + } + + /* find ".." node in opened directory */ + rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, MSDOS_DOTDOT_NAME, paux, + 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 ) + */ + paux->cln = FAT_ROOTDIR_CLUSTER_NUM; + paux->ofs = 0; + } + + /* open fat-file corresponded to second ".." */ + rc = fat_file_open(mt_entry, paux->cln, paux->ofs, &fat_fd); + if (rc != RC_OK) + return rc; + + fat_fd->info_cln = paux->cln; + fat_fd->info_ofs = paux->ofs; + + 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, + paux, 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; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned short time_val; + unsigned short date; + unsigned32 sec = 0; + unsigned32 byte = 0; + + msdos_date_unix2dos(fat_fd->mtime, &time_val, &date); + + /* + * 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->info_cln); + sec += (fat_fd->info_ofs >> fs_info->fat.vol.sec_log2); + /* byte points to start of 32bytes structure */ + byte = fat_fd->info_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)); + + if ( (ret1 < 0) || (ret2 < 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; + unsigned32 new_cln = fat_fd->cln; + unsigned16 le_cl_low = 0; + unsigned16 le_cl_hi = 0; + unsigned32 sec = 0; + unsigned32 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->info_cln); + sec += (fat_fd->info_ofs >> fs_info->fat.vol.sec_log2); + /* byte from points to start of 32bytes structure */ + byte = fat_fd->info_ofs & (fs_info->fat.vol.bps - 1); + + le_cl_low = CT_LE_W((unsigned16)(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((unsigned16)((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; + unsigned32 le_new_length = 0; + unsigned32 sec = 0; + unsigned32 byte = 0; + + sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->info_cln); + sec += (fat_fd->info_ofs >> fs_info->fat.vol.sec_log2); + byte = (fat_fd->info_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, + unsigned32 cl, + unsigned32 ofs, + unsigned char fchar + ) +{ + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 sec = 0; + unsigned32 byte = 0; + + sec = fat_cluster_num_to_sector_num(mt_entry, cl); + sec += (ofs >> fs_info->fat.vol.sec_log2); + byte = (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; + + 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, + rtems_boolean *ret_val + ) +{ + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 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); + + for (i = 0; + i < fs_info->fat.vol.bps; + i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + if (((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_EMPTY) || + (strncmp(MSDOS_DIR_NAME((fs_info->cl_buf + i)), MSDOS_DOT_NAME, + MSDOS_SHORT_NAME_LEN) == 0) || + (strncmp(MSDOS_DIR_NAME((fs_info->cl_buf + i)), + MSDOS_DOTDOT_NAME, + MSDOS_SHORT_NAME_LEN) == 0)) + continue; + + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) + { + *ret_val = TRUE; + return RC_OK; + } + return RC_OK; + } + j++; + } + *ret_val = TRUE; + return RC_OK; +} + + +/* msdos_find_name_in_fat_file -- + * This routine is used in two ways: for a new mode 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 corresponded to directory we are going to create + * node in. If found free slot write contents of name_dir_entry into + * it. + * + * (b): reading fat-file corresponded to directory and trying to find slot + * with the name field == name parameter + * + * 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) + * + */ +int +msdos_find_name_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + char *name, + fat_auxiliary_t *paux, + char *name_dir_entry + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 i = 0, j = 0; + unsigned32 bts2rd = 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) + set_errno_and_return_minus_one(EIO); + + assert(ret == bts2rd); + + for (i = 0; i < bts2rd; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + /* is the entry empty ? */ + if (((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) || + ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_EMPTY)) + { + /* whether we are looking for an empty entry */ + if (name == NULL) + { + /* get current cluster number */ + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, + j * bts2rd, &paux->cln); + if (rc != RC_OK) + return rc; + + /* offset is computed in bytes */ + paux->ofs = i; + + /* write new node entry */ + ret = fat_file_write(mt_entry, fat_fd, j * bts2rd + i, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE, + name_dir_entry); + if (ret != MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + return -1; + + /* + * we don't update fat_file_size here - it should not + * increase + */ + return RC_OK; + } + + /* + * if name != NULL and there is no more entries in the + * directory - return name-not-found + */ + if (((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)) + return MSDOS_NAME_NOT_FOUND_ERR; + } + else + { + /* entry not empty and name != NULL -> compare names */ + if (name != NULL) + { + if (strncmp(MSDOS_DIR_NAME((fs_info->cl_buf + i)), name, + MSDOS_SHORT_NAME_LEN) == 0) + { + /* + * we get the entry we looked for - fill auxiliary + * structure and copy all 32 bytes of the entry + */ + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, + j * bts2rd, &paux->cln); + if (rc != RC_OK) + return rc; + + /* offset is computed in bytes */ + paux->ofs = i; + memcpy(name_dir_entry,(fs_info->cl_buf + i), + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + return RC_OK; + } + } + } + } + j++; + } + return MSDOS_NAME_NOT_FOUND_ERR; +} + +/* msdos_find_node_by_cluster_num_in_fat_file -- + * Find node with specified number of cluster in fat-file. + * + * 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, + unsigned32 cl4find, + fat_auxiliary_t *paux, + char *dir_entry + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + unsigned32 bts2rd = 0; + unsigned32 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 ) + set_errno_and_return_minus_one( EIO ); + + assert(ret == bts2rd); + + for (i = 0; i < bts2rd; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + /* if this and all rest entries are empty - return not-found */ + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) + return MSDOS_NAME_NOT_FOUND_ERR; + + /* if this entry is empty - skip it */ + if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == + MSDOS_THIS_DIR_ENTRY_EMPTY) + continue; + + /* if get a non-empty entry - compare clusters num */ + if (MSDOS_EXTRACT_CLUSTER_NUM((fs_info->cl_buf + i)) == cl4find) + { + /* on success fill aux structure and copy all 32 bytes */ + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, j * bts2rd, + &paux->cln); + if (rc != RC_OK) + return rc; + + paux->ofs = i; + memcpy(dir_entry, fs_info->cl_buf + i, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + return RC_OK; + } + } + j++; + } + return MSDOS_NAME_NOT_FOUND_ERR; +} diff --git a/cpukit/libfs/src/dosfs/msdos_mknod.c b/cpukit/libfs/src/dosfs/msdos_mknod.c new file mode 100644 index 0000000000..5e32dbf3bf --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_mknod.c @@ -0,0 +1,90 @@ +/* + * Routine for node creation in MSDOS filesystem. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_mknod -- + * The following function checks spelling and formats name for a new node, + * determines type of the node to be created and creates it. + * + * PARAMETERS: + * token - non-formatted name of a new node + * mode - node type + * dev - dev + * pathloc - parent directory description + * + * RETURNS: + * RC_OK on succes, or -1 if error occured and set errno + * + */ +int +msdos_mknod( + const char *token, + mode_t mode, + dev_t dev, + rtems_filesystem_location_info_t *pathloc + ) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info; + msdos_token_types_t type = 0; + char new_name[ MSDOS_NAME_MAX + 1 ]; + int len; + + /* check spelling and format new node name */ + msdos_get_token(token, new_name, &len); + + /* + * Figure out what type of msdos node this is. + */ + if (S_ISDIR(mode)) + { + type = MSDOS_DIRECTORY; + } + else if (S_ISREG(mode)) + { + type = MSDOS_REGULAR_FILE; + } + else + set_errno_and_return_minus_one(EINVAL); + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + set_errno_and_return_minus_one(EIO); + + /* Create an MSDOS node */ + rc = msdos_creat_node(pathloc, type, new_name, mode); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/cpukit/libfs/src/dosfs/msdos_node_type.c b/cpukit/libfs/src/dosfs/msdos_node_type.c new file mode 100644 index 0000000000..517dabda3f --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_node_type.c @@ -0,0 +1,58 @@ +/* + * The following returns the type of node that the loc refers to. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_node_type -- + * Determine type of the node that the pathloc refers to. + * + * PARAMETERS: + * pathloc - node description + * + * RETURNS: + * node type + * + */ +rtems_filesystem_node_types_t +msdos_node_type(rtems_filesystem_location_info_t *pathloc) +{ + fat_file_fd_t *fat_fd; + + /* + * we don't need to obtain the volume semaphore here because node_type_h + * call always follows evalpath_h call(hence link increment occured) and + * hence node_access memory can't be freed during processing node_type_h + * call + */ + fat_fd = pathloc->node_access; + + return fat_fd->fat_file_type; +} diff --git a/cpukit/libfs/wrapup/Makefile.am b/cpukit/libfs/wrapup/Makefile.am index 5dfd76e83d..13406dcb5f 100644 --- a/cpukit/libfs/wrapup/Makefile.am +++ b/cpukit/libfs/wrapup/Makefile.am @@ -13,7 +13,9 @@ include $(top_srcdir)/../../../automake/lib.am IMFSLIB = ../src/imfs/$(ARCH)/libimfs.a -TMP_LIBS = $(IMFSLIB) +DOSFSLIB = ../src/dosfs/$(ARCH)/libdosfs.a + +TMP_LIBS = $(IMFSLIB) $(DOSFSLIB) $(PROJECT_RELEASE)/lib/$(LIBNAME)$(LIB_VARIANT).a: $(LIB) $(INSTALL_DATA) $< $@ -- cgit v1.2.3