diff options
Diffstat (limited to 'cpukit/libfs/src')
178 files changed, 42822 insertions, 0 deletions
diff --git a/cpukit/libfs/src/defaults/default_chown.c b/cpukit/libfs/src/defaults/default_chown.c new file mode 100644 index 0000000000..82cb0b694f --- /dev/null +++ b/cpukit/libfs/src/defaults/default_chown.c @@ -0,0 +1,23 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_chown( + rtems_filesystem_location_info_t *pathloc, /* IN */ + uid_t owner, /* IN */ + gid_t group /* IN */ +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_close.c b/cpukit/libfs/src/defaults/default_close.c new file mode 100644 index 0000000000..bd7082184c --- /dev/null +++ b/cpukit/libfs/src/defaults/default_close.c @@ -0,0 +1,19 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> + +int rtems_filesystem_default_close( + rtems_libio_t *iop +) +{ + return 0; +} diff --git a/cpukit/libfs/src/defaults/default_evalformake.c b/cpukit/libfs/src/defaults/default_evalformake.c new file mode 100644 index 0000000000..891d961dc1 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_evalformake.c @@ -0,0 +1,32 @@ +/** + * @file + * + * @ingroup LibIO + * + * @brief rtems_filesystem_default_evalformake() implementation. + */ + +/* + * Copyright (c) 2010 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#include <rtems/libio.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_evalformake( + const char *path, + rtems_filesystem_location_info_t *pathloc, + const char **name +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_evalpath.c b/cpukit/libfs/src/defaults/default_evalpath.c new file mode 100644 index 0000000000..1e3f762cbe --- /dev/null +++ b/cpukit/libfs/src/defaults/default_evalpath.c @@ -0,0 +1,25 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_evalpath( + const char *pathname, /* IN */ + size_t pathnamelen, /* IN */ + int flags, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */ +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} + diff --git a/cpukit/libfs/src/defaults/default_evaluate_link.c b/cpukit/libfs/src/defaults/default_evaluate_link.c new file mode 100644 index 0000000000..a82c73729f --- /dev/null +++ b/cpukit/libfs/src/defaults/default_evaluate_link.c @@ -0,0 +1,23 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_evaluate_link( + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + int flags /* IN */ +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} + diff --git a/cpukit/libfs/src/defaults/default_fchmod.c b/cpukit/libfs/src/defaults/default_fchmod.c new file mode 100644 index 0000000000..cda32c82d5 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_fchmod.c @@ -0,0 +1,22 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_fchmod( + rtems_filesystem_location_info_t *loc, + mode_t mode +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_fcntl.c b/cpukit/libfs/src/defaults/default_fcntl.c new file mode 100644 index 0000000000..d9d8602e40 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_fcntl.c @@ -0,0 +1,22 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_fcntl( + int cmd, + rtems_libio_t *iop +) +{ + return 0; +} diff --git a/cpukit/libfs/src/defaults/default_fdatasync.c b/cpukit/libfs/src/defaults/default_fdatasync.c new file mode 100644 index 0000000000..90677df5c4 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_fdatasync.c @@ -0,0 +1,21 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_fdatasync( + rtems_libio_t *iop +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_fpathconf.c b/cpukit/libfs/src/defaults/default_fpathconf.c new file mode 100644 index 0000000000..f65d7d92fc --- /dev/null +++ b/cpukit/libfs/src/defaults/default_fpathconf.c @@ -0,0 +1,22 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_fpathconf( + rtems_libio_t *iop, + int name +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_freenode.c b/cpukit/libfs/src/defaults/default_freenode.c new file mode 100644 index 0000000000..bb231d13f7 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_freenode.c @@ -0,0 +1,22 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_freenode( + rtems_filesystem_location_info_t *pathloc /* IN */ +) +{ + return 0; +} + diff --git a/cpukit/libfs/src/defaults/default_fsmount.c b/cpukit/libfs/src/defaults/default_fsmount.c new file mode 100644 index 0000000000..5a442c82b6 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_fsmount.c @@ -0,0 +1,22 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_fsmount( + rtems_filesystem_mount_table_entry_t *mt_entry, /* IN */ + const void *data /* IN */ +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_fstat.c b/cpukit/libfs/src/defaults/default_fstat.c new file mode 100644 index 0000000000..6c8d501a21 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_fstat.c @@ -0,0 +1,22 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_fstat( + rtems_filesystem_location_info_t *loc, + struct stat *buf +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_fsunmount.c b/cpukit/libfs/src/defaults/default_fsunmount.c new file mode 100644 index 0000000000..e879f5c2f8 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_fsunmount.c @@ -0,0 +1,21 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_fsunmount( + rtems_filesystem_mount_table_entry_t *mt_entry /* IN */ +) +{ + return 0; +} diff --git a/cpukit/libfs/src/defaults/default_fsync.c b/cpukit/libfs/src/defaults/default_fsync.c new file mode 100644 index 0000000000..5cd352e9d4 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_fsync.c @@ -0,0 +1,21 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_fsync( + rtems_libio_t *iop +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_ftruncate.c b/cpukit/libfs/src/defaults/default_ftruncate.c new file mode 100644 index 0000000000..9214b77448 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_ftruncate.c @@ -0,0 +1,22 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_ftruncate( + rtems_libio_t *iop, + rtems_off64_t length +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_handlers.c b/cpukit/libfs/src/defaults/default_handlers.c new file mode 100644 index 0000000000..5343875295 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_handlers.c @@ -0,0 +1,39 @@ +/** + * @file + * + * @ingroup LibIO + * + * @brief rtems_filesystem_handlers_default definition. + */ + +/* + * Copyright (c) 2010 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#include <rtems/libio.h> + +const rtems_filesystem_file_handlers_r rtems_filesystem_handlers_default = { + .open_h = rtems_filesystem_default_open, + .close_h = rtems_filesystem_default_close, + .read_h = rtems_filesystem_default_read, + .write_h = rtems_filesystem_default_write, + .ioctl_h = rtems_filesystem_default_ioctl, + .lseek_h = rtems_filesystem_default_lseek, + .fstat_h = rtems_filesystem_default_fstat, + .fchmod_h = rtems_filesystem_default_fchmod, + .ftruncate_h = rtems_filesystem_default_ftruncate, + .fpathconf_h = rtems_filesystem_default_fpathconf, + .fsync_h = rtems_filesystem_default_fsync, + .fdatasync_h = rtems_filesystem_default_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .rmnod_h = rtems_filesystem_default_rmnod +}; diff --git a/cpukit/libfs/src/defaults/default_ioctl.c b/cpukit/libfs/src/defaults/default_ioctl.c new file mode 100644 index 0000000000..06816c48d8 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_ioctl.c @@ -0,0 +1,23 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_ioctl( + rtems_libio_t *iop, + uint32_t command, + void *buffer +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_link.c b/cpukit/libfs/src/defaults/default_link.c new file mode 100644 index 0000000000..d9bc913b27 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_link.c @@ -0,0 +1,24 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_link( + rtems_filesystem_location_info_t *to_loc, /* IN */ + rtems_filesystem_location_info_t *parent_loc, /* IN */ + const char *name /* IN */ +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} + diff --git a/cpukit/libfs/src/defaults/default_lseek.c b/cpukit/libfs/src/defaults/default_lseek.c new file mode 100644 index 0000000000..21ae23ee93 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_lseek.c @@ -0,0 +1,23 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +rtems_off64_t rtems_filesystem_default_lseek( + rtems_libio_t *iop, + rtems_off64_t length, + int whence +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_mknod.c b/cpukit/libfs/src/defaults/default_mknod.c new file mode 100644 index 0000000000..fcd03be4c2 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_mknod.c @@ -0,0 +1,24 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_mknod( + const char *path, /* IN */ + mode_t mode, /* IN */ + dev_t dev, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */ +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_mount.c b/cpukit/libfs/src/defaults/default_mount.c new file mode 100644 index 0000000000..9063b554bc --- /dev/null +++ b/cpukit/libfs/src/defaults/default_mount.c @@ -0,0 +1,21 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_mount ( + rtems_filesystem_mount_table_entry_t *mt_entry /* IN */ +) +{ + return 0; +} diff --git a/cpukit/libfs/src/defaults/default_node_type.c b/cpukit/libfs/src/defaults/default_node_type.c new file mode 100644 index 0000000000..a118a04f72 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_node_type.c @@ -0,0 +1,29 @@ +/** + * @file + * + * @ingroup LibIO + * + * @brief rtems_filesystem_default_node_type() implementation. + */ + +/* + * Copyright (c) 2010 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#include <rtems/libio.h> + +rtems_filesystem_node_types_t rtems_filesystem_default_node_type( + rtems_filesystem_location_info_t *pathloc +) +{ + return RTEMS_FILESYSTEM_INVALID_NODE_TYPE; +} diff --git a/cpukit/libfs/src/defaults/default_open.c b/cpukit/libfs/src/defaults/default_open.c new file mode 100644 index 0000000000..b119fbf5be --- /dev/null +++ b/cpukit/libfs/src/defaults/default_open.c @@ -0,0 +1,24 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_open( + rtems_libio_t *iop, + const char *pathname, + uint32_t flag, + uint32_t mode +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_ops.c b/cpukit/libfs/src/defaults/default_ops.c new file mode 100644 index 0000000000..575cf6b2d4 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_ops.c @@ -0,0 +1,43 @@ +/** + * @file + * + * @ingroup LibIO + * + * @brief rtems_filesystem_operations_default definition. + */ + +/* + * Copyright (c) 2010 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#include <rtems/libio.h> + +const rtems_filesystem_operations_table rtems_filesystem_operations_default = { + .evalpath_h = rtems_filesystem_default_evalpath, + .evalformake_h = rtems_filesystem_default_evalformake, + .link_h = rtems_filesystem_default_link, + .unlink_h = rtems_filesystem_default_unlink, + .node_type_h = rtems_filesystem_default_node_type, + .mknod_h = rtems_filesystem_default_mknod, + .chown_h = rtems_filesystem_default_chown, + .freenod_h = rtems_filesystem_default_freenode, + .mount_h = rtems_filesystem_default_mount, + .fsmount_me_h = rtems_filesystem_default_fsmount, + .unmount_h = rtems_filesystem_default_unmount, + .fsunmount_me_h = rtems_filesystem_default_fsunmount, + .utime_h = rtems_filesystem_default_utime, + .eval_link_h = rtems_filesystem_default_evaluate_link, + .symlink_h = rtems_filesystem_default_symlink, + .readlink_h = rtems_filesystem_default_readlink, + .rename_h = rtems_filesystem_default_rename, + .statvfs_h = rtems_filesystem_default_statvfs +}; diff --git a/cpukit/libfs/src/defaults/default_read.c b/cpukit/libfs/src/defaults/default_read.c new file mode 100644 index 0000000000..2f6d7e9d72 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_read.c @@ -0,0 +1,23 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +ssize_t rtems_filesystem_default_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_readlink.c b/cpukit/libfs/src/defaults/default_readlink.c new file mode 100644 index 0000000000..1d07970999 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_readlink.c @@ -0,0 +1,23 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +ssize_t rtems_filesystem_default_readlink( + rtems_filesystem_location_info_t *loc, /* IN */ + char *buf, /* OUT */ + size_t bufsize +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_rename.c b/cpukit/libfs/src/defaults/default_rename.c new file mode 100644 index 0000000000..93bf15f2e0 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_rename.c @@ -0,0 +1,24 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_rename( + rtems_filesystem_location_info_t *old_parent_loc, /* IN */ + rtems_filesystem_location_info_t *old_loc, /* IN */ + rtems_filesystem_location_info_t *new_parent_loc, /* IN */ + const char *name /* IN */ +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_rmnod.c b/cpukit/libfs/src/defaults/default_rmnod.c new file mode 100644 index 0000000000..776ee4b659 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_rmnod.c @@ -0,0 +1,22 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_rmnod( + rtems_filesystem_location_info_t *parent_loc, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN */ +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_statvfs.c b/cpukit/libfs/src/defaults/default_statvfs.c new file mode 100644 index 0000000000..22d891815f --- /dev/null +++ b/cpukit/libfs/src/defaults/default_statvfs.c @@ -0,0 +1,22 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_statvfs( + rtems_filesystem_location_info_t *loc, /* IN */ + struct statvfs *buf /* OUT */ +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_symlink.c b/cpukit/libfs/src/defaults/default_symlink.c new file mode 100644 index 0000000000..8985d76aef --- /dev/null +++ b/cpukit/libfs/src/defaults/default_symlink.c @@ -0,0 +1,23 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_symlink( + rtems_filesystem_location_info_t *loc, /* IN */ + const char *link_name, /* IN */ + const char *node_name +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_unlink.c b/cpukit/libfs/src/defaults/default_unlink.c new file mode 100644 index 0000000000..cf633c6ce1 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_unlink.c @@ -0,0 +1,22 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_unlink( + rtems_filesystem_location_info_t *parent_pathloc, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN */ +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/defaults/default_unmount.c b/cpukit/libfs/src/defaults/default_unmount.c new file mode 100644 index 0000000000..9fc53c060f --- /dev/null +++ b/cpukit/libfs/src/defaults/default_unmount.c @@ -0,0 +1,21 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_unmount( + rtems_filesystem_mount_table_entry_t *mt_entry /* IN */ +) +{ + return 0; +} diff --git a/cpukit/libfs/src/defaults/default_utime.c b/cpukit/libfs/src/defaults/default_utime.c new file mode 100644 index 0000000000..77a2f2e8d6 --- /dev/null +++ b/cpukit/libfs/src/defaults/default_utime.c @@ -0,0 +1,24 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int rtems_filesystem_default_utime( + rtems_filesystem_location_info_t *pathloc, /* IN */ + time_t actime, /* IN */ + time_t modtime /* IN */ +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} + diff --git a/cpukit/libfs/src/defaults/default_write.c b/cpukit/libfs/src/defaults/default_write.c new file mode 100644 index 0000000000..97f057f08f --- /dev/null +++ b/cpukit/libfs/src/defaults/default_write.c @@ -0,0 +1,23 @@ +/* + * COPYRIGHT (c) 2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +ssize_t rtems_filesystem_default_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ + rtems_set_errno_and_return_minus_one( ENOTSUP ); +} diff --git a/cpukit/libfs/src/devfs/devclose.c b/cpukit/libfs/src/devfs/devclose.c new file mode 100644 index 0000000000..773cade5c6 --- /dev/null +++ b/cpukit/libfs/src/devfs/devclose.c @@ -0,0 +1,41 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/io.h> + +#include "devfs.h" + +int devFS_close( + rtems_libio_t *iop +) +{ + rtems_libio_open_close_args_t args; + rtems_status_code status; + rtems_device_name_t *np; + + np = (rtems_device_name_t *)iop->pathinfo.node_access; + + args.iop = iop; + args.flags = 0; + args.mode = 0; + + status = rtems_io_close( + np->major, + np->minor, + (void *) &args + ); + + return rtems_deviceio_errno(status); +} + + diff --git a/cpukit/libfs/src/devfs/devfs.h b/cpukit/libfs/src/devfs/devfs.h new file mode 100644 index 0000000000..eafa069cb8 --- /dev/null +++ b/cpukit/libfs/src/devfs/devfs.h @@ -0,0 +1,280 @@ +/** +* @file libfs/devfs/devfs.h +* +* This include file contains all constants and structures associated +* with the 'device-only' filesystem. +*/ + +#ifndef _RTEMS_DEVFS_H +#define _RTEMS_DEVFS_H + +#include <rtems/libio_.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * This structure define the type of device table + */ + +typedef struct +{ + /** This member points to device name which is a null-terminated string */ + char *device_name; + /** This member is the name length of a device */ + uint32_t device_name_length; + /** major number of a device */ + rtems_device_major_number major; + /** minor number of a device */ + rtems_device_minor_number minor; + /** device creation mode, only device file can be created */ + mode_t mode; + +} rtems_device_name_t; + + + +/** + * This routine associates RTEMS status code with errno + */ + +extern int rtems_deviceio_errno(rtems_status_code code); + + +/** + * The following defines the device table size. This values + * is configured during application configuration time by + * the user. The default value is set to 4. + */ + +extern uint32_t rtems_device_table_size; + +/** + * This handler maps open operation to rtems_io_open. + * @param iop This is the RTEMS's internal representation of file. + * @param pathname a null-terminated string that starts with /dev. + * @param flag access flags + * @param mode access mode + * @retval the same as open + */ + +extern int devFS_open( + rtems_libio_t *iop, + const char *pathname, + uint32_t flag, + uint32_t mode +); + + +/** + * This handler maps close operation to rtems_io_close. + * @param iop This is the RTEMS's internal representation of file + * @retval the same as close + */ + + +extern int devFS_close( + rtems_libio_t *iop +); + + +/** + * This handler maps read operation to rtems_io_read. + * @param iop This is the RTEMS's internal representation of file + * @param buffer memory location to store read data + * @param count how many bytes to read + * @retval On successful, this routine returns total bytes read. On error + * it returns -1 and errno is set to proper value. + */ + +extern ssize_t devFS_read( + rtems_libio_t *iop, + void *buffer, + size_t count +); + + +/** + * This handler maps write operation to rtems_io_write. + * @param iop This is the RTEMS's internal representation of file + * @param buffer data to be written + * @param count how many bytes to write + * @retval On successful, this routine returns total bytes written. On error + * it returns -1 and errno is set to proper value. + */ + +extern ssize_t devFS_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +); + + +/** + * This handler maps ioctl operation to rtems_io_ioctl. + * @param iop This is the RTEMS's internal representation of file + * @param command io control command + * @param buffer io control parameters + * @retval On successful, this routine returns total bytes written. On error + * it returns -1 and errno is set to proper value. + */ + +extern int devFS_ioctl( + rtems_libio_t *iop, + uint32_t command, + void *buffer +); + + + + +/** + * This handler gets the device file information. This routine only set the following member of struct stat: + * st_dev : device number + * st_mode: device file creation mode, only two mode are accepted: + * S_IFCHR: character device file + * S_IFBLK: block device file + * @param loc contains filesystem access information + * @param buf buffer to hold the device file's information + * @retval On successful, this routine returns 0. On error + * it returns -1 and errno is set to proper value. + */ + +extern int devFS_stat( + rtems_filesystem_location_info_t *loc, + struct stat *buf +); + + + +/** + * This routine is invoked upon determination of a node type. + * Since this is a device-only filesystem, so there is only + * one node type in the system. + * + * @param pathloc contains filesytem access information, this + * parameter is ignored + * @retval always returns RTEMS_FILESYSTEM_DEVICE + */ + +extern rtems_filesystem_node_types_t devFS_node_type( + rtems_filesystem_location_info_t *pathloc +); + + + +/** + * This routine is invoked to determine if 'pathname' exists. + * This routine first check access flags, then it searches + * the device table to get the information. + * + * @param pathname device name to be searched + * @param flags access flags + * @param pathloc contains filesystem access information + * @retval upon success(pathname exists), this routines + * returns 0; if 'flag' is invalid, it returns -1 and errno + * is set to EIO; otherwise, it returns -1 and errno is set to ENOENT + */ + +extern int devFS_evaluate_path( + const char *pathname, + size_t pathnamelen, + int flags, + rtems_filesystem_location_info_t *pathloc +); + + +/** + * This routine is given a path to evaluate and a valid start + * location. It is responsible for finding the parent node for + * a requested make command, setting pathloc information to + * identify the parent node, and setting the name pointer to + * the first character of the name of the new node. In device + * only filesystem, devices do not has a tree hierarchy, there + * are no parent-child relationship. So this routine is rather + * simple, it just set *name to path and returns + * + * @param path device path to be evaluated + * @param pathloc contains filesystem access information, this + * parameter is ignored + * @param name + * @retval always returns 0 + */ + +extern int devFS_evaluate_for_make( + const char *path, + rtems_filesystem_location_info_t *pathloc, + const char **name +); + + + +/** + * This routine is invoked upon registration of a new device + * file. It is responsible for creating a item in the main + * device table. This routine searches the device table in + * sequential order, when found a empty slot, it fills the slot + * with proper values. + * + * @param path the device file name to be registered + * @param mode file mode, this parameter is ignored + * @param dev device major and minor number + * @param pathloc contains filesystem access information + * @retval upon success, this routine returns 0; if 'path' + * already exist, it returns -1 and errno is set to EEXIST; + * if device table is full, it returns -1 and errno is set + * to ENOMEM + */ + +extern int devFS_mknod( + const char *path, + mode_t mode, + dev_t dev, + rtems_filesystem_location_info_t *pathloc +); + + +/** + * This routine is invoked upon rtems filesystem initialization. + * It is responsible for creating the main device table, + * initializing it to a known state, and set device file operation + * handlers. After this, the device-only filesytem is ready for use + * + * @param mt_entry The filesystem mount table entry. + * @param data Filesystem specific data. + * @retval upon success, this routine returns 0; otherwise it returns + * -1 and errno is set to proper value. The only error is when malloc + * failed, and errno is set to NOMEM. + */ + +extern int devFS_initialize( + rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data +); + + +/** + * This routine retrieves all the device registered in system, and + * prints out their detail information. For example, on one system, + * devFS_show will print out following message: + * + * /dev/console 0 0 + * /dev/clock 1 0 + * /dev/tty0 0 0 + * /flash 2 0 + * + * This routine is intended for debugging, and can be used by shell + * program to provide user with the system information. + * + * @retval 0 + */ + +extern int devFS_Show(void); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/cpukit/libfs/src/devfs/devfs_eval.c b/cpukit/libfs/src/devfs/devfs_eval.c new file mode 100644 index 0000000000..a3169fb3fa --- /dev/null +++ b/cpukit/libfs/src/devfs/devfs_eval.c @@ -0,0 +1,85 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/seterr.h> +#include <fcntl.h> +#include <assert.h> +#include "devfs.h" + +/** + * The following defines the device-only filesystem operating + * handlers. + */ + +extern rtems_filesystem_operations_table devFS_ops; + +/** + * The following defines the device-only filesystem operating + * handlers. + */ + +extern rtems_filesystem_file_handlers_r devFS_file_handlers; + +int devFS_evaluate_path( + const char *pathname, + size_t pathnamelen, + int flags, + rtems_filesystem_location_info_t *pathloc +) +{ + int i; + rtems_device_name_t *device_name_table; + + /* see if 'flags' is valid */ + if ( !rtems_libio_is_valid_perms( flags ) ) + rtems_set_errno_and_return_minus_one( EPERM ); + + /* get the device name table */ + device_name_table = (rtems_device_name_t *)pathloc->node_access; + if (!device_name_table) + rtems_set_errno_and_return_minus_one( EFAULT ); + + for (i = 0; i < rtems_device_table_size; i++) { + if (!device_name_table[i].device_name) + continue; + + if (strncmp(pathname, device_name_table[i].device_name, pathnamelen) != 0) + continue; + + if (device_name_table[i].device_name[pathnamelen] != '\0') + continue; + + /* find the device, set proper values */ + pathloc->node_access = (void *)&device_name_table[i]; + pathloc->handlers = &devFS_file_handlers; + pathloc->ops = &devFS_ops; + pathloc->mt_entry = rtems_filesystem_root.mt_entry; + return 0; + } + + /* no such file or directory */ + rtems_set_errno_and_return_minus_one( ENOENT ); +} + + + +int devFS_evaluate_for_make( + const char *path, + rtems_filesystem_location_info_t *pathloc, + const char **name +) +{ + /* we do nothing, just set name to path */ + *name = path; + return 0; +} + diff --git a/cpukit/libfs/src/devfs/devfs_init.c b/cpukit/libfs/src/devfs/devfs_init.c new file mode 100644 index 0000000000..5f6521a045 --- /dev/null +++ b/cpukit/libfs/src/devfs/devfs_init.c @@ -0,0 +1,92 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <rtems.h> +#include <rtems/seterr.h> +#include <rtems/score/wkspace.h> +#include "devfs.h" + +rtems_filesystem_operations_table devFS_ops = +{ + devFS_evaluate_path, + devFS_evaluate_for_make, + rtems_filesystem_default_link, + rtems_filesystem_default_unlink, + devFS_node_type, + devFS_mknod, + rtems_filesystem_default_chown, + rtems_filesystem_default_freenode, + rtems_filesystem_default_mount, + devFS_initialize, + rtems_filesystem_default_unmount, + rtems_filesystem_default_fsunmount, + rtems_filesystem_default_utime, + rtems_filesystem_default_evaluate_link, + rtems_filesystem_default_symlink, + rtems_filesystem_default_readlink, + rtems_filesystem_default_rename, + rtems_filesystem_default_statvfs +}; + + +rtems_filesystem_file_handlers_r devFS_file_handlers = +{ + devFS_open, + devFS_close, + devFS_read, + devFS_write, + devFS_ioctl, + rtems_filesystem_default_lseek, + devFS_stat, + rtems_filesystem_default_fchmod, + rtems_filesystem_default_ftruncate, + rtems_filesystem_default_fpathconf, + rtems_filesystem_default_fsync, + rtems_filesystem_default_fdatasync, + rtems_filesystem_default_fcntl, + rtems_filesystem_default_rmnod +}; + + + +int devFS_initialize( + rtems_filesystem_mount_table_entry_t *temp_mt_entry, + const void *data +) +{ + rtems_device_name_t *device_name_table; + + /* allocate device only filesystem name table */ + device_name_table = (rtems_device_name_t *)_Workspace_Allocate( + sizeof( rtems_device_name_t ) * ( rtems_device_table_size ) + ); + + /* no memory for device filesystem */ + if (!device_name_table) + rtems_set_errno_and_return_minus_one( ENOMEM ); + + memset( + device_name_table, 0, + sizeof( rtems_device_name_t ) * ( rtems_device_table_size ) + ); + + /* set file handlers */ + temp_mt_entry->mt_fs_root.handlers = &devFS_file_handlers; + temp_mt_entry->mt_fs_root.ops = &devFS_ops; + + /* Set the node_access to device name table */ + temp_mt_entry->mt_fs_root.node_access = (void *)device_name_table; + + return 0; +} + diff --git a/cpukit/libfs/src/devfs/devfs_mknod.c b/cpukit/libfs/src/devfs/devfs_mknod.c new file mode 100644 index 0000000000..d8e5a30d5f --- /dev/null +++ b/cpukit/libfs/src/devfs/devfs_mknod.c @@ -0,0 +1,81 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> + +#include <rtems/seterr.h> +#include "devfs.h" + +int devFS_mknod( + const char *path, + mode_t mode, + dev_t dev, + rtems_filesystem_location_info_t *pathloc +) +{ + int i; + int slot; + rtems_device_name_t *device_name_table; + rtems_device_major_number major; + rtems_device_minor_number minor; + ISR_Level level; + + /* + * This is a special case. In rtems_filesystem_initialize, + * a special device '/dev' will be created. We check this + * condition and do not create the '/dev' and the 'path' + * actually passed in is 'dev', not '/dev'. Just return 0 to + * indicate we are OK. + */ + + if ((path[0] == 'd') && (path[1] == 'e') && + (path[2] == 'v') && (path[3] == '\0')) + return 0; + + /* must be a character device or a block device */ + if (!S_ISBLK(mode) && !S_ISCHR(mode)) + rtems_set_errno_and_return_minus_one( EINVAL ); + else + rtems_filesystem_split_dev_t(dev, major, minor); + + /* Find an empty slot in device name table */ + device_name_table = (rtems_device_name_t *)pathloc->node_access; + if (!device_name_table) + rtems_set_errno_and_return_minus_one( EFAULT ); + + for (slot = -1, i = 0; i < rtems_device_table_size; i++){ + if (device_name_table[i].device_name == NULL) + slot = i; + else + if (strcmp(path, device_name_table[i].device_name) == 0) + rtems_set_errno_and_return_minus_one( EEXIST ); + } + + if (slot == -1) + rtems_set_errno_and_return_minus_one( ENOMEM ); + + _ISR_Disable(level); + device_name_table[slot].device_name = (char *)path; + device_name_table[slot].device_name_length = strlen(path); + device_name_table[slot].major = major; + device_name_table[slot].minor = minor; + device_name_table[slot].mode = mode; + _ISR_Enable(level); + + return 0; +} + diff --git a/cpukit/libfs/src/devfs/devfs_node_type.c b/cpukit/libfs/src/devfs/devfs_node_type.c new file mode 100644 index 0000000000..0bede52d2d --- /dev/null +++ b/cpukit/libfs/src/devfs/devfs_node_type.c @@ -0,0 +1,26 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "devfs.h" + +rtems_filesystem_node_types_t devFS_node_type( + rtems_filesystem_location_info_t *pathloc +) +{ + /* + * There is only one type of node: device + */ + + return RTEMS_FILESYSTEM_DEVICE; +} + + diff --git a/cpukit/libfs/src/devfs/devfs_show.c b/cpukit/libfs/src/devfs/devfs_show.c new file mode 100644 index 0000000000..e449caf49f --- /dev/null +++ b/cpukit/libfs/src/devfs/devfs_show.c @@ -0,0 +1,36 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/seterr.h> +#include "devfs.h" + +int devFS_Show(void) +{ + int i; + rtems_filesystem_location_info_t *temp_loc; + rtems_device_name_t *device_name_table; + + temp_loc = &rtems_filesystem_root; + device_name_table = (rtems_device_name_t *)temp_loc->node_access; + if (!device_name_table) + rtems_set_errno_and_return_minus_one( EFAULT ); + + for (i = 0; i < rtems_device_table_size; i++){ + if (device_name_table[i].device_name){ + printk("/%s %d %d\n", device_name_table[i].device_name, + device_name_table[i].major, device_name_table[i].minor); + } + } + return 0; +} + + diff --git a/cpukit/libfs/src/devfs/devioctl.c b/cpukit/libfs/src/devfs/devioctl.c new file mode 100644 index 0000000000..15965b8c0e --- /dev/null +++ b/cpukit/libfs/src/devfs/devioctl.c @@ -0,0 +1,45 @@ +#if HAVE_CONFIG_H +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/io.h> + +#include "devfs.h" + +int devFS_ioctl( + rtems_libio_t *iop, + uint32_t command, + void *buffer +) +{ + rtems_libio_ioctl_args_t args; + rtems_status_code status; + rtems_device_name_t *np; + + np = (rtems_device_name_t *)iop->pathinfo.node_access; + + args.iop = iop; + args.command = command; + args.buffer = buffer; + + status = rtems_io_control( + np->major, + np->minor, + (void *) &args + ); + + if ( status ) + return rtems_deviceio_errno(status); + + return args.ioctl_return; +} + diff --git a/cpukit/libfs/src/devfs/devopen.c b/cpukit/libfs/src/devfs/devopen.c new file mode 100644 index 0000000000..67be3678e1 --- /dev/null +++ b/cpukit/libfs/src/devfs/devopen.c @@ -0,0 +1,42 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/io.h> + +#include "devfs.h" + +int devFS_open( + rtems_libio_t *iop, + const char *pathname, + uint32_t flag, + uint32_t mode +) +{ + rtems_libio_open_close_args_t args; + rtems_status_code status; + rtems_device_name_t *np; + + np = (rtems_device_name_t *)iop->pathinfo.node_access; + + args.iop = iop; + args.flags = iop->flags; + args.mode = mode; + + status = rtems_io_open( + np->major, + np->minor, + (void *) &args + ); + + return rtems_deviceio_errno(status); +} diff --git a/cpukit/libfs/src/devfs/devread.c b/cpukit/libfs/src/devfs/devread.c new file mode 100644 index 0000000000..10f74e81c9 --- /dev/null +++ b/cpukit/libfs/src/devfs/devread.c @@ -0,0 +1,48 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/io.h> + +#include "devfs.h" + +ssize_t devFS_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + rtems_libio_rw_args_t args; + rtems_status_code status; + rtems_device_name_t *np; + + np = (rtems_device_name_t *)iop->pathinfo.node_access; + + args.iop = iop; + args.offset = iop->offset; + args.buffer = buffer; + args.count = count; + args.flags = iop->flags; + args.bytes_moved = 0; + + status = rtems_io_read( + np->major, + np->minor, + (void *) &args + ); + + if ( status ) + return rtems_deviceio_errno(status); + + return (ssize_t) args.bytes_moved; +} + diff --git a/cpukit/libfs/src/devfs/devstat.c b/cpukit/libfs/src/devfs/devstat.c new file mode 100644 index 0000000000..db17595621 --- /dev/null +++ b/cpukit/libfs/src/devfs/devstat.c @@ -0,0 +1,46 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/io.h> +#include <rtems/seterr.h> +#include <rtems/libio.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "devfs.h" + +int devFS_stat( + rtems_filesystem_location_info_t *loc, + struct stat *buf +) +{ + rtems_device_name_t *the_dev; + + the_dev = (rtems_device_name_t *)loc->node_access; + + /* + * stat() invokes devFS_evaluate_path() which checks that node_access + * is not NULL. So this should NEVER be NULL unless someone breaks + * other code in this filesystem. + */ + #if defined(RTEMS_DEBUG) + if (!the_dev) + rtems_set_errno_and_return_minus_one( EFAULT ); + #endif + + buf->st_rdev = rtems_filesystem_make_dev_t( the_dev->major, the_dev->minor ); + buf->st_mode = the_dev->mode; + return 0; +} + + diff --git a/cpukit/libfs/src/devfs/devwrite.c b/cpukit/libfs/src/devfs/devwrite.c new file mode 100644 index 0000000000..5389c69bc5 --- /dev/null +++ b/cpukit/libfs/src/devfs/devwrite.c @@ -0,0 +1,48 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/io.h> + +#include "devfs.h" + +ssize_t devFS_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ + rtems_libio_rw_args_t args; + rtems_status_code status; + rtems_device_name_t *np; + + np = (rtems_device_name_t *)iop->pathinfo.node_access; + + args.iop = iop; + args.offset = iop->offset; + args.buffer = (void *) buffer; + args.count = count; + args.flags = iop->flags; + args.bytes_moved = 0; + + status = rtems_io_write( + np->major, + np->minor, + (void *) &args + ); + + if ( status ) + return rtems_deviceio_errno(status); + + return (ssize_t) args.bytes_moved; +} + diff --git a/cpukit/libfs/src/dosfs/.cvsignore b/cpukit/libfs/src/dosfs/.cvsignore new file mode 100644 index 0000000000..4b0640c9c5 --- /dev/null +++ b/cpukit/libfs/src/dosfs/.cvsignore @@ -0,0 +1,4 @@ +config.h +config.h.in +stamp-h +stamp-h.in diff --git a/cpukit/libfs/src/dosfs/dosfs.h b/cpukit/libfs/src/dosfs/dosfs.h new file mode 100644 index 0000000000..5b6cdec2bf --- /dev/null +++ b/cpukit/libfs/src/dosfs/dosfs.h @@ -0,0 +1,88 @@ +/** + * @file rtems/dosfs.h + * + * Application interface to MSDOS filesystem. + */ + +/* + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ + +#ifndef _RTEMS_DOSFS_H +#define _RTEMS_DOSFS_H + +#include <rtems.h> +#include <rtems/libio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +int rtems_dosfs_initialize(rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data); + +#define MSDOS_FMT_FATANY 0 +#define MSDOS_FMT_FAT12 1 +#define MSDOS_FMT_FAT16 2 +#define MSDOS_FMT_FAT32 3 + +#define MSDOS_FMT_INFO_LEVEL_NONE (0) +#define MSDOS_FMT_INFO_LEVEL_INFO (1) +#define MSDOS_FMT_INFO_LEVEL_DETAIL (2) +#define MSDOS_FMT_INFO_LEVEL_DEBUG (3) + +/* + * data to be filled out for formatter: parameters for format call + * any parameter set to 0 or NULL will be automatically detected/computed + */ +typedef struct { + const char *OEMName; /* OEM Name string or NULL */ + const char *VolLabel; /* Volume Label string or NULL */ + uint32_t sectors_per_cluster; /* request value: sectors per cluster */ + uint32_t fat_num; /* request value: number of FATs on disk */ + uint32_t files_per_root_dir; /* request value: file entries in root */ + uint8_t fattype; /* request value: MSDOS_FMT_FAT12/16/32 */ + uint8_t media; /* media code. default: 0xF8 */ + bool quick_format; /* true: do not clear out data sectors */ + uint32_t cluster_align; /* requested value: cluster alignment */ + /* make sector number of first sector */ + /* of first cluster divisible by this */ + /* value. This can optimize clusters */ + /* to be located at start of track */ + /* or start of flash block */ + int info_level; /* The amount of info to output */ +} msdos_format_request_param_t; + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int msdos_format +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| format device with msdos filesystem | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const char *devname, /* device name */ + const msdos_format_request_param_t *rqdata /* requested fmt parameters */ + /* set to NULL for automatic */ + /* determination */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libfs/src/dosfs/fat.c b/cpukit/libfs/src/dosfs/fat.c new file mode 100644 index 0000000000..ba9ae23e86 --- /dev/null +++ b/cpukit/libfs/src/dosfs/fat.c @@ -0,0 +1,863 @@ +/* + * fat.c + * + * Low-level operations on a volume with FAT filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> + +#include <rtems/libio_.h> + +#include "fat.h" +#include "fat_fat_operations.h" + +int +fat_buf_access(fat_fs_info_t *fs_info, uint32_t blk, int op_type, + rtems_bdbuf_buffer **buf) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + uint8_t i; + bool 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) + rtems_set_errno_and_return_minus_one(EIO); + fs_info->c.blk_num = blk; + fs_info->c.modified = 0; + 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); + fs_info->c.state = FAT_CACHE_EMPTY; + fs_info->c.modified = 0; + if (sc != RTEMS_SUCCESSFUL) + rtems_set_errno_and_return_minus_one(EIO); + + if (sec_of_fat && !fs_info->vol.mirror) + { + rtems_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) + rtems_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) + rtems_set_errno_and_return_minus_one(ENOMEM); + } + } + } + else + { + sc = rtems_bdbuf_release(fs_info->c.buf); + fs_info->c.state = FAT_CACHE_EMPTY; + if (sc != RTEMS_SUCCESSFUL) + rtems_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) + rtems_set_errno_and_return_minus_one(EIO); + fs_info->c.blk_num = blk; + fs_info->c.state = FAT_CACHE_ACTUAL; + } + *buf = fs_info->c.buf; + return RC_OK; +} + +int +fat_buf_release(fat_fs_info_t *fs_info) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + uint8_t i; + bool 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) + rtems_set_errno_and_return_minus_one(EIO); + fs_info->c.modified = 0; + + if (sec_of_fat && !fs_info->vol.mirror) + { + rtems_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) + rtems_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) + rtems_set_errno_and_return_minus_one(ENOMEM); + } + } + } + else + { + sc = rtems_bdbuf_release(fs_info->c.buf); + if (sc != RTEMS_SUCCESSFUL) + rtems_set_errno_and_return_minus_one(EIO); + } + fs_info->c.state = FAT_CACHE_EMPTY; + return RC_OK; +} + +/* _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, + uint32_t start, + uint32_t offset, + uint32_t count, + void *buff + ) +{ + int rc = RC_OK; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + ssize_t cmpltd = 0; + uint32_t blk = start; + uint32_t ofs = offset; + rtems_bdbuf_buffer *block = NULL; + uint32_t c = 0; + + while (count > 0) + { + rc = fat_buf_access(fs_info, blk, FAT_OP_TYPE_READ, &block); + if (rc != RC_OK) + return -1; + + 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, + uint32_t start, + uint32_t offset, + uint32_t count, + const void *buff) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + ssize_t cmpltd = 0; + uint32_t blk = start; + uint32_t ofs = offset; + rtems_bdbuf_buffer *block = NULL; + uint32_t 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 -1; + + memcpy((block->buffer + ofs), (buff + cmpltd), c); + + fat_buf_mark_modified(fs_info); + + count -= c; + cmpltd +=c; + blk++; + ofs = 0; + } + return cmpltd; +} + +/* _fat_block_release -- + * This function works around the hack that hold a bdbuf and does + * not release it. + * + * PARAMETERS: + * mt_entry - mount table entry + * + * RETURNS: + * 0 on success, or -1 if error occured and errno set appropriately + */ +int +_fat_block_release( + rtems_filesystem_mount_table_entry_t *mt_entry) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + return fat_buf_release(fs_info); +} + +/* 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, + uint32_t cln, + void *buff + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t 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, + uint32_t cln, + const void *buff + ) +{ + fat_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t 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) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + register fat_vol_t *vol = &fs_info->vol; + uint32_t data_secs = 0; + char boot_rec[FAT_MAX_BPB_SIZE]; + char fs_info_sector[FAT_USEFUL_INFO_SIZE]; + ssize_t ret = 0; + struct stat stat_buf; + int i = 0; + rtems_bdbuf_buffer *block = NULL; + + rc = stat(mt_entry->dev, &stat_buf); + if (rc == -1) + return rc; + + /* Must be a block device. */ + if (!S_ISBLK(stat_buf.st_mode)) + rtems_set_errno_and_return_minus_one(ENOTTY); + + /* check that device is registred as block device and lock it */ + vol->dd = rtems_disk_obtain(stat_buf.st_rdev); + if (vol->dd == NULL) + rtems_set_errno_and_return_minus_one(EIO); + + vol->dev = stat_buf.st_rdev; + + /* Read boot record */ + /* FIXME: Asserts FAT_MAX_BPB_SIZE < bdbuf block size */ + sc = rtems_bdbuf_read( vol->dev, 0, &block); + if (sc != RTEMS_SUCCESSFUL) + { + rtems_disk_release(vol->dd); + rtems_set_errno_and_return_minus_one( EIO); + } + + memcpy( boot_rec, block->buffer, FAT_MAX_BPB_SIZE); + + sc = rtems_bdbuf_release( block); + if (sc != RTEMS_SUCCESSFUL) + { + rtems_disk_release(vol->dd); + rtems_set_errno_and_return_minus_one( EIO ); + } + + /* Evaluate boot record */ + vol->bps = FAT_GET_BR_BYTES_PER_SECTOR(boot_rec); + + if ( (vol->bps != 512) && + (vol->bps != 1024) && + (vol->bps != 2048) && + (vol->bps != 4096)) + { + rtems_disk_release(vol->dd); + rtems_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_GET_BR_SECTORS_PER_CLUSTER(boot_rec); + /* + * "sectors per cluster" of zero is invalid + * (and would hang the following loop) + */ + if (vol->spc == 0) + { + rtems_disk_release(vol->dd); + rtems_set_errno_and_return_minus_one(EINVAL); + } + + for (vol->spc_log2 = 0, i = vol->spc; (i & 1) == 0; + i >>= 1, vol->spc_log2++); + + /* + * "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); + rtems_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_GET_BR_FAT_NUM(boot_rec); + vol->fat_loc = FAT_GET_BR_RESERVED_SECTORS_NUM(boot_rec); + + vol->rdir_entrs = FAT_GET_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_GET_BR_SECTORS_PER_FAT(boot_rec)) != 0) + vol->fat_length = FAT_GET_BR_SECTORS_PER_FAT(boot_rec); + else + vol->fat_length = FAT_GET_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_GET_BR_TOTAL_SECTORS_NUM16(boot_rec)) != 0) + vol->tot_secs = FAT_GET_BR_TOTAL_SECTORS_NUM16(boot_rec); + else + vol->tot_secs = FAT_GET_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_GET_BR_FAT32_ROOT_CLUSTER(boot_rec); + + vol->mirror = FAT_GET_BR_EXT_FLAGS(boot_rec) & FAT_BR_EXT_FLAGS_MIRROR; + if (vol->mirror) + vol->afat = FAT_GET_BR_EXT_FLAGS(boot_rec) & FAT_BR_EXT_FLAGS_FAT_NUM; + else + vol->afat = 0; + + vol->info_sec = FAT_GET_BR_FAT32_FS_INFO_SECTOR(boot_rec); + if( vol->info_sec == 0 ) + { + rtems_disk_release(vol->dd); + rtems_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_GET_FSINFO_LEAD_SIGNATURE(fs_info_sector) != + FAT_FSINFO_LEAD_SIGNATURE_VALUE) + { + _fat_block_release(mt_entry); + rtems_disk_release(vol->dd); + rtems_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 ) + { + _fat_block_release(mt_entry); + rtems_disk_release(vol->dd); + return -1; + } + + vol->free_cls = FAT_GET_FSINFO_FREE_CLUSTER_COUNT(fs_info_sector); + vol->next_cl = FAT_GET_FSINFO_NEXT_FREE_CLUSTER(fs_info_sector); + rc = fat_fat32_update_fsinfo_sector(mt_entry, 0xFFFFFFFF, + 0xFFFFFFFF); + if ( rc != RC_OK ) + { + _fat_block_release(mt_entry); + 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; + } + + _fat_block_release(mt_entry); + + 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(rtems_chain_control)); + if ( fs_info->vhash == NULL ) + { + rtems_disk_release(vol->dd); + rtems_set_errno_and_return_minus_one( ENOMEM ); + } + + for (i = 0; i < FAT_HASH_SIZE; i++) + rtems_chain_initialize_empty(fs_info->vhash + i); + + fs_info->rhash = calloc(FAT_HASH_SIZE, sizeof(rtems_chain_control)); + if ( fs_info->rhash == NULL ) + { + rtems_disk_release(vol->dd); + free(fs_info->vhash); + rtems_set_errno_and_return_minus_one( ENOMEM ); + } + for (i = 0; i < FAT_HASH_SIZE; i++) + rtems_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); + rtems_set_errno_and_return_minus_one( ENOMEM ); + } + fs_info->sec_buf = (uint8_t *)calloc(vol->bps, sizeof(uint8_t)); + if (fs_info->sec_buf == NULL) + { + rtems_disk_release(vol->dd); + free(fs_info->vhash); + free(fs_info->rhash); + free(fs_info->uino); + rtems_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++) + { + rtems_chain_node *node = NULL; + rtems_chain_control *the_chain = fs_info->vhash + i; + + while ( (node = rtems_chain_get(the_chain)) != NULL ) + free(node); + } + + for (i = 0; i < FAT_HASH_SIZE; i++) + { + rtems_chain_node *node = NULL; + rtems_chain_control *the_chain = fs_info->rhash + i; + + while ( (node = rtems_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, + uint32_t start_cln + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t cur_cln = start_cln; + char *buf; + + buf = calloc(fs_info->vol.bpc, sizeof(char)); + if ( buf == NULL ) + rtems_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 !!! + * + */ +uint32_t +fat_get_unique_ino(rtems_filesystem_mount_table_entry_t *mt_entry) +{ + register fat_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t j = 0; + bool 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, + uint32_t 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 bool +fat_ino_is_unique( + rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t 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, + uint32_t free_count, + uint32_t next_free + ) +{ + ssize_t ret1 = 0, ret2 = 0; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t le_free_count = 0; + uint32_t 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; +} diff --git a/cpukit/libfs/src/dosfs/fat.h b/cpukit/libfs/src/dosfs/fat.h new file mode 100644 index 0000000000..8d62662c10 --- /dev/null +++ b/cpukit/libfs/src/dosfs/fat.h @@ -0,0 +1,517 @@ +/* + * 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 <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ + +#ifndef __DOSFS_FAT_H__ +#define __DOSFS_FAT_H__ + +#include <string.h> + +#include <rtems/seterr.h> + +#include <rtems/score/cpu.h> +#include <errno.h> +#include <rtems/bdbuf.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef RC_OK +#define RC_OK 0 +#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((uint16_t)(v)) +# define CF_LE_L(v) CPU_swap_u32((uint32_t)(v)) +# define CT_LE_W(v) CPU_swap_u16((uint16_t)(v)) +# define CT_LE_L(v) CPU_swap_u32((uint32_t)(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 (uint32_t)0xFFFFFFFF + +#define FAT_FAT12_EOC 0x0FF8 +#define FAT_FAT16_EOC 0xFFF8 +#define FAT_FAT32_EOC (uint32_t)0x0FFFFFF8 + +#define FAT_FAT12_FREE 0x0000 +#define FAT_FAT16_FREE 0x0000 +#define FAT_FAT32_FREE 0x00000000 + +#define FAT_GENFAT_EOC (uint32_t)0xFFFFFFFF +#define FAT_GENFAT_FREE (uint32_t)0x00000000 + +#define FAT_FAT12_SHIFT 0x04 + +#define FAT_FAT12_MASK 0x00000FFF +#define FAT_FAT16_MASK 0x0000FFFF +#define FAT_FAT32_MASK (uint32_t)0x0FFFFFFF + +#define FAT_MAX_BPB_SIZE 90 +#define FAT_TOTAL_MBR_SIZE 512 + +/* size of useful information in FSInfo sector */ +#define FAT_USEFUL_INFO_SIZE 12 + +#define FAT_GET_ADDR(x, ofs) ((uint8_t *)(x) + (ofs)) + +#define FAT_GET_VAL8(x, ofs) (uint8_t)(*((uint8_t *)(x) + (ofs))) + +#define FAT_GET_VAL16(x, ofs) \ + (uint16_t)( (*((uint8_t *)(x) + (ofs))) | \ + ((*((uint8_t *)(x) + (ofs) + 1)) << 8) ) + +#define FAT_GET_VAL32(x, ofs) \ + (uint32_t)( (uint32_t)(*((uint8_t *)(x) + (ofs))) | \ + ((uint32_t)(*((uint8_t *)(x) + (ofs) + 1)) << 8) | \ + ((uint32_t)(*((uint8_t *)(x) + (ofs) + 2)) << 16) | \ + ((uint32_t)(*((uint8_t *)(x) + (ofs) + 3)) << 24) ) + +#define FAT_SET_VAL8(x, ofs,val) \ + (*((uint8_t *)(x)+(ofs))=(uint8_t)(val)) + +#define FAT_SET_VAL16(x, ofs,val) do { \ + FAT_SET_VAL8((x),(ofs),(val)); \ + FAT_SET_VAL8((x),(ofs)+1,(val)>>8);\ + } while (0) + +#define FAT_SET_VAL32(x, ofs,val) do { \ + uint32_t val1 = val; \ + FAT_SET_VAL16((x),(ofs),(val1)&0xffff);\ + FAT_SET_VAL16((x),(ofs)+2,(val1)>>16);\ + } while (0) + +/* macros to access boot sector fields */ +#define FAT_GET_BR_JMPBOOT(x) FAT_GET_VAL8( x, 0) +#define FAT_SET_BR_JMPBOOT(x,val) FAT_SET_VAL8( x, 0,val) + +#define FAT_GET_ADDR_BR_OEMNAME(x) FAT_GET_ADDR( x, 3) +#define FAT_BR_OEMNAME_SIZE (8) + +#define FAT_GET_BR_BYTES_PER_SECTOR(x) FAT_GET_VAL16(x, 11) +#define FAT_SET_BR_BYTES_PER_SECTOR(x,val) FAT_SET_VAL16(x, 11,val) + +#define FAT_GET_BR_SECTORS_PER_CLUSTER(x) FAT_GET_VAL8( x, 13) +#define FAT_SET_BR_SECTORS_PER_CLUSTER(x,val)FAT_SET_VAL8( x, 13,val) + +#define FAT_GET_BR_RESERVED_SECTORS_NUM(x) FAT_GET_VAL16(x, 14) +#define FAT_SET_BR_RESERVED_SECTORS_NUM(x,val) FAT_SET_VAL16(x, 14,val) + +#define FAT_GET_BR_FAT_NUM(x) FAT_GET_VAL8( x, 16) +#define FAT_SET_BR_FAT_NUM(x,val) FAT_SET_VAL8( x, 16,val) + +#define FAT_GET_BR_FILES_PER_ROOT_DIR(x) FAT_GET_VAL16(x, 17) +#define FAT_SET_BR_FILES_PER_ROOT_DIR(x,val) FAT_SET_VAL16(x, 17,val) + +#define FAT_GET_BR_TOTAL_SECTORS_NUM16(x) FAT_GET_VAL16(x, 19) +#define FAT_SET_BR_TOTAL_SECTORS_NUM16(x,val)FAT_SET_VAL16(x, 19,val) + +#define FAT_GET_BR_MEDIA(x) FAT_GET_VAL8( x, 21) +#define FAT_SET_BR_MEDIA(x,val) FAT_SET_VAL8( x, 21,val) + +#define FAT_GET_BR_SECTORS_PER_FAT(x) FAT_GET_VAL16(x, 22) +#define FAT_SET_BR_SECTORS_PER_FAT(x,val) FAT_SET_VAL16(x, 22,val) + +#define FAT_GET_BR_SECTORS_PER_TRACK(x) FAT_GET_VAL16(x, 24) +#define FAT_SET_BR_SECTORS_PER_TRACK(x,val) FAT_SET_VAL16(x, 24,val) + +#define FAT_GET_BR_NUMBER_OF_HEADS(x) FAT_GET_VAL16(x, 26) +#define FAT_SET_BR_NUMBER_OF_HEADS(x,val) FAT_SET_VAL16(x, 26,val) + +#define FAT_GET_BR_HIDDEN_SECTORS(x) FAT_GET_VAL32(x, 28) +#define FAT_SET_BR_HIDDEN_SECTORS(x,val) FAT_SET_VAL32(x, 28,val) + +#define FAT_GET_BR_TOTAL_SECTORS_NUM32(x) FAT_GET_VAL32(x, 32) +#define FAT_SET_BR_TOTAL_SECTORS_NUM32(x,val) FAT_SET_VAL32(x, 32,val) + /* --- start of FAT12/16 specific fields */ +#define FAT_GET_BR_DRVNUM(x) FAT_GET_VAL8( x, 36) +#define FAT_SET_BR_DRVNUM(x,val) FAT_SET_VAL8( x, 36,val) + +#define FAT_GET_BR_RSVD1(x) FAT_GET_VAL8( x, 37) +#define FAT_SET_BR_RSVD1(x,val) FAT_SET_VAL8( x, 37,val) + +#define FAT_GET_BR_BOOTSIG(x) FAT_GET_VAL8( x, 38) +#define FAT_SET_BR_BOOTSIG(x,val) FAT_SET_VAL8( x, 38,val) +#define FAT_BR_BOOTSIG_VAL (0x29) + +#define FAT_GET_BR_VOLID(x) FAT_GET_VAL32(x, 39) +#define FAT_SET_BR_VOLID(x,val) FAT_SET_VAL32(x, 39,val) + +#define FAT_GET_ADDR_BR_VOLLAB(x) FAT_GET_ADDR (x, 43) +#define FAT_BR_VOLLAB_SIZE (11) + +#define FAT_GET_ADDR_BR_FILSYSTYPE(x) FAT_GET_ADDR (x, 54) +#define FAT_BR_FILSYSTYPE_SIZE (8) + /* --- end of FAT12/16 specific fields */ + /* --- start of FAT32 specific fields */ +#define FAT_GET_BR_SECTORS_PER_FAT32(x) FAT_GET_VAL32(x, 36) +#define FAT_SET_BR_SECTORS_PER_FAT32(x,val) FAT_SET_VAL32(x, 36,val) + +#define FAT_GET_BR_EXT_FLAGS(x) FAT_GET_VAL16(x, 40) +#define FAT_SET_BR_EXT_FLAGS(x,val) FAT_SET_VAL16(x, 40,val) + +#define FAT_GET_BR_FSVER(x) FAT_GET_VAL16(x, 42) +#define FAT_SET_BR_FSVER(x,val) FAT_SET_VAL16(x, 42,val) + +#define FAT_GET_BR_FAT32_ROOT_CLUSTER(x) FAT_GET_VAL32(x, 44) +#define FAT_SET_BR_FAT32_ROOT_CLUSTER(x,val) FAT_SET_VAL32(x, 44,val) + +#define FAT_GET_BR_FAT32_FS_INFO_SECTOR(x) FAT_GET_VAL16(x, 48) +#define FAT_SET_BR_FAT32_FS_INFO_SECTOR(x,val) FAT_SET_VAL16(x, 48,val) + +#define FAT_GET_BR_FAT32_BK_BOOT_SECTOR(x) FAT_GET_VAL16(x, 50) +#define FAT_SET_BR_FAT32_BK_BOOT_SECTOR(x,val) FAT_SET_VAL16(x, 50,val) + +#define FAT_GET_ADDR_BR_FAT32_RESERVED(x) FAT_GET_ADDR (x, 52) +#define FAT_BR_FAT32_RESERVED_SIZE (12) + +#define FAT_GET_BR_FAT32_DRVNUM(x) FAT_GET_VAL8( x, 64) +#define FAT_SET_BR_FAT32_DRVNUM(x,val) FAT_SET_VAL8( x, 64,val) + +#define FAT_GET_BR_FAT32_RSVD1(x) FAT_GET_VAL8( x, 65) +#define FAT_SET_BR_FAT32_RSVD1(x,val) FAT_SET_VAL8( x, 65,val) + +#define FAT_GET_BR_FAT32_BOOTSIG(x) FAT_GET_VAL8( x, 66) +#define FAT_SET_BR_FAT32_BOOTSIG(x,val) FAT_SET_VAL8( x, 66,val) +#define FAT_BR_FAT32_BOOTSIG_VAL (0x29) + +#define FAT_GET_BR_FAT32_VOLID(x) FAT_GET_VAL32(x, 67) +#define FAT_SET_BR_FAT32_VOLID(x,val) FAT_SET_VAL32(x, 67,val) + +#define FAT_GET_ADDR_BR_FAT32_VOLLAB(x) FAT_GET_ADDR (x, 71) +#define FAT_BR_FAT32_VOLLAB_SIZE (11) + +#define FAT_GET_ADDR_BR_FAT32_FILSYSTYPE(x) FAT_GET_ADDR (x, 82) +#define FAT_BR_FAT32_FILSYSTYPE_SIZE (8) + /* --- end of FAT32 specific fields */ + +#define FAT_GET_BR_SIGNATURE(x) FAT_GET_VAL16(x,510) +#define FAT_SET_BR_SIGNATURE(x,val) FAT_SET_VAL16(x,510,val) +#define FAT_BR_SIGNATURE_VAL (0xAA55) + + /* + * FAT32 FSINFO description + */ +#define FAT_GET_FSINFO_LEAD_SIGNATURE(x) FAT_GET_VAL32(x, 0) +#define FAT_SET_FSINFO_LEAD_SIGNATURE(x,val) FAT_SET_VAL32(x, 0,val) +#define FAT_FSINFO_LEAD_SIGNATURE_VALUE (0x41615252) + +#define FAT_GET_FSINFO_STRUC_SIGNATURE(x) FAT_GET_VAL32(x,484) +#define FAT_SET_FSINFO_STRUC_SIGNATURE(x,val) FAT_SET_VAL32(x,484,val) +#define FAT_FSINFO_STRUC_SIGNATURE_VALUE (0x61417272) + +#define FAT_GET_FSINFO_TRAIL_SIGNATURE(x) FAT_GET_VAL32(x,508) +#define FAT_SET_FSINFO_TRAIL_SIGNATURE(x,val) FAT_SET_VAL32(x,508,val) +#define FAT_FSINFO_TRAIL_SIGNATURE_VALUE (0xAA550000) +/* + * I read FSInfo sector from offset 484 to access the information, so offsets + * of these fields a relative + */ +#define FAT_GET_FSINFO_FREE_CLUSTER_COUNT(x) FAT_GET_VAL32(x, 4) +#define FAT_SET_FSINFO_FREE_CLUSTER_COUNT(x,val) FAT_SET_VAL32(x, 4,val) +#define FAT_GET_FSINFO_NEXT_FREE_CLUSTER(x) FAT_GET_VAL32(x, 8) +#define FAT_SET_FSINFO_NEXT_FREE_CLUSTER(x,val) FAT_SET_VAL32(x, 8,val) + +#define FAT_FSI_INFO 484 +#define FAT_FSINFO_STRUCT_OFFSET 488 +#define FAT_FSINFO_FREE_CLUSTER_COUNT_OFFSET (FAT_FSINFO_STRUCT_OFFSET+0) + +#define FAT_FSINFO_NEXT_FREE_CLUSTER_OFFSET (FAT_FSINFO_STRUCT_OFFSET+4) + +#define FAT_RSRVD_CLN 0x02 + +#define FAT_FSI_LEADSIG_SIZE 0x04 + +#define FAT_TOTAL_FSINFO_SIZE 512 + +#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_BR_MEDIA_FIXED 0xf8 + +#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 correspond to Boot Sector and BPB Structure + * fields + */ +typedef struct fat_vol_s +{ + uint16_t bps; /* bytes per sector */ + uint8_t sec_log2; /* log2 of bps */ + uint8_t sec_mul; /* log2 of 512bts sectors number per sector */ + uint8_t spc; /* sectors per cluster */ + uint8_t spc_log2; /* log2 of spc */ + uint16_t bpc; /* bytes per cluster */ + uint8_t bpc_log2; /* log2 of bytes per cluster */ + uint8_t fats; /* number of FATs */ + uint8_t type; /* FAT type */ + uint32_t mask; + uint32_t eoc_val; + uint16_t fat_loc; /* FAT start */ + uint32_t fat_length; /* sectors per FAT */ + uint32_t rdir_loc; /* root directory start */ + uint16_t rdir_entrs; /* files per root directory */ + uint32_t rdir_secs; /* sectors per root directory */ + uint32_t rdir_size; /* root directory size in bytes */ + uint32_t tot_secs; /* total count of sectors */ + uint32_t data_fsec; /* first data sector */ + uint32_t data_cls; /* count of data clusters */ + uint32_t rdir_cl; /* first cluster of the root directory */ + uint16_t info_sec; /* FSInfo Sector Structure location */ + uint32_t free_cls; /* last known free clusters count */ + uint32_t next_cl; /* next free cluster number */ + uint8_t mirror; /* mirroring enabla/disable */ + uint32_t afat_loc; /* active FAT location */ + uint8_t afat; /* the number of active FAT */ + dev_t dev; /* device ID */ + rtems_disk_device *dd; /* disk device (see libblock) */ + void *private_data; /* reserved */ +} fat_vol_t; + + +typedef struct fat_cache_s +{ + uint32_t blk_num; + bool modified; + uint8_t state; + rtems_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 */ + rtems_chain_control *vhash; /* "vhash" of fat-file descriptors */ + rtems_chain_control *rhash; /* "rhash" of fat-file descriptors */ + char *uino; /* array of unique ino numbers */ + uint32_t index; + uint32_t uino_pool_size; /* size */ + uint32_t uino_base; + fat_cache_t c; /* cache */ + uint8_t *sec_buf; /* just placeholder for anything */ +} fat_fs_info_t; + +/* + * FAT position is a the cluster and the offset into the + * cluster. + */ +typedef struct fat_pos_s +{ + uint32_t cln; + uint32_t ofs; +} fat_pos_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. We also add the LFN start offset so we can delete it the whole + * file name. We can then use this to delete the file. + */ +typedef struct fat_dir_pos_s +{ + fat_pos_t sname; + fat_pos_t lname; +} fat_dir_pos_t; + +/* + * Set the long name entries to this value for a short file name. + */ +#define FAT_FILE_SHORT_NAME (0xffffffff) + +#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 void +fat_dir_pos_init( + fat_dir_pos_t *dir_pos + ) +{ + dir_pos->sname.cln = 0; + dir_pos->sname.ofs = 0; + dir_pos->lname.cln = FAT_FILE_SHORT_NAME; + dir_pos->lname.ofs = FAT_FILE_SHORT_NAME; +} + +static inline uint32_t +fat_cluster_num_to_sector_num( + rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t 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 uint32_t +fat_cluster_num_to_sector512_num( + rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t 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 void +fat_buf_mark_modified(fat_fs_info_t *fs_info) +{ + fs_info->c.modified = true; +} + +int +fat_buf_access(fat_fs_info_t *fs_info, uint32_t blk, int op_type, + rtems_bdbuf_buffer **buf); + +int +fat_buf_release(fat_fs_info_t *fs_info); + +ssize_t +_fat_block_read(rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t start, + uint32_t offset, + uint32_t count, + void *buff); + +ssize_t +_fat_block_write(rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t start, + uint32_t offset, + uint32_t count, + const void *buff); + +int +_fat_block_release(rtems_filesystem_mount_table_entry_t *mt_entry); + +ssize_t +fat_cluster_read(rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t cln, + void *buff); + +ssize_t +fat_cluster_write(rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t 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, + uint32_t start_cln); + +uint32_t +fat_cluster_num_to_sector_num(rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t cln); + +int +fat_shutdown_drive(rtems_filesystem_mount_table_entry_t *mt_entry); + + +uint32_t +fat_get_unique_ino(rtems_filesystem_mount_table_entry_t *mt_entry); + +bool +fat_ino_is_unique(rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t ino); + +void +fat_free_unique_ino(rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t ino); + +int +fat_fat32_update_fsinfo_sector( + rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t free_count, + uint32_t 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..c4f82805e8 --- /dev/null +++ b/cpukit/libfs/src/dosfs/fat_fat_operations.c @@ -0,0 +1,436 @@ +/* + * fat_fat_operations.c + * + * General operations on File Allocation Table + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> + +#include <rtems/libio_.h> + +#include "fat.h" +#include "fat_fat_operations.h" + +/* fat_scan_fat_for_free_clusters -- + * Allocate chain of free clusters from Files Allocation Table + * + * PARAMETERS: + * mt_entry - mount table entry + * chain - the number of the first allocated cluster (first cluster + * in the chain) + * count - count of clusters to allocate (chain length) + * + * RETURNS: + * RC_OK on success, or error code if error occured (errno set + * appropriately) + * + * + */ +int +fat_scan_fat_for_free_clusters( + rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t *chain, + uint32_t count, + uint32_t *cls_added, + uint32_t *last_cl + ) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t cl4find = 2; + uint32_t next_cln = 0; + uint32_t save_cln = 0; + uint32_t data_cls_val = fs_info->vol.data_cls + 2; + uint32_t i = 2; + + *cls_added = 0; + + if (count == 0) + return rc; + + if (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 == 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) + { + 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; + } + + 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, + uint32_t chain + ) +{ + int rc = RC_OK, rc1 = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t cur_cln = chain; + uint32_t next_cln = 0; + uint32_t 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.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; + } + + 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, + uint32_t cln, + uint32_t *ret_val + ) +{ + int rc = RC_OK; + register fat_fs_info_t *fs_info = mt_entry->fs_info; + rtems_bdbuf_buffer *block0 = NULL; + uint32_t sec = 0; + uint32_t ofs = 0; + + /* sanity check */ + if ( (cln < 2) || (cln > (fs_info->vol.data_cls + 1)) ) + rtems_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 = (*((uint8_t *)(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 |= (*((uint8_t *)(block0->buffer)))<<8; + } + else + { + *ret_val |= (*((uint8_t *)(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 = *((uint16_t *)(block0->buffer + ofs)); + *ret_val = CF_LE_W(*ret_val); + break; + + case FAT_FAT32: + *ret_val = *((uint32_t *)(block0->buffer + ofs)); + *ret_val = CF_LE_L(*ret_val); + break; + + default: + rtems_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, + uint32_t cln, + uint32_t in_val + ) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t sec = 0; + uint32_t ofs = 0; + uint16_t fat16_clv = 0; + uint32_t fat32_clv = 0; + rtems_bdbuf_buffer *block0 = NULL; + + /* sanity check */ + if ( (cln < 2) || (cln > (fs_info->vol.data_cls + 1)) ) + rtems_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 = ((uint16_t )in_val) << FAT_FAT12_SHIFT; + *((uint8_t *)(block0->buffer + ofs)) = + (*((uint8_t *)(block0->buffer + ofs))) & 0x0F; + + *((uint8_t *)(block0->buffer + ofs)) = + (*((uint8_t *)(block0->buffer + ofs))) | + (uint8_t )(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; + + *((uint8_t *)(block0->buffer)) &= 0x00; + + *((uint8_t *)(block0->buffer)) = + (*((uint8_t *)(block0->buffer))) | + (uint8_t )((fat16_clv & 0xFF00)>>8); + + fat_buf_mark_modified(fs_info); + } + else + { + *((uint8_t *)(block0->buffer + ofs + 1)) &= 0x00; + + *((uint8_t *)(block0->buffer + ofs + 1)) = + (*((uint8_t *)(block0->buffer + ofs + 1))) | + (uint8_t )((fat16_clv & 0xFF00)>>8); + } + } + else + { + fat16_clv = ((uint16_t )in_val) & FAT_FAT12_MASK; + *((uint8_t *)(block0->buffer + ofs)) &= 0x00; + + *((uint8_t *)(block0->buffer + ofs)) = + (*((uint8_t *)(block0->buffer + ofs))) | + (uint8_t )(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; + + *((uint8_t *)(block0->buffer)) = + (*((uint8_t *)(block0->buffer))) & 0xF0; + + *((uint8_t *)(block0->buffer)) = + (*((uint8_t *)(block0->buffer))) | + (uint8_t )((fat16_clv & 0xFF00)>>8); + + fat_buf_mark_modified(fs_info); + } + else + { + *((uint8_t *)(block0->buffer + ofs + 1)) = + (*((uint8_t *)(block0->buffer + ofs + 1))) & 0xF0; + + *((uint8_t *)(block0->buffer + ofs+1)) = + (*((uint8_t *)(block0->buffer + ofs+1))) | + (uint8_t )((fat16_clv & 0xFF00)>>8); + } + } + break; + + case FAT_FAT16: + *((uint16_t *)(block0->buffer + ofs)) = + (uint16_t )(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)); + + *((uint32_t *)(block0->buffer + ofs)) = + (*((uint32_t *)(block0->buffer + ofs))) & (CT_LE_L(0xF0000000)); + + *((uint32_t *)(block0->buffer + ofs)) = + fat32_clv | (*((uint32_t *)(block0->buffer + ofs))); + + fat_buf_mark_modified(fs_info); + break; + + default: + rtems_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..d516057ad0 --- /dev/null +++ b/cpukit/libfs/src/dosfs/fat_fat_operations.h @@ -0,0 +1,59 @@ +/* + * 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 <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_FAT_FAT_OPERATIONS_H__ +#define __DOSFS_FAT_FAT_OPERATIONS_H__ + +#include <rtems.h> +#include <rtems/libio_.h> + +#include <rtems/bdbuf.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include "fat.h" + +int +fat_get_fat_cluster(rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t cln, + uint32_t *ret_val); + +int +fat_set_fat_cluster(rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t cln, + uint32_t in_val); + +int +fat_scan_fat_for_free_clusters( + rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t *chain, + uint32_t count, + uint32_t *cls_added, + uint32_t *last_cl +); + +int +fat_free_fat_clusters_chain( + rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t 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..9a3ad6677e --- /dev/null +++ b/cpukit/libfs/src/dosfs/fat_file.c @@ -0,0 +1,993 @@ +/* + * fat_file.c + * + * General operations on "fat-file" + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * @(#) $Id$ + * + */ + +#define MSDOS_TRACE 1 + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdarg.h> +#include <errno.h> +#include <stdlib.h> +#include <assert.h> +#include <time.h> + +#include <rtems/libio_.h> + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +static inline void +_hash_insert(rtems_chain_control *hash, uint32_t key1, uint32_t key2, + fat_file_fd_t *el); + +static inline void +_hash_delete(rtems_chain_control *hash, uint32_t key1, uint32_t key2, + fat_file_fd_t *el); + +static inline int +_hash_search( + rtems_filesystem_mount_table_entry_t *mt_entry, + rtems_chain_control *hash, + uint32_t key1, + uint32_t key2, + fat_file_fd_t **ret +); + +static off_t +fat_file_lseek( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + uint32_t file_cln, + uint32_t *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 + * pos - cluster and 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, + fat_dir_pos_t *dir_pos, + 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; + uint32_t key = 0; + + /* construct key */ + key = fat_construct_key(mt_entry, &dir_pos->sname); + + /* access "valid" hash table */ + rc = _hash_search(mt_entry, fs_info->vhash, key, 0, &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, &lfat_fd); + + lfat_fd = (*fat_fd) = (fat_file_fd_t*)malloc(sizeof(fat_file_fd_t)); + if ( lfat_fd == NULL ) + rtems_set_errno_and_return_minus_one( ENOMEM ); + + memset(lfat_fd, 0, sizeof(fat_file_fd_t)); + + lfat_fd->links_num = 1; + lfat_fd->flags &= ~FAT_FILE_REMOVED; + lfat_fd->map.last_cln = FAT_UNDEFINED_VALUE; + + lfat_fd->dir_pos = *dir_pos; + + 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 :( + */ + rtems_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; + uint32_t 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->dir_pos.sname); + + 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); + } + } + /* + * flush any modified "cached" buffer back to disk + */ + rc = fat_buf_release(fs_info); + + 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, + uint32_t start, + uint32_t count, + uint8_t *buf +) +{ + int rc = RC_OK; + ssize_t ret = 0; + fat_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t cmpltd = 0; + uint32_t cur_cln = 0; + uint32_t cl_start = 0; + uint32_t save_cln = 0; + uint32_t ofs = 0; + uint32_t save_ofs; + uint32_t sec = 0; + uint32_t byte = 0; + uint32_t 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, + uint32_t start, + uint32_t count, + const uint8_t *buf + ) +{ + int rc = 0; + ssize_t ret = 0; + fat_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t cmpltd = 0; + uint32_t cur_cln = 0; + uint32_t save_cln = 0; /* FIXME: This might be incorrect, cf. below */ + uint32_t cl_start = 0; + uint32_t ofs = 0; + uint32_t save_ofs; + uint32_t sec = 0; + uint32_t byte = 0; + uint32_t c = 0; + + if ( count == 0 ) + return cmpltd; + + if ( start > fat_fd->fat_file_size ) + rtems_set_errno_and_return_minus_one( EIO ); + + if ((count > fat_fd->size_limit) || + (start > fat_fd->size_limit - count)) + rtems_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, + uint32_t new_length, + uint32_t *a_length + ) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t chain = 0; + uint32_t bytes2add = 0; + uint32_t cls2add = 0; + uint32_t old_last_cl; + uint32_t last_cl = 0; + uint32_t bytes_remain = 0; + uint32_t 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))) + rtems_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)) + rtems_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; + } + } + } + + fat_fd->fat_file_size = new_length; + + 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, + uint32_t new_length + ) +{ + int rc = RC_OK; + fat_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t cur_cln = 0; + uint32_t cl_start = 0; + uint32_t 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; + uint32_t cur_cln = 0; + uint32_t cl_start = 0; + uint32_t pos = 0; + uint32_t *ret; + va_list ap; + + va_start(ap, cmd); + + switch (cmd) + { + case F_CLU_NUM: + pos = va_arg(ap, uint32_t ); + ret = va_arg(ap, uint32_t *); + + /* sanity check */ + if ( pos >= fat_fd->fat_file_size ) { + va_end(ap); + rtems_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; + rc = RC_OK; + break; + } + + cl_start = pos >> fs_info->vol.bpc_log2; + + rc = fat_file_lseek(mt_entry, fat_fd, cl_start, &cur_cln); + if ( rc != RC_OK ) + break; + + *ret = cur_cln; + break; + + default: + errno = EINVAL; + rc = -1; + break; + } + va_end(ap); + 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; + uint32_t key = 0; + + key = fat_construct_key(mt_entry, &fat_fd->dir_pos.sname); + + _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; + uint32_t cur_cln = fat_fd->cln; + rtems_bdbuf_buffer *block = NULL; + uint32_t sec = 0; + uint32_t 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) + rtems_set_errno_and_return_minus_one( EIO ); + + sc = rtems_bdbuf_sync(block); + if ( sc != RTEMS_SUCCESSFUL ) + rtems_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; + uint32_t cur_cln = fat_fd->cln; + uint32_t 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(rtems_chain_control *hash, uint32_t key1, uint32_t key2, + fat_file_fd_t *el) +{ + rtems_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(rtems_chain_control *hash, uint32_t key1, uint32_t key2, + fat_file_fd_t *el) +{ + rtems_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, + rtems_chain_control *hash, + uint32_t key1, + uint32_t key2, + fat_file_fd_t **ret + ) +{ + uint32_t mod = (key1) % FAT_HASH_MODULE; + rtems_chain_node *the_node = rtems_chain_first(hash + mod); + + for ( ; !rtems_chain_is_tail((hash) + mod, the_node) ; ) + { + fat_file_fd_t *ffd = (fat_file_fd_t *)the_node; + uint32_t ck = fat_construct_key(mt_entry, &ffd->dir_pos.sname); + + if ( (key1) == ck) + { + if ( ((key2) == 0) || ((key2) == ffd->ino) ) + { + *ret = (void *)the_node; + return 0; + } + } + the_node = the_node->next; + } + return -1; +} + +static off_t +fat_file_lseek( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + uint32_t file_cln, + uint32_t *disk_cln + ) +{ + int rc = RC_OK; + + if (file_cln == fat_fd->map.file_cln) + *disk_cln = fat_fd->map.disk_cln; + else + { + uint32_t cur_cln; + uint32_t count; + uint32_t 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..01ab73ef98 --- /dev/null +++ b/cpukit/libfs/src/dosfs/fat_file.h @@ -0,0 +1,192 @@ +/* + * fat_file.h + * + * Constants/data structures/prototypes for operations on "fat-file" + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_FAT_FILE_H__ +#define __DOSFS_FAT_FILE_H__ + +#include <rtems.h> +#include <rtems/libio_.h> + +#include <time.h> + +#include "fat.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* "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 +{ + uint32_t file_cln; + uint32_t disk_cln; + uint32_t last_cln; +} fat_file_map_t; +/* + * descriptor of a fat-file + * + * To each particular clusters chain + */ +typedef struct fat_file_fd_s +{ + rtems_chain_node link; /* + * fat-file descriptors organized into hash; + * collision lists are handled via link + * field + */ + uint32_t links_num; /* + * the number of fat_file_open call on + * this fat-file + */ + uint32_t ino; /* inode, file serial number :)))) */ + fat_file_type_t fat_file_type; + uint32_t size_limit; + uint32_t fat_file_size; /* length */ + uint32_t cln; + fat_dir_pos_t dir_pos; + uint8_t 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->dir_pos.sname.cln == FAT_ROOTDIR_CLUSTER_NUM) && \ + (fat_fd->dir_pos.sname.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 uint32_t +fat_construct_key( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_pos_t *pos) +{ + return ( ((fat_cluster_num_to_sector512_num(mt_entry, pos->cln) + + (pos->ofs >> FAT_SECTOR512_BITS)) << 4) + + ((pos->ofs >> 5) & (FAT_DIRENTRIES_PER_SEC512 - 1)) ); +} + +/* Prototypes for "fat-file" operations */ +int +fat_file_open(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_dir_pos_t *dir_pos, + 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, + uint32_t start, + uint32_t count, + uint8_t *buf); + +ssize_t +fat_file_write(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + uint32_t start, + uint32_t count, + const uint8_t *buf); + +int +fat_file_extend(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + uint32_t new_length, + uint32_t *a_length); + +int +fat_file_truncate(rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + uint32_t 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..696dac37cf --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos.h @@ -0,0 +1,467 @@ +/* + * msdos.h + * + * The MSDOS filesystem constants/data structures/prototypes + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ +#ifndef __DOSFS_MSDOS_H__ +#define __DOSFS_MSDOS_H__ + +#include <rtems.h> +#include <rtems/libio_.h> + +#include "fat.h" +#include "fat_file.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MSDOS_NAME_NOT_FOUND_ERR 0x7D01 + +/* + * 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 + */ + const rtems_filesystem_file_handlers_r *directory_handlers; /* + * a set of routines + * that handles the + * nodes of directory + * type + */ + const 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 + */ + uint8_t *cl_buf; /* + * just placeholder + * for anything + */ +} msdos_fs_info_t; + +/* a set of routines that handle the nodes which are directories */ +extern const rtems_filesystem_file_handlers_r msdos_dir_handlers; + +/* a set of routines that handle the nodes which are files */ +extern const rtems_filesystem_file_handlers_r msdos_file_handlers; + +/* Volume semaphore timeout value. This value can be changed to a number + * of ticks to help debugging or if you need such a */ +#define MSDOS_VOLUME_SEMAPHORE_TIMEOUT RTEMS_NO_TIMEOUT + +/* Node types */ +#define MSDOS_DIRECTORY RTEMS_FILESYSTEM_DIRECTORY +#define MSDOS_REGULAR_FILE RTEMS_FILESYSTEM_MEMORY_FILE +#define MSDOS_HARD_LINK RTEMS_FILESYSTEM_HARD_LINK /* pseudo type */ + +typedef rtems_filesystem_node_types_t msdos_node_type_t; + +/* + * Macros for fetching fields from 32 bytes long FAT Directory Entry + * Structure + */ +#define MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE 32 /* 32 bytes */ + +#define MSDOS_DIR_NAME(x) (char *)((x) + 0) +#define MSDOS_DIR_ENTRY_TYPE(x) (uint8_t *)((x) + 0) +#define MSDOS_DIR_ATTR(x) (uint8_t *)((x) + 11) +#define MSDOS_DIR_NT_RES(x) (uint8_t *)((x) + 12) +#define MSDOS_DIR_LFN_CHECKSUM(x) (uint8_t *)((x) + 13) +#define MSDOS_DIR_CRT_TIME_TENTH(x) (uint8_t *)((x) + 13) +#define MSDOS_DIR_CRT_TIME(x) (uint16_t *)((x) + 14) +#define MSDOS_DIR_CRT_DATE(x) (uint16_t *)((x) + 16) +#define MSDOS_DIR_LAST_ACCESS_DATE(x) (uint16_t *)((x) + 18) +#define MSDOS_DIR_FIRST_CLUSTER_HI(x) (uint16_t *)((x) + 20) +#define MSDOS_DIR_WRITE_TIME(x) (uint16_t *)((x) + 22) +#define MSDOS_DIR_WRITE_DATE(x) (uint16_t *)((x) + 24) +#define MSDOS_DIR_FIRST_CLUSTER_LOW(x) (uint16_t *)((x) + 26) +#define MSDOS_DIR_FILE_SIZE(x) (uint32_t *)((x) + 28) + +#define MSDOS_EXTRACT_CLUSTER_NUM(p) \ + (uint32_t)( (CF_LE_W(*MSDOS_DIR_FIRST_CLUSTER_LOW(p))) | \ + ((uint32_t)(CF_LE_W((*MSDOS_DIR_FIRST_CLUSTER_HI(p))))<<16) ) + +/* + * Fields offset in 32 bytes long FAT Directory Entry + * Structure + */ +#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 +#define MSDOS_FILE_ADATE_OFFSET 18 + +/* + * Possible values of DIR_Attr field of 32 bytes long FAT Directory Entry + * Structure + */ +#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 +#define MSDOS_ATTR_LFN (MSDOS_ATTR_READ_ONLY | \ + MSDOS_ATTR_HIDDEN | \ + MSDOS_ATTR_SYSTEM | \ + MSDOS_ATTR_VOLUME_ID) +#define MSDOS_ATTR_LFN_MASK (MSDOS_ATTR_READ_ONLY | \ + MSDOS_ATTR_HIDDEN | \ + MSDOS_ATTR_SYSTEM | \ + MSDOS_ATTR_VOLUME_ID | \ + MSDOS_ATTR_DIRECTORY | \ + MSDOS_ATTR_ARCHIVE) + +#define MSDOS_LAST_LONG_ENTRY 0x40 +#define MSDOS_LAST_LONG_ENTRY_MASK 0x3F + +#define MSDOS_DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ +#define MSDOS_DT_2SECONDS_SHIFT 0 +#define MSDOS_DT_MINUTES_MASK 0x7E0 /* minutes */ +#define MSDOS_DT_MINUTES_SHIFT 5 +#define MSDOS_DT_HOURS_MASK 0xF800 /* hours */ +#define MSDOS_DT_HOURS_SHIFT 11 + +#define MSDOS_DD_DAY_MASK 0x1F /* day of month */ +#define MSDOS_DD_DAY_SHIFT 0 +#define MSDOS_DD_MONTH_MASK 0x1E0 /* month */ +#define MSDOS_DD_MONTH_SHIFT 5 +#define MSDOS_DD_YEAR_MASK 0xFE00 /* year - 1980 */ +#define MSDOS_DD_YEAR_SHIFT 9 + + +/* + * Possible values of DIR_Name[0] field of 32 bytes long FAT Directory Entry + * Structure + */ +#define MSDOS_THIS_DIR_ENTRY_EMPTY 0xE5 +#define MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY 0x00 + +/* + * Number of characters per directory entry for a long filename. + */ +#define MSDOS_LFN_LEN_PER_ENTRY (13) + +/* + * Macros for names parsing and formatting + */ +#define msdos_is_separator(_ch) rtems_filesystem_is_separator(_ch) + +#define MSDOS_SHORT_BASE_LEN 8 /* 8 characters */ +#define MSDOS_SHORT_EXT_LEN 3 /* 3 characters */ +#define MSDOS_SHORT_NAME_LEN (MSDOS_SHORT_BASE_LEN+\ + MSDOS_SHORT_EXT_LEN) /* 11 chars */ +#define MSDOS_NAME_MAX_LNF_LEN (255) +#define MSDOS_NAME_MAX MSDOS_SHORT_NAME_LEN +#define MSDOS_NAME_MAX_WITH_DOT (MSDOS_NAME_MAX + 1) +#define MSDOS_NAME_MAX_LFN_WITH_DOT (260) + + +extern const char *const MSDOS_DOT_NAME; /* ".", padded to MSDOS_NAME chars */ +extern const char *const MSDOS_DOTDOT_NAME; /* ".", padded to MSDOS_NAME chars */ + +typedef enum msdos_name_types_e +{ + MSDOS_NAME_INVALID = 0, /* Unknown name type. Has invalid characters. */ + MSDOS_NAME_SHORT, /* Name can be short. */ + MSDOS_NAME_LONG /* Name is long; cannot be short. */ +} msdos_name_type_t; + +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 */ +#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_shut_down(rtems_filesystem_mount_table_entry_t *temp_mt_entry); + +int msdos_eval_path( + const char *pathname, /* IN */ + size_t pathnamelen, /* 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_rename(rtems_filesystem_location_info_t *old_parent_loc, + rtems_filesystem_location_info_t *old_loc, + rtems_filesystem_location_info_t *new_parent_loc, + const char *new_name); + +int msdos_initialize_support( + rtems_filesystem_mount_table_entry_t *temp_mt_entry, + const rtems_filesystem_operations_table *op_table, + const rtems_filesystem_file_handlers_r *file_handlers, + const rtems_filesystem_file_handlers_r *directory_handlers +); + +int msdos_file_open( + rtems_libio_t *iop, /* IN */ + const char *pathname, /* IN */ + uint32_t flag, /* IN */ + uint32_t mode /* IN */ +); + +int msdos_file_close(rtems_libio_t *iop /* IN */); + +ssize_t msdos_file_read( + rtems_libio_t *iop, /* IN */ + void *buffer, /* IN */ + size_t count /* IN */ +); + +ssize_t msdos_file_write( + rtems_libio_t *iop, /* IN */ + const void *buffer, /* IN */ + size_t count /* IN */ +); + +rtems_off64_t msdos_file_lseek( + rtems_libio_t *iop, /* IN */ + rtems_off64_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 */ + rtems_off64_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 */ + uint32_t command, /* IN */ + void *buffer /* IN */ +); + +int +msdos_dir_chmod( + rtems_filesystem_location_info_t *pathloc, /* IN */ + mode_t mode /* IN */ +); + +int msdos_file_rmnod(rtems_filesystem_location_info_t *parent_pathloc, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN */); + +int msdos_dir_open( + rtems_libio_t *iop, /* IN */ + const char *pathname, /* IN */ + uint32_t flag, /* IN */ + uint32_t mode /* IN */ +); + +int msdos_dir_close(rtems_libio_t *iop /* IN */); + +ssize_t msdos_dir_read( + rtems_libio_t *iop, /* IN */ + void *buffer, /* IN */ + size_t count /* IN */ +); + +rtems_off64_t msdos_dir_lseek( + rtems_libio_t *iop, /* IN */ + rtems_off64_t offset, /* IN */ + int whence /* IN */ +); + +int +msdos_file_chmod( + rtems_filesystem_location_info_t *pathloc, /* IN */ + mode_t mode /* IN */ +); + +int msdos_dir_rmnod(rtems_filesystem_location_info_t *parent_pathloc, /* IN */ + 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, + const char *name, + int name_len, + mode_t mode, + const fat_file_fd_t *link_fd); + +/* Misc prototypes */ +msdos_token_types_t msdos_get_token(const char *path, + int pathlen, + const char **token, + int *token_len); + +int msdos_find_name( + rtems_filesystem_location_info_t *parent_loc, + const char *name, + int name_len +); + +int msdos_get_name_node( + rtems_filesystem_location_info_t *parent_loc, + bool create_node, + const char *name, + int name_len, + msdos_name_type_t name_type, + fat_dir_pos_t *dir_pos, + char *name_dir_entry +); + +int msdos_dir_info_remove(rtems_filesystem_location_info_t *pathloc); + +msdos_name_type_t msdos_long_to_short(const char *lfn, int lfn_len, + char* sfn, int sfn_len); + +int msdos_filename_unix2dos(const char *un, int unlen, char *dn); + +void msdos_date_unix2dos( + unsigned int tsp, uint16_t *ddp, + uint16_t *dtp); + +unsigned int msdos_date_dos2unix(unsigned int dd, unsigned int dt); + +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, + fat_dir_pos_t *dir_pos, + 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, + bool *ret_val +); + +int msdos_find_name_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + bool create_node, + const char *name, + int name_len, + msdos_name_type_t name_type, + fat_dir_pos_t *dir_pos, + char *name_dir_entry +); + +int msdos_find_node_by_cluster_num_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + uint32_t cl4find, + fat_dir_pos_t *dir_pos, + char *dir_entry +); + +int msdos_get_dotdot_dir_info_cluster_num_and_offset( + rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t cln, + fat_dir_pos_t *dir_pos, + char *dir_entry +); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOSFS_MSDOS_H__ */ diff --git a/cpukit/libfs/src/dosfs/msdos_conv.c b/cpukit/libfs/src/dosfs/msdos_conv.c new file mode 100644 index 0000000000..95b25814db --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_conv.c @@ -0,0 +1,317 @@ +/* + * Adaptation of NetBSD code for RTEMS by Victor V. Vengerov <vvv@oktet.ru> + */ +/* $NetBSD: msdosfs_conv.c,v 1.10 1994/12/27 18:36:24 mycroft Exp $ */ +/* + * Written by Paul Popelka (paulp@uts.amdahl.com) + * + * You can do anything you want with this software, just don't say you wrote + * it, and don't remove this notice. + * + * This software is provided "as is". + * + * The author supplies this software to be publicly redistributed on the + * understanding that the author is not responsible for the correct + * functioning of this software in any circumstances and is not liable for + * any damages caused by this software. + * + * October 1992 + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include "msdos.h" + +/* #define SECONDSPERDAY (24 * 60 * 60) */ +#define SECONDSPERDAY ((uint32_t) 86400) + +/* + * Days in each month in a regular year. + */ +static uint16_t regyear[] = { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 +}; + +/* + * Days in each month in a leap year. + */ +static uint16_t leapyear[] = { + 31, 29, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 +}; + +/* + * Variables used to remember parts of the last time conversion. Maybe we + * can avoid a full conversion. + */ +static uint32_t lasttime; +static uint32_t lastday; +static uint16_t lastddate; +static uint16_t lastdtime; + +/* + * Convert the unix version of time to dos's idea of time to be used in + * file timestamps. The passed in unix time is assumed to be in GMT. + */ +void +msdos_date_unix2dos(unsigned int t, uint16_t *ddp, + uint16_t *dtp) +{ + uint32_t days; + uint32_t inc; + uint32_t year; + uint32_t month; + uint16_t *months; + + /* + * If the time from the last conversion is the same as now, then + * skip the computations and use the saved result. + */ + if (lasttime != t) { + lasttime = t; + lastdtime = (((t % 60) >> 1) << MSDOS_DT_2SECONDS_SHIFT) + + (((t / 60) % 60) << MSDOS_DT_MINUTES_SHIFT) + + (((t / 3600) % 24) << MSDOS_DT_HOURS_SHIFT); + + /* + * If the number of days since 1970 is the same as the last + * time we did the computation then skip all this leap year + * and month stuff. + */ + days = t / (SECONDSPERDAY); + if (days != lastday) { + lastday = days; + for (year = 1970;; year++) { + inc = year & 0x03 ? 365 : 366; + if (days < inc) + break; + days -= inc; + } + months = year & 0x03 ? regyear : leapyear; + for (month = 0; month < 12; month++) { + if (days < months[month]) + break; + days -= months[month]; + } + lastddate = ((days + 1) << MSDOS_DD_DAY_SHIFT) + + ((month + 1) << MSDOS_DD_MONTH_SHIFT); + /* + * Remember dos's idea of time is relative to 1980. + * unix's is relative to 1970. If somehow we get a + * time before 1980 then don't give totally crazy + * results. + */ + if (year > 1980) + lastddate += (year - 1980) << + MSDOS_DD_YEAR_SHIFT; + } + } + *dtp = lastdtime; + *ddp = lastddate; +} + +/* + * The number of days between Jan 1, 1970 and Jan 1, 1980. In that + * interval there were 8 regular years and 2 leap years. + */ +/* #define DAYSTO1980 ((8 * 365) + (2 * 366)) */ +#define DAYSTO1980 ((uint32_t) 3652) + +static uint16_t lastdosdate; +static uint32_t lastseconds; + +/* + * Convert from dos' idea of time to unix'. This will probably only be + * called from the stat(), and fstat() system calls and so probably need + * not be too efficient. + */ +unsigned int +msdos_date_dos2unix(unsigned int dd, unsigned int dt) +{ + uint32_t seconds; + uint32_t m, month; + uint32_t y, year; + uint32_t days; + uint16_t *months; + + seconds = 2 * ((dt & MSDOS_DT_2SECONDS_MASK) >> MSDOS_DT_2SECONDS_SHIFT) + + ((dt & MSDOS_DT_MINUTES_MASK) >> MSDOS_DT_MINUTES_SHIFT) * 60 + + ((dt & MSDOS_DT_HOURS_MASK) >> MSDOS_DT_HOURS_SHIFT) * 3600; + /* + * If the year, month, and day from the last conversion are the + * same then use the saved value. + */ + if (lastdosdate != dd) { + lastdosdate = dd; + days = 0; + year = (dd & MSDOS_DD_YEAR_MASK) >> MSDOS_DD_YEAR_SHIFT; + for (y = 0; y < year; y++) + days += y & 0x03 ? 365 : 366; + months = year & 0x03 ? regyear : leapyear; + /* + * Prevent going from 0 to 0xffffffff in the following + * loop. + */ + month = (dd & MSDOS_DD_MONTH_MASK) >> MSDOS_DD_MONTH_SHIFT; + if (month == 0) { + month = 1; + } + for (m = 0; m < month - 1; m++) + days += months[m]; + days += ((dd & MSDOS_DD_DAY_MASK) >> MSDOS_DD_DAY_SHIFT) - 1; + lastseconds = (days + DAYSTO1980) * SECONDSPERDAY; + } + return seconds + lastseconds; +} + +static const uint8_t msdos_map[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 00-07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 08-0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 10-17 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 18-1f */ + 0, '!', 0, '#', '$', '%', '&', '\'', /* 20-27 */ + '(', ')', 0, '+', 0, '-', 0, 0, /* 28-2f */ + '0', '1', '2', '3', '4', '5', '6', '7', /* 30-37 */ + '8', '9', 0, 0, 0, 0, 0, 0, /* 38-3f */ + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 40-47 */ + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 48-4f */ + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 50-57 */ + 'X', 'Y', 'Z', 0, 0, 0, '^', '_', /* 58-5f */ + '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 60-67 */ + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 68-6f */ + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 70-77 */ + 'X', 'Y', 'Z', '{', 0, '}', '~', 0, /* 78-7f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 80-87 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 88-8f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 90-97 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 98-9f */ + 0, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* a0-a7 */ + 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* a8-af */ + 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* b0-b7 */ + 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* b8-bf */ + 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* c0-c7 */ + 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* c8-cf */ + 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, /* d0-d7 */ + 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, /* d8-df */ + 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* e0-e7 */ + 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* e8-ef */ + 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0xf6, /* f0-f7 */ + 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0x98, /* f8-ff */ +#if OLD_TABLE +/* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 20 */ 0x00, 0x21, 0x00, 0x23, 0x24, 0x25, 0x26, 0x27, /* !"#$%&' */ +/* 28 */ 0x28, 0x29, 0x00, 0x00, 0x00, 0x2D, 0x2E, 0x00, /* ()*+,-./ */ +/* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 01234567 */ +/* 38 */ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 89:;<=>? */ +/* 40 */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* @ABCDEFG */ +/* 48 */ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* HIJKLMNO */ +/* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* PQRSTUVW */ +/* 58 */ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x00, 0x5E, 0x5F, /* XYZ[\]^_ */ +/* 60 */ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* `abcdefg */ +/* 68 */ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* hijklmno */ +/* 70 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* pqrstuvw */ +/* 78 */ 0x58, 0x59, 0x5A, 0x5B, 0x7C, 0x00, 0x7E, 0x00, /* xyz{|}~ */ +/* 80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* B0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* B8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* C0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* C8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* D0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* D8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* E0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* E8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* F0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* F8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +#endif +}; +/* + * Convert a unix filename to a DOS filename. Return -1 if wrong name is + * supplied. + */ +int +msdos_filename_unix2dos(const char *un, int unlen, char *dn) +{ + int i; + uint8_t c; + + /* + * Fill the dos filename string with blanks. These are DOS's pad + * characters. + */ + for (i = 0; i <= 10; i++) + dn[i] = ' '; + + /* + * The filenames "." and ".." are handled specially, since they + * don't follow dos filename rules. + */ + if (un[0] == '.' && unlen == 1) { + dn[0] = '.'; + return 0; + } + if (un[0] == '.' && un[1] == '.' && unlen == 2) { + dn[0] = '.'; + dn[1] = '.'; + return 0; + } + + /* + * Remove any dots from the start of a file name. + */ + while (unlen && (*un == '.')) { + un++; + unlen--; + } + + /* + * Copy the unix filename into the dos filename string upto the end + * of string, a '.', or 8 characters. Whichever happens first stops + * us. This forms the name portion of the dos filename. Fold to + * upper case. + */ + for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) { + if (msdos_map[c] == 0) + break; + dn[i] = msdos_map[c]; + un++; + unlen--; + } + + /* + * Strip any further characters up to a '.' or the end of the + * string. + */ + while (unlen && (c = *un)) { + un++; + unlen--; + /* Make sure we've skipped over the dot before stopping. */ + if (c == '.') + break; + } + + /* + * Copy in the extension part of the name, if any. Force to upper + * case. Note that the extension is allowed to contain '.'s. + * Filenames in this form are probably inaccessable under dos. + */ + for (i = 8; i <= 10 && unlen && (c = *un); i++) { + if (msdos_map[c] == 0) + break; + dn[i] = msdos_map[c]; + un++; + unlen--; + } + return 0; +} diff --git a/cpukit/libfs/src/dosfs/msdos_create.c b/cpukit/libfs/src/dosfs/msdos_create.c new file mode 100644 index 0000000000..ec5862a178 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_create.c @@ -0,0 +1,267 @@ +/* + * Routine to create a new MSDOS filesystem node + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + * + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <rtems/libio_.h> +#include <time.h> + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_creat_node -- + * Create a new node. Determine if the name is a long name. If long we to + * scan the directory to create a short entry. + * + * + + + + * If a new node is file, FAT 32 Bytes Directory + * Entry Structure 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 + * link_info - fs_info of existing node for a pseudo "hard-link" + * (see msdos_file.c, msdos_link for documentation) + * + * 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, + const char *name, + int name_len, + mode_t mode, + const fat_file_fd_t *link_fd) +{ + 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; + uint16_t time_val = 0; + uint16_t date = 0; + fat_dir_pos_t dir_pos; + msdos_name_type_t name_type; + char short_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + char dot_dotdot[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2]; + char link_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + uint32_t sec = 0; + uint32_t byte = 0; + + fat_dir_pos_init(&dir_pos); + + memset(short_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memset(dot_dotdot, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2); + + name_type = msdos_long_to_short (name, name_len, + MSDOS_DIR_NAME(short_node), + MSDOS_NAME_MAX); + + /* fill reserved field */ + *MSDOS_DIR_NT_RES(short_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, &date, &time_val); + *MSDOS_DIR_CRT_TIME(short_node) = CT_LE_W(time_val); + *MSDOS_DIR_CRT_DATE(short_node) = CT_LE_W(date); + *MSDOS_DIR_WRITE_TIME(short_node) = CT_LE_W(time_val); + *MSDOS_DIR_WRITE_DATE(short_node) = CT_LE_W(date); + *MSDOS_DIR_LAST_ACCESS_DATE(short_node) = CT_LE_W(date); + + /* initialize directory/file size */ + *MSDOS_DIR_FILE_SIZE(short_node) = MSDOS_INIT_DIR_SIZE; + + if (type == MSDOS_DIRECTORY) { + *MSDOS_DIR_ATTR(short_node) |= MSDOS_ATTR_DIRECTORY; + } + else if (type == MSDOS_HARD_LINK) { + /* + * when we establish a (temporary) hard link, + * we must copy some information from the original + * node to the newly created + */ + /* + * read the original directory entry + */ + sec = fat_cluster_num_to_sector_num(parent_loc->mt_entry, + link_fd->dir_pos.sname.cln); + sec += (link_fd->dir_pos.sname.ofs >> fs_info->fat.vol.sec_log2); + byte = (link_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1)); + + ret = _fat_block_read(parent_loc->mt_entry, + sec, byte, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE, + link_node); + if (ret < 0) { + return -1; + } + /* + * copy various attributes + */ + *MSDOS_DIR_ATTR(short_node) =*MSDOS_DIR_ATTR(link_node); + *MSDOS_DIR_CRT_TIME_TENTH(short_node)=*MSDOS_DIR_CRT_TIME_TENTH(link_node); + *MSDOS_DIR_CRT_TIME(short_node) =*MSDOS_DIR_CRT_TIME(link_node); + *MSDOS_DIR_CRT_DATE(short_node) =*MSDOS_DIR_CRT_DATE(link_node); + + /* + * copy/set "file size", "first cluster" + */ + *MSDOS_DIR_FILE_SIZE(short_node) =*MSDOS_DIR_FILE_SIZE(link_node); + + *MSDOS_DIR_FIRST_CLUSTER_LOW(short_node) = + *MSDOS_DIR_FIRST_CLUSTER_LOW(link_node); + *MSDOS_DIR_FIRST_CLUSTER_HI(short_node) = + *MSDOS_DIR_FIRST_CLUSTER_HI(link_node); + /* + * set "archive bit" due to changes + */ + *MSDOS_DIR_ATTR(short_node) |= MSDOS_ATTR_ARCHIVE; + } + else { /* regular file... */ + *MSDOS_DIR_ATTR(short_node) |= MSDOS_ATTR_ARCHIVE; + } + + /* + * find free space in the parent directory and write new initialized + * FAT 32 Bytes Directory Entry Structure to the disk + */ + rc = msdos_get_name_node(parent_loc, true, name, name_len, + name_type, &dir_pos, short_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, &dir_pos, &fat_fd); + if (rc != RC_OK) + goto err; + + /* + * we opened fat-file for node we just created, so initialize fat-file + * descritor + */ + 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), short_node, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + memcpy(DOTDOT_NODE_P(dot_dotdot), short_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((uint16_t )((parent_fat_fd->cln) & 0x0000FFFF)); + *MSDOS_DIR_FIRST_CLUSTER_HI(DOTDOT_NODE_P(dot_dotdot)) = + CT_LE_W((uint16_t )(((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, + (uint8_t *)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((uint16_t )((fat_fd->cln) & 0x0000FFFF)); + *MSDOS_DIR_FIRST_CLUSTER_HI(DOT_NODE_P(dot_dotdot)) = + CT_LE_W((uint16_t )(((fat_fd->cln) & 0xFFFF0000) >> 16)); + + /* rewrite dot entry */ + ret = fat_file_write(parent_loc->mt_entry, fat_fd, 0, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE, + (uint8_t *)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 the used 32bytes structure on the disk as free */ + msdos_set_first_char4file_name(parent_loc->mt_entry, &dir_pos, 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..7e6bcf3db6 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_dir.c @@ -0,0 +1,706 @@ +/* + * MSDOS directory handlers implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <rtems/libio_.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <dirent.h> + +#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, uint32_t flag, + uint32_t 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->pathinfo.node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_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->pathinfo.node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_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_format_dirent_with_dot -- + * This routine convert a (short) MSDOS filename as present on disk + * (fixed 8+3 characters, filled with blanks, without separator dot) + * to a "normal" format, with between 0 and 8 name chars, + * a separating dot and up to 3 extension characters + * Rules to work: + * - copy any (0-8) "name" part characters that are non-blank + * - if an extension exists, append a dot + * - copy any (0-3) non-blank extension characters + * - append a '\0' (dont count it for the rturn code + * + * PARAMETERS: + * dst: pointer to destination char array (must be big enough) + * src: pointer to source characters + * + * + * RETURNS: + * the number of bytes (without trailing '\0'(written to destination + */ +static ssize_t +msdos_format_dirent_with_dot(char *dst,const char *src) +{ + ssize_t len; + int i; + const char *src_tmp; + + /* + * find last non-blank character of base name + */ + for ((i = MSDOS_SHORT_BASE_LEN , + src_tmp = src + MSDOS_SHORT_BASE_LEN-1); + ((i > 0) && + (*src_tmp == ' ')); + i--,src_tmp--) + {}; + /* + * copy base name to destination + */ + src_tmp = src; + len = i; + while (i-- > 0) { + *dst++ = tolower((unsigned char)(*src_tmp++)); + } + /* + * find last non-blank character of extension + */ + for ((i = MSDOS_SHORT_EXT_LEN , + src_tmp = src + MSDOS_SHORT_BASE_LEN+MSDOS_SHORT_EXT_LEN-1); + ((i > 0) && + (*src_tmp == ' ')); + i--,src_tmp--) + {}; + /* + * extension is not empty + */ + if (i > 0) { + *dst++ = '.'; /* append dot */ + len += i + 1; /* extension + dot */ + src_tmp = src + MSDOS_SHORT_BASE_LEN; + while (i-- > 0) { + *dst++ = tolower((unsigned char)(*src_tmp++)); + len++; + } + } + *dst = '\0'; /* terminate string */ + + return len; +} + +/* 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, size_t 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->pathinfo.node_access; + fat_file_fd_t *tmp_fat_fd = NULL; + struct dirent tmp_dirent; + uint32_t start = 0; + ssize_t ret = 0; + uint32_t cmpltd = 0; + uint32_t j = 0, i = 0; + uint32_t bts2rd = 0; + uint32_t cur_cln = 0; + uint32_t lfn_start = FAT_FILE_SHORT_NAME; + uint8_t lfn_checksum = 0; + int lfn_entries = 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) + rtems_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); + rtems_set_errno_and_return_minus_one(EIO); + } + + for (i = 0; i < ret; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + char* entry = (char*) fs_info->cl_buf + i; + + /* + * Is this directory from here on empty ? + */ + if ((*MSDOS_DIR_ENTRY_TYPE(entry)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) + { + rtems_semaphore_release(fs_info->vol_sema); + return cmpltd; + } + + /* Is the directory entry empty */ + if ((*MSDOS_DIR_ENTRY_TYPE(entry)) == MSDOS_THIS_DIR_ENTRY_EMPTY) + continue; + + /* Is the directory entry empty a volume label */ + if (((*MSDOS_DIR_ATTR(entry)) & MSDOS_ATTR_VOLUME_ID) && + ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) != MSDOS_ATTR_LFN)) + continue; + + /* + * Check the attribute to see if the entry is for a long file + * name. + */ + if ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) == + MSDOS_ATTR_LFN) + { + int o; + char* p; + int q; + + /* + * Is this is the first entry of a LFN ? + */ + if (lfn_start == FAT_FILE_SHORT_NAME) + { + /* + * The first entry must have the last long entry flag set. + */ + if ((*MSDOS_DIR_ENTRY_TYPE(entry) & + MSDOS_LAST_LONG_ENTRY) == 0) + continue; + + /* + * Remember the start location of the long file name. + */ + lfn_start = + ((j * bts2rd) + i) / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE; + + /* + * Get the number of entries so we can count down and + * also the checksum of the short entry. + */ + lfn_entries = (*MSDOS_DIR_ENTRY_TYPE(entry) & + MSDOS_LAST_LONG_ENTRY_MASK); + lfn_checksum = *MSDOS_DIR_LFN_CHECKSUM(entry); + memset (tmp_dirent.d_name, 0, sizeof(tmp_dirent.d_name)); + } + + /* + * If the entry number or the check sum do not match + * forget this series of long directory entries. These could + * be orphaned entries depending on the history of the + * disk. + */ + if ((lfn_entries != (*MSDOS_DIR_ENTRY_TYPE(entry) & + MSDOS_LAST_LONG_ENTRY_MASK)) || + (lfn_checksum != *MSDOS_DIR_LFN_CHECKSUM(entry))) + { + lfn_start = FAT_FILE_SHORT_NAME; + continue; + } + + /* + * Extract the file name into the directory entry. The data is + * stored in UNICODE characters (16bit). No translation is + * currently supported. + * + * The DOS maximum length is 255 characters without the + * trailing nul character. We need to range check the length to + * fit in the directory entry name field. + */ + + lfn_entries--; + p = entry + 1; + o = lfn_entries * MSDOS_LFN_LEN_PER_ENTRY; + + for (q = 0; q < MSDOS_LFN_LEN_PER_ENTRY; q++) + { + if (o >= (sizeof(tmp_dirent.d_name) - 1)) + break; + + tmp_dirent.d_name[o++] = *p; + + if (*p == '\0') + break; + + switch (q) + { + case 4: + p += 5; + break; + case 10: + p += 4; + break; + default: + p += 2; + break; + } + } + } + else + { + fat_dir_pos_t dir_pos; + + /* + * Skip active entries until get the entry to start from. + */ + if (start) + { + lfn_start = FAT_FILE_SHORT_NAME; + 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; + } + + fat_dir_pos_init(&dir_pos); + dir_pos.sname.cln = cur_cln; + dir_pos.sname.ofs = i; + rc = fat_file_open(iop->pathinfo.mt_entry, &dir_pos, &tmp_fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + /* 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; + + /* + * If a long file name check if the correct number of + * entries have been found and if the checksum is correct. + * If not return the short file name. + */ + if (lfn_start != FAT_FILE_SHORT_NAME) + { + uint8_t cs = 0; + uint8_t* p = (uint8_t*) entry; + int i; + + for (i = 0; i < 11; i++, p++) + cs = ((cs & 1) ? 0x80 : 0) + (cs >> 1) + *p; + + if (lfn_entries || (lfn_checksum != cs)) + lfn_start = FAT_FILE_SHORT_NAME; + } + + if (lfn_start == FAT_FILE_SHORT_NAME) + { + /* + * convert dir entry from fixed 8+3 format (without dot) + * to 0..8 + 1dot + 0..3 format + */ + tmp_dirent.d_namlen = msdos_format_dirent_with_dot( + tmp_dirent.d_name, entry); /* src text */ + } + else + { + tmp_dirent.d_namlen = strlen(tmp_dirent.d_name); + } + + 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). + */ +rtems_off64_t +msdos_dir_lseek(rtems_libio_t *iop, rtems_off64_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: + rtems_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) + rtems_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 | S_IRWXU | S_IRWXG | S_IRWXO; + 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 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->pathinfo.node_access; + 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) + rtems_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_chmod -- + * Change the attributes of the directory. This currently does + * nothing and returns no error. + * + * PARAMETERS: + * pathloc - node description + * mode - the new mode + * + * RETURNS: + * RC_OK always + */ +int +msdos_dir_chmod(rtems_filesystem_location_info_t *pathloc, + mode_t mode) +{ + return RC_OK; +} + +/* 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 *parent_pathloc, + 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; + bool is_empty = false; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_set_errno_and_return_minus_one(EIO); + + /* + * We deny attempts 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); + rtems_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); + rtems_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); + rtems_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->dir_pos, + 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..c128be8ff5 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_eval.c @@ -0,0 +1,437 @@ +/* + * MSDOS evaluation routines + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> + +#include <rtems/libio_.h> + +#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, + size_t pathnamelen, + 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 token_len = 0; + msdos_token_types_t type = MSDOS_CURRENT_DIR; + const char *token; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_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], pathnamelen, &token, &token_len); + pathnamelen -= token_len; + i += token_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 = ENOTSUP; + 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-token_len]), + pathnamelen + token_len, + flags, pathloc); + } + } + else + { + rc = msdos_find_name(pathloc, token, token_len); + 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 = ENOTSUP; + 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, token_len); + 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 token_len; + const char *token; + bool done = false; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_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], strlen(&path[i]), &token, &token_len); + i += token_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-token_len], + pathloc, name); + } + } + else + { + rc = msdos_find_name(pathloc, token, token_len); + 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, token_len); + 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 - token_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..0b05296df9 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_file.c @@ -0,0 +1,503 @@ +/* + * MSDOS file handlers implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/libio_.h> + +#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, uint32_t flag, + uint32_t 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->pathinfo.node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_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->pathinfo.node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_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, size_t count) +{ + ssize_t ret = 0; + 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->pathinfo.node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_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, size_t count) +{ + ssize_t ret = 0; + 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->pathinfo.node_access; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_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). + */ +rtems_off64_t +msdos_file_lseek(rtems_libio_t *iop, rtems_off64_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->pathinfo.node_access; + uint32_t real_size = 0; + + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_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) + rtems_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 | S_IRWXU | S_IRWXG | S_IRWXO; + 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, rtems_off64_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->pathinfo.node_access; + + 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) + rtems_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->pathinfo.node_access; + 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) + rtems_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->pathinfo.node_access; + 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) + rtems_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,uint32_t command, void *buffer) +{ + int rc = RC_OK; + + return rc; +} + +/* msdos_file_chmod -- + * Change the attributes of the file. This currently does + * nothing and returns no error. + * + * PARAMETERS: + * pathloc - node description + * mode - the new mode + * + * RETURNS: + * RC_OK always + */ +int +msdos_file_chmod(rtems_filesystem_location_info_t *pathloc, + mode_t mode) +{ + return RC_OK; +} + +/* 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 *parent_pathloc, + 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) + rtems_set_errno_and_return_minus_one(EIO); + + /* mark file removed */ + rc = msdos_set_first_char4file_name(pathloc->mt_entry, + &fat_fd->dir_pos, + 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_format.c b/cpukit/libfs/src/dosfs/msdos_format.c new file mode 100644 index 0000000000..1394f811c4 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_format.c @@ -0,0 +1,1127 @@ +/*===============================================================*\ +| Project: RTEMS msdos format functionality | ++-----------------------------------------------------------------+ +| File: msdos_format.c | ++-----------------------------------------------------------------+ +| Copyright (c) 2004 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file contains msdos_format function. This function | +| formats a disk partition conforming to MS-DOS conventions | +| | +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| http://www.rtems.com/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 29.10.04 creation doe | +\*===============================================================*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> + +#include <rtems/libio_.h> + +#include "fat.h" +#include "fat_fat_operations.h" +#include "msdos.h" +#include "dosfs.h" + +typedef struct { + uint32_t bytes_per_sector; + uint32_t totl_sector_cnt; + uint32_t rsvd_sector_cnt; + + uint32_t sectors_per_cluster; + uint32_t sectors_per_fat; + + uint32_t fat_start_sec; + uint32_t files_per_root_dir; + uint32_t root_dir_sectors; + uint32_t root_dir_start_sec; + uint32_t root_dir_fmt_sec_cnt; + uint32_t mbr_copy_sec; /* location of copy of mbr or 0 */ + uint32_t fsinfo_sec; /* location of fsinfo sector or 0 */ + uint8_t fat_num; + uint8_t media_code; + uint8_t fattype; + char OEMName[FAT_BR_OEMNAME_SIZE+1]; + char VolLabel[FAT_BR_VOLLAB_SIZE+1]; + bool VolLabel_present; + uint32_t vol_id; +} msdos_format_param_t; + +/* + * Formatted output. + */ +static void +msdos_format_printf (const msdos_format_request_param_t *rqdata, + int info_level, + const char *format, ...) +{ + va_list args; + va_start (args, format); + if (rqdata != NULL && rqdata->info_level >= info_level) + { + vfprintf (stdout, format, args); + fflush (stdout); + } + va_end (args); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int msdos_format_read_sec +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| function to read a sector | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int fd, /* file descriptor index */ + uint32_t start_sector, /* sector number to write to */ + uint32_t sector_size, /* size of sector */ + char *buffer /* buffer with read data into */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ +{ + int ret_val = 0; + + if (0 > lseek(fd,((off_t)start_sector)*sector_size,SEEK_SET)) { + ret_val = -1; + } + if (ret_val == 0) { + if (0 > read(fd,buffer,sector_size)) { + ret_val = -1; + } + } + + return ret_val; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int msdos_format_write_sec +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| function to write to a sector | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int fd, /* file descriptor index */ + uint32_t start_sector, /* sector number to write to */ + uint32_t sector_size, /* size of sector */ + const char *buffer /* buffer with write data */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ +{ + int ret_val = 0; + + if (0 > lseek(fd,((off_t)start_sector)*sector_size,SEEK_SET)) { + ret_val = -1; + } + if (ret_val == 0) { + if (0 > write(fd,buffer,sector_size)) { + ret_val = -1; + } + } + + return ret_val; +} + +/*=========================================================================* \ +| Function: | +\*-------------------------------------------------------------------------*/ +static int msdos_format_fill_sectors +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| function to fill sectors with byte | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const msdos_format_request_param_t *rqdata, + int fd, /* file descriptor index */ + uint32_t start_sector, /* sector number to fill to */ + uint32_t sector_cnt, /* number of sectors to fill to */ + uint32_t sector_size, /* size of sector */ + const char fill_byte /* byte to fill into sectors */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ +{ + int ret_val = 0; + char *fill_buffer = NULL; + uint32_t total_sectors = sector_cnt; + int last_percent = -1; + + /* + * allocate and fill buffer + */ + if (ret_val == 0) { + fill_buffer = malloc(sector_size); + if (fill_buffer == NULL) { + errno = ENOMEM; + ret_val = -1; + } + else { + memset(fill_buffer,fill_byte,sector_size); + } + } + + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, + "Filling : "); + /* + * write to consecutive sectors + */ + while ((ret_val == 0) && + (sector_cnt > 0)) { + int percent = (sector_cnt * 100) / total_sectors; + if (percent != last_percent) { + if ((percent & 1) == 0) + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "."); + last_percent = percent; + } + ret_val = msdos_format_write_sec(fd,start_sector,sector_size,fill_buffer); + start_sector++; + sector_cnt--; + } + + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "\n"); + + if (ret_val) + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_INFO, + "filling error on sector: %d\n", start_sector); + + /* + * cleanup + */ + if (fill_buffer != NULL) { + free(fill_buffer); + fill_buffer = NULL; + } + return ret_val; +} + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int msdos_format_gen_volid +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| function to generate a pseudo-random volume id | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + uint32_t *volid_ptr /* volume ID return pointer */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ +{ + int ret_val = 0; + int rc; + struct timeval time_value; + + rc = rtems_clock_get_tod_timeval(&time_value); + if (rc == RTEMS_SUCCESSFUL) { + *volid_ptr = time_value.tv_sec + time_value.tv_sec; + } + else { + *volid_ptr = rand(); + } + + return ret_val; +} + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int msdos_format_eval_sectors_per_cluster +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| function to check/adjust sectors_per_cluster to legal values | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int fattype, /* type code of FAT (FAT_FAT12 ...) */ + uint32_t bytes_per_sector, /* byte count per sector (512) */ + uint32_t fatdata_sec_cnt, /* sectors available for FAT and data */ + uint8_t fat_num, /* number of fat copies */ + uint32_t sectors_per_cluster, /* sectors per cluster (requested) */ + uint32_t *sectors_per_cluster_adj, /* ret: sec per cluster (granted) */ + uint32_t *sectors_per_fat_ptr /* ret: sectors needed for one FAT */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ +{ + + bool finished = false; + int ret_val = 0; + uint32_t fatdata_cluster_cnt; + uint32_t fat_capacity; + uint32_t sectors_per_fat; + uint32_t data_cluster_cnt; + /* + * ensure, that maximum cluster size (32KByte) is not exceeded + */ + while (MS_BYTES_PER_CLUSTER_LIMIT / bytes_per_sector < sectors_per_cluster) { + sectors_per_cluster /= 2; + } + + do { + /* + * compute number of data clusters for current data: + * - compute cluster count for data AND fat + * - compute storage size for FAT + * - subtract from total cluster count + */ + fatdata_cluster_cnt = fatdata_sec_cnt/sectors_per_cluster; + if (fattype == FAT_FAT12) { + fat_capacity = fatdata_cluster_cnt * 3 / 2; + } + else if (fattype == FAT_FAT16) { + fat_capacity = fatdata_cluster_cnt * 2; + } + else { /* FAT32 */ + fat_capacity = fatdata_cluster_cnt * 4; + } + + sectors_per_fat = ((fat_capacity + + (bytes_per_sector - 1)) + / bytes_per_sector); + + data_cluster_cnt = (fatdata_cluster_cnt - + (((sectors_per_fat * fat_num) + + (sectors_per_cluster - 1)) + / sectors_per_cluster)); + /* + * data cluster count too big? then make sectors bigger + */ + if (((fattype == FAT_FAT12) && (data_cluster_cnt > FAT_FAT12_MAX_CLN)) || + ((fattype == FAT_FAT16) && (data_cluster_cnt > FAT_FAT16_MAX_CLN))) { + sectors_per_cluster *= 2; + } + else { + finished = true; + } + /* + * when maximum cluster size is exceeded, we have invalid data, abort... + */ + if ((sectors_per_cluster * bytes_per_sector) + > MS_BYTES_PER_CLUSTER_LIMIT) { + ret_val = EINVAL; + finished = true; + } + } while (!finished); + + if (ret_val != 0) { + rtems_set_errno_and_return_minus_one(ret_val); + } + else { + *sectors_per_cluster_adj = sectors_per_cluster; + *sectors_per_fat_ptr = sectors_per_fat; + return 0; + } +} + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int msdos_format_determine_fmt_params +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| determine parameters for formatting | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const rtems_disk_device *dd, /* disk device structure */ + const msdos_format_request_param_t *rqdata, /* requested fmt parameters */ + msdos_format_param_t *fmt_params/* computed fmt parameters */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ +{ + int ret_val = 0; + uint32_t fatdata_sect_cnt; + uint32_t onebit; + uint32_t sectors_per_cluster_adj = 0; + uint64_t total_size = 0; + + memset(fmt_params,0,sizeof(*fmt_params)); + /* + * this one is fixed in this implementation. + * At least one thing we don't have to magically guess... + */ + if (ret_val == 0) { + fmt_params->bytes_per_sector = dd->block_size; + fmt_params->totl_sector_cnt = dd->size; + total_size = dd->block_size * dd->size; + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, + "bytes per sector: %d\ntotal sectors: %d\ntotal size: %lu\n", + dd->block_size, dd->size, total_size); + } + /* + * determine number of FATs + */ + if (ret_val == 0) { + if ((rqdata == NULL) || + (rqdata->fat_num == 0)) { + fmt_params->fat_num = 2; + } + else if (rqdata->fat_num <= 6) { + fmt_params->fat_num = rqdata->fat_num; + } + else { + ret_val = EINVAL; + } + } + + if (ret_val == 0) + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, + "number of fats: %d\n", fmt_params->fat_num); + + /* + * Now we get sort of a loop when determining things: + * The FAT type (FAT12/16/32) is determined ONLY from the + * data cluster count: + * Disks with data cluster count < 4085 are FAT12. + * Disks with data cluster count < 65525 are FAT16. + * The rest is FAT32 (no FAT128 available yet :-) + * + * The number of data clusters is the + * total capacity + * minus reserved sectors + * minus root directory ares + * minus storage needed for the FAT (and its copy/copies). + * + * The last item once again depends on the FAT type and the cluster count. + * + * So here is what we do in this formatter: + * - If a FAT type is requested from the caller, we try to modify + * the cluster size, until the data cluster count is in range + * - If no FAT type is given, we estimate a useful FAT type from + * the disk capacity and then adapt the cluster size + */ + + /* + * determine characteristic values: + * - number of sectors + * - number of reserved sectors + * - number of used sectors + * - sectors per cluster + */ + /* + * determine FAT type and sectors per cluster + * depends on + */ + if (ret_val == 0) { + fmt_params->sectors_per_cluster = 1; + if ((rqdata != NULL) && + (rqdata->fattype == MSDOS_FMT_FAT12)) { + fmt_params->fattype = FAT_FAT12; + } + else if ((rqdata != NULL) && + (rqdata->fattype == MSDOS_FMT_FAT16)) { + fmt_params->fattype = FAT_FAT16; + } + else if ((rqdata != NULL) && + (rqdata->fattype == MSDOS_FMT_FAT32)) { + fmt_params->fattype = FAT_FAT32; + } + else if ((rqdata != NULL) && + (rqdata->fattype != MSDOS_FMT_FATANY)) { + ret_val = -1; + errno = EINVAL; + } + else { + /* + * limiting values for disk size, fat type, sectors per cluster + * NOTE: maximum sect_per_clust is arbitrarily choosen with values that + * are a compromise concerning capacity and efficency + */ + if (fmt_params->totl_sector_cnt + < ((uint32_t)FAT_FAT12_MAX_CLN)*8) { + fmt_params->fattype = FAT_FAT12; + /* start trying with small clusters */ + fmt_params->sectors_per_cluster = 2; + } + else if (fmt_params->totl_sector_cnt + < ((uint32_t)FAT_FAT16_MAX_CLN)*32) { + fmt_params->fattype = FAT_FAT16; + /* start trying with small clusters */ + fmt_params->sectors_per_cluster = 2; + } + else { + #define ONE_GB (1024L * 1024L * 1024L) + uint32_t gigs = (total_size + ONE_GB) / ONE_GB; + int b; + fmt_params->fattype = FAT_FAT32; + /* scale with the size of disk... */ + for (b = 31; b > 0; b--) + if ((gigs & (1 << b)) != 0) + break; + fmt_params->sectors_per_cluster = 1 << b; + } + } + /* + * try to use user requested cluster size + */ + if ((rqdata != NULL) && + (rqdata->sectors_per_cluster > 0)) { + fmt_params->sectors_per_cluster = + rqdata->sectors_per_cluster; + } + /* + * check sectors per cluster. + * must be power of 2 + * must be smaller than or equal to 128 + * sectors_per_cluster*bytes_per_sector must not be bigger than 32K + */ + for (onebit = 128;onebit >= 1;onebit = onebit>>1) { + if (fmt_params->sectors_per_cluster >= onebit) { + fmt_params->sectors_per_cluster = onebit; + if (fmt_params->sectors_per_cluster + <= 32768L/fmt_params->bytes_per_sector) { + /* value is small enough so this value is ok */ + onebit = 1; + } + } + } + } + + if (ret_val == 0) { + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, + "sectors per cluster: %d\n", fmt_params->sectors_per_cluster); + + if (fmt_params->fattype == FAT_FAT32) { + /* recommended: for FAT32, always set reserved sector count to 32 */ + fmt_params->rsvd_sector_cnt = 32; + /* for FAT32, always set files per root directory 0 */ + fmt_params->files_per_root_dir = 0; + /* location of copy of MBR */ + fmt_params->mbr_copy_sec = 6; + /* location of fsinfo sector */ + fmt_params->fsinfo_sec = 1; + + } + else { + /* recommended: for FAT12/FAT16, always set reserved sector count to 1 */ + fmt_params->rsvd_sector_cnt = 1; + /* recommended: for FAT16, set files per root directory to 512 */ + /* for FAT12/FAT16, set files per root directory */ + /* must fill up an even count of sectors */ + if ((rqdata != NULL) && + (rqdata->files_per_root_dir > 0)) { + fmt_params->files_per_root_dir = rqdata->files_per_root_dir; + } + else { + if (fmt_params->fattype == FAT_FAT16) { + fmt_params->files_per_root_dir = 512; + } + else { + fmt_params->files_per_root_dir = 64; + } + } + fmt_params->files_per_root_dir = (fmt_params->files_per_root_dir + + (2*fmt_params->bytes_per_sector/ + FAT_DIRENTRY_SIZE-1)); + fmt_params->files_per_root_dir -= (fmt_params->files_per_root_dir % + (2*fmt_params->bytes_per_sector + /FAT_DIRENTRY_SIZE)); + } + fmt_params->root_dir_sectors = + (((fmt_params->files_per_root_dir * FAT_DIRENTRY_SIZE) + + fmt_params->bytes_per_sector - 1) + / fmt_params->bytes_per_sector); + } + if (ret_val == 0) { + fatdata_sect_cnt = (fmt_params->totl_sector_cnt - + fmt_params->rsvd_sector_cnt - + fmt_params->root_dir_sectors); + + /* + * check values to get legal arrangement of FAT type and cluster count + */ + + ret_val = msdos_format_eval_sectors_per_cluster + (fmt_params->fattype, + fmt_params->bytes_per_sector, + fatdata_sect_cnt, + fmt_params->fat_num, + fmt_params->sectors_per_cluster, + §ors_per_cluster_adj, + &(fmt_params->sectors_per_fat)); + fmt_params->sectors_per_cluster = sectors_per_cluster_adj; + } + + /* + * determine media code + */ + if (ret_val == 0) { + if ((rqdata != NULL) && + (rqdata->media != 0)) { + const char valid_media_codes[] = + {0xF0,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}; + if (NULL==memchr(valid_media_codes, + rqdata->media, + sizeof(valid_media_codes))) { + ret_val = -1; + errno = EINVAL; + } + else { + fmt_params->media_code = rqdata->media; + } + } + else { + fmt_params->media_code = FAT_BR_MEDIA_FIXED; + } + } + /* + * determine location and size of root directory + * for formatting + */ + if (fmt_params->root_dir_sectors > 0) { + fmt_params->root_dir_start_sec = + fmt_params->rsvd_sector_cnt + + (fmt_params-> fat_num*fmt_params->sectors_per_fat); + fmt_params->root_dir_fmt_sec_cnt = fmt_params->root_dir_sectors; + } + else { + /* + * for FAT32: root directory is in cluster 2 + */ + fmt_params->root_dir_start_sec = + fmt_params->rsvd_sector_cnt + + (fmt_params-> fat_num*fmt_params->sectors_per_fat); + fmt_params->root_dir_fmt_sec_cnt = fmt_params->sectors_per_cluster; + } + /* + * determine usable OEMName + */ + if (ret_val == 0) { + const char *from; + char *to = fmt_params->OEMName; + int cnt; + from = "RTEMS"; /* default: make "from" point to OS Name */ + if ((rqdata != NULL) && + (rqdata->OEMName != NULL)) { + from = rqdata->OEMName; + } + for (cnt = 0; + cnt < (sizeof(fmt_params->OEMName)-1); + cnt++) { + if (isprint((unsigned char)*from)) { + *to++ = *from++; + } + else { + /* + * non-printable character in given name, so keep stuck + * at that character and replace all following characters + * with a ' ' + */ + *to++=' '; + } + *to = '\0'; + } + } + + /* + * determine usable Volume Label + */ + if (ret_val == 0) { + const char *from; + char *to = fmt_params->VolLabel; + int cnt; + from = ""; /* default: make "from" point to empty string */ + if ((rqdata != NULL) && + (rqdata->VolLabel != NULL)) { + from = rqdata->VolLabel; + fmt_params->VolLabel_present = true; + } + for (cnt = 0; + cnt < (sizeof(fmt_params->VolLabel)-1); + cnt++) { + if (isprint((unsigned char)*from)) { + *to++ = *from++; + } + else { + /* + * non-printable character in given name, so keep stuck + * at that character and replace all following characters + * with a ' ' + */ + *to++=' '; + } + *to = '\0'; + } + } + + /* + * determine usable Volume ID + */ + if (ret_val == 0) { + msdos_format_gen_volid(&(fmt_params->vol_id)); + } + /* + * Phuuu.... That's it. + */ + if (ret_val != 0) { + rtems_set_errno_and_return_minus_one(ret_val); + } + else { + return 0; + } +} +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int msdos_format_gen_mbr +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| create master boot record content from parameter set | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + char mbr[], /* sector buffer */ + const msdos_format_param_t *fmt_params/* computed fmt parameters */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ +{ + uint32_t total_sectors_num16 = 0; + uint32_t total_sectors_num32 = 0; + + /* store total sector count in either 16 or 32 bit field in mbr */ + if (fmt_params->totl_sector_cnt < 0x10000) { + total_sectors_num16 = fmt_params->totl_sector_cnt; + } + else { + total_sectors_num32 = fmt_params->totl_sector_cnt; + } + /* + * finally we are there: let's fill in the values into the MBR + * but first clear the MRB leaving the partition table. + */ +#define RTEMS_IDE_PARTITION_TABLE_OFFSET 0x1be +#define RTEMS_IDE_PARTITION_TABLE_SIZE (4 * 16) + memset(mbr,0,RTEMS_IDE_PARTITION_TABLE_OFFSET); + memset(mbr + RTEMS_IDE_PARTITION_TABLE_OFFSET + RTEMS_IDE_PARTITION_TABLE_SIZE, + 0, + FAT_TOTAL_MBR_SIZE - (RTEMS_IDE_PARTITION_TABLE_OFFSET + RTEMS_IDE_PARTITION_TABLE_SIZE)); + /* + * FIXME: fill jmpBoot and Boot code... + * with 0xEB,.... + */ + /* + * fill OEMName + */ + memcpy(FAT_GET_ADDR_BR_OEMNAME(mbr), + fmt_params->OEMName, + FAT_BR_OEMNAME_SIZE); + FAT_SET_BR_BYTES_PER_SECTOR(mbr , fmt_params->bytes_per_sector); + FAT_SET_BR_SECTORS_PER_CLUSTER(mbr , fmt_params->sectors_per_cluster); + FAT_SET_BR_RESERVED_SECTORS_NUM(mbr, fmt_params->rsvd_sector_cnt); + + /* number of FATs on medium */ + FAT_SET_BR_FAT_NUM(mbr , 2); /* standard/recommended value */ + FAT_SET_BR_FILES_PER_ROOT_DIR(mbr , fmt_params->files_per_root_dir); + FAT_SET_BR_TOTAL_SECTORS_NUM16(mbr , total_sectors_num16); + FAT_SET_BR_MEDIA(mbr , fmt_params->media_code); + + FAT_SET_BR_SECTORS_PER_TRACK(mbr , 255); /* only needed for INT13... */ + FAT_SET_BR_NUMBER_OF_HEADS(mbr , 6); /* only needed for INT13... */ + FAT_SET_BR_HIDDEN_SECTORS(mbr , 1); /* only needed for INT13... */ + + FAT_SET_BR_TOTAL_SECTORS_NUM32(mbr , total_sectors_num32); + if (fmt_params->fattype != FAT_FAT32) { + FAT_SET_BR_SECTORS_PER_FAT(mbr ,fmt_params->sectors_per_fat); + FAT_SET_BR_DRVNUM(mbr , 0); /* only needed for INT13... */ + FAT_SET_BR_RSVD1(mbr , 0); /* fill with zero */ + FAT_SET_BR_BOOTSIG(mbr , FAT_BR_BOOTSIG_VAL); + FAT_SET_BR_VOLID(mbr , fmt_params->vol_id); /* volume id */ + memcpy(FAT_GET_ADDR_BR_VOLLAB(mbr), + fmt_params->VolLabel, + FAT_BR_VOLLAB_SIZE); + memcpy(FAT_GET_ADDR_BR_FILSYSTYPE(mbr), + (fmt_params->fattype == FAT_FAT12) + ? "FAT12 " + : "FAT16 ", + FAT_BR_FILSYSTYPE_SIZE); + } + else { + FAT_SET_BR_SECTORS_PER_FAT32(mbr ,fmt_params->sectors_per_fat); + FAT_SET_BR_EXT_FLAGS(mbr , 0); + FAT_SET_BR_FSVER(mbr , 0); /* FAT32 Version:0.0 */ + FAT_SET_BR_FAT32_ROOT_CLUSTER(mbr , 2); /* put root dir to cluster 2 */ + FAT_SET_BR_FAT32_FS_INFO_SECTOR(mbr, 1); /* Put fsinfo to rsrvd sec 1*/ + FAT_SET_BR_FAT32_BK_BOOT_SECTOR(mbr, fmt_params->mbr_copy_sec ); /* Put MBR copy to rsrvd sec */ + memset(FAT_GET_ADDR_BR_FAT32_RESERVED(mbr),0,FAT_BR_FAT32_RESERVED_SIZE); + + FAT_SET_BR_FAT32_DRVNUM(mbr , 0); /* only needed for INT13... */ + FAT_SET_BR_FAT32_RSVD1(mbr , 0); /* fill with zero */ + FAT_SET_BR_FAT32_BOOTSIG(mbr ,FAT_BR_FAT32_BOOTSIG_VAL); + FAT_SET_BR_FAT32_VOLID(mbr , 0); /* not set */ + memset(FAT_GET_ADDR_BR_FAT32_VOLLAB(mbr) ,0,FAT_BR_VOLLAB_SIZE); + memcpy(FAT_GET_ADDR_BR_FAT32_FILSYSTYPE(mbr), + "FAT32 ", + FAT_BR_FILSYSTYPE_SIZE); + } + /* + * add boot record signature + */ + FAT_SET_BR_SIGNATURE(mbr, FAT_BR_SIGNATURE_VAL); + + /* + * add jump to boot loader at start of sector + */ + FAT_SET_VAL8(mbr,0,0xeb); + FAT_SET_VAL8(mbr,1,0x3c); + FAT_SET_VAL8(mbr,2,0x90); + /* + * FIXME: a nice little PC boot loader would be nice here. + * but where can I get one for free? + */ + /* + * Phuuu.... That's it. + */ + return 0; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int msdos_format_gen_fsinfo +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| create FAT32 fsinfo sector | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + char fsinfo[] /* sector buffer */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ +{ + /* + * clear fsinfo sector data + */ + memset(fsinfo,0,FAT_TOTAL_FSINFO_SIZE); + /* + * write LEADSIG, STRUCTSIG, TRAILSIG + */ + FAT_SET_FSINFO_LEAD_SIGNATURE (fsinfo,FAT_FSINFO_LEAD_SIGNATURE_VALUE ); + FAT_SET_FSINFO_STRUC_SIGNATURE(fsinfo,FAT_FSINFO_STRUC_SIGNATURE_VALUE); + FAT_SET_FSINFO_TRAIL_SIGNATURE(fsinfo,FAT_FSINFO_TRAIL_SIGNATURE_VALUE); + /* + * write "empty" values for free cluster count and next cluster number + */ + FAT_SET_FSINFO_FREE_CLUSTER_COUNT(fsinfo+FAT_FSI_INFO, + 0xffffffff); + FAT_SET_FSINFO_NEXT_FREE_CLUSTER (fsinfo+FAT_FSI_INFO, + 0xffffffff); + return 0; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int msdos_format +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| format device with msdos filesystem | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const char *devname, /* device name */ + const msdos_format_request_param_t *rqdata /* requested fmt parameters */ + /* set to NULL for automatic */ + /* determination */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ +{ + char tmp_sec[FAT_TOTAL_MBR_SIZE]; + int rc; + rtems_disk_device *dd = NULL; + struct stat stat_buf; + int ret_val = 0; + int fd = -1; + int i; + msdos_format_param_t fmt_params; + + /* + * open device for writing + */ + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "open device\n"); + fd = open(devname, O_RDWR); + if (fd == -1) { + ret_val= -1; + } + + /* + * sanity check on device + */ + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, + "stat check: %s\n", devname); + if (ret_val == 0) { + rc = fstat(fd, &stat_buf); + ret_val = rc; + } + + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_INFO, + "formating: %s\n", devname); + /* rtems feature: no block devices, all are character devices */ + if ((ret_val == 0) && (!S_ISBLK(stat_buf.st_mode))) { + errno = ENOTTY; + ret_val = -1; + } + + /* check that device is registered as block device and lock it */ + if (ret_val == 0) { + dd = rtems_disk_obtain(stat_buf.st_rdev); + if (dd == NULL) { + errno = ENOTTY; + ret_val = -1; + } + } + + /* + * compute formatting parameters + */ + if (ret_val == 0) { + ret_val = msdos_format_determine_fmt_params(dd,rqdata,&fmt_params); + } + /* + * if requested, write whole disk/partition with 0xe5 + */ + if ((ret_val == 0) && + (rqdata != NULL) && + !(rqdata->quick_format)) { + ret_val = msdos_format_fill_sectors + (rqdata, + fd, + 0, /* start sector */ + fmt_params.totl_sector_cnt, /* sector count */ + fmt_params.bytes_per_sector, + 0xe5); + } + + /* + * create master boot record + */ + if (ret_val == 0) { + /* + * Read the current MBR to obtain the partition table. + */ + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, + "read MRB sector\n"); + ret_val = msdos_format_read_sec(fd, + 0, + fmt_params.bytes_per_sector, + tmp_sec); + if (ret_val == 0) { + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, + "generate MRB sector\n"); + ret_val = msdos_format_gen_mbr(tmp_sec,&fmt_params); + } + + /* + * write master boot record to disk + * also write copy of MBR to disk + */ + if (ret_val == 0) { + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, + "write MRB sector\n"); + ret_val = msdos_format_write_sec(fd, + 0, + fmt_params.bytes_per_sector, + tmp_sec); + } + if ((ret_val == 0) && + (fmt_params.mbr_copy_sec != 0)) { + /* + * write copy of MBR + */ + msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, + "write back up MRB sector\n"); + ret_val = msdos_format_write_sec(fd, + fmt_params.mbr_copy_sec , + fmt_params.bytes_per_sector, + tmp_sec); + } + } + /* + * for FAT32: initialize info sector on disk + */ + if ((ret_val == 0) && + (fmt_params.fsinfo_sec != 0)) { + ret_val = msdos_format_gen_fsinfo(tmp_sec); + } + /* + * write fsinfo sector + */ + if ((ret_val == 0) && + (fmt_params.fsinfo_sec != 0)) { + ret_val = msdos_format_write_sec(fd, + fmt_params.fsinfo_sec, + fmt_params.bytes_per_sector, + tmp_sec); + } + /* + * write FAT as all empty + * -> write all FAT sectors as zero + */ + if (ret_val == 0) { + ret_val = msdos_format_fill_sectors + (rqdata, + fd, + fmt_params.rsvd_sector_cnt, /* start sector */ + fmt_params.fat_num*fmt_params.sectors_per_fat,/* sector count */ + fmt_params.bytes_per_sector, + 0x00); + } + /* + * clear/init root directory + * -> write all directory sectors as 0x00 + */ + if (ret_val == 0) { + ret_val = msdos_format_fill_sectors + (rqdata, + fd, + fmt_params.root_dir_start_sec, /* start sector */ + fmt_params.root_dir_fmt_sec_cnt, /* sector count */ + fmt_params.bytes_per_sector, + 0x00); + } + /* + * write volume label to first entry of directory + */ + if ((ret_val == 0) && fmt_params.VolLabel_present) { + memset(tmp_sec,0,sizeof(tmp_sec)); + memcpy(MSDOS_DIR_NAME(tmp_sec),fmt_params.VolLabel,MSDOS_SHORT_NAME_LEN); + *MSDOS_DIR_ATTR(tmp_sec) = MSDOS_ATTR_VOLUME_ID; + ret_val = msdos_format_write_sec + (fd, + fmt_params.root_dir_start_sec, + fmt_params.bytes_per_sector, + tmp_sec); + } + /* + * write FAT entry 0 as (0xffffff00|Media_type)EOC, + * write FAT entry 1 as EOC + * allocate directory in a FAT32 FS + */ + if ((ret_val == 0) && fmt_params.VolLabel_present){ + /* + * empty sector: all clusters are free/do not link further on + */ + memset(tmp_sec,0,sizeof(tmp_sec)); + + switch(fmt_params.fattype) { + case FAT_FAT12: + /* LSBits of FAT entry 0: media_type */ + FAT_SET_VAL8(tmp_sec,0,(fmt_params.media_code)); + /* MSBits of FAT entry 0:0xf, LSBits of FAT entry 1: LSB of EOC */ + FAT_SET_VAL8(tmp_sec,1,(0x0f | (FAT_FAT12_EOC << 4))); + /* MSBits of FAT entry 1: MSBits of EOC */ + FAT_SET_VAL8(tmp_sec,2,(FAT_FAT12_EOC >> 4)); + break; + + case FAT_FAT16: + /* FAT entry 0: 0xff00|media_type */ + FAT_SET_VAL8(tmp_sec,0,fmt_params.media_code); + FAT_SET_VAL8(tmp_sec,1,0xff); + /* FAT entry 1: EOC */ + FAT_SET_VAL16(tmp_sec,2,FAT_FAT16_EOC); + break; + + case FAT_FAT32: + /* FAT entry 0: 0xffffff00|media_type */ + FAT_SET_VAL32(tmp_sec,0,0xffffff00|fmt_params.media_code); + /* FAT entry 1: EOC */ + FAT_SET_VAL32(tmp_sec,4,FAT_FAT32_EOC); + break; + + default: + ret_val = -1; + errno = EINVAL; + } + if (fmt_params.fattype == FAT_FAT32) { + /* + * only first valid cluster (cluster number 2) belongs + * to root directory, and is end of chain + * mark this in every copy of the FAT + */ + FAT_SET_VAL32(tmp_sec,8,FAT_FAT32_EOC); + } + for (i = 0; + (i < fmt_params.fat_num) && (ret_val == 0); + i++) { + ret_val = msdos_format_write_sec + (fd, + fmt_params.rsvd_sector_cnt + + (i * fmt_params.sectors_per_fat), + fmt_params.bytes_per_sector, + tmp_sec); + } + } + /* + * cleanup: + * sync and unlock disk + * free any data structures (not needed now) + */ + if (fd != -1) { + close(fd); + } + if (dd != NULL) { + rtems_disk_release(dd); + } + return ret_val; +} diff --git a/cpukit/libfs/src/dosfs/msdos_free.c b/cpukit/libfs/src/dosfs/msdos_free.c new file mode 100644 index 0000000000..90fc586a10 --- /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 <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/libio_.h> + +#include <errno.h> + +#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) + rtems_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..310da9c8ff --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_fsunmount.c @@ -0,0 +1,70 @@ +/* + * MSDOS shut down handler implementation + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include <rtems.h> +#include <rtems/libio_.h> + +#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..cc32af86d4 --- /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 <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/libio.h> +#include "msdos.h" + +const rtems_filesystem_file_handlers_r msdos_dir_handlers = { + msdos_dir_open, + msdos_dir_close, + msdos_dir_read, + rtems_filesystem_default_write, + rtems_filesystem_default_ioctl, + msdos_dir_lseek, + msdos_dir_stat, + msdos_dir_chmod, + rtems_filesystem_default_ftruncate, + rtems_filesystem_default_fpathconf, + msdos_dir_sync, + msdos_dir_sync, + rtems_filesystem_default_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..18d4fdd018 --- /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 <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/libio.h> +#include "msdos.h" + +const 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, + msdos_file_chmod, + msdos_file_ftruncate, + rtems_filesystem_default_fpathconf, + msdos_file_sync, + msdos_file_datasync, + rtems_filesystem_default_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..217e0c1f3a --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_init.c @@ -0,0 +1,64 @@ +/* + * Init routine for MSDOS + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/libio_.h> +#include "dosfs.h" +#include "msdos.h" + +const rtems_filesystem_operations_table msdos_ops = { + .evalpath_h = msdos_eval_path, + .evalformake_h = msdos_eval4make, + .link_h = rtems_filesystem_default_link, + .unlink_h = msdos_file_rmnod, + .node_type_h = msdos_node_type, + .mknod_h = msdos_mknod, + .chown_h = rtems_filesystem_default_chown, + .freenod_h = msdos_free_node_info, + .mount_h = rtems_filesystem_default_mount, + .fsmount_me_h = rtems_dosfs_initialize, + .unmount_h = rtems_filesystem_default_unmount, + .fsunmount_me_h = msdos_shut_down, + .utime_h = rtems_filesystem_default_utime, + .eval_link_h = rtems_filesystem_default_evaluate_link, + .symlink_h = rtems_filesystem_default_symlink, + .readlink_h = rtems_filesystem_default_readlink, + .rename_h = msdos_rename, + .statvfs_h = rtems_filesystem_default_statvfs +}; + +/* msdos_initialize -- + * MSDOS filesystem initialization. Called when mounting an + * MSDOS filesystem. + * + * PARAMETERS: + * temp_mt_entry - mount table entry + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + * + */ +int rtems_dosfs_initialize(rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data) +{ + int rc; + + rc = msdos_initialize_support(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..fc10fda71a --- /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 <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include <rtems.h> +#include <rtems/libio_.h> + +#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, + const rtems_filesystem_operations_table *op_table, + const rtems_filesystem_file_handlers_r *file_handlers, + const 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; + fat_dir_pos_t root_pos; + uint32_t cl_buf_size; + + fs_info = (msdos_fs_info_t *)calloc(1, sizeof(msdos_fs_info_t)); + if (!fs_info) + rtems_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) + */ + fat_dir_pos_init(&root_pos); + root_pos.sname.cln = FAT_ROOTDIR_CLUSTER_NUM; + rc = fat_file_open(temp_mt_entry, &root_pos, &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->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 = (uint8_t *)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); + rtems_set_errno_and_return_minus_one(ENOMEM); + } + + sc = rtems_semaphore_create(3, + 1, + RTEMS_BINARY_SEMAPHORE | RTEMS_FIFO, + 0, + &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); + rtems_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..d49b89048b --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_misc.c @@ -0,0 +1,1731 @@ +/* + * Miscellaneous routines implementation for MSDOS filesystem + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ + +#define MSDOS_TRACE 1 + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <ctype.h> +#include <sys/time.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> +#include <rtems/libio_.h> + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + + +#include <stdio.h> + +/* + * External strings. Saves spave this way. + */ +const char *const MSDOS_DOT_NAME = ". "; +const char *const MSDOS_DOTDOT_NAME = ".. "; + +/* msdos_is_valid_name_char -- + * Routine to check the character in a file or directory name. + * The characters support in the short file name are letters, + * digits, or characters with code points values greater than + * 127 (not sure what this last is) plus the following special + * characters "$%'-_@~`!(){}^#&". The must be uppercase. + * + * The following 6 characters are allowed in a long names, + * " +,;=[]" including a space and lower case letters. + * + * PARAMETERS: + * ch - character to check. + * + * RETURNS: + * MSDOS_NAME_INVALID - Not valid in a long or short name. + * MSDOS_NAME_SHORT - Valid in a short name or long name. + * MSDOS_NAME_LONG - Valid in a long name only. + * + */ +static msdos_name_type_t +msdos_is_valid_name_char(const char ch) +{ + if (strchr(" +,;=[]", ch) != NULL) + return MSDOS_NAME_LONG; + + if ((ch == '.') || isalnum((unsigned char)ch) || + (strchr("$%'-_@~`!(){}^#&", ch) != NULL)) + return MSDOS_NAME_SHORT; + + return MSDOS_NAME_INVALID; +} + +/* msdos_short_hex_number -- + * Routine to set the hex number in the SFN. + * + * PARAMETERS: + * name - name to change + * num - number to set + * + * RETURNS: + * nothing + * + */ +static void +msdos_short_name_hex(char* sfn, int num) +{ + static const char* hex = "0123456789ABCDEF"; + char* c = MSDOS_DIR_NAME(sfn); + int i; + for (i = 0; i < 2; i++, c++) + if ((*c == ' ') || (*c == '.')) + *c = '_'; + for (i = 0; i < 4; i++, c++) + *c = hex[(num >> ((3 - i) * 4)) & 0xf]; + *c++ = '~'; + *c++ = '1'; +} + +/* msdos_name_type -- + * Routine the type of file name. + * + * PARAMETERS: + * name - name to check + * + * RETURNS: + * true the name is long, else the name is short. + * + */ +#define MSDOS_NAME_TYPE_PRINT 0 +static msdos_name_type_t +msdos_name_type(const char *name, int name_len) +{ + bool lowercase = false; + bool uppercase = false; + int dot_at = -1; + int count = 0; + + while (*name && (count < name_len)) + { + bool is_dot = *name == '.'; + msdos_name_type_t type = msdos_is_valid_name_char(*name); + +#if MSDOS_NAME_TYPE_PRINT + printf ("MSDOS_NAME_TYPE: c:%02x type:%d\n", *name, type); +#endif + + if ((type == MSDOS_NAME_INVALID) || (type == MSDOS_NAME_LONG)) + return type; + + if (dot_at >= 0) + { + if (is_dot || ((count - dot_at) > 3)) + { +#if MSDOS_NAME_TYPE_PRINT + printf ("MSDOS_NAME_TYPE: LONG[1]: is_dot:%d, at:%d cnt\n", + is_dot, dot_at, count); +#endif + return MSDOS_NAME_LONG; + } + } + else + { + if (count == 8 && !is_dot) + { +#if MSDOS_NAME_TYPE_PRINT + printf ("MSDOS_NAME_TYPE: LONG[2]: is_dot:%d, at:%d cnt\n", + is_dot, dot_at, count); +#endif + return MSDOS_NAME_LONG; + } + } + + if (is_dot) + dot_at = count; + else if ((*name >= 'A') && (*name <= 'Z')) + uppercase = true; + else if ((*name >= 'a') && (*name <= 'z')) + lowercase = true; + + count++; + name++; + } + + if (lowercase && uppercase) + { +#if MSDOS_NAME_TYPE_PRINT + printf ("MSDOS_NAME_TYPE: LONG[3]\n"); +#endif + return MSDOS_NAME_LONG; + } + +#if MSDOS_NAME_TYPE_PRINT + printf ("MSDOS_NAME_TYPE: SHORT[1]\n"); +#endif + return MSDOS_NAME_SHORT; +} + +/* msdos_long_to_short -- + * Routine to creates a short name from a long. Start the end of the + * + * PARAMETERS: + * name - name to check + * + * RETURNS: + * true the name is long, else the name is short. + * + */ +#define MSDOS_L2S_PRINT 0 +msdos_name_type_t +msdos_long_to_short(const char *lfn, int lfn_len, char* sfn, int sfn_len) +{ + msdos_name_type_t type; + int i; + + /* + * Fill with spaces. This is how a short directory entry is padded. + */ + memset (sfn, ' ', sfn_len); + + /* + * Handle '.' and '..' specially. + */ + if ((lfn[0] == '.') && (lfn_len == 1)) + { + sfn[0] = '.'; +#if MSDOS_L2S_PRINT + printf ("MSDOS_L2S: SHORT[1]: lfn:'%s' SFN:'%s'\n", lfn, sfn); +#endif + return MSDOS_NAME_SHORT; + } + + if ((lfn[0] == '.') && (lfn[1] == '.') && (lfn_len == 2)) + { + sfn[0] = sfn[1] = '.'; +#if MSDOS_L2S_PRINT + printf ("MSDOS_L2S: SHORT[2]: lfn:'%s' SFN:'%s'\n", lfn, sfn); +#endif + return MSDOS_NAME_SHORT; + } + + /* + * Filenames with only blanks and dots are not allowed! + */ + for (i = 0; i < lfn_len; i++) + if ((lfn[i] != ' ') && (lfn[i] != '.')) + break; + + if (i == lfn_len) + { +#if MSDOS_L2S_PRINT + printf ("MSDOS_L2S: INVALID[1]: lfn:'%s' SFN:'%s'\n", lfn, sfn); +#endif + return MSDOS_NAME_INVALID; + } + + /* + * Is this a short name ? + */ + + type = msdos_name_type (lfn, lfn_len); + + if (type == MSDOS_NAME_INVALID) + { +#if MSDOS_L2S_PRINT + printf ("MSDOS_L2S: INVALID[2]: lfn:'%s' SFN:'%s'\n", lfn, sfn); +#endif + return MSDOS_NAME_INVALID; + } + + msdos_filename_unix2dos (lfn, lfn_len, sfn); + +#if MSDOS_L2S_PRINT + printf ("MSDOS_L2S: TYPE:%d lfn:'%s' SFN:'%s'\n", type, lfn, sfn); +#endif + return type; +} + +/* msdos_get_token -- + * Routine to get a token (name or separator) from the path. + * + * PARAMETERS: + * path - path to get token from + * ret_token - returned token + * token_len - length of returned token + * + * RETURNS: + * token type, token and token length + * + */ +msdos_token_types_t +msdos_get_token(const char *path, + int pathlen, + const char **ret_token, + int *ret_token_len) +{ + msdos_token_types_t type = MSDOS_NAME; + int i = 0; + + *ret_token = NULL; + *ret_token_len = 0; + + if (pathlen == 0) + return MSDOS_NO_MORE_PATH; + + /* + * Check for a separator. + */ + while (!msdos_is_separator(path[i]) && (i < pathlen)) + { + if ( !msdos_is_valid_name_char(path[i]) ) + return MSDOS_INVALID_TOKEN; + ++i; + if ( i == MSDOS_NAME_MAX_LFN_WITH_DOT ) + return MSDOS_INVALID_TOKEN; + } + + *ret_token = path; + + /* + * If it is just a separator then it is the current dir. + */ + if ( i == 0 ) + { + if ( (*path != '\0') && pathlen ) + { + i++; + type = MSDOS_CURRENT_DIR; + } + else + type = MSDOS_NO_MORE_PATH; + } + + /* + * Set the token and token_len to the token start and length. + */ + *ret_token_len = i; + + /* + * If we copied something that was not a seperator see if + * it was a special name. + */ + if ( type == MSDOS_NAME ) + { + if ((i == 2) && ((*ret_token)[0] == '.') && ((*ret_token)[1] == '.')) + { + type = MSDOS_UP_DIR; + return type; + } + + if ((i == 1) && ((*ret_token)[0] == '.')) + { + type = MSDOS_CURRENT_DIR; + return type; + } + } + + return type; +} + + +/* msdos_find_name -- + * Find the node which correspondes to the name, open fat-file which + * correspondes to the found node and close fat-file which correspondes + * to the node we searched in. + * + * PARAMETERS: + * parent_loc - parent node description + * name - name to find + * + * RETURNS: + * RC_OK and updated 'parent_loc' on success, or -1 if error + * occured (errno set apropriately) + * + */ +int +msdos_find_name( + rtems_filesystem_location_info_t *parent_loc, + const char *name, + int name_len + ) +{ + int rc = RC_OK; + msdos_fs_info_t *fs_info = parent_loc->mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + msdos_name_type_t name_type; + fat_dir_pos_t dir_pos; + unsigned short time_val = 0; + unsigned short date = 0; + char node_entry[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + + memset(node_entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + + name_type = msdos_long_to_short (name, + name_len, + MSDOS_DIR_NAME(node_entry), + MSDOS_NAME_MAX); + + /* + * find the node which correspondes to the name in the directory pointed by + * 'parent_loc' + */ + rc = msdos_get_name_node(parent_loc, false, name, name_len, name_type, + &dir_pos, node_entry); + if (rc != RC_OK) + return rc; + + if (((*MSDOS_DIR_ATTR(node_entry)) & MSDOS_ATTR_VOLUME_ID) || + ((*MSDOS_DIR_ATTR(node_entry) & MSDOS_ATTR_LFN_MASK) == MSDOS_ATTR_LFN)) + return MSDOS_NAME_NOT_FOUND_ERR; + + /* open fat-file corresponded to the found node */ + rc = fat_file_open(parent_loc->mt_entry, &dir_pos, &fat_fd); + if (rc != RC_OK) + return rc; + + fat_fd->dir_pos = dir_pos; + + /* + * I don't like this if, but: we should do it, or should write new file + * size and first cluster num to the disk after each write operation + * (even if one byte is written - that is TOO slow) because + * otherwise real values of these fields stored in fat-file descriptor + * may be accidentally rewritten with wrong values stored on the disk + */ + if (fat_fd->links_num == 1) + { + fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(node_entry); + + time_val = *MSDOS_DIR_WRITE_TIME(node_entry); + date = *MSDOS_DIR_WRITE_DATE(node_entry); + + fat_fd->mtime = msdos_date_dos2unix(CF_LE_W(date), CF_LE_W(time_val)); + + if ((*MSDOS_DIR_ATTR(node_entry)) & MSDOS_ATTR_DIRECTORY) + { + fat_fd->fat_file_type = FAT_DIRECTORY; + fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT; + + rc = fat_file_size(parent_loc->mt_entry, fat_fd); + if (rc != RC_OK) + { + fat_file_close(parent_loc->mt_entry, fat_fd); + return rc; + } + } + else + { + fat_fd->fat_file_size = CF_LE_L(*MSDOS_DIR_FILE_SIZE(node_entry)); + fat_fd->fat_file_type = FAT_FILE; + fat_fd->size_limit = MSDOS_MAX_FILE_SIZE; + } + + /* these data is not actual for zero-length fat-file */ + fat_fd->map.file_cln = 0; + fat_fd->map.disk_cln = fat_fd->cln; + + if ((fat_fd->fat_file_size != 0) && + (fat_fd->fat_file_size <= fs_info->fat.vol.bpc)) + { + fat_fd->map.last_cln = fat_fd->cln; + } + else + { + fat_fd->map.last_cln = FAT_UNDEFINED_VALUE; + } + } + + /* close fat-file corresponded to the node we searched in */ + rc = fat_file_close(parent_loc->mt_entry, parent_loc->node_access); + if (rc != RC_OK) + { + fat_file_close(parent_loc->mt_entry, fat_fd); + return rc; + } + + /* update node_info_ptr field */ + parent_loc->node_access = fat_fd; + + return rc; +} + +/* msdos_get_name_node -- + * This routine is used in two ways: for a new node creation (a) or for + * search the node which correspondes to the name parameter (b). + * In case (a) 'name' should be set up to NULL and 'name_dir_entry' should + * point to initialized 32 bytes structure described a new node. + * In case (b) 'name' should contain a valid string. + * + * (a): reading fat-file which correspondes to directory we are going to + * create node in. If free slot is found write contents of + * 'name_dir_entry' into it. If reach end of fat-file and no free + * slot found, write 32 bytes to the end of fat-file. + * + * (b): reading fat-file which correspondes to directory and trying to + * find slot with the name field == 'name' parameter + * + * + * PARAMETERS: + * parent_loc - node description to create node in or to find name in + * name - NULL or name to find + * paux - identify a node location on the disk - + * cluster num and offset inside the cluster + * short_dir_entry - node to create/placeholder for found node (IN/OUT) + * + * RETURNS: + * RC_OK, filled aux_struct_ptr and name_dir_entry on success, or -1 if + * error occured (errno set apropriately) + * + */ +int +msdos_get_name_node( + rtems_filesystem_location_info_t *parent_loc, + bool create_node, + const char *name, + int name_len, + msdos_name_type_t name_type, + fat_dir_pos_t *dir_pos, + char *name_dir_entry + ) +{ + int rc = RC_OK; + fat_file_fd_t *fat_fd = parent_loc->node_access; + uint32_t dotdot_cln = 0; + + /* find name in fat-file which corresponds to the directory */ + rc = msdos_find_name_in_fat_file(parent_loc->mt_entry, fat_fd, + create_node, name, name_len, name_type, + dir_pos, name_dir_entry); + if ((rc != RC_OK) && (rc != MSDOS_NAME_NOT_FOUND_ERR)) + return rc; + + if (!create_node) + { + /* if we search for valid name and name not found -> return */ + if (rc == MSDOS_NAME_NOT_FOUND_ERR) + return rc; + + /* + * if we have deal with ".." - it is a special case :((( + * + * Really, we should return cluster num and offset not of ".." slot, but + * slot which correspondes to real directory name. + */ + if (rc == RC_OK) + { + if (strncmp(name, "..", 2) == 0) + { + dotdot_cln = MSDOS_EXTRACT_CLUSTER_NUM((name_dir_entry)); + + /* are we right under root dir ? */ + if (dotdot_cln == 0) + { + /* + * we can relax about first_char field - it never should be + * used for root dir + */ + fat_dir_pos_init(dir_pos); + dir_pos->sname.cln = FAT_ROOTDIR_CLUSTER_NUM; + } + else + { + rc = + msdos_get_dotdot_dir_info_cluster_num_and_offset(parent_loc->mt_entry, + dotdot_cln, + dir_pos, + name_dir_entry); + if (rc != RC_OK) + return rc; + } + } + } + } + return rc; +} + +/* + * msdos_get_dotdot_dir_info_cluster_num_and_offset + * + * Unfortunately, in general, we cann't work here in fat-file ideologic + * (open fat_file "..", get ".." and ".", open "..", find an entry ...) + * because if we open + * fat-file ".." it may happend that we have two different fat-file + * descriptors ( for real name of directory and ".." name ) for a single + * file ( cluster num of both pointers to the same cluster ) + * But...we do it because we protected by semaphore + * + */ + +/* msdos_get_dotdot_dir_info_cluster_num_and_offset -- + * Get cluster num and offset not of ".." slot, but slot which correspondes + * to real directory name. + * + * PARAMETERS: + * mt_entry - mount table entry + * cln - data cluster num extracted drom ".." slot + * paux - identify a node location on the disk - + * number of cluster and offset inside the cluster + * dir_entry - placeholder for found node + * + * RETURNS: + * RC_OK, filled 'paux' and 'dir_entry' on success, or -1 if error occured + * (errno set apropriately) + * + */ +int +msdos_get_dotdot_dir_info_cluster_num_and_offset( + rtems_filesystem_mount_table_entry_t *mt_entry, + uint32_t cln, + fat_dir_pos_t *dir_pos, + char *dir_entry + ) +{ + int rc = RC_OK; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + fat_file_fd_t *fat_fd = NULL; + char dot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + char dotdot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE]; + uint32_t cl4find = 0; + + /* + * open fat-file corresponded to ".." + */ + rc = fat_file_open(mt_entry, dir_pos, &fat_fd); + if (rc != RC_OK) + return rc; + + fat_fd->cln = cln; + fat_fd->fat_file_type = FAT_DIRECTORY; + fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT; + + fat_fd->map.file_cln = 0; + fat_fd->map.disk_cln = fat_fd->cln; + + rc = fat_file_size(mt_entry, fat_fd); + if (rc != RC_OK) + { + fat_file_close(mt_entry, fat_fd); + return rc; + } + + /* find "." node in opened directory */ + memset(dot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + msdos_long_to_short(".", 1, dot_node, MSDOS_SHORT_NAME_LEN); + rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, false, ".", 1, + MSDOS_NAME_SHORT, dir_pos, dot_node); + + if (rc != RC_OK) + { + fat_file_close(mt_entry, fat_fd); + return rc; + } + + /* find ".." node in opened directory */ + memset(dotdot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + msdos_long_to_short("..", 2, dotdot_node, MSDOS_SHORT_NAME_LEN); + rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, false, "..", 2, + MSDOS_NAME_SHORT, dir_pos, + dotdot_node); + + if (rc != RC_OK) + { + fat_file_close(mt_entry, fat_fd); + return rc; + } + + cl4find = MSDOS_EXTRACT_CLUSTER_NUM(dot_node); + + /* close fat-file corresponded to ".." directory */ + rc = fat_file_close(mt_entry, fat_fd); + if ( rc != RC_OK ) + return rc; + + if ( (MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node)) == 0) + { + /* + * we handle root dir for all FAT types in the same way with the + * ordinary directories ( through fat_file_* calls ) + */ + fat_dir_pos_init(dir_pos); + dir_pos->sname.cln = FAT_ROOTDIR_CLUSTER_NUM; + } + + /* open fat-file corresponded to second ".." */ + rc = fat_file_open(mt_entry, dir_pos, &fat_fd); + if (rc != RC_OK) + return rc; + + if ((MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node)) == 0) + fat_fd->cln = fs_info->fat.vol.rdir_cl; + else + fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node); + + fat_fd->fat_file_type = FAT_DIRECTORY; + fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT; + + fat_fd->map.file_cln = 0; + fat_fd->map.disk_cln = fat_fd->cln; + + rc = fat_file_size(mt_entry, fat_fd); + if (rc != RC_OK) + { + fat_file_close(mt_entry, fat_fd); + return rc; + } + + /* in this directory find slot with specified cluster num */ + rc = msdos_find_node_by_cluster_num_in_fat_file(mt_entry, fat_fd, cl4find, + dir_pos, dir_entry); + if (rc != RC_OK) + { + fat_file_close(mt_entry, fat_fd); + return rc; + } + rc = fat_file_close(mt_entry, fat_fd); + return rc; +} + + +/* msdos_set_dir_wrt_time_and_date -- + * Write last write date and time for a file to the disk (to corresponded + * 32bytes node) + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + * + */ +int +msdos_set_dir_wrt_time_and_date( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd + ) +{ + ssize_t ret1 = 0, ret2 = 0, ret3 = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + uint16_t time_val; + uint16_t date; + uint32_t sec = 0; + uint32_t byte = 0; + + msdos_date_unix2dos(fat_fd->mtime, &date, &time_val); + + /* + * calculate input for _fat_block_write: convert (cluster num, offset) to + * (sector num, new offset) + */ + sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->dir_pos.sname.cln); + sec += (fat_fd->dir_pos.sname.ofs >> fs_info->fat.vol.sec_log2); + /* byte points to start of 32bytes structure */ + byte = fat_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1); + + time_val = CT_LE_W(time_val); + ret1 = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_WTIME_OFFSET, + 2, (char *)(&time_val)); + date = CT_LE_W(date); + ret2 = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_WDATE_OFFSET, + 2, (char *)(&date)); + ret3 = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_ADATE_OFFSET, + 2, (char *)(&date)); + + if ( (ret1 < 0) || (ret2 < 0) || (ret3 < 0) ) + return -1; + + return RC_OK; +} + +/* msdos_set_first_cluster_num -- + * Write number of first cluster of the file to the disk (to corresponded + * 32bytes slot) + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * + * RETURNS: + * RC_OK on success, or -1 if error occured + * + */ +int +msdos_set_first_cluster_num( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd + ) +{ + ssize_t ret1 = 0, ret2 = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t new_cln = fat_fd->cln; + uint16_t le_cl_low = 0; + uint16_t le_cl_hi = 0; + uint32_t sec = 0; + uint32_t byte = 0; + + /* + * calculate input for _fat_block_write: convert (cluster num, offset) to + * (sector num, new offset) + */ + sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->dir_pos.sname.cln); + sec += (fat_fd->dir_pos.sname.ofs >> fs_info->fat.vol.sec_log2); + /* byte from points to start of 32bytes structure */ + byte = fat_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1); + + le_cl_low = CT_LE_W((uint16_t )(new_cln & 0x0000FFFF)); + ret1 = _fat_block_write(mt_entry, sec, + byte + MSDOS_FIRST_CLUSTER_LOW_OFFSET, 2, + (char *)(&le_cl_low)); + le_cl_hi = CT_LE_W((uint16_t )((new_cln & 0xFFFF0000) >> 16)); + ret2 = _fat_block_write(mt_entry, sec, + byte + MSDOS_FIRST_CLUSTER_HI_OFFSET, 2, + (char *)(&le_cl_hi)); + if ( (ret1 < 0) || (ret2 < 0) ) + return -1; + + return RC_OK; +} + + +/* msdos_set_file size -- + * Write file size of the file to the disk (to corresponded 32bytes slot) + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately). + * + */ +int +msdos_set_file_size( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd + ) +{ + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t le_new_length = 0; + uint32_t sec = 0; + uint32_t byte = 0; + + sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->dir_pos.sname.cln); + sec += (fat_fd->dir_pos.sname.ofs >> fs_info->fat.vol.sec_log2); + byte = (fat_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1)); + + le_new_length = CT_LE_L((fat_fd->fat_file_size)); + ret = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_SIZE_OFFSET, 4, + (char *)(&le_new_length)); + if ( ret < 0 ) + return -1; + + return RC_OK; +} + +/* + * We should not check whether this routine is called for root dir - it + * never can happend + */ + +/* msdos_set_first_char4file_name -- + * Write first character of the name of the file to the disk (to + * corresponded 32bytes slot) + * + * PARAMETERS: + * mt_entry - mount table entry + * cl - number of cluster + * ofs - offset inside cluster + * fchar - character to set up + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set apropriately) + * + */ +int +msdos_set_first_char4file_name( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_dir_pos_t *dir_pos, + unsigned char fchar + ) +{ + ssize_t ret; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t dir_block_size; + fat_pos_t start = dir_pos->lname; + fat_pos_t end = dir_pos->sname; + + if ((end.cln == fs_info->fat.vol.rdir_cl) && + (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16))) + dir_block_size = fs_info->fat.vol.rdir_size; + else + dir_block_size = fs_info->fat.vol.bpc; + + if (dir_pos->lname.cln == FAT_FILE_SHORT_NAME) + start = dir_pos->sname; + + /* + * We handle the changes directly due the way the short file + * name code was written rather than use the fat_file_write + * interface. + */ + while (true) + { + uint32_t sec = (fat_cluster_num_to_sector_num(mt_entry, start.cln) + + (start.ofs >> fs_info->fat.vol.sec_log2)); + uint32_t byte = (start.ofs & (fs_info->fat.vol.bps - 1));; + + ret = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_NAME_OFFSET, 1, + &fchar); + if (ret < 0) + return -1; + + if ((start.cln == end.cln) && (start.ofs == end.ofs)) + break; + + start.ofs += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE; + if (start.ofs >= dir_block_size) + { + int rc; + if ((end.cln == fs_info->fat.vol.rdir_cl) && + (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16))) + break; + rc = fat_get_fat_cluster(mt_entry, start.cln, &start.cln); + if ( rc != RC_OK ) + return rc; + start.ofs = 0; + } + } + + return RC_OK; +} + +/* msdos_dir_is_empty -- + * Check whether directory which correspondes to the fat-file descriptor is + * empty. + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * ret_val - placeholder for result + * + * RETURNS: + * RC_OK on success, or -1 if error occured + * + */ +int +msdos_dir_is_empty( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + bool *ret_val + ) +{ + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t j = 0, i = 0; + + /* dir is not empty */ + *ret_val = false; + + while ((ret = fat_file_read(mt_entry, fat_fd, j * fs_info->fat.vol.bps, + fs_info->fat.vol.bps, + fs_info->cl_buf)) != FAT_EOF) + { + if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + return -1; + + assert(ret == fs_info->fat.vol.bps); + + /* have to look at the DIR_NAME as "raw" 8-bit data */ + for (i = 0; + i < fs_info->fat.vol.bps; + i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + char* entry = (char*) fs_info->cl_buf + i; + + /* + * If the entry is empty, a long file name entry, or '.' or '..' + * then consider it as empty. + * + * Just ignore long file name entries. They must have a short entry to + * be valid. + */ + if (((*MSDOS_DIR_ENTRY_TYPE(entry)) == + MSDOS_THIS_DIR_ENTRY_EMPTY) || + ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) == + MSDOS_ATTR_LFN) || + (strncmp(MSDOS_DIR_NAME((entry)), MSDOS_DOT_NAME, + MSDOS_SHORT_NAME_LEN) == 0) || + (strncmp(MSDOS_DIR_NAME((entry)), + MSDOS_DOTDOT_NAME, + MSDOS_SHORT_NAME_LEN) == 0)) + continue; + + /* + * Nothing more to look at. + */ + if ((*MSDOS_DIR_NAME(entry)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) + { + *ret_val = true; + return RC_OK; + } + + /* + * Short file name entries mean not empty. + */ + return RC_OK; + } + j++; + } + *ret_val = true; + return RC_OK; +} + +/* msdos_create_name_in_fat_file -- + * This routine creates an entry in the fat file for the file name + * provided by the user. The directory entry passed is the short + * file name and is added as it. If the file name is long a long + * file name set of entries is added. + * + * Scan the directory for the file and if not found add the new entry. + * When scanning remember the offset in the file where the directory + * entry can be added. + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * name - NULL or name to find + * paux - identify a node location on the disk - + * number of cluster and offset inside the cluster + * name_dir_entry - node to create/placeholder for found node + * + * RETURNS: + * RC_OK on success, or error code if error occured (errno set + * appropriately) + * + */ +#define MSDOS_FIND_PRINT 0 +int msdos_find_name_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + bool create_node, + const char *name, + int name_len, + msdos_name_type_t name_type, + fat_dir_pos_t *dir_pos, + char *name_dir_entry + ) +{ + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t dir_offset = 0; + uint32_t dir_entry = 0; + uint32_t bts2rd = 0; + fat_pos_t lfn_start; + bool lfn_matched = false; + uint8_t lfn_checksum = 0; + int lfn_entries; + int lfn_entry = 0; + uint32_t empty_space_offset = 0; + uint32_t empty_space_entry = 0; + uint32_t empty_space_count = 0; + bool empty_space_found = false; + uint32_t entries_per_block; + bool read_cluster = false; + + assert(name_len > 0); + + fat_dir_pos_init(dir_pos); + + lfn_start.cln = lfn_start.ofs = FAT_FILE_SHORT_NAME; + + /* + * Set the number of short entries needed to store the LFN. If the name + * is short still check for possible long entries with the short name. + * + * In PR1491 we need to have a LFN for a short file name entry. To + * test this make this test always fail, ie add "0 &&". + */ + if (create_node && (name_type == MSDOS_NAME_SHORT)) + lfn_entries = 0; + else + lfn_entries = + ((name_len - 1) + MSDOS_LFN_LEN_PER_ENTRY) / MSDOS_LFN_LEN_PER_ENTRY; + + if (FAT_FD_OF_ROOT_DIR(fat_fd) && + (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16))) + bts2rd = fat_fd->fat_file_size; + else + bts2rd = fs_info->fat.vol.bpc; + + entries_per_block = bts2rd / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE; + +#if MSDOS_FIND_PRINT + printf ("MSFS:[1] nt:%d, cn:%i ebp:%li bts2rd:%li lfne:%d nl:%i n:%s\n", + name_type, create_node, entries_per_block, bts2rd, + lfn_entries, name_len, name); +#endif + /* + * Scan the directory seeing if the file is present. While + * doing this see if a suitable location can be found to + * create the entry if the name is not found. + */ + while ((ret = fat_file_read(mt_entry, fat_fd, (dir_offset * bts2rd), + bts2rd, fs_info->cl_buf)) != FAT_EOF) + { + bool remainder_empty = false; +#if MSDOS_FIND_PRINT + printf ("MSFS:[2] dir_offset:%li\n", dir_offset); +#endif + + if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + rtems_set_errno_and_return_minus_one(EIO); + + assert(ret == bts2rd); + + /* have to look at the DIR_NAME as "raw" 8-bit data */ + for (dir_entry = 0; + dir_entry < bts2rd; + dir_entry += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + char* entry = (char*) fs_info->cl_buf + dir_entry; + + /* + * See if the entry is empty or the remainder of the directory is + * empty ? Localise to make the code read better. + */ + bool entry_empty = (*MSDOS_DIR_ENTRY_TYPE(entry) == + MSDOS_THIS_DIR_ENTRY_EMPTY); + remainder_empty = (*MSDOS_DIR_ENTRY_TYPE(entry) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY); +#if MSDOS_FIND_PRINT + printf ("MSFS:[3] re:%i ee:%i do:%li de:%li(%ld)\n", + remainder_empty, entry_empty, dir_offset, + dir_entry, (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)); +#endif + /* + * Remember where the we are, ie the start, so we can come back + * to here and write the long file name if this is the start of + * a series of empty entries. If empty_space_count is 0 then + * we are currently not inside an empty series of entries. It + * is a count of empty entries. + */ + if (empty_space_count == 0) + { + empty_space_entry = dir_entry; + empty_space_offset = dir_offset; + } + + if (remainder_empty) + { +#if MSDOS_FIND_PRINT + printf ("MSFS:[3.1] cn:%i esf:%i\n", create_node, empty_space_found); +#endif + /* + * If just looking and there is no more entries in the + * directory - return name-not-found + */ + if (!create_node) + return MSDOS_NAME_NOT_FOUND_ERR; + + /* + * Lets go and write the directory entries. If we have not found + * any available space add the remaining number of entries to any that + * we may have already found that are just before this entry. If more + * are needed FAT_EOF is returned by the read and we extend the file. + */ + if (!empty_space_found) + { + empty_space_count += + entries_per_block - (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + empty_space_found = true; +#if MSDOS_FIND_PRINT + printf ("MSFS:[3.2] esf:%i esc%i\n", empty_space_found, empty_space_count); +#endif + } + break; + } + else if (entry_empty) + { + if (create_node) + { + /* + * Remainder is not empty so is this entry empty ? + */ + empty_space_count++; + + if (empty_space_count == (lfn_entries + 1)) + empty_space_found = true; + } +#if MSDOS_FIND_PRINT + printf ("MSFS:[4.1] esc:%li esf:%i\n", + empty_space_count, empty_space_found); +#endif + } + else + { + /* + * A valid entry so handle it. + * + * If empty space has not been found we need to start the + * count again. + */ + if (create_node && !empty_space_found) + { + empty_space_entry = 0; + empty_space_count = 0; + } + + /* + * Check the attribute to see if the entry is for a long + * file name. + */ + if ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) == + MSDOS_ATTR_LFN) + { + char* p; + int o; + int i; +#if MSDOS_FIND_PRINT + printf ("MSFS:[4.2] lfn:%c entry:%i checksum:%i\n", + lfn_start.cln == FAT_FILE_SHORT_NAME ? 'f' : 't', + *MSDOS_DIR_ENTRY_TYPE(entry) & MSDOS_LAST_LONG_ENTRY_MASK, + *MSDOS_DIR_LFN_CHECKSUM(entry)); +#endif + /* + * If we are not already processing a LFN see if this is + * the first entry of a LFN ? + */ + if (lfn_start.cln == FAT_FILE_SHORT_NAME) + { + lfn_matched = false; + + /* + * The first entry must have the last long entry + * flag set. + */ + if ((*MSDOS_DIR_ENTRY_TYPE(entry) & + MSDOS_LAST_LONG_ENTRY) == 0) + continue; + + /* + * Does the number of entries in the LFN directory + * entry match the number we expect for this + * file name. Note we do not know the number of + * characters in the entry so this is check further + * on when the characters are checked. + */ + if (lfn_entries != (*MSDOS_DIR_ENTRY_TYPE(entry) & + MSDOS_LAST_LONG_ENTRY_MASK)) + continue; + + /* + * Get the checksum of the short entry. + */ + lfn_start.cln = dir_offset; + lfn_start.ofs = dir_entry; + lfn_entry = lfn_entries; + lfn_checksum = *MSDOS_DIR_LFN_CHECKSUM(entry); + +#if MSDOS_FIND_PRINT + printf ("MSFS:[4.3] lfn_checksum:%i\n", + *MSDOS_DIR_LFN_CHECKSUM(entry)); +#endif + } + + /* + * If the entry number or the check sum do not match + * forget this series of long directory entries. These + * could be orphaned entries depending on the history + * of the disk. + */ + if ((lfn_entry != (*MSDOS_DIR_ENTRY_TYPE(entry) & + MSDOS_LAST_LONG_ENTRY_MASK)) || + (lfn_checksum != *MSDOS_DIR_LFN_CHECKSUM(entry))) + { +#if MSDOS_FIND_PRINT + printf ("MSFS:[4.4] no match\n"); +#endif + lfn_start.cln = FAT_FILE_SHORT_NAME; + continue; + } + + lfn_entry--; + o = lfn_entry * MSDOS_LFN_LEN_PER_ENTRY; + p = entry + 1; + +#if MSDOS_FIND_PRINT + printf ("MSFS:[5] lfne:%i\n", lfn_entry); +#endif + for (i = 0; i < MSDOS_LFN_LEN_PER_ENTRY; i++) + { +#if MSDOS_FIND_PRINT > 1 + printf ("MSFS:[6] o:%i i:%i *p:%c(%02x) name[o + i]:%c(%02x)\n", + o, i, *p, *p, name[o + i], name[o + i]); +#endif + if (*p == '\0') + { + /* + * If this is the first entry, ie the last part of the + * long file name and the length does not match then + * the file names do not match. + */ + if (((lfn_entry + 1) == lfn_entries) && + ((o + i) != name_len)) + lfn_start.cln = FAT_FILE_SHORT_NAME; + break; + } + + if (((o + i) >= name_len) || (*p != name[o + i])) + { + lfn_start.cln = FAT_FILE_SHORT_NAME; + break; + } + + switch (i) + { + case 4: + p += 5; + break; + case 10: + p += 4; + break; + default: + p += 2; + break; + } + } + + lfn_matched = ((lfn_entry == 0) && + (lfn_start.cln != FAT_FILE_SHORT_NAME)); + +#if MSDOS_FIND_PRINT + printf ("MSFS:[8.1] lfn_matched:%i\n", lfn_matched); +#endif + } + else + { +#if MSDOS_FIND_PRINT + printf ("MSFS:[9.1] SFN entry, lfn_matched:%i\n", lfn_matched); +#endif + /* + * SFN entry found. + * + * If a LFN has been found and it matched check the + * entries have all been found and the checksum is + * correct. If this is the case return the short file + * name entry. + */ + if (lfn_matched) + { + uint8_t cs = 0; + uint8_t* p = (uint8_t*) MSDOS_DIR_NAME(entry); + int i; + + for (i = 0; i < MSDOS_SHORT_NAME_LEN; i++, p++) + cs = ((cs & 1) ? 0x80 : 0) + (cs >> 1) + *p; + + if (lfn_entry || (lfn_checksum != cs)) + lfn_matched = false; +#if MSDOS_FIND_PRINT + printf ("MSFS:[9.2] checksum, lfn_matched:%i, lfn_entry:%i, lfn_checksum:%02x/%02x\n", + lfn_matched, lfn_entry, lfn_checksum, cs); +#endif + } + + /* + * If the long file names matched or the file name is + * short and they match then we have the entry. We will not + * match a long file name against a short file name because + * a long file name that generates a matching short file + * name is not a long file name. + */ + if (lfn_matched || + ((name_type == MSDOS_NAME_SHORT) && + (lfn_start.cln == FAT_FILE_SHORT_NAME) && + (memcmp(MSDOS_DIR_NAME(entry), + MSDOS_DIR_NAME(name_dir_entry), + MSDOS_SHORT_NAME_LEN) == 0))) + { +#if MSDOS_FIND_PRINT + printf ("MSFS:[9.3] SNF found\n"); +#endif + /* + * We get the entry we looked for - fill the position + * structure and the 32 bytes of the short entry + */ + int rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, + dir_offset * bts2rd, + &dir_pos->sname.cln); + if (rc != RC_OK) + return rc; + + dir_pos->sname.ofs = dir_entry; + + if (lfn_start.cln != FAT_FILE_SHORT_NAME) + { + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, + lfn_start.cln * bts2rd, + &lfn_start.cln); + if (rc != RC_OK) + return rc; + } + + dir_pos->lname.cln = lfn_start.cln; + dir_pos->lname.ofs = lfn_start.ofs; + + memcpy(name_dir_entry, entry, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + return RC_OK; + } + + lfn_start.cln = FAT_FILE_SHORT_NAME; + lfn_matched = false; + } + } + } + + if (remainder_empty) + break; + + dir_offset++; + } + + /* + * If we are not to create the entry return a not found error. + */ + if (!create_node) + return MSDOS_NAME_NOT_FOUND_ERR; + +#if MSDOS_FIND_PRINT + printf ("MSFS:[8.1] WRITE do:%ld esc:%ld eso:%ld ese:%ld\n", + dir_offset, empty_space_count, empty_space_offset, empty_space_entry); +#endif + + /* + * If a long file name calculate the checksum of the short file name + * data to place in each long file name entry. First set the short + * file name to the slot of the SFN entry. This will mean no clashes + * in this directory. + */ + lfn_checksum = 0; + if (name_type == MSDOS_NAME_LONG) + { + int slot = (((empty_space_offset * bts2rd) + empty_space_entry) / + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + lfn_entries + 1; + msdos_short_name_hex(MSDOS_DIR_NAME(name_dir_entry), slot); + } + + if (lfn_entries) + { + uint8_t* p = (uint8_t*) MSDOS_DIR_NAME(name_dir_entry); + int i; + for (i = 0; i < 11; i++, p++) + lfn_checksum = + ((lfn_checksum & 1) ? 0x80 : 0) + (lfn_checksum >> 1) + *p; + } + + /* + * If there is no space available then extend the file. The + * empty_space_count is a count of empty entries in the currently + * read cluster so if 0 there is no space. Note, dir_offset will + * be at the next cluster so we can just make empty_space_offset + * that value. + */ + if (empty_space_count == 0) + { + read_cluster = true; + empty_space_offset = dir_offset; + empty_space_entry = 0; + } + + /* + * Have we read past the empty block ? If so go back and read it again. + */ + if (dir_offset != empty_space_offset) + read_cluster = true; + + /* + * Handle the entry writes. + */ + lfn_start.cln = lfn_start.ofs = FAT_FILE_SHORT_NAME; + lfn_entry = 0; + +#if MSDOS_FIND_PRINT + printf ("MSFS:[9] read_cluster:%d eso:%ld ese:%ld\n", + read_cluster, empty_space_offset, empty_space_entry); +#endif + + /* + * The one more is the short entry. + */ + while (lfn_entry < (lfn_entries + 1)) + { + int length = 0; + + if (read_cluster) + { + uint32_t new_length; +#if MSDOS_FIND_PRINT + printf ("MSFS:[9.1] eso:%li\n", empty_space_offset); +#endif + ret = fat_file_read(mt_entry, fat_fd, + (empty_space_offset * bts2rd), bts2rd, + fs_info->cl_buf); + + if (ret != bts2rd) + { + if (ret != FAT_EOF) + rtems_set_errno_and_return_minus_one(EIO); + +#if MSDOS_FIND_PRINT + printf ("MSFS:[9.2] extending file:%li\n", empty_space_offset); +#endif + ret = fat_file_extend (mt_entry, fat_fd, empty_space_offset * bts2rd, + &new_length); + + if (ret != RC_OK) + return ret; + +#if MSDOS_FIND_PRINT + printf ("MSFS:[9.3] extended: %d <-> %d\n", new_length, empty_space_offset * bts2rd); +#endif + if (new_length != (empty_space_offset * bts2rd)) + rtems_set_errno_and_return_minus_one(EIO); + + memset(fs_info->cl_buf, 0, bts2rd); + + ret = fat_file_write(mt_entry, fat_fd, + empty_space_offset * bts2rd, + bts2rd, fs_info->cl_buf); +#if MSDOS_FIND_PRINT + printf ("MSFS:[9.4] clear write: %d\n", ret); +#endif + if (ret == -1) + return ret; + else if (ret != bts2rd) + rtems_set_errno_and_return_minus_one(EIO); + } + } + +#if MSDOS_FIND_PRINT + printf ("MSFS:[10] eso:%li\n", empty_space_offset); +#endif + + for (dir_entry = empty_space_entry; + dir_entry < bts2rd; + dir_entry += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + char* entry = (char*) fs_info->cl_buf + dir_entry; + char* p; + const char* n; + int i; + char fill = 0; + + length += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE; + lfn_entry++; + +#if MSDOS_FIND_PRINT + printf ("MSFS:[10] de:%li(%li) length:%i lfn_entry:%i\n", + dir_entry, (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE), + length, lfn_entry); +#endif + /* + * Time to write the short file name entry. + */ + if (lfn_entry == (lfn_entries + 1)) + { + /* get current cluster number */ + int rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, + empty_space_offset * bts2rd, + &dir_pos->sname.cln); + if (rc != RC_OK) + return rc; + + dir_pos->sname.ofs = dir_entry; + + if (lfn_start.cln != FAT_FILE_SHORT_NAME) + { + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, + lfn_start.cln * bts2rd, + &lfn_start.cln); + if (rc != RC_OK) + return rc; + } + + dir_pos->lname.cln = lfn_start.cln; + dir_pos->lname.ofs = lfn_start.ofs; + + /* write new node entry */ + memcpy (entry, (uint8_t *) name_dir_entry, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + break; + } + + /* + * This is a long file name and we need to write + * a long file name entry. See if this is the + * first entry written and if so remember the + * the location of the long file name. + */ + if (lfn_start.cln == FAT_FILE_SHORT_NAME) + { + lfn_start.cln = empty_space_offset; + lfn_start.ofs = dir_entry; + } + + /* + * Clear the entry before loading the data. + */ + memset (entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + + *MSDOS_DIR_LFN_CHECKSUM(entry) = lfn_checksum; + + p = entry + 1; + n = name + (lfn_entries - lfn_entry) * MSDOS_LFN_LEN_PER_ENTRY; + + for (i = 0; i < MSDOS_LFN_LEN_PER_ENTRY; i++) + { + if (*n != 0) + { + *p = *n; + n++; + } + else + { + p [0] = fill; + p [1] = fill; + fill = 0xff; + } + + switch (i) + { + case 4: + p += 5; + break; + case 10: + p += 4; + break; + default: + p += 2; + break; + } + } + + *MSDOS_DIR_ENTRY_TYPE(entry) = (lfn_entries - lfn_entry) + 1; + if (lfn_entry == 1) + *MSDOS_DIR_ENTRY_TYPE(entry) |= MSDOS_LAST_LONG_ENTRY; + *MSDOS_DIR_ATTR(entry) |= MSDOS_ATTR_LFN; + } + + ret = fat_file_write(mt_entry, fat_fd, + (empty_space_offset * bts2rd) + empty_space_entry, + length, fs_info->cl_buf + empty_space_entry); + if (ret == -1) + return ret; + else if (ret != length) + rtems_set_errno_and_return_minus_one(EIO); + + empty_space_offset++; + empty_space_entry = 0; + read_cluster = true; + } + + return 0; +} + +/* msdos_find_node_by_cluster_num_in_fat_file -- + * Find node with specified number of cluster in fat-file. + * + * Note, not updated in the LFN change because it is only used + * for . and .. entries and these are always short. + * + * PARAMETERS: + * mt_entry - mount table entry + * fat_fd - fat-file descriptor + * cl4find - number of cluster to find + * paux - identify a node location on the disk - + * cluster num and offset inside the cluster + * dir_entry - placeholder for found node + * + * RETURNS: + * RC_OK on success, or error code if error occured + * + */ +int msdos_find_node_by_cluster_num_in_fat_file( + rtems_filesystem_mount_table_entry_t *mt_entry, + fat_file_fd_t *fat_fd, + uint32_t cl4find, + fat_dir_pos_t *dir_pos, + char *dir_entry + ) +{ + int rc = RC_OK; + ssize_t ret = 0; + msdos_fs_info_t *fs_info = mt_entry->fs_info; + uint32_t bts2rd = 0; + uint32_t i = 0, j = 0; + + if (FAT_FD_OF_ROOT_DIR(fat_fd) && + (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16))) + bts2rd = fat_fd->fat_file_size; + else + bts2rd = fs_info->fat.vol.bpc; + + while ((ret = fat_file_read(mt_entry, fat_fd, j * bts2rd, bts2rd, + fs_info->cl_buf)) != FAT_EOF) + { + if ( ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE ) + rtems_set_errno_and_return_minus_one( EIO ); + + assert(ret == bts2rd); + + for (i = 0; i < bts2rd; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + { + char* entry = (char*) fs_info->cl_buf + i; + + /* if this and all rest entries are empty - return not-found */ + if ((*MSDOS_DIR_ENTRY_TYPE(entry)) == + MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY) + return MSDOS_NAME_NOT_FOUND_ERR; + + /* if this entry is empty - skip it */ + if ((*MSDOS_DIR_ENTRY_TYPE(entry)) == + MSDOS_THIS_DIR_ENTRY_EMPTY) + continue; + + /* if get a non-empty entry - compare clusters num */ + if (MSDOS_EXTRACT_CLUSTER_NUM(entry) == cl4find) + { + /* on success fill aux structure and copy all 32 bytes */ + rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, j * bts2rd, + &dir_pos->sname.cln); + if (rc != RC_OK) + return rc; + + dir_pos->sname.ofs = i; + dir_pos->lname.cln = FAT_FILE_SHORT_NAME; + dir_pos->lname.ofs = FAT_FILE_SHORT_NAME; + + memcpy(dir_entry, entry, + MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE); + return RC_OK; + } + } + j++; + } + return MSDOS_NAME_NOT_FOUND_ERR; +} diff --git a/cpukit/libfs/src/dosfs/msdos_mknod.c b/cpukit/libfs/src/dosfs/msdos_mknod.c new file mode 100644 index 0000000000..ad4ad50d00 --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_mknod.c @@ -0,0 +1,84 @@ +/* + * Routine for node creation in MSDOS filesystem. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <rtems.h> + +#include <rtems/libio_.h> + +#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: + * name - file name to create + * 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 *name, + 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; + + /* + * 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 + rtems_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) + rtems_set_errno_and_return_minus_one(EIO); + + /* Create an MSDOS node */ + rc = msdos_creat_node(pathloc, type, name, strlen(name), mode, NULL); + + 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..877cf60492 --- /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 <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <rtems.h> + +#include <rtems/libio_.h> + +#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/src/dosfs/msdos_rename.c b/cpukit/libfs/src/dosfs/msdos_rename.c new file mode 100644 index 0000000000..1f5a6d9c6a --- /dev/null +++ b/cpukit/libfs/src/dosfs/msdos_rename.c @@ -0,0 +1,92 @@ +/* + * Routine to rename a MSDOS filesystem node + * + * Copyright (C) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) $Id$ + * + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <rtems/libio_.h> +#include <time.h> + +#include "fat.h" +#include "fat_fat_operations.h" +#include "fat_file.h" + +#include "msdos.h" + +/* msdos_rename -- + * Rename the node by removing the exitsing directory entry and creating a + * new one. + * + * PARAMETERS: + * old_parent_loc - node description for the "old parent" node + * old_loc - node description for the "old" node + * new_parent_loc - node description for the "parent" node + * name - name of new node + * + * RETURNS: + * RC_OK on success, or -1 if error occured (errno set appropriately) + */ +int +msdos_rename(rtems_filesystem_location_info_t *old_parent_loc, + rtems_filesystem_location_info_t *old_loc, + rtems_filesystem_location_info_t *new_parent_loc, + const char *new_name) +{ + int rc = RC_OK; + rtems_status_code sc = RTEMS_SUCCESSFUL; + msdos_fs_info_t *fs_info = new_parent_loc->mt_entry->fs_info; + fat_file_fd_t *old_fat_fd = old_loc->node_access; + const char *token; + int len; + + /* + * check spelling and format new node name + */ + if (MSDOS_NAME != msdos_get_token(new_name, strlen(new_name), &token, &len)) { + rtems_set_errno_and_return_minus_one(ENAMETOOLONG); + } + /* + * lock volume + */ + sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT, + MSDOS_VOLUME_SEMAPHORE_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_set_errno_and_return_minus_one(EIO); + + /* + * create new directory entry as "hard link", copying relevant info from + * existing file + */ + rc = msdos_creat_node(new_parent_loc, + MSDOS_HARD_LINK,new_name,len,S_IFREG, + old_fat_fd); + if (rc != RC_OK) + { + rtems_semaphore_release(fs_info->vol_sema); + return rc; + } + + /* + * mark file removed + */ + rc = msdos_set_first_char4file_name(old_loc->mt_entry, + &old_fat_fd->dir_pos, + MSDOS_THIS_DIR_ENTRY_EMPTY); + + rtems_semaphore_release(fs_info->vol_sema); + return rc; +} diff --git a/cpukit/libfs/src/imfs/.cvsignore b/cpukit/libfs/src/imfs/.cvsignore new file mode 100644 index 0000000000..c62d89f265 --- /dev/null +++ b/cpukit/libfs/src/imfs/.cvsignore @@ -0,0 +1,5 @@ +config.h +config.h.in +stamp-h +stamp-h.in +stamp-h1.in diff --git a/cpukit/libfs/src/imfs/deviceerrno.c b/cpukit/libfs/src/imfs/deviceerrno.c new file mode 100644 index 0000000000..396df7619d --- /dev/null +++ b/cpukit/libfs/src/imfs/deviceerrno.c @@ -0,0 +1,73 @@ +/* + * IMFS Device Node Handlers + * + * This file contains the set of handlers used to map operations on + * IMFS device nodes onto calls to the RTEMS Classic API IO Manager. + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> + +#include <rtems.h> +#include <rtems/libio.h> + +static const int status_code_to_errno [RTEMS_STATUS_CODES_LAST + 1] = { + [RTEMS_SUCCESSFUL] = 0, + [RTEMS_TASK_EXITTED] = EIO, + [RTEMS_MP_NOT_CONFIGURED] = EIO, + [RTEMS_INVALID_NAME] = EINVAL, + [RTEMS_INVALID_ID] = EIO, + [RTEMS_TOO_MANY] = EIO, + [RTEMS_TIMEOUT] = ETIMEDOUT, + [RTEMS_OBJECT_WAS_DELETED] = EIO, + [RTEMS_INVALID_SIZE] = EIO, + [RTEMS_INVALID_ADDRESS] = EIO, + [RTEMS_INVALID_NUMBER] = EBADF, + [RTEMS_NOT_DEFINED] = EIO, + [RTEMS_RESOURCE_IN_USE] = EBUSY, + [RTEMS_UNSATISFIED] = ENODEV, + [RTEMS_INCORRECT_STATE] = EIO, + [RTEMS_ALREADY_SUSPENDED] = EIO, + [RTEMS_ILLEGAL_ON_SELF] = EIO, + [RTEMS_ILLEGAL_ON_REMOTE_OBJECT] = EIO, + [RTEMS_CALLED_FROM_ISR] = EIO, + [RTEMS_INVALID_PRIORITY] = EIO, + [RTEMS_INVALID_CLOCK] = EINVAL, + [RTEMS_INVALID_NODE] = EINVAL, + [RTEMS_NOT_CONFIGURED] = ENOSYS, + [RTEMS_NOT_OWNER_OF_RESOURCE] = EPERM, + [RTEMS_NOT_IMPLEMENTED] = ENOSYS, + [RTEMS_INTERNAL_ERROR] = EIO, + [RTEMS_NO_MEMORY] = ENOMEM, + [RTEMS_IO_ERROR] = EIO, + [RTEMS_PROXY_BLOCKING] = EIO +}; + +int rtems_deviceio_errno(rtems_status_code sc) +{ + if (sc == RTEMS_SUCCESSFUL) { + return 0; + } else { + int eno = EINVAL; + + if ((unsigned) sc <= RTEMS_STATUS_CODES_LAST) { + eno = status_code_to_errno [sc]; + } + + errno = eno; + + return -1; + } +} diff --git a/cpukit/libfs/src/imfs/deviceio.c b/cpukit/libfs/src/imfs/deviceio.c new file mode 100644 index 0000000000..ff6cf2a01e --- /dev/null +++ b/cpukit/libfs/src/imfs/deviceio.c @@ -0,0 +1,233 @@ +/* + * IMFS Device Node Handlers + * + * This file contains the set of handlers used to map operations on + * IMFS device nodes onto calls to the RTEMS Classic API IO Manager. + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/devfs.h> + +#include "imfs.h" + +/* + * device_open + * + * This handler maps an open() operation onto rtems_io_open(). + */ + +int device_open( + rtems_libio_t *iop, + const char *pathname, + uint32_t flag, + uint32_t mode +) +{ + rtems_libio_open_close_args_t args; + rtems_status_code status; + IMFS_jnode_t *the_jnode; + + the_jnode = iop->pathinfo.node_access; + + args.iop = iop; + args.flags = iop->flags; + args.mode = mode; + + status = rtems_io_open( + the_jnode->info.device.major, + the_jnode->info.device.minor, + (void *) &args + ); + + return rtems_deviceio_errno( status ); +} + +/* + * device_close + * + * This handler maps a close() operation onto rtems_io_close(). + */ + +int device_close( + rtems_libio_t *iop +) +{ + rtems_libio_open_close_args_t args; + rtems_status_code status; + IMFS_jnode_t *the_jnode; + + the_jnode = iop->pathinfo.node_access; + + args.iop = iop; + args.flags = 0; + args.mode = 0; + + status = rtems_io_close( + the_jnode->info.device.major, + the_jnode->info.device.minor, + (void *) &args + ); + + IMFS_check_node_remove( the_jnode ); + + return rtems_deviceio_errno( status ); +} + +/* + * device_read + * + * This handler maps a read() operation onto rtems_io_read(). + */ + +ssize_t device_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + rtems_libio_rw_args_t args; + rtems_status_code status; + IMFS_jnode_t *the_jnode; + + the_jnode = iop->pathinfo.node_access; + + args.iop = iop; + args.offset = iop->offset; + args.buffer = buffer; + args.count = count; + args.flags = iop->flags; + args.bytes_moved = 0; + + status = rtems_io_read( + the_jnode->info.device.major, + the_jnode->info.device.minor, + (void *) &args + ); + + if ( status ) + return rtems_deviceio_errno(status); + + return (ssize_t) args.bytes_moved; +} + +/* + * device_write + * + * This handler maps a write() operation onto rtems_io_write(). + */ + +ssize_t device_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ + rtems_libio_rw_args_t args; + rtems_status_code status; + IMFS_jnode_t *the_jnode; + + the_jnode = iop->pathinfo.node_access; + + args.iop = iop; + args.offset = iop->offset; + args.buffer = (void *) buffer; + args.count = count; + args.flags = iop->flags; + args.bytes_moved = 0; + + status = rtems_io_write( + the_jnode->info.device.major, + the_jnode->info.device.minor, + (void *) &args + ); + + if ( status ) + return rtems_deviceio_errno(status); + + return (ssize_t) args.bytes_moved; +} + +/* + * device_ioctl + * + * This handler maps an ioctl() operation onto rtems_io_ioctl(). + */ + +int device_ioctl( + rtems_libio_t *iop, + uint32_t command, + void *buffer +) +{ + rtems_libio_ioctl_args_t args; + rtems_status_code status; + IMFS_jnode_t *the_jnode; + + args.iop = iop; + args.command = command; + args.buffer = buffer; + + the_jnode = iop->pathinfo.node_access; + + status = rtems_io_control( + the_jnode->info.device.major, + the_jnode->info.device.minor, + (void *) &args + ); + + if ( status ) + return rtems_deviceio_errno(status); + + return args.ioctl_return; +} + +/* + * device_lseek + * + * This handler eats all lseek() operations and does not create + * an error. It assumes all devices can handle the seek. The + * writes fail. + */ + +rtems_off64_t device_lseek( + rtems_libio_t *iop, + rtems_off64_t offset, + int whence +) +{ + return offset; +} + +/* + * device_stat + * + * The IMFS_stat() is used. + */ + +/* + * device_rmnod + * + * The IMFS_rmnod() is used. + */ + +int device_ftruncate( + rtems_libio_t *iop, + rtems_off64_t length +) +{ + return 0; +} diff --git a/cpukit/libfs/src/imfs/fifoimfs_init.c b/cpukit/libfs/src/imfs/fifoimfs_init.c new file mode 100644 index 0000000000..13dc373eec --- /dev/null +++ b/cpukit/libfs/src/imfs/fifoimfs_init.c @@ -0,0 +1,61 @@ +/** + * @file + * + * @ingroup LibFSIMFS + * + * @brief IMFS without fifo support initialization. + */ + +/* + * Copyright (c) 2010 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imfs.h" + +const rtems_filesystem_operations_table fifoIMFS_ops = { + .evalpath_h = IMFS_eval_path, + .evalformake_h = IMFS_evaluate_for_make, + .link_h = IMFS_link, + .unlink_h = IMFS_unlink, + .node_type_h = IMFS_node_type, + .mknod_h = IMFS_mknod, + .chown_h = IMFS_chown, + .freenod_h = rtems_filesystem_default_freenode, + .mount_h = IMFS_mount, + .fsmount_me_h = fifoIMFS_initialize, + .unmount_h = IMFS_unmount, + .fsunmount_me_h = IMFS_fsunmount, + .utime_h = IMFS_utime, + .eval_link_h = IMFS_evaluate_link, + .symlink_h = IMFS_symlink, + .readlink_h = IMFS_readlink, + .rename_h = IMFS_rename, + .statvfs_h = rtems_filesystem_default_statvfs +}; + +int fifoIMFS_initialize( + rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data +) +{ + return IMFS_initialize_support( + mt_entry, + &fifoIMFS_ops, + &IMFS_memfile_handlers, + &IMFS_directory_handlers, + &IMFS_fifo_handlers + ); +} diff --git a/cpukit/libfs/src/imfs/imfs.h b/cpukit/libfs/src/imfs/imfs.h new file mode 100644 index 0000000000..aec8febdc5 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs.h @@ -0,0 +1,574 @@ +/* + * Header file for the In-Memory File System + * + * COPYRIGHT (c) 1989-2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef _RTEMS_IMFS_H +#define _RTEMS_IMFS_H + +#include <rtems.h> +#include <rtems/chain.h> + +#include <sys/types.h> +#include <limits.h> +#include <rtems/libio.h> + +#include <rtems/pipe.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * File name macros + */ + +#define IMFS_is_valid_name_char( _ch ) ( 1 ) + +#define IMFS_is_separator( _ch ) \ + rtems_filesystem_is_separator( _ch ) + +/* + * Data types + */ + +struct IMFS_jnode_tt; +typedef struct IMFS_jnode_tt IMFS_jnode_t; + +typedef struct { + rtems_chain_control Entries; + rtems_filesystem_mount_table_entry_t *mt_fs; +} IMFS_directory_t; + +typedef struct { + rtems_device_major_number major; + rtems_device_minor_number minor; +} IMFS_device_t; + +typedef struct { + IMFS_jnode_t *link_node; +} IMFS_link_t; + +typedef struct { + char *name; +} IMFS_sym_link_t; + +typedef struct { + pipe_control_t *pipe; +} IMFS_fifo_t; + +/* + * IMFS "memfile" information + * + * The data structure for the in-memory "memfiles" is based on classic UNIX. + * + * block_ptr is a pointer to a block of IMFS_MEMFILE_BYTES_PER_BLOCK in + * length which could be data or a table of pointers to blocks. + * + * Setting IMFS_MEMFILE_BYTES_PER_BLOCK to different values has a significant + * impact on the maximum file size supported as well as the amount of + * memory wasted due to internal file fragmentation. The following + * is a list of maximum file sizes based on various settings + * + * max_filesize with blocks of 16 is 1,328 + * max_filesize with blocks of 32 is 18,656 + * max_filesize with blocks of 64 is 279,488 + * max_filesize with blocks of 128 is 4,329,344 + * max_filesize with blocks of 256 is 68,173,568 + * max_filesize with blocks of 512 is 1,082,195,456 + */ + +#define IMFS_MEMFILE_DEFAULT_BYTES_PER_BLOCK 128 + extern int imfs_rq_memfile_bytes_per_block; + extern int imfs_memfile_bytes_per_block; + +#define IMFS_MEMFILE_BYTES_PER_BLOCK imfs_memfile_bytes_per_block +#define IMFS_MEMFILE_BLOCK_SLOTS \ + (IMFS_MEMFILE_BYTES_PER_BLOCK / sizeof(void *)) + +typedef uint8_t *block_p; +typedef block_p *block_ptr; + +typedef struct { + rtems_off64_t size; /* size of file in bytes */ + block_ptr indirect; /* array of 128 data blocks pointers */ + block_ptr doubly_indirect; /* 128 indirect blocks */ + block_ptr triply_indirect; /* 128 doubly indirect blocks */ +} IMFS_memfile_t; + +typedef struct { + rtems_off64_t size; /* size of file in bytes */ + block_p direct; /* pointer to file image */ +} IMFS_linearfile_t; + +/* + * Important block numbers for "memfiles" + */ + +#define FIRST_INDIRECT (0) +#define LAST_INDIRECT (IMFS_MEMFILE_BLOCK_SLOTS - 1) + +#define FIRST_DOUBLY_INDIRECT (LAST_INDIRECT + 1) +#define LAST_DOUBLY_INDIRECT \ + (LAST_INDIRECT + \ + (IMFS_MEMFILE_BLOCK_SLOTS * IMFS_MEMFILE_BLOCK_SLOTS)) + +#define FIRST_TRIPLY_INDIRECT (LAST_DOUBLY_INDIRECT + 1) +#define LAST_TRIPLY_INDIRECT \ + (LAST_DOUBLY_INDIRECT +\ + (IMFS_MEMFILE_BLOCK_SLOTS * \ + IMFS_MEMFILE_BLOCK_SLOTS * IMFS_MEMFILE_BLOCK_SLOTS)) + +#define IMFS_MEMFILE_MAXIMUM_SIZE \ + (LAST_TRIPLY_INDIRECT * IMFS_MEMFILE_BYTES_PER_BLOCK) + +/* + * What types of IMFS file systems entities there can be. + */ + +typedef enum { + IMFS_DIRECTORY = RTEMS_FILESYSTEM_DIRECTORY, + IMFS_DEVICE = RTEMS_FILESYSTEM_DEVICE, + IMFS_HARD_LINK = RTEMS_FILESYSTEM_HARD_LINK, + IMFS_SYM_LINK = RTEMS_FILESYSTEM_SYM_LINK, + IMFS_MEMORY_FILE = RTEMS_FILESYSTEM_MEMORY_FILE, + IMFS_LINEAR_FILE, + IMFS_FIFO +} IMFS_jnode_types_t; + +typedef union { + IMFS_directory_t directory; + IMFS_device_t device; + IMFS_link_t hard_link; + IMFS_sym_link_t sym_link; + IMFS_memfile_t file; + IMFS_linearfile_t linearfile; + IMFS_fifo_t fifo; +} IMFS_types_union; + +/* + * Major device number for the IMFS. This is not a real device number because + * the IMFS is just a file system and does not have a driver. + */ +#define IMFS_DEVICE_MAJOR_NUMBER (0xfffe) + +/* + * Maximum length of a "basename" of an IMFS file/node. + */ + +#define IMFS_NAME_MAX 32 + +/* + * The control structure for an IMFS jnode. + */ + +struct IMFS_jnode_tt { + rtems_chain_node Node; /* for chaining them together */ + IMFS_jnode_t *Parent; /* Parent node */ + char name[IMFS_NAME_MAX+1]; /* "basename" */ + mode_t st_mode; /* File mode */ + nlink_t st_nlink; /* Link count */ + ino_t st_ino; /* inode */ + + uid_t st_uid; /* User ID of owner */ + gid_t st_gid; /* Group ID of owner */ + + time_t stat_atime; /* Time of last access */ + time_t stat_mtime; /* Time of last modification */ + time_t stat_ctime; /* Time of last status change */ + IMFS_jnode_types_t type; /* Type of this entry */ + IMFS_types_union info; +}; + +#define IMFS_update_atime( _jnode ) \ + do { \ + struct timeval tv; \ + gettimeofday( &tv, 0 ); \ + _jnode->stat_atime = (time_t) tv.tv_sec; \ + } while (0) + +#define IMFS_update_mtime( _jnode ) \ + do { \ + struct timeval tv; \ + gettimeofday( &tv, 0 ); \ + _jnode->stat_mtime = (time_t) tv.tv_sec; \ + } while (0) + +#define IMFS_update_ctime( _jnode ) \ + do { \ + struct timeval tv; \ + gettimeofday( &tv, 0 ); \ + _jnode->stat_ctime = (time_t) tv.tv_sec; \ + } while (0) + +#define IMFS_mtime_ctime_update( _jnode ) \ + do { \ + struct timeval tv; \ + gettimeofday( &tv, 0 ); \ + _jnode->stat_mtime = (time_t) tv.tv_sec; \ + _jnode->stat_ctime = (time_t) tv.tv_sec; \ + } while (0) + +typedef struct { + int instance; + ino_t ino_count; + const rtems_filesystem_file_handlers_r *memfile_handlers; + const rtems_filesystem_file_handlers_r *directory_handlers; + const rtems_filesystem_file_handlers_r *fifo_handlers; +} IMFS_fs_info_t; + +/* + * Type defination for tokens returned from IMFS_get_token + */ + +typedef enum { + IMFS_NO_MORE_PATH, + IMFS_CURRENT_DIR, + IMFS_UP_DIR, + IMFS_NAME, + IMFS_INVALID_TOKEN +} IMFS_token_types; + +/* + * Shared Data + */ + +extern const rtems_filesystem_file_handlers_r IMFS_directory_handlers; +extern const rtems_filesystem_file_handlers_r IMFS_device_handlers; +extern const rtems_filesystem_file_handlers_r IMFS_link_handlers; +extern const rtems_filesystem_file_handlers_r IMFS_memfile_handlers; +extern const rtems_filesystem_file_handlers_r IMFS_fifo_handlers; +extern const rtems_filesystem_operations_table IMFS_ops; +extern const rtems_filesystem_operations_table fifoIMFS_ops; +extern const rtems_filesystem_limits_and_options_t IMFS_LIMITS_AND_OPTIONS; + +/* + * Routines + */ + +extern int IMFS_initialize( + rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data +); + +extern int fifoIMFS_initialize( + rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data +); + +extern int miniIMFS_initialize( + rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data +); + +extern int IMFS_initialize_support( + rtems_filesystem_mount_table_entry_t *mt_entry, + const rtems_filesystem_operations_table *op_table, + const rtems_filesystem_file_handlers_r *memfile_handlers, + const rtems_filesystem_file_handlers_r *directory_handlers, + const rtems_filesystem_file_handlers_r *fifo_handlers +); + +extern int IMFS_fsunmount( + rtems_filesystem_mount_table_entry_t *mt_entry +); + +extern int rtems_tarfs_load( + char *mountpoint, + uint8_t *tar_image, + size_t tar_size +); + +/* + * Returns the number of characters copied from path to token. + */ +extern IMFS_token_types IMFS_get_token( + const char *path, + int pathlen, + char *token, + int *token_len +); + +extern void IMFS_dump( void ); + +extern void IMFS_initialize_jnode( + IMFS_jnode_t *the_jnode, + IMFS_jnode_types_t type, + IMFS_jnode_t *the_parent, + char *name, + mode_t mode +); + +extern IMFS_jnode_t *IMFS_find_match_in_dir( + IMFS_jnode_t *directory, /* IN */ + char *name /* IN */ +); + +extern rtems_filesystem_node_types_t IMFS_node_type( + rtems_filesystem_location_info_t *pathloc /* IN */ +); + +extern int IMFS_stat( + rtems_filesystem_location_info_t *loc, /* IN */ + struct stat *buf /* OUT */ +); + +extern int IMFS_Set_handlers( + rtems_filesystem_location_info_t *loc +); + +extern int IMFS_evaluate_link( + rtems_filesystem_location_info_t *node, /* IN/OUT */ + int flags /* IN */ +); + +extern int IMFS_eval_path( + const char *pathname, /* IN */ + size_t pathnamelen, /* IN */ + int flags, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */ +); + +extern int IMFS_link( + rtems_filesystem_location_info_t *to_loc, /* IN */ + rtems_filesystem_location_info_t *parent_loc, /* IN */ + const char *token /* IN */ +); + +extern int IMFS_unlink( + rtems_filesystem_location_info_t *parent_pathloc, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN */ +); + +extern int IMFS_chown( + rtems_filesystem_location_info_t *pathloc, /* IN */ + uid_t owner, /* IN */ + gid_t group /* IN */ +); + +extern int IMFS_mknod( + const char *path, /* IN */ + mode_t mode, /* IN */ + dev_t dev, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */ +); + +extern IMFS_jnode_t *IMFS_allocate_node( + IMFS_jnode_types_t type, /* IN */ + const char *name, /* IN */ + mode_t mode /* IN */ +); + +extern IMFS_jnode_t *IMFS_create_root_node(void); + +extern IMFS_jnode_t *IMFS_create_node( + rtems_filesystem_location_info_t *parent_loc, /* IN */ + IMFS_jnode_types_t type, /* IN */ + const char *name, /* IN */ + mode_t mode, /* IN */ + const IMFS_types_union *info /* IN */ +); + +extern int IMFS_evaluate_for_make( + const char *path, /* IN */ + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + const char **name /* OUT */ +); + +extern int IMFS_mount( + rtems_filesystem_mount_table_entry_t *mt_entry /* IN */ +); + +extern int IMFS_unmount( + rtems_filesystem_mount_table_entry_t *mt_entry /* IN */ +); + +extern int IMFS_memfile_remove( + IMFS_jnode_t *the_jnode /* IN/OUT */ +); + +extern int memfile_ftruncate( + rtems_libio_t *iop, /* IN */ + rtems_off64_t length /* IN */ +); + +extern int imfs_dir_open( + rtems_libio_t *iop, /* IN */ + const char *pathname, /* IN */ + uint32_t flag, /* IN */ + uint32_t mode /* IN */ +); + +extern int imfs_dir_close( + rtems_libio_t *iop /* IN */ +); + +extern ssize_t imfs_dir_read( + rtems_libio_t *iop, /* IN */ + void *buffer, /* IN */ + size_t count /* IN */ +); + +extern rtems_off64_t imfs_dir_lseek( + rtems_libio_t *iop, /* IN */ + rtems_off64_t offset, /* IN */ + int whence /* IN */ +); + +extern int imfs_dir_fstat( + rtems_filesystem_location_info_t *loc, /* IN */ + struct stat *buf /* OUT */ +); + +extern int imfs_dir_rmnod( + rtems_filesystem_location_info_t *parent_pathloc, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN */ +); + +extern int memfile_open( + rtems_libio_t *iop, /* IN */ + const char *pathname, /* IN */ + uint32_t flag, /* IN */ + uint32_t mode /* IN */ +); + +extern int memfile_close( + rtems_libio_t *iop /* IN */ +); + +extern ssize_t memfile_read( + rtems_libio_t *iop, /* IN */ + void *buffer, /* IN */ + size_t count /* IN */ +); + +extern ssize_t memfile_write( + rtems_libio_t *iop, /* IN */ + const void *buffer, /* IN */ + size_t count /* IN */ +); + +extern int memfile_ioctl( + rtems_libio_t *iop, /* IN */ + uint32_t command, /* IN */ + void *buffer /* IN */ +); + +extern rtems_off64_t memfile_lseek( + rtems_libio_t *iop, /* IN */ + rtems_off64_t offset, /* IN */ + int whence /* IN */ +); + +extern int device_open( + rtems_libio_t *iop, /* IN */ + const char *pathname, /* IN */ + uint32_t flag, /* IN */ + uint32_t mode /* IN */ +); + +extern int device_close( + rtems_libio_t *iop /* IN */ +); + +extern ssize_t device_read( + rtems_libio_t *iop, /* IN */ + void *buffer, /* IN */ + size_t count /* IN */ +); + +extern ssize_t device_write( + rtems_libio_t *iop, /* IN */ + const void *buffer, /* IN */ + size_t count /* IN */ +); + +extern int device_ioctl( + rtems_libio_t *iop, /* IN */ + uint32_t command, /* IN */ + void *buffer /* IN */ +); + +extern rtems_off64_t device_lseek( + rtems_libio_t *iop, /* IN */ + rtems_off64_t offset, /* IN */ + int whence /* IN */ +); + +extern int device_ftruncate( + rtems_libio_t *iop, /* IN */ + rtems_off64_t length /* IN */ +); + +extern int IMFS_utime( + rtems_filesystem_location_info_t *pathloc, /* IN */ + time_t actime, /* IN */ + time_t modtime /* IN */ +); + +extern int IMFS_fchmod( + rtems_filesystem_location_info_t *loc, + mode_t mode +); + +extern int IMFS_symlink( + rtems_filesystem_location_info_t *parent_loc, /* IN */ + const char *link_name, + const char *node_name +); + +extern ssize_t IMFS_readlink( + rtems_filesystem_location_info_t *loc, /* IN */ + char *buf, /* OUT */ + size_t bufsize +); + +extern int IMFS_rename( + rtems_filesystem_location_info_t *old_loc, /* IN */ + rtems_filesystem_location_info_t *old_parent_loc, /* IN */ + rtems_filesystem_location_info_t *new_parent_loc, /* IN */ + const char *new_name /* IN */ +); + +extern int IMFS_fdatasync( + rtems_libio_t *iop +); + +extern void IMFS_create_orphan( + IMFS_jnode_t *jnode +); + +extern void IMFS_check_node_remove( + IMFS_jnode_t *jnode +); + +extern int IMFS_rmnod( + rtems_filesystem_location_info_t *parent_pathloc, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN */ +); + +/* + * Turn on IMFS assertions when RTEMS_DEBUG is defined. + */ +#ifdef RTEMS_DEBUG + #include <assert.h> + + #define IMFS_assert(_x) assert(_x) +#else + #define IMFS_assert(_x) +#endif + +#ifdef __cplusplus +} +#endif + +#endif +/* end of include file */ diff --git a/cpukit/libfs/src/imfs/imfs_chown.c b/cpukit/libfs/src/imfs/imfs_chown.c new file mode 100644 index 0000000000..7f0c7b5688 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_chown.c @@ -0,0 +1,56 @@ +/* + * IMFS_chown + * + * This routine is the implementation of the chown() system + * call for the IMFS. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> +#include "imfs.h" + +int IMFS_chown( + rtems_filesystem_location_info_t *pathloc, /* IN */ + uid_t owner, /* IN */ + gid_t group /* IN */ +) +{ + IMFS_jnode_t *jnode; +#if defined(RTEMS_POSIX_API) + uid_t st_uid; +#endif + + jnode = (IMFS_jnode_t *) pathloc->node_access; + + /* + * Verify I am the owner of the node or the super user. + */ + +#if defined(RTEMS_POSIX_API) + st_uid = geteuid(); + + if ( ( st_uid != jnode->st_uid ) && ( st_uid != 0 ) ) + rtems_set_errno_and_return_minus_one( EPERM ); +#endif + + jnode->st_uid = owner; + jnode->st_gid = group; + + IMFS_update_ctime( jnode ); + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_config.c b/cpukit/libfs/src/imfs/imfs_config.c new file mode 100644 index 0000000000..78f947e81f --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_config.c @@ -0,0 +1,35 @@ +/* + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/libio_.h> +#include "imfs.h" + +/* XXX this structure should use real constants */ + +const rtems_filesystem_limits_and_options_t IMFS_LIMITS_AND_OPTIONS = { + 5, /* link_max */ + 6, /* max_canon */ + 7, /* max_input */ + IMFS_NAME_MAX, /* name_max */ + 255, /* path_max */ + 2, /* pipe_buf */ + 1, /* posix_async_io */ + 2, /* posix_chown_restrictions */ + 3, /* posix_no_trunc */ + 4, /* posix_prio_io */ + 5, /* posix_sync_io */ + 6 /* posix_vdisable */ +}; diff --git a/cpukit/libfs/src/imfs/imfs_creat.c b/cpukit/libfs/src/imfs/imfs_creat.c new file mode 100644 index 0000000000..3b602d0c14 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_creat.c @@ -0,0 +1,170 @@ +/* + * IMFS_create_node() + * + * Routine to create a new in memory file system node. + * + * COPYRIGHT (c) 1989-2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include "imfs.h" +#include <rtems/libio_.h> + +/* + * Create an IMFS filesystem node of an arbitrary type that is NOT + * the root directory node. + */ +IMFS_jnode_t *IMFS_create_node( + rtems_filesystem_location_info_t *parent_loc, + IMFS_jnode_types_t type, + const char *name, + mode_t mode, + const IMFS_types_union *info +) +{ + IMFS_jnode_t *node; + IMFS_jnode_t *parent; + IMFS_fs_info_t *fs_info; + + /* + * MUST have a parent node to call this routine. + */ + if ( parent_loc == NULL ) + return NULL; + + parent = parent_loc->node_access; + fs_info = parent_loc->mt_entry->fs_info; + + /* + * Reject creation of FIFOs if support is disabled. + */ + if ( type == IMFS_FIFO && + fs_info->fifo_handlers == &rtems_filesystem_handlers_default ) + return NULL; + + /* + * Allocate filesystem node and fill in basic information + */ + node = IMFS_allocate_node( type, name, mode & ~rtems_filesystem_umask ); + if ( !node ) + return NULL; + + /* + * Set the type specific information + */ + if ( type == IMFS_DIRECTORY ) { + rtems_chain_initialize_empty(&node->info.directory.Entries); + } else if ( type == IMFS_HARD_LINK ) { + node->info.hard_link.link_node = info->hard_link.link_node; + } else if ( type == IMFS_SYM_LINK ) { + node->info.sym_link.name = info->sym_link.name; + } else if ( type == IMFS_DEVICE ) { + node->info.device.major = info->device.major; + node->info.device.minor = info->device.minor; + } else if ( type == IMFS_LINEAR_FILE ) { + node->info.linearfile.size = 0; + node->info.linearfile.direct = 0; + } else if ( type == IMFS_MEMORY_FILE ) { + node->info.file.size = 0; + node->info.file.indirect = 0; + node->info.file.doubly_indirect = 0; + node->info.file.triply_indirect = 0; + } else if ( type == IMFS_FIFO ) { + node->info.fifo.pipe = NULL; + } else { + IMFS_assert(0); + } + + /* + * This node MUST have a parent, so put it in that directory list. + */ + node->Parent = parent; + node->st_ino = ++fs_info->ino_count; + + rtems_chain_append( &parent->info.directory.Entries, &node->Node ); + + return node; +} + +/* + * Allocate filesystem node and fill in basic information + */ +IMFS_jnode_t *IMFS_allocate_node( + IMFS_jnode_types_t type, + const char *name, + mode_t mode +) +{ + IMFS_jnode_t *node; + struct timeval tv; + + /* + * Allocate an IMFS jnode + */ + node = calloc( 1, sizeof( IMFS_jnode_t ) ); + if ( !node ) + return NULL; + + /* + * Fill in the basic information + */ + node->st_nlink = 1; + node->type = type; + strncpy( node->name, name, IMFS_NAME_MAX ); + + /* + * Fill in the mode and permission information for the jnode structure. + */ + node->st_mode = mode; + #if defined(RTEMS_POSIX_API) + node->st_uid = geteuid(); + node->st_gid = getegid(); + #else + node->st_uid = 0; + node->st_gid = 0; + #endif + + /* + * Now set all the times. + */ + gettimeofday( &tv, 0 ); + + node->stat_atime = (time_t) tv.tv_sec; + node->stat_mtime = (time_t) tv.tv_sec; + node->stat_ctime = (time_t) tv.tv_sec; + + return node; +} + +IMFS_jnode_t *IMFS_create_root_node(void) +{ + IMFS_jnode_t *node; + + /* + * Allocate filesystem node and fill in basic information + */ + node = IMFS_allocate_node( IMFS_DIRECTORY, "", (S_IFDIR | 0755) ); + if ( !node ) + return NULL; + + /* + * Set the type specific information + * + * NOTE: Root node is always a directory. + */ + rtems_chain_initialize_empty(&node->info.directory.Entries); + + return node; +} diff --git a/cpukit/libfs/src/imfs/imfs_debug.c b/cpukit/libfs/src/imfs/imfs_debug.c new file mode 100644 index 0000000000..0a4e3a9bba --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_debug.c @@ -0,0 +1,159 @@ +/* + * IMFS debug support routines + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> /* for close */ +#include <inttypes.h> + +#include <stdio.h> +#include <sys/stat.h> + +#include "imfs.h" +#include <rtems/libio_.h> + +/* + * IMFS_print_jnode + * + * This routine prints the contents of the specified jnode. + */ +void IMFS_print_jnode( + IMFS_jnode_t *the_jnode +) +{ + IMFS_assert( the_jnode ); + + fprintf(stdout, "%s", the_jnode->name ); + switch( the_jnode->type ) { + case IMFS_DIRECTORY: + fprintf(stdout, "/" ); + break; + + case IMFS_DEVICE: + fprintf(stdout, " (device %" PRId32 ", %" PRId32 ")", + the_jnode->info.device.major, the_jnode->info.device.minor ); + break; + + case IMFS_LINEAR_FILE: + fprintf(stdout, " (file %" PRId32 " %p)", + (uint32_t)the_jnode->info.linearfile.size, + the_jnode->info.linearfile.direct + ); + break; + + case IMFS_MEMORY_FILE: + /* Useful when debugging .. varies between targets */ +#if 0 + fprintf(stdout, " (file %" PRId32 " %p %p %p)", + (uint32_t)the_jnode->info.file.size, + the_jnode->info.file.indirect, + the_jnode->info.file.doubly_indirect, + the_jnode->info.file.triply_indirect + ); +#else + fprintf(stdout, " (file %" PRId32 ")", + (uint32_t)the_jnode->info.file.size ); +#endif + break; + + case IMFS_HARD_LINK: + fprintf(stdout, " links not printed\n" ); + return; + + case IMFS_SYM_LINK: + fprintf(stdout, " links not printed\n" ); + return; + + case IMFS_FIFO: + fprintf(stdout, " FIFO not printed\n" ); + return; + + default: + fprintf(stdout, " bad type %d\n", the_jnode->type ); + return; + } + puts(""); +} + +/* + * IMFS_dump_directory + * + * This routine prints the contents of a directory in the IMFS. If a + * directory is encountered, then this routine will recurse to process + * the subdirectory. + */ +void IMFS_dump_directory( + IMFS_jnode_t *the_directory, + int level +) +{ + rtems_chain_node *the_node; + rtems_chain_control *the_chain; + IMFS_jnode_t *the_jnode; + int i; + + IMFS_assert( the_directory ); + IMFS_assert( level >= 0 ); + IMFS_assert( the_directory->type == IMFS_DIRECTORY ); + + the_chain = &the_directory->info.directory.Entries; + + for ( the_node = rtems_chain_first( the_chain ); + !rtems_chain_is_tail( the_chain, the_node ); + the_node = the_node->next ) { + + the_jnode = (IMFS_jnode_t *) the_node; + + for ( i=0 ; i<=level ; i++ ) + fprintf(stdout, "...." ); + IMFS_print_jnode( the_jnode ); + if ( the_jnode->type == IMFS_DIRECTORY ) + IMFS_dump_directory( the_jnode, level + 1 ); + } +} + +/* + * IMFS_dump + * + * This routine dumps the entire IMFS that is mounted at the root + * directory. + * + * NOTE: Assuming the "/" directory is bad. + * Not checking that the starting directory is in an IMFS is bad. + */ + +void IMFS_dump( void ) +{ + fprintf(stdout, "*************** Dump of Entire IMFS ***************\n" ); + fprintf(stdout, "/\n" ); + IMFS_dump_directory( rtems_filesystem_root.node_access, 0 ); + fprintf(stdout, "*************** End of Dump ***************\n" ); +} + +/* + * IMFS_memfile_maximum_size() + * + * This routine returns the size of the largest file which can be created + * using the IMFS memory file type. + * + */ +int IMFS_memfile_maximum_size( void ) +{ + return IMFS_MEMFILE_MAXIMUM_SIZE; +} diff --git a/cpukit/libfs/src/imfs/imfs_directory.c b/cpukit/libfs/src/imfs/imfs_directory.c new file mode 100644 index 0000000000..46ff335e59 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_directory.c @@ -0,0 +1,317 @@ +/* + * IMFS Directory Access Routines + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <rtems/chain.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <dirent.h> + +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +/* + * imfs_dir_open + * + * This rountine will verify that the node being opened as a directory is + * in fact a directory node. If it is then the offset into the directory + * will be set to 0 to position to the first directory entry. + */ + +int imfs_dir_open( + rtems_libio_t *iop, + const char *pathname, + uint32_t flag, + uint32_t mode +) +{ + IMFS_jnode_t *the_jnode; + + /* Is the node a directory ? */ + the_jnode = (IMFS_jnode_t *) iop->pathinfo.node_access; + + if ( the_jnode->type != IMFS_DIRECTORY ) + return -1; /* It wasn't a directory --> return error */ + + iop->offset = 0; + return 0; +} + +/* + * imfs_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. + */ + +ssize_t imfs_dir_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + /* + * Read up to element iop->offset in the directory chain of the + * imfs_jnode_t struct for this file descriptor. + */ + rtems_chain_node *the_node; + rtems_chain_control *the_chain; + IMFS_jnode_t *the_jnode; + int bytes_transferred; + int current_entry; + int first_entry; + int last_entry; + struct dirent tmp_dirent; + + the_jnode = (IMFS_jnode_t *)iop->pathinfo.node_access; + the_chain = &the_jnode->info.directory.Entries; + + if ( rtems_chain_is_empty( the_chain ) ) + return 0; + + /* Move to the first of the desired directory entries */ + the_node = rtems_chain_first( the_chain ); + + bytes_transferred = 0; + first_entry = iop->offset; + /* protect against using sizes that are not exact multiples of the */ + /* -dirent- size. These could result in unexpected results */ + last_entry = first_entry + (count/sizeof(struct dirent)) * sizeof(struct dirent); + + /* The directory was not empty so try to move to the desired entry in chain*/ + for ( + current_entry = 0; + current_entry < last_entry; + current_entry = current_entry + sizeof(struct dirent) ){ + + if ( rtems_chain_is_tail( the_chain, the_node ) ){ + /* We hit the tail of the chain while trying to move to the first */ + /* entry in the read */ + return bytes_transferred; /* Indicate that there are no more */ + /* entries to return */ + } + + if( current_entry >= first_entry ) { + /* Move the entry to the return buffer */ + tmp_dirent.d_off = current_entry; + tmp_dirent.d_reclen = sizeof( struct dirent ); + the_jnode = (IMFS_jnode_t *) the_node; + tmp_dirent.d_ino = the_jnode->st_ino; + tmp_dirent.d_namlen = strlen( the_jnode->name ); + strcpy( tmp_dirent.d_name, the_jnode->name ); + memcpy( + buffer + bytes_transferred, + (void *)&tmp_dirent, + sizeof( struct dirent ) + ); + iop->offset = iop->offset + sizeof(struct dirent); + bytes_transferred = bytes_transferred + sizeof( struct dirent ); + } + + the_node = the_node->next; + } + + /* Success */ + return bytes_transferred; +} + + + +/* + * imfs_dir_close + * + * This routine will be called by the generic close routine to cleanup any + * resources that have been allocated for the management of the file + */ + +int imfs_dir_close( + rtems_libio_t *iop +) +{ + /* + * The generic close routine handles the deallocation of the file control + * and associated memory. At present the imfs_dir_close simply + * returns a successful completion status. + */ + + return 0; +} + + + +/* + * imfs_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 EINVAL to be returned. + */ + +rtems_off64_t imfs_dir_lseek( + rtems_libio_t *iop, + rtems_off64_t offset, + int whence +) +{ + switch( whence ) { + case SEEK_SET: /* absolute move from the start of the file */ + case SEEK_CUR: /* relative move */ + iop->offset = (iop->offset/sizeof(struct dirent)) * + sizeof(struct dirent); + break; + + case SEEK_END: /* Movement past the end of the directory via lseek */ + /* is not a permitted operation */ + default: + rtems_set_errno_and_return_minus_one( EINVAL ); + break; + } + + return 0; +} + + + +/* + * imfs_dir_fstat + * + * This routine will obtain the following information concerning the current + * directory: + * st_dev 0ll + * st_ino 1 + * st_mode mode extracted from the jnode + * st_nlink number of links to this node + * st_uid uid extracted from the jnode + * st_gid gid extracted from the jnode + * st_rdev 0ll + * st_size the number of bytes in the directory + * This is calculated by taking the number of entries + * in the directory and multiplying by the size of a + * dirent structure + * st_blksize 0 + * st_blocks 0 + * stat_atime time of last access + * stat_mtime time of last modification + * stat_ctime time of the last change + * + * This information will be returned to the calling function in a -stat- struct + * + */ + +int imfs_dir_fstat( + rtems_filesystem_location_info_t *loc, + struct stat *buf +) +{ + rtems_chain_node *the_node; + rtems_chain_control *the_chain; + IMFS_jnode_t *the_jnode; + + + the_jnode = (IMFS_jnode_t *) loc->node_access; + + buf->st_dev = 0ll; + buf->st_ino = the_jnode->st_ino; + buf->st_mode = the_jnode->st_mode; + buf->st_nlink = the_jnode->st_nlink; + buf->st_uid = the_jnode->st_uid; + buf->st_gid = the_jnode->st_gid; + buf->st_rdev = 0ll; + buf->st_blksize = 0; + buf->st_blocks = 0; + buf->st_atime = the_jnode->stat_atime; + buf->st_mtime = the_jnode->stat_mtime; + buf->st_ctime = the_jnode->stat_ctime; + + buf->st_size = 0; + + the_chain = &the_jnode->info.directory.Entries; + + /* Run through the chain and count the number of directory entries */ + /* that are subordinate to this directory node */ + for ( the_node = rtems_chain_first( the_chain ); + !rtems_chain_is_tail( the_chain, the_node ) ; + the_node = the_node->next ) { + + buf->st_size = buf->st_size + sizeof( struct dirent ); + } + + return 0; +} + +/* + * IMFS_dir_rmnod + * + * This routine is available from the optable to remove a node + * from the IMFS file system. + */ + +int imfs_dir_rmnod( + rtems_filesystem_location_info_t *parent_pathloc, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN */ +) +{ + IMFS_jnode_t *the_jnode; + + the_jnode = (IMFS_jnode_t *) pathloc->node_access; + + /* + * You cannot remove a node that still has children + */ + + if ( ! rtems_chain_is_empty( &the_jnode->info.directory.Entries ) ) + rtems_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_set_errno_and_return_minus_one( EBUSY ); + + /* + * You cannot remove a mountpoint. + */ + + if ( the_jnode->info.directory.mt_fs != NULL ) + rtems_set_errno_and_return_minus_one( EBUSY ); + + IMFS_create_orphan( the_jnode ); + IMFS_check_node_remove( the_jnode ); + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_eval.c b/cpukit/libfs/src/imfs/imfs_eval.c new file mode 100644 index 0000000000..503c65179a --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_eval.c @@ -0,0 +1,663 @@ +/* + * Evaluation IMFS Node Support Routines + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> + +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +#define RTEMS_LIBIO_PERMS_RX (RTEMS_LIBIO_PERMS_SEARCH | RTEMS_LIBIO_PERMS_READ) +#define RTEMS_LIBIO_PERMS_WX (RTEMS_LIBIO_PERMS_SEARCH | RTEMS_LIBIO_PERMS_WRITE) + +#define MAXSYMLINK 5 + +int IMFS_Set_handlers( + rtems_filesystem_location_info_t *loc +) +{ + IMFS_jnode_t *node = loc->node_access; + IMFS_fs_info_t *fs_info; + + fs_info = loc->mt_entry->fs_info; + switch( node->type ) { + case IMFS_DIRECTORY: + loc->handlers = fs_info->directory_handlers; + break; + case IMFS_DEVICE: + loc->handlers = &IMFS_device_handlers; + break; + case IMFS_SYM_LINK: + case IMFS_HARD_LINK: + loc->handlers = &IMFS_link_handlers; + break; + case IMFS_LINEAR_FILE: + loc->handlers = fs_info->memfile_handlers; + break; + case IMFS_MEMORY_FILE: + loc->handlers = fs_info->memfile_handlers; + break; + case IMFS_FIFO: + loc->handlers = fs_info->fifo_handlers; + break; + } + + return 0; +} + +/* + * IMFS_evaluate_permission + * + * The following routine evaluates that we have permission + * to do flags on the node. + */ +int IMFS_evaluate_permission( + rtems_filesystem_location_info_t *node, + int flags +) +{ + uid_t st_uid; + gid_t st_gid; + IMFS_jnode_t *jnode; + int flags_to_test; + + if ( !rtems_libio_is_valid_perms( flags ) ) + rtems_set_errno_and_return_minus_one( EPERM ); + + jnode = node->node_access; + +#if defined(RTEMS_POSIX_API) + st_uid = geteuid(); + st_gid = getegid(); +#else + st_uid = jnode->st_uid; + st_gid = jnode->st_gid; +#endif + + /* + * Check if I am owner or a group member or someone else. + */ + + flags_to_test = flags; + + if ( st_uid == jnode->st_uid ) + flags_to_test <<= 6; + else if ( st_gid == jnode->st_gid ) + flags_to_test <<= 3; + else { + /* must be other - do nothing */; + } + + /* + * If all of the flags are set we have permission + * to do this. + */ + if ( ( flags_to_test & jnode->st_mode) == flags_to_test ) + return 1; + + return 0; +} + +/* + * IMFS_evaluate_hard_link + * + * The following routine evaluates a hardlink to the actual node. + */ + +int IMFS_evaluate_hard_link( + rtems_filesystem_location_info_t *node, /* IN/OUT */ + int flags /* IN */ +) +{ + IMFS_jnode_t *jnode = node->node_access; + int result = 0; + + /* + * Check for things that should never happen. + */ + IMFS_assert( jnode->type == IMFS_HARD_LINK ); + + /* + * Set the hard link value and the handlers. + */ + node->node_access = jnode->info.hard_link.link_node; + + IMFS_Set_handlers( node ); + + /* + * Verify we have the correct permissions for this node. + */ + + if ( !IMFS_evaluate_permission( node, flags ) ) + rtems_set_errno_and_return_minus_one( EACCES ); + + return result; +} + + +/* + * IMFS_evaluate_sym_link + * + * The following routine evaluates a symbolic link to the actual node. + */ + +int IMFS_evaluate_sym_link( + rtems_filesystem_location_info_t *node, /* IN/OUT */ + int flags /* IN */ +) +{ + IMFS_jnode_t *jnode = node->node_access; + int result = 0; + int i; + + /* + * Check for things that should never happen. + */ + IMFS_assert( jnode->type == IMFS_SYM_LINK ); + IMFS_assert( jnode->Parent ); + + /* + * Move the node_access to either the symbolic links parent or + * root depending on the symbolic links path. + */ + node->node_access = jnode->Parent; + + rtems_filesystem_get_sym_start_loc( + jnode->info.sym_link.name, + &i, + node + ); + + /* + * Use eval path to evaluate the path of the symbolic link. + */ + result = IMFS_eval_path( + &jnode->info.sym_link.name[i], + strlen( &jnode->info.sym_link.name[i] ), + flags, + node + ); + + IMFS_Set_handlers( node ); + + /* + * Verify we have the correct permissions for this node. + */ + if ( !IMFS_evaluate_permission( node, flags ) ) + rtems_set_errno_and_return_minus_one( EACCES ); + + return result; +} + +/* + * IMFS_evaluate_link + * + * The following routine returns the real node pointed to by a link. + */ +int IMFS_evaluate_link( + rtems_filesystem_location_info_t *node, /* IN/OUT */ + int flags /* IN */ +) +{ + IMFS_jnode_t *jnode; + int result = 0; + + do { + jnode = node->node_access; + + /* + * Increment and check the link counter. + */ + + rtems_filesystem_link_counts ++; + if ( rtems_filesystem_link_counts > MAXSYMLINK ) { + rtems_filesystem_link_counts = 0; + rtems_set_errno_and_return_minus_one( ELOOP ); + } + + /* + * Follow the Link node. + */ + + if ( jnode->type == IMFS_HARD_LINK ) + result = IMFS_evaluate_hard_link( node, flags ); + + else if (jnode->type == IMFS_SYM_LINK ) + result = IMFS_evaluate_sym_link( node, flags ); + + } while ( ( result == 0 ) && ( ( jnode->type == IMFS_SYM_LINK ) || + ( jnode->type == IMFS_HARD_LINK ) ) ); + + /* + * Clear link counter. + */ + + rtems_filesystem_link_counts = 0; + + return result; +} + + +/* + * IMFS_evaluate_for_make + * + * 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. + */ + +int IMFS_evaluate_for_make( + const char *path, /* IN */ + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + const char **name /* OUT */ +) +{ + int i = 0; + int len; + IMFS_token_types type; + char token[ IMFS_NAME_MAX + 1 ]; + rtems_filesystem_location_info_t newloc; + IMFS_jnode_t *node; + bool done = false; + int pathlen; + int result; + + /* + * This was filled in by the caller and is valid in the + * mount table. + */ + node = pathloc->node_access; + + /* + * Get the path length. + */ + pathlen = strlen( path ); + /* + * Evaluate all tokens until we are done or an error occurs. + */ + + while( !done ) { + + type = IMFS_get_token( &path[i], pathlen, token, &len ); + pathlen -= len; + i += len; + + if ( !pathloc->node_access ) + rtems_set_errno_and_return_minus_one( ENOENT ); + + /* + * I cannot move out of this directory without execute permission. + */ + + if ( type != IMFS_NO_MORE_PATH ) + if ( node->type == IMFS_DIRECTORY ) + if ( !IMFS_evaluate_permission( pathloc, RTEMS_LIBIO_PERMS_SEARCH ) ) + rtems_set_errno_and_return_minus_one( EACCES ); + + node = pathloc->node_access; + + switch( type ) { + + case IMFS_UP_DIR: + /* + * Am I at the root of all filesystems? (chroot'ed?) + */ + + if ( pathloc->node_access == rtems_filesystem_root.node_access ) + break; /* Throw out the .. in this case */ + + + /* + * 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? + */ + + if ( pathloc->node_access == rtems_filesystem_root.node_access ) { + break; + + } else { + newloc = pathloc->mt_entry->mt_point_node; + *pathloc = newloc; + return (*pathloc->ops->evalformake_h)( &path[i-len], pathloc, name ); + } + } else { + + if ( !node->Parent ) + rtems_set_errno_and_return_minus_one( ENOENT ); + + node = node->Parent; + } + + pathloc->node_access = node; + break; + + case IMFS_NAME: + + if ( node->type == IMFS_HARD_LINK ) { + + result = IMFS_evaluate_link( pathloc, 0 ); + if ( result == -1 ) + return -1; + + } else if ( node->type == IMFS_SYM_LINK ) { + + result = IMFS_evaluate_link( pathloc, 0 ); + + if ( result == -1 ) + return -1; + } + + node = pathloc->node_access; + if ( !node ) + rtems_set_errno_and_return_minus_one( ENOTDIR ); + + /* + * Only a directory can be decended into. + */ + + if ( node->type != IMFS_DIRECTORY ) + rtems_set_errno_and_return_minus_one( ENOTDIR ); + + /* + * If we are at a node that is a mount point. Set loc to the + * new fs root node and let them finish evaluating the path. + */ + + if ( node->info.directory.mt_fs != NULL ) { + newloc = node->info.directory.mt_fs->mt_fs_root; + *pathloc = newloc; + return (*pathloc->ops->evalformake_h)( &path[i-len], pathloc, name ); + } + + /* + * Otherwise find the token name in the present location. + */ + + node = IMFS_find_match_in_dir( node, token ); + + /* + * If there is no node we have found the name of the node we + * wish to create. + */ + + if ( ! node ) + done = true; + else + pathloc->node_access = node; + + break; + + case IMFS_NO_MORE_PATH: + rtems_set_errno_and_return_minus_one( EEXIST ); + break; + + case IMFS_INVALID_TOKEN: + rtems_set_errno_and_return_minus_one( ENAMETOOLONG ); + break; + + case IMFS_CURRENT_DIR: + 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 ( !IMFS_is_separator( path[ i ] ) ) + rtems_set_errno_and_return_minus_one( ENOENT ); + } + + /* + * Verify we can execute and write to this directory. + */ + + result = IMFS_Set_handlers( pathloc ); + + /* + * The returned node must be a directory + */ + node = pathloc->node_access; + if ( node->type != IMFS_DIRECTORY ) + rtems_set_errno_and_return_minus_one( ENOTDIR ); + + /* + * We must have Write and execute permission on the returned node. + */ + + if ( !IMFS_evaluate_permission( pathloc, RTEMS_LIBIO_PERMS_WX ) ) + rtems_set_errno_and_return_minus_one( EACCES ); + + return result; +} + + +/* + * IMFS_eval_path + * + * The following routine evaluate path for a node that wishes to be + * accessed with mode. pathloc is returned with a pointer to the + * node to be accessed. + */ + +int IMFS_eval_path( + const char *pathname, /* IN */ + size_t pathnamelen, /* IN */ + int flags, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */ + ) +{ + int i = 0; + int len; + IMFS_token_types type = IMFS_CURRENT_DIR; + char token[ IMFS_NAME_MAX + 1 ]; + rtems_filesystem_location_info_t newloc; + IMFS_jnode_t *node; + int result; + + if ( !rtems_libio_is_valid_perms( flags ) ) { + rtems_set_errno_and_return_minus_one( EIO ); + } + + /* + * This was filled in by the caller and is valid in the + * mount table. + */ + + node = pathloc->node_access; + + /* + * Evaluate all tokens until we are done or an error occurs. + */ + + while( (type != IMFS_NO_MORE_PATH) && (type != IMFS_INVALID_TOKEN) ) { + + type = IMFS_get_token( &pathname[i], pathnamelen, token, &len ); + pathnamelen -= len; + i += len; + + if ( !pathloc->node_access ) + rtems_set_errno_and_return_minus_one( ENOENT ); + + /* + * I cannot move out of this directory without execute permission. + */ + if ( type != IMFS_NO_MORE_PATH ) + if ( node->type == IMFS_DIRECTORY ) + if ( !IMFS_evaluate_permission( pathloc, RTEMS_LIBIO_PERMS_SEARCH ) ) + rtems_set_errno_and_return_minus_one( EACCES ); + + node = pathloc->node_access; + + switch( type ) { + case IMFS_UP_DIR: + /* + * Am I at the root of all filesystems? (chroot'ed?) + */ + + if ( pathloc->node_access == rtems_filesystem_root.node_access ) + break; /* Throw out the .. in this case */ + + /* + * 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? + */ + + 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; + return (*pathloc->ops->evalpath_h)(&(pathname[i-len]), + pathnamelen+len, + flags,pathloc); + } + } else { + + if ( !node->Parent ) + rtems_set_errno_and_return_minus_one( ENOENT ); + + node = node->Parent; + pathloc->node_access = node; + + } + + pathloc->node_access = node; + break; + + case IMFS_NAME: + /* + * If we are at a link follow it. + */ + if ( node->type == IMFS_HARD_LINK ) { + IMFS_evaluate_hard_link( pathloc, 0 ); + node = pathloc->node_access; + + /* + * It would be a design error if we evaluated the link and + * was broken. + */ + IMFS_assert( node ); + + } else if ( node->type == IMFS_SYM_LINK ) { + result = IMFS_evaluate_sym_link( pathloc, 0 ); + + /* + * In contrast to a hard link, it is possible to have a broken + * symbolic link. + */ + node = pathloc->node_access; + if ( result == -1 ) + return -1; + } + + /* + * Only a directory can be decended into. + */ + if ( node->type != IMFS_DIRECTORY ) + rtems_set_errno_and_return_minus_one( ENOTDIR ); + + /* + * If we are at a node that is a mount point. Set loc to the + * new fs root node and let them finish evaluating the path. + */ + if ( node->info.directory.mt_fs != NULL ) { + newloc = node->info.directory.mt_fs->mt_fs_root; + *pathloc = newloc; + return (*pathloc->ops->evalpath_h)( &pathname[i-len], + pathnamelen+len, + flags, pathloc ); + } + + /* + * Otherwise find the token name in the present location. + */ + node = IMFS_find_match_in_dir( node, token ); + if ( !node ) + rtems_set_errno_and_return_minus_one( ENOENT ); + + /* + * Set the node access to the point we have found. + */ + + pathloc->node_access = node; + break; + + case IMFS_NO_MORE_PATH: + case IMFS_CURRENT_DIR: + break; + + case IMFS_INVALID_TOKEN: + rtems_set_errno_and_return_minus_one( ENAMETOOLONG ); + 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. + */ + + if ( node->type == IMFS_DIRECTORY ) { + if ( node->info.directory.mt_fs != NULL ) { + newloc = node->info.directory.mt_fs->mt_fs_root; + *pathloc = newloc; + return (*pathloc->ops->evalpath_h)( &pathname[i-len], + pathnamelen+len, + flags, pathloc ); + } else { + result = IMFS_Set_handlers( pathloc ); + } + } else { + result = IMFS_Set_handlers( pathloc ); + } + + /* + * Verify we have the correct permissions for this node. + */ + + if ( !IMFS_evaluate_permission( pathloc, flags ) ) + rtems_set_errno_and_return_minus_one( EACCES ); + + return result; +} diff --git a/cpukit/libfs/src/imfs/imfs_fchmod.c b/cpukit/libfs/src/imfs/imfs_fchmod.c new file mode 100644 index 0000000000..54a093de2f --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_fchmod.c @@ -0,0 +1,56 @@ +/* + * IMFS file change mode routine. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> + +#include <rtems/libio_.h> +#include <rtems/seterr.h> +#include "imfs.h" + +int IMFS_fchmod( + rtems_filesystem_location_info_t *loc, + mode_t mode +) +{ + IMFS_jnode_t *jnode; +#if defined(RTEMS_POSIX_API) + uid_t st_uid; +#endif + + jnode = loc->node_access; + + /* + * Verify I am the owner of the node or the super user. + */ +#if defined(RTEMS_POSIX_API) + st_uid = geteuid(); + + if ( ( st_uid != jnode->st_uid ) && ( st_uid != 0 ) ) + rtems_set_errno_and_return_minus_one( EPERM ); +#endif + + /* + * Change only the RWX permissions on the jnode to mode. + */ + + jnode->st_mode &= ~(S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); + jnode->st_mode |= mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); + + IMFS_update_ctime( jnode ); + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_fdatasync.c b/cpukit/libfs/src/imfs/imfs_fdatasync.c new file mode 100644 index 0000000000..6b107bfaf8 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_fdatasync.c @@ -0,0 +1,29 @@ +/* + * IMFS_fdatasync + * + * The following routine does a sync on an IMFS node. The In Memory + * File System is always in sync, therefore this routine always returns + * pass. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imfs.h" + +int IMFS_fdatasync( + rtems_libio_t *iop +) +{ + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_fifo.c b/cpukit/libfs/src/imfs/imfs_fifo.c new file mode 100644 index 0000000000..7cb3bbcd85 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_fifo.c @@ -0,0 +1,148 @@ +/* + * imfs_fifo.c: FIFO support for IMFS + * + * Author: Wei Shen <cquark@gmail.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +#include "imfs.h" + +#define JNODE2PIPE(_jnode) ( (_jnode)->info.fifo.pipe ) + +#define LIBIO2PIPE(_iop) ( JNODE2PIPE((IMFS_jnode_t *)(_iop)->pathinfo.node_access) ) + +/* Set errno and return -1 if error, else return _err */ +#define IMFS_FIFO_RETURN(_err) \ +do { \ + if (_err < 0) \ + rtems_set_errno_and_return_minus_one(-_err); \ + return _err; \ +} while (0) + +int IMFS_fifo_open( + rtems_libio_t *iop, + const char *pathname, + uint32_t flag, + uint32_t mode +) +{ + IMFS_jnode_t *jnode = iop->pathinfo.node_access; + + int err = fifo_open(&JNODE2PIPE(jnode), iop); + IMFS_FIFO_RETURN(err); +} + +int IMFS_fifo_close( + rtems_libio_t *iop +) +{ + int err = 0; + IMFS_jnode_t *jnode = iop->pathinfo.node_access; + + pipe_release(&JNODE2PIPE(jnode), iop); + + iop->flags &= ~LIBIO_FLAGS_OPEN; + IMFS_check_node_remove(jnode); + + IMFS_FIFO_RETURN(err); +} + +ssize_t IMFS_fifo_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + IMFS_jnode_t *jnode = iop->pathinfo.node_access; + + int err = pipe_read(JNODE2PIPE(jnode), buffer, count, iop); + if (err > 0) + IMFS_update_atime(jnode); + + IMFS_FIFO_RETURN(err); +} + +ssize_t IMFS_fifo_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ + IMFS_jnode_t *jnode = iop->pathinfo.node_access; + + int err = pipe_write(JNODE2PIPE(jnode), buffer, count, iop); + if (err > 0) { + IMFS_mtime_ctime_update(jnode); + } + + IMFS_FIFO_RETURN(err); +} + +int IMFS_fifo_ioctl( + rtems_libio_t *iop, + uint32_t command, + void *buffer +) +{ + int err; + + if (command == FIONBIO) { + if (buffer == NULL) + err = -EFAULT; + else { + if (*(int *)buffer) + iop->flags |= LIBIO_FLAGS_NO_DELAY; + else + iop->flags &= ~LIBIO_FLAGS_NO_DELAY; + return 0; + } + } + else + err = pipe_ioctl(LIBIO2PIPE(iop), command, buffer, iop); + + IMFS_FIFO_RETURN(err); +} + +rtems_off64_t IMFS_fifo_lseek( + rtems_libio_t *iop, + rtems_off64_t offset, + int whence +) +{ + off_t err = pipe_lseek(LIBIO2PIPE(iop), offset, whence, iop); + IMFS_FIFO_RETURN(err); +} + +/* + * Handler table for IMFS FIFO nodes + */ + +const rtems_filesystem_file_handlers_r IMFS_fifo_handlers = { + IMFS_fifo_open, + IMFS_fifo_close, + IMFS_fifo_read, + IMFS_fifo_write, + IMFS_fifo_ioctl, + IMFS_fifo_lseek, + IMFS_stat, + IMFS_fchmod, + rtems_filesystem_default_ftruncate, + rtems_filesystem_default_fpathconf, + rtems_filesystem_default_fsync, + rtems_filesystem_default_fdatasync, + rtems_filesystem_default_fcntl, + IMFS_rmnod, +}; diff --git a/cpukit/libfs/src/imfs/imfs_fsunmount.c b/cpukit/libfs/src/imfs/imfs_fsunmount.c new file mode 100644 index 0000000000..b65c20e60d --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_fsunmount.c @@ -0,0 +1,94 @@ +/* + * IMFS Initialization + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> /* for mkdir */ +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include "imfs.h" +#include <rtems/libio_.h> + +#if defined(IMFS_DEBUG) +#include <stdio.h> +#endif + +/* + * IMFS_fsunmount + */ + +#define jnode_get_control( jnode ) \ + (&jnode->info.directory.Entries) + +#define jnode_has_no_children( jnode ) \ + rtems_chain_is_empty( jnode_get_control( jnode ) ) + +#define jnode_has_children( jnode ) \ + ( ! jnode_has_no_children( jnode ) ) + +#define jnode_get_first_child( jnode ) \ + ((IMFS_jnode_t *)( rtems_chain_head( jnode_get_control( jnode ) )->next)) + +int IMFS_fsunmount( + rtems_filesystem_mount_table_entry_t *temp_mt_entry +) +{ + IMFS_jnode_t *jnode; + IMFS_jnode_t *next; + rtems_filesystem_location_info_t loc; + int result = 0; + + /* + * Traverse tree that starts at the mt_fs_root and deallocate memory + * associated memory space + */ + + jnode = (IMFS_jnode_t *)temp_mt_entry->mt_fs_root.node_access; + loc = temp_mt_entry->mt_fs_root; + + /* + * Set this to null to indicate that it is being unmounted. + */ + + temp_mt_entry->mt_fs_root.node_access = NULL; + + do { + next = jnode->Parent; + loc.node_access = (void *)jnode; + IMFS_Set_handlers( &loc ); + + if ( jnode->type != IMFS_DIRECTORY ) { + result = IMFS_unlink( NULL, &loc ); + if (result != 0) + return -1; + jnode = next; + } else if ( jnode_has_no_children( jnode ) ) { + result = IMFS_unlink( NULL, &loc ); + if (result != 0) + return -1; + jnode = next; + } + if ( jnode != NULL ) { + if ( jnode->type == IMFS_DIRECTORY ) { + if ( jnode_has_children( jnode ) ) + jnode = jnode_get_first_child( jnode ); + } + } + } while (jnode != NULL); + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_getchild.c b/cpukit/libfs/src/imfs/imfs_getchild.c new file mode 100644 index 0000000000..1cfeb2a537 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_getchild.c @@ -0,0 +1,66 @@ +/* + * IMFS_find_match_in_dir() + * + * This routine returns the child name in the given directory. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <string.h> +#include "imfs.h" + +static const char dotname[2] = "."; +static const char dotdotname[3] = ".."; + +IMFS_jnode_t *IMFS_find_match_in_dir( + IMFS_jnode_t *directory, + char *name +) +{ + rtems_chain_node *the_node; + rtems_chain_control *the_chain; + IMFS_jnode_t *the_jnode; + + /* + * Check for fatal errors. A NULL directory show a problem in the + * the IMFS code. + */ + IMFS_assert( directory ); + IMFS_assert( name ); + + /* + * Check for "." and ".." + */ + + if ( !strcmp( name, dotname ) ) + return directory; + + if ( !strcmp( name, dotdotname ) ) + return directory->Parent; + + the_chain = &directory->info.directory.Entries; + + for ( the_node = rtems_chain_first( the_chain ); + !rtems_chain_is_tail( the_chain, the_node ); + the_node = the_node->next ) { + + the_jnode = (IMFS_jnode_t *) the_node; + + if ( !strcmp( name, the_jnode->name ) ) + return the_jnode; + } + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_gtkn.c b/cpukit/libfs/src/imfs/imfs_gtkn.c new file mode 100644 index 0000000000..b15b2f1136 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_gtkn.c @@ -0,0 +1,91 @@ +/* + * IMFS_get_token + * + * Routine to get a token (name or separator) from the path + * the length of the token is returned in token_len. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "imfs.h" +#include <rtems/libio_.h> + +IMFS_token_types IMFS_get_token( + const char *path, + int pathlen, + char *token, + int *token_len +) +{ + register int i = 0; + IMFS_token_types type = IMFS_NAME; + register char c; + + /* + * Copy a name into token. (Remember NULL is a token.) + */ + c = path[i]; + while ( (!IMFS_is_separator(c)) && (i < pathlen) && (i <= IMFS_NAME_MAX) ) { + + token[i] = c; + + if ( i == IMFS_NAME_MAX ) + return IMFS_INVALID_TOKEN; + + if ( !IMFS_is_valid_name_char(c) ) + type = IMFS_INVALID_TOKEN; + + c = path [++i]; + } + + /* + * Copy a seperator into token. + */ + + if ( i == 0 ) { + token[i] = c; + + if ( (token[i] != '\0') && pathlen ) { + i++; + type = IMFS_CURRENT_DIR; + } else { + type = IMFS_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 == IMFS_NAME ) { + if ( strcmp( token, "..") == 0 ) + type = IMFS_UP_DIR; + else if ( strcmp( token, "." ) == 0 ) + type = IMFS_CURRENT_DIR; + } + + return type; +} diff --git a/cpukit/libfs/src/imfs/imfs_handlers_device.c b/cpukit/libfs/src/imfs/imfs_handlers_device.c new file mode 100644 index 0000000000..025fb985cf --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_handlers_device.c @@ -0,0 +1,41 @@ +/* + * Device Operations Table for the IMFS + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> + +#include "imfs.h" + +/* + * Handler table for IMFS device nodes + */ + +const rtems_filesystem_file_handlers_r IMFS_device_handlers = { + device_open, + device_close, + device_read, + device_write, + device_ioctl, + device_lseek, + IMFS_stat, + IMFS_fchmod, + device_ftruncate, + rtems_filesystem_default_fpathconf, + rtems_filesystem_default_fsync, + rtems_filesystem_default_fdatasync, + rtems_filesystem_default_fcntl, + IMFS_rmnod +}; diff --git a/cpukit/libfs/src/imfs/imfs_handlers_directory.c b/cpukit/libfs/src/imfs/imfs_handlers_directory.c new file mode 100644 index 0000000000..d70957d018 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_handlers_directory.c @@ -0,0 +1,41 @@ +/* + * Operations Table for Directories for the IMFS + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> + +#include "imfs.h" + +/* + * Set of operations handlers for operations on directories. + */ + +const rtems_filesystem_file_handlers_r IMFS_directory_handlers = { + imfs_dir_open, + imfs_dir_close, + imfs_dir_read, + rtems_filesystem_default_write, + rtems_filesystem_default_ioctl, + imfs_dir_lseek, + imfs_dir_fstat, + IMFS_fchmod, + rtems_filesystem_default_ftruncate, + rtems_filesystem_default_fpathconf, + rtems_filesystem_default_fsync, + IMFS_fdatasync, + rtems_filesystem_default_fcntl, + imfs_dir_rmnod +}; diff --git a/cpukit/libfs/src/imfs/imfs_handlers_link.c b/cpukit/libfs/src/imfs/imfs_handlers_link.c new file mode 100644 index 0000000000..74f93e1a53 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_handlers_link.c @@ -0,0 +1,41 @@ +/* + * Link Operations Table for the IMFS + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> + +#include "imfs.h" + +/* + * Handler table for IMFS device nodes + */ + +const rtems_filesystem_file_handlers_r IMFS_link_handlers = { + rtems_filesystem_default_open, + rtems_filesystem_default_close, + rtems_filesystem_default_read, + rtems_filesystem_default_write, + rtems_filesystem_default_ioctl, + rtems_filesystem_default_lseek, + IMFS_stat, /* stat */ + rtems_filesystem_default_fchmod, + rtems_filesystem_default_ftruncate, + rtems_filesystem_default_fpathconf, + rtems_filesystem_default_fsync, + rtems_filesystem_default_fdatasync, + rtems_filesystem_default_fcntl, + IMFS_rmnod +}; diff --git a/cpukit/libfs/src/imfs/imfs_handlers_memfile.c b/cpukit/libfs/src/imfs/imfs_handlers_memfile.c new file mode 100644 index 0000000000..33003ecba9 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_handlers_memfile.c @@ -0,0 +1,41 @@ +/* + * Memfile Operations Tables for the IMFS + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> + +#include "imfs.h" + +/* + * Set of operations handlers for operations on memfile entities. + */ + +const rtems_filesystem_file_handlers_r IMFS_memfile_handlers = { + memfile_open, + memfile_close, + memfile_read, + memfile_write, + memfile_ioctl, + memfile_lseek, + IMFS_stat, + IMFS_fchmod, + memfile_ftruncate, + rtems_filesystem_default_fpathconf, + IMFS_fdatasync, /* fsync */ + IMFS_fdatasync, + rtems_filesystem_default_fcntl, + IMFS_rmnod +}; diff --git a/cpukit/libfs/src/imfs/imfs_init.c b/cpukit/libfs/src/imfs/imfs_init.c new file mode 100644 index 0000000000..c74d493b40 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_init.c @@ -0,0 +1,61 @@ +/** + * @file + * + * @ingroup LibFSIMFS + * + * @brief IMFS initialization. + */ + +/* + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/libio_.h> + +#include "imfs.h" + +const rtems_filesystem_operations_table IMFS_ops = { + .evalpath_h = IMFS_eval_path, + .evalformake_h = IMFS_evaluate_for_make, + .link_h = IMFS_link, + .unlink_h = IMFS_unlink, + .node_type_h = IMFS_node_type, + .mknod_h = IMFS_mknod, + .chown_h = IMFS_chown, + .freenod_h = rtems_filesystem_default_freenode, + .mount_h = IMFS_mount, + .fsmount_me_h = IMFS_initialize, + .unmount_h = IMFS_unmount, + .fsunmount_me_h = IMFS_fsunmount, + .utime_h = IMFS_utime, + .eval_link_h = IMFS_evaluate_link, + .symlink_h = IMFS_symlink, + .readlink_h = IMFS_readlink, + .rename_h = IMFS_rename, + .statvfs_h = rtems_filesystem_default_statvfs +}; + +int IMFS_initialize( + rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data +) +{ + return IMFS_initialize_support( + mt_entry, + &IMFS_ops, + &IMFS_memfile_handlers, + &IMFS_directory_handlers, + &rtems_filesystem_handlers_default /* for fifos */ + ); +} diff --git a/cpukit/libfs/src/imfs/imfs_initsupp.c b/cpukit/libfs/src/imfs/imfs_initsupp.c new file mode 100644 index 0000000000..47e1d392e8 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_initsupp.c @@ -0,0 +1,119 @@ +/* + * IMFS Initialization + * + * COPYRIGHT (c) 1989-2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> /* for mkdir */ +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +#if defined(IMFS_DEBUG) +#include <stdio.h> +#endif + +/* + * IMFS_determine_bytes_per_block + */ +int imfs_memfile_bytes_per_block = 0; + +static int IMFS_determine_bytes_per_block( + int *dest_bytes_per_block, + int requested_bytes_per_block, + int default_bytes_per_block +) +{ + bool is_valid = false; + int bit_mask; + + /* + * check, whether requested bytes per block is valid + */ + for (bit_mask = 16; !is_valid && (bit_mask <= 512); bit_mask <<= 1) { + if (bit_mask == requested_bytes_per_block) { + is_valid = true; + break; + } + if(bit_mask > requested_bytes_per_block) + break; + } + *dest_bytes_per_block = ((is_valid) + ? requested_bytes_per_block + : default_bytes_per_block); + return 0; +} + + +/* + * IMFS_initialize + */ +int IMFS_initialize_support( + rtems_filesystem_mount_table_entry_t *temp_mt_entry, + const rtems_filesystem_operations_table *op_table, + const rtems_filesystem_file_handlers_r *memfile_handlers, + const rtems_filesystem_file_handlers_r *directory_handlers, + const rtems_filesystem_file_handlers_r *fifo_handlers +) +{ + static int imfs_instance; + IMFS_fs_info_t *fs_info; + IMFS_jnode_t *jnode; + + /* + * determine/check value for imfs_memfile_bytes_per_block + */ + IMFS_determine_bytes_per_block(&imfs_memfile_bytes_per_block, + imfs_rq_memfile_bytes_per_block, + IMFS_MEMFILE_DEFAULT_BYTES_PER_BLOCK); + + /* + * Create the root node + * + * NOTE: UNIX root is 755 and owned by root/root (0/0). + */ + temp_mt_entry->mt_fs_root.node_access = IMFS_create_root_node(); + temp_mt_entry->mt_fs_root.handlers = directory_handlers; + temp_mt_entry->mt_fs_root.ops = op_table; + temp_mt_entry->pathconf_limits_and_options = IMFS_LIMITS_AND_OPTIONS; + + /* + * Create custom file system data. + */ + fs_info = calloc( 1, sizeof( IMFS_fs_info_t ) ); + if ( !fs_info ) { + free(temp_mt_entry->mt_fs_root.node_access); + rtems_set_errno_and_return_minus_one(ENOMEM); + } + temp_mt_entry->fs_info = fs_info; + + /* + * Set st_ino for the root to 1. + */ + + fs_info->instance = imfs_instance++; + fs_info->ino_count = 1; + fs_info->memfile_handlers = memfile_handlers; + fs_info->directory_handlers = directory_handlers; + fs_info->fifo_handlers = fifo_handlers; + + jnode = temp_mt_entry->mt_fs_root.node_access; + jnode->st_ino = fs_info->ino_count; + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_link.c b/cpukit/libfs/src/imfs/imfs_link.c new file mode 100644 index 0000000000..4c2136ba71 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_link.c @@ -0,0 +1,78 @@ +/* + * IMFS_link + * + * The following rouine creates a new link node under parent with the + * name given in name. The link node is set to point to the node at + * to_loc. + * + * COPYRIGHT (c) 1989-2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int IMFS_link( + rtems_filesystem_location_info_t *to_loc, /* IN */ + rtems_filesystem_location_info_t *parent_loc, /* IN */ + const char *token /* IN */ +) +{ + IMFS_types_union info; + IMFS_jnode_t *new_node; + char new_name[ IMFS_NAME_MAX + 1 ]; + int i; + + /* + * Verify this node can be linked to. + */ + info.hard_link.link_node = to_loc->node_access; + if ( info.hard_link.link_node->st_nlink >= LINK_MAX ) + rtems_set_errno_and_return_minus_one( EMLINK ); + + /* + * Remove any separators at the end of the string. + */ + IMFS_get_token( token, strlen( token ), new_name, &i ); + + /* + * Create a new link node. + * + * NOTE: Coverity Id 19 reports this as a leak + * While technically not a leak, it indicated that IMFS_create_node + * was ONLY passed a NULL when we created the root node. We + * added a new IMFS_create_root_node() so this path no longer + * existed. The result was simpler code which should not have + * this path. + */ + new_node = IMFS_create_node( + parent_loc, + IMFS_HARD_LINK, + new_name, + ( S_IFLNK | ( S_IRWXU | S_IRWXG | S_IRWXO )), + &info + ); + + if ( !new_node ) + rtems_set_errno_and_return_minus_one( ENOMEM ); + + /* + * Increment the link count of the node being pointed to. + */ + info.hard_link.link_node->st_nlink++; + IMFS_update_ctime( info.hard_link.link_node ); + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_load_tar.c b/cpukit/libfs/src/imfs/imfs_load_tar.c new file mode 100644 index 0000000000..3cb3c4e195 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_load_tar.c @@ -0,0 +1,186 @@ +/* + * COPYRIGHT (c) 1989-2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +/* + * This file implements the "mount" procedure for tar-based IMFS + * extensions. The TAR is not actually mounted under the IMFS. + * Directories from the TAR file are created as usual in the IMFS. + * File entries are created as IMFS_LINEAR_FILE nodes with their nods + * pointing to addresses in the TAR image. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <tar.h> + +#include <string.h> + +#include <rtems.h> +#include <rtems/libio_.h> +#include <rtems/imfs.h> +#include <rtems/untar.h> + +/* + * TAR file format: + * + * Offset Length Contents + * 0 100 bytes File name ('\0' terminated, 99 maxmum length) + * 100 8 bytes File mode (in octal ascii) + * 108 8 bytes User ID (in octal ascii) + * 116 8 bytes Group ID (in octal ascii) + * 124 12 bytes File size (s) (in octal ascii) + * 136 12 bytes Modify time (in octal ascii) + * 148 8 bytes Header checksum (in octal ascii) + * 156 1 bytes Link flag + * 157 100 bytes Linkname ('\0' terminated, 99 maxmum length) + * 257 8 bytes Magic PAX ("ustar\0" + 2 bytes padding) + * 257 8 bytes Magic GNU tar ("ustar \0") + * 265 32 bytes User name ('\0' terminated, 31 maxmum length) + * 297 32 bytes Group name ('\0' terminated, 31 maxmum length) + * 329 8 bytes Major device ID (in octal ascii) + * 337 8 bytes Minor device ID (in octal ascii) + * 345 167 bytes Padding + * 512 (s+p)bytes File contents (s+p) := (((s) + 511) & ~511), + * round up to 512 bytes + * + * Checksum: + * int i, sum; + * char* header = tar_header_pointer; + * sum = 0; + * for(i = 0; i < 512; i++) + * sum += 0xFF & header[i]; + */ + +#define MAX_NAME_FIELD_SIZE 99 + +#define MIN(a,b) ((a)>(b)?(b):(a)) + +/* + * rtems_tarfs_load + * + * Here we create the mountpoint directory and load the tarfs at + * that node. Once the IMFS has been mounted, we work through the + * tar image and perform as follows: + * - For directories, simply call mkdir(). The IMFS creates nodes as + * needed. + * - For files, we make our own calls to IMFS eval_for_make and + * create_node. + */ +int rtems_tarfs_load( + char *mountpoint, + uint8_t *tar_image, + size_t tar_size +) +{ + rtems_filesystem_location_info_t root_loc; + rtems_filesystem_location_info_t loc; + const char *hdr_ptr; + char filename[100]; + char full_filename[256]; + int hdr_chksum; + unsigned char linkflag; + unsigned long file_size; + unsigned long file_mode; + int offset; + unsigned long nblocks; + IMFS_jnode_t *node; + int status; + + status = rtems_filesystem_evaluate_path( + mountpoint, + strlen(mountpoint), + 0, + &root_loc, + 0 + ); + if (status != 0) + return -1; + + if (root_loc.ops != &IMFS_ops && root_loc.ops != &fifoIMFS_ops) + return -1; + + /* + * Create an IMFS node structure pointing to tar image memory. + */ + offset = 0; + while (1) { + if (offset + 512 > tar_size) + break; + + /* + * Read a header. + */ + hdr_ptr = (char *) &tar_image[offset]; + offset += 512; + if (strncmp(&hdr_ptr[257], "ustar", 5)) + break; + + strncpy(filename, hdr_ptr, MAX_NAME_FIELD_SIZE); + filename[MAX_NAME_FIELD_SIZE] = '\0'; + + linkflag = hdr_ptr[156]; + file_mode = _rtems_octal2ulong(&hdr_ptr[100], 8); + file_size = _rtems_octal2ulong(&hdr_ptr[124], 12); + hdr_chksum = _rtems_octal2ulong(&hdr_ptr[148], 8); + + if (_rtems_tar_header_checksum(hdr_ptr) != hdr_chksum) + break; + + /* + * Generate an IMFS node depending on the file type. + * - For directories, just create directories as usual. IMFS + * will take care of the rest. + * - For files, create a file node with special tarfs properties. + */ + if (linkflag == DIRTYPE) { + strcpy(full_filename, mountpoint); + if (full_filename[strlen(full_filename)-1] != '/') + strcat(full_filename, "/"); + strcat(full_filename, filename); + mkdir(full_filename, S_IRWXU | S_IRWXG | S_IRWXO); + } + /* + * Create a LINEAR_FILE node + * + * NOTE: Coverity Id 20 reports this as a leak. + * While technically not a leak, it indicated that + * IMFS_create_node was ONLY passed a NULL when we created the + * root node. We added a new IMFS_create_root_node() so this + * path no longer existed. The result was simpler code which + * should not have this path. + */ + else if (linkflag == REGTYPE) { + const char *name; + + loc = root_loc; + if (IMFS_evaluate_for_make(filename, &loc, &name) == 0) { + node = IMFS_create_node( + &loc, + IMFS_LINEAR_FILE, (char *)name, + (file_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG, + NULL + ); + node->info.linearfile.size = file_size; + node->info.linearfile.direct = &tar_image[offset]; + } + + nblocks = (((file_size) + 511) & ~511) / 512; + offset += 512 * nblocks; + } + } + return status; +} + diff --git a/cpukit/libfs/src/imfs/imfs_mknod.c b/cpukit/libfs/src/imfs/imfs_mknod.c new file mode 100644 index 0000000000..1a0175af0a --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_mknod.c @@ -0,0 +1,76 @@ +/* + * IMFS_mknod + * + * Routine to create a node in the IMFS file system. + * + * COPYRIGHT (c) 1989-2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> + +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int IMFS_mknod( + const char *token, /* IN */ + mode_t mode, /* IN */ + dev_t dev, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */ +) +{ + IMFS_token_types type = 0; + IMFS_jnode_t *new_node; + int result; + char new_name[ IMFS_NAME_MAX + 1 ]; + IMFS_types_union info; + + IMFS_get_token( token, strlen( token ), new_name, &result ); + + /* + * Figure out what type of IMFS node this is. + */ + if ( S_ISDIR(mode) ) + type = IMFS_DIRECTORY; + else if ( S_ISREG(mode) ) + type = IMFS_MEMORY_FILE; + else if ( S_ISBLK(mode) || S_ISCHR(mode) ) { + type = IMFS_DEVICE; + rtems_filesystem_split_dev_t( dev, info.device.major, info.device.minor ); + } else if (S_ISFIFO(mode)) + type = IMFS_FIFO; + else + IMFS_assert( 0 ); + + /* + * Allocate and fill in an IMFS jnode + * + * NOTE: Coverity Id 21 reports this as a leak. + * While technically not a leak, it indicated that IMFS_create_node + * was ONLY passed a NULL when we created the root node. We + * added a new IMFS_create_root_node() so this path no longer + * existed. The result was simpler code which should not have + * this path. + */ + new_node = IMFS_create_node( pathloc, type, new_name, mode, &info ); + if ( !new_node ) + rtems_set_errno_and_return_minus_one( ENOMEM ); + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_mount.c b/cpukit/libfs/src/imfs/imfs_mount.c new file mode 100644 index 0000000000..3ec16da3f7 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_mount.c @@ -0,0 +1,53 @@ +/* + * IMFS_mount + * + * This routine will look at a mount table entry that we are going to + * add to the mount table. If the mount point rtems_filesystem + * location_info_t struct refers to a node that is a directory, + * the node will be marked as a mount point by setting its directory.mt_fs + * pointer to point to the mount table entry that we are about to add + * to the mount table chain. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> + +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int IMFS_mount( + rtems_filesystem_mount_table_entry_t *mt_entry +) +{ + IMFS_jnode_t *node; + + node = mt_entry->mt_point_node.node_access; + + /* + * Is the node that we are mounting onto a directory node ? + */ + + if ( node->type != IMFS_DIRECTORY ) + rtems_set_errno_and_return_minus_one( ENOTDIR ); + + /* + * Set mt_fs pointer to point to the mount table entry for + * the mounted file system. + */ + + node->info.directory.mt_fs = mt_entry; + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_ntype.c b/cpukit/libfs/src/imfs/imfs_ntype.c new file mode 100644 index 0000000000..f80182b144 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_ntype.c @@ -0,0 +1,32 @@ +/* + * IMFS_node_type + * + * The following verifies that returns the type of node that the + * loc refers to. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include "imfs.h" + +rtems_filesystem_node_types_t IMFS_node_type( + rtems_filesystem_location_info_t *pathloc /* IN */ +) +{ + IMFS_jnode_t *node; + + node = pathloc->node_access; + return node->type; +} diff --git a/cpukit/libfs/src/imfs/imfs_readlink.c b/cpukit/libfs/src/imfs/imfs_readlink.c new file mode 100644 index 0000000000..b598fcf2c1 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_readlink.c @@ -0,0 +1,43 @@ +/* + * IMFS_readlink + * + * The following rouine puts the symblic links destination name into + * buff. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +ssize_t IMFS_readlink( + rtems_filesystem_location_info_t *loc, + char *buf, /* OUT */ + size_t bufsize +) +{ + IMFS_jnode_t *node; + ssize_t i; + + node = loc->node_access; + + IMFS_assert( node->type == IMFS_SYM_LINK ); + + for( i=0; ((i<bufsize) && (node->info.sym_link.name[i] != '\0')); i++ ) + buf[i] = node->info.sym_link.name[i]; + + return i; +} diff --git a/cpukit/libfs/src/imfs/imfs_rename.c b/cpukit/libfs/src/imfs/imfs_rename.c new file mode 100644 index 0000000000..f45aa5b694 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_rename.c @@ -0,0 +1,54 @@ +/* + * IMFS_rename + * + * The following rouine creates a new link node under parent with the + * name given in name and removes the old. + * + * COPYRIGHT (c) 1989-2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int IMFS_rename( + rtems_filesystem_location_info_t *old_parent_loc, /* IN */ + rtems_filesystem_location_info_t *old_loc, /* IN */ + rtems_filesystem_location_info_t *new_parent_loc, /* IN */ + const char *new_name /* IN */ +) +{ + IMFS_jnode_t *the_jnode; + IMFS_jnode_t *new_parent; + + the_jnode = old_loc->node_access; + + strncpy( the_jnode->name, new_name, IMFS_NAME_MAX ); + + if ( the_jnode->Parent != NULL ) + rtems_chain_extract( (rtems_chain_node *) the_jnode ); + + new_parent = new_parent_loc->node_access; + the_jnode->Parent = new_parent; + + rtems_chain_append( &new_parent->info.directory.Entries, &the_jnode->Node ); + + /* + * Update the time. + */ + IMFS_update_ctime( the_jnode ); + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_rmnod.c b/cpukit/libfs/src/imfs/imfs_rmnod.c new file mode 100644 index 0000000000..25c7cde084 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_rmnod.c @@ -0,0 +1,77 @@ +/* + * IMFS Node Removal Handler + * + * This file contains the handler used to remove a node when a file type + * does not require special actions. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/libio_.h> + +#include "imfs.h" + +void IMFS_create_orphan( IMFS_jnode_t *jnode ) +{ + if ( jnode->Parent != NULL ) { + rtems_chain_extract( &jnode->Node ); + jnode->Parent = NULL; + } + + --jnode->st_nlink; + + IMFS_update_ctime( jnode ); +} + +void IMFS_check_node_remove( IMFS_jnode_t *jnode ) +{ + if ( !rtems_libio_is_file_open( jnode ) && jnode->st_nlink < 1 ) { + if ( rtems_filesystem_current.node_access == jnode ) + rtems_filesystem_current.node_access = NULL; + + switch ( jnode->type ) { + case IMFS_MEMORY_FILE: + IMFS_memfile_remove( jnode ); + break; + case IMFS_SYM_LINK: + free( jnode->info.sym_link.name ); + break; + default: + break; + } + + free( jnode ); + } +} + +/* + * IMFS_rmnod + */ + +int IMFS_rmnod( + rtems_filesystem_location_info_t *parent_pathloc, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN */ +) +{ + IMFS_jnode_t *jnode = (IMFS_jnode_t *) pathloc->node_access; + + IMFS_create_orphan( jnode ); + IMFS_check_node_remove( jnode ); + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_stat.c b/cpukit/libfs/src/imfs/imfs_stat.c new file mode 100644 index 0000000000..29867faa7b --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_stat.c @@ -0,0 +1,81 @@ +/* + * IMFS_stat + * + * This routine provides a stat for the IMFS file system. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int IMFS_stat( + rtems_filesystem_location_info_t *loc, + struct stat *buf +) +{ + IMFS_fs_info_t *fs_info; + IMFS_jnode_t *the_jnode; + IMFS_device_t *io; + + the_jnode = loc->node_access; + + + switch ( the_jnode->type ) { + + case IMFS_DEVICE: + io = &the_jnode->info.device; + buf->st_rdev = rtems_filesystem_make_dev_t( io->major, io->minor ); + break; + + case IMFS_LINEAR_FILE: + case IMFS_MEMORY_FILE: + buf->st_size = the_jnode->info.file.size; + break; + + case IMFS_SYM_LINK: + buf->st_size = 0; + break; + + case IMFS_FIFO: + buf->st_size = 0; + break; + + default: + rtems_set_errno_and_return_minus_one( ENOTSUP ); + break; + } + + /* + * The device number of the IMFS is the major number and the minor is the + * instance. + */ + fs_info = loc->mt_entry->fs_info; + buf->st_dev = + rtems_filesystem_make_dev_t( IMFS_DEVICE_MAJOR_NUMBER, fs_info->instance ); + + buf->st_mode = the_jnode->st_mode; + buf->st_nlink = the_jnode->st_nlink; + buf->st_ino = the_jnode->st_ino; + buf->st_uid = the_jnode->st_uid; + buf->st_gid = the_jnode->st_gid; + + buf->st_atime = the_jnode->stat_atime; + buf->st_mtime = the_jnode->stat_mtime; + buf->st_ctime = the_jnode->stat_ctime; + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_symlink.c b/cpukit/libfs/src/imfs/imfs_symlink.c new file mode 100644 index 0000000000..7094f219fb --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_symlink.c @@ -0,0 +1,77 @@ +/* + * IMFS_symlink + * + * The following rouine creates a new symbolic link node under parent + * with the name given in name. The node is set to point to the node at + * to_loc. + * + * COPYRIGHT (c) 1989-2009. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int IMFS_symlink( + rtems_filesystem_location_info_t *parent_loc, + const char *link_name, + const char *node_name +) +{ + IMFS_types_union info; + IMFS_jnode_t *new_node; + char new_name[ IMFS_NAME_MAX + 1 ]; + int i; + + /* + * Remove any separators at the end of the string. + */ + IMFS_get_token( node_name, strlen( node_name ), new_name, &i ); + + /* + * Duplicate link name + */ + info.sym_link.name = strdup(link_name); + if (info.sym_link.name == NULL) { + rtems_set_errno_and_return_minus_one(ENOMEM); + } + + /* + * Create a new link node. + * + * NOTE: Coverity CID 22 notes this as a resource leak. + * While technically not a leak, it indicated that IMFS_create_node + * was ONLY passed a NULL when we created the root node. We + * added a new IMFS_create_root_node() so this path no longer + * existed. The result was simpler code which should not have + * this path. + */ + new_node = IMFS_create_node( + parent_loc, + IMFS_SYM_LINK, + new_name, + ( S_IFLNK | ( S_IRWXU | S_IRWXG | S_IRWXO )), + &info + ); + + if (new_node == NULL) { + free(info.sym_link.name); + rtems_set_errno_and_return_minus_one(ENOMEM); + } + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_unlink.c b/cpukit/libfs/src/imfs/imfs_unlink.c new file mode 100644 index 0000000000..0ec176ed03 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_unlink.c @@ -0,0 +1,82 @@ +/* + * IMFS_unlink + * + * Routine to remove a link node from the tree. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> + +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int IMFS_unlink( + rtems_filesystem_location_info_t *parentloc, /* IN */ + rtems_filesystem_location_info_t *loc /* IN */ +) +{ + IMFS_jnode_t *node; + rtems_filesystem_location_info_t the_link; + int result = 0; + + node = loc->node_access; + + /* + * Decrement the link counter of node pointed to and free the + * space. + */ + + /* + * If this is the last last pointer to the node + * free the node. + */ + + if ( node->type == IMFS_HARD_LINK ) { + + if ( !node->info.hard_link.link_node ) + rtems_set_errno_and_return_minus_one( EINVAL ); + + the_link = *loc; + the_link.node_access = node->info.hard_link.link_node; + IMFS_Set_handlers( &the_link ); + + /* + * If removing the last hard link to a node, then we need + * to remove the node that is a link and the node itself. + */ + + if ( node->info.hard_link.link_node->st_nlink == 1) + { + result = (*the_link.handlers->rmnod_h)( parentloc, &the_link ); + if ( result != 0 ) + return -1; + } + else + { + node->info.hard_link.link_node->st_nlink --; + IMFS_update_ctime( node->info.hard_link.link_node ); + } + } + + /* + * Now actually free the node we were asked to free. + */ + + result = (*loc->handlers->rmnod_h)( parentloc, loc ); + + return result; +} diff --git a/cpukit/libfs/src/imfs/imfs_unmount.c b/cpukit/libfs/src/imfs/imfs_unmount.c new file mode 100644 index 0000000000..ee1482bfa5 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_unmount.c @@ -0,0 +1,62 @@ +/* + * IMFS_unmount + * + * This routine will look at a mount table entry that we are going to + * add to the mount table. If the mount point + * rtems_filesystem_location_info_t struct refers to a node that is a + * directory that has a file system mounted on it, the node will be + * marked as a mount point by * setting its directory.mt_fs pointer + * to NULL. This indicates that a directory is no longer mounted on + * this node. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> + +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +int IMFS_unmount( + rtems_filesystem_mount_table_entry_t *mt_entry +) +{ + IMFS_jnode_t *node; + + node = mt_entry->mt_point_node.node_access; + + /* + * Is the node that we are mounting onto a directory node ? + */ + + if ( node->type != IMFS_DIRECTORY ) + rtems_set_errno_and_return_minus_one( ENOTDIR ); + + /* + * Did the node indicate that there was a directory mounted here? + */ + + if ( node->info.directory.mt_fs == NULL ) + rtems_set_errno_and_return_minus_one( EINVAL ); /* XXX */ + + /* + * Set the mt_fs pointer to indicate that there is no longer + * a file system mounted to this point. + */ + + node->info.directory.mt_fs = NULL; + + return 0; +} diff --git a/cpukit/libfs/src/imfs/imfs_utime.c b/cpukit/libfs/src/imfs/imfs_utime.c new file mode 100644 index 0000000000..2867e13ed6 --- /dev/null +++ b/cpukit/libfs/src/imfs/imfs_utime.c @@ -0,0 +1,42 @@ +/* + * IMFS_utime + * + * This routine is the implementation of the utime() system + * call for the IMFS. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <sys/time.h> + +#include <rtems/libio_.h> +#include "imfs.h" + +int IMFS_utime( + rtems_filesystem_location_info_t *pathloc, /* IN */ + time_t actime, /* IN */ + time_t modtime /* IN */ +) +{ + IMFS_jnode_t *the_jnode; + + the_jnode = (IMFS_jnode_t *) pathloc->node_access; + + the_jnode->stat_atime = actime; + the_jnode->stat_mtime = modtime; + the_jnode->stat_ctime = time( NULL ); + + return 0; +} diff --git a/cpukit/libfs/src/imfs/ioman.c b/cpukit/libfs/src/imfs/ioman.c new file mode 100644 index 0000000000..fa9918b266 --- /dev/null +++ b/cpukit/libfs/src/imfs/ioman.c @@ -0,0 +1,93 @@ +/* + * This file emulates the old Classic RTEMS IO manager directives + * which register and lookup names using the in-memory filesystem. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + +#include <rtems.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> +#include "imfs.h" + +/* + * rtems_io_register_name + * + * This assumes that all registered devices are character devices. + */ + +rtems_status_code rtems_io_register_name( + const char *device_name, + rtems_device_major_number major, + rtems_device_minor_number minor +) +{ + int status; + dev_t dev; + + dev = rtems_filesystem_make_dev_t( major, minor ); + status = mknod( device_name, 0777 | S_IFCHR, dev ); + + /* this is the only error returned by the old version */ + if ( status ) + return RTEMS_TOO_MANY; + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_io_lookup_name + * + * This version is reentrant. + * + * XXX - This is dependent upon IMFS and should not be. + * Suggest adding a filesystem routine to fill in the device_info. + */ + +rtems_status_code rtems_io_lookup_name( + const char *name, + rtems_driver_name_t *device_info +) +{ + IMFS_jnode_t *the_jnode; + rtems_filesystem_location_info_t loc; + int result; + rtems_filesystem_node_types_t node_type; + + result = rtems_filesystem_evaluate_path( + name, strlen( name ), 0x00, &loc, true ); + the_jnode = loc.node_access; + + node_type = (*loc.ops->node_type_h)( &loc ); + + if ( (result != 0) || node_type != RTEMS_FILESYSTEM_DEVICE ) { + rtems_filesystem_freenode( &loc ); + return RTEMS_UNSATISFIED; + } + + device_info->device_name = (char *) name; + device_info->device_name_length = strlen( name ); + device_info->major = the_jnode->info.device.major; + device_info->minor = the_jnode->info.device.minor; + + rtems_filesystem_freenode( &loc ); + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libfs/src/imfs/memfile.c b/cpukit/libfs/src/imfs/memfile.c new file mode 100644 index 0000000000..98616d1b36 --- /dev/null +++ b/cpukit/libfs/src/imfs/memfile.c @@ -0,0 +1,975 @@ +/* + * IMFS Device Node Handlers + * + * This file contains the set of handlers used to process operations on + * IMFS memory file nodes. The memory files are created in memory using + * malloc'ed memory. Thus any data stored in one of these files is lost + * at system shutdown unless special arrangements to copy the data to + * some type of non-volailte storage are made by the application. + * + * COPYRIGHT (c) 1989-2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/libio.h> +#include "imfs.h" +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +#define MEMFILE_STATIC + +/* + * Prototypes of private routines + */ +MEMFILE_STATIC int IMFS_memfile_extend( + IMFS_jnode_t *the_jnode, + off_t new_length +); + +MEMFILE_STATIC int IMFS_memfile_addblock( + IMFS_jnode_t *the_jnode, + unsigned int block +); + +MEMFILE_STATIC int IMFS_memfile_remove_block( + IMFS_jnode_t *the_jnode, + unsigned int block +); + +MEMFILE_STATIC block_p *IMFS_memfile_get_block_pointer( + IMFS_jnode_t *the_jnode, + unsigned int block, + int malloc_it +); + +MEMFILE_STATIC ssize_t IMFS_memfile_read( + IMFS_jnode_t *the_jnode, + off_t start, + unsigned char *destination, + unsigned int length +); + +ssize_t IMFS_memfile_write( /* cannot be static as used in imfs_fchmod.c */ + IMFS_jnode_t *the_jnode, + off_t start, + const unsigned char *source, + unsigned int length +); + +void *memfile_alloc_block(void); + +void memfile_free_block( + void *memory +); + +/* + * memfile_open + * + * This routine processes the open() system call. Note that there is + * nothing special to be done at open() time. + */ +int memfile_open( + rtems_libio_t *iop, + const char *pathname, + uint32_t flag, + uint32_t mode +) +{ + IMFS_jnode_t *the_jnode; + + the_jnode = iop->pathinfo.node_access; + + /* + * Perform 'copy on write' for linear files + */ + if ((iop->flags & (LIBIO_FLAGS_WRITE | LIBIO_FLAGS_APPEND)) + && (the_jnode->type == IMFS_LINEAR_FILE)) { + uint32_t count = the_jnode->info.linearfile.size; + const unsigned char *buffer = the_jnode->info.linearfile.direct; + + the_jnode->type = IMFS_MEMORY_FILE; + the_jnode->info.file.size = 0; + the_jnode->info.file.indirect = 0; + the_jnode->info.file.doubly_indirect = 0; + the_jnode->info.file.triply_indirect = 0; + if ((count != 0) + && (IMFS_memfile_write(the_jnode, 0, buffer, count) == -1)) + return -1; + } + if (iop->flags & LIBIO_FLAGS_APPEND) + iop->offset = the_jnode->info.file.size; + + iop->size = the_jnode->info.file.size; + return 0; +} + +/* + * memfile_close + * + * This routine processes the close() system call. Note that there is + * nothing to flush or memory to free at this point. + */ +int memfile_close( + rtems_libio_t *iop +) +{ + IMFS_jnode_t *the_jnode; + + the_jnode = iop->pathinfo.node_access; + + if (iop->flags & LIBIO_FLAGS_APPEND) + iop->offset = the_jnode->info.file.size; + + IMFS_check_node_remove( the_jnode ); + + return 0; +} + +/* + * memfile_read + * + * This routine processes the read() system call. + */ +ssize_t memfile_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + IMFS_jnode_t *the_jnode; + + the_jnode = iop->pathinfo.node_access; + + return IMFS_memfile_read( the_jnode, iop->offset, buffer, count ); +} + +/* + * memfile_write + * + * This routine processes the write() system call. + */ +ssize_t memfile_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ + IMFS_jnode_t *the_jnode; + ssize_t status; + + the_jnode = iop->pathinfo.node_access; + + status = IMFS_memfile_write( the_jnode, iop->offset, buffer, count ); + iop->size = the_jnode->info.file.size; + + return status; +} + +/* + * memfile_ioctl + * + * This routine processes the ioctl() system call. + * + * NOTE: No ioctl()'s are supported for in-memory files. + */ +int memfile_ioctl( + rtems_libio_t *iop, + uint32_t command, + void *buffer +) +{ + IMFS_jnode_t *the_jnode; + + the_jnode = iop->pathinfo.node_access; + + return 0; +} + +/* + * memfile_lseek + * + * This routine processes the lseek() system call. + */ +rtems_off64_t memfile_lseek( + rtems_libio_t *iop, + rtems_off64_t offset, + int whence +) +{ + IMFS_jnode_t *the_jnode; + + the_jnode = iop->pathinfo.node_access; + + if (the_jnode->type == IMFS_LINEAR_FILE) { + if (iop->offset > the_jnode->info.linearfile.size) + iop->offset = the_jnode->info.linearfile.size; + } + else { /* Must be a block file (IMFS_MEMORY_FILE). */ + if (IMFS_memfile_extend( the_jnode, iop->offset )) + rtems_set_errno_and_return_minus_one( ENOSPC ); + + iop->size = the_jnode->info.file.size; + } + return iop->offset; +} + +/* + * memfile_stat + * + * This IMFS_stat() can be used. + */ + +/* + * memfile_ftruncate + * + * This routine processes the ftruncate() system call. + */ +int memfile_ftruncate( + rtems_libio_t *iop, + rtems_off64_t length +) +{ + IMFS_jnode_t *the_jnode; + + the_jnode = iop->pathinfo.node_access; + + /* + * POSIX 1003.1b does not specify what happens if you truncate a file + * and the new length is greater than the current size. We treat this + * as an extend operation. + */ + + if ( length > the_jnode->info.file.size ) + return IMFS_memfile_extend( the_jnode, length ); + + /* + * The in-memory files do not currently reclaim memory until the file is + * deleted. So we leave the previously allocated blocks in place for + * future use and just set the length. + */ + the_jnode->info.file.size = length; + iop->size = the_jnode->info.file.size; + + IMFS_update_atime( the_jnode ); + + return 0; +} + +/* + * IMFS_memfile_extend + * + * This routine insures that the in-memory file is of the length + * specified. If necessary, it will allocate memory blocks to + * extend the file. + */ +MEMFILE_STATIC int IMFS_memfile_extend( + IMFS_jnode_t *the_jnode, + off_t new_length +) +{ + unsigned int block; + unsigned int new_blocks; + unsigned int old_blocks; + + /* + * Perform internal consistency checks + */ + IMFS_assert( the_jnode ); + IMFS_assert( the_jnode->type == IMFS_MEMORY_FILE ); + + /* + * Verify new file size is supported + */ + if ( new_length >= IMFS_MEMFILE_MAXIMUM_SIZE ) + rtems_set_errno_and_return_minus_one( EINVAL ); + + /* + * Verify new file size is actually larger than current size + */ + if ( new_length <= the_jnode->info.file.size ) + return 0; + + /* + * Calculate the number of range of blocks to allocate + */ + new_blocks = new_length / IMFS_MEMFILE_BYTES_PER_BLOCK; + old_blocks = the_jnode->info.file.size / IMFS_MEMFILE_BYTES_PER_BLOCK; + + /* + * Now allocate each of those blocks. + */ + for ( block=old_blocks ; block<=new_blocks ; block++ ) { + if ( IMFS_memfile_addblock( the_jnode, block ) ) { + for ( ; block>=old_blocks ; block-- ) { + IMFS_memfile_remove_block( the_jnode, block ); + } + rtems_set_errno_and_return_minus_one( ENOSPC ); + } + } + + /* + * Set the new length of the file. + */ + the_jnode->info.file.size = new_length; + return 0; +} + +/* + * IMFS_memfile_addblock + * + * This routine adds a single block to the specified in-memory file. + */ +MEMFILE_STATIC int IMFS_memfile_addblock( + IMFS_jnode_t *the_jnode, + unsigned int block +) +{ + block_p memory; + block_p *block_entry_ptr; + + IMFS_assert( the_jnode ); + IMFS_assert( the_jnode->type == IMFS_MEMORY_FILE ); + + /* + * Obtain the pointer for the specified block number + */ + block_entry_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 1 ); + if ( *block_entry_ptr ) + return 0; + + /* + * There is no memory for this block number so allocate it. + */ + memory = memfile_alloc_block(); + if ( !memory ) + return 1; + + *block_entry_ptr = memory; + return 0; +} + +/* + * IMFS_memfile_remove_block + * + * This routine removes the specified block from the in-memory file. + * + * NOTE: This is a support routine and is called only to remove + * the last block or set of blocks in a file. Removing a + * block from the middle of a file would be exceptionally + * dangerous and the results unpredictable. + */ +MEMFILE_STATIC int IMFS_memfile_remove_block( + IMFS_jnode_t *the_jnode, + unsigned int block +) +{ + block_p *block_ptr; + block_p ptr; + + block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); + IMFS_assert( block_ptr ); + + ptr = *block_ptr; + *block_ptr = 0; + memfile_free_block( ptr ); + + return 1; +} + +/* + * memfile_free_blocks_in_table + * + * This is a support routine for IMFS_memfile_remove. It frees all the + * blocks in one of the indirection tables. + */ +void memfile_free_blocks_in_table( + block_p **block_table, + int entries +) +{ + int i; + block_p *b; + + /* + * Perform internal consistency checks + */ + IMFS_assert( block_table ); + + /* + * Now go through all the slots in the table and free the memory. + */ + b = *block_table; + + for ( i=0 ; i<entries ; i++ ) { + if ( b[i] ) { + memfile_free_block( b[i] ); + b[i] = 0; + } + } + + /* + * Now that all the blocks in the block table are free, we can + * free the block table itself. + */ + memfile_free_block( *block_table ); + *block_table = 0; +} + +/* + * IMFS_memfile_remove + * + * This routine frees all memory associated with an in memory file. + * + * NOTE: This is an exceptionally conservative implementation. + * It will check EVERY pointer which is non-NULL and insure + * any child non-NULL pointers are freed. Optimistically, all that + * is necessary is to scan until a NULL pointer is found. There + * should be no allocated data past that point. + * + * In experimentation on the powerpc simulator, it was noted + * that using blocks which held 128 slots versus 16 slots made + * a significant difference in the performance of this routine. + * + * Regardless until the IMFS implementation is proven, it + * is better to stick to simple, easy to understand algorithms. + */ +int IMFS_memfile_remove( + IMFS_jnode_t *the_jnode +) +{ + IMFS_memfile_t *info; + int i; + int j; + unsigned int to_free; + block_p *p; + + /* + * Perform internal consistency checks + */ + IMFS_assert( the_jnode ); + IMFS_assert( the_jnode->type == IMFS_MEMORY_FILE ); + + /* + * Eventually this could be set smarter at each call to + * memfile_free_blocks_in_table to greatly speed this up. + */ + to_free = IMFS_MEMFILE_BLOCK_SLOTS; + + /* + * Now start freeing blocks in this order: + * + indirect + * + doubly indirect + * + triply indirect + */ + info = &the_jnode->info.file; + + if ( info->indirect ) { + memfile_free_blocks_in_table( &info->indirect, to_free ); + } + + if ( info->doubly_indirect ) { + for ( i=0 ; i<IMFS_MEMFILE_BLOCK_SLOTS ; i++ ) { + if ( info->doubly_indirect[i] ) { + memfile_free_blocks_in_table( + (block_p **)&info->doubly_indirect[i], to_free ); + } + } + memfile_free_blocks_in_table( &info->doubly_indirect, to_free ); + + } + + if ( info->triply_indirect ) { + for ( i=0 ; i<IMFS_MEMFILE_BLOCK_SLOTS ; i++ ) { + p = (block_p *) info->triply_indirect[i]; + if ( !p ) /* ensure we have a valid pointer */ + break; + for ( j=0 ; j<IMFS_MEMFILE_BLOCK_SLOTS ; j++ ) { + if ( p[j] ) { + memfile_free_blocks_in_table( (block_p **)&p[j], to_free); + } + } + memfile_free_blocks_in_table( + (block_p **)&info->triply_indirect[i], to_free ); + } + memfile_free_blocks_in_table( + (block_p **)&info->triply_indirect, to_free ); + } + + return 0; +} + +/* + * IMFS_memfile_read + * + * This routine read from memory file pointed to by the_jnode into + * the specified data buffer specified by destination. The file + * is NOT extended. An offset greater than the length of the file + * is considered an error. Read from an offset for more bytes than + * are between the offset and the end of the file will result in + * reading the data between offset and the end of the file (truncated + * read). + */ +MEMFILE_STATIC ssize_t IMFS_memfile_read( + IMFS_jnode_t *the_jnode, + off_t start, + unsigned char *destination, + unsigned int length +) +{ + block_p *block_ptr; + unsigned int block; + unsigned int my_length; + unsigned int to_copy = 0; + unsigned int last_byte; + unsigned int copied; + unsigned int start_offset; + unsigned char *dest; + + dest = destination; + + /* + * Perform internal consistency checks + */ + IMFS_assert( the_jnode ); + IMFS_assert( the_jnode->type == IMFS_MEMORY_FILE || + the_jnode->type != IMFS_LINEAR_FILE ); + IMFS_assert( dest ); + + /* + * Linear files (as created from a tar file are easier to handle + * than block files). + */ + my_length = length; + + if (the_jnode->type == IMFS_LINEAR_FILE) { + unsigned char *file_ptr; + + file_ptr = (unsigned char *)the_jnode->info.linearfile.direct; + + if (my_length > (the_jnode->info.linearfile.size - start)) + my_length = the_jnode->info.linearfile.size - start; + + memcpy(dest, &file_ptr[start], my_length); + + IMFS_update_atime( the_jnode ); + + return my_length; + } + + /* + * If the last byte we are supposed to read is past the end of this + * in memory file, then shorten the length to read. + */ + last_byte = start + length; + if ( last_byte > the_jnode->info.file.size ) + my_length = the_jnode->info.file.size - start; + + copied = 0; + + /* + * Three phases to the read: + * + possibly the last part of one block + * + all of zero of more blocks + * + possibly the first part of one block + */ + + /* + * Phase 1: possibly the last part of one block + */ + start_offset = start % IMFS_MEMFILE_BYTES_PER_BLOCK; + block = start / IMFS_MEMFILE_BYTES_PER_BLOCK; + if ( start_offset ) { + to_copy = IMFS_MEMFILE_BYTES_PER_BLOCK - start_offset; + if ( to_copy > my_length ) + to_copy = my_length; + block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); + if ( !block_ptr ) + return copied; + memcpy( dest, &(*block_ptr)[ start_offset ], to_copy ); + dest += to_copy; + block++; + my_length -= to_copy; + copied += to_copy; + } + + /* + * Phase 2: all of zero of more blocks + */ + to_copy = IMFS_MEMFILE_BYTES_PER_BLOCK; + while ( my_length >= IMFS_MEMFILE_BYTES_PER_BLOCK ) { + block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); + if ( !block_ptr ) + return copied; + memcpy( dest, &(*block_ptr)[ 0 ], to_copy ); + dest += to_copy; + block++; + my_length -= to_copy; + copied += to_copy; + } + + /* + * Phase 3: possibly the first part of one block + */ + IMFS_assert( my_length < IMFS_MEMFILE_BYTES_PER_BLOCK ); + + if ( my_length ) { + block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); + if ( !block_ptr ) + return copied; + memcpy( dest, &(*block_ptr)[ 0 ], my_length ); + copied += my_length; + } + + IMFS_update_atime( the_jnode ); + + return copied; +} + +/* + * IMFS_memfile_write + * + * This routine writes the specified data buffer into the in memory + * file pointed to by the_jnode. The file is extended as needed. + */ +MEMFILE_STATIC ssize_t IMFS_memfile_write( + IMFS_jnode_t *the_jnode, + off_t start, + const unsigned char *source, + unsigned int length +) +{ + block_p *block_ptr; + unsigned int block; + int status; + unsigned int my_length; + unsigned int to_copy = 0; + unsigned int last_byte; + unsigned int start_offset; + int copied; + const unsigned char *src; + + src = source; + + /* + * Perform internal consistency checks + */ + IMFS_assert( source ); + IMFS_assert( the_jnode ); + IMFS_assert( the_jnode->type == IMFS_MEMORY_FILE ); + + my_length = length; + /* + * If the last byte we are supposed to write is past the end of this + * in memory file, then extend the length. + */ + + last_byte = start + my_length; + if ( last_byte > the_jnode->info.file.size ) { + status = IMFS_memfile_extend( the_jnode, last_byte ); + if ( status ) + rtems_set_errno_and_return_minus_one( ENOSPC ); + } + + copied = 0; + + /* + * Three phases to the write: + * + possibly the last part of one block + * + all of zero of more blocks + * + possibly the first part of one block + */ + + /* + * Phase 1: possibly the last part of one block + */ + start_offset = start % IMFS_MEMFILE_BYTES_PER_BLOCK; + block = start / IMFS_MEMFILE_BYTES_PER_BLOCK; + if ( start_offset ) { + to_copy = IMFS_MEMFILE_BYTES_PER_BLOCK - start_offset; + if ( to_copy > my_length ) + to_copy = my_length; + block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); + if ( !block_ptr ) + return copied; + #if 0 + fprintf( + stderr, + "write %d at %d in %d: %*s\n", + to_copy, + start_offset, + block, + to_copy, + src + ); + #endif + memcpy( &(*block_ptr)[ start_offset ], src, to_copy ); + src += to_copy; + block++; + my_length -= to_copy; + copied += to_copy; + } + + /* + * Phase 2: all of zero of more blocks + */ + + to_copy = IMFS_MEMFILE_BYTES_PER_BLOCK; + while ( my_length >= IMFS_MEMFILE_BYTES_PER_BLOCK ) { + block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); + if ( !block_ptr ) + return copied; + #if 0 + fprintf(stdout, "write %d in %d: %*s\n", to_copy, block, to_copy, src ); + #endif + memcpy( &(*block_ptr)[ 0 ], src, to_copy ); + src += to_copy; + block++; + my_length -= to_copy; + copied += to_copy; + } + + /* + * Phase 3: possibly the first part of one block + */ + IMFS_assert( my_length < IMFS_MEMFILE_BYTES_PER_BLOCK ); + + to_copy = my_length; + if ( my_length ) { + block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); + if ( !block_ptr ) + return copied; + #if 0 + fprintf(stdout, "write %d in %d: %*s\n", to_copy, block, to_copy, src ); + #endif + memcpy( &(*block_ptr)[ 0 ], src, my_length ); + my_length = 0; + copied += to_copy; + } + + IMFS_mtime_ctime_update( the_jnode ); + + return copied; +} + +/* + * IMFS_memfile_get_block_pointer + * + * This routine looks up the block pointer associated with the given block + * number. If that block has not been allocated and "malloc_it" is + * TRUE, then the block is allocated. Otherwise, it is an error. + */ +#if 0 +block_p *IMFS_memfile_get_block_pointer_DEBUG( + IMFS_jnode_t *the_jnode, + unsigned int block, + int malloc_it +); + +block_p *IMFS_memfile_get_block_pointer( + IMFS_jnode_t *the_jnode, + unsigned int block, + int malloc_it +) +{ + block_p *p; + + p = IMFS_memfile_get_block_pointer_DEBUG( the_jnode, block, malloc_it ); + fprintf(stdout, "(%d -> %p) ", block, p ); + return p; +} + +block_p *IMFS_memfile_get_block_pointer_DEBUG( +#else +block_p *IMFS_memfile_get_block_pointer( +#endif + IMFS_jnode_t *the_jnode, + unsigned int block, + int malloc_it +) +{ + unsigned int my_block; + IMFS_memfile_t *info; + unsigned int singly; + unsigned int doubly; + unsigned int triply; + block_p *p; + block_p *p1; + block_p *p2; + + /* + * Perform internal consistency checks + */ + IMFS_assert( the_jnode ); + IMFS_assert( the_jnode->type == IMFS_MEMORY_FILE ); + + info = &the_jnode->info.file; + my_block = block; + + /* + * Is the block number in the simple indirect portion? + */ + if ( my_block <= LAST_INDIRECT ) { + p = info->indirect; + + if ( malloc_it ) { + + if ( !p ) { + p = memfile_alloc_block(); + if ( !p ) + return 0; + info->indirect = p; + } + return &info->indirect[ my_block ]; + } + + if ( !p ) + return 0; + + return &info->indirect[ my_block ]; + } + + /* + * Is the block number in the doubly indirect portion? + */ + + if ( my_block <= LAST_DOUBLY_INDIRECT ) { + my_block -= FIRST_DOUBLY_INDIRECT; + + singly = my_block % IMFS_MEMFILE_BLOCK_SLOTS; + doubly = my_block / IMFS_MEMFILE_BLOCK_SLOTS; + + p = info->doubly_indirect; + if ( malloc_it ) { + + if ( !p ) { + p = memfile_alloc_block(); + if ( !p ) + return 0; + info->doubly_indirect = p; + } + + p1 = (block_p *)p[ doubly ]; + if ( !p1 ) { + p1 = memfile_alloc_block(); + if ( !p1 ) + return 0; + p[ doubly ] = (block_p) p1; + } + + return (block_p *)&p1[ singly ]; + } + + if ( !p ) + return 0; + + p = (block_p *)p[ doubly ]; + if ( !p ) + return 0; + + return (block_p *)&p[ singly ]; + } + + /* + * Is the block number in the triply indirect portion? + */ + if ( my_block <= LAST_TRIPLY_INDIRECT ) { + my_block -= FIRST_TRIPLY_INDIRECT; + + singly = my_block % IMFS_MEMFILE_BLOCK_SLOTS; + doubly = my_block / IMFS_MEMFILE_BLOCK_SLOTS; + triply = doubly / IMFS_MEMFILE_BLOCK_SLOTS; + doubly %= IMFS_MEMFILE_BLOCK_SLOTS; + + p = info->triply_indirect; + + if ( malloc_it ) { + if ( !p ) { + p = memfile_alloc_block(); + if ( !p ) + return 0; + info->triply_indirect = p; + } + + p1 = (block_p *) p[ triply ]; + if ( !p1 ) { + p1 = memfile_alloc_block(); + if ( !p1 ) + return 0; + p[ triply ] = (block_p) p1; + } + + p2 = (block_p *)p1[ doubly ]; + if ( !p2 ) { + p2 = memfile_alloc_block(); + if ( !p2 ) + return 0; + p1[ doubly ] = (block_p) p2; + } + return (block_p *)&p2[ singly ]; + } + + if ( !p ) + return 0; + + p1 = (block_p *) p[ triply ]; + if ( !p1 ) + return 0; + + p2 = (block_p *)p1[ doubly ]; + if ( !p2 ) + return 0; + + return (block_p *)&p2[ singly ]; + } + + /* + * This means the requested block number is out of range. + */ + return 0; +} + +/* + * memfile_alloc_block + * + * Allocate a block for an in-memory file. + */ +int memfile_blocks_allocated = 0; + +void *memfile_alloc_block(void) +{ + void *memory; + + memory = (void *)calloc(1, IMFS_MEMFILE_BYTES_PER_BLOCK); + if ( memory ) + memfile_blocks_allocated++; + + return memory; +} + +/* + * memfile_free_block + * + * Free a block from an in-memory file. + */ +void memfile_free_block( + void *memory +) +{ + free(memory); + memfile_blocks_allocated--; +} diff --git a/cpukit/libfs/src/imfs/miniimfs_init.c b/cpukit/libfs/src/imfs/miniimfs_init.c new file mode 100644 index 0000000000..b43a5318e8 --- /dev/null +++ b/cpukit/libfs/src/imfs/miniimfs_init.c @@ -0,0 +1,61 @@ +/** + * @file + * + * @ingroup LibFSIMFS + * + * @brief Mini-IMFS initialization. + */ + +/* + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/libio_.h> + +#include "imfs.h" + +static const rtems_filesystem_operations_table miniIMFS_ops = { + .evalpath_h = IMFS_eval_path, + .evalformake_h = IMFS_evaluate_for_make, + .link_h = rtems_filesystem_default_link, + .unlink_h = rtems_filesystem_default_unlink, + .node_type_h = IMFS_node_type, + .mknod_h = IMFS_mknod, + .chown_h = rtems_filesystem_default_chown, + .freenod_h = rtems_filesystem_default_freenode, + .mount_h = IMFS_mount, + .fsmount_me_h = miniIMFS_initialize, + .unmount_h = rtems_filesystem_default_unmount, + .fsunmount_me_h = rtems_filesystem_default_unmount, + .utime_h = rtems_filesystem_default_utime, + .eval_link_h = rtems_filesystem_default_evaluate_link, + .symlink_h = rtems_filesystem_default_symlink, + .readlink_h = rtems_filesystem_default_readlink, + .rename_h = rtems_filesystem_default_rename, + .statvfs_h = rtems_filesystem_default_statvfs +}; + +int miniIMFS_initialize( + rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data +) +{ + return IMFS_initialize_support( + mt_entry, + &miniIMFS_ops, + &rtems_filesystem_handlers_default, /* for memfiles */ + &rtems_filesystem_handlers_default, /* for directories */ + &rtems_filesystem_handlers_default /* for fifos */ + ); +} diff --git a/cpukit/libfs/src/nfsclient/.cvsignore b/cpukit/libfs/src/nfsclient/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/cpukit/libfs/src/nfsclient/ChangeLog.slac b/cpukit/libfs/src/nfsclient/ChangeLog.slac new file mode 100644 index 0000000000..94dea06763 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/ChangeLog.slac @@ -0,0 +1,112 @@ +Changes since RTEMS-NFS 1.4: +LICENSE: + - changed license terms; RTEMS-NFS is now released under the more liberal + 'SLAC license'. +NFS: + - silenced compiler warnings (follow handler declaration changes in libio.h + by using 'size_t' instead of 'uint32_t' etc.). + +Changes since RTEMS-NFS 1.3: +RPCIOD: + - round timeout to next system clock tick and ensure it is at least + 1 tick. + - cap retransmission timeout to transaction lifetime. + - BUGFIX (reported by Steven Johnson, 12/5/06): we must change the XID + before sending a timed-out transaction back to the requestor to prevent + a late reply from being accepted. + - Made task priority run-time configurable (and read from rtems_bsdnet_config + by default). + +NFS: + - added nfsGetTimeout() and nfsSetTimeout() to retrieve and modify, + respectively the (global) timeout for NFS/MOUNT operations. + - Minor mod. to fix 'type-punned pointer' warning (can avoid pointer cast). + - Added global variable 'nfsStBlksize' (defaults to 8k) which is now + used to report the file system's 'preferred' blocksize (stat(2)/st_blksize). + The old behavior (server's fattr.st_blksize is passed through) can be + obtained by setting nfsStBlksize=0. + The new feature lets stdio use 8k buffers (only if newlib was built with + HAVE_BLKSIZE defined). This enhances NFS (buffered) read performance quite + a bit. Thanks to Steven Johnson <sjohnson@sakuraindustries.com> for helping + with this. + - Updated README (performance section). + - Added simple performance test: nfsTest.c + +Changes since RTEMS-NFS 1.3_pre2: + RPCIOD: + - fix a problem with NFS server clusters (reply comes from an IP address + different from the destination of the request) by relaxing paranoia checks. + +Changes since RTEMS-NFS 1.2: + NFS: + - replaced inet_aton -> inet_pton + - replaced unsigned32 -> uint32_t + - added _KERNEL definition for 4.7 compilation + - silenced compiler warnings (4.7) + - added -Wno-unused-variable in 'proto' -- rpcgen produces a lot of them. + - new locking scheme. The 'in-use' counters cannot be protected by a mutex + because files might be closed when a thread is deleted from a dispatch-disabled + section where mutexes must not be locked. The counters are now protected by + disabling interrupts. + The only critical race-condition I can see ATM is while the NFS is being + unmounted and the mount point is crossed by another thread. It should be the + generic FS code's responsibility to handle that (but AFAIK, it doesn't) -- + it's out of our scope... + - ftruncate didn't work. The functionality is achieved by nfs_sattr() + setting the file size to 0. However, nfs_sattr() always tried to set + all attributes (re-applying the current values to fields we didn't + want to change) which failed (EPERM) if we were not the owner. + Now, we restrict modifications to the requested fields (in case of + ftruncate this is *only* the size), adhering to rfc1094 (which states + that unused fields shall be set to -1). + - lseek(SEEK_END) didn't work. The underlying RTEMS filesystem code + uses an internal file 'size' field to compute the offset whence SEEK_END. + Instead of painfully maintaining 'size' across all system calls, we + just tweak the offset for SEEK_END and leave 'size' unused. + - fix: O_APPEND wasn't honoured. Note that there is no NFS 'append' call - + the client simply uses the currently available idea of the file size + to set the write offset. This obviously is subject to race conditions + if multiple clients are writing the same file. + dirutils: + - replaced read/write calls by stdio; In case of copying to stdout, I + experienced occasional crashes when write(fileno(stdout),...) -- according + to the standard, mixing low-level i/o with stdio might produce undefined + results; there we go... + +Changes since RTEMS-NFS 1.1: + NFS: + - unlink() didnt work. The underlying RTEMS filesystem code evaluates + a '..' path on a non-directory node to find out the file's parent + directory. Workaround to this semantically inelegant RTEMS feature + was implemented. + +Changes since RTEMS-NFS 1.0.beta3: + NFS: + - fixed possible string overrun in nfsMount + - nfs_read_dir() must reset the 'eofreached' flag if it skipped + dirents present in the xdr but not fitting into the user buffer. + - nfsMountsShow() released the wrong lock! + RPCIO: + - cannot delete locked binary semaphore (authlock) -- must unlock + first (authlock was never deleted and hence effectively leaked) + - added ASSERT paranoia around mutex primitives + - Relaxed paranoia check / ASSERTion failure: + paranoia_free() is called more than once on an ext_buf - it must + undo calls to paranoia_refcnt() - hence the 0 == --refcnt check + is too strict. + - Added a DEBUG flag to introduce random packet losses for testing + retransmission. + xdr_mbuf: + - make sure we do a signed comparison + +Changes since rtemsNFS-1.0.beta2: + - moved 'tar' command to the 'config' area; use + predefined 'make-tar' in individual Makefiles + - use INSTALL_CHANGE for headers, not INSTALL_VARIANT (probably doesn't + matter, though) + - use LD not LD_FOR_TARGET (to get absolute path) + - fixed assertion failure print format + - print requestor id if send_event fails - had just experienced this :-( + - hint about fprintf using FP registers is probably PPC specific + - provided implementation for xdrmbuf_getlong_aligned(). i386-rtems + seems to use it. diff --git a/cpukit/libfs/src/nfsclient/LICENSE b/cpukit/libfs/src/nfsclient/LICENSE new file mode 100644 index 0000000000..4687f9a22f --- /dev/null +++ b/cpukit/libfs/src/nfsclient/LICENSE @@ -0,0 +1,44 @@ +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ diff --git a/cpukit/libfs/src/nfsclient/Makefile.am b/cpukit/libfs/src/nfsclient/Makefile.am new file mode 100644 index 0000000000..98ba99286c --- /dev/null +++ b/cpukit/libfs/src/nfsclient/Makefile.am @@ -0,0 +1,74 @@ +# +# $Id$ +# + +include $(top_srcdir)/automake/compile.am + +#%dirutils.obj: %dirutils.o +# $(LD) -r -o $@ $^ +project_lib_PROGRAMS = dirutils.rel +dirutils_rel_SOURCES = src/dirutils.c +dirutils_rel_CPPFLAGS = $(AM_CPPFLAGS) +dirutils_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +if LIBNETWORKING +project_lib_LIBRARIES = libnfs.a + +# if you have CEXP set this variable to 'YES' +# and some "help" info will be compiled in. +libnfs_a_SOURCES = src/rpcio.c src/rpcio.h +libnfs_a_SOURCES += src/nfs.c src/sock_mbuf.c src/xdr_mbuf.c +libnfs_a_SOURCES += proto/mount_prot.h proto/nfs_prot.h + +# With CEXP +# libnfs_a_SOURCES += dirutils.c rpcio.modini.c nfs.modini.c cexphelp.c + +include_HEADERS = src/librtemsNfs.h + +# PGMS=${ARCH}/nfs.obj ${ARCH}/rpcio.obj ${ARCH}/dirutils.obj + +libnfs_a_CPPFLAGS = $(AM_CPPFLAGS) +libnfs_a_CPPFLAGS += -I$(srcdir)/proto +libnfs_a_CPPFLAGS += -UHAVE_CEXP + +#%nfs.obj: %nfs.o %nfs.modini.o +# $(LD) -r -o $@ $^ -L../proto/$(ARCH) -lnfsprot +project_lib_PROGRAMS += nfs.rel +nfs_rel_SOURCES = src/nfs.c proto/nfs_prot.h proto/mount_prot.h +nfs_rel_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/proto +nfs_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) +nfs_rel_LDADD = libnfs.a + +#%rpcio.obj: %rpcio.o %sock_mbuf.o %xdr_mbuf.o %rpcio.modini.o +# $(LD) -r -o $@ $^ +project_lib_PROGRAMS += rpcio.rel +rpcio_rel_SOURCES = src/rpcio.c src/sock_mbuf.c src/xdr_mbuf.c +rpcio_rel_CPPFLAGS = $(AM_CPPFLAGS) +rpcio_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +libnfs_a_SOURCES += proto/nfs_prot_xdr.c proto/nfs_prot.h +libnfs_a_SOURCES += proto/mount_prot_xdr.c proto/mount_prot.h + +libnfs_a_CPPFLAGS += -I$(srcdir)/proto +libnfs_a_CPPFLAGS += -UHAVE_CEXP +endif + + +if RPCTOOLS +$(srcdir)/proto/mount_prot.h: proto/mount_prot.x + (cd $(srcdir)/proto && $(RPCGEN) -h -C mount_prot.x > mount_prot.h) + +$(srcdir)/proto/mount_prot_xdr.c: proto/mount_prot.x proto/mount_prot.h + (cd $(srcdir)/proto && $(RPCGEN) -c -C mount_prot.x \ + > mount_prot_xdr.c) + +$(srcdir)/proto/nfs_prot.h: proto/nfs_prot.x + (cd $(srcdir)/proto && $(RPCGEN) -h -C nfs_prot.x > nfs_prot.h) + +$(srcdir)/proto/nfs_prot_xdr.c: proto/nfs_prot.x proto/nfs_prot.h + (cd $(srcdir)/proto && $(RPCGEN) -c -C nfs_prot.x \ + > nfs_prot_xdr.c) +endif + +include $(srcdir)/preinstall.am +include $(top_srcdir)/automake/local.am diff --git a/cpukit/libfs/src/nfsclient/README b/cpukit/libfs/src/nfsclient/README new file mode 100644 index 0000000000..944b830e2e --- /dev/null +++ b/cpukit/libfs/src/nfsclient/README @@ -0,0 +1,548 @@ +RTEMS-NFS +========= + +A NFS-V2 client implementation for the RTEMS real-time +executive. + +Author: Till Straumann <strauman@slac.stanford.edu>, 2002 + +Copyright 2002, Stanford University and + Till Straumann <strauman@slac.stanford.edu> + +Stanford Notice +*************** + +Acknowledgement of sponsorship +* * * * * * * * * * * * * * * * +This software was produced by the Stanford Linear Accelerator Center, +Stanford University, under Contract DE-AC03-76SFO0515 with the Department +of Energy. + + +Contents +-------- +I Overview + 1) Performance + 2) Reference Platform / Test Environment + +II Usage + 1) Initialization + 2) Mounting Remote Server Filesystems + 3) Unmounting + 4) Unloading + 5) Dumping Information / Statistics + +III Implementation Details + 1) RPCIOD + 2) NFS + 3) RTEMS Resources Used By NFS/RPCIOD + 4) Caveats & Bugs + +IV Licensing & Disclaimers + +I Overview +----------- + +This package implements a simple non-caching NFS +client for RTEMS. Most of the system calls are +supported with the exception of 'mount', i.e. it +is not possible to mount another FS on top of NFS +(mostly because of the difficulty that arises when +mount points are deleted on the server). It +shouldn't be hard to do, though. + +Note: this client supports NFS vers. 2 / MOUNT vers. 1; + NFS Version 3 or higher are NOT supported. + +The package consists of two modules: RPCIOD and NFS +itself. + + - RPCIOD is a UDP/RPC multiplexor daemon. It takes + RPC requests from multiple local client threads, + funnels them through a single socket to multiple + servers and dispatches the replies back to the + (blocked) requestor threads. + RPCIOD does packet retransmission and handles + timeouts etc. + Note however, that it does NOT do any XDR + marshalling - it is up to the requestor threads + to do the XDR encoding/decoding. RPCIOD _is_ RPC + specific, though, because its message dispatching + is based on the RPC transaction ID. + + - The NFS package maps RTEMS filesystem calls + to proper RPCs, it does the XDR work and + hands marshalled RPC requests to RPCIOD. + All of the calls are synchronous, i.e. they + block until they get a reply. + +1) Performance +- - - - - - - - +Performance sucks (due to the lack of +readahead/delayed write and caching). On a fast +(100Mb/s) ethernet, it takes about 20s to copy a +10MB file from NFS to NFS. I found, however, that +vxWorks' NFS client doesn't seem to be any +faster... + +Since there is no buffer cache with read-ahead +implemented, all NFS reads are synchronous RPC +calls. Every read operation involves sending a +request and waiting for the reply. As long as the +overhead (sending request + processing it on the +server) is significant compared to the time it +takes to transferring the actual data, increasing +the amount of data per request results in better +throughput. The UDP packet size limit imposes a +limit of 8k per RPC call, hence reading from NFS +in chunks of 8k is better than chunks of 1k [but +chunks >8k are not possible, i.e., simply not +honoured: read(a_nfs_fd, buf, 20000) returns +8192]. This is similar to the old linux days +(mount with rsize=8k). You can let stdio take +care of the buffering or use 8k buffers with +explicit read(2) operations. Note that stdio +honours the file-system's st_blksize field +if newlib is compiled with HAVE_BLKSIZE defined. +In this case, stdio uses 8k buffers for files +on NFS transparently. The blocksize NFS +reports can be tuned with a global variable +setting (see nfs.c for details). + +Further increase of throughput can be achieved +with read-ahead (issuing RPC calls in parallel +[send out request for block n+1 while you are +waiting for data of block n to arrive]). Since +this is not handled by the file system itself, you +would have to code this yourself e.g., using +parallel threads to read from a single file from +interleaved offsets. + +Another obvious improvement can be achieved if +processing the data takes a significant amount of +time. Then, having a pipeline of threads for +reading data and processing them makes sense +[thread b processes chunk n while thread a blocks +in read(chunk n+1)]. + +Some performance figures: +Software: src/nfsTest.c:nfsReadTest() [data not + processed in any way]. +Hardware: MVME6100 +Network: 100baseT-FD +Server: Linux-2.6/RHEL4-smp [dell precision 420] +File: 10MB + +Results: +Single threaded ('normal') NFS read, 1k buffers: 3.46s (2.89MB/s) +Single threaded ('normal') NFS read, 8k buffers: 1.31s (7.63MB/s) +Multi threaded; 2 readers, 8k buffers/xfers: 1.12s (8.9 MB/s) +Multi threaded; 3 readers, 8k buffers/xfers: 1.04s (9.6 MB/s) + +2) Reference Platform +- - - - - - - - - - - +RTEMS-NFS was developed and tested on + + o RTEMS-ss20020301 (local patches applied) + o PowerPC G3, G4 on Synergy SVGM series board + (custom 'SVGM' BSP, to be released soon) + o PowerPC 604 on MVME23xx + (powerpc/shared/motorola-powerpc BSP) + o Test Environment: + - RTEMS executable running CEXP + - rpciod/nfs dynamically loaded from TFTPfs + - EPICS application dynamically loaded from NFS; + the executing IOC accesses all of its files + on NFS. + +II Usage +--------- + +After linking into the system and proper initialization +(rtems-NFS supports 'magic' module initialization when +loaded into a running system with the CEXP loader), +you are ready for mounting NFSes from a server +(I avoid the term NFS filesystem because NFS already +stands for 'Network File System'). + +You should also read the + + - "RTEMS Resources Used By NFS/RPCIOD" + - "CAVEATS & BUGS" + +below. + +1) Initialization +- - - - - - - - - +NFS consists of two modules who must be initialized: + + a) the RPCIO daemon package; by calling + + rpcUdpInit(); + + note that this step must be performed prior to + initializing NFS: + + b) NFS is initialized by calling + + nfsInit( smallPoolDepth, bigPoolDepth ); + + if you supply 0 (zero) values for the pool + depths, the compile-time default configuration + is used which should work fine. + +NOTE: when using CEXP to load these modules into a +running system, initialization will be performed +automagically. + +2) Mounting Remote Server Filesystems +- - - - - - - - - - - - - - - - - - - + +There are two interfaces for mounting an NFS: + + - The (non-POSIX) RTEMS 'mount()' call: + + mount( &mount_table_entry_pointer, + &filesystem_operations_table_pointer, + options, + device, + mount_point ) + + Note that you must specify a 'mount_table_entry_pointer' + (use a dummy) - RTEMS' mount() doesn't grok a NULL for + the first argument. + + o for the 'filesystem_operations_table_pointer', supply + + &nfs_fs_ops + + o options are constants (see RTEMS headers) for specifying + read-only / read-write mounts. + + o the 'device' string specifies the remote filesystem + who is to be mounted. NFS expects a string conforming + to the following format (EBNF syntax): + + [ <uid> '.' <gid> '@' ] <hostip> ':' <path> + + The first optional part of the string allows you + to specify the credentials to be used for all + subsequent transactions with this server. If the + string is omitted, the EUID/EGID of the executing + thread (i.e. the thread performing the 'mount' - + NFS will still 'remember' these values and use them + for all future communication with this server). + + The <hostip> part denotes the server IP address + in standard 'dot' notation. It is followed by + a colon and the (absolute) path on the server. + Note that no extra characters or whitespace must + be present in the string. Example 'device' strings + are: + + "300.99@192.168.44.3:/remote/rtems/root" + + "192.168.44.3:/remote/rtems/root" + + o the 'mount_point' string identifies the local + directory (most probably on IMFS) where the NFS + is to be mounted. Note that the mount point must + already exist with proper permissions. + + - Alternate 'mount' interface. NFS offers a more + convenient wrapper taking three string arguments: + + nfsMount(uidgid_at_host, server_path, mount_point) + + This interface does DNS lookup (see reentrancy note + below) and creates the mount point if necessary. + + o the first argument specifies the server and + optionally the uid/gid to be used for authentication. + The semantics are exactly as described above: + + [ <uid> '.' <gid> '@' ] <host> + + The <host> part may be either a host _name_ or + an IP address in 'dot' notation. In the former + case, nfsMount() uses 'gethostbyname()' to do + a DNS lookup. + + IMPORTANT NOTE: gethostbyname() is NOT reentrant/ + thread-safe and 'nfsMount()' (if not provided with an + IP/dot address string) is hence subject to race conditions. + + o the 'server_path' and 'mount_point' arguments + are described above. + NOTE: If the mount point does not exist yet, + nfsMount() tries to create it. + + o if nfsMount() is called with a NULL 'uidgid_at_host' + argument, it lists all currently mounted NFS + +3) Unmounting +- - - - - - - +An NFS can be unmounted using RTEMS 'unmount()' +call (yep, it is unmount() - not umount()): + + unmount(mount_point) + +Note that you _must_ supply the mount point (string +argument). It is _not_ possible to specify the +'mountee' when unmounting. NFS implements no +convenience wrapper for this (yet), essentially because +(although this sounds unbelievable) it is non-trivial +to lookup the path leading to an RTEMS filesystem +directory node. + +4) Unloading +- - - - - - - +After unmounting all NFS from the system, the NFS +and RPCIOD modules may be stopped and unloaded. +Just call 'nfsCleanup()' and 'rpcUdpCleanup()' +in this order. You should evaluate the return value +of these routines which is non-zero if either +of them refuses to yield (e.g. because there are +still mounted filesystems). +Again, when unloading is done by CEXP this is +transparently handled. + +5) Dumping Information / Statistics +- - - - - - - - - - - - - - - - - - + +Rudimentary RPCIOD statistics are printed +to a file (stdout when NULL) by + + int rpcUdpStats(FILE *f) + +A list of all currently mounted NFS can be +printed to a file (stdout if NULL) using + + int nfsMountsShow(FILE *f) + +For convenience, this routine is also called +by nfsMount() when supplying NULL arguments. + +III Implementation Details +-------------------------- + +1) RPCIOD +- - - - - + +RPCIOD was created to + +a) avoid non-reentrant librpc calls. +b) support 'asynchronous' operation over a single + socket. + +RPCIOD is a daemon thread handling 'transaction objects' +(XACTs) through an UDP socket. XACTs are marshalled RPC +calls/replies associated with RPC servers and requestor +threads. + +requestor thread: network: + + XACT packet + | | + V V + | message queue | ( socket ) + | | ^ + ----------> <----- | | + RPCIOD | + / -------------- + timeout/ (re) transmission + + +A requestor thread drops a transaction into +the message queue and goes to sleep. The XACT is +picked up by rpciod who is listening for events from +three sources: + + o the request queue + o packet arrival at the socket + o timeouts + +RPCIOD sends the XACT to its destination server and +enqueues the pending XACT into an ordered list of +outstanding transactions. + +When a packet arrives, RPCIOD (based on the RPC transaction +ID) looks up the matching XACT and wakes up the requestor +who can then XDR-decode the RPC results found in the XACT +object's buffer. + +When a timeout expires, RPCIOD examines the outstanding +XACT that is responsible for the timeout. If its lifetime +has not expired yet, RPCIOD resends the request. Otherwise, +the XACT's error status is set and the requestor is woken up. + +RPCIOD dynamically adjusts the retransmission intervals +based on the average round-trip time measured (on a per-server +basis). + +Having the requestors event driven (rather than blocking +e.g. on a semaphore) is geared to having many different +requestors (one synchronization object per requestor would +be needed otherwise). + +Requestors who want to do asynchronous IO need a different +interface which will be added in the future. + +1.a) Reentrancy +- - - - - - - - +RPCIOD does no non-reentrant librpc calls. + +1.b) Efficiency +- - - - - - - - +We shouldn't bother about efficiency until pipelining (read-ahead/ +delayed write) and caching are implemented. The round-trip delay +associated with every single RPC transaction clearly is a big +performance killer. + +Nevertheless, I could not withstand the temptation to eliminate +the extra copy step involved with socket IO: + +A user data object has to be XDR encoded into a buffer. The +buffer given to the socket where it is copied into MBUFs. +(The network chip driver might even do more copying). + +Likewise, on reception 'recvfrom' copies MBUFS into a user +buffer which is XDR decoded into the final user data object. + +Eliminating the copying into (possibly multiple) MBUFS by +'sendto()' is actually a piece of cake. RPCIOD uses the +'sosend()' routine [properly wrapped] supplying a single +MBUF header who directly points to the marshalled buffer +:-) + +Getting rid of the extra copy on reception was (only a little) +harder: I derived a 'XDR-mbuf' stream from SUN's xdr_mem which +allows for XDR-decoding out of a MBUF chain who is obtained by +soreceive(). + +2) NFS +- - - - +The actual NFS implementation is straightforward and essentially +'passive' (no threads created). Any RTEMS task executing a +filesystem call dispatched to NFS (such as 'opendir()', 'lseek()' +or 'unlink()') ends up XDR encoding arguments, dropping a +XACT into RPCIOD's message queue and going to sleep. +When woken up by RPCIOD, the XACT is decoded (using the XDR-mbuf +stream mentioned above) and the properly cooked-up results are +returned. + +3) RTEMS Resources Used By NFS/RPCIOD +- - - - - - - - - - - - - - - - - - - + +The RPCIOD/NFS package uses the following resources. Some +parameters are compile-time configurable - consult the +source files for details. + +RPCIOD: + o 1 task + o 1 message queue + o 1 socket/filedescriptor + o 2 semaphores (a third one is temporarily created during + rpcUdpCleanup()). + o 1 RTEMS EVENT (by default RTEMS_EVENT_30). + IMPORTANT: this event is used by _every_ thread executing + NFS system calls and hence is RESERVED. + o 3 events only used by RPCIOD itself, i.e. these must not + be sent to RPCIOD by no other thread (except for the intended + use, of course). The events involved are 1,2,3. + o preemption disabled sections: NONE + o sections with interrupts disabled: NONE + o NO 'timers' are used (timer code would run in IRQ context) + o memory usage: n.a + +NFS: + o 2 message queues + o 2 semaphores + o 1 semaphore per mounted NFS + o 1 slot in driver entry table (for major number) + o preemption disabled sections: NONE + o sections with interrupts disabled: NONE + o 1 task + 1 semaphore temporarily created when + listing mounted filesystems (rtems_filesystem_resolve_location()) + +4) CAVEATS & BUGS +- - - - - - - - - +Unfortunately, some bugs crawl around in the filesystem generics. +(Some of them might already be fixed in versions later than +rtems-ss-20020301). +I recommend to use the patch distributed with RTEMS-NFS. + + o RTEMS uses/used (Joel said it has been fixed already) a 'short' + ino_t which is not enough for NFS. + The driver detects this problem and enables a workaround. In rare + situations (mainly involving 'getcwd()' improper inode comparison + may result (due to the restricted size, stat() returns st_ino modulo + 2^16). In most cases, however, st_dev is compared along with st_ino + which will give correct results (different files may yield identical + st_ino but they will have different st_dev). However, there is + code (in getcwd(), for example) who assumes that files residing + in one directory must be hosted by the same device and hence omits + the st_dev comparison. In such a case, the workaround will fail. + + NOTE: changing the size (sys/types.h) of ino_t from 'short' to 'long' + is strongly recommended. It is NOT included in the patch, however + as this is a major change requiring ALL of your sources to + be recompiled. + + THE ino_t SIZE IS FIXED IN GCC-3.2/NEWLIB-1.10.0-2 DISTRIBUTED BY + OAR. + + o You may work around most filesystem bugs by observing the following + rules: + + * never use chroot() (fixed by the patch) + * never use getpwent(), getgrent() & friends - they are NOT THREAD + safe (fixed by the patch) + * NEVER use rtems_libio_share_private_env() - not even with the + patch applied. Just DONT - it is broken by design. + * All threads who have their own userenv (who have called + rtems_libio_set_private_env()) SHOULD 'chdir("/")' before + terminating. Otherwise, (i.e. if their cwd is on NFS), it will + be impossible to unmount the NFS involved. + + o The patch slightly changes the semantics of 'getpwent()' and + 'getgrent()' & friends (to what is IMHO correct anyways - the patch is + also needed to fix another problem, however): with the patch applied, + the passwd and group files are always accessed from the 'current' user + environment, i.e. a thread who has changed its 'root' or 'uid' might + not be able to access these files anymore. + + o NOTE: RTEMS 'mount()' / 'unmount()' are NOT THREAD SAFE. + + o The NFS protocol has no 'append' or 'seek_end' primitive. The client + must query the current file size (this client uses cached info) and + change the local file pointer accordingly (in 'O_APPEND' mode). + Obviously, this involves a race condition and hence multiple clients + writing the same file may lead to corruption. + +IV Licensing & Disclaimers +-------------------------- + +NFS is distributed under the SLAC License - consult the +separate 'LICENSE' file. + +Government disclaimer of liability +- - - - - - - - - - - - - - - - - +Neither the United States nor the United States Department of Energy, +nor any of their employees, makes any warranty, express or implied, +or assumes any legal liability or responsibility for the accuracy, +completeness, or usefulness of any data, apparatus, product, or process +disclosed, or represents that its use would not infringe privately +owned rights. + +Stanford disclaimer of liability +- - - - - - - - - - - - - - - - - +Stanford University makes no representations or warranties, express or +implied, nor assumes any liability for the use of this software. + +Maintenance of notice +- - - - - - - - - - - +In the interest of clarity regarding the origin and status of this +software, Stanford University requests that any recipient of it maintain +this notice affixed to any distribution by the recipient that contains a +copy or derivative of this software. diff --git a/cpukit/libfs/src/nfsclient/preinstall.am b/cpukit/libfs/src/nfsclient/preinstall.am new file mode 100644 index 0000000000..c65f7f76dc --- /dev/null +++ b/cpukit/libfs/src/nfsclient/preinstall.am @@ -0,0 +1,51 @@ +## Automatically generated by ampolish3 - Do not edit + +if AMPOLISH3 +$(srcdir)/preinstall.am: Makefile.am + $(AMPOLISH3) $(srcdir)/Makefile.am > $(srcdir)/preinstall.am +endif + +PREINSTALL_DIRS = +DISTCLEANFILES = $(PREINSTALL_DIRS) + +all-local: $(TMPINSTALL_FILES) + +TMPINSTALL_FILES = +CLEANFILES = $(TMPINSTALL_FILES) + +all-am: $(PREINSTALL_FILES) + +PREINSTALL_FILES = +CLEANFILES += $(PREINSTALL_FILES) + +$(PROJECT_LIB)/$(dirstamp): + @$(MKDIR_P) $(PROJECT_LIB) + @: > $(PROJECT_LIB)/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_LIB)/$(dirstamp) + +$(PROJECT_INCLUDE)/$(dirstamp): + @$(MKDIR_P) $(PROJECT_INCLUDE) + @: > $(PROJECT_INCLUDE)/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/$(dirstamp) + +$(PROJECT_LIB)/dirutils.rel: dirutils.rel $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_PROGRAM) $< $(PROJECT_LIB)/dirutils.rel +TMPINSTALL_FILES += $(PROJECT_LIB)/dirutils.rel + +if LIBNETWORKING +$(PROJECT_LIB)/libnfs.a: libnfs.a $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_LIB)/libnfs.a +TMPINSTALL_FILES += $(PROJECT_LIB)/libnfs.a + +$(PROJECT_INCLUDE)/librtemsNfs.h: src/librtemsNfs.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/librtemsNfs.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/librtemsNfs.h + +$(PROJECT_LIB)/nfs.rel: nfs.rel $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_PROGRAM) $< $(PROJECT_LIB)/nfs.rel +TMPINSTALL_FILES += $(PROJECT_LIB)/nfs.rel + +$(PROJECT_LIB)/rpcio.rel: rpcio.rel $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_PROGRAM) $< $(PROJECT_LIB)/rpcio.rel +TMPINSTALL_FILES += $(PROJECT_LIB)/rpcio.rel +endif diff --git a/cpukit/libfs/src/nfsclient/proto/mount_prot.h b/cpukit/libfs/src/nfsclient/proto/mount_prot.h new file mode 100644 index 0000000000..1cde517aea --- /dev/null +++ b/cpukit/libfs/src/nfsclient/proto/mount_prot.h @@ -0,0 +1,144 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#ifndef _MOUNT_PROT_H_RPCGEN +#define _MOUNT_PROT_H_RPCGEN + +#include <rpc/rpc.h> + + +#ifdef __cplusplus +extern "C" { +#endif + +#define MNTPATHLEN 1024 +#define MNTNAMLEN 255 +#define FHSIZE 32 + +typedef char fhandle[FHSIZE]; + +struct fhstatus { + u_int fhs_status; + union { + fhandle fhs_fhandle; + } fhstatus_u; +}; +typedef struct fhstatus fhstatus; + +typedef char *dirpath; + +typedef char *name; + +typedef struct mountbody *mountlist; + +struct mountbody { + name ml_hostname; + dirpath ml_directory; + mountlist ml_next; +}; +typedef struct mountbody mountbody; + +typedef struct groupnode *groups; + +struct groupnode { + name gr_name; + groups gr_next; +}; +typedef struct groupnode groupnode; + +typedef struct exportnode *exports; + +struct exportnode { + dirpath ex_dir; + groups ex_groups; + exports ex_next; +}; +typedef struct exportnode exportnode; + +#define MOUNTPROG 100005 +#define MOUNTVERS 1 + +#if defined(__STDC__) || defined(__cplusplus) +#define MOUNTPROC_NULL 0 +extern void * mountproc_null_1(void *, CLIENT *); +extern void * mountproc_null_1_svc(void *, struct svc_req *); +#define MOUNTPROC_MNT 1 +extern fhstatus * mountproc_mnt_1(dirpath *, CLIENT *); +extern fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *); +#define MOUNTPROC_DUMP 2 +extern mountlist * mountproc_dump_1(void *, CLIENT *); +extern mountlist * mountproc_dump_1_svc(void *, struct svc_req *); +#define MOUNTPROC_UMNT 3 +extern void * mountproc_umnt_1(dirpath *, CLIENT *); +extern void * mountproc_umnt_1_svc(dirpath *, struct svc_req *); +#define MOUNTPROC_UMNTALL 4 +extern void * mountproc_umntall_1(void *, CLIENT *); +extern void * mountproc_umntall_1_svc(void *, struct svc_req *); +#define MOUNTPROC_EXPORT 5 +extern exports * mountproc_export_1(void *, CLIENT *); +extern exports * mountproc_export_1_svc(void *, struct svc_req *); +#define MOUNTPROC_EXPORTALL 6 +extern exports * mountproc_exportall_1(void *, CLIENT *); +extern exports * mountproc_exportall_1_svc(void *, struct svc_req *); +extern int mountprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t); + +#else /* K&R C */ +#define MOUNTPROC_NULL 0 +extern void * mountproc_null_1(); +extern void * mountproc_null_1_svc(); +#define MOUNTPROC_MNT 1 +extern fhstatus * mountproc_mnt_1(); +extern fhstatus * mountproc_mnt_1_svc(); +#define MOUNTPROC_DUMP 2 +extern mountlist * mountproc_dump_1(); +extern mountlist * mountproc_dump_1_svc(); +#define MOUNTPROC_UMNT 3 +extern void * mountproc_umnt_1(); +extern void * mountproc_umnt_1_svc(); +#define MOUNTPROC_UMNTALL 4 +extern void * mountproc_umntall_1(); +extern void * mountproc_umntall_1_svc(); +#define MOUNTPROC_EXPORT 5 +extern exports * mountproc_export_1(); +extern exports * mountproc_export_1_svc(); +#define MOUNTPROC_EXPORTALL 6 +extern exports * mountproc_exportall_1(); +extern exports * mountproc_exportall_1_svc(); +extern int mountprog_1_freeresult (); +#endif /* K&R C */ + +/* the xdr functions */ + +#if defined(__STDC__) || defined(__cplusplus) +extern bool_t xdr_fhandle (XDR *, fhandle); +extern bool_t xdr_fhstatus (XDR *, fhstatus*); +extern bool_t xdr_dirpath (XDR *, dirpath*); +extern bool_t xdr_name (XDR *, name*); +extern bool_t xdr_mountlist (XDR *, mountlist*); +extern bool_t xdr_mountbody (XDR *, mountbody*); +extern bool_t xdr_groups (XDR *, groups*); +extern bool_t xdr_groupnode (XDR *, groupnode*); +extern bool_t xdr_exports (XDR *, exports*); +extern bool_t xdr_exportnode (XDR *, exportnode*); + +#else /* K&R C */ +extern bool_t xdr_fhandle (); +extern bool_t xdr_fhstatus (); +extern bool_t xdr_dirpath (); +extern bool_t xdr_name (); +extern bool_t xdr_mountlist (); +extern bool_t xdr_mountbody (); +extern bool_t xdr_groups (); +extern bool_t xdr_groupnode (); +extern bool_t xdr_exports (); +extern bool_t xdr_exportnode (); + +#endif /* K&R C */ + +#ifdef __cplusplus +} +#endif + +#endif /* !_MOUNT_PROT_H_RPCGEN */ diff --git a/cpukit/libfs/src/nfsclient/proto/mount_prot.x b/cpukit/libfs/src/nfsclient/proto/mount_prot.x new file mode 100644 index 0000000000..7e0d7f3ad6 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/proto/mount_prot.x @@ -0,0 +1,161 @@ +/* @(#)mount.x 2.1 88/08/01 4.0 RPCSRC */ +/* @(#)mount.x 1.2 87/09/18 Copyr 1987 Sun Micro */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Protocol description for the mount program + */ + + +const MNTPATHLEN = 1024; /* maximum bytes in a pathname argument */ +const MNTNAMLEN = 255; /* maximum bytes in a name argument */ +const FHSIZE = 32; /* size in bytes of a file handle */ + +/* + * The fhandle is the file handle that the server passes to the client. + * All file operations are done using the file handles to refer to a file + * or a directory. The file handle can contain whatever information the + * server needs to distinguish an individual file. + */ +typedef opaque fhandle[FHSIZE]; + +/* + * If a status of zero is returned, the call completed successfully, and + * a file handle for the directory follows. A non-zero status indicates + * some sort of error. The status corresponds with UNIX error numbers. + */ +union fhstatus switch (unsigned fhs_status) { +case 0: + fhandle fhs_fhandle; +default: + void; +}; + +/* + * The type dirpath is the pathname of a directory + */ +typedef string dirpath<MNTPATHLEN>; + +/* + * The type name is used for arbitrary names (hostnames, groupnames) + */ +typedef string name<MNTNAMLEN>; + +/* + * A list of who has what mounted + */ +typedef struct mountbody *mountlist; +struct mountbody { + name ml_hostname; + dirpath ml_directory; + mountlist ml_next; +}; + +/* + * A list of netgroups + */ +typedef struct groupnode *groups; +struct groupnode { + name gr_name; + groups gr_next; +}; + +/* + * A list of what is exported and to whom + */ +typedef struct exportnode *exports; +struct exportnode { + dirpath ex_dir; + groups ex_groups; + exports ex_next; +}; + +program MOUNTPROG { + /* + * Version one of the mount protocol communicates with version two + * of the NFS protocol. The only connecting point is the fhandle + * structure, which is the same for both protocols. + */ + version MOUNTVERS { + /* + * Does no work. It is made available in all RPC services + * to allow server reponse testing and timing + */ + void + MOUNTPROC_NULL(void) = 0; + + /* + * If fhs_status is 0, then fhs_fhandle contains the + * file handle for the directory. This file handle may + * be used in the NFS protocol. This procedure also adds + * a new entry to the mount list for this client mounting + * the directory. + * Unix authentication required. + */ + fhstatus + MOUNTPROC_MNT(dirpath) = 1; + + /* + * Returns the list of remotely mounted filesystems. The + * mountlist contains one entry for each hostname and + * directory pair. + */ + mountlist + MOUNTPROC_DUMP(void) = 2; + + /* + * Removes the mount list entry for the directory + * Unix authentication required. + */ + void + MOUNTPROC_UMNT(dirpath) = 3; + + /* + * Removes all of the mount list entries for this client + * Unix authentication required. + */ + void + MOUNTPROC_UMNTALL(void) = 4; + + /* + * Returns a list of all the exported filesystems, and which + * machines are allowed to import it. + */ + exports + MOUNTPROC_EXPORT(void) = 5; + + /* + * Identical to MOUNTPROC_EXPORT above + */ + exports + MOUNTPROC_EXPORTALL(void) = 6; + } = 1; +} = 100005; diff --git a/cpukit/libfs/src/nfsclient/proto/mount_prot_xdr.c b/cpukit/libfs/src/nfsclient/proto/mount_prot_xdr.c new file mode 100644 index 0000000000..b439ef341f --- /dev/null +++ b/cpukit/libfs/src/nfsclient/proto/mount_prot_xdr.c @@ -0,0 +1,104 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include "mount_prot.h" + +bool_t +xdr_fhandle (XDR *xdrs, fhandle objp) +{ + if (!xdr_opaque (xdrs, objp, FHSIZE)) + return FALSE; + return TRUE; +} + +bool_t +xdr_fhstatus (XDR *xdrs, fhstatus *objp) +{ + if (!xdr_u_int (xdrs, &objp->fhs_status)) + return FALSE; + switch (objp->fhs_status) { + case 0: + if (!xdr_fhandle (xdrs, objp->fhstatus_u.fhs_fhandle)) + return FALSE; + break; + default: + break; + } + return TRUE; +} + +bool_t +xdr_dirpath (XDR *xdrs, dirpath *objp) +{ + if (!xdr_string (xdrs, objp, MNTPATHLEN)) + return FALSE; + return TRUE; +} + +bool_t +xdr_name (XDR *xdrs, name *objp) +{ + if (!xdr_string (xdrs, objp, MNTNAMLEN)) + return FALSE; + return TRUE; +} + +bool_t +xdr_mountlist (XDR *xdrs, mountlist *objp) +{ + if (!xdr_pointer (xdrs, (char **)objp, sizeof (struct mountbody), (xdrproc_t) xdr_mountbody)) + return FALSE; + return TRUE; +} + +bool_t +xdr_mountbody (XDR *xdrs, mountbody *objp) +{ + if (!xdr_name (xdrs, &objp->ml_hostname)) + return FALSE; + if (!xdr_dirpath (xdrs, &objp->ml_directory)) + return FALSE; + if (!xdr_mountlist (xdrs, &objp->ml_next)) + return FALSE; + return TRUE; +} + +bool_t +xdr_groups (XDR *xdrs, groups *objp) +{ + if (!xdr_pointer (xdrs, (char **)objp, sizeof (struct groupnode), (xdrproc_t) xdr_groupnode)) + return FALSE; + return TRUE; +} + +bool_t +xdr_groupnode (XDR *xdrs, groupnode *objp) +{ + if (!xdr_name (xdrs, &objp->gr_name)) + return FALSE; + if (!xdr_groups (xdrs, &objp->gr_next)) + return FALSE; + return TRUE; +} + +bool_t +xdr_exports (XDR *xdrs, exports *objp) +{ + if (!xdr_pointer (xdrs, (char **)objp, sizeof (struct exportnode), (xdrproc_t) xdr_exportnode)) + return FALSE; + return TRUE; +} + +bool_t +xdr_exportnode (XDR *xdrs, exportnode *objp) +{ + if (!xdr_dirpath (xdrs, &objp->ex_dir)) + return FALSE; + if (!xdr_groups (xdrs, &objp->ex_groups)) + return FALSE; + if (!xdr_exports (xdrs, &objp->ex_next)) + return FALSE; + return TRUE; +} diff --git a/cpukit/libfs/src/nfsclient/proto/nfs_prot.h b/cpukit/libfs/src/nfsclient/proto/nfs_prot.h new file mode 100644 index 0000000000..de812dbfcf --- /dev/null +++ b/cpukit/libfs/src/nfsclient/proto/nfs_prot.h @@ -0,0 +1,453 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#ifndef _NFS_PROT_H_RPCGEN +#define _NFS_PROT_H_RPCGEN + +#include <rpc/rpc.h> + + +#ifdef __cplusplus +extern "C" { +#endif + +#define NFS_PORT 2049 +#define NFS_MAXDATA 8192 +#define NFS_MAXPATHLEN 1024 +#define NFS_MAXNAMLEN 255 +#define NFS_FHSIZE 32 +#define NFS_COOKIESIZE 4 +#define NFS_FIFO_DEV -1 +#define NFSMODE_FMT 0170000 +#define NFSMODE_DIR 0040000 +#define NFSMODE_CHR 0020000 +#define NFSMODE_BLK 0060000 +#define NFSMODE_REG 0100000 +#define NFSMODE_LNK 0120000 +#define NFSMODE_SOCK 0140000 +#define NFSMODE_FIFO 0010000 + +enum nfsstat { + NFS_OK = 0, + NFSERR_PERM = 1, + NFSERR_NOENT = 2, + NFSERR_IO = 5, + NFSERR_NXIO = 6, + NFSERR_ACCES = 13, + NFSERR_EXIST = 17, + NFSERR_NODEV = 19, + NFSERR_NOTDIR = 20, + NFSERR_ISDIR = 21, + NFSERR_FBIG = 27, + NFSERR_NOSPC = 28, + NFSERR_ROFS = 30, + NFSERR_NAMETOOLONG = 63, + NFSERR_NOTEMPTY = 66, + NFSERR_DQUOT = 69, + NFSERR_STALE = 70, + NFSERR_WFLUSH = 99, +}; +typedef enum nfsstat nfsstat; + +enum ftype { + NFNON = 0, + NFREG = 1, + NFDIR = 2, + NFBLK = 3, + NFCHR = 4, + NFLNK = 5, + NFSOCK = 6, + NFBAD = 7, + NFFIFO = 8, +}; +typedef enum ftype ftype; + +struct nfs_fh { + char data[NFS_FHSIZE]; +}; +typedef struct nfs_fh nfs_fh; + +struct nfstime { + u_int seconds; + u_int useconds; +}; +typedef struct nfstime nfstime; + +struct fattr { + ftype type; + u_int mode; + u_int nlink; + u_int uid; + u_int gid; + u_int size; + u_int blocksize; + u_int rdev; + u_int blocks; + u_int fsid; + u_int fileid; + nfstime atime; + nfstime mtime; + nfstime ctime; +}; +typedef struct fattr fattr; + +struct sattr { + u_int mode; + u_int uid; + u_int gid; + u_int size; + nfstime atime; + nfstime mtime; +}; +typedef struct sattr sattr; + +typedef char *filename; + +typedef char *nfspath; + +struct attrstat { + nfsstat status; + union { + fattr attributes; + } attrstat_u; +}; +typedef struct attrstat attrstat; + +struct sattrargs { + nfs_fh file; + sattr attributes; +}; +typedef struct sattrargs sattrargs; + +struct diropargs { + nfs_fh dir; + filename name; +}; +typedef struct diropargs diropargs; + +struct diropokres { + nfs_fh file; + fattr attributes; +}; +typedef struct diropokres diropokres; + +struct diropres { + nfsstat status; + union { + diropokres diropres; + } diropres_u; +}; +typedef struct diropres diropres; + +struct readlinkres { + nfsstat status; + union { + nfspath data; + } readlinkres_u; +}; +typedef struct readlinkres readlinkres; + +struct readargs { + nfs_fh file; + u_int offset; + u_int count; + u_int totalcount; +}; +typedef struct readargs readargs; + +struct readokres { + fattr attributes; + struct { + u_int data_len; + char *data_val; + } data; +}; +typedef struct readokres readokres; + +struct readres { + nfsstat status; + union { + readokres reply; + } readres_u; +}; +typedef struct readres readres; + +struct writeargs { + nfs_fh file; + u_int beginoffset; + u_int offset; + u_int totalcount; + struct { + u_int data_len; + char *data_val; + } data; +}; +typedef struct writeargs writeargs; + +struct createargs { + diropargs where; + sattr attributes; +}; +typedef struct createargs createargs; + +struct renameargs { + diropargs from; + diropargs to; +}; +typedef struct renameargs renameargs; + +struct linkargs { + nfs_fh from; + diropargs to; +}; +typedef struct linkargs linkargs; + +struct symlinkargs { + diropargs from; + nfspath to; + sattr attributes; +}; +typedef struct symlinkargs symlinkargs; + +struct nfscookie { + char data[NFS_COOKIESIZE]; +}; +typedef struct nfscookie nfscookie; + +struct readdirargs { + nfs_fh dir; + nfscookie cookie; + u_int count; +}; +typedef struct readdirargs readdirargs; + +struct entry { + u_int fileid; + filename name; + nfscookie cookie; + struct entry *nextentry; +}; +typedef struct entry entry; + +struct dirlist { + entry *entries; + bool_t eof; +}; +typedef struct dirlist dirlist; + +struct readdirres { + nfsstat status; + union { + dirlist reply; + } readdirres_u; +}; +typedef struct readdirres readdirres; + +struct statfsokres { + u_int tsize; + u_int bsize; + u_int blocks; + u_int bfree; + u_int bavail; +}; +typedef struct statfsokres statfsokres; + +struct statfsres { + nfsstat status; + union { + statfsokres reply; + } statfsres_u; +}; +typedef struct statfsres statfsres; + +#define NFS_PROGRAM 100003 +#define NFS_VERSION 2 + +#if defined(__STDC__) || defined(__cplusplus) +#define NFSPROC_NULL 0 +extern void * nfsproc_null_2(void *, CLIENT *); +extern void * nfsproc_null_2_svc(void *, struct svc_req *); +#define NFSPROC_GETATTR 1 +extern attrstat * nfsproc_getattr_2(nfs_fh *, CLIENT *); +extern attrstat * nfsproc_getattr_2_svc(nfs_fh *, struct svc_req *); +#define NFSPROC_SETATTR 2 +extern attrstat * nfsproc_setattr_2(sattrargs *, CLIENT *); +extern attrstat * nfsproc_setattr_2_svc(sattrargs *, struct svc_req *); +#define NFSPROC_ROOT 3 +extern void * nfsproc_root_2(void *, CLIENT *); +extern void * nfsproc_root_2_svc(void *, struct svc_req *); +#define NFSPROC_LOOKUP 4 +extern diropres * nfsproc_lookup_2(diropargs *, CLIENT *); +extern diropres * nfsproc_lookup_2_svc(diropargs *, struct svc_req *); +#define NFSPROC_READLINK 5 +extern readlinkres * nfsproc_readlink_2(nfs_fh *, CLIENT *); +extern readlinkres * nfsproc_readlink_2_svc(nfs_fh *, struct svc_req *); +#define NFSPROC_READ 6 +extern readres * nfsproc_read_2(readargs *, CLIENT *); +extern readres * nfsproc_read_2_svc(readargs *, struct svc_req *); +#define NFSPROC_WRITECACHE 7 +extern void * nfsproc_writecache_2(void *, CLIENT *); +extern void * nfsproc_writecache_2_svc(void *, struct svc_req *); +#define NFSPROC_WRITE 8 +extern attrstat * nfsproc_write_2(writeargs *, CLIENT *); +extern attrstat * nfsproc_write_2_svc(writeargs *, struct svc_req *); +#define NFSPROC_CREATE 9 +extern diropres * nfsproc_create_2(createargs *, CLIENT *); +extern diropres * nfsproc_create_2_svc(createargs *, struct svc_req *); +#define NFSPROC_REMOVE 10 +extern nfsstat * nfsproc_remove_2(diropargs *, CLIENT *); +extern nfsstat * nfsproc_remove_2_svc(diropargs *, struct svc_req *); +#define NFSPROC_RENAME 11 +extern nfsstat * nfsproc_rename_2(renameargs *, CLIENT *); +extern nfsstat * nfsproc_rename_2_svc(renameargs *, struct svc_req *); +#define NFSPROC_LINK 12 +extern nfsstat * nfsproc_link_2(linkargs *, CLIENT *); +extern nfsstat * nfsproc_link_2_svc(linkargs *, struct svc_req *); +#define NFSPROC_SYMLINK 13 +extern nfsstat * nfsproc_symlink_2(symlinkargs *, CLIENT *); +extern nfsstat * nfsproc_symlink_2_svc(symlinkargs *, struct svc_req *); +#define NFSPROC_MKDIR 14 +extern diropres * nfsproc_mkdir_2(createargs *, CLIENT *); +extern diropres * nfsproc_mkdir_2_svc(createargs *, struct svc_req *); +#define NFSPROC_RMDIR 15 +extern nfsstat * nfsproc_rmdir_2(diropargs *, CLIENT *); +extern nfsstat * nfsproc_rmdir_2_svc(diropargs *, struct svc_req *); +#define NFSPROC_READDIR 16 +extern readdirres * nfsproc_readdir_2(readdirargs *, CLIENT *); +extern readdirres * nfsproc_readdir_2_svc(readdirargs *, struct svc_req *); +#define NFSPROC_STATFS 17 +extern statfsres * nfsproc_statfs_2(nfs_fh *, CLIENT *); +extern statfsres * nfsproc_statfs_2_svc(nfs_fh *, struct svc_req *); +extern int nfs_program_2_freeresult (SVCXPRT *, xdrproc_t, caddr_t); + +#else /* K&R C */ +#define NFSPROC_NULL 0 +extern void * nfsproc_null_2(); +extern void * nfsproc_null_2_svc(); +#define NFSPROC_GETATTR 1 +extern attrstat * nfsproc_getattr_2(); +extern attrstat * nfsproc_getattr_2_svc(); +#define NFSPROC_SETATTR 2 +extern attrstat * nfsproc_setattr_2(); +extern attrstat * nfsproc_setattr_2_svc(); +#define NFSPROC_ROOT 3 +extern void * nfsproc_root_2(); +extern void * nfsproc_root_2_svc(); +#define NFSPROC_LOOKUP 4 +extern diropres * nfsproc_lookup_2(); +extern diropres * nfsproc_lookup_2_svc(); +#define NFSPROC_READLINK 5 +extern readlinkres * nfsproc_readlink_2(); +extern readlinkres * nfsproc_readlink_2_svc(); +#define NFSPROC_READ 6 +extern readres * nfsproc_read_2(); +extern readres * nfsproc_read_2_svc(); +#define NFSPROC_WRITECACHE 7 +extern void * nfsproc_writecache_2(); +extern void * nfsproc_writecache_2_svc(); +#define NFSPROC_WRITE 8 +extern attrstat * nfsproc_write_2(); +extern attrstat * nfsproc_write_2_svc(); +#define NFSPROC_CREATE 9 +extern diropres * nfsproc_create_2(); +extern diropres * nfsproc_create_2_svc(); +#define NFSPROC_REMOVE 10 +extern nfsstat * nfsproc_remove_2(); +extern nfsstat * nfsproc_remove_2_svc(); +#define NFSPROC_RENAME 11 +extern nfsstat * nfsproc_rename_2(); +extern nfsstat * nfsproc_rename_2_svc(); +#define NFSPROC_LINK 12 +extern nfsstat * nfsproc_link_2(); +extern nfsstat * nfsproc_link_2_svc(); +#define NFSPROC_SYMLINK 13 +extern nfsstat * nfsproc_symlink_2(); +extern nfsstat * nfsproc_symlink_2_svc(); +#define NFSPROC_MKDIR 14 +extern diropres * nfsproc_mkdir_2(); +extern diropres * nfsproc_mkdir_2_svc(); +#define NFSPROC_RMDIR 15 +extern nfsstat * nfsproc_rmdir_2(); +extern nfsstat * nfsproc_rmdir_2_svc(); +#define NFSPROC_READDIR 16 +extern readdirres * nfsproc_readdir_2(); +extern readdirres * nfsproc_readdir_2_svc(); +#define NFSPROC_STATFS 17 +extern statfsres * nfsproc_statfs_2(); +extern statfsres * nfsproc_statfs_2_svc(); +extern int nfs_program_2_freeresult (); +#endif /* K&R C */ + +/* the xdr functions */ + +#if defined(__STDC__) || defined(__cplusplus) +extern bool_t xdr_nfsstat (XDR *, nfsstat*); +extern bool_t xdr_ftype (XDR *, ftype*); +extern bool_t xdr_nfs_fh (XDR *, nfs_fh*); +extern bool_t xdr_nfstime (XDR *, nfstime*); +extern bool_t xdr_fattr (XDR *, fattr*); +extern bool_t xdr_sattr (XDR *, sattr*); +extern bool_t xdr_filename (XDR *, filename*); +extern bool_t xdr_nfspath (XDR *, nfspath*); +extern bool_t xdr_attrstat (XDR *, attrstat*); +extern bool_t xdr_sattrargs (XDR *, sattrargs*); +extern bool_t xdr_diropargs (XDR *, diropargs*); +extern bool_t xdr_diropokres (XDR *, diropokres*); +extern bool_t xdr_diropres (XDR *, diropres*); +extern bool_t xdr_readlinkres (XDR *, readlinkres*); +extern bool_t xdr_readargs (XDR *, readargs*); +extern bool_t xdr_readokres (XDR *, readokres*); +extern bool_t xdr_readres (XDR *, readres*); +extern bool_t xdr_writeargs (XDR *, writeargs*); +extern bool_t xdr_createargs (XDR *, createargs*); +extern bool_t xdr_renameargs (XDR *, renameargs*); +extern bool_t xdr_linkargs (XDR *, linkargs*); +extern bool_t xdr_symlinkargs (XDR *, symlinkargs*); +extern bool_t xdr_nfscookie (XDR *, nfscookie*); +extern bool_t xdr_readdirargs (XDR *, readdirargs*); +extern bool_t xdr_entry (XDR *, entry*); +extern bool_t xdr_dirlist (XDR *, dirlist*); +extern bool_t xdr_readdirres (XDR *, readdirres*); +extern bool_t xdr_statfsokres (XDR *, statfsokres*); +extern bool_t xdr_statfsres (XDR *, statfsres*); + +#else /* K&R C */ +extern bool_t xdr_nfsstat (); +extern bool_t xdr_ftype (); +extern bool_t xdr_nfs_fh (); +extern bool_t xdr_nfstime (); +extern bool_t xdr_fattr (); +extern bool_t xdr_sattr (); +extern bool_t xdr_filename (); +extern bool_t xdr_nfspath (); +extern bool_t xdr_attrstat (); +extern bool_t xdr_sattrargs (); +extern bool_t xdr_diropargs (); +extern bool_t xdr_diropokres (); +extern bool_t xdr_diropres (); +extern bool_t xdr_readlinkres (); +extern bool_t xdr_readargs (); +extern bool_t xdr_readokres (); +extern bool_t xdr_readres (); +extern bool_t xdr_writeargs (); +extern bool_t xdr_createargs (); +extern bool_t xdr_renameargs (); +extern bool_t xdr_linkargs (); +extern bool_t xdr_symlinkargs (); +extern bool_t xdr_nfscookie (); +extern bool_t xdr_readdirargs (); +extern bool_t xdr_entry (); +extern bool_t xdr_dirlist (); +extern bool_t xdr_readdirres (); +extern bool_t xdr_statfsokres (); +extern bool_t xdr_statfsres (); + +#endif /* K&R C */ + +#ifdef __cplusplus +} +#endif + +#endif /* !_NFS_PROT_H_RPCGEN */ diff --git a/cpukit/libfs/src/nfsclient/proto/nfs_prot.x b/cpukit/libfs/src/nfsclient/proto/nfs_prot.x new file mode 100644 index 0000000000..a40d9a5f5f --- /dev/null +++ b/cpukit/libfs/src/nfsclient/proto/nfs_prot.x @@ -0,0 +1,1268 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#ifndef RPC_HDR +%#ifndef lint +%/*static char sccsid[] = "from: @(#)nfs_prot.x 1.2 87/10/12 Copyr 1987 Sun Micro";*/ +%/*static char sccsid[] = "from: @(#)nfs_prot.x 2.1 88/08/01 4.0 RPCSRC";*/ +%static char rcsid[] = "$Id$"; +%#endif /* not lint */ +#endif + +const NFS_PORT = 2049; +const NFS_MAXDATA = 8192; +const NFS_MAXPATHLEN = 1024; +const NFS_MAXNAMLEN = 255; +const NFS_FHSIZE = 32; +const NFS_COOKIESIZE = 4; +const NFS_FIFO_DEV = -1; /* size kludge for named pipes */ + +/* + * File types + */ +const NFSMODE_FMT = 0170000; /* type of file */ +const NFSMODE_DIR = 0040000; /* directory */ +const NFSMODE_CHR = 0020000; /* character special */ +const NFSMODE_BLK = 0060000; /* block special */ +const NFSMODE_REG = 0100000; /* regular */ +const NFSMODE_LNK = 0120000; /* symbolic link */ +const NFSMODE_SOCK = 0140000; /* socket */ +const NFSMODE_FIFO = 0010000; /* fifo */ + +/* + * Error status + */ +enum nfsstat { + NFS_OK= 0, /* no error */ + NFSERR_PERM=1, /* Not owner */ + NFSERR_NOENT=2, /* No such file or directory */ + NFSERR_IO=5, /* I/O error */ + NFSERR_NXIO=6, /* No such device or address */ + NFSERR_ACCES=13, /* Permission denied */ + NFSERR_EXIST=17, /* File exists */ + NFSERR_NODEV=19, /* No such device */ + NFSERR_NOTDIR=20, /* Not a directory*/ + NFSERR_ISDIR=21, /* Is a directory */ + NFSERR_FBIG=27, /* File too large */ + NFSERR_NOSPC=28, /* No space left on device */ + NFSERR_ROFS=30, /* Read-only file system */ + NFSERR_NAMETOOLONG=63, /* File name too long */ + NFSERR_NOTEMPTY=66, /* Directory not empty */ + NFSERR_DQUOT=69, /* Disc quota exceeded */ + NFSERR_STALE=70, /* Stale NFS file handle */ + NFSERR_WFLUSH=99 /* write cache flushed */ +}; + +/* + * File types + */ +enum ftype { + NFNON = 0, /* non-file */ + NFREG = 1, /* regular file */ + NFDIR = 2, /* directory */ + NFBLK = 3, /* block special */ + NFCHR = 4, /* character special */ + NFLNK = 5, /* symbolic link */ + NFSOCK = 6, /* unix domain sockets */ + NFBAD = 7, /* unused */ + NFFIFO = 8 /* named pipe */ +}; + +/* + * File access handle + */ +struct nfs_fh { + opaque data[NFS_FHSIZE]; +}; + +/* + * Timeval + */ +struct nfstime { + unsigned seconds; + unsigned useconds; +}; + + +/* + * File attributes + */ +struct fattr { + ftype type; /* file type */ + unsigned mode; /* protection mode bits */ + unsigned nlink; /* # hard links */ + unsigned uid; /* owner user id */ + unsigned gid; /* owner group id */ + unsigned size; /* file size in bytes */ + unsigned blocksize; /* prefered block size */ + unsigned rdev; /* special device # */ + unsigned blocks; /* Kb of disk used by file */ + unsigned fsid; /* device # */ + unsigned fileid; /* inode # */ + nfstime atime; /* time of last access */ + nfstime mtime; /* time of last modification */ + nfstime ctime; /* time of last change */ +}; + +/* + * File attributes which can be set + */ +struct sattr { + unsigned mode; /* protection mode bits */ + unsigned uid; /* owner user id */ + unsigned gid; /* owner group id */ + unsigned size; /* file size in bytes */ + nfstime atime; /* time of last access */ + nfstime mtime; /* time of last modification */ +}; + + +typedef string filename<NFS_MAXNAMLEN>; +typedef string nfspath<NFS_MAXPATHLEN>; + +/* + * Reply status with file attributes + */ +union attrstat switch (nfsstat status) { +case NFS_OK: + fattr attributes; +default: + void; +}; + +struct sattrargs { + nfs_fh file; + sattr attributes; +}; + +/* + * Arguments for directory operations + */ +struct diropargs { + nfs_fh dir; /* directory file handle */ + filename name; /* name (up to NFS_MAXNAMLEN bytes) */ +}; + +struct diropokres { + nfs_fh file; + fattr attributes; +}; + +/* + * Results from directory operation + */ +union diropres switch (nfsstat status) { +case NFS_OK: + diropokres diropres; +default: + void; +}; + +union readlinkres switch (nfsstat status) { +case NFS_OK: + nfspath data; +default: + void; +}; + +/* + * Arguments to remote read + */ +struct readargs { + nfs_fh file; /* handle for file */ + unsigned offset; /* byte offset in file */ + unsigned count; /* immediate read count */ + unsigned totalcount; /* total read count (from this offset)*/ +}; + +/* + * Status OK portion of remote read reply + */ +struct readokres { + fattr attributes; /* attributes, need for pagin*/ + opaque data<NFS_MAXDATA>; +}; + +union readres switch (nfsstat status) { +case NFS_OK: + readokres reply; +default: + void; +}; + +/* + * Arguments to remote write + */ +struct writeargs { + nfs_fh file; /* handle for file */ + unsigned beginoffset; /* beginning byte offset in file */ + unsigned offset; /* current byte offset in file */ + unsigned totalcount; /* total write count (to this offset)*/ + opaque data<NFS_MAXDATA>; +}; + +struct createargs { + diropargs where; + sattr attributes; +}; + +struct renameargs { + diropargs from; + diropargs to; +}; + +struct linkargs { + nfs_fh from; + diropargs to; +}; + +struct symlinkargs { + diropargs from; + nfspath to; + sattr attributes; +}; + + +/* TS, 10/21/2002; converted cookie to struct */ +struct nfscookie { + opaque data[NFS_COOKIESIZE]; +}; + +/* + * Arguments to readdir + */ +struct readdirargs { + nfs_fh dir; /* directory handle */ + nfscookie cookie; + unsigned count; /* number of directory bytes to read */ +}; + +struct entry { + unsigned fileid; + filename name; + nfscookie cookie; + entry *nextentry; +}; + +struct dirlist { + entry *entries; + bool eof; +}; + +union readdirres switch (nfsstat status) { +case NFS_OK: + dirlist reply; +default: + void; +}; + +struct statfsokres { + unsigned tsize; /* preferred transfer size in bytes */ + unsigned bsize; /* fundamental file system block size */ + unsigned blocks; /* total blocks in file system */ + unsigned bfree; /* free blocks in fs */ + unsigned bavail; /* free blocks avail to non-superuser */ +}; + +union statfsres switch (nfsstat status) { +case NFS_OK: + statfsokres reply; +default: + void; +}; + +#ifdef WANT_NFS3 + +/* + * NFSv3 constants and types + */ +const NFS3_FHSIZE = 64; /* maximum size in bytes of a file handle */ +const NFS3_COOKIEVERFSIZE = 8; /* size of a cookie verifier for READDIR */ +const NFS3_CREATEVERFSIZE = 8; /* size of the verifier used for CREATE */ +const NFS3_WRITEVERFSIZE = 8; /* size of the verifier used for WRITE */ + +typedef unsigned hyper uint64; +typedef hyper int64; +typedef unsigned long uint32; +typedef long int32; +typedef string filename3<>; +typedef string nfspath3<>; +typedef uint64 fileid3; +typedef uint64 cookie3; +typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE]; +typedef opaque createverf3[NFS3_CREATEVERFSIZE]; +typedef opaque writeverf3[NFS3_WRITEVERFSIZE]; +typedef uint32 uid3; +typedef uint32 gid3; +typedef uint64 size3; +typedef uint64 offset3; +typedef uint32 mode3; +typedef uint32 count3; + +/* + * Error status (v3) + */ +enum nfsstat3 { + NFS3_OK = 0, + NFS3ERR_PERM = 1, + NFS3ERR_NOENT = 2, + NFS3ERR_IO = 5, + NFS3ERR_NXIO = 6, + NFS3ERR_ACCES = 13, + NFS3ERR_EXIST = 17, + NFS3ERR_XDEV = 18, + NFS3ERR_NODEV = 19, + NFS3ERR_NOTDIR = 20, + NFS3ERR_ISDIR = 21, + NFS3ERR_INVAL = 22, + NFS3ERR_FBIG = 27, + NFS3ERR_NOSPC = 28, + NFS3ERR_ROFS = 30, + NFS3ERR_MLINK = 31, + NFS3ERR_NAMETOOLONG = 63, + NFS3ERR_NOTEMPTY = 66, + NFS3ERR_DQUOT = 69, + NFS3ERR_STALE = 70, + NFS3ERR_REMOTE = 71, + NFS3ERR_BADHANDLE = 10001, + NFS3ERR_NOT_SYNC = 10002, + NFS3ERR_BAD_COOKIE = 10003, + NFS3ERR_NOTSUPP = 10004, + NFS3ERR_TOOSMALL = 10005, + NFS3ERR_SERVERFAULT = 10006, + NFS3ERR_BADTYPE = 10007, + NFS3ERR_JUKEBOX = 10008 +}; + +/* + * File types (v3) + */ +enum ftype3 { + NF3REG = 1, /* regular file */ + NF3DIR = 2, /* directory */ + NF3BLK = 3, /* block special */ + NF3CHR = 4, /* character special */ + NF3LNK = 5, /* symbolic link */ + NF3SOCK = 6, /* unix domain sockets */ + NF3FIFO = 7 /* named pipe */ +}; + +struct specdata3 { + uint32 specdata1; + uint32 specdata2; +}; + +/* + * File access handle (v3) + */ +struct nfs_fh3 { + opaque data<NFS3_FHSIZE>; +}; + +/* + * Timeval (v3) + */ +struct nfstime3 { + uint32 seconds; + uint32 nseconds; +}; + + +/* + * File attributes (v3) + */ +struct fattr3 { + ftype3 type; /* file type */ + mode3 mode; /* protection mode bits */ + uint32 nlink; /* # hard links */ + uid3 uid; /* owner user id */ + gid3 gid; /* owner group id */ + size3 size; /* file size in bytes */ + size3 used; /* prefered block size */ + specdata3 rdev; /* special device # */ + uint64 fsid; /* device # */ + fileid3 fileid; /* inode # */ + nfstime3 atime; /* time of last access */ + nfstime3 mtime; /* time of last modification */ + nfstime3 ctime; /* time of last change */ +}; + +union post_op_attr switch (bool attributes_follow) { +case TRUE: + fattr3 attributes; +case FALSE: + void; +}; + +struct wcc_attr { + size3 size; + nfstime3 mtime; + nfstime3 ctime; +}; + +union pre_op_attr switch (bool attributes_follow) { +case TRUE: + wcc_attr attributes; +case FALSE: + void; +}; + +struct wcc_data { + pre_op_attr before; + post_op_attr after; +}; + +union post_op_fh3 switch (bool handle_follows) { +case TRUE: + nfs_fh3 handle; +case FALSE: + void; +}; + +/* + * File attributes which can be set (v3) + */ +enum time_how { + DONT_CHANGE = 0, + SET_TO_SERVER_TIME = 1, + SET_TO_CLIENT_TIME = 2 +}; + +union set_mode3 switch (bool set_it) { +case TRUE: + mode3 mode; +default: + void; +}; + +union set_uid3 switch (bool set_it) { +case TRUE: + uid3 uid; +default: + void; +}; + +union set_gid3 switch (bool set_it) { +case TRUE: + gid3 gid; +default: + void; +}; + +union set_size3 switch (bool set_it) { +case TRUE: + size3 size; +default: + void; +}; + +union set_atime switch (time_how set_it) { +case SET_TO_CLIENT_TIME: + nfstime3 atime; +default: + void; +}; + +union set_mtime switch (time_how set_it) { +case SET_TO_CLIENT_TIME: + nfstime3 mtime; +default: + void; +}; + +struct sattr3 { + set_mode3 mode; + set_uid3 uid; + set_gid3 gid; + set_size3 size; + set_atime atime; + set_mtime mtime; +}; + +/* + * Arguments for directory operations (v3) + */ +struct diropargs3 { + nfs_fh3 dir; /* directory file handle */ + filename3 name; /* name (up to NFS_MAXNAMLEN bytes) */ +}; + +/* + * Arguments to getattr (v3). + */ +struct GETATTR3args { + nfs_fh3 object; +}; + +struct GETATTR3resok { + fattr3 obj_attributes; +}; + +union GETATTR3res switch (nfsstat3 status) { +case NFS3_OK: + GETATTR3resok resok; +default: + void; +}; + +/* + * Arguments to setattr (v3). + */ +union sattrguard3 switch (bool check) { +case TRUE: + nfstime3 obj_ctime; +case FALSE: + void; +}; + +struct SETATTR3args { + nfs_fh3 object; + sattr3 new_attributes; + sattrguard3 guard; +}; + +struct SETATTR3resok { + wcc_data obj_wcc; +}; + +struct SETATTR3resfail { + wcc_data obj_wcc; +}; + +union SETATTR3res switch (nfsstat3 status) { +case NFS3_OK: + SETATTR3resok resok; +default: + SETATTR3resfail resfail; +}; + +/* + * Arguments to lookup (v3). + */ +struct LOOKUP3args { + diropargs3 what; +}; + +struct LOOKUP3resok { + nfs_fh3 object; + post_op_attr obj_attributes; + post_op_attr dir_attributes; +}; + +struct LOOKUP3resfail { + post_op_attr dir_attributes; +}; + +union LOOKUP3res switch (nfsstat3 status) { +case NFS3_OK: + LOOKUP3resok resok; +default: + LOOKUP3resfail resfail; +}; + +/* + * Arguments to access (v3). + */ +const ACCESS3_READ = 0x0001; +const ACCESS3_LOOKUP = 0x0002; +const ACCESS3_MODIFY = 0x0004; +const ACCESS3_EXTEND = 0x0008; +const ACCESS3_DELETE = 0x0010; +const ACCESS3_EXECUTE = 0x0020; + +struct ACCESS3args { + nfs_fh3 object; + uint32 access; +}; + +struct ACCESS3resok { + post_op_attr obj_attributes; + uint32 access; +}; + +struct ACCESS3resfail { + post_op_attr obj_attributes; +}; + +union ACCESS3res switch (nfsstat3 status) { +case NFS3_OK: + ACCESS3resok resok; +default: + ACCESS3resfail resfail; +}; + +/* + * Arguments to readlink (v3). + */ +struct READLINK3args { + nfs_fh3 symlink; +}; + +struct READLINK3resok { + post_op_attr symlink_attributes; + nfspath3 data; +}; + +struct READLINK3resfail { + post_op_attr symlink_attributes; +}; + +union READLINK3res switch (nfsstat3 status) { +case NFS3_OK: + READLINK3resok resok; +default: + READLINK3resfail resfail; +}; + +/* + * Arguments to read (v3). + */ +struct READ3args { + nfs_fh3 file; + offset3 offset; + count3 count; +}; + +struct READ3resok { + post_op_attr file_attributes; + count3 count; + bool eof; + opaque data<>; +}; + +struct READ3resfail { + post_op_attr file_attributes; +}; + +/* XXX: solaris 2.6 uses ``nfsstat'' here */ +union READ3res switch (nfsstat3 status) { +case NFS3_OK: + READ3resok resok; +default: + READ3resfail resfail; +}; + +/* + * Arguments to write (v3). + */ +enum stable_how { + UNSTABLE = 0, + DATA_SYNC = 1, + FILE_SYNC = 2 +}; + +struct WRITE3args { + nfs_fh3 file; + offset3 offset; + count3 count; + stable_how stable; + opaque data<>; +}; + +struct WRITE3resok { + wcc_data file_wcc; + count3 count; + stable_how committed; + writeverf3 verf; +}; + +struct WRITE3resfail { + wcc_data file_wcc; +}; + +union WRITE3res switch (nfsstat3 status) { +case NFS3_OK: + WRITE3resok resok; +default: + WRITE3resfail resfail; +}; + +/* + * Arguments to create (v3). + */ +enum createmode3 { + UNCHECKED = 0, + GUARDED = 1, + EXCLUSIVE = 2 +}; + +union createhow3 switch (createmode3 mode) { +case UNCHECKED: +case GUARDED: + sattr3 obj_attributes; +case EXCLUSIVE: + createverf3 verf; +}; + +struct CREATE3args { + diropargs3 where; + createhow3 how; +}; + +struct CREATE3resok { + post_op_fh3 obj; + post_op_attr obj_attributes; + wcc_data dir_wcc; +}; + +struct CREATE3resfail { + wcc_data dir_wcc; +}; + +union CREATE3res switch (nfsstat3 status) { +case NFS3_OK: + CREATE3resok resok; +default: + CREATE3resfail resfail; +}; + +/* + * Arguments to mkdir (v3). + */ +struct MKDIR3args { + diropargs3 where; + sattr3 attributes; +}; + +struct MKDIR3resok { + post_op_fh3 obj; + post_op_attr obj_attributes; + wcc_data dir_wcc; +}; + +struct MKDIR3resfail { + wcc_data dir_wcc; +}; + +union MKDIR3res switch (nfsstat3 status) { +case NFS3_OK: + MKDIR3resok resok; +default: + MKDIR3resfail resfail; +}; + +/* + * Arguments to symlink (v3). + */ +struct symlinkdata3 { + sattr3 symlink_attributes; + nfspath3 symlink_data; +}; + +struct SYMLINK3args { + diropargs3 where; + symlinkdata3 symlink; +}; + +struct SYMLINK3resok { + post_op_fh3 obj; + post_op_attr obj_attributes; + wcc_data dir_wcc; +}; + +struct SYMLINK3resfail { + wcc_data dir_wcc; +}; + +union SYMLINK3res switch (nfsstat3 status) { +case NFS3_OK: + SYMLINK3resok resok; +default: + SYMLINK3resfail resfail; +}; + +/* + * Arguments to mknod (v3). + */ +struct devicedata3 { + sattr3 dev_attributes; + specdata3 spec; +}; + +union mknoddata3 switch (ftype3 type) { +case NF3CHR: +case NF3BLK: + devicedata3 device; +case NF3SOCK: +case NF3FIFO: + sattr3 pipe_attributes; +default: + void; +}; + +struct MKNOD3args { + diropargs3 where; + mknoddata3 what; +}; + +struct MKNOD3resok { + post_op_fh3 obj; + post_op_attr obj_attributes; + wcc_data dir_wcc; +}; + +struct MKNOD3resfail { + wcc_data dir_wcc; +}; + +union MKNOD3res switch (nfsstat3 status) { +case NFS3_OK: + MKNOD3resok resok; +default: + MKNOD3resfail resfail; +}; + +/* + * Arguments to remove (v3). + */ +struct REMOVE3args { + diropargs3 object; +}; + +struct REMOVE3resok { + wcc_data dir_wcc; +}; + +struct REMOVE3resfail { + wcc_data dir_wcc; +}; + +union REMOVE3res switch (nfsstat3 status) { +case NFS3_OK: + REMOVE3resok resok; +default: + REMOVE3resfail resfail; +}; + +/* + * Arguments to rmdir (v3). + */ +struct RMDIR3args { + diropargs3 object; +}; + +struct RMDIR3resok { + wcc_data dir_wcc; +}; + +struct RMDIR3resfail { + wcc_data dir_wcc; +}; + +union RMDIR3res switch (nfsstat3 status) { +case NFS3_OK: + RMDIR3resok resok; +default: + RMDIR3resfail resfail; +}; + +/* + * Arguments to rename (v3). + */ +struct RENAME3args { + diropargs3 from; + diropargs3 to; +}; + +struct RENAME3resok { + wcc_data fromdir_wcc; + wcc_data todir_wcc; +}; + +struct RENAME3resfail { + wcc_data fromdir_wcc; + wcc_data todir_wcc; +}; + +union RENAME3res switch (nfsstat3 status) { +case NFS3_OK: + RENAME3resok resok; +default: + RENAME3resfail resfail; +}; + +/* + * Arguments to link (v3). + */ +struct LINK3args { + nfs_fh3 file; + diropargs3 link; +}; + +struct LINK3resok { + post_op_attr file_attributes; + wcc_data linkdir_wcc; +}; + +struct LINK3resfail { + post_op_attr file_attributes; + wcc_data linkdir_wcc; +}; + +union LINK3res switch (nfsstat3 status) { +case NFS3_OK: + LINK3resok resok; +default: + LINK3resfail resfail; +}; + +/* + * Arguments to readdir (v3). + */ +struct READDIR3args { + nfs_fh3 dir; + cookie3 cookie; + cookieverf3 cookieverf; + count3 count; +}; + +struct entry3 { + fileid3 fileid; + filename3 name; + cookie3 cookie; + entry3 *nextentry; +}; + +struct dirlist3 { + entry3 *entries; + bool eof; +}; + +struct READDIR3resok { + post_op_attr dir_attributes; + cookieverf3 cookieverf; + dirlist3 reply; +}; + +struct READDIR3resfail { + post_op_attr dir_attributes; +}; + +union READDIR3res switch (nfsstat3 status) { +case NFS3_OK: + READDIR3resok resok; +default: + READDIR3resfail resfail; +}; + +/* + * Arguments to readdirplus (v3). + */ +struct READDIRPLUS3args { + nfs_fh3 dir; + cookie3 cookie; + cookieverf3 cookieverf; + count3 dircount; + count3 maxcount; +}; + +struct entryplus3 { + fileid3 fileid; + filename3 name; + cookie3 cookie; + post_op_attr name_attributes; + post_op_fh3 name_handle; + entryplus3 *nextentry; +}; + +struct dirlistplus3 { + entryplus3 *entries; + bool eof; +}; + +struct READDIRPLUS3resok { + post_op_attr dir_attributes; + cookieverf3 cookieverf; + dirlistplus3 reply; +}; + +struct READDIRPLUS3resfail { + post_op_attr dir_attributes; +}; + +union READDIRPLUS3res switch (nfsstat3 status) { +case NFS3_OK: + READDIRPLUS3resok resok; +default: + READDIRPLUS3resfail resfail; +}; + +/* + * Arguments to fsstat (v3). + */ +struct FSSTAT3args { + nfs_fh3 fsroot; +}; + +struct FSSTAT3resok { + post_op_attr obj_attributes; + size3 tbytes; + size3 fbytes; + size3 abytes; + size3 tfiles; + size3 ffiles; + size3 afiles; + uint32 invarsec; +}; + +struct FSSTAT3resfail { + post_op_attr obj_attributes; +}; + +union FSSTAT3res switch (nfsstat3 status) { +case NFS3_OK: + FSSTAT3resok resok; +default: + FSSTAT3resfail resfail; +}; + +/* + * Arguments to fsinfo (v3). + */ +const FSF3_LINK = 0x0001; +const FSF3_SYMLINK = 0x0002; +const FSF3_HOMOGENEOUS = 0x0008; +const FSF3_CANSETTIME = 0x0010; + +struct FSINFO3args { + nfs_fh3 fsroot; +}; + +struct FSINFO3resok { + post_op_attr obj_attributes; + uint32 rtmax; + uint32 rtpref; + uint32 rtmult; + uint32 wtmax; + uint32 wtpref; + uint32 wtmult; + uint32 dtpref; + size3 maxfilesize; + nfstime3 time_delta; + uint32 properties; +}; + +struct FSINFO3resfail { + post_op_attr obj_attributes; +}; + +union FSINFO3res switch (nfsstat3 status) { +case NFS3_OK: + FSINFO3resok resok; +default: + FSINFO3resfail resfail; +}; + +/* + * Arguments to pathconf (v3). + */ +struct PATHCONF3args { + nfs_fh3 object; +}; + +struct PATHCONF3resok { + post_op_attr obj_attributes; + uint32 linkmax; + uint32 name_max; + bool no_trunc; + bool chown_restricted; + bool case_insensitive; + bool case_preserving; +}; + +struct PATHCONF3resfail { + post_op_attr obj_attributes; +}; + +union PATHCONF3res switch (nfsstat3 status) { +case NFS3_OK: + PATHCONF3resok resok; +default: + PATHCONF3resfail resfail; +}; + +/* + * Arguments to commit (v3). + */ +struct COMMIT3args { + nfs_fh3 file; + offset3 offset; + count3 count; +}; + +struct COMMIT3resok { + wcc_data file_wcc; + writeverf3 verf; +}; + +struct COMMIT3resfail { + wcc_data file_wcc; +}; + +union COMMIT3res switch (nfsstat3 status) { +case NFS3_OK: + COMMIT3resok resok; +default: + COMMIT3resfail resfail; +}; + +#endif /* WANT_NFS3 */ + +/* + * Remote file service routines + */ +program NFS_PROGRAM { + version NFS_VERSION { + void + NFSPROC_NULL(void) = 0; + + attrstat + NFSPROC_GETATTR(nfs_fh) = 1; + + attrstat + NFSPROC_SETATTR(sattrargs) = 2; + + void + NFSPROC_ROOT(void) = 3; + + diropres + NFSPROC_LOOKUP(diropargs) = 4; + + readlinkres + NFSPROC_READLINK(nfs_fh) = 5; + + readres + NFSPROC_READ(readargs) = 6; + + void + NFSPROC_WRITECACHE(void) = 7; + + attrstat + NFSPROC_WRITE(writeargs) = 8; + + diropres + NFSPROC_CREATE(createargs) = 9; + + nfsstat + NFSPROC_REMOVE(diropargs) = 10; + + nfsstat + NFSPROC_RENAME(renameargs) = 11; + + nfsstat + NFSPROC_LINK(linkargs) = 12; + + nfsstat + NFSPROC_SYMLINK(symlinkargs) = 13; + + diropres + NFSPROC_MKDIR(createargs) = 14; + + nfsstat + NFSPROC_RMDIR(diropargs) = 15; + + readdirres + NFSPROC_READDIR(readdirargs) = 16; + + statfsres + NFSPROC_STATFS(nfs_fh) = 17; + } = 2; +} = 100003; +#ifdef WANT_NFS3 +program NFS3_PROGRAM { + version NFS_V3 { + void + NFSPROC3_NULL(void) = 0; + + GETATTR3res + NFSPROC3_GETATTR(GETATTR3args) = 1; + + SETATTR3res + NFSPROC3_SETATTR(SETATTR3args) = 2; + + LOOKUP3res + NFSPROC3_LOOKUP(LOOKUP3args) = 3; + + ACCESS3res + NFSPROC3_ACCESS(ACCESS3args) = 4; + + READLINK3res + NFSPROC3_READLINK(READLINK3args) = 5; + + READ3res + NFSPROC3_READ(READ3args) = 6; + + WRITE3res + NFSPROC3_WRITE(WRITE3args) = 7; + + CREATE3res + NFSPROC3_CREATE(CREATE3args) = 8; + + MKDIR3res + NFSPROC3_MKDIR(MKDIR3args) = 9; + + SYMLINK3res + NFSPROC3_SYMLINK(SYMLINK3args) = 10; + + MKNOD3res + NFSPROC3_MKNOD(MKNOD3args) = 11; + + REMOVE3res + NFSPROC3_REMOVE(REMOVE3args) = 12; + + RMDIR3res + NFSPROC3_RMDIR(RMDIR3args) = 13; + + RENAME3res + NFSPROC3_RENAME(RENAME3args) = 14; + + LINK3res + NFSPROC3_LINK(LINK3args) = 15; + + READDIR3res + NFSPROC3_READDIR(READDIR3args) = 16; + + READDIRPLUS3res + NFSPROC3_READDIRPLUS(READDIRPLUS3args) = 17; + + FSSTAT3res + NFSPROC3_FSSTAT(FSSTAT3args) = 18; + + FSINFO3res + NFSPROC3_FSINFO(FSINFO3args) = 19; + + PATHCONF3res + NFSPROC3_PATHCONF(PATHCONF3args) = 20; + + COMMIT3res + NFSPROC3_COMMIT(COMMIT3args) = 21; + } = 3; +} = 100003; +#endif + diff --git a/cpukit/libfs/src/nfsclient/proto/nfs_prot_xdr.c b/cpukit/libfs/src/nfsclient/proto/nfs_prot_xdr.c new file mode 100644 index 0000000000..cde005e2b4 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/proto/nfs_prot_xdr.c @@ -0,0 +1,621 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include "nfs_prot.h" +#ifndef lint +/*static char sccsid[] = "from: @(#)nfs_prot.x 1.2 87/10/12 Copyr 1987 Sun Micro";*/ +/*static char sccsid[] = "from: @(#)nfs_prot.x 2.1 88/08/01 4.0 RPCSRC";*/ + #if !defined(__rtems__) + static char rcsid[] = "$Id$"; + #endif +#endif /* not lint */ + +bool_t +xdr_nfsstat (XDR *xdrs, nfsstat *objp) +{ + if (!xdr_enum (xdrs, (enum_t *) objp)) + return FALSE; + return TRUE; +} + +bool_t +xdr_ftype (XDR *xdrs, ftype *objp) +{ + if (!xdr_enum (xdrs, (enum_t *) objp)) + return FALSE; + return TRUE; +} + +bool_t +xdr_nfs_fh (XDR *xdrs, nfs_fh *objp) +{ + if (!xdr_opaque (xdrs, objp->data, NFS_FHSIZE)) + return FALSE; + return TRUE; +} + +bool_t +xdr_nfstime (XDR *xdrs, nfstime *objp) +{ + if (!xdr_u_int (xdrs, &objp->seconds)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->useconds)) + return FALSE; + return TRUE; +} + +bool_t +xdr_fattr (XDR *xdrs, fattr *objp) +{ + register int32_t *buf; + + + if (xdrs->x_op == XDR_ENCODE) { + if (!xdr_ftype (xdrs, &objp->type)) + return FALSE; + buf = XDR_INLINE (xdrs, 10 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_u_int (xdrs, &objp->mode)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->nlink)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->uid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->gid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->size)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->blocksize)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->rdev)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->blocks)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->fsid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->fileid)) + return FALSE; + + } else { + IXDR_PUT_U_LONG(buf, objp->mode); + IXDR_PUT_U_LONG(buf, objp->nlink); + IXDR_PUT_U_LONG(buf, objp->uid); + IXDR_PUT_U_LONG(buf, objp->gid); + IXDR_PUT_U_LONG(buf, objp->size); + IXDR_PUT_U_LONG(buf, objp->blocksize); + IXDR_PUT_U_LONG(buf, objp->rdev); + IXDR_PUT_U_LONG(buf, objp->blocks); + IXDR_PUT_U_LONG(buf, objp->fsid); + IXDR_PUT_U_LONG(buf, objp->fileid); + } + if (!xdr_nfstime (xdrs, &objp->atime)) + return FALSE; + if (!xdr_nfstime (xdrs, &objp->mtime)) + return FALSE; + if (!xdr_nfstime (xdrs, &objp->ctime)) + return FALSE; + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + if (!xdr_ftype (xdrs, &objp->type)) + return FALSE; + buf = XDR_INLINE (xdrs, 10 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_u_int (xdrs, &objp->mode)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->nlink)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->uid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->gid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->size)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->blocksize)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->rdev)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->blocks)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->fsid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->fileid)) + return FALSE; + + } else { + objp->mode = IXDR_GET_U_LONG(buf); + objp->nlink = IXDR_GET_U_LONG(buf); + objp->uid = IXDR_GET_U_LONG(buf); + objp->gid = IXDR_GET_U_LONG(buf); + objp->size = IXDR_GET_U_LONG(buf); + objp->blocksize = IXDR_GET_U_LONG(buf); + objp->rdev = IXDR_GET_U_LONG(buf); + objp->blocks = IXDR_GET_U_LONG(buf); + objp->fsid = IXDR_GET_U_LONG(buf); + objp->fileid = IXDR_GET_U_LONG(buf); + } + if (!xdr_nfstime (xdrs, &objp->atime)) + return FALSE; + if (!xdr_nfstime (xdrs, &objp->mtime)) + return FALSE; + if (!xdr_nfstime (xdrs, &objp->ctime)) + return FALSE; + return TRUE; + } + + if (!xdr_ftype (xdrs, &objp->type)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->mode)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->nlink)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->uid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->gid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->size)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->blocksize)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->rdev)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->blocks)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->fsid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->fileid)) + return FALSE; + if (!xdr_nfstime (xdrs, &objp->atime)) + return FALSE; + if (!xdr_nfstime (xdrs, &objp->mtime)) + return FALSE; + if (!xdr_nfstime (xdrs, &objp->ctime)) + return FALSE; + return TRUE; +} + +bool_t +xdr_sattr (XDR *xdrs, sattr *objp) +{ + register int32_t *buf; + + + if (xdrs->x_op == XDR_ENCODE) { + buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_u_int (xdrs, &objp->mode)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->uid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->gid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->size)) + return FALSE; + + } else { + IXDR_PUT_U_LONG(buf, objp->mode); + IXDR_PUT_U_LONG(buf, objp->uid); + IXDR_PUT_U_LONG(buf, objp->gid); + IXDR_PUT_U_LONG(buf, objp->size); + } + if (!xdr_nfstime (xdrs, &objp->atime)) + return FALSE; + if (!xdr_nfstime (xdrs, &objp->mtime)) + return FALSE; + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_u_int (xdrs, &objp->mode)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->uid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->gid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->size)) + return FALSE; + + } else { + objp->mode = IXDR_GET_U_LONG(buf); + objp->uid = IXDR_GET_U_LONG(buf); + objp->gid = IXDR_GET_U_LONG(buf); + objp->size = IXDR_GET_U_LONG(buf); + } + if (!xdr_nfstime (xdrs, &objp->atime)) + return FALSE; + if (!xdr_nfstime (xdrs, &objp->mtime)) + return FALSE; + return TRUE; + } + + if (!xdr_u_int (xdrs, &objp->mode)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->uid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->gid)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->size)) + return FALSE; + if (!xdr_nfstime (xdrs, &objp->atime)) + return FALSE; + if (!xdr_nfstime (xdrs, &objp->mtime)) + return FALSE; + return TRUE; +} + +bool_t +xdr_filename (XDR *xdrs, filename *objp) +{ + if (!xdr_string (xdrs, objp, NFS_MAXNAMLEN)) + return FALSE; + return TRUE; +} + +bool_t +xdr_nfspath (XDR *xdrs, nfspath *objp) +{ + if (!xdr_string (xdrs, objp, NFS_MAXPATHLEN)) + return FALSE; + return TRUE; +} + +bool_t +xdr_attrstat (XDR *xdrs, attrstat *objp) +{ + if (!xdr_nfsstat (xdrs, &objp->status)) + return FALSE; + switch (objp->status) { + case NFS_OK: + if (!xdr_fattr (xdrs, &objp->attrstat_u.attributes)) + return FALSE; + break; + default: + break; + } + return TRUE; +} + +bool_t +xdr_sattrargs (XDR *xdrs, sattrargs *objp) +{ + if (!xdr_nfs_fh (xdrs, &objp->file)) + return FALSE; + if (!xdr_sattr (xdrs, &objp->attributes)) + return FALSE; + return TRUE; +} + +bool_t +xdr_diropargs (XDR *xdrs, diropargs *objp) +{ + if (!xdr_nfs_fh (xdrs, &objp->dir)) + return FALSE; + if (!xdr_filename (xdrs, &objp->name)) + return FALSE; + return TRUE; +} + +bool_t +xdr_diropokres (XDR *xdrs, diropokres *objp) +{ + if (!xdr_nfs_fh (xdrs, &objp->file)) + return FALSE; + if (!xdr_fattr (xdrs, &objp->attributes)) + return FALSE; + return TRUE; +} + +bool_t +xdr_diropres (XDR *xdrs, diropres *objp) +{ + if (!xdr_nfsstat (xdrs, &objp->status)) + return FALSE; + switch (objp->status) { + case NFS_OK: + if (!xdr_diropokres (xdrs, &objp->diropres_u.diropres)) + return FALSE; + break; + default: + break; + } + return TRUE; +} + +bool_t +xdr_readlinkres (XDR *xdrs, readlinkres *objp) +{ + if (!xdr_nfsstat (xdrs, &objp->status)) + return FALSE; + switch (objp->status) { + case NFS_OK: + if (!xdr_nfspath (xdrs, &objp->readlinkres_u.data)) + return FALSE; + break; + default: + break; + } + return TRUE; +} + +bool_t +xdr_readargs (XDR *xdrs, readargs *objp) +{ + if (!xdr_nfs_fh (xdrs, &objp->file)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->offset)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->count)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->totalcount)) + return FALSE; + return TRUE; +} + +bool_t +xdr_readokres (XDR *xdrs, readokres *objp) +{ + if (!xdr_fattr (xdrs, &objp->attributes)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, NFS_MAXDATA)) + return FALSE; + return TRUE; +} + +bool_t +xdr_readres (XDR *xdrs, readres *objp) +{ + if (!xdr_nfsstat (xdrs, &objp->status)) + return FALSE; + switch (objp->status) { + case NFS_OK: + if (!xdr_readokres (xdrs, &objp->readres_u.reply)) + return FALSE; + break; + default: + break; + } + return TRUE; +} + +bool_t +xdr_writeargs (XDR *xdrs, writeargs *objp) +{ + register int32_t *buf; + + + if (xdrs->x_op == XDR_ENCODE) { + if (!xdr_nfs_fh (xdrs, &objp->file)) + return FALSE; + buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_u_int (xdrs, &objp->beginoffset)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->offset)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->totalcount)) + return FALSE; + + } else { + IXDR_PUT_U_LONG(buf, objp->beginoffset); + IXDR_PUT_U_LONG(buf, objp->offset); + IXDR_PUT_U_LONG(buf, objp->totalcount); + } + if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, NFS_MAXDATA)) + return FALSE; + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + if (!xdr_nfs_fh (xdrs, &objp->file)) + return FALSE; + buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_u_int (xdrs, &objp->beginoffset)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->offset)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->totalcount)) + return FALSE; + + } else { + objp->beginoffset = IXDR_GET_U_LONG(buf); + objp->offset = IXDR_GET_U_LONG(buf); + objp->totalcount = IXDR_GET_U_LONG(buf); + } + if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, NFS_MAXDATA)) + return FALSE; + return TRUE; + } + + if (!xdr_nfs_fh (xdrs, &objp->file)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->beginoffset)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->offset)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->totalcount)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, NFS_MAXDATA)) + return FALSE; + return TRUE; +} + +bool_t +xdr_createargs (XDR *xdrs, createargs *objp) +{ + if (!xdr_diropargs (xdrs, &objp->where)) + return FALSE; + if (!xdr_sattr (xdrs, &objp->attributes)) + return FALSE; + return TRUE; +} + +bool_t +xdr_renameargs (XDR *xdrs, renameargs *objp) +{ + if (!xdr_diropargs (xdrs, &objp->from)) + return FALSE; + if (!xdr_diropargs (xdrs, &objp->to)) + return FALSE; + return TRUE; +} + +bool_t +xdr_linkargs (XDR *xdrs, linkargs *objp) +{ + if (!xdr_nfs_fh (xdrs, &objp->from)) + return FALSE; + if (!xdr_diropargs (xdrs, &objp->to)) + return FALSE; + return TRUE; +} + +bool_t +xdr_symlinkargs (XDR *xdrs, symlinkargs *objp) +{ + if (!xdr_diropargs (xdrs, &objp->from)) + return FALSE; + if (!xdr_nfspath (xdrs, &objp->to)) + return FALSE; + if (!xdr_sattr (xdrs, &objp->attributes)) + return FALSE; + return TRUE; +} + +bool_t +xdr_nfscookie (XDR *xdrs, nfscookie *objp) +{ + if (!xdr_opaque (xdrs, objp->data, NFS_COOKIESIZE)) + return FALSE; + return TRUE; +} + +bool_t +xdr_readdirargs (XDR *xdrs, readdirargs *objp) +{ + if (!xdr_nfs_fh (xdrs, &objp->dir)) + return FALSE; + if (!xdr_nfscookie (xdrs, &objp->cookie)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->count)) + return FALSE; + return TRUE; +} + +bool_t +xdr_entry (XDR *xdrs, entry *objp) +{ + if (!xdr_u_int (xdrs, &objp->fileid)) + return FALSE; + if (!xdr_filename (xdrs, &objp->name)) + return FALSE; + if (!xdr_nfscookie (xdrs, &objp->cookie)) + return FALSE; + if (!xdr_pointer (xdrs, (char **)&objp->nextentry, sizeof (entry), (xdrproc_t) xdr_entry)) + return FALSE; + return TRUE; +} + +bool_t +xdr_dirlist (XDR *xdrs, dirlist *objp) +{ + if (!xdr_pointer (xdrs, (char **)&objp->entries, sizeof (entry), (xdrproc_t) xdr_entry)) + return FALSE; + if (!xdr_bool (xdrs, &objp->eof)) + return FALSE; + return TRUE; +} + +bool_t +xdr_readdirres (XDR *xdrs, readdirres *objp) +{ + if (!xdr_nfsstat (xdrs, &objp->status)) + return FALSE; + switch (objp->status) { + case NFS_OK: + if (!xdr_dirlist (xdrs, &objp->readdirres_u.reply)) + return FALSE; + break; + default: + break; + } + return TRUE; +} + +bool_t +xdr_statfsokres (XDR *xdrs, statfsokres *objp) +{ + register int32_t *buf; + + + if (xdrs->x_op == XDR_ENCODE) { + buf = XDR_INLINE (xdrs, 5 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_u_int (xdrs, &objp->tsize)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->bsize)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->blocks)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->bfree)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->bavail)) + return FALSE; + } else { + IXDR_PUT_U_LONG(buf, objp->tsize); + IXDR_PUT_U_LONG(buf, objp->bsize); + IXDR_PUT_U_LONG(buf, objp->blocks); + IXDR_PUT_U_LONG(buf, objp->bfree); + IXDR_PUT_U_LONG(buf, objp->bavail); + } + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + buf = XDR_INLINE (xdrs, 5 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_u_int (xdrs, &objp->tsize)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->bsize)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->blocks)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->bfree)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->bavail)) + return FALSE; + } else { + objp->tsize = IXDR_GET_U_LONG(buf); + objp->bsize = IXDR_GET_U_LONG(buf); + objp->blocks = IXDR_GET_U_LONG(buf); + objp->bfree = IXDR_GET_U_LONG(buf); + objp->bavail = IXDR_GET_U_LONG(buf); + } + return TRUE; + } + + if (!xdr_u_int (xdrs, &objp->tsize)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->bsize)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->blocks)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->bfree)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->bavail)) + return FALSE; + return TRUE; +} + +bool_t +xdr_statfsres (XDR *xdrs, statfsres *objp) +{ + if (!xdr_nfsstat (xdrs, &objp->status)) + return FALSE; + switch (objp->status) { + case NFS_OK: + if (!xdr_statfsokres (xdrs, &objp->statfsres_u.reply)) + return FALSE; + break; + default: + break; + } + return TRUE; +} diff --git a/cpukit/libfs/src/nfsclient/rfc1094.txt b/cpukit/libfs/src/nfsclient/rfc1094.txt new file mode 100644 index 0000000000..7ad0f737ef --- /dev/null +++ b/cpukit/libfs/src/nfsclient/rfc1094.txt @@ -0,0 +1,1258 @@ + + RFC 1094 (RFC1094) + +Internet RFC/STD/FYI/BCP Archives + + + RFC 1094 - NFS: Network File System Protocol specification + +------------------------------------------------------------------------ + + +Network Working Group Sun Microsystems, Inc. +Request for Comments: 1094 March 1989 + + NFS: Network File System Protocol Specification + +STATUS OF THIS MEMO + + This RFC describes a protocol that Sun Microsystems, Inc., and others + are using. A new version of the protocol is under development, but + others may benefit from the descriptions of the current protocol, and + discussion of some of the design issues. Distribution of this memo + is unlimited. + +1. INTRODUCTION + + The Sun Network Filesystem (NFS) protocol provides transparent remote + access to shared files across networks. The NFS protocol is designed + to be portable across different machines, operating systems, network + architectures, and transport protocols. This portability is achieved + through the use of Remote Procedure Call (RPC) primitives built on + top of an eXternal Data Representation (XDR). Implementations + already exist for a variety of machines, from personal computers to + supercomputers. + + The supporting mount protocol allows the server to hand out remote + access privileges to a restricted set of clients. It performs the + operating system-specific functions that allow, for example, to + attach remote directory trees to some local file system. + +1.1. Remote Procedure Call + + Sun's Remote Procedure Call specification provides a procedure- + oriented interface to remote services. Each server supplies a + "program" that is a set of procedures. NFS is one such program. The + combination of host address, program number, and procedure number + specifies one remote procedure. A goal of NFS was to not require any + specific level of reliability from its lower levels, so it could + potentially be used on many underlying transport protocols, or even + another remote procedure call implementation. For ease of + discussion, the rest of this document will assume NFS is implemented + on top of Sun RPC, described in RFC 1057 </rfcs/rfc1057.html>, "RPC: Remote Procedure + Call Protocol Specification". + +1.2. External Data Representation + + The eXternal Data Representation (XDR) standard provides a common way + of representing a set of data types over a network. The NFS Protocol + + Specification is written using the RPC data description language. + For more information, see RFC 1014 </rfcs/rfc1014.html>, "XDR: External Data + Representation Standard". Although automated RPC/XDR compilers exist + to generate server and client "stubs", NFS does not require their + use. Any software that provides equivalent functionality can be + used, and if the encoding is exactly the same it can interoperate + with other implementations of NFS. + +1.3. Stateless Servers + + The NFS protocol was intended to be as stateless as possible. That + is, a server should not need to maintain any protocol state + information about any of its clients in order to function correctly. + Stateless servers have a distinct advantage over stateful servers in + the event of a failure. With stateless servers, a client need only + retry a request until the server responds; it does not even need to + know that the server has crashed, or the network temporarily went + down. The client of a stateful server, on the other hand, needs to + either detect a server failure and rebuild the server's state when it + comes back up, or cause client operations to fail. + + This may not sound like an important issue, but it affects the + protocol in some unexpected ways. We feel that it may be worth a bit + of extra complexity in the protocol to be able to write very simple + servers that do not require fancy crash recovery. Note that even if + a so-called "reliable" transport protocol such as TCP is used, the + client must still be able to handle interruptions of service by re- + opening connections when they time out. Thus, a stateless protocol + may actually simplify the implementation. + + On the other hand, NFS deals with objects such as files and + directories that inherently have state -- what good would a file be + if it did not keep its contents intact? The goal was to not + introduce any extra state in the protocol itself. Inherently + stateful operations such as file or record locking, and remote + execution, were implemented as separate services, not described in + this document. + + The basic way to simplify recovery was to make operations as + "idempotent" as possible (so that they can potentially be repeated). + Some operations in this version of the protocol did not attain this + goal; luckily most of the operations (such as Read and Write) are + idempotent. Also, most server failures occur between operations, not + between the receipt of an operation and the response. Finally, + although actual server failures may be rare, in complex networks, + failures of any network, router, or bridge may be indistinguishable + from a server failure. + +2. NFS PROTOCOL DEFINITION + + Servers change over time, and so can the protocol that they use. RPC + provides a version number with each RPC request. This RFC describes + version two of the NFS protocol. Even in the second version, there + are a few obsolete procedures and parameters, which will be removed + in later versions. An RFC for version three of the NFS protocol is + currently under preparation. + +2.1. File System Model + + NFS assumes a file system that is hierarchical, with directories as + all but the bottom level of files. Each entry in a directory (file, + directory, device, etc.) has a string name. Different operating + systems may have restrictions on the depth of the tree or the names + used, as well as using different syntax to represent the "pathname", + which is the concatenation of all the "components" (directory and + file names) in the name. A "file system" is a tree on a single + server (usually a single disk or physical partition) with a specified + "root". Some operating systems provide a "mount" operation to make + all file systems appear as a single tree, while others maintain a + "forest" of file systems. Files are unstructured streams of + uninterpreted bytes. Version 3 of NFS uses slightly more general + file system model. + + NFS looks up one component of a pathname at a time. It may not be + obvious why it does not just take the whole pathname, traipse down + the directories, and return a file handle when it is done. There are + several good reasons not to do this. First, pathnames need + separators between the directory components, and different operating + systems use different separators. We could define a Network Standard + Pathname Representation, but then every pathname would have to be + parsed and converted at each end. Other issues are discussed in + section 3, NFS Implementation Issues. + + Although files and directories are similar objects in many ways, + different procedures are used to read directories and files. This + provides a network standard format for representing directories. The + same argument as above could have been used to justify a procedure + that returns only one directory entry per call. The problem is + efficiency. Directories can contain many entries, and a remote call + to return each would be just too slow. + +2.2. Server Procedures + + The protocol definition is given as a set of procedures with + arguments and results defined using the RPC language (XDR language + extended with program, version, and procedure declarations). A brief + + description of the function of each procedure should provide enough + information to allow implementation. Section 2.3 describes the basic + data types in more detail. + + All of the procedures in the NFS protocol are assumed to be + synchronous. When a procedure returns to the client, the client can + assume that the operation has completed and any data associated with + the request is now on stable storage. For example, a client WRITE + request may cause the server to update data blocks, filesystem + information blocks (such as indirect blocks), and file attribute + information (size and modify times). When the WRITE returns to the + client, it can assume that the write is safe, even in case of a + server crash, and it can discard the data written. This is a very + important part of the statelessness of the server. If the server + waited to flush data from remote requests, the client would have to + save those requests so that it could resend them in case of a server + crash. + + /* + * Remote file service routines + */ + program NFS_PROGRAM { + version NFS_VERSION { + void + NFSPROC_NULL(void) = 0; + + attrstat + NFSPROC_GETATTR(fhandle) = 1; + + attrstat + NFSPROC_SETATTR(sattrargs) = 2; + + void + NFSPROC_ROOT(void) = 3; + + diropres + NFSPROC_LOOKUP(diropargs) = 4; + + readlinkres + NFSPROC_READLINK(fhandle) = 5; + + readres + NFSPROC_READ(readargs) = 6; + + void + NFSPROC_WRITECACHE(void) = 7; + + attrstat + NFSPROC_WRITE(writeargs) = 8; + + diropres + NFSPROC_CREATE(createargs) = 9; + + stat + NFSPROC_REMOVE(diropargs) = 10; + + stat + NFSPROC_RENAME(renameargs) = 11; + + stat + NFSPROC_LINK(linkargs) = 12; + + stat + NFSPROC_SYMLINK(symlinkargs) = 13; + + diropres + NFSPROC_MKDIR(createargs) = 14; + + stat + NFSPROC_RMDIR(diropargs) = 15; + + readdirres + NFSPROC_READDIR(readdirargs) = 16; + + statfsres + NFSPROC_STATFS(fhandle) = 17; + } = 2; + } = 100003; + +2.2.1. Do Nothing + + void + NFSPROC_NULL(void) = 0; + + This procedure does no work. It is made available in all RPC + services to allow server response testing and timing. + +2.2.2. Get File Attributes + + attrstat + NFSPROC_GETATTR (fhandle) = 1; + + If the reply status is NFS_OK, then the reply attributes contains the + attributes for the file given by the input fhandle. + +2.2.3. Set File Attributes + + struct sattrargs { + fhandle file; + sattr attributes; + }; + + attrstat + NFSPROC_SETATTR (sattrargs) = 2; + + The "attributes" argument contains fields which are either -1 or are + the new value for the attributes of "file". If the reply status is + NFS_OK, then the reply attributes have the attributes of the file + after the "SETATTR" operation has completed. + + Notes: The use of -1 to indicate an unused field in "attributes" is + changed in the next version of the protocol. + +2.2.4. Get Filesystem Root + + void + NFSPROC_ROOT(void) = 3; + + Obsolete. This procedure is no longer used because finding the root + file handle of a filesystem requires moving pathnames between client + and server. To do this right, we would have to define a network + standard representation of pathnames. Instead, the function of + looking up the root file handle is done by the MNTPROC_MNT procedure. + (See Appendix A, "Mount Protocol Definition", for details). + +2.2.5. Look Up File Name + + diropres + NFSPROC_LOOKUP(diropargs) = 4; + + If the reply "status" is NFS_OK, then the reply "file" and reply + "attributes" are the file handle and attributes for the file "name" + in the directory given by "dir" in the argument. + +2.2.6. Read From Symbolic Link + + union readlinkres switch (stat status) { + case NFS_OK: + path data; + default: + void; + }; + + readlinkres + NFSPROC_READLINK(fhandle) = 5; + + If "status" has the value NFS_OK, then the reply "data" is the data + in the symbolic link given by the file referred to by the fhandle + argument. + + Notes: Since NFS always parses pathnames on the client, the pathname + in a symbolic link may mean something different (or be meaningless) + on a different client or on the server if a different pathname syntax + is used. + +2.2.7. Read From File + + struct readargs { + fhandle file; + unsigned offset; + unsigned count; + unsigned totalcount; + }; + + union readres switch (stat status) { + case NFS_OK: + fattr attributes; + nfsdata data; + default: + void; + }; + + readres + NFSPROC_READ(readargs) = 6; + + Returns up to "count" bytes of "data" from the file given by "file", + starting at "offset" bytes from the beginning of the file. The first + byte of the file is at offset zero. The file attributes after the + read takes place are returned in "attributes". + + Notes: The argument "totalcount" is unused, and is removed in the + next protocol revision. + +2.2.8. Write to Cache + + void + NFSPROC_WRITECACHE(void) = 7; + + To be used in the next protocol revision. + +2.2.9. Write to File + + struct writeargs { + fhandle file; + unsigned beginoffset; + unsigned offset; + unsigned totalcount; + nfsdata data; + }; + + attrstat + NFSPROC_WRITE(writeargs) = 8; + + Writes "data" beginning "offset" bytes from the beginning of "file". + The first byte of the file is at offset zero. If the reply "status" + is NFS_OK, then the reply "attributes" contains the attributes of the + file after the write has completed. The write operation is atomic. + Data from this "WRITE" will not be mixed with data from another + client's "WRITE". + + Notes: The arguments "beginoffset" and "totalcount" are ignored and + are removed in the next protocol revision. + +2.2.10. Create File + + struct createargs { + diropargs where; + sattr attributes; + }; + + diropres + NFSPROC_CREATE(createargs) = 9; + + The file "name" is created in the directory given by "dir". The + initial attributes of the new file are given by "attributes". A + reply "status" of NFS_OK indicates that the file was created, and + reply "file" and reply "attributes" are its file handle and + attributes. Any other reply "status" means that the operation failed + and no file was created. + + Notes: This routine should pass an exclusive create flag, meaning + "create the file only if it is not already there". + +2.2.11. Remove File + + stat + NFSPROC_REMOVE(diropargs) = 10; + + The file "name" is removed from the directory given by "dir". A + reply of NFS_OK means the directory entry was removed. + + Notes: possibly non-idempotent operation. + +2.2.12. Rename File + + struct renameargs { + diropargs from; + diropargs to; + }; + + stat + NFSPROC_RENAME(renameargs) = 11; + + The existing file "from.name" in the directory given by "from.dir" is + renamed to "to.name" in the directory given by "to.dir". If the + reply is NFS_OK, the file was renamed. The RENAME operation is + atomic on the server; it cannot be interrupted in the middle. + + Notes: possibly non-idempotent operation. + +2.2.13. Create Link to File + + Procedure 12, Version 2. + + struct linkargs { + fhandle from; + diropargs to; + }; + + stat + NFSPROC_LINK(linkargs) = 12; + + Creates the file "to.name" in the directory given by "to.dir", which + is a hard link to the existing file given by "from". If the return + value is NFS_OK, a link was created. Any other return value + indicates an error, and the link was not created. + + A hard link should have the property that changes to either of the + linked files are reflected in both files. When a hard link is made + to a file, the attributes for the file should have a value for + "nlink" that is one greater than the value before the link. + + Notes: possibly non-idempotent operation. + +2.2.14. Create Symbolic Link + + struct symlinkargs { + diropargs from; + path to; + sattr attributes; + }; + + stat + NFSPROC_SYMLINK(symlinkargs) = 13; + + Creates the file "from.name" with ftype NFLNK in the directory given + by "from.dir". The new file contains the pathname "to" and has + initial attributes given by "attributes". If the return value is + NFS_OK, a link was created. Any other return value indicates an + error, and the link was not created. + + A symbolic link is a pointer to another file. The name given in "to" + is not interpreted by the server, only stored in the newly created + file. When the client references a file that is a symbolic link, the + contents of the symbolic link are normally transparently + reinterpreted as a pathname to substitute. A READLINK operation + returns the data to the client for interpretation. + + Notes: On UNIX servers the attributes are never used, since symbolic + links always have mode 0777. + +2.2.15. Create Directory + + diropres + NFSPROC_MKDIR (createargs) = 14; + + The new directory "where.name" is created in the directory given by + "where.dir". The initial attributes of the new directory are given + by "attributes". A reply "status" of NFS_OK indicates that the new + directory was created, and reply "file" and reply "attributes" are + its file handle and attributes. Any other reply "status" means that + the operation failed and no directory was created. + + Notes: possibly non-idempotent operation. + +2.2.16. Remove Directory + + stat + NFSPROC_RMDIR(diropargs) = 15; + + The existing empty directory "name" in the directory given by "dir" + is removed. If the reply is NFS_OK, the directory was removed. + + Notes: possibly non-idempotent operation. + +2.2.17. Read From Directory + + struct readdirargs { + fhandle dir; + nfscookie cookie; + unsigned count; + }; + + struct entry { + unsigned fileid; + filename name; + nfscookie cookie; + entry *nextentry; + }; + + union readdirres switch (stat status) { + case NFS_OK: + struct { + entry *entries; + bool eof; + } readdirok; + default: + void; + }; + + readdirres + NFSPROC_READDIR (readdirargs) = 16; + + Returns a variable number of directory entries, with a total size of + up to "count" bytes, from the directory given by "dir". If the + returned value of "status" is NFS_OK, then it is followed by a + variable number of "entry"s. Each "entry" contains a "fileid" which + consists of a unique number to identify the file within a filesystem, + the "name" of the file, and a "cookie" which is an opaque pointer to + the next entry in the directory. The cookie is used in the next + READDIR call to get more entries starting at a given point in the + directory. The special cookie zero (all bits zero) can be used to + get the entries starting at the beginning of the directory. The + "fileid" field should be the same number as the "fileid" in the the + attributes of the file. (See section "2.3.5. fattr" under "Basic + Data Types".) The "eof" flag has a value of TRUE if there are no + more entries in the directory. + +2.2.18. Get Filesystem Attributes + + union statfsres (stat status) { + case NFS_OK: + struct { + unsigned tsize; + unsigned bsize; + unsigned blocks; + unsigned bfree; + unsigned bavail; + } info; + default: + void; + }; + + statfsres + NFSPROC_STATFS(fhandle) = 17; + + If the reply "status" is NFS_OK, then the reply "info" gives the + attributes for the filesystem that contains file referred to by the + input fhandle. The attribute fields contain the following values: + + tsize The optimum transfer size of the server in bytes. This is + the number of bytes the server would like to have in the + data part of READ and WRITE requests. + + bsize The block size in bytes of the filesystem. + + blocks The total number of "bsize" blocks on the filesystem. + + bfree The number of free "bsize" blocks on the filesystem. + + bavail The number of "bsize" blocks available to non-privileged + users. + + Notes: This call does not work well if a filesystem has variable + size blocks. + +2.3. Basic Data Types + + The following XDR definitions are basic structures and types used in + other structures described further on. + +2.3.1. stat + + enum stat { + NFS_OK = 0, + NFSERR_PERM=1, + + NFSERR_NOENT=2, + NFSERR_IO=5, + NFSERR_NXIO=6, + NFSERR_ACCES=13, + NFSERR_EXIST=17, + NFSERR_NODEV=19, + NFSERR_NOTDIR=20, + NFSERR_ISDIR=21, + NFSERR_FBIG=27, + NFSERR_NOSPC=28, + NFSERR_ROFS=30, + NFSERR_NAMETOOLONG=63, + NFSERR_NOTEMPTY=66, + NFSERR_DQUOT=69, + NFSERR_STALE=70, + NFSERR_WFLUSH=99 + }; + + The "stat" type is returned with every procedure's results. A value + of NFS_OK indicates that the call completed successfully and the + results are valid. The other values indicate some kind of error + occurred on the server side during the servicing of the procedure. + The error values are derived from UNIX error numbers. + + NFSERR_PERM + Not owner. The caller does not have correct ownership to perform + the requested operation. + + NFSERR_NOENT + No such file or directory. The file or directory specified does + not exist. + + NFSERR_IO + Some sort of hard error occurred when the operation was in + progress. This could be a disk error, for example. + + NFSERR_NXIO + No such device or address. + + NFSERR_ACCES + Permission denied. The caller does not have the correct + permission to perform the requested operation. + + NFSERR_EXIST + File exists. The file specified already exists. + + NFSERR_NODEV + No such device. + + NFSERR_NOTDIR + Not a directory. The caller specified a non-directory in a + directory operation. + + NFSERR_ISDIR + Is a directory. The caller specified a directory in a non- + directory operation. + + NFSERR_FBIG + File too large. The operation caused a file to grow beyond the + server's limit. + + NFSERR_NOSPC + No space left on device. The operation caused the server's + filesystem to reach its limit. + + NFSERR_ROFS + Read-only filesystem. Write attempted on a read-only filesystem. + + NFSERR_NAMETOOLONG + File name too long. The file name in an operation was too long. + + NFSERR_NOTEMPTY + Directory not empty. Attempted to remove a directory that was not + empty. + + NFSERR_DQUOT + Disk quota exceeded. The client's disk quota on the server has + been exceeded. + + NFSERR_STALE + The "fhandle" given in the arguments was invalid. That is, the + file referred to by that file handle no longer exists, or access + to it has been revoked. + + NFSERR_WFLUSH + The server's write cache used in the "WRITECACHE" call got flushed + to disk. + +2.3.2. ftype + + enum ftype { + NFNON = 0, + NFREG = 1, + NFDIR = 2, + NFBLK = 3, + NFCHR = 4, + NFLNK = 5 + }; + + The enumeration "ftype" gives the type of a file. The type NFNON + indicates a non-file, NFREG is a regular file, NFDIR is a + directory, NFBLK is a block-special device, NFCHR is a character- + special device, and NFLNK is a symbolic link. + +2.3.3. fhandle + + typedef opaque fhandle[FHSIZE]; + + The "fhandle" is the file handle passed between the server and the + client. All file operations are done using file handles to refer + to a file or directory. The file handle can contain whatever + information the server needs to distinguish an individual file. + +2.3.4. timeval + + struct timeval { + unsigned int seconds; + unsigned int useconds; + }; + + The "timeval" structure is the number of seconds and microseconds + since midnight January 1, 1970, Greenwich Mean Time. It is used + to pass time and date information. + +2.3.5. fattr + + struct fattr { + ftype type; + unsigned int mode; + unsigned int nlink; + unsigned int uid; + unsigned int gid; + unsigned int size; + unsigned int blocksize; + unsigned int rdev; + unsigned int blocks; + + unsigned int fsid; + unsigned int fileid; + timeval atime; + timeval mtime; + timeval ctime; + }; + + The "fattr" structure contains the attributes of a file; "type" is + the type of the file; "nlink" is the number of hard links to the + file (the number of different names for the same file); "uid" is + the user identification number of the owner of the file; "gid" is + the group identification number of the group of the file; "size" + is the size in bytes of the file; "blocksize" is the size in bytes + of a block of the file; "rdev" is the device number of the file if + it is type NFCHR or NFBLK; "blocks" is the number of blocks the + file takes up on disk; "fsid" is the file system identifier for + the filesystem containing the file; "fileid" is a number that + uniquely identifies the file within its filesystem; "atime" is the + time when the file was last accessed for either read or write; + "mtime" is the time when the file data was last modified + (written); and "ctime" is the time when the status of the file was + last changed. Writing to the file also changes "ctime" if the + size of the file changes. + + "Mode" is the access mode encoded as a set of bits. Notice that + the file type is specified both in the mode bits and in the file + type. This is really a bug in the protocol and will be fixed in + future versions. The descriptions given below specify the bit + positions using octal numbers. + + 0040000 This is a directory; "type" field should be NFDIR. + 0020000 This is a character special file; "type" field should + be NFCHR. + 0060000 This is a block special file; "type" field should be + NFBLK. + 0100000 This is a regular file; "type" field should be NFREG. + 0120000 This is a symbolic link file; "type" field should be + NFLNK. + 0140000 This is a named socket; "type" field should be NFNON. + 0004000 Set user id on execution. + 0002000 Set group id on execution. + 0001000 Save swapped text even after use. + 0000400 Read permission for owner. + 0000200 Write permission for owner. + 0000100 Execute and search permission for owner. + 0000040 Read permission for group. + 0000020 Write permission for group. + 0000010 Execute and search permission for group. + + 0000004 Read permission for others. + 0000002 Write permission for others. + 0000001 Execute and search permission for others. + + Notes: The bits are the same as the mode bits returned by the + stat(2) system call in UNIX. The file type is specified both in + the mode bits and in the file type. This is fixed in future + versions. + + The "rdev" field in the attributes structure is an operating + system specific device specifier. It will be removed and + generalized in the next revision of the protocol. + +2.3.6. sattr + + struct sattr { + unsigned int mode; + unsigned int uid; + unsigned int gid; + unsigned int size; + timeval atime; + timeval mtime; + }; + + The "sattr" structure contains the file attributes which can be + set from the client. The fields are the same as for "fattr" + above. A "size" of zero means the file should be truncated. A + value of -1 indicates a field that should be ignored. + +2.3.7. filename + + typedef string filename<MAXNAMLEN>; + + The type "filename" is used for passing file names or pathname + components. + +2.3.8. path + + typedef string path<MAXPATHLEN>; + + The type "path" is a pathname. The server considers it as a + string with no internal structure, but to the client it is the + name of a node in a filesystem tree. + +2.3.9. attrstat + + union attrstat switch (stat status) { + case NFS_OK: + + fattr attributes; + default: + void; + }; + + The "attrstat" structure is a common procedure result. It + contains a "status" and, if the call succeeded, it also contains + the attributes of the file on which the operation was done. + +2.3.10. diropargs + + struct diropargs { + fhandle dir; + filename name; + }; + + The "diropargs" structure is used in directory operations. The + "fhandle" "dir" is the directory in which to find the file "name". + A directory operation is one in which the directory is affected. + +2.3.11. diropres + + union diropres switch (stat status) { + case NFS_OK: + struct { + fhandle file; + fattr attributes; + } diropok; + default: + void; + }; + + The results of a directory operation are returned in a "diropres" + structure. If the call succeeded, a new file handle "file" and + the "attributes" associated with that file are returned along with + the "status". + +3. NFS IMPLEMENTATION ISSUES + + The NFS protocol was designed to allow different operating systems to + share files. However, since it was designed in a UNIX environment, + many operations have semantics similar to the operations of the UNIX + file system. This section discusses some of the implementation- + specific details and semantic issues. + +3.1. Server/Client Relationship + + The NFS protocol is designed to allow servers to be as simple and + + general as possible. Sometimes the simplicity of the server can be a + problem, if the client wants to implement complicated filesystem + semantics. + + For example, some operating systems allow removal of open files. A + process can open a file and, while it is open, remove it from the + directory. The file can be read and written as long as the process + keeps it open, even though the file has no name in the filesystem. + It is impossible for a stateless server to implement these semantics. + The client can do some tricks such as renaming the file on remove, + and only removing it on close. We believe that the server provides + enough functionality to implement most file system semantics on the + client. + + Every NFS client can also potentially be a server, and remote and + local mounted filesystems can be freely intermixed. This leads to + some interesting problems when a client travels down the directory + tree of a remote filesystem and reaches the mount point on the server + for another remote filesystem. Allowing the server to follow the + second remote mount would require loop detection, server lookup, and + user revalidation. Instead, we decided not to let clients cross a + server's mount point. When a client does a LOOKUP on a directory on + which the server has mounted a filesystem, the client sees the + underlying directory instead of the mounted directory. + + For example, if a server has a file system called "/usr" and mounts + another file system on "/usr/src", if a client mounts "/usr", it + does NOT see the mounted version of "/usr/src". A client could do + remote mounts that match the server's mount points to maintain the + server's view. In this example, the client would also have to mount + "/usr/src" in addition to "/usr", even if they are from the same + server. + +3.2. Pathname Interpretation + + There are a few complications to the rule that pathnames are always + parsed on the client. For example, symbolic links could have + different interpretations on different clients. Another common + problem for non-UNIX implementations is the special interpretation of + the pathname ".." to mean the parent of a given directory. The next + revision of the protocol uses an explicit flag to indicate the parent + instead. + +3.3. Permission Issues + + The NFS protocol, strictly speaking, does not define the permission + checking used by servers. However, it is expected that a server will + do normal operating system permission checking using AUTH_UNIX style + + authentication as the basis of its protection mechanism. The server + gets the client's effective "uid", effective "gid", and groups on + each call and uses them to check permission. There are various + problems with this method that can been resolved in interesting ways. + + Using "uid" and "gid" implies that the client and server share the + same "uid" list. Every server and client pair must have the same + mapping from user to "uid" and from group to "gid". Since every + client can also be a server, this tends to imply that the whole + network shares the same "uid/gid" space. AUTH_DES (and the next + revision of the NFS protocol) uses string names instead of numbers, + but there are still complex problems to be solved. + + Another problem arises due to the usually stateful open operation. + Most operating systems check permission at open time, and then check + that the file is open on each read and write request. With stateless + servers, the server has no idea that the file is open and must do + permission checking on each read and write call. On a local + filesystem, a user can open a file and then change the permissions so + that no one is allowed to touch it, but will still be able to write + to the file because it is open. On a remote filesystem, by contrast, + the write would fail. To get around this problem, the server's + permission checking algorithm should allow the owner of a file to + access it regardless of the permission setting. + + A similar problem has to do with paging in from a file over the + network. The operating system usually checks for execute permission + before opening a file for demand paging, and then reads blocks from + the open file. The file may not have read permission, but after it + is opened it does not matter. An NFS server can not tell the + difference between a normal file read and a demand page-in read. To + make this work, the server allows reading of files if the "uid" given + in the call has either execute or read permission on the file. + + In most operating systems, a particular user (on UNIX, the user ID + zero) has access to all files no matter what permission and ownership + they have. This "super-user" permission may not be allowed on the + server, since anyone who can become super-user on their workstation + could gain access to all remote files. The UNIX server by default + maps user id 0 to -2 before doing its access checking. This works + except for NFS root filesystems, where super-user access cannot be + avoided. + +3.4. RPC Information + + Authentication + The NFS service uses AUTH_UNIX, AUTH_DES, or AUTH_SHORT style + authentication, except in the NULL procedure where AUTH_NONE is + + also allowed. + + Transport Protocols + NFS is supported normally on UDP. + + Port Number + The NFS protocol currently uses the UDP port number 2049. This is + not an officially assigned port, so later versions of the protocol + use the "Portmapping" facility of RPC. + +3.5. Sizes of XDR Structures + + These are the sizes, given in decimal bytes, of various XDR + structures used in the protocol: + + /* + * The maximum number of bytes of data in a READ or WRITE + * request. + */ + const MAXDATA = 8192; + + /* The maximum number of bytes in a pathname argument. */ + const MAXPATHLEN = 1024; + + /* The maximum number of bytes in a file name argument. */ + const MAXNAMLEN = 255; + + /* The size in bytes of the opaque "cookie" passed by READDIR. */ + const COOKIESIZE = 4; + + /* The size in bytes of the opaque file handle. */ + const FHSIZE = 32; + +3.6. Setting RPC Parameters + + Various file system parameters and options should be set at mount + time. The mount protocol is described in the appendix below. For + example, "Soft" mounts as well as "Hard" mounts are usually both + provided. Soft mounted file systems return errors when RPC + operations fail (after a given number of optional retransmissions), + while hard mounted file systems continue to retransmit forever. The + maximum transfer sizes are implementation dependent. For efficient + operation over a local network, 8192 bytes of data are normally used. + This may result in lower-level fragmentation (such as at the IP + level). Since some network interfaces may not allow such packets, + for operation over slower-speed networks or hosts, or through + gateways, transfer sizes of 512 or 1024 bytes often provide better + results. + + Clients and servers may need to keep caches of recent operations to + help avoid problems with non-idempotent operations. For example, if + the transport protocol drops the response for a Remove File + operation, upon retransmission the server may return an error code of + NFSERR_NOENT instead of NFS_OK. But if the server keeps around the + last operation requested and its result, it could return the proper + success code. Of course, the server could be crashed and rebooted + between retransmissions, but a small cache (even a single entry) + would solve most problems. + + Appendix A. MOUNT PROTOCOL DEFINITION + +A.1. Introduction + + The mount protocol is separate from, but related to, the NFS + protocol. It provides operating system specific services to get the + NFS off the ground -- looking up server path names, validating user + identity, and checking access permissions. Clients use the mount + protocol to get the first file handle, which allows them entry into a + remote filesystem. + + The mount protocol is kept separate from the NFS protocol to make it + easy to plug in new access checking and validation methods without + changing the NFS server protocol. + + Notice that the protocol definition implies stateful servers because + the server maintains a list of client's mount requests. The mount + list information is not critical for the correct functioning of + either the client or the server. It is intended for advisory use + only, for example, to warn possible clients when a server is going + down. + + Version one of the mount protocol is used with version two of the NFS + protocol. The only information communicated between these two + protocols is the "fhandle" structure. + +A.2. RPC Information + + Authentication + The mount service uses AUTH_UNIX and AUTH_NONE style + authentication only. + + Transport Protocols + The mount service is supported on both UDP and TCP. + + Port Number + Consult the server's portmapper, described in RFC 1057 </rfcs/rfc1057.html>, "RPC: + Remote Procedure Call Protocol Specification", to find the port + number on which the mount service is registered. + +A.3. Sizes of XDR Structures + + These are the sizes, given in decimal bytes, of various XDR + structures used in the protocol: + + /* The maximum number of bytes in a pathname argument. */ + const MNTPATHLEN = 1024; + + /* The maximum number of bytes in a name argument. */ + const MNTNAMLEN = 255; + + /* The size in bytes of the opaque file handle. */ + const FHSIZE = 32; + +A.4. Basic Data Types + + This section presents the data types used by the mount protocol. In + many cases they are similar to the types used in NFS. + +A.4.1. fhandle + + typedef opaque fhandle[FHSIZE]; + + The type "fhandle" is the file handle that the server passes to the + client. All file operations are done using file handles to refer to + a file or directory. The file handle can contain whatever + information the server needs to distinguish an individual file. + + This is the same as the "fhandle" XDR definition in version 2 of the + NFS protocol; see section "2.3.3. fhandle" under "Basic Data Types". + +A.4.2. fhstatus + + union fhstatus switch (unsigned status) { + case 0: + fhandle directory; + default: + void; + } + + The type "fhstatus" is a union. If a "status" of zero is returned, + the call completed successfully, and a file handle for the + "directory" follows. A non-zero status indicates some sort of error. + In this case, the status is a UNIX error number. + +A.4.3. dirpath + + typedef string dirpath<MNTPATHLEN>; + + The type "dirpath" is a server pathname of a directory. + +A.4.4. name + + typedef string name<MNTNAMLEN>; + + The type "name" is an arbitrary string used for various names. + +A.5. Server Procedures + + The following sections define the RPC procedures supplied by a mount + server. + + /* + * Protocol description for the mount program + */ + program MOUNTPROG { + /* + * Version 1 of the mount protocol used with + * version 2 of the NFS protocol. + */ + version MOUNTVERS { + + void + MOUNTPROC_NULL(void) = 0; + + fhstatus + MOUNTPROC_MNT(dirpath) = 1; + + mountlist + MOUNTPROC_DUMP(void) = 2; + + void + MOUNTPROC_UMNT(dirpath) = 3; + + void + MOUNTPROC_UMNTALL(void) = 4; + + exportlist + MOUNTPROC_EXPORT(void) = 5; + } = 1; + } = 100005; + +A.5.1. Do Nothing + + void + MNTPROC_NULL(void) = 0; + + This procedure does no work. It is made available in all RPC + services to allow server response testing and timing. + +A.5.2. Add Mount Entry + + fhstatus + MNTPROC_MNT(dirpath) = 1; + + If the reply "status" is 0, then the reply "directory" contains the + file handle for the directory "dirname". This file handle may be + used in the NFS protocol. This procedure also adds a new entry to + the mount list for this client mounting "dirname". + +A.5.3. Return Mount Entries + + struct *mountlist { + name hostname; + dirpath directory; + mountlist nextentry; + }; + + mountlist + MNTPROC_DUMP(void) = 2; + + Returns the list of remote mounted filesystems. The "mountlist" + contains one entry for each "hostname" and "directory" pair. + +A.5.4. Remove Mount Entry + + void + MNTPROC_UMNT(dirpath) = 3; + + Removes the mount list entry for the input "dirpath". + +A.5.5. Remove All Mount Entries + + void + MNTPROC_UMNTALL(void) = 4; + + Removes all of the mount list entries for this client. + +A.5.6. Return Export List + + struct *groups { + name grname; + groups grnext; + }; + + struct *exportlist { + dirpath filesys; + groups groups; + exportlist next; + }; + + exportlist + MNTPROC_EXPORT(void) = 5; + + Returns a variable number of export list entries. Each entry + contains a filesystem name and a list of groups that are allowed to + import it. The filesystem name is in "filesys", and the group name + is in the list "groups". + + Notes: The exportlist should contain more information about the + status of the filesystem, such as a read-only flag. + +Author's Address: + + Bill Nowicki + Sun Microsystems, Inc. + Mail Stop 1-40 + 2550 Garcia Avenue + Mountain View, CA 94043 + + Phone: (415) 336-7278 + + Email: nowicki@SUN.COM <mailto:nowicki@SUN.COM> + +Comment on RFC 1094 </rfccomment.php?rfcnum=1094> + + + +Comments about this RFC: + + * RFC 1094: I am preparing for doing a project in File System in + Network. can you specify... </qa/rfcc-377.html> by Ravik (12/4/2003) + + +Previous: RFC 1093 - NSFNET routing architecture </rfcs/rfc1093.html> + + + +Next: RFC 1095 - Common Management Information Services and Protocol +over TCP/IP (CMOT) </rfcs/rfc1095.html> + + + +------------------------------------------------------------------------ diff --git a/cpukit/libfs/src/nfsclient/src/cexphelp.c b/cpukit/libfs/src/nfsclient/src/cexphelp.c new file mode 100644 index 0000000000..d0406ad33a --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/cexphelp.c @@ -0,0 +1,20 @@ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <librtemsNfs.h> +#include <cexpHelp.h> +CEXP_HELP_TAB_BEGIN(rtemsNfs) + HELP( +"Mount a remote filesystem (NFS). The mount point (must not be a NFS dir)\n" +"is created on the fly if not existing already.\n" +"uid/gid to use may be specified:\n" +" hostspec: [uid.gid@]hostname_or_ipaddr\n" + , int, nfsMount, (char *hostspec, char *exportdir, char *mntpoint) + ), + HELP( +"Print all currently mounted NFS directories to open file handle.\n" +"Pass f = 0 to print to stdout\n" + , int, nfsMountsShow, (FILE *f) + ), +CEXP_HELP_TAB_END diff --git a/cpukit/libfs/src/nfsclient/src/dirutils.c b/cpukit/libfs/src/nfsclient/src/dirutils.c new file mode 100644 index 0000000000..99c65876f3 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/dirutils.c @@ -0,0 +1,385 @@ +/* $Id$ */ + +/* very crude and basic fs utilities for testing the NFS */ + +/* Till Straumann, <strauman@slac.stanford.edu>, 10/2002 */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __vxworks +#include <vxWorks.h> +#endif +#include <stdio.h> +#include <dirent.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <limits.h> /* PATH_MAX */ + +#include <inttypes.h> /* PRI* */ + +#if SIZEOF_MODE_T == 8 +#define PRIomode_t PRIo64 +#elif SIZEOF_MODE_T == 4 +#define PRIomode_t PRIo32 +#else +#error "unsupport size of mode_t" +#endif + +#if SIZEOF_OFF_T == 8 +#define PRIdoff_t PRIo64 +#elif SIZEOF_OFF_T == 4 +#define PRIdoff_t PRIo32 +#else +#error "unsupported size of off_t" +#endif + +#ifdef HAVE_CEXP +#include <cexpHelp.h> +#endif + +#ifndef __vxworks +int +pwd(void) +{ +char buf[PATH_MAX]; + + if ( !getcwd(buf,PATH_MAX)) { + perror("getcwd"); + return -1; + } else { + printf("%s\n",buf); + } + return 0; +} + +static int +ls_r(char *path, char *chpt, char *name, struct stat *buf) +{ +char *t; + sprintf(chpt, "/%s", name); + if (lstat(path,buf)) { + fprintf(stderr,"stat(%s): %s\n", path, strerror(errno)); + return -1; + } + switch ( buf->st_mode & S_IFMT ) { + case S_IFSOCK: + case S_IFIFO: t = "|"; break; + + default: + case S_IFREG: + case S_IFBLK: + case S_IFCHR: + t = ""; break; + case S_IFDIR: + t = "/"; break; + case S_IFLNK: + t = "@"; break; + } + + printf("%10li, %10" PRIdoff_t "b, %5i.%-5i 0%04" PRIomode_t " %s%s\n", + buf->st_ino, + buf->st_size, + buf->st_uid, + buf->st_gid, + buf->st_mode & ~S_IFMT, + name, + t); + *chpt = 0; + return 0; +} + +int +ls(char *dir, char *opts) +{ +struct dirent *de; +char path[PATH_MAX+1]; +char *chpt; +DIR *dp = 0; +int rval = -1; +struct stat buf; + + if ( !dir ) + dir = "."; + + strncpy(path, dir, PATH_MAX); + path[PATH_MAX] = 0; + chpt = path+strlen(path); + + if ( !(dp=opendir(dir)) ) { + perror("opendir"); + goto cleanup; + } + + while ( (de = readdir(dp)) ) { + ls_r(path, chpt, de->d_name, &buf); + } + + rval = 0; + +cleanup: + if (dp) + closedir(dp); + return rval; +} +#endif + +#if 0 + fprintf(stderr, "usage: cp(""from"",[""to""[,""-f""]]\n"); + fprintf(stderr, " ""to""==NULL -> stdout\n"); + fprintf(stderr, " ""-f"" -> overwrite existing file\n"); +#endif + +int +cp(char *from, char *to, char *opts) +{ +struct stat st; +int rval = -1; +int fd = -1; +FILE *fst = 0; +FILE *tst = 0; +int flags = O_CREAT | O_WRONLY | O_TRUNC | O_EXCL; + + if (from) { + + if ((fd=open(from,O_RDONLY,0)) < 0) { + fprintf(stderr, + "Opening %s for reading: %s\n", + from, + strerror(errno)); + goto cleanup; + } + + if (fstat(fd, &st)) { + fprintf(stderr, + "rstat(%s): %s\n", + from, + strerror(errno)); + goto cleanup; + } + + + if (!S_ISREG(st.st_mode)) { + fprintf(stderr,"Refuse to copy a non-regular file\n"); + errno = EINVAL; + goto cleanup; + } + /* Now create a stream -- I experienced occasional weirdness + * when circumventing the streams attached to fildno(stdin) + * by reading/writing to the underlying fd's directly -> + * for now we always go through buffered I/O... + */ + if ( !(fst=fdopen(fd,"r")) ) { + fprintf(stderr, + "Opening input stream [fdopen()] failed: %s\n", + strerror(errno)); + goto cleanup; + } + /* at this point, we have a stream and don't need 'fd' anymore */ + fd = -1; + + } else { + fst = stdin; + st.st_mode = 0644; + } + + if (opts && strchr(opts,'f')) + flags &= ~ O_EXCL; + + if (to) { + if ( (fd=open(to,flags,st.st_mode)) < 0 ) { + fprintf(stderr, + "Opening %s for writing: %s\n", + to, + strerror(errno)); + goto cleanup; + } + if ( !(tst=fdopen(fd, "w")) ) { + fprintf(stderr, + "Opening output stream [fdopen()] failed: %s\n", + strerror(errno)); + goto cleanup; + } + /* at this point we have a stream and don't need 'fd' anymore */ + fd = -1; + } else { + tst = stdout; + } + + /* clear old errors */ + clearerr(fst); + clearerr(tst); + + /* use macro versions on register vars; stdio is already buffered, + * there's nothing to be gained by reading/writing blocks into + * a secondary buffer... + */ + { + register int ch; + register FILE *f = fst; + register FILE *t = tst; + while ( EOF != (ch = getc(f)) && EOF != putc(ch, t) ) + /* nothing else */; + } + + if ( ferror(fst) ) { + fprintf(stderr,"Read error: %s\n",strerror(errno)); + goto cleanup; + } + if ( ferror(tst) ) { + fprintf(stderr,"Write error: %s\n",strerror(errno)); + goto cleanup; + } + + rval = 0; + +cleanup: + + if ( fd >= 0 ) + close(fd); + + if ( fst ) { + if ( from ) + fclose(fst); + else + clearerr(fst); + } + if ( tst ) { + if ( to ) + fclose(tst); + else { + /* flush stdout */ + fflush(tst); + clearerr(tst); + } + } + + return rval; +} + +int +ln(char *to, char *name, char *opts) +{ + if (!to) { + fprintf(stderr,"ln: need 'to' argument\n"); + return -1; + } + if (!name) { + if ( !(name = strrchr(to,'/')) ) { + fprintf(stderr, + "ln: 'unable to link %s to %s\n", + to,to); + return -1; + } + name++; + } + if (opts || strchr(opts,'s')) { + if (symlink(name,to)) { + fprintf(stderr,"symlink: %s\n",strerror(errno)); + return -1; + } + } else { + if (link(name,to)) { + fprintf(stderr,"hardlink: %s\n",strerror(errno)); + return -1; + } + } + return 0; +} + +int +rm(char *path) +{ + return unlink(path); +} + +int +cd(char *path) +{ + return chdir(path); +} + +#ifdef HAVE_CEXP +static CexpHelpTabRec _cexpHelpTabDirutils[] __attribute__((unused)) = { + HELP( +"copy a file: cp(""from"",[""to""[,""-f""]])\n\ + from = NULL <-- stdin\n\ + to = NULL --> stdout\n\ + option -f: overwrite existing file\n", + int, + cp, (char *from, char *to, char *options) + ), + HELP( +"list a directory: ls([""dir""])\n", + int, + ls, (char *dir) + ), + HELP( +"remove a file\n", + int, + rm, (char *path) + ), + HELP( +"change the working directory\n", + int, + cd, (char *path) + ), + HELP( +"create a link: ln(""to"",""name"",""[-s]""\n\ + -s creates a symlink\n", + int, + ln, (char *to, char *name, char *options) + ), + HELP("",,0,) +}; +#endif diff --git a/cpukit/libfs/src/nfsclient/src/librtemsNfs.h b/cpukit/libfs/src/nfsclient/src/librtemsNfs.h new file mode 100644 index 0000000000..fc17d1de7f --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/librtemsNfs.h @@ -0,0 +1,180 @@ +#ifndef LIB_RTEMS_NFS_CLIENT_H +#define LIB_RTEMS_NFS_CLIENT_H +/* $Id$ */ + +/* public interface to the NFS client library for RTEMS */ + +/* Author: Till Straumann <strauman@slac.stanford.edu> 2002-2003 */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <sys/stat.h> +#include <dirent.h> +#include <netdb.h> +#include <ctype.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* RPCIO driver interface. + * If you need RPCIO for other purposes than NFS + * you may want to include <rpcio.h> +#include "rpcio.h" + */ + +/* Priority of daemon; may be setup prior to calling rpcUdpInit(); + * otherwise the network task priority from the rtems_bsdnet_config + * is used... + */ +extern rtems_task_priority rpciodPriority; + +/* Initialize the driver. + * + * Note, called in nfsfs initialise when mount is called. + * + * RETURNS: 0 on success, -1 on failure + */ +int +rpcUdpInit(void); + +/* Cleanup/Stop + * + * RETURNS: 0 on success, nonzero if still in use + */ +int +rpcUdpCleanup(void); + +/* NFS driver interface */ + +/* Initialize the NFS driver. + * + * NOTE: The RPCIO driver must have been initialized prior to + * calling this. + * + * Note, called in nfsfs initialise when mount is called with defaults. + * + * ARGS: depth of the small and big + * transaction pools, i.e. how + * many transactions (buffers) + * should always be kept around. + * + * (If more transactions are needed, + * they are created and destroyed + * on the fly). + * + * Supply zero values to have the + * driver chose reasonable defaults. + */ +int +nfsInit(int smallPoolDepth, int bigPoolDepth); + +/* Driver cleanup code + * + * RETURNS: 0 on success, nonzero if still in use + */ +int +nfsCleanup(void); + +/* Dump a list of the currently mounted NFS to a file + * (stdout is used in case f==NULL) + */ +int +nfsMountsShow(FILE *f); + +/* + * Filesystem mount table mount handler. Do not call, use the mount call. + */ +int +rtems_nfs_initialize(rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data); + +/* A utility routine to find the path leading to a + * rtems_filesystem_location_info_t node. + * + * This should really be present in libcsupport... + * + * INPUT: 'loc' and a buffer 'buf' (length 'len') to hold the + * path. + * OUTPUT: path copied into 'buf' + * + * RETURNS: 0 on success, RTEMS error code on error. + */ +rtems_status_code +rtems_filesystem_resolve_location(char *buf, int len, rtems_filesystem_location_info_t *loc); + +/* Set the timeout (initial default: 10s) for NFS and mount calls. + * + * RETURNS 0 on success, nonzero if the requested timeout is less than + * a clock tick or if the system clock rate cannot be determined. + */ + +int +nfsSetTimeout(uint32_t timeout_ms); + +/* Read current timeout (in milliseconds) */ +uint32_t +nfsGetTimeout(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libfs/src/nfsclient/src/nfs.c b/cpukit/libfs/src/nfsclient/src/nfs.c new file mode 100644 index 0000000000..b8a7ffdb57 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/nfs.c @@ -0,0 +1,3354 @@ +/* $Id$ */ + +/* NFS client implementation for RTEMS; hooks into the RTEMS filesystem */ + +/* Author: Till Straumann <strauman@slac.stanford.edu> 2002 */ + +/* Hacked on by others. */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <sys/stat.h> +#include <dirent.h> +#include <netdb.h> +#include <ctype.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <nfs_prot.h> +#include <mount_prot.h> + +#include "rpcio.h" + +/* Configurable parameters */ + +/* Estimated average length of a filename (including terminating 0). + * This was calculated by doing + * + * find <some root> -print -exec basename '{}' \; > feil + * wc feil + * + * AVG_NAMLEN = (num_chars + num_lines)/num_lines + */ +#define CONFIG_AVG_NAMLEN 10 + +#define CONFIG_NFS_SMALL_XACT_SIZE 800 /* size of RPC arguments for non-write ops */ +/* lifetime of NFS attributes in a NfsNode; + * the time is in seconds and the lifetime is + * infinite if the symbol is #undef + */ +#define CONFIG_ATTR_LIFETIME 10/*secs*/ + +/* + * The 'st_blksize' (stat(2)) value this nfs + * client should report. If set to zero then the server's fattr data + * is passed throught which is not necessary optimal. + * Newlib's stdio uses 'st_blksize' (if built with HAVE_BLKSIZE defined) + * to size the default buffer. + * Due to the overhead of NFS it is probably better to use the maximum + * size of an NFS read request (8k) rather than the optimal block + * size on the server. + * This value can be overridden at run-time by setting the global + * variable 'nfsStBlksize'. + * Thanks to Steven Johnson <sjohnson@sakuraindustries.com> for helping + * working on this issue. + */ +#define DEFAULT_NFS_ST_BLKSIZE NFS_MAXDATA + +/* dont change this without changing the maximal write size */ +#define CONFIG_NFS_BIG_XACT_SIZE UDPMSGSIZE /* dont change this */ + +/* The real values for these are specified further down */ +#define NFSCALL_TIMEOUT (&_nfscalltimeout) +#define MNTCALL_TIMEOUT (&_nfscalltimeout) +static struct timeval _nfscalltimeout = { 10, 0 }; /* {secs, us } */ + +/* More or less fixed constants; in particular, NFS3 is not supported */ +#define DELIM '/' +#define HOSTDELIM ':' +#define UPDIR ".." +#define UIDSEP '@' +#define NFS_VERSION_2 NFS_VERSION + +/* we use a dynamically assigned major number */ +#define NFS_MAJOR (nfsGlob.nfs_major) + + +/* NOTE: RTEMS (ss-20020301) uses a 'short st_ino' type :-( but the + * NFS fileid is 32 bit. [Later versions of RTEMS have fixed this; + * nfsInit() issues a warning if you run a version with 'short st_ino'.] + * + * As a workarount, we merge the upper 16bits of the fileid into the + * minor device no. Hence, it is still possible to uniquely identify + * a file by looking at its device number (major = nfs, minor = part + * of the fileid + our 'nfs-id' identifier). + * + * This has an impact on performance, as e.g. getcwd() stats() all + * directory entries when it believes it has crossed a mount point + * (a.st_dev != b.st_dev). + * + * OTOH, it also might cause node comparison failure! E.g. 'getcwd()' + * assumes that two nodes residing in the same directory must be located + * on the same device and hence compares 'st_ino' only. + * If two files in the same directory have the same inode number + * modulo 2^16, they will be considered equal (although their device + * number doesn't match - getcwd doesn't look at it). + * + * Other software might or might not be affected. + * + * The only clean solution to this problem is bumping up the size of + * 'ino_t' at least to 'long'. + * Note that this requires _all_ software (libraries etc.) to be + * recompiled. + */ + +#define NFS_MAKE_DEV_T_INO_HACK(node) \ + rtems_filesystem_make_dev_t( NFS_MAJOR, \ + (((rtems_device_minor_number)((node)->nfs->id))<<16) | (((rtems_device_minor_number)SERP_ATTR((node)).fileid) >> 16) ) + +/* use our 'nfs id' and the server's fsid for the minor device number + * this should be fairly unique + */ +#define NFS_MAKE_DEV_T(node) \ + rtems_filesystem_make_dev_t( NFS_MAJOR, \ + (((rtems_device_minor_number)((node)->nfs->id))<<16) | (SERP_ATTR((node)).fsid & (((rtems_device_minor_number)1<<16)-1)) ) + +#define DIRENT_HEADER_SIZE ( sizeof(struct dirent) - \ + sizeof( ((struct dirent *)0)->d_name ) ) + + +/* debugging flags */ +#define DEBUG_COUNT_NODES (1<<0) +#define DEBUG_TRACK_NODES (1<<1) +#define DEBUG_EVALPATH (1<<2) +#define DEBUG_READDIR (1<<3) +#define DEBUG_SYSCALLS (1<<4) + +/* #define DEBUG ( DEBUG_SYSCALLS | DEBUG_COUNT_NODES ) */ + +#ifdef DEBUG +#define STATIC +#else +#define STATIC static +#endif + +#define MUTEX_ATTRIBUTES (RTEMS_LOCAL | \ + RTEMS_PRIORITY | \ + RTEMS_INHERIT_PRIORITY | \ + RTEMS_BINARY_SEMAPHORE) + +#define LOCK(s) do { \ + rtems_semaphore_obtain((s), \ + RTEMS_WAIT, \ + RTEMS_NO_TIMEOUT); \ + } while (0) + +#define UNLOCK(s) do { rtems_semaphore_release((s)); \ + } while (0) + +/***************************************** + Types with Associated XDR Routines + *****************************************/ + +/* a string buffer with a maximal length. + * If the buffer pointer is NULL, it is updated + * with an appropriately allocated area. + */ +typedef struct strbuf { + char *buf; + u_int max; +} strbuf; + +static bool_t +xdr_strbuf(XDR *xdrs, strbuf *obj) +{ + return xdr_string(xdrs, &obj->buf, obj->max); +} + +/* Read 'readlink' results into a 'strbuf'. + * This is convenient as it avoids + * one extra step of copying / lenght + * checking. + */ +typedef struct readlinkres_strbuf { + nfsstat status; + strbuf strbuf; +} readlinkres_strbuf; + +static bool_t +xdr_readlinkres_strbuf(XDR *xdrs, readlinkres_strbuf *objp) +{ + if ( !xdr_nfsstat(xdrs, &objp->status) ) + return FALSE; + + if ( NFS_OK == objp->status ) { + if ( !xdr_string(xdrs, &objp->strbuf.buf, objp->strbuf.max) ) + return FALSE; + } + return TRUE; +} + + +/* DirInfoRec is used instead of dirresargs + * to convert recursion into iteration. The + * 'rpcgen'erated xdr_dirresargs ends up + * doing nested calls when unpacking the + * 'next' pointers. + */ + +typedef struct DirInfoRec_ { + readdirargs readdirargs; + /* clone of the 'readdirres' fields; + * the cookie is put into the readdirargs above + */ + nfsstat status; + char *buf, *ptr; + int len; + bool_t eofreached; +} DirInfoRec, *DirInfo; + +/* this deals with one entry / record */ +static bool_t +xdr_dir_info_entry(XDR *xdrs, DirInfo di) +{ +union { + char nambuf[NFS_MAXNAMLEN+1]; + nfscookie cookie; +} dummy; +struct dirent *pde = (struct dirent *)di->ptr; +u_int fileid; +char *name; +register int nlen = 0,len,naligned = 0; +nfscookie *pcookie; + + len = di->len; + + if ( !xdr_u_int(xdrs, &fileid) ) + return FALSE; + + /* we must pass the address of a char* */ + name = (len > NFS_MAXNAMLEN) ? pde->d_name : dummy.nambuf; + + if ( !xdr_filename(xdrs, &name) ) { + return FALSE; + } + + if (len >= 0) { + nlen = strlen(name); + naligned = nlen + 1 /* string delimiter */ + 3 /* alignment */; + naligned &= ~3; + len -= naligned; + } + + /* if the cookie goes into the DirInfo, we hope this doesn't fail + * - the caller ends up with an invalid readdirargs cookie otherwise... + */ + pcookie = (len >= 0) ? &di->readdirargs.cookie : &dummy.cookie; + if ( !xdr_nfscookie(xdrs, pcookie) ) { + return FALSE; + } + + di->len = len; + /* adjust the buffer pointer */ + if (len >= 0) { + pde->d_ino = fileid; + pde->d_namlen = nlen; + pde->d_off = di->ptr - di->buf; + if (name == dummy.nambuf) { + memcpy(pde->d_name, dummy.nambuf, nlen + 1); + } + pde->d_reclen = DIRENT_HEADER_SIZE + naligned; + di->ptr += pde->d_reclen; + } + + return TRUE; +} + +/* this routine loops over all entries */ +static bool_t +xdr_dir_info(XDR *xdrs, DirInfo di) +{ +DirInfo dip; + + if ( !xdr_nfsstat(xdrs, &di->status) ) + return FALSE; + + if ( NFS_OK != di->status ) + return TRUE; + + dip = di; + + while (dip) { + /* reserve space for the dirent 'header' - we assume it's word aligned! */ +#ifdef DEBUG + assert( DIRENT_HEADER_SIZE % 4 == 0 ); +#endif + dip->len -= DIRENT_HEADER_SIZE; + + /* we pass a 0 size - size is unused since + * we always pass a non-NULL pointer + */ + if ( !xdr_pointer(xdrs, (void*)&dip, 0 /* size */, (xdrproc_t)xdr_dir_info_entry) ) + return FALSE; + } + + if ( ! xdr_bool(xdrs, &di->eofreached) ) + return FALSE; + + /* if everything fits into the XDR buffer but not the user's buffer, + * they must resume reading from where xdr_dir_info_entry() started + * skipping and 'eofreached' needs to be adjusted + */ + if ( di->len < 0 && di->eofreached ) + di->eofreached = FALSE; + + return TRUE; +} + + +/* a type better suited for node operations + * than diropres. + * fattr and fhs are swapped so parts of this + * structure may be used as a diroparg which + * is practical when looking up paths. + */ + +/* Macro for accessing serporid fields + */ +#define SERP_ARGS(node) ((node)->serporid.serporid_u.serporid.arg_u) +#define SERP_ATTR(node) ((node)->serporid.serporid_u.serporid.attributes) +#define SERP_FILE(node) ((node)->serporid.serporid_u.serporid.file) + + +typedef struct serporidok { + fattr attributes; + nfs_fh file; + union { + struct { + filename name; + } diroparg; + struct { + sattr attributes; + } sattrarg; + struct { + uint32_t offset; + uint32_t count; + uint32_t totalcount; + } readarg; + struct { + uint32_t beginoffset; + uint32_t offset; + uint32_t totalcount; + struct { + uint32_t data_len; + char* data_val; + } data; + } writearg; + struct { + filename name; + sattr attributes; + } createarg; + struct { + filename name; + diropargs to; + } renamearg; + struct { + diropargs to; + } linkarg; + struct { + filename name; + nfspath to; + sattr attributes; + } symlinkarg; + struct { + nfscookie cookie; + uint32_t count; + } readdirarg; + } arg_u; +} serporidok; + +typedef struct serporid { + nfsstat status; + union { + serporidok serporid; + } serporid_u; +} serporid; + +/* an XDR routine to encode/decode the inverted diropres + * into an nfsnodestat; + * + * NOTE: this routine only acts on + * - 'serporid.status' + * - 'serporid.file' + * - 'serporid.attributes' + * and leaves the 'arg_u' alone. + * + * The idea is that a 'diropres' is read into 'serporid' + * which can then be used as an argument to subsequent + * NFS-RPCs (after filling in the node's arg_u). + */ +static bool_t +xdr_serporidok(XDR *xdrs, serporidok *objp) +{ + if (!xdr_nfs_fh (xdrs, &objp->file)) + return FALSE; + if (!xdr_fattr (xdrs, &objp->attributes)) + return FALSE; + return TRUE; +} + +static bool_t +xdr_serporid(XDR *xdrs, serporid *objp) +{ + if (!xdr_nfsstat (xdrs, &objp->status)) + return FALSE; + switch (objp->status) { + case NFS_OK: + if (!xdr_serporidok(xdrs, &objp->serporid_u.serporid)) + return FALSE; + break; + default: + break; + } + return TRUE; +} + +/***************************************** + Data Structures and Types + *****************************************/ + +/* 'time()' hack with less overhead; */ + +/* assume reading a long word is atomic */ +#define READ_LONG_IS_ATOMIC + +typedef uint32_t TimeStamp; + +static inline TimeStamp +nowSeconds(void) +{ + rtems_interval rval; + rtems_clock_get_seconds_since_epoch( &rval ); + return rval; +} + + +/* Per mounted FS structure */ +typedef struct NfsRec_ { + /* the NFS server we're talking to. + */ + RpcUdpServer server; + /* statistics; how many NfsNodes are + * currently alive. + */ + volatile int nodesInUse; +#if DEBUG & DEBUG_COUNT_NODES + /* statistics; how many 'NfsNode.str' + * strings are currently allocated. + */ + volatile int stringsInUse; +#endif + /* A small number who uniquely + * identifies a mounted NFS within + * this driver (i.e. this NfsRec). + * Each time a NFS is mounted, the + * global ID counter is incremented + * and its value is assigned to the + * newly created NfsRec. + */ + u_short id; + /* Our RTEMS filesystem mt_entry + */ + rtems_filesystem_mount_table_entry_t *mt_entry; + /* Next NfsRec on a linked list who + * is anchored at nfsGlob + */ + struct NfsRec_ *next; + /* Who we pretend we are + */ + u_long uid,gid; +} NfsRec, *Nfs; + +typedef struct NfsNodeRec_ { + /* This holds this node's attributes + * (stats) and its nfs filehandle. + * It also contains room for nfs rpc + * arguments. + */ + serporid serporid; + /* The arguments we used when doing + * the 'lookup' call for this node. + * We need this information (especially + * the directory FH) for performing + * certain operations on this + * node (in particular: for unlinking + * it from a parent directory) + */ + diropargs args; + /* FS this node belongs to + */ + Nfs nfs; + /* A buffer for the string the + * args.name points to. + * We need this because args.name might + * temporarily point to strings on the + * stack. Duplicates are allocated from + * the heap and attached to 'str' so + * they can be released as appropriate. + */ + char *str; + /* A timestamp for the stats + */ + TimeStamp age; +} NfsNodeRec, *NfsNode; + +/***************************************** + Forward Declarations + *****************************************/ + +static ssize_t nfs_readlink( + rtems_filesystem_location_info_t *loc, /* IN */ + char *buf, /* OUT */ + size_t len +); + +static int updateAttr(NfsNode node, int force); + +/* Mask bits when setting attributes. + * Only the 'arg' fields with their + * corresponding bit set in the mask + * will be used. The others are left + * unchanged. + * The 'TOUCH' bits instruct nfs_sattr() + * to update the respective time + * fields to the current time + */ +#define SATTR_MODE (1<<0) +#define SATTR_UID (1<<1) +#define SATTR_GID (1<<2) +#define SATTR_SIZE (1<<3) +#define SATTR_ATIME (1<<4) +#define SATTR_TOUCHA (1<<5) +#define SATTR_MTIME (1<<6) +#define SATTR_TOUCHM (1<<7) +#define SATTR_TOUCH (SATTR_TOUCHM | SATTR_TOUCHA) + +static int +nfs_sattr(NfsNode node, sattr *arg, u_long mask); + +extern struct _rtems_filesystem_operations_table nfs_fs_ops; +static struct _rtems_filesystem_file_handlers_r nfs_file_file_handlers; +static struct _rtems_filesystem_file_handlers_r nfs_dir_file_handlers; +static struct _rtems_filesystem_file_handlers_r nfs_link_file_handlers; +static rtems_driver_address_table drvNfs; + +int +nfsMountsShow(FILE*); + +rtems_status_code +rtems_filesystem_resolve_location(char *buf, int len, rtems_filesystem_location_info_t *loc); + + +/***************************************** + Inline Routines + *****************************************/ + + +/* * * * * * * * * * * * * * * * * * + Trivial Operations on a NfsNode + * * * * * * * * * * * * * * * * * */ + +/* determine if a location 'l' is an NFS root node */ +static inline int +locIsRoot(rtems_filesystem_location_info_t *l) +{ +NfsNode me = (NfsNode) l->node_access; +NfsNode r; + r = (NfsNode)l->mt_entry->mt_fs_root.node_access; + return SERP_ATTR(r).fileid == SERP_ATTR(me).fileid && + SERP_ATTR(r).fsid == SERP_ATTR(me).fsid; +} + +/* determine if a location 'l' is an NFS node */ +static inline int +locIsNfs(rtems_filesystem_location_info_t *l) +{ + return l->ops == &nfs_fs_ops; +} + +/* determine if two locations refer to the + * same entity. We know that 'nfsloc' is a + * location inside nfs. However, we needn't + * know anything about 'anyloc'. + */ +static inline int +locAreEqual( + rtems_filesystem_location_info_t *nfsloc, + rtems_filesystem_location_info_t *anyloc +) +{ +NfsNode na = (NfsNode) nfsloc->node_access; +NfsNode nb; + + if (!locIsNfs(anyloc)) + return 0; + + nb = (NfsNode) anyloc->node_access; + + if (na->nfs != nb->nfs) + return 0; + + updateAttr(nb, 0); + + return SERP_ATTR(na).fileid == SERP_ATTR(nb).fileid && + SERP_ATTR(na).fsid == SERP_ATTR(nb).fsid; +} + + + +/***************************************** + Global Variables + *****************************************/ + +/* These are (except for MAXNAMLEN/MAXPATHLEN) copied from IMFS */ + +static rtems_filesystem_limits_and_options_t +nfs_limits_and_options = { + 5, /* link_max */ + 6, /* max_canon */ + 7, /* max_input */ + NFS_MAXNAMLEN, /* name_max */ + NFS_MAXPATHLEN, /* path_max */ + 2, /* pipe_buf */ + 1, /* posix_async_io */ + 2, /* posix_chown_restrictions */ + 3, /* posix_no_trunc */ + 4, /* posix_prio_io */ + 5, /* posix_sync_io */ + 6 /* posix_vdisable */ +}; + +/* size of an encoded 'entry' object */ +static int dirres_entry_size; + +/* Global stuff and statistics */ +static struct nfsstats { + /* A lock for protecting the + * linked ist of mounted NFS + * and the num_mounted_fs field + */ + rtems_id llock; + /* A lock for protecting misc + * stuff within the driver. + * The lock must only be held + * for short periods of time. + */ + rtems_id lock; + /* Our major number as assigned + * by RTEMS + */ + rtems_device_major_number nfs_major; + /* The number of currently + * mounted NFS + */ + int num_mounted_fs; + /* A list of the currently + * mounted NFS + */ + struct NfsRec_ *mounted_fs; + /* A counter for allocating + * unique IDs to each mounted + * NFS. + * Assume we are not going to + * do more than 16k mounts + * during the system lifetime + */ + u_short fs_ids; +} nfsGlob = {0, 0, 0, 0, 0, 0}; + +/* + * Global variable to tune the 'st_blksize' (stat(2)) value this nfs + * client should report. + * size on the server. + */ +#ifndef DEFAULT_NFS_ST_BLKSIZE +#define DEFAULT_NFS_ST_BLKSIZE NFS_MAXDATA +#endif +int nfsStBlksize = DEFAULT_NFS_ST_BLKSIZE; + +/* Two pools of RPC transactions; + * One with small send buffers + * the other with a big one. + * The actual size of the small + * buffer is configurable (see top). + * + * Note: The RX buffers are always + * big + */ +static RpcUdpXactPool smallPool = 0; +static RpcUdpXactPool bigPool = 0; + + +/***************************************** + Implementation + *****************************************/ + +/* Create a Nfs object. This is + * per-mounted NFS information. + * + * ARGS: The Nfs server handle. + * + * RETURNS: Nfs on success, + * NULL on failure with + * errno set + * + * NOTE: The submitted server + * object is 'owned' by + * this Nfs and will be + * destroyed by nfsDestroy() + */ +static Nfs +nfsCreate(RpcUdpServer server) +{ +Nfs rval = calloc(1,sizeof(*rval)); + + if (rval) { + rval->server = server; + LOCK(nfsGlob.llock); + rval->next = nfsGlob.mounted_fs; + nfsGlob.mounted_fs = rval; + UNLOCK(nfsGlob.llock); + } else { + errno = ENOMEM; + } + return rval; +} + +/* Destroy an Nfs object and + * its associated server + */ +static void +nfsDestroy(Nfs nfs) +{ +register Nfs prev; + if (!nfs) + return; + + LOCK(nfsGlob.llock); + if (nfs == nfsGlob.mounted_fs) + nfsGlob.mounted_fs = nfs->next; + else { + for (prev = nfsGlob.mounted_fs; + prev && prev->next != nfs; + prev = prev->next) + /* nothing else to do */; + assert( prev ); + prev->next = nfs->next; + } + UNLOCK(nfsGlob.llock); + + nfs->next = 0; /* paranoia */ + rpcUdpServerDestroy(nfs->server); + free(nfs); +} + +/* + * Create a Node. The node will + * be associated with a particular + * mounted NFS identified by 'nfs' + * Optionally, a NFS file handle + * may be copied into this node. + * + * ARGS: nfs of the NFS this node + * belongs to. + * NFS file handle identifying + * this node. + * RETURNS: node on success, + * NULL on failure with errno + * set. + * + * NOTE: The caller of this routine + * is responsible for copying + * a NFS file handle if she + * choses to pass a NULL fh. + * + * The driver code assumes the + * a node always has a valid + * NFS filehandle and file + * attributes (unless the latter + * are aged). + */ +static NfsNode +nfsNodeCreate(Nfs nfs, fhandle *fh) +{ +NfsNode rval = malloc(sizeof(*rval)); +unsigned long flags; + +#if DEBUG & DEBUG_TRACK_NODES + fprintf(stderr,"NFS: creating a node\n"); +#endif + + if (rval) { + if (fh) + memcpy( &SERP_FILE(rval), fh, sizeof(*fh) ); + rtems_interrupt_disable(flags); + nfs->nodesInUse++; + rtems_interrupt_enable(flags); + rval->nfs = nfs; + rval->str = 0; + } else { + errno = ENOMEM; + } + + return rval; +} + +/* destroy a node */ +static void +nfsNodeDestroy(NfsNode node) +{ +unsigned long flags; + +#if DEBUG & DEBUG_TRACK_NODES + fprintf(stderr,"NFS: destroying a node\n"); +#endif +#if 0 + if (!node) + return; + /* this probably does nothing... */ + xdr_free(xdr_serporid, &node->serporid); +#endif + + rtems_interrupt_disable(flags); + node->nfs->nodesInUse--; +#if DEBUG & DEBUG_COUNT_NODES + if (node->str) + node->nfs->stringsInUse--; +#endif + rtems_interrupt_enable(flags); + + if (node->str) + free(node->str); + + free(node); +} + +/* Clone a given node (AKA copy constructor), + * i.e. create an exact copy. + * + * ARGS: node to clone + * RETURNS: new node on success + * NULL on failure with errno set. + * + * NOTE: a string attached to 'str' + * is cloned as well. Outdated + * attributes (of the new copy + * only) will be refreshed + * (if unsuccessful, this could + * be a reason for failure to + * clone a node). + */ +static NfsNode +nfsNodeClone(NfsNode node) +{ +NfsNode rval = nfsNodeCreate(node->nfs, 0); + + if (rval) { + *rval = *node; + + /* must clone the string also */ + if (node->str) { + rval->args.name = rval->str = strdup(node->str); + if (!rval->str) { + errno = ENOMEM; + nfsNodeDestroy(rval); + return 0; + } +#if DEBUG & DEBUG_COUNT_NODES + { unsigned long flags; + rtems_interrupt_disable(flags); + node->nfs->stringsInUse++; + rtems_interrupt_enable(flags); + } +#endif + } + + /* possibly update the stats */ + if (updateAttr(rval, 0 /* only if necessary */)) { + nfsNodeDestroy(rval); + return 0; + } + } + return rval; +} + +/* Initialize the driver. + * + * ARGS: depth of the small and big + * transaction pools, i.e. how + * many transactions (buffers) + * should always be kept around. + * + * (If more transactions are needed, + * they are created and destroyed + * on the fly). + */ +void +nfsInit(int smallPoolDepth, int bigPoolDepth) +{ +static int initialised = 0; +entry dummy; +rtems_status_code status; + + if (initialised) + return; + + initialised = 1; + + fprintf(stderr, + "RTEMS-NFS $Release$, " \ + "Till Straumann, Stanford/SLAC/SSRL 2002, " \ + "See LICENSE file for licensing info.\n"); + + /* Get a major number */ + + if (RTEMS_SUCCESSFUL != rtems_io_register_driver(0, &drvNfs, &nfsGlob.nfs_major)) { + fprintf(stderr,"Registering NFS driver failed - %s\n", strerror(errno)); + return; + } + + if (0==smallPoolDepth) + smallPoolDepth = 20; + if (0==bigPoolDepth) + bigPoolDepth = 10; + + /* it's crucial to zero out the 'next' pointer + * because it terminates the xdr_entry recursion + * + * we also must make the filename some non-zero + * char pointer! + */ + + memset(&dummy, 0, sizeof(dummy)); + + dummy.nextentry = 0; + dummy.name = "somename"; /* guess average length of a filename */ + dirres_entry_size = xdr_sizeof((xdrproc_t)xdr_entry, &dummy); + + smallPool = rpcUdpXactPoolCreate( + NFS_PROGRAM, + NFS_VERSION_2, + CONFIG_NFS_SMALL_XACT_SIZE, + smallPoolDepth); + assert( smallPool ); + + bigPool = rpcUdpXactPoolCreate( + NFS_PROGRAM, + NFS_VERSION_2, + CONFIG_NFS_BIG_XACT_SIZE, + bigPoolDepth); + assert( bigPool ); + + status = rtems_semaphore_create( + rtems_build_name('N','F','S','l'), + 1, + MUTEX_ATTRIBUTES, + 0, + &nfsGlob.llock); + assert( status == RTEMS_SUCCESSFUL ); + status = rtems_semaphore_create( + rtems_build_name('N','F','S','m'), + 1, + MUTEX_ATTRIBUTES, + 0, + &nfsGlob.lock); + assert( status == RTEMS_SUCCESSFUL ); + + if (sizeof(ino_t) < sizeof(u_int)) { + fprintf(stderr, + "WARNING: Using 'short st_ino' hits performance and may fail to access/find correct files\n"); + fprintf(stderr, + "you should fix newlib's sys/stat.h - for now I'll enable a hack...\n"); + + } +} + +/* Driver cleanup code + */ +int +nfsCleanup(void) +{ +rtems_id l; +int refuse; + + if (!nfsGlob.llock) { + /* registering the driver failed - let them still cleanup */ + return 0; + } + + LOCK(nfsGlob.llock); + if ( (refuse = nfsGlob.num_mounted_fs) ) { + fprintf(stderr,"Refuse to unload NFS; %i filesystems still mounted.\n", + refuse); + nfsMountsShow(stderr); + /* yes, printing is slow - but since you try to unload the driver, + * you assume nobody is using NFS, so what if they have to wait? + */ + UNLOCK(nfsGlob.llock); + return -1; + } + + rtems_semaphore_delete(nfsGlob.lock); + nfsGlob.lock = 0; + + /* hold the lock while cleaning up... */ + + rpcUdpXactPoolDestroy(smallPool); + rpcUdpXactPoolDestroy(bigPool); + l = nfsGlob.llock; + rtems_io_unregister_driver(nfsGlob.nfs_major); + + rtems_semaphore_delete(l); + nfsGlob.llock = 0; + return 0; +} + +/* NFS RPC wrapper. + * + * ARGS: srvr the NFS server we want to call + * proc the NFSPROC_xx we want to invoke + * xargs xdr routine to wrap the arguments + * pargs pointer to the argument object + * xres xdr routine to unwrap the results + * pres pointer to the result object + * + * RETURNS: 0 on success, -1 on error with errno set. + * + * NOTE: the caller assumes that errno is set to + * a nonzero value if this routine returns + * an error (nonzero return value). + * + * This routine prints RPC error messages to + * stderr. + */ +STATIC int +nfscall( + RpcUdpServer srvr, + int proc, + xdrproc_t xargs, + void * pargs, + xdrproc_t xres, + void * pres) +{ +RpcUdpXact xact; +enum clnt_stat stat; +RpcUdpXactPool pool; +int rval = -1; + + + switch (proc) { + case NFSPROC_SYMLINK: + case NFSPROC_WRITE: + pool = bigPool; break; + default: pool = smallPool; break; + } + + xact = rpcUdpXactPoolGet(pool, XactGetCreate); + + if ( !xact ) { + errno = ENOMEM; + return -1; + } + + if ( RPC_SUCCESS != (stat=rpcUdpSend( + xact, + srvr, + NFSCALL_TIMEOUT, + proc, + xres, + pres, + xargs, + pargs, + 0)) || + RPC_SUCCESS != (stat=rpcUdpRcv(xact)) ) { + + fprintf(stderr, + "NFS (proc %i) - %s\n", + proc, + clnt_sperrno(stat)); + + switch (stat) { + /* TODO: this is probably not complete and/or fully accurate */ + case RPC_CANTENCODEARGS : errno = EINVAL; break; + case RPC_AUTHERROR : errno = EPERM; break; + + case RPC_CANTSEND : + case RPC_CANTRECV : /* hope they have errno set */ + case RPC_SYSTEMERROR : break; + + default : errno = EIO; break; + } + } else { + rval = 0; + } + + /* release the transaction back into the pool */ + rpcUdpXactPoolPut(xact); + + if (rval && !errno) + errno = EIO; + + return rval; +} + +/* Check the 'age' of a node's stats + * and read the attributes from the server + * if necessary. + * + * ARGS: node node to update + * force enforce updating ignoring + * the timestamp/age + * + * RETURNS: 0 on success, + * -1 on failure with errno set + */ + +static int +updateAttr(NfsNode node, int force) +{ + + if (force +#ifdef CONFIG_ATTR_LIFETIME + || (nowSeconds() - node->age > CONFIG_ATTR_LIFETIME) +#endif + ) { + if ( nfscall(node->nfs->server, + NFSPROC_GETATTR, + (xdrproc_t)xdr_nfs_fh, &SERP_FILE(node), + (xdrproc_t)xdr_attrstat, &node->serporid) ) + return -1; + + if ( NFS_OK != node->serporid.status ) { + errno = node->serporid.status; + return -1; + } + + node->age = nowSeconds(); + } + + return 0; +} + +/* + * IP address helper. + * + * initialize a sockaddr_in from a + * [<uid>'.'<gid>'@']<host>':'<path>" string and let + * pPath point to the <path> part; retrieve the optional + * uid/gids + * + * ARGS: see description above + * + * RETURNS: 0 on success, + * -1 on failure with errno set + */ +static int +buildIpAddr(u_long *puid, u_long *pgid, + char **pHost, struct sockaddr_in *psa, + char **pPath) +{ +struct hostent *h; +char host[64]; +char *chpt = *pPath; +char *path; +int len; + + if ( !chpt ) { + errno = EINVAL; + return -1; + } + + /* look for the optional uid/gid */ + if ( (chpt = strchr(chpt, UIDSEP)) ) { + if ( 2 != sscanf(*pPath,"%li.%li",puid,pgid) ) { + errno = EINVAL; + return -1; + } + chpt++; + } else { + *puid = RPCIOD_DEFAULT_ID; + *pgid = RPCIOD_DEFAULT_ID; + chpt = *pPath; + } + if ( pHost ) + *pHost = chpt; + + /* split the device name which is in the form + * + * <host> ':' <path> + * + * into its components using a local buffer + */ + + if ( !(path = strchr(chpt, HOSTDELIM)) || + (len = path - chpt) >= sizeof(host) - 1 ) { + errno = EINVAL; + return -1; + } + /* point to path beyond ':' */ + path++; + + strncpy(host, chpt, len); + host[len]=0; + + /* BEGIN OF NON-THREAD SAFE REGION */ + + h = gethostbyname(host); + + if ( !h ) { + errno = EINVAL; + return -1; + } + + memcpy(&psa->sin_addr, h->h_addr, sizeof (struct in_addr)); + + /* END OF NON-THREAD SAFE REGION */ + + psa->sin_family = AF_INET; + psa->sin_port = 0; + *pPath = path; + return 0; +} + +/* wrapper similar to nfscall. + * However, since it is not used + * very often, the simpler and less + * efficient rpcUdpCallRp API is used. + * + * ARGS: see 'nfscall()' above + * + * RETURNS: RPC status + */ +static enum clnt_stat +mntcall( + struct sockaddr_in *psrvr, + int proc, + xdrproc_t xargs, + void * pargs, + xdrproc_t xres, + void * pres, + u_long uid, + u_long gid) +{ +#ifdef MOUNT_V1_PORT +int retry; +#endif +enum clnt_stat stat = RPC_FAILED; + +#ifdef MOUNT_V1_PORT + /* if the portmapper fails, retry a fixed port */ + for (retry = 1, psrvr->sin_port = 0, stat = RPC_FAILED; + retry >= 0 && stat; + stat && (psrvr->sin_port = htons(MOUNT_V1_PORT)), retry-- ) +#endif + stat = rpcUdpCallRp( + psrvr, + MOUNTPROG, + MOUNTVERS, + proc, + xargs, + pargs, + xres, + pres, + uid, + gid, + MNTCALL_TIMEOUT + ); + return stat; +} + +/***************************************** + RTEMS File System Operations for NFS + *****************************************/ + +#if 0 /* for reference */ + +struct rtems_filesystem_location_info_tt +{ + void *node_access; + rtems_filesystem_file_handlers_r *handlers; + rtems_filesystem_operations_table *ops; + rtems_filesystem_mount_table_entry_t *mt_entry; +}; + +#endif + +/* + * Evaluate a path letting 'pathloc' travel along. + * + * The important semantics of this operation are: + * + * If this routine returns -1, the caller assumes + * pathloc to be _invalid_ and hence it will not + * invoke rtems_filesystem_freenode() on it. + * + * OTOH, if evalpath returns 0, + * rtems_filesystem_freenode() will eventually be + * called which results in our freeing the associated + * NfsNode attached to node_access. + * + * Therefore, this routine will _always_ allocate + * a NfsNode and pass it out to *pathloc (provided + * that the evaluation succeeds). + * + * However, if the evaluation finds that it has to + * step across FS boundaries (mount point or a symlink + * pointing outside), the NfsNode is destroyed + * before passing control to the new FS' evalpath_h() + * + */ + +union nfs_evalpath_arg { + int i; + const char **c; + }; + +STATIC int nfs_do_evalpath( + const char *pathname, /* IN */ + int pathnamelen, /* IN */ + union nfs_evalpath_arg *arg, + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + int forMake +) +{ +char *del = 0, *part; +int e = 0; +NfsNode node = pathloc->node_access; +char *p = malloc(MAXPATHLEN+1); +Nfs nfs = (Nfs)pathloc->mt_entry->fs_info; +RpcUdpServer server = nfs->server; +unsigned long flags; +#if DEBUG & DEBUG_COUNT_NODES +unsigned long niu,siu; +#endif + + if ( !p ) { + e = ENOMEM; + goto cleanup; + } + memset(p, 0, MAXPATHLEN+1); + memcpy(p, pathname, pathnamelen); + + LOCK(nfsGlob.lock); + node = nfsNodeClone(node); + UNLOCK(nfsGlob.lock); + + /* from here on, the NFS is protected from being unmounted + * since the node refcount is > 1 + */ + + /* clone the node */ + if ( !node ) { + /* nodeClone sets errno */ + pathloc->node_access = 0; + if ( ! (e = errno) ) { + /* if we have no node, e must not be zero! */ + e = ENOMEM; + } + goto cleanup; + } + + pathloc->node_access = node; + + /* Special case: the RTEMS filesystem code + * may emit '..' on a regular file node to + * find the parent directory :-(. + * (eval.c: rtems_filesystem_evaluate_parent()) + * Try to catch this case here: + */ + if ( NFDIR != SERP_ATTR(node).type && '.'==*p && '.'==*(p+1) ) { + for ( part = p+2; '/'==*part; part++ ) + /* skip trailing '/' */; + if ( !*part ) { + /* this is it; back out dir and let them look up the dir itself... */ + memcpy( &SERP_FILE(node), + &node->args.dir, + sizeof(node->args.dir)); + *(p+1)=0; + } + } + + for (part=p; part && *part; part=del) { + + if ( NFLNK == SERP_ATTR(node).type ) { + /* follow midpath link */ + char *b = malloc(NFS_MAXPATHLEN+1); + int l; + + if (!b) { + e = ENOMEM; + goto cleanup; + } + if (nfs_readlink(pathloc, b, NFS_MAXPATHLEN+1)) { + free(b); + e = errno; + goto cleanup; + } + + /* prepend the link value to the rest of the path */ + if ( (l=strlen(b)) + strlen(part) + 1 > NFS_MAXPATHLEN ) { + free(b); + e = EINVAL; + goto cleanup; + } + /* swap string buffers and reset delimiter */ + b[l++] = DELIM; + strcpy(b+l,part); + part = b; + b = p; + p = del = part; + + free(b); + + /* back up the directory filehandle (only necessary + * if we don't back out to the root + */ + if (! (DELIM == *part) ) { + memcpy( &SERP_FILE(node), + &node->args.dir, + sizeof(node->args.dir)); + + if (updateAttr(node, 1 /* force */)) { + e = errno; + goto cleanup; + } + } + } + + /* find delimiter and eat /// sequences + * (only if we don't restart at the root) + */ + if ( DELIM != *part && (del = strchr(part, DELIM))) { + do { + *del++=0; + } while (DELIM==*del); + } + + /* refuse to backup over the root */ + if ( 0==strcmp(part,UPDIR) + && locAreEqual(pathloc, &rtems_filesystem_root) ) { + part++; + } + + /* cross mountpoint upwards */ + if ( (0==strcmp(part,UPDIR) && locIsRoot(pathloc)) /* cross mountpoint up */ + || DELIM == *part /* link starts at root */ + ) { + int rval; + +#if DEBUG & DEBUG_EVALPATH + fprintf(stderr, + "Crossing mountpoint upwards\n"); +#endif + + if (DELIM == *part) { + *pathloc = rtems_filesystem_root; + } else { + *pathloc = pathloc->mt_entry->mt_point_node; + /* re-append the rest of the path */ + if (del) + while ( 0 == *--del ) + *del = DELIM; + } + + nfsNodeDestroy(node); + +#if DEBUG & DEBUG_EVALPATH + fprintf(stderr, + "Re-evaluating '%s'\n", + part); +#endif + + if (forMake) + rval = pathloc->ops->evalformake_h(part, pathloc, arg->c); + else + rval = pathloc->ops->evalpath_h(part, strlen(part), arg->i, pathloc); + + free(p); + return rval; + } + + /* lookup one element */ + SERP_ARGS(node).diroparg.name = part; + + /* remember args / directory fh */ + memcpy( &node->args, &SERP_FILE(node), sizeof(node->args)); + + /* don't lookup the item we want to create */ + if ( forMake && (!del || !*del) ) + break; + +#if DEBUG & DEBUG_EVALPATH + fprintf(stderr,"Looking up '%s'\n",part); +#endif + + if ( nfscall(server, + NFSPROC_LOOKUP, + (xdrproc_t)xdr_diropargs, &SERP_FILE(node), + (xdrproc_t)xdr_serporid, &node->serporid) || + NFS_OK != (errno=node->serporid.status) ) { + e = errno; + goto cleanup; + } + node->age = nowSeconds(); + +#if DEBUG & DEBUG_EVALPATH + if (NFLNK == SERP_ATTR(node).type && del) { + fprintf(stderr, + "Following midpath link '%s'\n", + part); + } +#endif + + } + + if (forMake) { + /* remember the name - do this _before_ copying + * the name to local storage; the caller expects a + * pointer into pathloc + */ + assert( node->args.name ); + + *(const char**)arg = pathname + (node->args.name - p); + +#if 0 + /* restore the directory node */ + + memcpy( &SERP_FILE(node), + &node->args.dir, + sizeof(node->args.dir)); + + if ( (nfscall(nfs->server, + NFSPROC_GETATTR, + (xdrproc_t)xdr_nfs_fh, &SERP_FILE(node), + (xdrproc_t)xdr_attrstat, &node->serporid) && !errno && (errno = EIO)) || + (NFS_OK != (errno=node->serporid.status) ) ) { + goto cleanup; + } +#endif + } + + if (locIsRoot(pathloc)) { + + /* stupid filesystem code has no 'op' for comparing nodes + * but just compares the 'node_access' pointers. + * Luckily, this is only done for comparing the root nodes. + * Hence, we never give them a copy of the root but always + * the root itself. + */ + pathloc->node_access = pathloc->mt_entry->mt_fs_root.node_access; + /* increment the 'in use' counter since we return one more + * reference to the root node + */ + rtems_interrupt_disable(flags); + nfs->nodesInUse++; + rtems_interrupt_enable(flags); + nfsNodeDestroy(node); + + + } else { + switch (SERP_ATTR(node).type) { + case NFDIR: pathloc->handlers = &nfs_dir_file_handlers; break; + case NFREG: pathloc->handlers = &nfs_file_file_handlers; break; + case NFLNK: pathloc->handlers = &nfs_link_file_handlers; break; + default: pathloc->handlers = &rtems_filesystem_handlers_default; break; + } + pathloc->node_access = node; + + /* remember the name of this directory entry */ + + if (node->args.name) { + if (node->str) { +#if DEBUG & DEBUG_COUNT_NODES + rtems_interrupt_disable(flags); + nfs->stringsInUse--; + rtems_interrupt_enable(flags); +#endif + free(node->str); + } + node->args.name = node->str = strdup(node->args.name); + if (!node->str) { + e = ENOMEM; + goto cleanup; + } + +#if DEBUG & DEBUG_COUNT_NODES + rtems_interrupt_disable(flags); + nfs->stringsInUse++; + rtems_interrupt_enable(flags); +#endif + } + + } + node = 0; + + e = 0; + +cleanup: + free(p); +#if DEBUG & DEBUG_COUNT_NODES + /* cache counters; nfs may be unmounted by other thread after the last + * node is destroyed + */ + niu = nfs->nodesInUse; + siu = nfs->stringsInUse; +#endif + if (node) { + nfsNodeDestroy(node); + pathloc->node_access = 0; + } +#if DEBUG & DEBUG_COUNT_NODES + fprintf(stderr, + "leaving evalpath, in use count is %i nodes, %i strings\n", + niu,siu); +#endif + if (e) { +#if DEBUG & DEBUG_EVALPATH + perror("Evalpath"); +#endif + rtems_set_errno_and_return_minus_one(e); + } else { + return 0; + } +} + +/* MANDATORY; may set errno=ENOSYS and return -1 */ +static int nfs_evalformake( + const char *path, /* IN */ + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + const char **pname /* OUT */ +) +{ + union nfs_evalpath_arg args; + args.c = pname; + + return nfs_do_evalpath(path, strlen(path), &args, pathloc, 1 /*forMake*/); +} + +static int nfs_evalpath( + const char *path, /* IN */ + size_t pathlen, /* IN */ + int flags, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */ +) +{ + union nfs_evalpath_arg args; + args.i = flags; + return nfs_do_evalpath(path, pathlen, &args, pathloc, 0 /*not forMake*/); +} + + +/* create a hard link */ + +static int nfs_link( + rtems_filesystem_location_info_t *to_loc, /* IN */ + rtems_filesystem_location_info_t *parent_loc, /* IN */ + const char *name /* IN */ +) +{ +NfsNode pNode; +nfsstat status; +NfsNode tNode = to_loc->node_access; + +#if DEBUG & DEBUG_SYSCALLS + fprintf(stderr,"Creating link '%s'\n",name); +#endif + + if ( !locIsNfs(parent_loc) ) { + errno = EXDEV; + return -1; + } + + pNode = parent_loc->node_access; + if ( tNode->nfs != pNode->nfs ) { + errno = EXDEV; + return -1; + } + memcpy(&SERP_ARGS(tNode).linkarg.to.dir, + &SERP_FILE(pNode), + sizeof(SERP_FILE(pNode))); + + SERP_ARGS(tNode).linkarg.to.name = (filename)name; + + if ( nfscall(tNode->nfs->server, + NFSPROC_LINK, + (xdrproc_t)xdr_linkargs, &SERP_FILE(tNode), + (xdrproc_t)xdr_nfsstat, &status) + || (NFS_OK != (errno = status)) + ) { +#if DEBUG & DEBUG_SYSCALLS + perror("nfs_link"); +#endif + return -1; + } + + return 0; + +} + +static int nfs_do_unlink( + rtems_filesystem_location_info_t *parent_loc,/* IN */ + rtems_filesystem_location_info_t *loc, /* IN */ + int proc +) +{ +nfsstat status; +NfsNode node = loc->node_access; +Nfs nfs = node->nfs; +#if DEBUG & DEBUG_SYSCALLS +char *name = NFSPROC_REMOVE == proc ? + "nfs_unlink" : "nfs_rmdir"; +#endif + + /* The FS generics have determined that pathloc is _not_ + * a directory. Hence we may assume that the parent + * is in our NFS. + */ + +#if DEBUG & DEBUG_SYSCALLS + assert( node->args.name == node->str && node->str ); + + fprintf(stderr,"%s '%s'\n", name, node->args.name); +#endif + + if ( nfscall(nfs->server, + proc, + (xdrproc_t)xdr_diropargs, &node->args, + (xdrproc_t)xdr_nfsstat, &status) + || (NFS_OK != (errno = status)) + ) { +#if DEBUG & DEBUG_SYSCALLS + perror(name); +#endif + return -1; + } + + return 0; +} + +static int nfs_unlink( + rtems_filesystem_location_info_t *parent_loc, /* IN */ + rtems_filesystem_location_info_t *loc /* IN */ +) +{ + return nfs_do_unlink(parent_loc, loc, NFSPROC_REMOVE); +} + +static int nfs_chown( + rtems_filesystem_location_info_t *pathloc, /* IN */ + uid_t owner, /* IN */ + gid_t group /* IN */ +) +{ +sattr arg; + + arg.uid = owner; + arg.gid = group; + + return nfs_sattr(pathloc->node_access, &arg, SATTR_UID | SATTR_GID); + +} + +/* Cleanup the FS private info attached to pathloc->node_access */ +static int nfs_freenode( + rtems_filesystem_location_info_t *pathloc /* IN */ +) +{ +Nfs nfs = ((NfsNode)pathloc->node_access)->nfs; + +#if DEBUG & DEBUG_COUNT_NODES + /* print counts at entry where they are > 0 so 'nfs' is safe from being destroyed + * and there's no race condition + */ + fprintf(stderr, + "entering freenode, in use count is %i nodes, %i strings\n", + nfs->nodesInUse, + nfs->stringsInUse); +#endif + + /* never destroy the root node; it is released by the unmount + * code + */ + if (locIsRoot(pathloc)) { + unsigned long flags; + /* just adjust the references to the root node but + * don't really release it + */ + rtems_interrupt_disable(flags); + nfs->nodesInUse--; + rtems_interrupt_enable(flags); + } else { + nfsNodeDestroy(pathloc->node_access); + pathloc->node_access = 0; + } + return 0; +} + +/* NOTE/TODO: mounting on top of NFS is not currently supported + * + * Challenge: stateless protocol. It would be possible to + * delete mount points on the server. We would need some sort + * of a 'garbage collector' looking for dead/unreachable + * mount points and unmounting them. + * Also, the path evaluation routine would have to check + * for crossing mount points. Crossing over from one NFS + * into another NFS could probably handled iteratively + * rather than by recursion. + */ + +int rtems_nfs_initialize( + rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data +) +{ +char *host; +struct sockaddr_in saddr; +enum clnt_stat stat; +fhstatus fhstat; +u_long uid,gid; +#ifdef NFS_V2_PORT +int retry; +#endif +Nfs nfs = 0; +NfsNode rootNode = 0; +RpcUdpServer nfsServer = 0; +int e = -1; +char *path = mt_entry->dev; + + if (rpcUdpInit () < 0) { + fprintf (stderr, "error: initialising RPC\n"); + return -1; + } + + nfsInit(0, 0); + +#if 0 + printf("Trying to mount %s on %s\n",path,mntpoint); +#endif + + if ( buildIpAddr(&uid, &gid, &host, &saddr, &path) ) + return -1; + +#ifdef NFS_V2_PORT + /* if the portmapper fails, retry a fixed port */ + for (retry = 1, saddr.sin_port = 0, stat = RPC_FAILED; + retry >= 0 && stat; + stat && (saddr.sin_port = htons(NFS_V2_PORT)), retry-- ) +#endif + stat = rpcUdpServerCreate( + &saddr, + NFS_PROGRAM, + NFS_VERSION_2, + uid, + gid, + &nfsServer + ); + + if ( RPC_SUCCESS != stat ) { + fprintf(stderr, + "Unable to contact NFS server - invalid port? (%s)\n", + clnt_sperrno(stat)); + e = EPROTONOSUPPORT; + goto cleanup; + } + + + /* first, try to ping the NFS server by + * calling the NULL proc. + */ + if ( nfscall(nfsServer, + NFSPROC_NULL, + (xdrproc_t)xdr_void, 0, + (xdrproc_t)xdr_void, 0) ) { + + fputs("NFS Ping ",stderr); + fwrite(host, 1, path-host-1, stderr); + fprintf(stderr," failed: %s\n", strerror(errno)); + + e = errno ? errno : EIO; + goto cleanup; + } + + /* that seemed to work - we now try the + * actual mount + */ + + /* reuse server address but let the mntcall() + * search for the mountd's port + */ + saddr.sin_port = 0; + + stat = mntcall( &saddr, + MOUNTPROC_MNT, + (xdrproc_t)xdr_dirpath, + &path, + (xdrproc_t)xdr_fhstatus, + &fhstat, + uid, + gid ); + + if (stat) { + fprintf(stderr,"MOUNT -- %s\n",clnt_sperrno(stat)); + if ( e<=0 ) + e = EIO; + goto cleanup; + } else if (NFS_OK != (e=fhstat.fhs_status)) { + fprintf(stderr,"MOUNT: %s\n",strerror(e)); + goto cleanup; + } + + nfs = nfsCreate(nfsServer); + assert( nfs ); + nfsServer = 0; + + nfs->uid = uid; + nfs->gid = gid; + + /* that seemed to work - we now create the root node + * and we also must obtain the root node attributes + */ + rootNode = nfsNodeCreate(nfs, &fhstat.fhstatus_u.fhs_fhandle); + assert( rootNode ); + + if ( updateAttr(rootNode, 1 /* force */) ) { + e = errno; + goto cleanup; + } + + /* looks good so far */ + + mt_entry->mt_fs_root.node_access = rootNode; + + rootNode = 0; + + mt_entry->mt_fs_root.ops = &nfs_fs_ops; + mt_entry->mt_fs_root.handlers = &nfs_dir_file_handlers; + mt_entry->pathconf_limits_and_options = nfs_limits_and_options; + + LOCK(nfsGlob.llock); + nfsGlob.num_mounted_fs++; + /* allocate a new ID for this FS */ + nfs->id = nfsGlob.fs_ids++; + UNLOCK(nfsGlob.llock); + + mt_entry->fs_info = nfs; + nfs->mt_entry = mt_entry; + nfs = 0; + + e = 0; + +cleanup: + if (nfs) + nfsDestroy(nfs); + if (nfsServer) + rpcUdpServerDestroy(nfsServer); + if (rootNode) + nfsNodeDestroy(rootNode); + if (e) + rtems_set_errno_and_return_minus_one(e); + else + return 0; +} + +/* This op is called when they try to unmount THIS fs */ +STATIC int nfs_fsunmount_me( + rtems_filesystem_mount_table_entry_t *mt_entry /* in */ +) +{ +enum clnt_stat stat; +struct sockaddr_in saddr; +char *path = mt_entry->dev; +int nodesInUse; +u_long uid,gid; +int status; + +LOCK(nfsGlob.llock); + nodesInUse = ((Nfs)mt_entry->fs_info)->nodesInUse; + + if (nodesInUse > 1 /* one ref to the root node used by us */) { + UNLOCK(nfsGlob.llock); + fprintf(stderr, + "Refuse to unmount; there are still %i nodes in use (1 used by us)\n", + nodesInUse); + rtems_set_errno_and_return_minus_one(EBUSY); + } + + status = buildIpAddr(&uid, &gid, 0, &saddr, &path); + assert( !status ); + + stat = mntcall( &saddr, + MOUNTPROC_UMNT, + (xdrproc_t)xdr_dirpath, &path, + (xdrproc_t)xdr_void, 0, + uid, + gid + ); + + if (stat) { + UNLOCK(nfsGlob.llock); + fprintf(stderr,"NFS UMOUNT -- %s\n", clnt_sperrno(stat)); + errno = EIO; + return -1; + } + + nfsNodeDestroy(mt_entry->mt_fs_root.node_access); + mt_entry->mt_fs_root.node_access = 0; + + nfsDestroy(mt_entry->fs_info); + mt_entry->fs_info = 0; + + nfsGlob.num_mounted_fs--; +UNLOCK(nfsGlob.llock); + + return 0; +} + +/* OPTIONAL; may be NULL - BUT: CAUTION; mount() doesn't check + * for this handler to be present - a fs bug + * //NOTE: (10/25/2002) patch submitted and probably applied + */ +static rtems_filesystem_node_types_t nfs_node_type( + rtems_filesystem_location_info_t *pathloc /* in */ +) +{ +NfsNode node = pathloc->node_access; + + if (updateAttr(node, 0 /* only if old */)) + return -1; + + switch( SERP_ATTR(node).type ) { + default: + /* rtems has no value for 'unknown'; + */ + case NFNON: + case NFSOCK: + case NFBAD: + case NFFIFO: + break; + + + case NFREG: return RTEMS_FILESYSTEM_MEMORY_FILE; + case NFDIR: return RTEMS_FILESYSTEM_DIRECTORY; + + case NFBLK: + case NFCHR: return RTEMS_FILESYSTEM_DEVICE; + + case NFLNK: return RTEMS_FILESYSTEM_SYM_LINK; + } + return -1; +} + +static int nfs_mknod( + const char *path, /* IN */ + mode_t mode, /* IN */ + dev_t dev, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */ +) +{ + +struct timeval now; +diropres res; +NfsNode node = pathloc->node_access; +mode_t type = S_IFMT & mode; + + if (type != S_IFDIR && type != S_IFREG) + rtems_set_errno_and_return_minus_one(ENOTSUP); + +#if DEBUG & DEBUG_SYSCALLS + fprintf(stderr,"nfs_mknod: creating %s\n", path); +#endif + + rtems_clock_get_tod_timeval(&now); + + SERP_ARGS(node).createarg.name = (filename)path; + SERP_ARGS(node).createarg.attributes.mode = mode; + /* TODO: either use our uid or use the Nfs credentials */ + SERP_ARGS(node).createarg.attributes.uid = 0; + SERP_ARGS(node).createarg.attributes.gid = 0; + SERP_ARGS(node).createarg.attributes.size = 0; + SERP_ARGS(node).createarg.attributes.atime.seconds = now.tv_sec; + SERP_ARGS(node).createarg.attributes.atime.useconds = now.tv_usec; + SERP_ARGS(node).createarg.attributes.mtime.seconds = now.tv_sec; + SERP_ARGS(node).createarg.attributes.mtime.useconds = now.tv_usec; + + if ( nfscall( node->nfs->server, + NFSPROC_CREATE, + (xdrproc_t)xdr_createargs, &SERP_FILE(node), + (xdrproc_t)xdr_diropres, &res) + || (NFS_OK != (errno = res.status)) ) { +#if DEBUG & DEBUG_SYSCALLS + perror("nfs_mknod"); +#endif + return -1; + } + + return 0; +} + +static int nfs_utime( + rtems_filesystem_location_info_t *pathloc, /* IN */ + time_t actime, /* IN */ + time_t modtime /* IN */ +) +{ +sattr arg; + + /* TODO: add rtems EPOCH - UNIX EPOCH seconds */ + arg.atime.seconds = actime; + arg.atime.useconds = 0; + arg.mtime.seconds = modtime; + arg.mtime.useconds = 0; + + return nfs_sattr(pathloc->node_access, &arg, SATTR_ATIME | SATTR_MTIME); +} + +static int nfs_symlink( + rtems_filesystem_location_info_t *loc, /* IN */ + const char *link_name, /* IN */ + const char *node_name +) +{ +struct timeval now; +nfsstat status; +NfsNode node = loc->node_access; + + +#if DEBUG & DEBUG_SYSCALLS + fprintf(stderr,"nfs_symlink: creating %s -> %s\n", link_name, node_name); +#endif + + rtems_clock_get_tod_timeval(&now); + + SERP_ARGS(node).symlinkarg.name = (filename)link_name; + SERP_ARGS(node).symlinkarg.to = (nfspath) node_name; + + SERP_ARGS(node).symlinkarg.attributes.mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; + /* TODO */ + SERP_ARGS(node).symlinkarg.attributes.uid = 0; + SERP_ARGS(node).symlinkarg.attributes.gid = 0; + SERP_ARGS(node).symlinkarg.attributes.size = 0; + SERP_ARGS(node).symlinkarg.attributes.atime.seconds = now.tv_sec; + SERP_ARGS(node).symlinkarg.attributes.atime.useconds = now.tv_usec; + SERP_ARGS(node).symlinkarg.attributes.mtime.seconds = now.tv_sec; + SERP_ARGS(node).symlinkarg.attributes.mtime.useconds = now.tv_usec; + + if ( nfscall( node->nfs->server, + NFSPROC_SYMLINK, + (xdrproc_t)xdr_symlinkargs, &SERP_FILE(node), + (xdrproc_t)xdr_nfsstat, &status) + || (NFS_OK != (errno = status)) ) { +#if DEBUG & DEBUG_SYSCALLS + perror("nfs_symlink"); +#endif + return -1; + } + + return 0; +} + +static int nfs_do_readlink( + rtems_filesystem_location_info_t *loc, /* IN */ + strbuf *psbuf /* IN/OUT */ +) +{ +NfsNode node = loc->node_access; +Nfs nfs = node->nfs; +readlinkres_strbuf rr; +int wasAlloced; +int rval; + + rr.strbuf = *psbuf; + + wasAlloced = (0 == psbuf->buf); + + if ( (rval = nfscall(nfs->server, + NFSPROC_READLINK, + (xdrproc_t)xdr_nfs_fh, &SERP_FILE(node), + (xdrproc_t)xdr_readlinkres_strbuf, &rr)) ) { + if (wasAlloced) + xdr_free( (xdrproc_t)xdr_strbuf, (caddr_t)&rr.strbuf ); + } + + + if (NFS_OK != rr.status) { + if (wasAlloced) + xdr_free( (xdrproc_t)xdr_strbuf, (caddr_t)&rr.strbuf ); + rtems_set_errno_and_return_minus_one(rr.status); + } + + *psbuf = rr.strbuf; + + return 0; +} + +static ssize_t nfs_readlink( + rtems_filesystem_location_info_t *loc, /* IN */ + char *buf, /* OUT */ + size_t len +) +{ +strbuf sbuf; + sbuf.buf = buf; + sbuf.max = len; + + return nfs_do_readlink(loc, &sbuf); +} + +/* The semantics of this routine are: + * + * The caller submits a valid pathloc, i.e. it has + * an NfsNode attached to node_access. + * On return, pathloc points to the target node which + * may or may not be an NFS node. + * Hence, the original NFS node is released in either + * case: + * - link evaluation fails; pathloc points to no valid node + * - link evaluation success; pathloc points to a new valid + * node. If it's an NFS node, a new NfsNode will be attached + * to node_access... + */ + +#define LINKVAL_BUFLEN (MAXPATHLEN+1) +#define RVAL_ERR_BUT_DONT_FREENODE (-1) +#define RVAL_ERR_AND_DO_FREENODE ( 1) +#define RVAL_OK ( 0) + +static int nfs_eval_link( + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + int flags /* IN */ +) +{ +rtems_filesystem_node_types_t type; +char *buf = malloc(LINKVAL_BUFLEN); +int rval = RVAL_ERR_AND_DO_FREENODE; + + if (!buf) { + errno = ENOMEM; + goto cleanup; + } + + /* in this loop, we must not use NFS specific ops as we might + * step out of our FS during the process... + * This algorithm should actually be performed by the + * generic's evaluate_path routine :-( + * + * Unfortunately, there is no way of finding the + * directory node who contains 'pathloc', however :-( + */ + do { + /* assume the generics have verified 'pathloc' to be + * a link... + */ + if ( !pathloc->ops->readlink_h ) { + errno = ENOTSUP; + goto cleanup; + } + + if ( pathloc->ops->readlink_h(pathloc, buf, LINKVAL_BUFLEN) ) { + goto cleanup; + } + +#if DEBUG & DEBUG_EVALPATH + fprintf(stderr, "link value is '%s'\n", buf); +#endif + + /* is the link value an absolute path ? */ + if ( DELIM != *buf ) { + /* NO; a relative path */ + + /* we must backup to the link's directory - we + * know only how to do that for NFS, however. + * In this special case, we can avoid recursion. + * Otherwise (i.e. if the link is on another FS), + * we must step into its eval_link_h(). + */ + if (locIsNfs(pathloc)) { + NfsNode node = pathloc->node_access; + int err; + + memcpy( &SERP_FILE(node), + &node->args.dir, + sizeof(node->args.dir) ); + + if (updateAttr(node, 1 /* force */)) + goto cleanup; + + if (SERP_ATTR(node).type != NFDIR) { + errno = ENOTDIR; + goto cleanup; + } + + pathloc->handlers = &nfs_dir_file_handlers; + + err = nfs_evalpath(buf, strlen(buf), flags, pathloc); + + /* according to its semantics, + * nfs_evalpath cloned the node attached + * to pathloc. Hence we have to + * release the old one (referring to + * the link; the new clone has been + * updated and refers to the link _value_). + */ + nfsNodeDestroy(node); + + if (err) { + /* nfs_evalpath has set errno; + * pathloc->node_access has no + * valid node attached in this case + */ + rval = RVAL_ERR_BUT_DONT_FREENODE; + goto cleanup; + } + + } else { + if ( ! pathloc->ops->eval_link_h ) { + errno = ENOTSUP; + goto cleanup; + } + if (!pathloc->ops->eval_link_h(pathloc, flags)) { + /* FS is responsible for freeing its pathloc->node_access + * if necessary + */ + rval = RVAL_ERR_BUT_DONT_FREENODE; + goto cleanup; + } + } + } else { + /* link points to an absolute path '/xxx' */ + + /* release this node; filesystem_evaluate_path() will + * lookup a new one. + */ + rtems_filesystem_freenode(pathloc); + + if (rtems_filesystem_evaluate_path(buf, strlen(buf), flags, pathloc, 1)) { + /* If evalpath fails then there is no valid node + * attached to pathloc; hence we must not attempt + * to free the node + */ + rval = RVAL_ERR_BUT_DONT_FREENODE; + goto cleanup; + } + } + + if ( !pathloc->ops->node_type_h ) { + errno = ENOTSUP; + goto cleanup; + } + + type = pathloc->ops->node_type_h(pathloc); + + + /* I dont know what to do about hard links */ + } while ( RTEMS_FILESYSTEM_SYM_LINK == type ); + + rval = RVAL_OK; + +cleanup: + + free(buf); + + if (RVAL_ERR_AND_DO_FREENODE == rval) { + rtems_filesystem_freenode(pathloc); + return -1; + } + + return rval; +} + + +struct _rtems_filesystem_operations_table nfs_fs_ops = { + nfs_evalpath, /* MANDATORY */ + nfs_evalformake, /* MANDATORY; may set errno=ENOSYS and return -1 */ + nfs_link, /* OPTIONAL; may be defaulted */ + nfs_unlink, /* OPTIONAL; may be defaulted */ + nfs_node_type, /* OPTIONAL; may be defaulted; BUG in mount - no test!! */ + nfs_mknod, /* OPTIONAL; may be defaulted */ + nfs_chown, /* OPTIONAL; may be defaulted */ + nfs_freenode, /* OPTIONAL; may be defaulted; (release node_access) */ + rtems_filesystem_default_mount, + rtems_nfs_initialize, /* OPTIONAL; may be defaulted -- not used anymore */ + rtems_filesystem_default_unmount, + nfs_fsunmount_me, /* OPTIONAL; may be defaulted */ + nfs_utime, /* OPTIONAL; may be defaulted */ + nfs_eval_link, /* OPTIONAL; may be defaulted */ + nfs_symlink, /* OPTIONAL; may be defaulted */ + nfs_readlink, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_rename, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_statvfs /* OPTIONAL; may be defaulted */ + +}; + +/***************************************** + File Handlers + + NOTE: the FS generics expect a FS' + evalpath_h() to switch the + pathloc->handlers according + to the pathloc/node's file + type. + We currently have 'file' and + 'directory' handlers and very + few 'symlink' handlers. + + The handlers for each type are + implemented or #defined ZERO + in a 'nfs_file_xxx', + 'nfs_dir_xxx', 'nfs_link_xxx' + sequence below this point. + + In some cases, a common handler, + can be used for all file types. + It is then simply called + 'nfs_xxx'. + *****************************************/ + +/* stateless NFS protocol makes this trivial */ +static int nfs_file_open( + rtems_libio_t *iop, + const char *pathname, + uint32_t flag, + uint32_t mode +) +{ + return 0; +} + +/* reading directories is not stateless; we must + * remember the last 'read' position, i.e. + * the server 'cookie'. We do manage this information + * attached to the pathinfo.node_access_2. + */ +static int nfs_dir_open( + rtems_libio_t *iop, + const char *pathname, + uint32_t flag, + uint32_t mode +) +{ +NfsNode node = iop->pathinfo.node_access; +DirInfo di; + + /* create a readdirargs object and copy the file handle; + * attach to the pathinfo.node_access_2 + */ + + di = (DirInfo) malloc(sizeof(*di)); + iop->pathinfo.node_access_2 = di; + + if ( !di ) { + errno = ENOMEM; + return -1; + } + + memcpy( &di->readdirargs.dir, + &SERP_FILE(node), + sizeof(di->readdirargs.dir) ); + + /* rewind cookie */ + memset( &di->readdirargs.cookie, + 0, + sizeof(di->readdirargs.cookie) ); + + di->eofreached = FALSE; + + return 0; +} + +static int nfs_file_close( + rtems_libio_t *iop +) +{ + return 0; +} + +static int nfs_dir_close( + rtems_libio_t *iop +) +{ + free(iop->pathinfo.node_access_2); + iop->pathinfo.node_access_2 = 0; + return 0; +} + +static ssize_t nfs_file_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ +readres rr; +NfsNode node = iop->pathinfo.node_access; +Nfs nfs = node->nfs; + + if (count > NFS_MAXDATA) + count = NFS_MAXDATA; + + SERP_ARGS(node).readarg.offset = iop->offset; + SERP_ARGS(node).readarg.count = count; + SERP_ARGS(node).readarg.totalcount = UINT32_C(0xdeadbeef); + + rr.readres_u.reply.data.data_val = buffer; + + if ( nfscall( nfs->server, + NFSPROC_READ, + (xdrproc_t)xdr_readargs, &SERP_FILE(node), + (xdrproc_t)xdr_readres, &rr) ) { + return -1; + } + + + if (NFS_OK != rr.status) { + rtems_set_errno_and_return_minus_one(rr.status); + } + +#if DEBUG & DEBUG_SYSCALLS + fprintf(stderr, + "Read %i (asked for %i) bytes from offset %i to 0x%08x\n", + rr.readres_u.reply.data.data_len, + count, + iop->offset, + rr.readres_u.reply.data.data_val); +#endif + + + return rr.readres_u.reply.data.data_len; +} + +/* this is called by readdir() / getdents() */ +static ssize_t nfs_dir_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ +DirInfo di = iop->pathinfo.node_access_2; +RpcUdpServer server = ((Nfs)iop->pathinfo.mt_entry->fs_info)->server; + + if ( di->eofreached ) + return 0; + + di->ptr = di->buf = buffer; + + /* align + round down the buffer */ + count &= ~ (DIRENT_HEADER_SIZE - 1); + di->len = count; + +#if 0 + /* now estimate the number of entries we should ask for */ + count /= DIRENT_HEADER_SIZE + CONFIG_AVG_NAMLEN; + + /* estimate the encoded size that might take up */ + count *= dirres_entry_size + CONFIG_AVG_NAMLEN; +#else + /* integer arithmetics are better done the other way round */ + count *= dirres_entry_size + CONFIG_AVG_NAMLEN; + count /= DIRENT_HEADER_SIZE + CONFIG_AVG_NAMLEN; +#endif + + if (count > NFS_MAXDATA) + count = NFS_MAXDATA; + + di->readdirargs.count = count; + +#if DEBUG & DEBUG_READDIR + fprintf(stderr, + "Readdir: asking for %i XDR bytes, buffer is %i\n", + count, di->len); +#endif + + if ( nfscall( + server, + NFSPROC_READDIR, + (xdrproc_t)xdr_readdirargs, &di->readdirargs, + (xdrproc_t)xdr_dir_info, di) ) { + return -1; + } + + + if (NFS_OK != di->status) { + rtems_set_errno_and_return_minus_one(di->status); + } + + return (char*)di->ptr - (char*)buffer; +} + +static ssize_t nfs_file_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ +NfsNode node = iop->pathinfo.node_access; +Nfs nfs = node->nfs; +int e; + + if (count > NFS_MAXDATA) + count = NFS_MAXDATA; + + + SERP_ARGS(node).writearg.beginoffset = UINT32_C(0xdeadbeef); + if ( LIBIO_FLAGS_APPEND & iop->flags ) { + if ( updateAttr(node, 0) ) { + return -1; + } + SERP_ARGS(node).writearg.offset = SERP_ATTR(node).size; + } else { + SERP_ARGS(node).writearg.offset = iop->offset; + } + SERP_ARGS(node).writearg.totalcount = UINT32_C(0xdeadbeef); + SERP_ARGS(node).writearg.data.data_len = count; + SERP_ARGS(node).writearg.data.data_val = (void*)buffer; + + /* write XDR buffer size will be chosen by nfscall based + * on the PROC specifier + */ + + if ( nfscall( nfs->server, + NFSPROC_WRITE, + (xdrproc_t)xdr_writeargs, &SERP_FILE(node), + (xdrproc_t)xdr_attrstat, &node->serporid) ) { + return -1; + } + + + if (NFS_OK != (e=node->serporid.status) ) { + /* try at least to recover the current attributes */ + updateAttr(node, 1 /* force */); + rtems_set_errno_and_return_minus_one(e); + } + + node->age = nowSeconds(); + + return count; +} + +static rtems_off64_t nfs_file_lseek( + rtems_libio_t *iop, + rtems_off64_t length, + int whence +) +{ +#if DEBUG & DEBUG_SYSCALLS + fprintf(stderr, + "lseek to %i (length %i, whence %i)\n", + iop->offset, + length, + whence); +#endif + if ( SEEK_END == whence ) { + /* rtems (4.6.2) libcsupport code 'lseek' uses iop->size to + * compute the offset. We don't want to track the file size + * by updating 'iop->size' constantly. + * Since lseek is the only place using iop->size, we work + * around this by tweaking the offset here... + */ + NfsNode node = iop->pathinfo.node_access; + fattr *fa = &SERP_ATTR(node); + + if (updateAttr(node, 0 /* only if old */)) { + return -1; + } + iop->offset = fa->size; + } + + /* this is particularly easy :-) */ + return iop->offset; +} + +static rtems_off64_t nfs_dir_lseek( + rtems_libio_t *iop, + rtems_off64_t length, + int whence +) +{ +DirInfo di = iop->pathinfo.node_access_2; + + /* we don't support anything other than + * rewinding + */ + if (SEEK_SET != whence || 0 != length) { + errno = ENOTSUP; + return -1; + } + + /* rewind cookie */ + memset( &di->readdirargs.cookie, + 0, + sizeof(di->readdirargs.cookie) ); + + di->eofreached = FALSE; + + return iop->offset; +} + +#if 0 /* structure types for reference */ +struct fattr { + ftype type; + u_int mode; + u_int nlink; + u_int uid; + u_int gid; + u_int size; + u_int blocksize; + u_int rdev; + u_int blocks; + u_int fsid; + u_int fileid; + nfstime atime; + nfstime mtime; + nfstime ctime; +}; + +struct stat +{ + dev_t st_dev; + ino_t st_ino; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + rtems_off64_t st_size; + /* SysV/sco doesn't have the rest... But Solaris, eabi does. */ +#if defined(__svr4__) && !defined(__PPC__) && !defined(__sun__) + time_t st_atime; + time_t st_mtime; + time_t st_ctime; +#else + time_t st_atime; + long st_spare1; + time_t st_mtime; + long st_spare2; + time_t st_ctime; + long st_spare3; + long st_blksize; + long st_blocks; + long st_spare4[2]; +#endif +}; +#endif + +/* common for file/dir/link */ +static int nfs_fstat( + rtems_filesystem_location_info_t *loc, + struct stat *buf +) +{ +NfsNode node = loc->node_access; +fattr *fa = &SERP_ATTR(node); + + if (updateAttr(node, 0 /* only if old */)) { + return -1; + } + +/* done by caller + memset(buf, 0, sizeof(*buf)); + */ + + /* translate */ + + /* one of the branches hopefully is optimized away */ + if (sizeof(ino_t) < sizeof(u_int)) { + buf->st_dev = NFS_MAKE_DEV_T_INO_HACK((NfsNode)loc->node_access); + } else { + buf->st_dev = NFS_MAKE_DEV_T((NfsNode)loc->node_access); + } + buf->st_mode = fa->mode; + buf->st_nlink = fa->nlink; + buf->st_uid = fa->uid; + buf->st_gid = fa->gid; + buf->st_size = fa->size; + /* Set to "preferred size" of this NFS client implementation */ + buf->st_blksize = nfsStBlksize ? nfsStBlksize : fa->blocksize; + buf->st_rdev = fa->rdev; + buf->st_blocks = fa->blocks; + buf->st_ino = fa->fileid; + buf->st_atime = fa->atime.seconds; + buf->st_mtime = fa->mtime.seconds; + buf->st_ctime = fa->ctime.seconds; + +#if 0 /* NFS should return the modes */ + switch(fa->type) { + default: + case NFNON: + case NFBAD: + break; + + case NFSOCK: buf->st_mode |= S_IFSOCK; break; + case NFFIFO: buf->st_mode |= S_IFIFO; break; + case NFREG : buf->st_mode |= S_IFREG; break; + case NFDIR : buf->st_mode |= S_IFDIR; break; + case NFBLK : buf->st_mode |= S_IFBLK; break; + case NFCHR : buf->st_mode |= S_IFCHR; break; + case NFLNK : buf->st_mode |= S_IFLNK; break; + } +#endif + + return 0; +} + +/* a helper which does the real work for + * a couple of handlers (such as chmod, + * ftruncate or utime) + */ +static int +nfs_sattr(NfsNode node, sattr *arg, u_long mask) +{ + +struct timeval now; +nfstime nfsnow, t; +int e; +u_int mode; + + if (updateAttr(node, 0 /* only if old */)) + return -1; + + rtems_clock_get_tod_timeval(&now); + + /* TODO: add rtems EPOCH - UNIX EPOCH seconds */ + nfsnow.seconds = now.tv_sec; + nfsnow.useconds = now.tv_usec; + + /* merge permission bits into existing type bits */ + mode = SERP_ATTR(node).mode; + if (mask & SATTR_MODE) { + mode &= S_IFMT; + mode |= arg->mode & ~S_IFMT; + } else { + mode = -1; + } + SERP_ARGS(node).sattrarg.attributes.mode = mode; + + SERP_ARGS(node).sattrarg.attributes.uid = + (mask & SATTR_UID) ? arg->uid : -1; + + SERP_ARGS(node).sattrarg.attributes.gid = + (mask & SATTR_GID) ? arg->gid : -1; + + SERP_ARGS(node).sattrarg.attributes.size = + (mask & SATTR_SIZE) ? arg->size : -1; + + if (mask & SATTR_ATIME) + t = arg->atime; + else if (mask & SATTR_TOUCHA) + t = nfsnow; + else + t.seconds = t.useconds = -1; + SERP_ARGS(node).sattrarg.attributes.atime = t; + + if (mask & SATTR_ATIME) + t = arg->mtime; + else if (mask & SATTR_TOUCHA) + t = nfsnow; + else + t.seconds = t.useconds = -1; + SERP_ARGS(node).sattrarg.attributes.mtime = t; + + node->serporid.status = NFS_OK; + + if ( nfscall( node->nfs->server, + NFSPROC_SETATTR, + (xdrproc_t)xdr_sattrargs, &SERP_FILE(node), + (xdrproc_t)xdr_attrstat, &node->serporid) ) { +#if DEBUG & DEBUG_SYSCALLS + fprintf(stderr, + "nfs_sattr (mask 0x%08x): %s", + mask, + strerror(errno)); +#endif + return -1; + } + + if (NFS_OK != (e=node->serporid.status) ) { +#if DEBUG & DEBUG_SYSCALLS + fprintf(stderr,"nfs_sattr: %s\n",strerror(e)); +#endif + /* try at least to recover the current attributes */ + updateAttr(node, 1 /* force */); + rtems_set_errno_and_return_minus_one(e); + } + + node->age = nowSeconds(); + + return 0; +} + + +/* common for file/dir/link */ +static int nfs_fchmod( + rtems_filesystem_location_info_t *loc, + mode_t mode +) +{ +sattr arg; + + arg.mode = mode; + return nfs_sattr(loc->node_access, &arg, SATTR_MODE); + +} + +/* just set the size attribute to 'length' + * the server will take care of the rest :-) + */ +static int nfs_file_ftruncate( + rtems_libio_t *iop, + rtems_off64_t length +) +{ +sattr arg; + + arg.size = length; + /* must not modify any other attribute; if we are not the owner + * of the file or directory but only have write access changing + * any attribute besides 'size' will fail... + */ + return nfs_sattr(iop->pathinfo.node_access, + &arg, + SATTR_SIZE); +} + +/* files and symlinks are removed + * by the common nfs_unlink() routine. + * NFS has a different NFSPROC_RMDIR + * call, though... + */ +static int nfs_dir_rmnod( + rtems_filesystem_location_info_t *parentpathloc, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN */ +) +{ + return nfs_do_unlink(parentpathloc, pathloc, NFSPROC_RMDIR); +} + +/* the file handlers table */ +static +struct _rtems_filesystem_file_handlers_r nfs_file_file_handlers = { + nfs_file_open, /* OPTIONAL; may be defaulted */ + nfs_file_close, /* OPTIONAL; may be defaulted */ + nfs_file_read, /* OPTIONAL; may be defaulted */ + nfs_file_write, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_ioctl, + nfs_file_lseek, /* OPTIONAL; may be defaulted */ + nfs_fstat, /* OPTIONAL; may be defaulted */ + nfs_fchmod, /* OPTIONAL; may be defaulted */ + nfs_file_ftruncate, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_fpathconf, + rtems_filesystem_default_fsync, + rtems_filesystem_default_fdatasync, + rtems_filesystem_default_fcntl, + nfs_unlink, /* OPTIONAL; may be defaulted */ +}; + +/* the directory handlers table */ +static +struct _rtems_filesystem_file_handlers_r nfs_dir_file_handlers = { + nfs_dir_open, /* OPTIONAL; may be defaulted */ + nfs_dir_close, /* OPTIONAL; may be defaulted */ + nfs_dir_read, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_write, + rtems_filesystem_default_ioctl, + nfs_dir_lseek, /* OPTIONAL; may be defaulted */ + nfs_fstat, /* OPTIONAL; may be defaulted */ + nfs_fchmod, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_ftruncate, + rtems_filesystem_default_fpathconf, + rtems_filesystem_default_fsync, + rtems_filesystem_default_fdatasync, + rtems_filesystem_default_fcntl, + nfs_dir_rmnod, /* OPTIONAL; may be defaulted */ +}; + +/* the link handlers table */ +static +struct _rtems_filesystem_file_handlers_r nfs_link_file_handlers = { + rtems_filesystem_default_open, + rtems_filesystem_default_close, + rtems_filesystem_default_read, + rtems_filesystem_default_write, + rtems_filesystem_default_ioctl, + rtems_filesystem_default_lseek, + nfs_fstat, /* OPTIONAL; may be defaulted */ + nfs_fchmod, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_ftruncate, + rtems_filesystem_default_fpathconf, + rtems_filesystem_default_fsync, + rtems_filesystem_default_fdatasync, + rtems_filesystem_default_fcntl, + nfs_unlink, /* OPTIONAL; may be defaulted */ +}; + +/* we need a dummy driver entry table to get a + * major number from the system + */ +static +rtems_device_driver nfs_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + /* we don't really use this routine because + * we cannot supply an argument (contrary + * to what the 'arg' parameter suggests - it + * is always set to 0 by the generics :-() + * and because we don't want the user to + * have to deal with the major number (which + * OTOH is something WE are interested in. The + * only reason for using this API was getting + * a major number, after all). + * + * Something must be present, however, to + * reserve a slot in the driver table. + */ + return RTEMS_SUCCESSFUL; +} + +static rtems_driver_address_table drvNfs = { + nfs_initialize, + 0, /* open */ + 0, /* close */ + 0, /* read */ + 0, /* write */ + 0 /* control */ +}; + +/* Dump a list of the currently mounted NFS to a file */ +int +nfsMountsShow(FILE *f) +{ +char *mntpt = 0; +Nfs nfs; + + if (!f) + f = stdout; + + if ( !(mntpt=malloc(MAXPATHLEN)) ) { + fprintf(stderr,"nfsMountsShow(): no memory\n"); + return -1; + } + + fprintf(f,"Currently Mounted NFS:\n"); + + LOCK(nfsGlob.llock); + + for (nfs = nfsGlob.mounted_fs; nfs; nfs=nfs->next) { + fprintf(f,"%s on ", nfs->mt_entry->dev); + if (rtems_filesystem_resolve_location(mntpt, MAXPATHLEN, &nfs->mt_entry->mt_fs_root)) + fprintf(f,"<UNABLE TO LOOKUP MOUNTPOINT>\n"); + else + fprintf(f,"%s\n",mntpt); + } + + UNLOCK(nfsGlob.llock); + + free(mntpt); + return 0; +} + +#if 0 +CCJ_REMOVE_MOUNT +/* convenience wrapper + * + * NOTE: this routine calls NON-REENTRANT + * gethostbyname() if the host is + * not in 'dot' notation. + */ +int +nfsMount(char *uidhost, char *path, char *mntpoint) +{ +struct stat st; +int devl; +char *host; +int rval = -1; +char *dev = 0; + + if (!uidhost || !path || !mntpoint) { + fprintf(stderr,"usage: nfsMount(""[uid.gid@]host"",""path"",""mountpoint"")\n"); + nfsMountsShow(stderr); + return -1; + } + + if ( !(dev = malloc((devl=strlen(uidhost) + 20 + strlen(path)+1))) ) { + fprintf(stderr,"nfsMount: out of memory\n"); + return -1; + } + + /* Try to create the mount point if nonexistent */ + if (stat(mntpoint, &st)) { + if (ENOENT != errno) { + perror("nfsMount trying to create mount point - stat failed"); + goto cleanup; + } else if (mkdir(mntpoint,0777)) { + perror("nfsMount trying to create mount point"); + goto cleanup; + } + } + + if ( !(host=strchr(uidhost,UIDSEP)) ) { + host = uidhost; + } else { + host++; + } + + if (isdigit((unsigned char)*host)) { + /* avoid using gethostbyname */ + sprintf(dev,"%s:%s",uidhost,path); + } else { + struct hostent *h; + + /* copy the uid part (hostname will be + * overwritten) + */ + strcpy(dev, uidhost); + + /* NOTE NOTE NOTE: gethostbyname is NOT + * thread safe. This is UGLY + */ + +/* BEGIN OF NON-THREAD SAFE REGION */ + + h = gethostbyname(host); + + if ( !h || + !inet_ntop( AF_INET, + (struct in_addr*)h->h_addr_list[0], + dev + (host - uidhost), + devl - (host - uidhost) ) + ) { + fprintf(stderr,"nfsMount: host '%s' not found\n",host); + goto cleanup; + } + +/* END OF NON-THREAD SAFE REGION */ + + /* append ':<path>' */ + strcat(dev,":"); + strcat(dev,path); + } + + printf("Trying to mount %s on %s\n",dev,mntpoint); + + if (mount(dev, + mntpoint, + "nfs", + RTEMS_FILESYSTEM_READ_WRITE, + NULL)) { + perror("nfsMount - mount"); + goto cleanup; + } + + rval = 0; + +cleanup: + free(dev); + return rval; +} +#endif + +/* HERE COMES A REALLY UGLY HACK */ + +/* This is stupid; it is _very_ hard to find the path + * leading to a rtems_filesystem_location_info_t node :-( + * The only easy way is making the location the current + * directory and issue a getcwd(). + * However, since we don't want to tamper with the + * current directory, we must create a separate + * task to do the job for us - sigh. + */ + +typedef struct ResolvePathArgRec_ { + rtems_filesystem_location_info_t *loc; /* IN: location to resolve */ + char *buf; /* IN/OUT: buffer where to put the path */ + int len; /* IN: buffer length */ + rtems_id sync; /* IN: synchronization */ + rtems_status_code status; /* OUT: result */ +} ResolvePathArgRec, *ResolvePathArg; + +static void +resolve_path(rtems_task_argument arg) +{ +ResolvePathArg rpa = (ResolvePathArg)arg; +rtems_filesystem_location_info_t old; + + /* IMPORTANT: let the helper task have its own libio environment (i.e. cwd) */ + if (RTEMS_SUCCESSFUL == (rpa->status = rtems_libio_set_private_env())) { + + old = rtems_filesystem_current; + + rtems_filesystem_current = *(rpa->loc); + + if ( !getcwd(rpa->buf, rpa->len) ) + rpa->status = RTEMS_UNSATISFIED; + + /* must restore the cwd because 'freenode' will be called on it */ + rtems_filesystem_current = old; + } + rtems_semaphore_release(rpa->sync); + rtems_task_delete(RTEMS_SELF); +} + + +/* a utility routine to find the path leading to a + * rtems_filesystem_location_info_t node + * + * INPUT: 'loc' and a buffer 'buf' (length 'len') to hold the + * path. + * OUTPUT: path copied into 'buf' + * + * RETURNS: 0 on success, RTEMS error code on error. + */ +rtems_status_code +rtems_filesystem_resolve_location(char *buf, int len, rtems_filesystem_location_info_t *loc) +{ +ResolvePathArgRec arg; +rtems_id tid = 0; +rtems_task_priority pri; +rtems_status_code status; + + arg.loc = loc; + arg.buf = buf; + arg.len = len; + arg.sync = 0; + + status = rtems_semaphore_create( + rtems_build_name('r','e','s','s'), + 0, + RTEMS_SIMPLE_BINARY_SEMAPHORE, + 0, + &arg.sync); + + if (RTEMS_SUCCESSFUL != status) + goto cleanup; + + rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &pri); + + status = rtems_task_create( + rtems_build_name('r','e','s','s'), + pri, + RTEMS_MINIMUM_STACK_SIZE + 50000, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &tid); + + if (RTEMS_SUCCESSFUL != status) + goto cleanup; + + status = rtems_task_start(tid, resolve_path, (rtems_task_argument)&arg); + + if (RTEMS_SUCCESSFUL != status) { + rtems_task_delete(tid); + goto cleanup; + } + + + /* synchronize with the helper task */ + rtems_semaphore_obtain(arg.sync, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + + status = arg.status; + +cleanup: + if (arg.sync) + rtems_semaphore_delete(arg.sync); + + return status; +} + +int +nfsSetTimeout(uint32_t timeout_ms) +{ +rtems_interrupt_level k; +uint32_t s,us; + + if ( timeout_ms > 100000 ) { + /* out of range */ + return -1; + } + + s = timeout_ms/1000; + us = (timeout_ms % 1000) * 1000; + + rtems_interrupt_disable(k); + _nfscalltimeout.tv_sec = s; + _nfscalltimeout.tv_usec = us; + rtems_interrupt_enable(k); + + return 0; +} + +uint32_t +nfsGetTimeout( void ) +{ +rtems_interrupt_level k; +uint32_t s,us; + rtems_interrupt_disable(k); + s = _nfscalltimeout.tv_sec; + us = _nfscalltimeout.tv_usec; + rtems_interrupt_enable(k); + return s*1000 + us/1000; +} diff --git a/cpukit/libfs/src/nfsclient/src/nfs.modini.c b/cpukit/libfs/src/nfsclient/src/nfs.modini.c new file mode 100644 index 0000000000..22095cf53f --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/nfs.modini.c @@ -0,0 +1,31 @@ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "librtemsNfs.h" + +/* CEXP dynamic loader support */ + +void +_cexpModuleInitialize(void *mod) +{ +#if defined(DEBUG) + /* print load address (in case we crash while initializing) */ +unsigned lr; + __asm__ __volatile__( + " bl thisis_loaded_at \n" + "thisis_loaded_at: \n" + " mflr %0 \n" + : "=r"(lr) ::"lr"); + printf("thisis_loaded_at: 0x%08x\n",lr); +#endif + nfsInit(0,0); +} + +int +_cexpModuleFinalize(void *mod) +{ + return nfsCleanup(); +} + + diff --git a/cpukit/libfs/src/nfsclient/src/nfsTest.c b/cpukit/libfs/src/nfsclient/src/nfsTest.c new file mode 100644 index 0000000000..a372a7511c --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/nfsTest.c @@ -0,0 +1,381 @@ +/* $Id$ */ + +/* Test program for evaluating NFS read throughput */ + +/* Author: Till Straumann <strauman@slac.stanford.edu>, 2006 */ + +/* This test code allows for evaluating NFS read performance + * under various scenarios: + * - synchronous reads with various buffer sizes (select + * 'num_readers' == 0, see below). + * - pseudo 'read-ahead' using multiple threads that issue + * NFS reads from the same file (but from different offsets) + * in parallel. + * Rationale: each NFS read request is synchronous, i.e., the + * caller sends a request to the server and waits for the + * reply to come back. Performance enhancement can be expected + * by requesting multiple blocks in parallel rather than + * sequentially. + * + * rtems_interval + * nfsTestRead(char *file_name, int chunk_size, int num_readers); + * + * 1) creates 'num_readers' threads, each opening 'file_name' for + * reading on a separate file descriptor. + * 2) creates message queues for communicating with reader threads + * + * 3) read file using nfsTestReadBigbuf() until EOF is reached + * + * 4) releases resources. + * + * RETURNS: Time elapsed during step 3 in ms. This is measured + * using the system clock so make sure the test file + * is big enough. + * + * nfsTestReadBigbuf() synchronously reads a block of + * 'num_readers * chunk_size' (which may be bigger than + * the UDP limit of 8k) using 'num_reader' threads to + * retrieve the various pieces of the big block in parallel. + * This speeds up things since several RPC calls can + * be in the works at once. + * + * NOTES: + * - if 'num_readers' == 0 this corresponds to an 'ordinary' + * NFS read. 'num_readers' == 1 schedules a single reader + * thread (== ordinary NFS read + message passing overhead). + * - no actual processing on the data is done; they are simply + * thrown away. A real, performance-critical application could + * pipeline 'reader' and 'cruncher' threads. + * - read is not completely asynchronous; synchronization is still + * performed at 'big block' boundaries (num_readers * chunk_size). + */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/error.h> + +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> + +unsigned nfsTestReaderPri = 80; + +struct nfsTestReq_ { + unsigned offset; /* file offset */ + int size; /* IN: block size to read (must be < 8192), OUT: bytes actually read */ + void *buf; /* data buffer address */ +}; + +/* Queue for sending requests to parallel reader tasks */ +rtems_id nfsTestRQ = 0; +/* Queue to pickup replies from parallel reader tasks */ +rtems_id nfsTestAQ = 0; + + +/* Reader task; opens its own file descriptor + * and works on requests: + * - obtain request from request queue. + * - lseek to the requested file offset + * - NFS read into buffer + * - queue reply. + * + * Note that this implementation is very simple + * - no full error checking. + * - file is opened/closed by thread + * it's main purpose is running quick tests. + */ +static rtems_task +nfsTestReader(rtems_task_argument arg) +{ +int fd = open((char*)arg,O_RDONLY); +unsigned long s; +struct nfsTestReq_ r; +rtems_status_code sc; + + if ( fd < 0 ) { + perror("nfsReader: opening file"); + goto cleanup; + } + do { + s = sizeof(r); + sc = rtems_message_queue_receive(nfsTestRQ, &r, &s, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc, "(Error) reading from message queue"); + goto cleanup; + } + if ( !r.buf ) { + /* They send a NULL buffer as a shutdown request */ + break; + } +#ifdef DEBUG + printf("Reader: reading offset %u, size %i to %p ... ", + r.offset, r.size, r.buf); +#endif + /* seek to requested offset */ + lseek(fd, r.offset, SEEK_SET); + r.size = read(fd, r.buf, r.size); +#ifdef DEBUG + printf("got %i\n",r.size); +#endif + rtems_message_queue_send(nfsTestAQ, &r, sizeof(r)); + } while (1) ; + +cleanup: + if ( fd >= 0 ) + close(fd); + rtems_task_delete(RTEMS_SELF); +} + + +/* helper to create and start a reader task */ +static rtems_id +taskSpawn(char *filenm, int inst) +{ +rtems_status_code sc; +rtems_id tid; + + sc = rtems_task_create( + rtems_build_name('n','t','t','0'+inst), + nfsTestReaderPri, + 1400, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &tid); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc,"(Error) Creating nfs reader task %i",inst); + return 0; + } + + sc = rtems_task_start(tid, nfsTestReader, (rtems_task_argument)filenm); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc,"(Error) Staritng nfs reader task %i",inst); + rtems_task_delete(tid); + return 0; + } + + return tid; +} + +/* + * Read nrd*sz bytes into 'buf' from file offset 'off' + * using 'nrd' parallel reader tasks to do the job. + * This helper routine schedules 'nrd' requests to + * the reader tasks and waits for all requests to + * finish. + * + * RETURNS: number of bytes read or -1 (error). + * + * CAVEATS: + * - assumes read requests always return 'sz' bytes + * unless the end of file is reached. + * THIS ASSUMPTION SHOULD NOT BE MADE WHEN WRITING + * ANY 'REAL' CODE. + */ +static int +nfsTestReadBigbuf(char *buf, int off, int sz, int nrd) +{ +int i,rval=0; +struct nfsTestReq_ r; + r.buf = buf; + r.size = sz; + r.offset = off; + /* send out parallel requests */ + for (i=0; i<nrd; i++) { + rtems_message_queue_send(nfsTestRQ, &r, sizeof(r)); + r.offset += sz; + r.buf += sz; + } + /* wait for answers */ + for (i=0; i<nrd; i++) { + unsigned long s = sizeof(r); + rtems_message_queue_receive(nfsTestAQ, &r, &s, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if ( r.size < 0 ) { + fprintf(stderr,"A reader failed\n"); + rval = -1; + } else { + /* FIXME sanity checks: + * - catch case where any-but-last read returns < sz + */ + if ( rval >= 0 ) { + rval += r.size; + } + } + } + return rval; +} + +/* Main test routine + * + * Read file 'fname' usint 'nrd' parallel reader tasks, + * each operating on chunks of 'sz' bytes. + * + * RETURNS: time elapsed in milliseconds. This is measured + * using the system clock. Hence, for the result + * to be meaningful, the file must be big enough. + * + */ +rtems_interval +nfsTestRead(char *fnam, int sz, int nrd) +{ +int i; +unsigned off; +rtems_interval now=-1, then, tickspsec; +rtems_status_code sc; +int fd=-1; +char *buf=0; + + if ( nrd < 0 ) + nrd = 0; + + if ( sz < 0 || sz > 8192 ) { + fprintf(stderr,"\n"); + return -1; + } + + nfsTestRQ = nfsTestAQ = 0; + + /* Allocate buffer */ + if ( ! (buf=malloc(sz*(nrd ? nrd : 1))) ) { + perror("allocating buffer"); + goto cleanup; + } + + /* Don't bother proceeding if we can't open the file for reading */ + if ( (fd=open(fnam,O_RDONLY)) < 0 ) { + perror("opening file"); + goto cleanup; + } + if ( nrd ) { + close(fd); fd = -1; + } + + /* Create request queue */ + if ( nrd ) { + sc = rtems_message_queue_create( + rtems_build_name('n','t','r','q'), + nrd, + sizeof(struct nfsTestReq_), + RTEMS_DEFAULT_ATTRIBUTES, + & nfsTestRQ ); + + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc, "(Error) creating request queue"); + nfsTestRQ = 0; + goto cleanup; + } + + /* Spawn reader tasks */ + for ( i=0; i<nrd; i++ ) { + if ( ! taskSpawn(fnam, i) ) + goto cleanup; + } + + /* Create reply queue */ + sc = rtems_message_queue_create( + rtems_build_name('n','t','a','q'), + nrd, + sizeof(struct nfsTestReq_), + RTEMS_DEFAULT_ATTRIBUTES, + & nfsTestAQ ); + + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc, "(Error) creating reply queue"); + nfsTestAQ = 0; + goto cleanup; + } + } + + /* Timed main loop */ + then = rtems_clock_get_ticks_since_boot(); + + if ( nrd ) { + off = 0; + while ((i = nfsTestReadBigbuf(buf, off, sz, nrd)) > 0 ) { +#ifdef DEBUG + printf("bigbuf got %i\n", i); +#endif + off += i; + } + } else { + while ( (i = read(fd, buf, sz)) > 0 ) + /* nothing else to do */; + if ( i < 0 ) { + perror("reading"); + goto cleanup; + } + } + + now = rtems_clock_get_ticks_since_boot(); + now = (now-then)*1000; + ticksspec = rtems_clock_get_ticks_per_second(); + now /= tickspsec; /* time in ms */ + +cleanup: + if ( fd >= 0 ) + close(fd); + + if ( nfsTestRQ ) { + /* request tasks to shutdown by sending NULL buf request */ + struct nfsTestReq_ r; + r.buf = 0; + for ( i=0; i<nrd; i++ ) { + rtems_message_queue_send( nfsTestRQ, &r, sizeof(r) ); + } + /* cheat: instead of proper synchronization with shutdown we simply + * delay for a second... + */ + rtems_task_wake_after( tickspsec ); + rtems_message_queue_delete( nfsTestRQ ); + } + if ( nfsTestAQ ) + rtems_message_queue_delete( nfsTestAQ ); + free(buf); + return now; +} diff --git a/cpukit/libfs/src/nfsclient/src/rpcio.c b/cpukit/libfs/src/nfsclient/src/rpcio.c new file mode 100644 index 0000000000..3316cccb5f --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/rpcio.c @@ -0,0 +1,1792 @@ +/* $Id$ */ + +/* RPC multiplexor for a multitasking environment */ + +/* Author: Till Straumann <strauman@slac.stanford.edu>, 2002 */ + +/* This code funnels arbitrary task's UDP/RPC requests + * through one socket to arbitrary servers. + * The replies are gathered and dispatched to the + * requestors. + * One task handles all the sending and receiving + * work including retries. + * It is up to the requestor, however, to do + * the XDR encoding of the arguments / decoding + * of the results (except for the RPC header which + * is handled by the daemon). + */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <rtems.h> +#include <rtems/error.h> +#include <rtems/rtems_bsdnet.h> +#include <stdlib.h> +#include <time.h> +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <assert.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "rpcio.h" + +/****************************************************************/ +/* CONFIGURABLE PARAMETERS */ +/****************************************************************/ + +#define MBUF_RX /* If defined: use mbuf XDR stream for + * decoding directly out of mbufs + * Otherwise, the regular 'recvfrom()' + * interface will be used involving an + * extra buffer allocation + copy step. + */ + +#define MBUF_TX /* If defined: avoid copying data when + * sending. Instead, use a wrapper to + * 'sosend()' which will point an MBUF + * directly to our buffer space. + * Note that the BSD stack does not copy + * data when fragmenting packets - it + * merely uses an mbuf chain pointing + * into different areas of the data. + * + * If undefined, the regular 'sendto()' + * interface is used. + */ + +#undef REJECT_SERVERIP_MISMATCH + /* If defined, RPC replies must come from the server + * that was queried. Eric Norum has reported problems + * with clustered NFS servers. So we disable this + * reducing paranoia... + */ + +/* daemon task parameters */ +#define RPCIOD_STACK 10000 +#define RPCIOD_PRIO 100 /* *fallback* priority */ + +/* depth of the message queue for sending + * RPC requests to the daemon + */ +#define RPCIOD_QDEPTH 20 + +/* Maximum retry limit for retransmission */ +#define RPCIOD_RETX_CAP_S 3 /* seconds */ + +/* Default timeout for RPC calls */ +#define RPCIOD_DEFAULT_TIMEOUT (&_rpc_default_timeout) +static struct timeval _rpc_default_timeout = { 10 /* secs */, 0 /* usecs */ }; + +/* how many times should we try to resend a failed + * transaction with refreshed AUTHs + */ +#define RPCIOD_REFRESH 2 + +/* Events we are using; the RPC_EVENT + * MUST NOT be used by any application + * thread doing RPC IO (e.g. NFS) + */ +#define RTEMS_RPC_EVENT RTEMS_EVENT_30 /* THE event used by RPCIO. Every task doing + * RPC IO will receive this - hence it is + * RESERVED + */ +#define RPCIOD_RX_EVENT RTEMS_EVENT_1 /* Events the RPCIOD is using/waiting for */ +#define RPCIOD_TX_EVENT RTEMS_EVENT_2 +#define RPCIOD_KILL_EVENT RTEMS_EVENT_3 /* send to the daemon to kill it */ + +#define LD_XACT_HASH 8 /* ld of the size of the transaction hash table */ + + +/* Debugging Flags */ + +/* NOTE: defining DEBUG 0 leaves some 'assert()' paranoia checks + * but produces no output + */ + +#define DEBUG_TRACE_XACT (1<<0) +#define DEBUG_EVENTS (1<<1) +#define DEBUG_MALLOC (1<<2) +#define DEBUG_TIMEOUT (1<<3) +#define DEBUG_PACKLOSS (1<<4) /* This introduces random, artificial packet losses to test retransmission */ + +#define DEBUG_PACKLOSS_FRACT (0xffffffff/10) + +/* USE PARENTHESIS WHEN 'or'ing MULTIPLE FLAGS: (DEBUG_XX | DEBUG_YY) */ +#define DEBUG (0) + +/****************************************************************/ +/* END OF CONFIGURABLE SECTION */ +/****************************************************************/ + +/* prevent rollover of our timers by readjusting the epoch on the fly */ +#if (DEBUG) & DEBUG_TIMEOUT +#define RPCIOD_EPOCH_SECS 10 +#else +#define RPCIOD_EPOCH_SECS 10000 +#endif + +#ifdef DEBUG +#define ASSERT(arg) assert(arg) +#else +#define ASSERT(arg) if (arg) +#endif + +/****************************************************************/ +/* MACROS */ +/****************************************************************/ + + +#define XACT_HASHS (1<<(LD_XACT_HASH)) /* the hash table size derived from the ld */ +#define XACT_HASH_MSK ((XACT_HASHS)-1) /* mask to extract the hash index from a RPC-XID */ + + +#define MU_LOCK(mutex) do { \ + assert( \ + RTEMS_SUCCESSFUL == \ + rtems_semaphore_obtain( \ + (mutex), \ + RTEMS_WAIT, \ + RTEMS_NO_TIMEOUT \ + ) ); \ + } while(0) + +#define MU_UNLOCK(mutex) do { \ + assert( \ + RTEMS_SUCCESSFUL == \ + rtems_semaphore_release( \ + (mutex) \ + ) ); \ + } while(0) + +#define MU_CREAT(pmutex) do { \ + assert( \ + RTEMS_SUCCESSFUL == \ + rtems_semaphore_create( \ + rtems_build_name( \ + 'R','P','C','l' \ + ), \ + 1, \ + MUTEX_ATTRIBUTES, \ + 0, \ + (pmutex)) ); \ + } while (0) + + +#define MU_DESTROY(mutex) do { \ + assert( \ + RTEMS_SUCCESSFUL == \ + rtems_semaphore_delete( \ + mutex \ + ) ); \ + } while (0) + +#define MUTEX_ATTRIBUTES (RTEMS_LOCAL | \ + RTEMS_PRIORITY | \ + RTEMS_INHERIT_PRIORITY | \ + RTEMS_BINARY_SEMAPHORE) + +#define FIRST_ATTEMPT 0x88888888 /* some time that is never reached */ + +/****************************************************************/ +/* TYPE DEFINITIONS */ +/****************************************************************/ + +typedef rtems_interval TimeoutT; + +/* 100000th implementation of a doubly linked list; + * since only one thread is looking at these, + * we need no locking + */ +typedef struct ListNodeRec_ { + struct ListNodeRec_ *next, *prev; +} ListNodeRec, *ListNode; + + +/* Structure representing an RPC server */ +typedef struct RpcUdpServerRec_ { + RpcUdpServer next; /* linked list of all servers; protected by hlock */ + union { + struct sockaddr_in sin; + struct sockaddr sa; + } addr; + AUTH *auth; + rtems_id authlock; /* must MUTEX the auth object - it's not clear + * what is better: + * 1 having one (MUTEXed) auth per server + * who is shared among all transactions + * using that server + * 2 maintaining an AUTH per transaction + * (there are then other options: manage + * XACT pools on a per-server basis instead + * of associating a server with a XACT when + * sending) + * experience will show if the current (1) + * approach has to be changed. + */ + TimeoutT retry_period; /* dynamically adjusted retry period + * (based on packet roundtrip time) + */ + /* STATISTICS */ + unsigned long retrans; /* how many retries were issued by this server */ + unsigned long requests; /* how many requests have been sent */ + unsigned long timeouts; /* how many requests have timed out */ + unsigned long errors; /* how many errors have occurred (other than timeouts) */ + char name[20]; /* server's address in IP 'dot' notation */ +} RpcUdpServerRec; + +typedef union RpcBufU_ { + uint32_t xid; + char buf[1]; +} RpcBufU, *RpcBuf; + +/* RX Buffer implementation; this is either + * an MBUF chain (MBUF_RX configuration) + * or a buffer allocated from the heap + * where recvfrom copies the (encoded) reply + * to. The XDR routines the copy/decode + * it into the user's data structures. + */ +#ifdef MBUF_RX +typedef struct mbuf * RxBuf; /* an MBUF chain */ +static void bufFree(struct mbuf **m); +#define XID(ibuf) (*(mtod((ibuf), u_long *))) +extern void xdrmbuf_create(XDR *, struct mbuf *, enum xdr_op); +#else +typedef RpcBuf RxBuf; +#define bufFree(b) do { MY_FREE(*(b)); *(b)=0; } while(0) +#define XID(ibuf) ((ibuf)->xid) +#endif + +/* A RPC 'transaction' consisting + * of server and requestor information, + * buffer space and an XDR object + * (for encoding arguments). + */ +typedef struct RpcUdpXactRec_ { + ListNodeRec node; /* so we can put XACTs on a list */ + RpcUdpServer server; /* server this XACT goes to */ + long lifetime; /* during the lifetime, retry attempts are made */ + long tolive; /* lifetime timer */ + struct rpc_err status; /* RPC reply error status */ + long age; /* age info; needed to manage retransmission */ + long trip; /* record round trip time in ticks */ + rtems_id requestor; /* the task waiting for this XACT to complete */ + RpcUdpXactPool pool; /* if this XACT belong to a pool, this is it */ + XDR xdrs; /* argument encoder stream */ + int xdrpos; /* stream position after the (permanent) header */ + xdrproc_t xres; /* reply decoder proc - TODO needn't be here */ + caddr_t pres; /* reply decoded obj - TODO needn't be here */ +#ifndef MBUF_RX + int ibufsize; /* size of the ibuf (bytes) */ +#endif +#ifdef MBUF_TX + int refcnt; /* mbuf external storage reference count */ +#endif + int obufsize; /* size of the obuf (bytes) */ + RxBuf ibuf; /* pointer to input buffer assigned by daemon */ + RpcBufU obuf; /* output buffer (encoded args) APPENDED HERE */ +} RpcUdpXactRec; + +typedef struct RpcUdpXactPoolRec_ { + rtems_id box; + int prog; + int version; + int xactSize; +} RpcUdpXactPoolRec; + +/* a global hash table where all 'living' transaction + * objects are registered. + * A number of bits in a transaction's XID maps 1:1 to + * an index in this table. Hence, the XACT matching + * an RPC/UDP reply packet can quickly be found + * The size of this table imposes a hard limit on the + * number of all created transactions in the system. + */ +static RpcUdpXact xactHashTbl[XACT_HASHS]={0}; +static u_long xidUpper [XACT_HASHS]={0}; +static unsigned xidHashSeed = 0 ; + +/* forward declarations */ +static RpcUdpXact +sockRcv(void); + +static void +rpcio_daemon(rtems_task_argument); + +#ifdef MBUF_TX +ssize_t +sendto_nocpy ( + int s, + const void *buf, size_t buflen, + int flags, + const struct sockaddr *toaddr, int tolen, + void *closure, + void (*freeproc)(caddr_t, u_int), + void (*refproc)(caddr_t, u_int) +); +static void paranoia_free(caddr_t closure, u_int size); +static void paranoia_ref (caddr_t closure, u_int size); +#define SENDTO sendto_nocpy +#else +#define SENDTO sendto +#endif + +static RpcUdpServer rpcUdpServers = 0; /* linked list of all servers; protected by llock */ + +static int ourSock = -1; /* the socket we are using for communication */ +static rtems_id rpciod = 0; /* task id of the RPC daemon */ +static rtems_id msgQ = 0; /* message queue where the daemon picks up + * requests + */ +#ifndef NDEBUG +static rtems_id llock = 0; /* MUTEX protecting the server list */ +static rtems_id hlock = 0; /* MUTEX protecting the hash table and the list of servers */ +#endif +static rtems_id fini = 0; /* a synchronization semaphore we use during + * module cleanup / driver unloading + */ +static rtems_interval ticksPerSec; /* cached system clock rate (WHO IS ASSUMED NOT + * TO CHANGE) + */ + +rtems_task_priority rpciodPriority = 0; + +#if (DEBUG) & DEBUG_MALLOC +/* malloc wrappers for debugging */ +static int nibufs = 0; + +static inline void *MY_MALLOC(int s) +{ + if (s) { + void *rval; + MU_LOCK(hlock); + assert(nibufs++ < 2000); + MU_UNLOCK(hlock); + assert((rval = malloc(s)) != 0); + return rval; + } + return 0; +} + +static inline void *MY_CALLOC(int n, int s) +{ + if (s) { + void *rval; + MU_LOCK(hlock); + assert(nibufs++ < 2000); + MU_UNLOCK(hlock); + assert((rval = calloc(n,s)) != 0); + return rval; + } + return 0; +} + + +static inline void MY_FREE(void *p) +{ + if (p) { + MU_LOCK(hlock); + nibufs--; + MU_UNLOCK(hlock); + free(p); + } +} +#else +#define MY_MALLOC malloc +#define MY_CALLOC calloc +#define MY_FREE free +#endif + +static inline bool_t +locked_marshal(RpcUdpServer s, XDR *xdrs) +{ +bool_t rval; + MU_LOCK(s->authlock); + rval = AUTH_MARSHALL(s->auth, xdrs); + MU_UNLOCK(s->authlock); + return rval; +} + +/* Locked operations on a server's auth object */ +static inline bool_t +locked_validate(RpcUdpServer s, struct opaque_auth *v) +{ +bool_t rval; + MU_LOCK(s->authlock); + rval = AUTH_VALIDATE(s->auth, v); + MU_UNLOCK(s->authlock); + return rval; +} + +static inline bool_t +locked_refresh(RpcUdpServer s) +{ +bool_t rval; + MU_LOCK(s->authlock); + rval = AUTH_REFRESH(s->auth); + MU_UNLOCK(s->authlock); + return rval; +} + +/* Create a server object + * + */ +enum clnt_stat +rpcUdpServerCreate( + struct sockaddr_in *paddr, + rpcprog_t prog, + rpcvers_t vers, + u_long uid, + u_long gid, + RpcUdpServer *psrv + ) +{ +RpcUdpServer rval; +u_short port; +char hname[MAX_MACHINE_NAME + 1]; +int theuid, thegid; +int thegids[NGRPS]; +gid_t gids[NGROUPS]; +int len,i; +AUTH *auth; +enum clnt_stat pmap_err; +struct pmap pmaparg; + + if ( gethostname(hname, MAX_MACHINE_NAME) ) { + fprintf(stderr, + "RPCIO - error: I have no hostname ?? (%s)\n", + strerror(errno)); + return RPC_UNKNOWNHOST; + } + + if ( (len = getgroups(NGROUPS, gids) < 0 ) ) { + fprintf(stderr, + "RPCIO - error: I unable to get group ids (%s)\n", + strerror(errno)); + return RPC_FAILED; + } + + if ( len > NGRPS ) + len = NGRPS; + + for (i=0; i<len; i++) + thegids[i] = (int)gids[i]; + + theuid = (int) ((RPCIOD_DEFAULT_ID == uid) ? geteuid() : uid); + thegid = (int) ((RPCIOD_DEFAULT_ID == gid) ? getegid() : gid); + + if ( !(auth = authunix_create(hname, theuid, thegid, len, thegids)) ) { + fprintf(stderr, + "RPCIO - error: unable to create RPC AUTH\n"); + return RPC_FAILED; + } + + /* if they specified no port try to ask the portmapper */ + if (!paddr->sin_port) { + + paddr->sin_port = htons(PMAPPORT); + + pmaparg.pm_prog = prog; + pmaparg.pm_vers = vers; + pmaparg.pm_prot = IPPROTO_UDP; + pmaparg.pm_port = 0; /* not needed or used */ + + + /* dont use non-reentrant pmap_getport ! */ + + pmap_err = rpcUdpCallRp( + paddr, + PMAPPROG, + PMAPVERS, + PMAPPROC_GETPORT, + xdr_pmap, + &pmaparg, + xdr_u_short, + &port, + uid, + gid, + 0); + + if ( RPC_SUCCESS != pmap_err ) { + paddr->sin_port = 0; + return pmap_err; + } + + paddr->sin_port = htons(port); + } + + if (0==paddr->sin_port) { + return RPC_PROGNOTREGISTERED; + } + + rval = (RpcUdpServer)MY_MALLOC(sizeof(*rval)); + memset(rval, 0, sizeof(*rval)); + + if (!inet_ntop(AF_INET, &paddr->sin_addr, rval->name, sizeof(rval->name))) + sprintf(rval->name,"?.?.?.?"); + rval->addr.sin = *paddr; + + /* start with a long retransmission interval - it + * will be adapted dynamically + */ + rval->retry_period = RPCIOD_RETX_CAP_S * ticksPerSec; + + rval->auth = auth; + + MU_CREAT( &rval->authlock ); + + /* link into list */ + MU_LOCK( llock ); + rval->next = rpcUdpServers; + rpcUdpServers = rval; + MU_UNLOCK( llock ); + + *psrv = rval; + return RPC_SUCCESS; +} + +void +rpcUdpServerDestroy(RpcUdpServer s) +{ +RpcUdpServer prev; + if (!s) + return; + /* we should probably verify (but how?) that nobody + * (at least: no outstanding XACTs) is using this + * server; + */ + + /* remove from server list */ + MU_LOCK(llock); + prev = rpcUdpServers; + if ( s == prev ) { + rpcUdpServers = s->next; + } else { + for ( ; prev ; prev = prev->next) { + if (prev->next == s) { + prev->next = s->next; + break; + } + } + } + MU_UNLOCK(llock); + + /* MUST have found it */ + assert(prev); + + auth_destroy(s->auth); + + MU_DESTROY(s->authlock); + MY_FREE(s); +} + +int +rpcUdpStats(FILE *f) +{ +RpcUdpServer s; + + if (!f) f = stdout; + + fprintf(f,"RPCIOD statistics:\n"); + + MU_LOCK(llock); + for (s = rpcUdpServers; s; s=s->next) { + fprintf(f,"\nServer -- %s:\n", s->name); + fprintf(f," requests sent: %10ld, retransmitted: %10ld\n", + s->requests, s->retrans); + fprintf(f," timed out: %10ld, send errors: %10ld\n", + s->timeouts, s->errors); + fprintf(f," current retransmission interval: %dms\n", + (unsigned)(s->retry_period * 1000 / ticksPerSec) ); + } + MU_UNLOCK(llock); + + return 0; +} + +RpcUdpXact +rpcUdpXactCreate( + u_long program, + u_long version, + u_long size + ) +{ +RpcUdpXact rval=0; +struct rpc_msg header; +register int i,j; + + if (!size) + size = UDPMSGSIZE; + /* word align */ + size = (size + 3) & ~3; + + rval = (RpcUdpXact)MY_CALLOC(1,sizeof(*rval) - sizeof(rval->obuf) + size); + + if (rval) { + + header.rm_xid = 0; + header.rm_direction = CALL; + header.rm_call.cb_rpcvers = RPC_MSG_VERSION; + header.rm_call.cb_prog = program; + header.rm_call.cb_vers = version; + xdrmem_create(&(rval->xdrs), rval->obuf.buf, size, XDR_ENCODE); + + if (!xdr_callhdr(&(rval->xdrs), &header)) { + MY_FREE(rval); + return 0; + } + /* pick a free table slot and initialize the XID */ + rval->obuf.xid = time(0) ^ (uintptr_t)rval; + MU_LOCK(hlock); + rval->obuf.xid = (xidHashSeed++ ^ ((uintptr_t)rval>>10)) & XACT_HASH_MSK; + i=j=(rval->obuf.xid & XACT_HASH_MSK); + if (msgQ) { + /* if there's no message queue, refuse to + * give them transactions; we might be in the process to + * go away... + */ + do { + i=(i+1) & XACT_HASH_MSK; /* cheap modulo */ + if (!xactHashTbl[i]) { +#if (DEBUG) & DEBUG_TRACE_XACT + fprintf(stderr,"RPCIO: entering index %i, val %x\n",i,rval); +#endif + xactHashTbl[i]=rval; + j=-1; + break; + } + } while (i!=j); + } + MU_UNLOCK(hlock); + if (i==j) { + XDR_DESTROY(&rval->xdrs); + MY_FREE(rval); + return 0; + } + rval->obuf.xid = xidUpper[i] | i; + rval->xdrpos = XDR_GETPOS(&(rval->xdrs)); + rval->obufsize = size; + } + return rval; +} + +void +rpcUdpXactDestroy(RpcUdpXact xact) +{ +int i = xact->obuf.xid & XACT_HASH_MSK; + +#if (DEBUG) & DEBUG_TRACE_XACT + fprintf(stderr,"RPCIO: removing index %i, val %x\n",i,xact); +#endif + + ASSERT( xactHashTbl[i]==xact ); + + MU_LOCK(hlock); + xactHashTbl[i]=0; + /* remember XID we used last time so we can avoid + * reusing the same one (incremented by rpcUdpSend routine) + */ + xidUpper[i] = xact->obuf.xid & ~XACT_HASH_MSK; + MU_UNLOCK(hlock); + + bufFree(&xact->ibuf); + + XDR_DESTROY(&xact->xdrs); + MY_FREE(xact); +} + + + +/* Send a transaction, i.e. enqueue it to the + * RPC daemon who will actually send it. + */ +enum clnt_stat +rpcUdpSend( + RpcUdpXact xact, + RpcUdpServer srvr, + struct timeval *timeout, + u_long proc, + xdrproc_t xres, caddr_t pres, + xdrproc_t xargs, caddr_t pargs, + ... + ) +{ +register XDR *xdrs; +unsigned long ms; +va_list ap; + + va_start(ap,pargs); + + if (!timeout) + timeout = RPCIOD_DEFAULT_TIMEOUT; + + ms = 1000 * timeout->tv_sec + timeout->tv_usec/1000; + + /* round lifetime to closest # of ticks */ + xact->lifetime = (ms * ticksPerSec + 500) / 1000; + if ( 0 == xact->lifetime ) + xact->lifetime = 1; + +#if (DEBUG) & DEBUG_TIMEOUT + { + static int once=0; + if (!once++) { + fprintf(stderr, + "Initial lifetime: %i (ticks)\n", + xact->lifetime); + } + } +#endif + + xact->tolive = xact->lifetime; + + xact->xres = xres; + xact->pres = pres; + xact->server = srvr; + + xdrs = &xact->xdrs; + xdrs->x_op = XDR_ENCODE; + /* increment transaction ID */ + xact->obuf.xid += XACT_HASHS; + XDR_SETPOS(xdrs, xact->xdrpos); + if ( !XDR_PUTLONG(xdrs,(long*)&proc) || !locked_marshal(srvr, xdrs) || + !xargs(xdrs, pargs) ) { + va_end(ap); + return(xact->status.re_status=RPC_CANTENCODEARGS); + } + while ((xargs=va_arg(ap,xdrproc_t))) { + if (!xargs(xdrs, va_arg(ap,caddr_t))) + va_end(ap); + return(xact->status.re_status=RPC_CANTENCODEARGS); + } + + va_end(ap); + + rtems_task_ident(RTEMS_SELF, RTEMS_WHO_AM_I, &xact->requestor); + if ( rtems_message_queue_send( msgQ, &xact, sizeof(xact)) ) { + return RPC_CANTSEND; + } + /* wakeup the rpciod */ + ASSERT( RTEMS_SUCCESSFUL==rtems_event_send(rpciod, RPCIOD_TX_EVENT) ); + + return RPC_SUCCESS; +} + +/* Block for the RPC reply to an outstanding + * transaction. + * The caller is woken by the RPC daemon either + * upon reception of the reply or on timeout. + */ +enum clnt_stat +rpcUdpRcv(RpcUdpXact xact) +{ +int refresh; +XDR reply_xdrs; +struct rpc_msg reply_msg; +rtems_status_code status; +rtems_event_set gotEvents; + + refresh = 0; + + do { + + /* block for the reply */ + status = rtems_event_receive( + RTEMS_RPC_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &gotEvents); + ASSERT( status == RTEMS_SUCCESSFUL ); + + if (xact->status.re_status) { +#ifdef MBUF_RX + /* add paranoia */ + ASSERT( !xact->ibuf ); +#endif + return xact->status.re_status; + } + +#ifdef MBUF_RX + xdrmbuf_create(&reply_xdrs, xact->ibuf, XDR_DECODE); +#else + xdrmem_create(&reply_xdrs, xact->ibuf->buf, xact->ibufsize, XDR_DECODE); +#endif + + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = xact->pres; + reply_msg.acpted_rply.ar_results.proc = xact->xres; + + if (xdr_replymsg(&reply_xdrs, &reply_msg)) { + /* OK */ + _seterr_reply(&reply_msg, &xact->status); + if (RPC_SUCCESS == xact->status.re_status) { + if ( !locked_validate(xact->server, + &reply_msg.acpted_rply.ar_verf) ) { + xact->status.re_status = RPC_AUTHERROR; + xact->status.re_why = AUTH_INVALIDRESP; + } + if (reply_msg.acpted_rply.ar_verf.oa_base) { + reply_xdrs.x_op = XDR_FREE; + xdr_opaque_auth(&reply_xdrs, &reply_msg.acpted_rply.ar_verf); + } + refresh = 0; + } else { + /* should we try to refresh our credentials ? */ + if ( !refresh ) { + /* had never tried before */ + refresh = RPCIOD_REFRESH; + } + } + } else { + reply_xdrs.x_op = XDR_FREE; + xdr_replymsg(&reply_xdrs, &reply_msg); + xact->status.re_status = RPC_CANTDECODERES; + } + XDR_DESTROY(&reply_xdrs); + + bufFree(&xact->ibuf); + +#ifndef MBUF_RX + xact->ibufsize = 0; +#endif + + if (refresh && locked_refresh(xact->server)) { + rtems_task_ident(RTEMS_SELF, RTEMS_WHO_AM_I, &xact->requestor); + if ( rtems_message_queue_send(msgQ, &xact, sizeof(xact)) ) { + return RPC_CANTSEND; + } + /* wakeup the rpciod */ + fprintf(stderr,"RPCIO INFO: refreshing my AUTH\n"); + ASSERT( RTEMS_SUCCESSFUL==rtems_event_send(rpciod, RPCIOD_TX_EVENT) ); + } + + } while ( 0 && refresh-- > 0 ); + + return xact->status.re_status; +} + + +/* On RTEMS, I'm told to avoid select(); this seems to + * be more efficient + */ +static void +rxWakeupCB(struct socket *sock, void *arg) +{ + rtems_id *rpciod = (rtems_id*) arg; + rtems_event_send(*rpciod, RPCIOD_RX_EVENT); +} + +int +rpcUdpInit(void) +{ +int s; +rtems_status_code status; +int noblock = 1; +struct sockwakeup wkup; + + if (ourSock < 0) { + fprintf(stderr,"RTEMS-RPCIOD $Release$, " \ + "Till Straumann, Stanford/SLAC/SSRL 2002, " \ + "See LICENSE file for licensing info.\n"); + + ourSock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (ourSock>=0) { + bindresvport(ourSock,(struct sockaddr_in*)0); + s = ioctl(ourSock, FIONBIO, (char*)&noblock); + assert( s == 0 ); + /* assume nobody tampers with the clock !! */ + ticksPerSec = rtems_clock_get_ticks_per_second(); + MU_CREAT( &hlock ); + MU_CREAT( &llock ); + + if ( !rpciodPriority ) { + /* use configured networking priority */ + if ( ! (rpciodPriority = rtems_bsdnet_config.network_task_priority) ) + rpciodPriority = RPCIOD_PRIO; /* fallback value */ + } + + status = rtems_task_create( + rtems_build_name('R','P','C','d'), + rpciodPriority, + RPCIOD_STACK, + RTEMS_DEFAULT_MODES, + /* fprintf saves/restores FP registers on PPC :-( */ + RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, + &rpciod); + assert( status == RTEMS_SUCCESSFUL ); + + wkup.sw_pfn = rxWakeupCB; + wkup.sw_arg = &rpciod; + assert( 0==setsockopt(ourSock, SOL_SOCKET, SO_RCVWAKEUP, &wkup, sizeof(wkup)) ); + status = rtems_message_queue_create( + rtems_build_name('R','P','C','q'), + RPCIOD_QDEPTH, + sizeof(RpcUdpXact), + RTEMS_DEFAULT_ATTRIBUTES, + &msgQ); + assert( status == RTEMS_SUCCESSFUL ); + status = rtems_task_start( rpciod, rpcio_daemon, 0 ); + assert( status == RTEMS_SUCCESSFUL ); + + } else { + return -1; + } + } + return 0; +} + +int +rpcUdpCleanup(void) +{ + rtems_semaphore_create( + rtems_build_name('R','P','C','f'), + 0, + RTEMS_DEFAULT_ATTRIBUTES, + 0, + &fini); + rtems_event_send(rpciod, RPCIOD_KILL_EVENT); + /* synchronize with daemon */ + rtems_semaphore_obtain(fini, RTEMS_WAIT, 5*ticksPerSec); + /* if the message queue is still there, something went wrong */ + if (!msgQ) { + rtems_task_delete(rpciod); + } + rtems_semaphore_delete(fini); + return (msgQ !=0); +} + +/* Another API - simpler but less efficient. + * For each RPCall, a server and a Xact + * are created and destroyed on the fly. + * + * This should be used for infrequent calls + * (e.g. a NFS mount request). + * + * This is roughly compatible with the original + * clnt_call() etc. API - but it uses our + * daemon and is fully reentrant. + */ +enum clnt_stat +rpcUdpClntCreate( + struct sockaddr_in *psaddr, + rpcprog_t prog, + rpcvers_t vers, + u_long uid, + u_long gid, + RpcUdpClnt *pclnt +) +{ +RpcUdpXact x; +RpcUdpServer s; +enum clnt_stat err; + + if ( RPC_SUCCESS != (err=rpcUdpServerCreate(psaddr, prog, vers, uid, gid, &s)) ) + return err; + + if ( !(x=rpcUdpXactCreate(prog, vers, UDPMSGSIZE)) ) { + rpcUdpServerDestroy(s); + return RPC_FAILED; + } + /* TODO: could maintain a server cache */ + + x->server = s; + + *pclnt = x; + + return RPC_SUCCESS; +} + +void +rpcUdpClntDestroy(RpcUdpClnt xact) +{ + rpcUdpServerDestroy(xact->server); + rpcUdpXactDestroy(xact); +} + +enum clnt_stat +rpcUdpClntCall( + RpcUdpClnt xact, + u_long proc, + XdrProcT xargs, + CaddrT pargs, + XdrProcT xres, + CaddrT pres, + struct timeval *timeout + ) +{ +enum clnt_stat stat; + + if ( (stat = rpcUdpSend(xact, xact->server, timeout, proc, + xres, pres, + xargs, pargs, + 0)) ) { + fprintf(stderr,"RPCIO Send failed: %i\n",stat); + return stat; + } + return rpcUdpRcv(xact); +} + +/* a yet simpler interface */ +enum clnt_stat +rpcUdpCallRp( + struct sockaddr_in *psrvr, + u_long prog, + u_long vers, + u_long proc, + XdrProcT xargs, + CaddrT pargs, + XdrProcT xres, + CaddrT pres, + u_long uid, /* RPCIO_DEFAULT_ID picks default */ + u_long gid, /* RPCIO_DEFAULT_ID picks default */ + struct timeval *timeout /* NULL picks default */ +) +{ +RpcUdpClnt clp; +enum clnt_stat stat; + + stat = rpcUdpClntCreate( + psrvr, + prog, + vers, + uid, + gid, + &clp); + + if ( RPC_SUCCESS != stat ) + return stat; + + stat = rpcUdpClntCall( + clp, + proc, + xargs, pargs, + xres, pres, + timeout); + + rpcUdpClntDestroy(clp); + + return stat; +} + +/* linked list primitives */ +static void +nodeXtract(ListNode n) +{ + if (n->prev) + n->prev->next = n->next; + if (n->next) + n->next->prev = n->prev; + n->next = n->prev = 0; +} + +static void +nodeAppend(ListNode l, ListNode n) +{ + if ( (n->next = l->next) ) + n->next->prev = n; + l->next = n; + n->prev = l; + +} + +/* this code does the work */ +static void +rpcio_daemon(rtems_task_argument arg) +{ +rtems_status_code stat; +RpcUdpXact xact; +RpcUdpServer srv; +rtems_interval next_retrans, then, unow; +long now; /* need to do signed comparison with age! */ +rtems_event_set events; +ListNode newList; +size_t size; +rtems_id q = 0; +ListNodeRec listHead = {0, 0}; +unsigned long epoch = RPCIOD_EPOCH_SECS * ticksPerSec; +unsigned long max_period = RPCIOD_RETX_CAP_S * ticksPerSec; +rtems_status_code status; + + + then = rtems_clock_get_ticks_since_boot(); + + for (next_retrans = epoch;;) { + + if ( RTEMS_SUCCESSFUL != + (stat = rtems_event_receive( + RPCIOD_RX_EVENT | RPCIOD_TX_EVENT | RPCIOD_KILL_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + next_retrans, + &events)) ) { + ASSERT( RTEMS_TIMEOUT == stat ); + events = 0; + } + + if (events & RPCIOD_KILL_EVENT) { + int i; + +#if (DEBUG) & DEBUG_EVENTS + fprintf(stderr,"RPCIO: got KILL event\n"); +#endif + + MU_LOCK(hlock); + for (i=XACT_HASHS-1; i>=0; i--) { + if (xactHashTbl[i]) { + break; + } + } + if (i<0) { + /* prevent them from creating and enqueueing more messages */ + q=msgQ; + /* messages queued after we executed this assignment will fail */ + msgQ=0; + } + MU_UNLOCK(hlock); + if (i>=0) { + fprintf(stderr,"RPCIO There are still transactions circulating; I refuse to go away\n"); + fprintf(stderr,"(1st in slot %i)\n",i); + rtems_semaphore_release(fini); + } else { + break; + } + } + + unow = rtems_clock_get_ticks_since_boot(); + + /* measure everything relative to then to protect against + * rollover + */ + now = unow - then; + + /* NOTE: we don't lock the hash table while we are operating + * on transactions; the paradigm is that we 'own' a particular + * transaction (and hence it's hash table slot) from the + * time the xact was put into the message queue until we + * wake up the requestor. + */ + + if (RPCIOD_RX_EVENT & events) { + +#if (DEBUG) & DEBUG_EVENTS + fprintf(stderr,"RPCIO: got RX event\n"); +#endif + + while ((xact=sockRcv())) { + + /* extract from the retransmission list */ + nodeXtract(&xact->node); + + /* change the ID - there might already be + * a retransmission on the way. When it's + * reply arrives we must not find it's ID + * in the hashtable + */ + xact->obuf.xid += XACT_HASHS; + + xact->status.re_status = RPC_SUCCESS; + + /* calculate roundtrip ticks */ + xact->trip = now - xact->trip; + + srv = xact->server; + + /* adjust the server's retry period */ + { + register TimeoutT rtry = srv->retry_period; + register TimeoutT trip = xact->trip; + + ASSERT( trip >= 0 ); + + if ( 0==trip ) + trip = 1; + + /* retry_new = 0.75*retry_old + 0.25 * 8 * roundrip */ + rtry = (3*rtry + (trip << 3)) >> 2; + + if ( rtry > max_period ) + rtry = max_period; + + srv->retry_period = rtry; + } + + /* wakeup requestor */ + rtems_event_send(xact->requestor, RTEMS_RPC_EVENT); + } + } + + if (RPCIOD_TX_EVENT & events) { + +#if (DEBUG) & DEBUG_EVENTS + fprintf(stderr,"RPCIO: got TX event\n"); +#endif + + while (RTEMS_SUCCESSFUL == rtems_message_queue_receive( + msgQ, + &xact, + &size, + RTEMS_NO_WAIT, + RTEMS_NO_TIMEOUT)) { + /* put to the head of timeout q */ + nodeAppend(&listHead, &xact->node); + + xact->age = now; + xact->trip = FIRST_ATTEMPT; + } + } + + + /* work the timeout q */ + newList = 0; + for ( xact=(RpcUdpXact)listHead.next; + xact && xact->age <= now; + xact=(RpcUdpXact)listHead.next ) { + + /* extract from the list */ + nodeXtract(&xact->node); + + srv = xact->server; + + if (xact->tolive < 0) { + /* this one timed out */ + xact->status.re_errno = ETIMEDOUT; + xact->status.re_status = RPC_TIMEDOUT; + + srv->timeouts++; + + /* Change the ID - there might still be + * a reply on the way. When it arrives we + * must not find it's ID in the hash table + * + * Thanks to Steven Johnson for hunting this + * one down. + */ + xact->obuf.xid += XACT_HASHS; + +#if (DEBUG) & DEBUG_TIMEOUT + fprintf(stderr,"RPCIO XACT timed out; waking up requestor\n"); +#endif + if ( rtems_event_send(xact->requestor, RTEMS_RPC_EVENT) ) { + rtems_panic("RPCIO PANIC file %s line: %i, requestor id was 0x%08x", + __FILE__, + __LINE__, + xact->requestor); + } + + } else { + int len; + + len = (int)XDR_GETPOS(&xact->xdrs); + +#ifdef MBUF_TX + xact->refcnt = 1; /* sendto itself */ +#endif + if ( len != SENDTO( ourSock, + xact->obuf.buf, + len, + 0, + &srv->addr.sa, + sizeof(srv->addr.sin) +#ifdef MBUF_TX + , xact, + paranoia_free, + paranoia_ref +#endif + ) ) { + + xact->status.re_errno = errno; + xact->status.re_status = RPC_CANTSEND; + srv->errors++; + + /* wakeup requestor */ + fprintf(stderr,"RPCIO: SEND failure\n"); + status = rtems_event_send(xact->requestor, RTEMS_RPC_EVENT); + assert( status == RTEMS_SUCCESSFUL ); + + } else { + /* send successful; calculate retransmission time + * and enqueue to temporary list + */ + if (FIRST_ATTEMPT != xact->trip) { +#if (DEBUG) & DEBUG_TIMEOUT + fprintf(stderr, + "timed out; tolive is %i (ticks), retry period is %i (ticks)\n", + xact->tolive, + srv->retry_period); +#endif + /* this is a real retry; we backup + * the server's retry interval + */ + if ( srv->retry_period < max_period ) { + + /* If multiple transactions for this server + * fail (e.g. because it died) this will + * back-off very agressively (doubling + * the retransmission period for every + * timed out transaction up to the CAP limit) + * which is desirable - single packet failure + * is treated more gracefully by this algorithm. + */ + + srv->retry_period<<=1; +#if (DEBUG) & DEBUG_TIMEOUT + fprintf(stderr, + "adjusted to; retry period %i\n", + srv->retry_period); +#endif + } else { + /* never wait longer than RPCIOD_RETX_CAP_S seconds */ + fprintf(stderr, + "RPCIO: server '%s' not responding - still trying\n", + srv->name); + } + if ( 0 == ++srv->retrans % 1000) { + fprintf(stderr, + "RPCIO - statistics: already %li retries to server %s\n", + srv->retrans, + srv->name); + } + } else { + srv->requests++; + } + xact->trip = now; + { + long capped_period = srv->retry_period; + if ( xact->lifetime < capped_period ) + capped_period = xact->lifetime; + xact->age = now + capped_period; + xact->tolive -= capped_period; + } + /* enqueue to the list of newly sent transactions */ + xact->node.next = newList; + newList = &xact->node; +#if (DEBUG) & DEBUG_TIMEOUT + fprintf(stderr, + "XACT (0x%08x) age is 0x%x, now: 0x%x\n", + xact, + xact->age, + now); +#endif + } + } + } + + /* insert the newly sent transactions into the + * sorted retransmission list + */ + for (; (xact = (RpcUdpXact)newList); ) { + register ListNode p,n; + newList = newList->next; + for ( p=&listHead; (n=p->next) && xact->age > ((RpcUdpXact)n)->age; p=n ) + /* nothing else to do */; + nodeAppend(p, &xact->node); + } + + if (now > epoch) { + /* every now and then, readjust the epoch */ + register ListNode n; + then += now; + for (n=listHead.next; n; n=n->next) { + /* readjust outstanding time intervals subject to the + * condition that the 'absolute' time must remain + * the same. 'age' and 'trip' are measured with + * respect to 'then' - hence: + * + * abs_age == old_age + old_then == new_age + new_then + * + * ==> new_age = old_age + old_then - new_then == old_age - 'now' + */ + ((RpcUdpXact)n)->age -= now; + ((RpcUdpXact)n)->trip -= now; +#if (DEBUG) & DEBUG_TIMEOUT + fprintf(stderr, + "readjusted XACT (0x%08x); age is 0x%x, trip: 0x%x now: 0x%x\n", + (RpcUdpXact)n, + ((RpcUdpXact)n)->trip, + ((RpcUdpXact)n)->age, + now); +#endif + } + now = 0; + } + + next_retrans = listHead.next ? + ((RpcUdpXact)listHead.next)->age - now : + epoch; /* make sure we don't miss updating the epoch */ +#if (DEBUG) & DEBUG_TIMEOUT + fprintf(stderr,"RPCIO: next timeout is %x\n",next_retrans); +#endif + } + /* close our socket; shut down the receiver */ + close(ourSock); + +#if 0 /* if we get here, no transactions exist, hence there can be none + * in the queue whatsoever + */ + /* flush the message queue */ + while (RTEMS_SUCCESSFUL == rtems_message_queue_receive( + q, + &xact, + &size, + RTEMS_NO_WAIT, + RTEMS_NO_TIMEOUT)) { + /* TODO enque xact */ + } + + /* flush all outstanding transactions */ + + for (xact=((RpcUdpXact)listHead.next); xact; xact=((RpcUdpXact)xact->node.next)) { + xact->status.re_status = RPC_TIMEDOUT; + rtems_event_send(xact->requestor, RTEMS_RPC_EVENT); + } +#endif + + rtems_message_queue_delete(q); + + MU_DESTROY(hlock); + + fprintf(stderr,"RPC daemon exited...\n"); + + rtems_semaphore_release(fini); + rtems_task_suspend(RTEMS_SELF); +} + + +/* support for transaction 'pools'. A number of XACT objects + * is always kept around. The initial number is 0 but it + * is allowed to grow up to a maximum. + * If the need grows beyond the maximum, behavior depends: + * Users can either block until a transaction becomes available, + * they can create a new XACT on the fly or get an error + * if no free XACT is available from the pool. + */ + +RpcUdpXactPool +rpcUdpXactPoolCreate( + rpcprog_t prog, rpcvers_t version, + int xactsize, int poolsize) +{ +RpcUdpXactPool rval = MY_MALLOC(sizeof(*rval)); +rtems_status_code status; + + ASSERT( rval ); + status = rtems_message_queue_create( + rtems_build_name('R','P','C','p'), + poolsize, + sizeof(RpcUdpXact), + RTEMS_DEFAULT_ATTRIBUTES, + &rval->box); + assert( status == RTEMS_SUCCESSFUL ); + + rval->prog = prog; + rval->version = version; + rval->xactSize = xactsize; + return rval; +} + +void +rpcUdpXactPoolDestroy(RpcUdpXactPool pool) +{ +RpcUdpXact xact; + + while ((xact = rpcUdpXactPoolGet(pool, XactGetFail))) { + rpcUdpXactDestroy(xact); + } + rtems_message_queue_delete(pool->box); + MY_FREE(pool); +} + +RpcUdpXact +rpcUdpXactPoolGet(RpcUdpXactPool pool, XactPoolGetMode mode) +{ +RpcUdpXact xact = 0; +size_t size; + + if (RTEMS_SUCCESSFUL != rtems_message_queue_receive( + pool->box, + &xact, + &size, + XactGetWait == mode ? + RTEMS_WAIT : RTEMS_NO_WAIT, + RTEMS_NO_TIMEOUT)) { + + /* nothing found in box; should we create a new one ? */ + + xact = (XactGetCreate == mode) ? + rpcUdpXactCreate( + pool->prog, + pool->version, + pool->xactSize) : 0 ; + if (xact) + xact->pool = pool; + + } + return xact; +} + +void +rpcUdpXactPoolPut(RpcUdpXact xact) +{ +RpcUdpXactPool pool; + + pool = xact->pool; + ASSERT( pool ); + + if (RTEMS_SUCCESSFUL != rtems_message_queue_send( + pool->box, + &xact, + sizeof(xact))) + rpcUdpXactDestroy(xact); +} + +#ifdef MBUF_RX + +/* WORKAROUND: include sys/mbuf.h (or other bsdnet headers) only + * _after_ using malloc()/free() & friends because + * the RTEMS/BSDNET headers redefine those :-( + */ + +#define _KERNEL +#include <sys/mbuf.h> + +ssize_t +recv_mbuf_from(int s, struct mbuf **ppm, long len, struct sockaddr *fromaddr, int *fromlen); + +static void +bufFree(struct mbuf **m) +{ + if (*m) { + rtems_bsdnet_semaphore_obtain(); + m_freem(*m); + rtems_bsdnet_semaphore_release(); + *m = 0; + } +} +#endif + +#ifdef MBUF_TX +static void +paranoia_free(caddr_t closure, u_int size) +{ +#if (DEBUG) +RpcUdpXact xact = (RpcUdpXact)closure; +int len = (int)XDR_GETPOS(&xact->xdrs); + + ASSERT( --xact->refcnt >= 0 && size == len ); +#endif +} + +static void +paranoia_ref (caddr_t closure, u_int size) +{ +#if (DEBUG) +RpcUdpXact xact = (RpcUdpXact)closure; +int len = (int)XDR_GETPOS(&xact->xdrs); + ASSERT( size == len ); + xact->refcnt++; +#endif +} +#endif + +/* receive from a socket and find + * the transaction corresponding to the + * transaction ID received in the server + * reply. + * + * The semantics of the 'pibuf' pointer are + * as follows: + * + * MBUF_RX: + * + */ + +#define RPCIOD_RXBUFSZ UDPMSGSIZE + +static RpcUdpXact +sockRcv(void) +{ +int len,i; +uint32_t xid; +union { + struct sockaddr_in sin; + struct sockaddr sa; +} fromAddr; +int fromLen = sizeof(fromAddr.sin); +RxBuf ibuf = 0; +RpcUdpXact xact = 0; + + do { + + /* rcv_mbuf() and recvfrom() differ in that the + * former allocates buffers and passes them back + * to us whereas the latter requires us to provide + * buffer space. + * Hence, in the first case whe have to make sure + * no old buffer is leaked - in the second case, + * we might well re-use an old buffer but must + * make sure we have one allocated + */ +#ifdef MBUF_RX + if (ibuf) + bufFree(&ibuf); + + len = recv_mbuf_from( + ourSock, + &ibuf, + RPCIOD_RXBUFSZ, + &fromAddr.sa, + &fromLen); +#else + if ( !ibuf ) + ibuf = (RpcBuf)MY_MALLOC(RPCIOD_RXBUFSZ); + if ( !ibuf ) + goto cleanup; /* no memory - drop this message */ + + len = recvfrom(ourSock, + ibuf->buf, + RPCIOD_RXBUFSZ, + 0, + &fromAddr.sa, + &fromLen); +#endif + + if (len <= 0) { + if (EAGAIN != errno) + fprintf(stderr,"RECV failed: %s\n",strerror(errno)); + goto cleanup; + } + +#if (DEBUG) & DEBUG_PACKLOSS + if ( (unsigned)rand() < DEBUG_PACKLOSS_FRACT ) { + /* lose packets once in a while */ + static int xxx = 0; + if ( ++xxx % 16 == 0 ) + fprintf(stderr,"DEBUG: dropped %i packets, so far...\n",xxx); + if ( ibuf ) + bufFree( &ibuf ); + continue; + } +#endif + + i = (xid=XID(ibuf)) & XACT_HASH_MSK; + + if ( !(xact=xactHashTbl[i]) || + xact->obuf.xid != xid || +#ifdef REJECT_SERVERIP_MISMATCH + xact->server->addr.sin.sin_addr.s_addr != fromAddr.sin.sin_addr.s_addr || +#endif + xact->server->addr.sin.sin_port != fromAddr.sin.sin_port ) { + + if (xact) { + if ( +#ifdef REJECT_SERVERIP_MISMATCH + xact->server->addr.sin.sin_addr.s_addr == fromAddr.sin.sin_addr.s_addr && +#endif + xact->server->addr.sin.sin_port == fromAddr.sin.sin_port && + ( xact->obuf.xid == xid + XACT_HASHS || + xact->obuf.xid == xid + 2*XACT_HASHS ) + ) { +#ifndef DEBUG /* don't complain if it's just a late arrival of a retry */ + fprintf(stderr,"RPCIO - FYI sockRcv(): dropping late/redundant retry answer\n"); +#endif + } else { + fprintf(stderr,"RPCIO WARNING sockRcv(): transaction mismatch\n"); + fprintf(stderr,"xact: xid 0x%08" PRIx32 " -- got 0x%08" PRIx32 "\n", + xact->obuf.xid, xid); + fprintf(stderr,"xact: addr 0x%08" PRIx32 " -- got 0x%08" PRIx32 "\n", + xact->server->addr.sin.sin_addr.s_addr, + fromAddr.sin.sin_addr.s_addr); + fprintf(stderr,"xact: port 0x%08x -- got 0x%08x\n", + xact->server->addr.sin.sin_port, + fromAddr.sin.sin_port); + } + } else { + fprintf(stderr, + "RPCIO WARNING sockRcv(): got xid 0x%08" PRIx32 " but its slot is empty\n", + xid); + } + /* forget about this one and try again */ + xact = 0; + } + + } while ( !xact ); + + xact->ibuf = ibuf; +#ifndef MBUF_RX + xact->ibufsize = RPCIOD_RXBUFSZ; +#endif + + return xact; + +cleanup: + + bufFree(&ibuf); + + return 0; +} + + +#include <rtems/rtems_bsdnet_internal.h> +/* double check the event configuration; should probably globally + * manage system events!! + * We do this at the end of the file for the same reason we had + * included mbuf.h only a couple of lines above - see comment up + * there... + */ +#if RTEMS_RPC_EVENT & SOSLEEP_EVENT & SBWAIT_EVENT & NETISR_EVENTS +#error ILLEGAL EVENT CONFIGURATION +#endif diff --git a/cpukit/libfs/src/nfsclient/src/rpcio.h b/cpukit/libfs/src/nfsclient/src/rpcio.h new file mode 100644 index 0000000000..0d65b76dc1 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/rpcio.h @@ -0,0 +1,209 @@ +#ifndef RPCIO_H +#define RPCIO_H +/* $Id$ */ + +/* A multihreaded RPC/UDP multiplexor */ + +/* Author: Till Straumann, <strauman@slac.stanford.edu>, 2002 */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#ifdef __rtems +#include <rtems.h> +#endif + +#include <rpc/rpc.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <stdarg.h> + +typedef struct RpcUdpServerRec_ *RpcUdpServer; +typedef struct RpcUdpXactRec_ *RpcUdpXact; + +typedef RpcUdpXact RpcUdpClnt; + +#define RPCIOD_DEFAULT_ID 0xdef10000 + +int +rpcUdpInit(void); + +enum clnt_stat +rpcUdpServerCreate( + struct sockaddr_in *paddr, + rpcprog_t prog, + rpcvers_t vers, + u_long uid, /* RPCIO_DEFAULT_ID picks default */ + u_long gid, /* RPCIO_DEFAULT_ID picks default */ + RpcUdpServer *pclnt /* new server is returned here */ + ); + + +void +rpcUdpServerDestroy(RpcUdpServer s); + +/* Dump statistics to a file (stdout if NULL); + * returns 0 for convenience + */ +int +rpcUdpStats(FILE *f); + +enum clnt_stat +rpcUdpClntCreate( + struct sockaddr_in *psaddr, + rpcprog_t prog, + rpcvers_t vers, + u_long uid, /* RPCIO_DEFAULT_ID picks default */ + u_long gid, /* RPCIO_DEFAULT_ID picks default */ + RpcUdpClnt *pclnt /* new client is returned here */ + ); + +void +RpcUdpClntDestroy(RpcUdpClnt clnt); + +/* mute compiler warnings */ +typedef void *XdrProcT; +typedef void *CaddrT; + +enum clnt_stat +rpcUdpClntCall( + RpcUdpClnt clnt, + u_long proc, + XdrProcT xargs, + CaddrT pargs, + XdrProcT xres, + CaddrT pres, + struct timeval *timeout /* optional timeout; maybe NULL to pick default */ + ); + +RpcUdpXact +rpcUdpXactCreate( + u_long program, + u_long version, + u_long size + ); + +void +rpcUdpXactDestroy( + RpcUdpXact xact + ); + +/* send a transaction */ +enum clnt_stat +rpcUdpSend( + RpcUdpXact xact, + RpcUdpServer srvr, + struct timeval *timeout, /* maybe NULL to pick default */ + u_long proc, + xdrproc_t xres, + caddr_t pres, + xdrproc_t xargs, + caddr_t pargs, + ... /* 0 terminated xdrproc/pobj additional argument list */ + ); + +/* wait for a transaction to complete */ +enum clnt_stat +rpcUdpRcv(RpcUdpXact xact); + +/* a yet simpler interface */ +enum clnt_stat +rpcUdpCallRp( + struct sockaddr_in *pserver_addr, + u_long prog, + u_long vers, + u_long proc, + XdrProcT xargs, + CaddrT pargs, + XdrProcT xres, + CaddrT pres, + u_long uid, /* RPCIO_DEFAULT_ID picks default */ + u_long gid, /* RPCIO_DEFAULT_ID picks default */ + struct timeval *timeout /* NULL picks default */ +); + + +/* manage pools of transactions */ + +/* A pool of transactions. The idea is not to malloc/free them + * all the time but keep a limited number around in a 'pool'. + * Users who need a XACT may get it from the pool and put it back + * when done. + * The pool is implemented by RTEMS message queues who manage + * the required task synchronization. + * A requestor has different options if the pool is empty: + * - it can wait (block) for a XACT to become available + * - it can get an error status + * - or it can malloc an extra XACT from the heap which + * will eventually be released. + */ + +typedef struct RpcUdpXactPoolRec_ *RpcUdpXactPool; + +/* NOTE: the pool is empty initially, must get messages (in + * GetCreate mode + */ +RpcUdpXactPool +rpcUdpXactPoolCreate( + rpcprog_t prog, rpcvers_t version, + int xactsize, int poolsize); + +void +rpcUdpXactPoolDestroy(RpcUdpXactPool pool); + +typedef enum { + XactGetFail, /* call fails if no transaction available */ + XactGetWait, /* call blocks until transaction available */ + XactGetCreate /* a new transaction is allocated (and freed when put back to the pool */ +} XactPoolGetMode; + +RpcUdpXact +rpcUdpXactPoolGet(RpcUdpXactPool pool, XactPoolGetMode mode); + +void +rpcUdpXactPoolPut(RpcUdpXact xact); + +#endif diff --git a/cpukit/libfs/src/nfsclient/src/rpcio.modini.c b/cpukit/libfs/src/nfsclient/src/rpcio.modini.c new file mode 100644 index 0000000000..7aa802fe51 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/rpcio.modini.c @@ -0,0 +1,19 @@ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "librtemsNfs.h" +/* CEXP module support (magic init) */ +void +_cexpModuleInitialize(void *mod) +{ + rpcUdpInit(); +} + +int +_cexpModuleFinalize(void *mod) +{ + return rpcUdpCleanup(); +} + + diff --git a/cpukit/libfs/src/nfsclient/src/sock_mbuf.c b/cpukit/libfs/src/nfsclient/src/sock_mbuf.c new file mode 100644 index 0000000000..3c589d9e81 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/sock_mbuf.c @@ -0,0 +1,283 @@ +/* + * $Id$ + * + * NOTE: + * This is derived from libnetworking/rtems/rtems_syscall.c + * + * RTEMS/libnetworking LICENSING restrictions may apply + * + * Author (modifications only): + * Copyright: 2002, Stanford University and + * Till Straumann, <strauman@slac.stanford.edu> + * Licensing: 'LICENSE.NET' file in the RTEMS top source directory + * for more information. + */ + +/* +The RTEMS TCP/IP stack is a port of the FreeBSD TCP/IP stack. The following +copyright and licensing information applies to this code. + +This code is found under the c/src/libnetworking directory but does not +constitute the entire contents of that subdirectory. + +============================================================================= + +Copyright (c) 1980, 1983, 1988, 1993 + The Regents of the University of California. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by the University of + California, Berkeley and its contributors. +4. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +- +Portions Copyright (c) 1993 by Digital Equipment Corporation. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies, and that +the name of Digital Equipment Corporation not be used in advertising or +publicity pertaining to distribution of the document or software without +specific, written prior permission. + +THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT +CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +============================================================================= +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/error.h> + +#define _KERNEL +#define __BSD_VISIBLE 1 +#include <rtems/rtems_bsdnet.h> + +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/protosw.h> +#include <sys/proc.h> +#include <sys/fcntl.h> +#include <sys/filio.h> + +#include <net/if.h> +#include <net/route.h> + +struct socket *rtems_bsdnet_fdToSocket(int fd); + +/* + * Package system call argument into mbuf. + * + * (unfortunately, the original is not public) + */ +static int +sockaddrtombuf (struct mbuf **mp, const struct sockaddr *buf, int buflen) +{ +struct mbuf *m; +struct sockaddr *sa; + + if ((u_int)buflen > MLEN) + return (EINVAL); + + rtems_bsdnet_semaphore_obtain(); + m = m_get(M_WAIT, MT_SONAME); + rtems_bsdnet_semaphore_release(); + + if (m == NULL) + return (ENOBUFS); + m->m_len = buflen; + memcpy (mtod(m, caddr_t), buf, buflen); + *mp = m; + sa = mtod(m, struct sockaddr *); + sa->sa_len = buflen; + + return 0; +} + +static void +dummyproc(caddr_t ext_buf, u_int ext_size) +{ +} + +/* + * send data by simply allocating an MBUF packet + * header and pointing it to our data region. + * + * Optionally, the caller may supply 'reference' + * and 'free' procs. (The latter may call the + * user back once the networking stack has + * released the buffer). + * + * The callbacks are provided with the 'closure' + * pointer and the 'buflen' argument. + */ +ssize_t +sendto_nocpy ( + int s, + const void *buf, size_t buflen, + int flags, + const struct sockaddr *toaddr, int tolen, + void *closure, + void (*freeproc)(caddr_t, u_int), + void (*refproc)(caddr_t, u_int) +) +{ + int error; + struct socket *so; + struct mbuf *to, *m; + int ret = -1; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) { + rtems_bsdnet_semaphore_release (); + return -1; + } + + error = sockaddrtombuf (&to, toaddr, tolen); + if (error) { + errno = error; + rtems_bsdnet_semaphore_release (); + return -1; + } + + MGETHDR(m, M_WAIT, MT_DATA); + m->m_pkthdr.len = 0; + m->m_pkthdr.rcvif = (struct ifnet *) 0; + + m->m_flags |= M_EXT; + m->m_ext.ext_buf = closure ? closure : (void*)buf; + m->m_ext.ext_size = buflen; + /* we _must_ supply non-null procs; otherwise, + * the kernel code assumes it's a mbuf cluster + */ + m->m_ext.ext_free = freeproc ? freeproc : dummyproc; + m->m_ext.ext_ref = refproc ? refproc : dummyproc; + m->m_pkthdr.len += buflen; + m->m_len = buflen; + m->m_data = (void*)buf; + + error = sosend (so, to, NULL, m, NULL, flags); + if (error) { + if (/*auio.uio_resid != len &&*/ (error == EINTR || error == EWOULDBLOCK)) + error = 0; + } + if (error) + errno = error; + else + ret = buflen; + if (to) + m_freem(to); + rtems_bsdnet_semaphore_release (); + return (ret); +} + + +/* + * receive data in an 'mbuf chain'. + * The chain must be released once the + * data has been extracted: + * + * rtems_bsdnet_semaphore_obtain(); + * m_freem(chain); + * rtems_bsdnet_semaphore_release(); + */ +ssize_t +recv_mbuf_from(int s, struct mbuf **ppm, long len, struct sockaddr *fromaddr, int *fromlen) +{ + int ret = -1; + int error; + struct uio auio; + struct socket *so; + struct mbuf *from = NULL; + + memset(&auio, 0, sizeof(auio)); + *ppm = 0; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) { + rtems_bsdnet_semaphore_release (); + return -1; + } +/* auio.uio_iov = mp->msg_iov; + auio.uio_iovcnt = mp->msg_iovlen; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_rw = UIO_READ; + auio.uio_offset = 0; +*/ + auio.uio_resid = len; + error = soreceive (so, &from, &auio, (struct mbuf **) ppm, + (struct mbuf **)NULL, + NULL); + if (error) { + if (auio.uio_resid != len && (error == EINTR || error == EWOULDBLOCK)) + error = 0; + } + if (error) { + errno = error; + } + else { + ret = len - auio.uio_resid; + if (fromaddr) { + len = *fromlen; + if ((len <= 0) || (from == NULL)) { + len = 0; + } + else { + if (len > from->m_len) + len = from->m_len; + memcpy (fromaddr, mtod(from, caddr_t), len); + } + *fromlen = len; + } + } + if (from) + m_freem (from); + if (error && *ppm) { + m_freem(*ppm); + *ppm = 0; + } + rtems_bsdnet_semaphore_release (); + return (ret); +} diff --git a/cpukit/libfs/src/nfsclient/src/xdr_mbuf.c b/cpukit/libfs/src/nfsclient/src/xdr_mbuf.c new file mode 100644 index 0000000000..4294d6dcd2 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/xdr_mbuf.c @@ -0,0 +1,539 @@ +/* $Id$ */ + +/* xdr_mbuf is derived from xdr_mem */ + +/* Author (mbuf specifica): Till Straumann <strauman@slac.stanford.edu>, 10/2002 */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)xdr_mem.c 1.19 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)xdr_mem.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$FreeBSD: src/lib/libc/xdr/xdr_mem.c,v 1.8 1999/08/28 00:02:56 peter Exp $"; +#endif + +/* + * xdr_mbuf, XDR implementation using mbuf buffers + * + * derived from: + * + * xdr_mem.h, XDR implementation using memory buffers. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * The MBUF stream is useful for BSDNET kernel (or RTEMS for that matter) + * use. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <rpc/types.h> +#include <rpc/xdr.h> +#include <netinet/in.h> + +#include <stdlib.h> + +#define TODO + +/* TODO remove: a hack because malloc is redefined */ +#ifdef TODO +static inline void * +my_malloc(size_t i) +{ + return malloc(i); +} + +static inline void +my_free(void *p) +{ + return free(p); +} +#endif + +#define DEBUG_ASSERT (1<<0) +#define DEBUG_VERB (1<<1) + +#define DEBUG DEBUG_ASSERT + +#define _KERNEL +#include <sys/mbuf.h> + +#include <assert.h> + +#if DEBUG & DEBUG_VERB || defined(TODO) +#include <stdio.h> +#endif + +static bool_t xdrmbuf_getlong_aligned(XDR *xdrs, long *lp); +static bool_t xdrmbuf_putlong_aligned(XDR *xdrs, const long *lp); +static bool_t xdrmbuf_getlong_unaligned(XDR *xdrs, long *lp); +static bool_t xdrmbuf_putlong_unaligned(XDR *xdrs, const long *lp); +static bool_t xdrmbuf_getbytes(XDR *xdrs, caddr_t addr, u_int len); +static bool_t xdrmbuf_putbytes(XDR *xdrs, const char *addr, u_int len); +static u_int xdrmbuf_getpos(XDR *xdrs); /* XXX w/64-bit pointers, u_int not enough! */ +static bool_t xdrmbuf_setpos(XDR *xdrs, u_int pos); +static int32_t *xdrmbuf_inline_aligned(XDR *xdrs, u_int len); +static int32_t *xdrmbuf_inline_unaligned(XDR *xdrs, u_int len); +static void xdrmbuf_destroy(XDR *); + +static struct xdr_ops xdrmbuf_ops_aligned = { + xdrmbuf_getlong_aligned, + xdrmbuf_putlong_aligned, + xdrmbuf_getbytes, + xdrmbuf_putbytes, + xdrmbuf_getpos, + xdrmbuf_setpos, + xdrmbuf_inline_aligned, + xdrmbuf_destroy +}; + +static struct xdr_ops xdrmbuf_ops_unaligned = { + xdrmbuf_getlong_unaligned, + xdrmbuf_putlong_unaligned, + xdrmbuf_getbytes, + xdrmbuf_putbytes, + xdrmbuf_getpos, + xdrmbuf_setpos, + xdrmbuf_inline_unaligned, + xdrmbuf_destroy +}; + +typedef struct MBPrivateRec_ { + struct mbuf *mchain; + struct mbuf *mcurrent; + u_int pos; /* number of bytes contained in all MUBFS ahead + * of mcurrent + */ +} MBPrivateRec, *MBPrivate; + +/* NOTE: the stream position helper 'pos' + * must be managed by the caller! + */ +static inline void +xdrmbuf_setup(XDR *xdrs, struct mbuf *m) +{ +MBPrivate mbp = (MBPrivate)xdrs->x_base; + + mbp->mcurrent = m; + xdrs->x_private = mtod(m,caddr_t); + xdrs->x_handy = m->m_len; + xdrs->x_ops = ((uintptr_t)xdrs->x_private & (sizeof(int32_t) - 1)) + ? &xdrmbuf_ops_unaligned : &xdrmbuf_ops_aligned; +} + +static struct mbuf * +xdrmbuf_next(XDR *xdrs) +{ +struct mbuf *rval; +MBPrivate mbp = (MBPrivate)xdrs->x_base; + + if (mbp->mcurrent) { + mbp->pos += mbp->mcurrent->m_len; + rval = mbp->mcurrent->m_next; + } else { + rval = 0; + } + + if (rval) { + xdrmbuf_setup(xdrs, rval); + } +#if DEBUG & DEBUG_VERB + else { + fprintf(stderr,"xdrmbuf: end of chain\n"); + } +#endif + + return rval; +} + +/* + * The procedure xdrmbuf_create initializes a stream descriptor for a + * memory buffer. + */ +void +xdrmbuf_create(XDR *xdrs, struct mbuf *mbuf, enum xdr_op op) +{ +MBPrivate mbp; + + xdrs->x_op = op; + mbp = (MBPrivate)my_malloc(sizeof(*mbp)); + assert( mbp ); + xdrs->x_base = (caddr_t) mbp; + + mbp->mchain = mbuf; + mbp->pos = 0; + +#if DEBUG & DEBUG_VERB + { + struct mbuf *mbf; + fprintf(stderr,"Dumping chain:\n"); + for (mbf = mbuf; mbf; mbf=mbf->m_next) { + int ii; + fprintf(stderr,"MBUF------------"); + for (ii=0; ii<mbf->m_len; ii++) { + fprintf(stderr,"%02x ",mtod(mbf,char*)[ii]); + if (ii%16==0) + fputc('\n',stderr); + } + fputc('\n',stderr); + } + } +#endif + + xdrmbuf_setup(xdrs, mbuf); +} + +static void +xdrmbuf_destroy(XDR *xdrs) +{ +MBPrivate mbp = (MBPrivate)xdrs->x_base; +#if 0 /* leave destroying the chain to the user */ +struct mbuf *m = mbp->mchain; + + rtems_bsdnet_semaphore_obtain(); + m_freem(m); + rtems_bsdnet_semaphore_release(); +#endif + + my_free(mbp); +} + +static bool_t +xdrmbuf_getlong_aligned(register XDR *xdrs, register long *lp) +{ + while ( (signed int)(xdrs->x_handy -= sizeof(int32_t)) < 0) { + if ((xdrs->x_handy += sizeof(int32_t)) == 0) { + /* handy was 0 on entry; request a new buffer. + * Coded this way, so the most frequently executed + * path needs only one comparison... + */ + if (!xdrmbuf_next(xdrs)) + return FALSE; + } else { + /* uh-oh an aligned long spread over two MBUFS ?? + * let the unaligned handler deal with this rare + * situation. + */ + return xdrmbuf_getlong_unaligned(xdrs,lp); + } + } + *lp = ntohl(*(int32_t *)(xdrs->x_private)); + xdrs->x_private += sizeof(int32_t); +#if DEBUG & DEBUG_VERB + fprintf(stderr,"Got aligned long %x\n",*lp); +#endif + return (TRUE); +} + +static bool_t +xdrmbuf_putlong_aligned( + XDR *xdrs, + const long *lp) +{ +fprintf(stderr,"TODO: xdrmbuf_putlong_aligned() is unimplemented\n"); + return FALSE; +#if 0 + if ((xdrs->x_handy -= sizeof(int32_t)) < 0) + return (FALSE); + *(int32_t *)xdrs->x_private = htonl(*lp); + xdrs->x_private += sizeof(int32_t); + return (TRUE); +#endif +} + +static bool_t +xdrmbuf_getlong_unaligned( + XDR *xdrs, + long *lp) +{ +union { + int32_t l; + char c[sizeof(int32_t)]; +} u; + +register int i,j; +register char *cp,*sp; + + i = xdrs->x_handy - sizeof(int32_t); + + /* handle the most common case first */ + if ( i >= 0 ) { + + xdrs->x_handy = i; + sp = (char*)xdrs->x_private; + xdrs->x_private = sp + sizeof(int32_t); + +#ifdef CANDO_UNALIGNED + { + *lp = ntohl(*(int32_t *)sp); +# if DEBUG & DEBUG_VERB + fprintf(stderr,"Got unaligned long %x (%i remaining)\n",*lp, xdrs->x_handy); +# endif + return TRUE; + } +#else /* machine can't do unaligned access */ + { + u.c[0] = *sp; + u.c[1] = *++sp; + u.c[2] = *++sp; + u.c[3] = *++sp; + + goto done; + } +#endif /* CANDO_UNALIGNED */ + } + + /* here the messy 'crossing buffers' business starts */ + + + j = sizeof(int32_t); + + cp = u.c-1; + + /* NOTE: on entry to this section, handy < j holds */ + do { + sp = ((char*)xdrs->x_private)-1; + + if ( (i=xdrs->x_handy) >= j ) { + /* more data in the buffer than we need: + * copy everything we need and goto 'done' + */ + xdrs->x_handy = i-j; + do { + *++cp = *++sp; + } while (--j > 0); + xdrs->x_private = (caddr_t)++sp; + + goto done; + + } else { + /* not enough data - copy as much as possible + * then get retrieve the next MBUF and start + * over + */ + j-=i; + while (i--) + *++cp = *++sp; + if (!xdrmbuf_next(xdrs)) + return FALSE; +#if DEBUG & DEBUG_VERB + fprintf(stderr,"getlong_unaligned: crossed mbuf boundary\n"); +#endif + } + } while (j > 0); + +done: + + *lp = ntohl(u.l); + +#if DEBUG & DEBUG_VERB + fprintf(stderr,"Got unaligned long %x (%i remaining)\n",*lp, xdrs->x_handy); +#endif + return (TRUE); +} + +static bool_t +xdrmbuf_putlong_unaligned( + XDR *xdrs, + const long *lp ) +{ + + fprintf(stderr,"TODO: xdrmbuf_putlong_unaligned() is unimplemented\n"); + return FALSE; +#if 0 + { + int32_t l; + + if ((xdrs->x_handy -= sizeof(int32_t)) < 0) + return (FALSE); + l = htonl(*lp); + memcpy(xdrs->x_private, &l, sizeof(int32_t)); + xdrs->x_private += sizeof(int32_t); + return (TRUE); + } +#endif +} + +static bool_t +xdrmbuf_getbytes( + XDR *xdrs, + caddr_t addr, + u_int len) +{ +#if DEBUG & DEBUG_VERB +int olen=len,bufs=0; +#endif + +#if DEBUG & DEBUG_VERB + fprintf(stderr,"wanting %i bytes (have %i)\n",olen,xdrs->x_handy); +#endif + + while (len>0) { + if (xdrs->x_handy >= len) { + memcpy(addr, xdrs->x_private, len); + xdrs->x_private += len; + xdrs->x_handy -= len; +#if 0 /* save a couple of instructions */ + len = 0; +#else + goto done; +#endif + } else { + if (xdrs->x_handy > 0) { + memcpy(addr, xdrs->x_private, xdrs->x_handy); + len -= xdrs->x_handy; + addr += xdrs->x_handy; + } + if (!xdrmbuf_next(xdrs)) + return FALSE; +#if DEBUG & DEBUG_VERB + bufs++; +#endif + } + } +done: +#if DEBUG & DEBUG_VERB + fprintf(stderr,"Got %i bytes (out of %i mbufs)\n",olen,bufs); +#endif + return (TRUE); +} + +static bool_t +xdrmbuf_putbytes( + XDR *xdrs, + const char *addr, + u_int len ) +{ + + fprintf(stderr,"TODO: xdrmbuf_putbytes() is unimplemented\n"); + return FALSE; +#if 0 + if ((xdrs->x_handy -= len) < 0) + return (FALSE); + memcpy(xdrs->x_private, addr, len); + xdrs->x_private += len; + return (TRUE); +#endif +} + +static u_int +xdrmbuf_getpos( + XDR *xdrs) +{ +#if 1 +MBPrivate mbp = (MBPrivate)xdrs->x_base; +struct mbuf *m = mbp->mcurrent; +u_int rval = mbp->pos; + + if (m) { + rval += xdrs->x_private - mtod(m, void*); + } +#else +struct mbuf *m; +u_int rval = 0; +MBPrivate mbp = (MBPrivate)xdrs->x_base; + + for ( m = mbp->mchain; m && m != mbp->mcurrent; m = m->m_next ) + rval += m->m_len; + if (m) { + rval += (u_long)xdrs->x_private - mtod(m, u_long); + } + +#endif + return rval; +} + +static bool_t +xdrmbuf_setpos( + XDR *xdrs, + u_int pos) +{ +struct mbuf *m; +MBPrivate mbp = (MBPrivate)xdrs->x_base; + + if (pos >= mbp->pos) { + pos -= mbp->pos; + m = mbp->mcurrent; + } else { + m = mbp->mchain; + mbp->pos = 0; + } + + while ( m && pos >= m->m_len ) { + pos -= m->m_len; + mbp->pos += m->m_len; + m = m->m_next; + } + + if (m) { + xdrmbuf_setup(xdrs, m); + xdrs->x_private += pos; + return TRUE; + } + + return 0 == pos ? TRUE : FALSE; +} + +static int32_t * +xdrmbuf_inline_aligned( + XDR *xdrs, + u_int len) +{ +int32_t *buf = 0; + + if (xdrs->x_handy == 0 && !xdrmbuf_next(xdrs)) + return 0; + + if (xdrs->x_handy >= len) { + xdrs->x_handy -= len; + buf = (int32_t *) xdrs->x_private; + xdrs->x_private += len; +#if DEBUG & DEBUG_VERB + fprintf(stderr,"Got %i aligned inline bytes at %x\n", len, buf); +#endif + } +#if DEBUG & DEBUG_VERB + else { + fprintf(stderr,"Skipped %i aligned inline bytes\n",len); + } +#endif + return (buf); +} + +static int32_t * +xdrmbuf_inline_unaligned( + XDR *xdrs, + u_int len ) +{ + return (0); +} diff --git a/cpukit/libfs/src/pipe/fifo.c b/cpukit/libfs/src/pipe/fifo.c new file mode 100644 index 0000000000..9579954611 --- /dev/null +++ b/cpukit/libfs/src/pipe/fifo.c @@ -0,0 +1,585 @@ +/* + * fifo.c: POSIX FIFO/pipe for RTEMS + * + * Author: Wei Shen <cquark@gmail.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef RTEMS_POSIX_API +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#endif + +#include <errno.h> +#include <stdlib.h> + +#include <rtems.h> +#include <rtems/libio_.h> + +#include "pipe.h" + + +#define MIN(a, b) ((a) < (b)? (a): (b)) + +#define LIBIO_ACCMODE(_iop) ((_iop)->flags & LIBIO_FLAGS_READ_WRITE) +#define LIBIO_NODELAY(_iop) ((_iop)->flags & LIBIO_FLAGS_NO_DELAY) + +static rtems_id pipe_semaphore = RTEMS_ID_NONE; + + +#define PIPE_EMPTY(_pipe) (_pipe->Length == 0) +#define PIPE_FULL(_pipe) (_pipe->Length == _pipe->Size) +#define PIPE_SPACE(_pipe) (_pipe->Size - _pipe->Length) +#define PIPE_WSTART(_pipe) ((_pipe->Start + _pipe->Length) % _pipe->Size) + +#define PIPE_LOCK(_pipe) \ + ( rtems_semaphore_obtain(_pipe->Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT) \ + == RTEMS_SUCCESSFUL ) + +#define PIPE_UNLOCK(_pipe) rtems_semaphore_release(_pipe->Semaphore) + +#define PIPE_READWAIT(_pipe) \ + ( rtems_barrier_wait(_pipe->readBarrier, RTEMS_NO_TIMEOUT) \ + == RTEMS_SUCCESSFUL) + +#define PIPE_WRITEWAIT(_pipe) \ + ( rtems_barrier_wait(_pipe->writeBarrier, RTEMS_NO_TIMEOUT) \ + == RTEMS_SUCCESSFUL) + +#define PIPE_WAKEUPREADERS(_pipe) \ + do {uint32_t n; rtems_barrier_release(_pipe->readBarrier, &n); } while(0) + +#define PIPE_WAKEUPWRITERS(_pipe) \ + do {uint32_t n; rtems_barrier_release(_pipe->writeBarrier, &n); } while(0) + + +#ifdef RTEMS_POSIX_API +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ + +#include <rtems/rtems/barrier.h> +#include <rtems/score/thread.h> + +/* Set barriers to be interruptible by signals. */ +static void pipe_interruptible(pipe_control_t *pipe) +{ + Objects_Locations location; + + _Barrier_Get(pipe->readBarrier, &location)->Barrier.Wait_queue.state + |= STATES_INTERRUPTIBLE_BY_SIGNAL; + _Thread_Enable_dispatch(); + _Barrier_Get(pipe->writeBarrier, &location)->Barrier.Wait_queue.state + |= STATES_INTERRUPTIBLE_BY_SIGNAL; + _Thread_Enable_dispatch(); +} +#endif + +/* + * Alloc pipe control structure, buffer, and resources. + * Called with pipe_semaphore held. + */ +static int pipe_alloc( + pipe_control_t **pipep +) +{ + static char c = 'a'; + pipe_control_t *pipe; + int err = -ENOMEM; + + pipe = malloc(sizeof(pipe_control_t)); + if (pipe == NULL) + return err; + memset(pipe, 0, sizeof(pipe_control_t)); + + pipe->Size = PIPE_BUF; + pipe->Buffer = malloc(pipe->Size); + if (! pipe->Buffer) + goto err_buf; + + err = -ENOMEM; + + if (rtems_barrier_create( + rtems_build_name ('P', 'I', 'r', c), + RTEMS_BARRIER_MANUAL_RELEASE, 0, + &pipe->readBarrier) != RTEMS_SUCCESSFUL) + goto err_rbar; + if (rtems_barrier_create( + rtems_build_name ('P', 'I', 'w', c), + RTEMS_BARRIER_MANUAL_RELEASE, 0, + &pipe->writeBarrier) != RTEMS_SUCCESSFUL) + goto err_wbar; + if (rtems_semaphore_create( + rtems_build_name ('P', 'I', 's', c), 1, + RTEMS_BINARY_SEMAPHORE | RTEMS_FIFO, + RTEMS_NO_PRIORITY, &pipe->Semaphore) != RTEMS_SUCCESSFUL) + goto err_sem; + +#ifdef RTEMS_POSIX_API + pipe_interruptible(pipe); +#endif + + *pipep = pipe; + if (c ++ == 'z') + c = 'a'; + return 0; + +err_sem: + rtems_barrier_delete(pipe->writeBarrier); +err_wbar: + rtems_barrier_delete(pipe->readBarrier); +err_rbar: + free(pipe->Buffer); +err_buf: + free(pipe); + return err; +} + +/* Called with pipe_semaphore held. */ +static inline void pipe_free( + pipe_control_t *pipe +) +{ + rtems_barrier_delete(pipe->readBarrier); + rtems_barrier_delete(pipe->writeBarrier); + rtems_semaphore_delete(pipe->Semaphore); + free(pipe->Buffer); + free(pipe); +} + +static rtems_status_code pipe_lock(void) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + if (pipe_semaphore == RTEMS_ID_NONE) { + rtems_libio_lock(); + + if (pipe_semaphore == RTEMS_ID_NONE) { + sc = rtems_semaphore_create( + rtems_build_name('P', 'I', 'P', 'E'), + 1, + RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY, + RTEMS_NO_PRIORITY, + &pipe_semaphore + ); + } + + rtems_libio_unlock(); + } + + if (sc == RTEMS_SUCCESSFUL) { + sc = rtems_semaphore_obtain(pipe_semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + } + + if (sc == RTEMS_SUCCESSFUL) { + return 0; + } else { + return -ENOMEM; + } +} + +static void pipe_unlock(void) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + sc = rtems_semaphore_release(pipe_semaphore); + #ifdef RTEMS_DEBUG + if (sc != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(0xdeadbeef); + } + #endif +} + +/* + * If called with *pipep = NULL, pipe_new will call pipe_alloc to allocate a + * pipe control structure and set *pipep to its address. + * pipe is locked, when pipe_new returns with no error. + */ +static int pipe_new( + pipe_control_t **pipep +) +{ + pipe_control_t *pipe; + int err = 0; + + err = pipe_lock(); + if (err) + return err; + + pipe = *pipep; + if (pipe == NULL) { + err = pipe_alloc(&pipe); + if (err) + goto out; + } + + if (! PIPE_LOCK(pipe)) + err = -EINTR; + + if (*pipep == NULL) { + if (err) + pipe_free(pipe); + else + *pipep = pipe; + } + +out: + pipe_unlock(); + return err; +} + +/* + * Interface to file system close. + * + * *pipep points to pipe control structure. When the last user releases pipe, + * it will be set to NULL. + */ +void pipe_release( + pipe_control_t **pipep, + rtems_libio_t *iop +) +{ + pipe_control_t *pipe = *pipep; + uint32_t mode; + + #if defined(RTEMS_DEBUG) + /* WARN pipe not freed and pipep not set to NULL! */ + if (pipe_lock()) + rtems_fatal_error_occurred(0xdeadbeef); + + /* WARN pipe not released! */ + if (!PIPE_LOCK(pipe)) + rtems_fatal_error_occurred(0xdeadbeef); + #endif + + mode = LIBIO_ACCMODE(iop); + if (mode & LIBIO_FLAGS_READ) + pipe->Readers --; + if (mode & LIBIO_FLAGS_WRITE) + pipe->Writers --; + + PIPE_UNLOCK(pipe); + + if (pipe->Readers == 0 && pipe->Writers == 0) { +#if 0 + /* To delete an anonymous pipe file when all users closed it */ + if (pipe->Anonymous) + delfile = TRUE; +#endif + pipe_free(pipe); + *pipep = NULL; + } + else if (pipe->Readers == 0 && mode != LIBIO_FLAGS_WRITE) + /* Notify waiting Writers that all their partners left */ + PIPE_WAKEUPWRITERS(pipe); + else if (pipe->Writers == 0 && mode != LIBIO_FLAGS_READ) + PIPE_WAKEUPREADERS(pipe); + + pipe_unlock(); + +#if 0 + if (! delfile) + return; + if (iop->pathinfo.ops->unlink_h == NULL) + return; + + /* This is safe for IMFS, but how about other FSes? */ + iop->flags &= ~LIBIO_FLAGS_OPEN; + if(iop->pathinfo.ops->unlink_h(&iop->pathinfo)) + return; +#endif + +} + +/* + * Interface to file system open. + * + * *pipep points to pipe control structure. If called with *pipep = NULL, + * fifo_open will try allocating and initializing a control structure. If the + * call succeeds, *pipep will be set to address of new control structure. + */ +int fifo_open( + pipe_control_t **pipep, + rtems_libio_t *iop +) +{ + pipe_control_t *pipe; + unsigned int prevCounter; + int err; + + err = pipe_new(pipep); + if (err) + return err; + pipe = *pipep; + + switch (LIBIO_ACCMODE(iop)) { + case LIBIO_FLAGS_READ: + pipe->readerCounter ++; + if (pipe->Readers ++ == 0) + PIPE_WAKEUPWRITERS(pipe); + + if (pipe->Writers == 0) { + /* Not an error */ + if (LIBIO_NODELAY(iop)) + break; + + prevCounter = pipe->writerCounter; + err = -EINTR; + /* Wait until a writer opens the pipe */ + do { + PIPE_UNLOCK(pipe); + if (! PIPE_READWAIT(pipe)) + goto out_error; + if (! PIPE_LOCK(pipe)) + goto out_error; + } while (prevCounter == pipe->writerCounter); + } + break; + + case LIBIO_FLAGS_WRITE: + pipe->writerCounter ++; + + if (pipe->Writers ++ == 0) + PIPE_WAKEUPREADERS(pipe); + + if (pipe->Readers == 0 && LIBIO_NODELAY(iop)) { + PIPE_UNLOCK(pipe); + err = -ENXIO; + goto out_error; + } + + if (pipe->Readers == 0) { + prevCounter = pipe->readerCounter; + err = -EINTR; + do { + PIPE_UNLOCK(pipe); + if (! PIPE_WRITEWAIT(pipe)) + goto out_error; + if (! PIPE_LOCK(pipe)) + goto out_error; + } while (prevCounter == pipe->readerCounter); + } + break; + + case LIBIO_FLAGS_READ_WRITE: + pipe->readerCounter ++; + if (pipe->Readers ++ == 0) + PIPE_WAKEUPWRITERS(pipe); + pipe->writerCounter ++; + if (pipe->Writers ++ == 0) + PIPE_WAKEUPREADERS(pipe); + break; + } + + PIPE_UNLOCK(pipe); + return 0; + +out_error: + pipe_release(pipep, iop); + return err; +} + +/* + * Interface to file system read. + */ +ssize_t pipe_read( + pipe_control_t *pipe, + void *buffer, + size_t count, + rtems_libio_t *iop +) +{ + int chunk, chunk1, read = 0, ret = 0; + + if (! PIPE_LOCK(pipe)) + return -EINTR; + + while (read < count) { + while (PIPE_EMPTY(pipe)) { + /* Not an error */ + if (pipe->Writers == 0) + goto out_locked; + + if (LIBIO_NODELAY(iop)) { + ret = -EAGAIN; + goto out_locked; + } + + /* Wait until pipe is no more empty or no writer exists */ + pipe->waitingReaders ++; + PIPE_UNLOCK(pipe); + if (! PIPE_READWAIT(pipe)) + ret = -EINTR; + if (! PIPE_LOCK(pipe)) { + /* WARN waitingReaders not restored! */ + ret = -EINTR; + goto out_nolock; + } + pipe->waitingReaders --; + if (ret != 0) + goto out_locked; + } + + /* Read chunk bytes */ + chunk = MIN(count - read, pipe->Length); + chunk1 = pipe->Size - pipe->Start; + if (chunk > chunk1) { + memcpy(buffer + read, pipe->Buffer + pipe->Start, chunk1); + memcpy(buffer + read + chunk1, pipe->Buffer, chunk - chunk1); + } + else + memcpy(buffer + read, pipe->Buffer + pipe->Start, chunk); + + pipe->Start += chunk; + pipe->Start %= pipe->Size; + pipe->Length -= chunk; + /* For buffering optimization */ + if (PIPE_EMPTY(pipe)) + pipe->Start = 0; + + if (pipe->waitingWriters > 0) + PIPE_WAKEUPWRITERS(pipe); + read += chunk; + } + +out_locked: + PIPE_UNLOCK(pipe); + +out_nolock: + if (read > 0) + return read; + return ret; +} + +/* + * Interface to file system write. + */ +ssize_t pipe_write( + pipe_control_t *pipe, + const void *buffer, + size_t count, + rtems_libio_t *iop +) +{ + int chunk, chunk1, written = 0, ret = 0; + + /* Write nothing */ + if (count == 0) + return 0; + + if (! PIPE_LOCK(pipe)) + return -EINTR; + + if (pipe->Readers == 0) { + ret = -EPIPE; + goto out_locked; + } + + /* Write of PIPE_BUF bytes or less shall not be interleaved */ + chunk = count <= pipe->Size ? count : 1; + + while (written < count) { + while (PIPE_SPACE(pipe) < chunk) { + if (LIBIO_NODELAY(iop)) { + ret = -EAGAIN; + goto out_locked; + } + + /* Wait until there is chunk bytes space or no reader exists */ + pipe->waitingWriters ++; + PIPE_UNLOCK(pipe); + if (! PIPE_WRITEWAIT(pipe)) + ret = -EINTR; + if (! PIPE_LOCK(pipe)) { + /* WARN waitingWriters not restored! */ + ret = -EINTR; + goto out_nolock; + } + pipe->waitingWriters --; + if (ret != 0) + goto out_locked; + + if (pipe->Readers == 0) { + ret = -EPIPE; + goto out_locked; + } + } + + chunk = MIN(count - written, PIPE_SPACE(pipe)); + chunk1 = pipe->Size - PIPE_WSTART(pipe); + if (chunk > chunk1) { + memcpy(pipe->Buffer + PIPE_WSTART(pipe), buffer + written, chunk1); + memcpy(pipe->Buffer, buffer + written + chunk1, chunk - chunk1); + } + else + memcpy(pipe->Buffer + PIPE_WSTART(pipe), buffer + written, chunk); + + pipe->Length += chunk; + if (pipe->waitingReaders > 0) + PIPE_WAKEUPREADERS(pipe); + written += chunk; + /* Write of more than PIPE_BUF bytes can be interleaved */ + chunk = 1; + } + +out_locked: + PIPE_UNLOCK(pipe); + +out_nolock: +#ifdef RTEMS_POSIX_API + /* Signal SIGPIPE */ + if (ret == -EPIPE) + kill(getpid(), SIGPIPE); +#endif + + if (written > 0) + return written; + return ret; +} + +/* + * Interface to file system ioctl. + */ +int pipe_ioctl( + pipe_control_t *pipe, + uint32_t cmd, + void *buffer, + rtems_libio_t *iop +) +{ + if (cmd == FIONREAD) { + if (buffer == NULL) + return -EFAULT; + + if (! PIPE_LOCK(pipe)) + return -EINTR; + + /* Return length of pipe */ + *(unsigned int *)buffer = pipe->Length; + PIPE_UNLOCK(pipe); + return 0; + } + + return -EINVAL; +} + +/* + * Interface to file system lseek. + */ +int pipe_lseek( + pipe_control_t *pipe, + off_t offset, + int whence, + rtems_libio_t *iop +) +{ + /* Seek on pipe is not supported */ + return -ESPIPE; +} diff --git a/cpukit/libfs/src/pipe/pipe.c b/cpukit/libfs/src/pipe/pipe.c new file mode 100644 index 0000000000..63a294f5f8 --- /dev/null +++ b/cpukit/libfs/src/pipe/pipe.c @@ -0,0 +1,79 @@ +/* + * pipe.c: anonymous pipe + * + * Author: Wei Shen <cquark@gmail.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <fcntl.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> + +/* Incremental number added to names of anonymous pipe files */ +/* FIXME: This approach is questionable */ +static uint16_t rtems_pipe_no = 0; + +/* + * Called by pipe() to create an anonymous pipe. + */ +int pipe_create( + int filsdes[2] +) +{ + rtems_libio_t *iop; + int err = 0; + + if (rtems_mkdir("/tmp", S_IRWXU | S_IRWXG | S_IRWXO) != 0) + return -1; + + /* /tmp/.fifoXXXX */ + char fifopath[15]; + memcpy(fifopath, "/tmp/.fifo", 10); + sprintf(fifopath + 10, "%04x", rtems_pipe_no ++); + + /* Try creating FIFO file until find an available file name */ + while (mkfifo(fifopath, S_IRUSR|S_IWUSR) != 0) { + if (errno != EEXIST){ + return -1; + } + /* Just try once... */ + return -1; + /* sprintf(fifopath + 10, "%04x", rtems_pipe_no ++); */ + } + + /* Non-blocking open to avoid waiting for writers */ + filsdes[0] = open(fifopath, O_RDONLY | O_NONBLOCK); + if (filsdes[0] < 0) { + err = errno; + /* Delete file at errors, or else if pipe is successfully created + the file node will be deleted after it is closed by all. */ + unlink(fifopath); + } + else { + /* Reset open file to blocking mode */ + iop = rtems_libio_iop(filsdes[0]); + iop->flags &= ~LIBIO_FLAGS_NO_DELAY; + + filsdes[1] = open(fifopath, O_WRONLY); + + if (filsdes[1] < 0) { + err = errno; + close(filsdes[0]); + } + unlink(fifopath); + } + if(err != 0) + rtems_set_errno_and_return_minus_one(err); + return 0; +} + diff --git a/cpukit/libfs/src/pipe/pipe.h b/cpukit/libfs/src/pipe/pipe.h new file mode 100644 index 0000000000..354cc9d130 --- /dev/null +++ b/cpukit/libfs/src/pipe/pipe.h @@ -0,0 +1,116 @@ +/* + * pipe.h: header of POSIX FIFO/pipe + * + * Author: Wei Shen <cquark@gmail.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef _RTEMS_PIPE_H +#define _RTEMS_PIPE_H + +#include <rtems/libio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Control block to manage each pipe */ +typedef struct pipe_control { + char *Buffer; + unsigned int Size; + unsigned int Start; + unsigned int Length; + unsigned int Readers; + unsigned int Writers; + unsigned int waitingReaders; + unsigned int waitingWriters; + unsigned int readerCounter; /* incremental counters */ + unsigned int writerCounter; /* for differentiation of successive opens */ + rtems_id Semaphore; + rtems_id readBarrier; /* wait queues */ + rtems_id writeBarrier; +#if 0 + boolean Anonymous; /* anonymous pipe or FIFO */ +#endif +} pipe_control_t; + +/* + * Called by pipe() to create an anonymous pipe. + */ +extern int pipe_create( + int filsdes[2] +); + +/* + * Interface to file system close. + * + * *pipep points to pipe control structure. When the last user releases pipe, + * it will be set to NULL. + */ +extern void pipe_release( + pipe_control_t **pipep, + rtems_libio_t *iop +); + +/* + * Interface to file system open. + * + * *pipep points to pipe control structure. If called with *pipep = NULL, + * fifo_open will try allocating and initializing a control structure. If the + * call succeeds, *pipep will be set to address of new control structure. + */ +extern int fifo_open( + pipe_control_t **pipep, + rtems_libio_t *iop +); + +/* + * Interface to file system read. + */ +extern ssize_t pipe_read( + pipe_control_t *pipe, + void *buffer, + size_t count, + rtems_libio_t *iop +); + +/* + * Interface to file system write. + */ +extern ssize_t pipe_write( + pipe_control_t *pipe, + const void *buffer, + size_t count, + rtems_libio_t *iop +); + +/* + * Interface to file system ioctl. + */ +extern int pipe_ioctl( + pipe_control_t *pipe, + uint32_t cmd, + void *buffer, + rtems_libio_t *iop +); + +/* + * Interface to file system lseek. + */ +extern int pipe_lseek( + pipe_control_t *pipe, + off_t offset, + int whence, + rtems_libio_t *iop +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-bitmaps-ut.c b/cpukit/libfs/src/rfs/rtems-rfs-bitmaps-ut.c new file mode 100644 index 0000000000..1240940bc2 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-bitmaps-ut.c @@ -0,0 +1,398 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Bitmap Unit Test.. + * + * This is a unit test module for the bit map functions. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> + +#include <rtems/rfs/rtems-rfs-bitmaps.h> +#include <rtems/rfs/rtems-rfs-file-system.h> + +#define rtems_rfs_exit_on_error(_rc, _r, _c, _b) \ + if ((_rc > 0) || _r) { free (_b); rtems_rfs_bitmap_close (_c); return; } + +static bool +rtems_rfs_bitmap_ut_test_range (rtems_rfs_bitmap_control* control, + int test, + bool set, + rtems_rfs_bitmap_bit bit, + size_t size) +{ + unsigned int count; + bool result; + for (count = 0; count < size; count++) + { + int rc = rtems_rfs_bitmap_map_test (control, bit + count, &result); + if (rc > 0) + { + printf (" %2d. Test bit %" PRId32 " in range (%" PRId32 ",%ld] is %s: ", + test, bit + count, bit, bit + size - 1, !set ? "set" : "clear"); + printf ("FAIL (%s)\n", strerror (rc)); + return false; + } + if (!set) + result = !result; + if (!result) + { + printf (" %2d. Test bit %" PRId32 " in range (%" PRId32 ",%ld] is %s: ", + test, bit + count, bit, bit + size - 1, !set ? "set" : "clear"); + printf (" %s\n", !result ? "pass" : "FAIL"); + return false; + } + } + + printf (" %2d. Test bit range (%" PRId32 ",%ld] all %s: pass\n", + test, bit, bit + size - 1, set ? "set" : "clear"); + + return true; +} + +static bool +rtems_rfs_bitmap_ut_alloc_seq_test (rtems_rfs_bitmap_control* control, + int test, + rtems_rfs_bitmap_bit bit, + size_t size) +{ + bool state; + int i; + int rc; + + printf (" %2d. Set all bits\n", test); + rc = rtems_rfs_bitmap_map_set_all (control); + if (rc > 0) + { + printf (" %2d. set all bits: FAIL (%s)\n", test, strerror (rc)); + return false; + } + + for (i = 0; i < size; i++) + rtems_rfs_bitmap_map_clear (control, bit + i); + + printf (" %2d. Cleared bits (%" PRId32 ", %ld] (%zd)\n", + test, bit, bit + size - 1, size); + + for (i = 0; i < rtems_rfs_bitmap_element_bits (); i++) + { + rc = rtems_rfs_bitmap_map_test (control, bit + i, &state); + if (rc > 0) + { + printf (" %2d. test bit: FAIL (%s)\n", test, strerror (rc)); + return false; + } + if (state) + { + printf (" %2d. Cleared bit still set: bit = %" PRId32 "\n", test, bit + i); + return false; + } + } + + for (i = 0, bit = 0; i < size; i++) + { + rtems_rfs_bitmap_bit seed = bit; + bool result; + int rc; + rc = rtems_rfs_bitmap_map_alloc (control, seed, &result, &bit); + if (rc > 0) + { + printf (" %2d. map all: FAIL (%s)\n", test, strerror (rc)); + return false; + } + if (!result) + { + printf (" %2d. Find bit with seed = %" PRId32 ": %s: bit = %" PRId32 "\n", + test, seed, result ? "pass" : "FAIL", bit); + return false; + } + } + + printf (" %2d. Alloc'ed all bits (%" PRId32 ", %ld] (%zd)\n", + test, bit, bit + size - 1, size); + + return true; +} + +static void +rtems_rfs_bitmap_ut_test_bitmap (size_t size) +{ + rtems_rfs_file_system fs; + rtems_rfs_bitmap_control control; + rtems_rfs_buffer_handle handle; + rtems_rfs_buffer buffer; + rtems_rfs_bitmap_bit bit = 0; + rtems_rfs_bitmap_bit first_bit; + rtems_rfs_bitmap_bit last_bit; + bool result; + size_t bytes; + size_t clear; + int rc; + + bytes = (rtems_rfs_bitmap_elements (size) * + sizeof (rtems_rfs_bitmap_element)); + + memset (&fs, 0, sizeof (fs)); + memset (&buffer, 0, sizeof (buffer)); + + buffer.buffer = malloc (bytes); + buffer.block = 1; + + if (!buffer.buffer) + { + printf (" Cannot allocate bitmap memory\n"); + return; + } + +#if RTEMS_RFS_BITMAP_CLEAR_ZERO + memset (buffer.buffer, 0, bytes); +#else + memset (buffer.buffer, 0xff, bytes); +#endif + + /* + * Do not close the handle so no writes need occur. + */ + rc = rtems_rfs_buffer_handle_open (&fs, &handle); + if (rc > 0) + { + printf (" Cannot open the handle: %d: %s\n", rc, strerror (rc)); + free (buffer.buffer); + return; + } + + handle.buffer = &buffer; + handle.bnum = 1; + + printf ("\nRFS Bitmap Test : size = %zd (%zd)\n", + size, rtems_rfs_bitmap_elements (size)); + rc = rtems_rfs_bitmap_open (&control, &fs, &handle, size, 1); + if (rc > 0) + { + printf (" Cannot open the bitmap: %s\n", strerror (rc)); + free (buffer.buffer); + return; + } + + /* + * This is a new bitmap with no bits set. Try and find a bit with a few + * seeds. + */ + rc = rtems_rfs_bitmap_map_alloc (&control, size * 2, &result, &bit); + printf (" 1. Find bit with seed > size: %s (%s)\n", + result ? "FAIL" : "pass", strerror (rc)); + rtems_rfs_exit_on_error (rc, result, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_alloc (&control, size, &result, &bit); + printf (" 2. Find bit with seed = size: %s (%s)\n", + result ? "FAIL" : "pass", strerror (rc)); + rtems_rfs_exit_on_error (rc, result, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_alloc (&control, 0, &result, &bit); + result = result && (bit == 0); + printf (" 3. Find bit 0 with seed = 0: %s (%s): bit = %" PRId32 "\n", + result ? "pass" : "FAIL", strerror (rc), bit); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_alloc (&control, size - 1, &result, &bit); + result = result && (bit == (size - 1)); + printf (" 4. Find bit (size - 1) with seed = (size - 1) (%zd): %s (%s): bit = %" PRId32 "\n", + size - 1, result ? "pass" : "FAIL", strerror (rc), bit); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + + /* + * Test the bits allocated to make sure they are set. + */ + + rc = rtems_rfs_bitmap_map_test (&control, 0, &result); + printf (" 5. Test bit 0: %s (%s)\n", + result ? "pass" : "FAIL", strerror (rc)); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_test (&control, size - 1, &result); + printf (" 6. Test bit (size - 1) (%zd): %s (%s)\n", + size - 1, result ? "pass" : "FAIL", strerror (rc)); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + if (!rtems_rfs_bitmap_ut_test_range (&control, 7, false, 1, size - 2)) + rtems_rfs_exit_on_error (0, !result, &control, buffer.buffer); + + /* + * Set all bits then clear one and find it. + */ + rc = rtems_rfs_bitmap_map_set_all (&control); + printf (" 8. Set all bits: %s (%s)\n", + rc == 0 ? "PASS" : "FAIL", strerror (rc)); + rtems_rfs_exit_on_error (rc, false, &control, buffer.buffer); + + bit = rand () % size; + + rc = rtems_rfs_bitmap_map_clear (&control, bit); + printf (" 9. Clear bit %" PRId32 ": %s (%s)\n", + bit, rc == 0 ? "PASS" : "FAIL", strerror (rc)); + rtems_rfs_exit_on_error (rc, false, &control, buffer.buffer); + + last_bit = bit; + rc = rtems_rfs_bitmap_map_alloc (&control, 0, &result, &bit); + result = result && (bit == last_bit); + printf (" 10. Find bit with seed = 0: %s (%s): bit = %" PRId32 "\n", + result ? "pass" : "FAIL", strerror (rc), bit); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_alloc (&control, 0, &result, &bit); + result = !result || (bit != last_bit); + printf (" 11. Fail to find bit with seed = 0: %s (%s): bit = %" PRId32 "\n", + result ? "pass" : "FAIL", strerror (rc), bit); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_clear (&control, 0); + printf (" 12. Clear bit 0: %s (%s)\n", + rc == 0 ? "pass" : "FAIL", strerror (rc)); + rtems_rfs_exit_on_error (rc, false, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_alloc (&control, size - 1, &result, &bit); + result = result && (bit == 0); + printf (" 13. Find bit with seed = (size - 1): %s (%s): bit = %" PRId32 "\n", + result ? "pass" : "FAIL", strerror (rc), bit); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_clear (&control, size - 1); + printf (" 14. Clear bit (size - 1) (%zd): %s (%s)\n", + size - 1, rc == 0 ? "pass" : "FAIL", strerror (rc)); + rtems_rfs_exit_on_error (rc, false, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_alloc (&control, 0, &result, &bit); + result = result && (bit == (size - 1)); + printf (" 15. Find bit with seed = 0: %s (%s): bit = %" PRId32 "\n", + result ? "pass" : "FAIL", strerror (rc), bit); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_clear (&control, 0); + printf (" 16. Clear bit 0: %s (%s)\n", + rc == 0 ? "pass" : "FAIL", strerror (rc)); + + rc = rtems_rfs_bitmap_map_alloc (&control, size / 2, &result, &bit); + result = result && (bit == 0); + printf (" 17. Find bit with seed = (size / 2) (%zd): %s (%s): bit = %" PRId32 "\n", + size / 2, result ? "pass" : "FAIL", strerror (rc), bit); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_clear (&control, size - 1); + printf (" 18. Clear bit (size - 1) (%zd): %s, (%s)\n", + size - 1, rc == 0 ? "pass" : "FAIL", strerror (rc)); + rtems_rfs_exit_on_error (rc, false, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_alloc (&control, size / 2, &result, &bit); + result = result && (bit == (size - 1)); + printf (" 19. Find bit with seed = (size / 2) (%zd): %s (%s): bit = %" PRId32 "\n", + size / 2, result ? "pass" : "FAIL", strerror (rc), bit); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_clear (&control, 0); + printf (" 20. Clear bit 0: %s (%s)\n", + rc == 0 ? "pass" : "FAIL", strerror (rc)); + + rc = rtems_rfs_bitmap_map_alloc (&control, (size / 2) - 1, &result, &bit); + result = result && (bit == 0); + printf (" 21. Find bit with seed = ((size / 2) - 1) (%zd): %s (%s): bit = %" PRId32 "\n", + (size / 2) - 1, result ? "pass" : "FAIL", strerror (rc), bit); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + rc = rtems_rfs_bitmap_map_clear (&control, size - 1); + printf (" 22. Clear bit (size - 1) (%zd): %s (%s)\n", + size - 1, rc == 0 ? "pass" : "FAIL", strerror (rc)); + + rc = rtems_rfs_bitmap_map_alloc (&control, (size / 2) - 1, &result, &bit); + result = result && (bit == (size - 1)); + printf (" 23. Find bit with seed = ((size / 2) - 1) (%zd): %s (%s): bit = %" PRId32 "\n", + (size / 2) - 1, result ? "pass" : "FAIL", strerror (rc), bit); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + bit = rand () % (size / 2) + rtems_rfs_bitmap_element_bits (); + result = rtems_rfs_bitmap_ut_alloc_seq_test (&control, 23, bit, + rtems_rfs_bitmap_element_bits ()); + rtems_rfs_exit_on_error (0, !result, &control, buffer.buffer); + + bit = rand () % (size / 2) + rtems_rfs_bitmap_element_bits (); + result = rtems_rfs_bitmap_ut_alloc_seq_test (&control, 24, bit, 57); + rtems_rfs_exit_on_error (0, !result, &control, buffer.buffer); + + /* + * Set all bits, clear a random numberone then create a search map and make + * sure the clear count is correct. + */ + rc = rtems_rfs_bitmap_map_set_all (&control); + printf (" 25. Set all bits: %s (%s)\n", + rc == 0 ? "PASS" : "FAIL", strerror (rc)); + rtems_rfs_exit_on_error (rc, false, &control, buffer.buffer); + + first_bit = rand () % (size / 2) + rtems_rfs_bitmap_element_bits (); + last_bit = first_bit + rand () % (size / 2) + rtems_rfs_bitmap_element_bits (); + + for (bit = first_bit; bit < last_bit; bit++) + { + rc = rtems_rfs_bitmap_map_clear (&control, bit); + if (rc > 0) + { + printf (" 26. Clear bit %" PRId32 ": %s (%s)\n", + bit, rc == 0 ? "PASS" : "FAIL", strerror (rc)); + rtems_rfs_exit_on_error (rc, false, &control, buffer.buffer); + } + } + + printf (" 26. Clear bit (%" PRId32 ", %" PRId32 "]: %s (%s)\n", + first_bit, last_bit, rc == 0 ? "PASS" : "FAIL", strerror (rc)); + + clear = rtems_rfs_bitmap_map_free (&control); + result = clear == (last_bit - first_bit); + printf (" 27. Check free count is %zd: %" PRId32 ": %s (%s)\n", + clear, last_bit - first_bit, + result ? "pass" : "FAIL", strerror (rc)); + + rc = rtems_rfs_bitmap_create_search (&control); + result = clear == rtems_rfs_bitmap_map_free (&control); + printf (" 28. Create search check free count is %zd: %zd: %s (%s)\n", + clear, rtems_rfs_bitmap_map_free (&control), + result ? "pass" : "FAIL", strerror (rc)); + rtems_rfs_exit_on_error (rc, !result, &control, buffer.buffer); + + rtems_rfs_bitmap_close (&control); + free (buffer.buffer); +} + +void +rtems_rfs_bitmap_unit_test (void) +{ + printf ("RTEMS File System Bitmap Unit Test\n"); + printf (" Bit set value : %d\n", RTEMS_RFS_BITMAP_BIT_SET); + printf (" Bit clear value : %d\n", RTEMS_RFS_BITMAP_BIT_CLEAR); + printf (" Num bit per element : %zd\n", rtems_rfs_bitmap_element_bits ()); + +#if INT_MAX >= 0x23984237 + srand (0x23984237); +#else + srand (0x2398); +#endif + + rtems_rfs_bitmap_ut_test_bitmap (2048); + rtems_rfs_bitmap_ut_test_bitmap (420); +} + diff --git a/cpukit/libfs/src/rfs/rtems-rfs-bitmaps.c b/cpukit/libfs/src/rfs/rtems-rfs-bitmaps.c new file mode 100644 index 0000000000..98715488f9 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-bitmaps.c @@ -0,0 +1,646 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Bitmap Routines. + * + * These functions manage bit maps. A bit map consists of the map of bit + * allocated in a block and a search map where a bit represents 32 actual + * bits. The search map allows for a faster search for an available bit as 32 + * search bits can checked in a test. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +/** + * Set to 1 to enable warnings when developing. + */ +#define RTEMS_RFS_BITMAP_WARNINGS 0 + +#if RTEMS_RFS_BITMAP_WARNINGS +#include <stdio.h> +#endif +#include <stdlib.h> +#include <rtems/rfs/rtems-rfs-bitmaps.h> + +/** + * Test a bit in an element. If set return true else return false. + * + * @param target The target to test the bit in. + * @param bit The bit to test. + * @retval true The bit is set. + * @retval false The bit is clear. + */ +static bool +rtems_rfs_bitmap_test (rtems_rfs_bitmap_element target, + rtems_rfs_bitmap_bit bit) +{ + return RTEMS_RFS_BITMAP_TEST_BIT (target, bit); +} + +/** + * Set the bits in the element. Bits not set in the bit argument are left + * unchanged. + * + * @param target The target element bits are set. + * @param bits The bits in the target to set. A 1 in the bits will set the + * same bit in the target. + */ +static rtems_rfs_bitmap_element +rtems_rfs_bitmap_set (rtems_rfs_bitmap_element target, + rtems_rfs_bitmap_element bits) +{ + return RTEMS_RFS_BITMAP_SET_BITS (target, bits); +} + +/** + * Clear the bits in the element. Bits not set in the bit argument are left + * unchanged. + * + * @param target The target element to clear the bits in. + * @param bits The bits in the target to clear. A 1 in the bits will clear the + * bit in the target. + */ +static rtems_rfs_bitmap_element +rtems_rfs_bitmap_clear (rtems_rfs_bitmap_element target, + rtems_rfs_bitmap_element bits) +{ + return RTEMS_RFS_BITMAP_CLEAR_BITS (target, bits); +} + +/** + * Merge the bits in 2 variables based on the mask. A set bit in the mask will + * merge the bits from bits1 and a clear bit will merge the bits from bits2. + * The mask is always defined as 1 being set and 0 being clear. + */ +static rtems_rfs_bitmap_element +rtems_rfs_bitmap_merge (rtems_rfs_bitmap_element bits1, + rtems_rfs_bitmap_element bits2, + rtems_rfs_bitmap_element mask) +{ + /* + * Use the normal bit operators because we do not change the bits just merge + * the 2 separate parts. + */ + bits1 &= mask; + bits2 &= RTEMS_RFS_BITMAP_INVERT_MASK (mask); + return bits1 | bits2; +} + +/** + * Match the bits of 2 elements and return true if they match else return + * false. + * + * @param bits1 One set of bits to match. + * @param bits2 The second set of bits to match. + * @retval true The bits match. + * @retval false The bits do not match. + */ +static bool +rtems_rfs_bitmap_match (rtems_rfs_bitmap_element bits1, + rtems_rfs_bitmap_element bits2) +{ + return bits1 ^ bits2 ? false : true; +} + +#if RTEMS_NOT_USED_BUT_KEPT +/** + * Match the bits of 2 elements within the mask and return true if they match + * else return false. + * + * @param mask The mask over which the match occurs. A 1 is a valid mask bit. + * @param bits1 One set of bits to match. + * @param bits2 The second set of bits to match. + * @retval true The bits match. + * @retval false The bits do not match. + */ +static bool +rtems_rfs_bitmap_match_masked (rtems_rfs_bitmap_element mask, + rtems_rfs_bitmap_element bits1, + rtems_rfs_bitmap_element bits2) +{ + return (bits1 ^ bits2) & mask ? false : true; +} +#endif + +/** + * Return the map after loading from disk if not already loaded. + * + * @param control The bitmap control. + * @param rtems_rfs_bitmap_map* Pointer to the bitmap map data if no error. + * @return int The error number (errno). No error if 0. + */ +static int +rtems_rfs_bitmap_load_map (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_map* map) +{ + int rc; + + if (!control->buffer) + return ENXIO; + + *map = NULL; + + rc = rtems_rfs_buffer_handle_request (control->fs, + control->buffer, + control->block, + true); + if (rc) + return rc; + + *map = rtems_rfs_buffer_data (control->buffer); + return 0; +} + +rtems_rfs_bitmap_element +rtems_rfs_bitmap_mask (unsigned int size) +{ + rtems_rfs_bitmap_element mask = RTEMS_RFS_BITMAP_ELEMENT_FULL_MASK; + mask >>= (rtems_rfs_bitmap_element_bits () - size); + return mask; +} + +rtems_rfs_bitmap_element +rtems_rfs_bitmap_mask_section (unsigned int start, unsigned int end) +{ + rtems_rfs_bitmap_element mask = 0; + if (end > start) + mask = rtems_rfs_bitmap_mask (end - start) << start; + return mask; +} + +int +rtems_rfs_bitmap_map_set (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit bit) +{ + rtems_rfs_bitmap_map map; + rtems_rfs_bitmap_map search_map; + int index; + int offset; + int rc; + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + if (bit >= control->size) + return EINVAL; + search_map = control->search_bits; + index = rtems_rfs_bitmap_map_index (bit); + offset = rtems_rfs_bitmap_map_offset (bit); + map[index] = rtems_rfs_bitmap_set (map[index], 1 << offset); + if (rtems_rfs_bitmap_match(map[index], RTEMS_RFS_BITMAP_ELEMENT_SET)) + { + bit = index; + index = rtems_rfs_bitmap_map_index (bit); + offset = rtems_rfs_bitmap_map_offset (bit); + search_map[index] = rtems_rfs_bitmap_set (search_map[index], 1 << offset); + control->free--; + rtems_rfs_buffer_mark_dirty (control->buffer); + } + return 0; +} + +int +rtems_rfs_bitmap_map_clear (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit bit) +{ + rtems_rfs_bitmap_map map; + rtems_rfs_bitmap_map search_map; + int index; + int offset; + int rc; + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + if (bit >= control->size) + return EINVAL; + search_map = control->search_bits; + index = rtems_rfs_bitmap_map_index (bit); + offset = rtems_rfs_bitmap_map_offset (bit); + map[index] = rtems_rfs_bitmap_clear (map[index], 1 << offset); + bit = index; + index = rtems_rfs_bitmap_map_index (bit); + offset = rtems_rfs_bitmap_map_offset(bit); + search_map[index] = rtems_rfs_bitmap_clear (search_map[index], 1 << offset); + rtems_rfs_buffer_mark_dirty (control->buffer); + control->free++; + return 0; +} + +int +rtems_rfs_bitmap_map_test (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit bit, + bool* state) +{ + rtems_rfs_bitmap_map map; + int index; + int rc; + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + if (bit >= control->size) + return EINVAL; + index = rtems_rfs_bitmap_map_index (bit); + *state = rtems_rfs_bitmap_test (map[index], bit); + return 0; +} + +int +rtems_rfs_bitmap_map_set_all (rtems_rfs_bitmap_control* control) +{ + rtems_rfs_bitmap_map map; + size_t elements; + int e; + int rc; + + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + + elements = rtems_rfs_bitmap_elements (control->size); + + control->free = 0; + + for (e = 0; e < elements; e++) + map[e] = RTEMS_RFS_BITMAP_ELEMENT_SET; + + elements = rtems_rfs_bitmap_elements (elements); + + for (e = 0; e < elements; e++) + control->search_bits[e] = RTEMS_RFS_BITMAP_ELEMENT_SET; + + rtems_rfs_buffer_mark_dirty (control->buffer); + + return 0; +} + +int +rtems_rfs_bitmap_map_clear_all (rtems_rfs_bitmap_control* control) +{ + rtems_rfs_bitmap_map map; + rtems_rfs_bitmap_bit last_search_bit; + size_t elements; + int e; + int rc; + + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + + elements = rtems_rfs_bitmap_elements (control->size); + + control->free = elements; + + for (e = 0; e < elements; e++) + map[e] = RTEMS_RFS_BITMAP_ELEMENT_CLEAR; + + /* + * Set the un-mapped bits in the last search element so the available logic + * works. + */ + last_search_bit = rtems_rfs_bitmap_map_offset (elements); + + if (last_search_bit == 0) + last_search_bit = rtems_rfs_bitmap_element_bits (); + + elements = rtems_rfs_bitmap_elements (elements); + + for (e = 0; e < (elements - 1); e++) + control->search_bits[e] = RTEMS_RFS_BITMAP_ELEMENT_CLEAR; + + control->search_bits[elements - 1] = + rtems_rfs_bitmap_merge (RTEMS_RFS_BITMAP_ELEMENT_CLEAR, + RTEMS_RFS_BITMAP_ELEMENT_SET, + rtems_rfs_bitmap_mask (last_search_bit)); + + rtems_rfs_buffer_mark_dirty (control->buffer); + + return 0; +} + +static int +rtems_rfs_search_map_for_clear_bit (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit* bit, + bool* found, + size_t window, + int direction) +{ + rtems_rfs_bitmap_map map; + rtems_rfs_bitmap_bit test_bit; + rtems_rfs_bitmap_bit end_bit; + rtems_rfs_bitmap_element* search_bits; + int search_index; + int search_offset; + rtems_rfs_bitmap_element* map_bits; + int map_index; + int map_offset; + int rc; + + *found = false; + + /* + * Load the bitmap. + */ + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + + /* + * Calculate the bit we are testing plus the end point we search over. + */ + test_bit = *bit; + end_bit = test_bit + (window * direction); + + if (end_bit < 0) + end_bit = 0; + else if (end_bit >= control->size) + end_bit = control->size - 1; + + map_index = rtems_rfs_bitmap_map_index (test_bit); + map_offset = rtems_rfs_bitmap_map_offset (test_bit); + search_index = rtems_rfs_bitmap_map_index (map_index); + search_offset = rtems_rfs_bitmap_map_offset (map_index); + + search_bits = &control->search_bits[search_index]; + map_bits = &map[map_index]; + + /* + * Check each bit from the search map offset for a clear bit. + */ + do + { + /* + * If any bit is clear find that bit and then search the map element. If + * all bits are set there are no map bits so move to the next search + * element. + */ + if (!rtems_rfs_bitmap_match (*search_bits, RTEMS_RFS_BITMAP_ELEMENT_SET)) + { + while ((search_offset >= 0) + && (search_offset < rtems_rfs_bitmap_element_bits ())) + { + if (!rtems_rfs_bitmap_test (*search_bits, search_offset)) + { + /* + * Find the clear bit in the map. Update the search map and map if + * found. We may find none are spare if searching up from the seed. + */ + while ((map_offset >= 0) + && (map_offset < rtems_rfs_bitmap_element_bits ())) + { + if (!rtems_rfs_bitmap_test (*map_bits, map_offset)) + { + *map_bits = rtems_rfs_bitmap_set (*map_bits, 1 << map_offset); + if (rtems_rfs_bitmap_match(*map_bits, + RTEMS_RFS_BITMAP_ELEMENT_SET)) + *search_bits = rtems_rfs_bitmap_set (*search_bits, + 1 << search_offset); + control->free--; + *bit = test_bit; + *found = true; + rtems_rfs_buffer_mark_dirty (control->buffer); + return 0; + } + + if (test_bit == end_bit) + break; + + map_offset += direction; + test_bit += direction; + } + } + + map_bits += direction; + map_index += direction; + map_offset = direction > 0 ? 0 : rtems_rfs_bitmap_element_bits () - 1; + + test_bit = (map_index * rtems_rfs_bitmap_element_bits ()) + map_offset; + + search_offset += direction; + + if (((direction < 0) && (test_bit <= end_bit)) + || ((direction > 0) && (test_bit >= end_bit))) + break; + } + } + else + { + /* + * Move to the next search element. We need to determine the number of + * bits in the search offset that are being skipped so the map bits + * pointer can be updated. If we are moving down and we have a search + * offset of 0 then the search map adjustment is to the top bit of the + * pervious search bit's value. + * + * Align test_bit either up or down depending on the direction to next 32 + * bit boundary. + */ + rtems_rfs_bitmap_bit bits_skipped; + test_bit &= ~((1 << RTEMS_RFS_ELEMENT_BITS_POWER_2) - 1); + if (direction > 0) + { + bits_skipped = rtems_rfs_bitmap_element_bits () - search_offset; + test_bit += bits_skipped * rtems_rfs_bitmap_element_bits (); + map_offset = 0; + } + else + { + bits_skipped = search_offset + 1; + /* + * Need to remove 1 for the rounding up. The above rounds down and + * adds 1. Remember the logic is for subtraction. + */ + test_bit -= ((bits_skipped - 1) * rtems_rfs_bitmap_element_bits ()) + 1; + map_offset = rtems_rfs_bitmap_element_bits () - 1; + } + map_bits += direction * bits_skipped; + map_index += direction * bits_skipped; + } + + search_bits += direction; + search_offset = direction > 0 ? 0 : rtems_rfs_bitmap_element_bits () - 1; + } + while (((direction < 0) && (test_bit >= end_bit)) + || ((direction > 0) && (test_bit <= end_bit))); + + return 0; +} + +int +rtems_rfs_bitmap_map_alloc (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit seed, + bool* allocated, + rtems_rfs_bitmap_bit* bit) +{ + rtems_rfs_bitmap_bit upper_seed; + rtems_rfs_bitmap_bit lower_seed; + rtems_rfs_bitmap_bit window; /* may become a parameter */ + int rc = 0; + + /* + * By default we assume the allocation failed. + */ + *allocated = false; + + /* + * The window is the number of bits we search over in either direction each + * time. + */ + window = RTEMS_RFS_BITMAP_SEARCH_WINDOW; + + /* + * Start from the seed and move in either direction. Search in window amounts + * of bits from the original seed above then below. That is search from the + * seed up then from the seed down a window number of bits, then repeat the + * process from the window distance from the seed, again above then + * below. Keep moving out until all bits have been searched. + */ + upper_seed = seed; + lower_seed = seed; + + /* + * If the upper and lower seed values have reached the limits of the bitmap + * we have searched all of the map. The seed may not be aligned to a window + * boundary so we may need to search a partial window and this may also not + * be balanced for the upper or lower seeds. We move to the limits, search + * then return false if no clear bits are found. + */ + while (((upper_seed >= 0) && (upper_seed < control->size)) + || ((lower_seed >= 0) && (lower_seed < control->size))) + { + /* + * Search up first so bits allocated in succession are grouped together. + */ + if (upper_seed < control->size) + { + *bit = upper_seed; + rc = rtems_rfs_search_map_for_clear_bit (control, bit, allocated, + window, 1); + if ((rc > 0) || *allocated) + break; + } + + if (lower_seed >= 0) + { + *bit = lower_seed; + rc = rtems_rfs_search_map_for_clear_bit (control, bit, allocated, + window, -1); + if ((rc > 0) || *allocated) + break; + } + + /* + * Do not bound the limits at the edges of the map. Do not update if an + * edge has been passed. + */ + if (upper_seed < control->size) + upper_seed += window; + if (lower_seed >= 0) + lower_seed -= window; + } + + return 0; +} + +int +rtems_rfs_bitmap_create_search (rtems_rfs_bitmap_control* control) +{ + rtems_rfs_bitmap_map search_map; + rtems_rfs_bitmap_map map; + size_t size; + rtems_rfs_bitmap_bit bit; + int rc; + + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + + control->free = 0; + search_map = control->search_bits; + size = control->size; + bit = 0; + + *search_map = RTEMS_RFS_BITMAP_ELEMENT_CLEAR; + while (size) + { + rtems_rfs_bitmap_element bits; + int available; + if (size < rtems_rfs_bitmap_element_bits ()) + { + bits = rtems_rfs_bitmap_merge (*map, + RTEMS_RFS_BITMAP_ELEMENT_SET, + rtems_rfs_bitmap_mask_section (0, size)); + available = size; + } + else + { + bits = *map; + available = rtems_rfs_bitmap_element_bits (); + } + + if (rtems_rfs_bitmap_match (bits, RTEMS_RFS_BITMAP_ELEMENT_SET)) + rtems_rfs_bitmap_set (*search_map, bit); + else + { + int b; + for (b = 0; b < available; b++) + if (!rtems_rfs_bitmap_test (bits, b)) + control->free++; + } + + size -= available; + + if (bit == rtems_rfs_bitmap_element_bits ()) + { + bit = 0; + search_map++; + *search_map = RTEMS_RFS_BITMAP_ELEMENT_CLEAR; + } + else + bit++; + map++; + } + + return 0; +} + +int +rtems_rfs_bitmap_open (rtems_rfs_bitmap_control* control, + rtems_rfs_file_system* fs, + rtems_rfs_buffer_handle* buffer, + size_t size, + rtems_rfs_buffer_block block) +{ + size_t elements = rtems_rfs_bitmap_elements (size); + + control->buffer = buffer; + control->fs = fs; + control->block = block; + control->size = size; + + elements = rtems_rfs_bitmap_elements (elements); + control->search_bits = malloc (elements * sizeof (rtems_rfs_bitmap_element)); + + if (!control->search_bits) + return ENOMEM; + + return rtems_rfs_bitmap_create_search (control); +} + +int +rtems_rfs_bitmap_close (rtems_rfs_bitmap_control* control) +{ + free (control->search_bits); + return 0; +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-bitmaps.h b/cpukit/libfs/src/rfs/rtems-rfs-bitmaps.h new file mode 100644 index 0000000000..50759b529d --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-bitmaps.h @@ -0,0 +1,303 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Bitmap Routines. + * + * These functions manage bit maps. A bit map consists of the map of bit + * allocated in a block and a search map where a bit represents 32 actual + * bits. The search map allows for a faster search for an available bit as 32 + * search bits can checked in a test. + */ + +#if !defined (_RTEMS_RFS_BITMAPS_H_) +#define _RTEMS_RFS_BITMAPS_H_ + +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-file-system-fwd.h> +#include <rtems/rfs/rtems-rfs-trace.h> + +/** + * Define the way the bits are configured. We can have them configured as clear + * being 0 or clear being 1. This does not effect how masks are defined. A mask + * always has a 1 for set and 0 for clear. + */ +#define RTEMS_RFS_BITMAP_CLEAR_ZERO 0 + +#if RTEMS_RFS_BITMAP_CLEAR_ZERO +/* + * Bit set is a 1 and clear is 0. + */ +#define RTEMS_RFS_BITMAP_BIT_CLEAR 0 +#define RTEMS_RFS_BITMAP_BIT_SET 1 +#define RTEMS_RFS_BITMAP_ELEMENT_SET (RTEMS_RFS_BITMAP_ELEMENT_FULL_MASK) +#define RTEMS_RFS_BITMAP_ELEMENT_CLEAR (0) +#define RTEMS_RFS_BITMAP_SET_BITS(_t, _b) ((_t) | (_b)) +#define RTEMS_RFS_BITMAP_CLEAR_BITS(_t, _b) ((_t) & ~(_b)) +#define RTEMS_RFS_BITMAP_TEST_BIT(_t, _b) (((_t) & (1 << (_b))) != 0 ? true : false) +#else +/* + * Bit set is a 0 and clear is 1. + */ +#define RTEMS_RFS_BITMAP_BIT_CLEAR 1 +#define RTEMS_RFS_BITMAP_BIT_SET 0 +#define RTEMS_RFS_BITMAP_ELEMENT_SET (0) +#define RTEMS_RFS_BITMAP_ELEMENT_CLEAR (RTEMS_RFS_BITMAP_ELEMENT_FULL_MASK) +#define RTEMS_RFS_BITMAP_SET_BITS(_t, _b) ((_t) & ~(_b)) +#define RTEMS_RFS_BITMAP_CLEAR_BITS(_t, _b) ((_t) | (_b)) +#define RTEMS_RFS_BITMAP_TEST_BIT(_t, _b) (((_t) & (1 << (_b))) == 0 ? true : false) +#endif + +/** + * Invert a mask. Masks are always 1 for set and 0 for clear. + */ +#define RTEMS_RFS_BITMAP_INVERT_MASK(_mask) (~(_mask)) + +/** + * This is the full mask of the length of the element. A mask is always a 1 for + * set and 0 for clear. It is not effected by the state of + * RTEMS_RFS_BITMAP_CLEAR_ZERO. + */ +#define RTEMS_RFS_BITMAP_ELEMENT_FULL_MASK (0xffffffffUL) + +/** + * The bitmap search window. Searches occur around a seed in either direction + * for half the window. + */ +#define RTEMS_RFS_BITMAP_SEARCH_WINDOW (rtems_rfs_bitmap_element_bits () * 64) + +/** + * A bit in a map. + */ +typedef int32_t rtems_rfs_bitmap_bit; + +/** + * The basic element of a bitmap. A bitmap is manipulated by elements. + */ +typedef uint32_t rtems_rfs_bitmap_element; + +/** + * The power of 2 number of bits in the element. + */ +#define RTEMS_RFS_ELEMENT_BITS_POWER_2 (5) + +/** + * A bitmap or map is an array of bitmap elements. + */ +typedef rtems_rfs_bitmap_element* rtems_rfs_bitmap_map; + +/** + * The bitmap control is a simple way to manage the various parts of a bitmap. + */ +typedef struct rtems_rfs_bitmap_control_s +{ + rtems_rfs_buffer_handle* buffer; //< Handle the to buffer with the bit + //map. + rtems_rfs_file_system* fs; //< The map's file system. + rtems_rfs_buffer_block block; //< The map's block number on disk. + size_t size; //< Number of bits in the map. Passed + //to create. + size_t free; //< Number of bits in the map that are + //free (clear). + rtems_rfs_bitmap_map search_bits; //< The search bit map memory. +} rtems_rfs_bitmap_control; + +/** + * Return the number of bits for the number of bytes provided. + */ +#define rtems_rfs_bitmap_numof_bits(_bytes) (8 * (_bytes)) + +/** + * Return the number of bits for the number of bytes provided. The search + * element and the element must have the same number of bits. + */ +#define rtems_rfs_bitmap_element_bits() \ + rtems_rfs_bitmap_numof_bits (sizeof (rtems_rfs_bitmap_element)) + +/** + * Return the number of bits a search element covers. + */ +#define rtems_rfs_bitmap_search_element_bits() \ + (rtems_rfs_bitmap_element_bits() * rtems_rfs_bitmap_element_bits()) + +/** + * Return the number of elements for a given number of bits. + */ +#define rtems_rfs_bitmap_elements(_bits) \ + ((((_bits) - 1) / rtems_rfs_bitmap_element_bits()) + 1) + +/** + * Release the bitmap buffer back to the buffer pool or cache. + */ +#define rtems_rfs_bitmap_release_buffer(_fs, _bm) \ + rtems_rfs_buffer_handle_release (_fs, (_bm)->buffer) + +/** + * Return the element index for a given bit. We use a macro to hide any + * implementation assuptions. Typically this would be calculated by dividing + * the bit index by the number of bits in an element. Given we have a power of + * 2 as the number of bits we can avoid the division by using a shift. A good + * compiler should figure this out but I would rather enforce this than rely on + * the specific backend of a compiler to do the right thing. + */ +#define rtems_rfs_bitmap_map_index(_b) \ + ((_b) >> RTEMS_RFS_ELEMENT_BITS_POWER_2) + +/** + * Return the bit offset for a given bit in an element in a map. See @ref + * rtems_rfs_bitmap_map_index for a detailed reason why. + */ +#define rtems_rfs_bitmap_map_offset(_b) \ + ((_b) & ((1 << RTEMS_RFS_ELEMENT_BITS_POWER_2) - 1)) + +/** + * Return the size of the bitmap. + */ +#define rtems_rfs_bitmap_map_size(_c) ((_c)->size) + +/** + * Return the number of free bits in the bitmap. + */ +#define rtems_rfs_bitmap_map_free(_c) ((_c)->free) + +/** + * Return the buffer handle. + */ +#define rtems_rfs_bitmap_map_handle(_c) ((_c)->buffer) + +/** + * Return the bitmap map block. + */ +#define rtems_rfs_bitmap_map_block(_c) ((_c)->block) + +/** + * Create a bit mask with the specified number of bits up to an element's + * size. The mask is aligned to bit 0 of the element. + * + * @param size The number of bits in the mask. + * @return The mask of the argument size number of bits. + */ +rtems_rfs_bitmap_element rtems_rfs_bitmap_mask (unsigned int size); + +/** + * Create a bit mask section. A mask section is a mask that is not aligned to + * an end of the element. + * + * @param start The first bit of the mask numbered from 0. + * @param end The end bit of the mask numbered from 0. + * @return Mask section as defined by the start and end arguments. + */ +rtems_rfs_bitmap_element rtems_rfs_bitmap_mask_section (unsigned int start, + unsigned int end); + +/** + * Set a bit in a map and if all the bits are set, set the search map bit as + * well. + * + * @param control The control for the map. + * @param bit The bit in the map to set. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_bitmap_map_set (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit bit); + +/** + * Clear a bit in a map and make sure the search map bit is clear so a search + * will find this bit available. + * + * @param control The control for the map. + * @param bit The bit in the map to clear. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_bitmap_map_clear (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit bit); + +/** + * Test a bit in the map. + * + * @param control The bitmap control. + * @param bit The bit to test. + * @param state The state of the bit if no error is returned. + * @return int The error number (errno). No error if 0. + */ +int +rtems_rfs_bitmap_map_test (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit bit, + bool* state); + +/** + * Set all bits in the bitmap and set the dirty bit. + * + * @param control The bitmap control. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_bitmap_map_set_all (rtems_rfs_bitmap_control* control); + +/** + * Clear all bits in the bitmap and set the dirty bit. + * + * @param control The bitmap control. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_bitmap_map_clear_all (rtems_rfs_bitmap_control* control); + +/** + * Find a free bit searching from the seed up and down until found. The search + * is performing by moving up from the seed for the window distance then to + * search down from the seed for the window distance. This is repeated out from + * the seed for each window until a free bit is found. The search is performed + * by checking the search map to see if the map has a free bit. + * + * @param control The map control. + * @param seed The bit to search out from. + * @param allocate A bit was allocated. + * @param bit Returns the bit found free if true is returned. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_bitmap_map_alloc (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit seed, + bool* allocate, + rtems_rfs_bitmap_bit* bit); + +/** + * Create a search bit map from the actual bit map. + * + * @param control The map control. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_bitmap_create_search (rtems_rfs_bitmap_control* control); + +/** + * Open a bitmap control with a map and search map. + * + * @param control The map control. + * @param fs The file system data. + * @param buffer The buffer handle the map is stored in. + * @param size The number of bits in the map. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_bitmap_open (rtems_rfs_bitmap_control* control, + rtems_rfs_file_system* fs, + rtems_rfs_buffer_handle* buffer, + size_t size, + rtems_rfs_buffer_block block); + +/** + * Close a bitmap. + * + * @param control The bit map control. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_bitmap_close (rtems_rfs_bitmap_control* control); + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-block-pos.h b/cpukit/libfs/src/rfs/rtems-rfs-block-pos.h new file mode 100644 index 0000000000..b15f0ba113 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-block-pos.h @@ -0,0 +1,239 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Block Position and Size Management. + * + * These functions manage the position in a block map as well as a size of data + * held in a block map. The position is the block count plus the offset into + * the last block where a block position of 0 and an offset of 0 is the start + * of a map. The size has a block count plus an offset, but the offset into the + * last block gives the actual size of the data in the map. This means a size + * will always have a block count greater than 0 when the file is not empty. A + * size offset of 0 and a non-zero block count means the length if aligned to + * the end of the block. For this reason there are 2 similar types so we know + * which set of rules are in use and the reason for this file. + */ + +#if !defined (_RTEMS_RFS_BLOCK_POS_H_) +#define _RTEMS_RFS_BLOCK_POS_H_ + +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> + +/** + * The block number is the same type as the inode block number. This makes sure + * the sizes of the types match. + */ +typedef rtems_rfs_inode_block rtems_rfs_block_no; + +/** + * The offset into a block. + */ +typedef uint32_t rtems_rfs_block_off; + +/** + * A block position is a block number times the block size plus the offset. The + * block field can be used hold a block number for the position as a look up + * cache. + */ +typedef struct rtems_rfs_block_pos_s +{ + /** + * The block index in the map. Range is from 0 to the maps block count minus + * 1. + */ + rtems_rfs_block_no bno; + + /** + * The offset into the block. Must be less than the block size. + */ + rtems_rfs_block_off boff; + + /** + * The block number that the bpos + boff map to. The 0 value is invalid and + * means no block number has been set. + */ + rtems_rfs_block_no block; + +} rtems_rfs_block_pos; + +/** + * Copy a block position. + * + * @param _lhs The left hand side. + * @param _rhs The right hand side. + */ +#define rtems_rfs_block_copy_bpos(_lhs, _rhs) \ + do { (_lhs)->bno = (_rhs)->bno; \ + (_lhs)->boff = (_rhs)->boff; \ + (_lhs)->block = (_rhs)->block; } while (0) + +/** + * Zero a block position. + * + * @param bpos A pointer to the block position. + */ +static inline void +rtems_rfs_block_set_bpos_zero (rtems_rfs_block_pos* bpos) +{ + bpos->bno = 0; + bpos->boff = 0; + bpos->block = 0; +} + +/** + * Given a position compute the block number and block offset. + * + * @param fs The file system data. + * @param pos The position as an absolute offset from the start. + * @param bpos Pointer to the block position to fill in. + */ +void rtems_rfs_block_get_bpos (rtems_rfs_file_system* fs, + rtems_rfs_pos pos, + rtems_rfs_block_pos* bpos); + +/** + * Given a block position compute the absolute offset. + * + * @param fs The file system data. + * @param bpos Pointer to the block position to fill in. + * @return rtems_rfs_pos The absolute offset. + */ +rtems_rfs_pos rtems_rfs_block_get_pos (rtems_rfs_file_system* fs, + rtems_rfs_block_pos* bpos); + +/** + * Add the relative position to the block position. The relative position is + * signed. + * + * @param fs The file system data. + * @param offset The relative offset add to the block position. + * @param bpos Pointer to the block position to fill in. + */ +static inline void +rtems_rfs_block_add_pos (rtems_rfs_file_system* fs, + rtems_rfs_pos_rel offset, + rtems_rfs_block_pos* bpos) +{ + rtems_rfs_block_get_bpos (fs, + rtems_rfs_block_get_pos (fs, bpos) + offset, + bpos); + bpos->block = 0; +} + +/** + * A block size is the number of blocks less one plus the offset where the + * offset must be less than the block size. + */ +typedef struct rtems_rfs_block_size_s +{ + /** + * The count of blocks in a map. A 0 means no blocks and a zero length and + * the offset should also be 0. + */ + rtems_rfs_block_no count; + + /** + * The offset into the block. An offset of 0 means block size, ie the first + * byte of the next block which is not allocated. + */ + rtems_rfs_block_off offset; + +} rtems_rfs_block_size; + +/** + * Copy a block size. + * + * @param _lhs The left hand side. + * @param _rhs The right hand side. + */ +#define rtems_rfs_block_copy_size(_lhs, _rhs) \ + do { (_lhs)->count = (_rhs)->count; \ + (_lhs)->offset = (_rhs)->offset; } while (0) + +/** + * Last block ? + */ +#define rtems_rfs_block_pos_last_block(_p, _s) \ + ((((_p)->bno == 0) && ((_s)->count == 0)) || ((_p)->bno == ((_s)->count - 1))) + +/** + * Last block ? + */ +#define rtems_rfs_block_pos_past_end(_p, _s) \ + (((_p)->bno && ((_s)->count == 0)) || \ + ((_p)->bno >= (_s)->count) || \ + (((_p)->bno == ((_s)->count - 1)) && ((_p)->boff > (_s)->offset))) + +/** + * Is the block position past the end. + */ +#define rtems_rfs_block_pos_block_past_end(_p, _s) \ + (((_p)->bno && ((_s)->count == 0)) || ((_p)->bno >= (_s)->count)) + +/** + * Copy the size to the block position. Note the block position and the size + * have different block counts. + */ +#define rtems_rfs_block_size_get_bpos(_s, _b) \ + do { (_b)->bno = (_s)->count; \ + (_b)->boff = (_s)->offset; \ + (_b)->block = 0; \ + if ((_b)->boff) --(_b)->bno; } while (0) + +/** + * Do the sizes match ? + */ +#define rtems_rfs_block_size_equal(_lhs, _rhs) \ + (((_lhs)->count == (_rhs)->count) && ((_lhs)->offset == (_rhs)->offset)) + +/** + * Zero a block size. + * + * @param size A pointer to the block size. + */ +static inline void +rtems_rfs_block_set_size_zero (rtems_rfs_block_size* size) +{ + size->count = 0; + size->offset = 0; +} + +/** + * Set the size given a position. + * + * @param fs The file system data. + * @param pos The position as an absolute offset from the start. + * @param size Pointer to the block size to fill in. + */ +void rtems_rfs_block_get_block_size (rtems_rfs_file_system* fs, + rtems_rfs_pos pos, + rtems_rfs_block_size* size); + +/** + * Calculate the position given the number of blocks and the offset. If the + * block count is 0 the size is 0. If the block is greater than 0 and the + * offset is 0 the size is number of blocks multipled by the block size and if + * the offset is not 0 it is the offset into the last block. For example if + * blocks is 1 and offset is 0 the size is the block size. If the block count + * is 1 and size is 100 the size is 100. + * + * @param fs The file system data. + * @param size The size in blocks and offset. + * @return rtems_rfs_pos The size in bytes. + */ +rtems_rfs_pos rtems_rfs_block_get_size (rtems_rfs_file_system* fs, + rtems_rfs_block_size* size); + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-block.c b/cpukit/libfs/src/rfs/rtems-rfs-block.c new file mode 100644 index 0000000000..776c7f314d --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-block.c @@ -0,0 +1,800 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Block Routines. + * + * These functions manage blocks in the RFS file system. A block is an area of + * the media and its size is set for a each specific media. The block size is + * set when the file system is set up and needs to be matched for it to be read + * correctly. + * + * Blocks are managed as groups. A block group or "group" is part of the total + * number of blocks being managed by the file system and exist to allow + * resources to localised. A file in a directory will be allocated blocks in + * the same group as the directory, and the blocks for the file will also be + * allocated in the same group. + * + * A group consist of a block bitmap, inodes and data blocks. The first block + * of the file system will hold the superblock. The block bitmap is a + * collection of blocks that hold a map of bits, one bit per block for each + * block in the group. When a file system is mounted the block bitmaps are read + * and a summary bit map is made. The summary bitmap has a single bit for 32 + * bits in the bitmap and is set when all 32 bits it maps to are set. This + * speeds up the search for a free block by a factor of 32. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <rtems/rfs/rtems-rfs-block.h> +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-group.h> +#include <rtems/rfs/rtems-rfs-inode.h> + +void +rtems_rfs_block_get_bpos (rtems_rfs_file_system* fs, + rtems_rfs_pos pos, + rtems_rfs_block_pos* bpos) +{ + bpos->bno = pos / rtems_rfs_fs_block_size (fs); + bpos->boff = pos % rtems_rfs_fs_block_size (fs); +} + +rtems_rfs_pos +rtems_rfs_block_get_pos (rtems_rfs_file_system* fs, + rtems_rfs_block_pos* bpos) +{ + return (bpos->bno * rtems_rfs_fs_block_size (fs)) + bpos->boff; +} + +void +rtems_rfs_block_get_block_size (rtems_rfs_file_system* fs, + rtems_rfs_pos pos, + rtems_rfs_block_size* size) +{ + if (pos == 0) + rtems_rfs_block_set_size_zero (size); + else + { + size->count = pos / rtems_rfs_fs_block_size (fs) + 1; + size->offset = pos % rtems_rfs_fs_block_size (fs); + } +} + +rtems_rfs_pos +rtems_rfs_block_get_size (rtems_rfs_file_system* fs, + rtems_rfs_block_size* size) +{ + uint32_t offset; + uint64_t block_size; + if (size->count == 0) + return 0; + if (size->offset == 0) + offset = rtems_rfs_fs_block_size (fs); + else + offset = size->offset; + block_size = rtems_rfs_fs_block_size (fs); + return (((uint64_t) (size->count - 1)) * block_size) + offset; +} + +int +rtems_rfs_block_map_open (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* inode, + rtems_rfs_block_map* map) +{ + int b; + int rc; + + /* + * Set the count to 0 so at least find fails, then open the handle and make + * sure the inode has been loaded into memory. If we did not load the inode + * do not unload it. The caller may assume it is still loaded when we return. + */ + + map->dirty = false; + map->inode = NULL; + rtems_rfs_block_set_size_zero (&map->size); + rtems_rfs_block_set_bpos_zero (&map->bpos); + + rc = rtems_rfs_buffer_handle_open (fs, &map->singly_buffer); + if (rc > 0) + return rc; + rc = rtems_rfs_buffer_handle_open (fs, &map->doubly_buffer); + if (rc > 0) + return rc; + + rc = rtems_rfs_inode_load (fs, inode); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &map->singly_buffer); + rtems_rfs_buffer_handle_close (fs, &map->doubly_buffer); + return rc; + } + + /* + * Extract the block and block count data from the inode into the targets + * byte order. + */ + map->inode = inode; + for (b = 0; b < RTEMS_RFS_INODE_BLOCKS; b++) + map->blocks[b] = rtems_rfs_inode_get_block (inode, b); + map->size.count = rtems_rfs_inode_get_block_count (inode); + map->size.offset = rtems_rfs_inode_get_block_offset (inode); + map->last_map_block = rtems_rfs_inode_get_last_map_block (inode); + map->last_data_block = rtems_rfs_inode_get_last_data_block (inode); + + rc = rtems_rfs_inode_unload (fs, inode, false); + + return rc; +} + +int +rtems_rfs_block_map_close (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map) +{ + int rc = 0; + int brc; + + if (map->dirty && map->inode) + { + brc = rtems_rfs_inode_load (fs, map->inode); + if (brc > 0) + rc = brc; + + if (rc == 0) + { + int b; + + for (b = 0; b < RTEMS_RFS_INODE_BLOCKS; b++) + rtems_rfs_inode_set_block (map->inode, b, map->blocks[b]); + rtems_rfs_inode_set_block_count (map->inode, map->size.count); + rtems_rfs_inode_set_block_offset (map->inode, map->size.offset); + rtems_rfs_inode_set_last_map_block (map->inode, map->last_map_block); + rtems_rfs_inode_set_last_data_block (map->inode, map->last_data_block); + + brc = rtems_rfs_inode_unload (fs, map->inode, true); + if (brc > 0) + rc = brc; + + map->dirty = false; + } + } + + map->inode = NULL; + + brc = rtems_rfs_buffer_handle_close (fs, &map->singly_buffer); + if ((brc > 0) && (rc == 0)) + rc = brc; + brc = rtems_rfs_buffer_handle_close (fs, &map->doubly_buffer); + if ((brc > 0) && (rc == 0)) + rc = brc; + return rc; +} + +/** + * Find a block indirectly held in a table of block numbers. + * + * @param fs The file system. + * @param buffer The handle to access the block data by. + * @param block The block number of the table of block numbers. + * @param offset The offset in the table of the block number to return. This is + * a block number offset not a byte offset into the table. + * @param result Pointer to the result of the search. + * @return int The error number (errno). No error if 0. + */ +static int +rtems_rfs_block_find_indirect (rtems_rfs_file_system* fs, + rtems_rfs_buffer_handle* buffer, + rtems_rfs_block_no block, + int offset, + rtems_rfs_block_no* result) +{ + int rc; + + /* + * If the handle has a buffer and this request is a different block the current + * buffer is released. + */ + rc = rtems_rfs_buffer_handle_request (fs, buffer, block, true); + if (rc > 0) + return rc; + + *result = rtems_rfs_block_get_number (buffer, offset); + if ((*result + 1) == 0) + *result = 0; + + if (*result >= rtems_rfs_fs_blocks (fs)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BLOCK_FIND)) + printf ("rtems-rfs: block-find: invalid block in table:" + " block=%" PRId32 ", indirect=%" PRId32 "/%d\n", *result, block, offset); + *result = 0; + rc = EIO; + } + + return 0; +} + +int +rtems_rfs_block_map_find (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map, + rtems_rfs_block_pos* bpos, + rtems_rfs_block_no* block) +{ + int rc = 0; + + *block = 0; + + /* + * Range checking here makes the remaining logic simpler. + */ + if (rtems_rfs_block_pos_block_past_end (bpos, &map->size)) + return ENXIO; + + /* + * If the block position is the same and we have found the block just return it. + */ + if ((bpos->bno == map->bpos.bno) && (map->bpos.block != 0)) + { + *block = map->bpos.block; + } + else + { + /* + * Determine the type of access we need to perform. If the number of blocks + * is less than or equal to the number of slots in the inode the blocks are + * directly accessed. + */ + if (map->size.count <= RTEMS_RFS_INODE_BLOCKS) + { + *block = map->blocks[bpos->bno]; + } + else + { + /* + * The map is either singly or doubly indirect. + */ + rtems_rfs_block_no direct; + rtems_rfs_block_no singly; + + direct = bpos->bno % fs->blocks_per_block; + singly = bpos->bno / fs->blocks_per_block; + + if (map->size.count <= fs->block_map_singly_blocks) + { + /* + * This is a single indirect table of blocks anchored off a slot in the + * inode. + */ + rc = rtems_rfs_block_find_indirect (fs, + &map->singly_buffer, + map->blocks[singly], + direct, block); + } + else + { + /* + * The map is doubly indirect. + */ + rtems_rfs_block_no doubly; + + doubly = singly / fs->blocks_per_block; + singly %= fs->blocks_per_block; + + if (map->size.count < fs->block_map_doubly_blocks) + { + rc = rtems_rfs_block_find_indirect (fs, + &map->doubly_buffer, + map->blocks[doubly], + singly, &singly); + if (rc == 0) + { + rc = rtems_rfs_block_find_indirect (fs, + &map->singly_buffer, + singly, direct, block); + } + } + else + { + /* + * This should never happen. Here so Joel can remove once his coverage + * testing gets to the file systems. + */ + rc = ENXIO; + } + } + } + } + + if (rc == 0) + { + rtems_rfs_block_copy_bpos (&map->bpos, bpos); + map->bpos.block = *block; + } + + return rc; +} + +int +rtems_rfs_block_map_seek (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map, + rtems_rfs_pos_rel offset, + rtems_rfs_block_no* block) +{ + rtems_rfs_block_pos bpos; + rtems_rfs_block_copy_bpos (&bpos, &map->bpos); + rtems_rfs_block_add_pos (fs, offset, &bpos); + return rtems_rfs_block_map_find (fs, map, &bpos, block); +} + +int +rtems_rfs_block_map_next_block (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map, + rtems_rfs_block_no* block) +{ + rtems_rfs_block_pos bpos; + bpos.bno = map->bpos.bno + 1; + bpos.boff = 0; + bpos.block = 0; + return rtems_rfs_block_map_find (fs, map, &bpos, block); +} + +/** + * Allocate an indirect block to a map. + * + * @param fs The file system data. + * @param map The map the allocation is for. + * @param buffer The buffer the indirect block is accessed by. + * @param block The block number of the indirect block allocated. + * @param upping True is upping the map to the next indirect level. + * @return int The error number (errno). No error if 0. + */ +static int +rtems_rfs_block_map_indirect_alloc (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map, + rtems_rfs_buffer_handle* buffer, + rtems_rfs_block_no* block, + bool upping) +{ + rtems_rfs_bitmap_bit new_block; + int rc; + /* + * Save the new block locally because upping can have *block pointing to the + * slots which are cleared when upping. + */ + rc = rtems_rfs_group_bitmap_alloc (fs, map->last_map_block, false, &new_block); + if (rc > 0) + return rc; + rc = rtems_rfs_buffer_handle_request (fs, buffer, new_block, false); + if (rc > 0) + { + rtems_rfs_group_bitmap_free (fs, false, new_block); + return rc; + } + memset (rtems_rfs_buffer_data (buffer), 0xff, rtems_rfs_fs_block_size (fs)); + if (upping) + { + int b; + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BLOCK_MAP_GROW)) + printf ("rtems-rfs: block-map-grow: upping: block-count=%" PRId32 "\n", + map->size.count); + for (b = 0; b < RTEMS_RFS_INODE_BLOCKS; b++) + rtems_rfs_block_set_number (buffer, b, map->blocks[b]); + memset (map->blocks, 0, sizeof (map->blocks)); + } + rtems_rfs_buffer_mark_dirty (buffer); + *block = new_block; + map->last_map_block = new_block; + return 0; +} + +int +rtems_rfs_block_map_grow (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map, + size_t blocks, + rtems_rfs_block_no* new_block) +{ + int b; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BLOCK_MAP_GROW)) + printf ("rtems-rfs: block-map-grow: entry: blocks=%zd count=%" PRIu32 "\n", + blocks, map->size.count); + + if ((map->size.count + blocks) >= rtems_rfs_fs_max_block_map_blocks (fs)) + return EFBIG; + + /* + * Allocate a block at a time. The buffer handles hold the blocks so adding + * this way does not thrash the cache with lots of requests. + */ + for (b = 0; b < blocks; b++) + { + rtems_rfs_bitmap_bit block; + int rc; + + /* + * Allocate the block. If an indirect block is needed and cannot be + * allocated free this block. + */ + + rc = rtems_rfs_group_bitmap_alloc (fs, map->last_data_block, + false, &block); + if (rc > 0) + return rc; + + if (map->size.count < RTEMS_RFS_INODE_BLOCKS) + map->blocks[map->size.count] = block; + else + { + /* + * Single indirect access is occuring. It could still be doubly indirect. + */ + rtems_rfs_block_no direct; + rtems_rfs_block_no singly; + + direct = map->size.count % fs->blocks_per_block; + singly = map->size.count / fs->blocks_per_block; + + if (map->size.count < fs->block_map_singly_blocks) + { + /* + * Singly indirect tables are being used. Allocate a new block for a + * mapping table if direct is 0 or we are moving up (upping). If upping + * move the direct blocks into the table and if not this is the first + * entry of a new block. + */ + if ((direct == 0) || + ((singly == 0) && (direct == RTEMS_RFS_INODE_BLOCKS))) + { + /* + * Upping is when we move from direct to singly indirect. + */ + bool upping; + upping = map->size.count == RTEMS_RFS_INODE_BLOCKS; + rc = rtems_rfs_block_map_indirect_alloc (fs, map, + &map->singly_buffer, + &map->blocks[singly], + upping); + } + else + { + rc = rtems_rfs_buffer_handle_request (fs, &map->singly_buffer, + map->blocks[singly], true); + } + + if (rc > 0) + { + rtems_rfs_group_bitmap_free (fs, false, block); + return rc; + } + } + else + { + /* + * Doubly indirect tables are being used. + */ + rtems_rfs_block_no doubly; + rtems_rfs_block_no singly_block; + + doubly = singly / fs->blocks_per_block; + singly %= fs->blocks_per_block; + + /* + * Allocate a new block for a singly indirect table if direct is 0 as + * it is the first entry of a new block. We may also need to allocate a + * doubly indirect block as well. Both always occur when direct is 0 + * and the doubly indirect block when singly is 0. + */ + if (direct == 0) + { + rc = rtems_rfs_block_map_indirect_alloc (fs, map, + &map->singly_buffer, + &singly_block, + false); + if (rc > 0) + { + rtems_rfs_group_bitmap_free (fs, false, block); + return rc; + } + + /* + * Allocate a new block for a doubly indirect table if singly is 0 as + * it is the first entry of a new singly indirect block. + */ + if ((singly == 0) || + ((doubly == 0) && (singly == RTEMS_RFS_INODE_BLOCKS))) + { + bool upping; + upping = map->size.count == fs->block_map_singly_blocks; + rc = rtems_rfs_block_map_indirect_alloc (fs, map, + &map->doubly_buffer, + &map->blocks[doubly], + upping); + if (rc > 0) + { + rtems_rfs_group_bitmap_free (fs, false, singly_block); + rtems_rfs_group_bitmap_free (fs, false, block); + return rc; + } + } + else + { + rc = rtems_rfs_buffer_handle_request (fs, &map->doubly_buffer, + map->blocks[doubly], true); + if (rc > 0) + { + rtems_rfs_group_bitmap_free (fs, false, singly_block); + rtems_rfs_group_bitmap_free (fs, false, block); + return rc; + } + } + + rtems_rfs_block_set_number (&map->doubly_buffer, + singly, + singly_block); + } + else + { + rc = rtems_rfs_buffer_handle_request (fs, + &map->doubly_buffer, + map->blocks[doubly], + true); + if (rc > 0) + { + rtems_rfs_group_bitmap_free (fs, false, block); + return rc; + } + + singly_block = rtems_rfs_block_get_number (&map->doubly_buffer, + singly); + + rc = rtems_rfs_buffer_handle_request (fs, &map->singly_buffer, + singly_block, true); + if (rc > 0) + { + rtems_rfs_group_bitmap_free (fs, false, block); + return rc; + } + } + } + + rtems_rfs_block_set_number (&map->singly_buffer, direct, block); + } + + map->size.count++; + map->size.offset = 0; + + if (b == 0) + *new_block = block; + map->last_data_block = block; + map->dirty = true; + } + + return 0; +} + +/** + * Shrink an indirect block. + * + * @param fs The file system data. + * @param map The map the allocation is for. + * @param buffer The buffer the indirect block is accessed by. + * @param indirect The index index in the inode's block table. + * @param index The index in the indirect table of the block. + * @return int The error number (errno). No error if 0. + */ +static int +rtems_rfs_block_map_indirect_shrink (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map, + rtems_rfs_buffer_handle* buffer, + rtems_rfs_block_no indirect, + rtems_rfs_block_no index) +{ + int rc = 0; + + /* + * If this is the first block in the indirect table (index == 0), ie the last + * block to be freed and the indirect block is now also free, or we have only + * one indirect table and we can fit the remaining blocks into the inode, + * then either move to the next indirect block or move the remaining blocks + * into the inode and free the indirect table's block. + */ + if ((index == 0) || + ((indirect == 0) && (index == RTEMS_RFS_INODE_BLOCKS))) + { + rtems_rfs_block_no block_to_free = map->blocks[indirect]; + + if ((indirect == 0) && (index == RTEMS_RFS_INODE_BLOCKS)) + { + /* + * Move to direct inode access. + */ + int b; + for (b = 0; b < RTEMS_RFS_INODE_BLOCKS; b++) + map->blocks[b] = rtems_rfs_block_get_number (buffer, b); + } + else + { + /* + * One less singly indirect block in the inode. + */ + map->blocks[indirect] = 0; + } + + rc = rtems_rfs_group_bitmap_free (fs, false, block_to_free); + if (rc > 0) + return rc; + + map->last_map_block = block_to_free; + } + + return rc; +} + +int +rtems_rfs_block_map_shrink (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map, + size_t blocks) +{ + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BLOCK_MAP_SHRINK)) + printf ("rtems-rfs: block-map-shrink: entry: blocks=%zd count=%" PRIu32 "\n", + blocks, map->size.count); + + if (map->size.count == 0) + return 0; + + if (blocks > map->size.count) + blocks = map->size.count; + + while (blocks) + { + rtems_rfs_block_no block; + rtems_rfs_block_no block_to_free; + int rc; + + block = map->size.count - 1; + + if (block < RTEMS_RFS_INODE_BLOCKS) + { + /* + * We have less than RTEMS_RFS_INODE_BLOCKS so they are held in the + * inode. + */ + block_to_free = map->blocks[block]; + map->blocks[block] = 0; + } + else + { + /* + * Single indirect access is occuring. It could still be doubly indirect. + * + * The 'direct' variable is the offset in to the indirect table of + * blocks, and 'singly' is the inode block index of the singly indirect + * table of block numbers. + */ + rtems_rfs_block_no direct; + rtems_rfs_block_no singly; + + direct = block % fs->blocks_per_block; + singly = block / fs->blocks_per_block; + + if (block < fs->block_map_singly_blocks) + { + /* + * Request the indirect block and then obtain the block number from the + * indirect block. + */ + rc = rtems_rfs_buffer_handle_request (fs, &map->singly_buffer, + map->blocks[singly], true); + if (rc > 0) + return rc; + + block_to_free = rtems_rfs_block_get_number (&map->singly_buffer, + direct); + + rc = rtems_rfs_block_map_indirect_shrink (fs, map, &map->singly_buffer, + singly, direct); + if (rc) + return rc; + } + else if (block < fs->block_map_doubly_blocks) + { + /* + * Doubly indirect tables are being used. The 'doubly' variable is the + * index in to the inode's block table and points to a singly indirect + * table of block numbers. The 'doubly_singly' variable is the index + * into the doubly indirect table pointing to the singly indirect table + * of block numbers that form the map. This is used later to determine + * if the current doubly indirect table needs to be freed. The 'direct' + * value is still valid for doubly indirect tables. + */ + rtems_rfs_block_no doubly; + rtems_rfs_block_no doubly_singly; + + doubly = singly / fs->blocks_per_block; + doubly_singly = singly % fs->blocks_per_block; + + rc = rtems_rfs_buffer_handle_request (fs, &map->doubly_buffer, + map->blocks[doubly], true); + if (rc > 0) + return rc; + + singly = rtems_rfs_block_get_number (&map->doubly_buffer, + doubly_singly); + + /* + * Read the singly indirect table and get the block number. + */ + rc = rtems_rfs_buffer_handle_request (fs, &map->singly_buffer, + singly, true); + if (rc > 0) + return rc; + + block_to_free = rtems_rfs_block_get_number (&map->singly_buffer, + direct); + + if (direct == 0) + { + rc = rtems_rfs_group_bitmap_free (fs, false, singly); + if (rc > 0) + return rc; + + map->last_map_block = singly; + + rc = rtems_rfs_block_map_indirect_shrink (fs, map, &map->doubly_buffer, + doubly, doubly_singly); + if (rc) + return rc; + } + } + else + { + rc = EIO; + break; + } + } + rc = rtems_rfs_group_bitmap_free (fs, false, block_to_free); + if (rc > 0) + return rc; + map->size.count--; + map->size.offset = 0; + map->last_data_block = block_to_free; + map->dirty = true; + blocks--; + } + + if (map->size.count == 0) + { + map->last_map_block = 0; + map->last_data_block = 0; + } + + /* + * Keep the position inside the map. + */ + if (rtems_rfs_block_pos_past_end (&map->bpos, &map->size)) + rtems_rfs_block_size_get_bpos (&map->size, &map->bpos); + + return 0; +} + +int +rtems_rfs_block_map_free_all (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map) +{ + return rtems_rfs_block_map_shrink (fs, map, map->size.count); +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-block.h b/cpukit/libfs/src/rfs/rtems-rfs-block.h new file mode 100644 index 0000000000..e4e3f95830 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-block.h @@ -0,0 +1,324 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Block Management. + * + * These functions manage the blocks used in the file system. + */ + +#if !defined (_RTEMS_RFS_BLOCK_H_) +#define _RTEMS_RFS_BLOCK_H_ + +#include <rtems/rfs/rtems-rfs-block-pos.h> +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-file-system.h> + +/** + * Get a block number in the media format and return it in the host format. + * + * @param _h The buffer handle of the block. + * @param _b The block number index. + * @return uint32_t The block number. + */ +#define rtems_rfs_block_get_number(_h, _b) \ + ((rtems_rfs_block_no) \ + (rtems_rfs_read_u32 (rtems_rfs_buffer_data (_h) + \ + ((_b) * sizeof (rtems_rfs_block_no))))) + +/** + * Set a block number in the media format given a number in the host format. + * + * @param _h The buffer handle of the block. + * @param _b The block number index, ie the number of block number not the + * buffer offset. + * @param _n The block number. + */ +#define rtems_rfs_block_set_number(_h, _b, _n) \ + do { \ + rtems_rfs_write_u32 (rtems_rfs_buffer_data (_h) + \ + ((_b) * sizeof (rtems_rfs_block_no)), (_n)); \ + rtems_rfs_buffer_mark_dirty (_h); \ + } while (0) + +/** + * A block map manges the block lists that originate from an inode. The inode + * contains a number of block numbers. A block map takes those block numbers + * and manages them. + * + * The blocks cannot have all ones as a block number nor block 0. The block map + * is series of block numbers in a blocks. The size of the map determines the + * way the block numbers are stored. The map uses the following: + * + * @li @e Direct Access, + * @li @e Single Indirect Access, and + * @li @e Double Indirect Access. + * + * Direct access has the blocks numbers in the inode slots. The Single Indirect + * Access has block numbers in the inode slots that pointer to a table of block + * numbers that point to data blocks. The Double Indirect Access has block + * numbers in the inode that point to Single Indirect block tables. + * + * The inode can hold a number of Direct, Single Indirect, and Double Indirect + * block tables. The move from Direct to Single occurs then the block count in + * the map is above the number of slots in the inode. The move from Single to + * Double occurs when the map block count is greated than the block numbers per + * block multipled by the slots in the inode. The move from Single to Double + * occurs when the map block count is over the block numbers per block squared + * multipled by the number of slots in the inode. + * + * The block map can managed files of the follow size verses block size with 5 + * inode slots: + * + * @li 41,943,040 bytes for a 512 byte block size, + * @li 335,544,320 bytes for a 1024 byte block size, + * @li 2,684,354,560 bytes for a 2048 byte block size, and + * @li 21,474,836,480 bytes for a 4096 byte block size. + */ +typedef struct rtems_rfs_block_map_s +{ + /** + * Is the map dirty ? + */ + bool dirty; + + /** + * The inode this map is attached to. + */ + rtems_rfs_inode_handle* inode; + + /** + * The size of the map. + */ + rtems_rfs_block_size size; + + /** + * The block map position. Used to navigate the map when seeking. The find + * call is to a position in the file/directory and is a block number plus + * offset. The block find only needs to locate a block and not worry about + * the offset while a seek can be less than a block size yet move across a + * block boundary. Therefore the position a block map has to maintain must + * include the offset so seeks work. + */ + rtems_rfs_block_pos bpos; + + /** + * The last map block allocated. This is used as the goal when allocating a + * new map block. + */ + rtems_rfs_block_no last_map_block; + + /** + * The last data block allocated. This is used as the goal when allocating a + * new data block. + */ + rtems_rfs_block_no last_data_block; + + /** + * The block map. + */ + uint32_t blocks[RTEMS_RFS_INODE_BLOCKS]; + + /** + * Singly Buffer handle. + */ + rtems_rfs_buffer_handle singly_buffer; + + /** + * Doubly Buffer handle. + */ + rtems_rfs_buffer_handle doubly_buffer; + +} rtems_rfs_block_map; + +/** + * Is the map dirty ? + */ +#define rtems_rfs_block_map_is_dirty(_m) ((_m)->dirty) + +/** + * Return the block count in the map. + */ +#define rtems_rfs_block_map_count(_m) ((_m)->size.count) + +/** + * Return the map's size element. + */ +#define rtems_rfs_block_map_size(_m) (&((_m)->size)) + +/** + * Return the size offset for the map. + */ +#define rtems_rfs_block_map_size_offset(_m) ((_m)->size.offset) + +/** + * Are we at the last block in the map ? + */ +#define rtems_rfs_block_map_last(_m) \ + rtems_rfs_block_pos_last_block (&(_m)->bpos, &(_m)->size) + +/** + * Is the position past the end of the block ? + */ +#define rtems_rfs_block_map_past_end(_m, _p) \ + rtems_rfs_block_pos_past_end (_p, &(_m)->size) + +/** + * Return the current position in the map. + */ +#define rtems_rfs_block_map_pos(_f, _m) \ + rtems_rfs_block_get_pos (_f, &(_m)->bpos) + +/** + * Return the map's current block number. + */ +#define rtems_rfs_block_map_block(_m) ((_m)->bpos.bno) + +/** + * Return the map's current block offset. + */ +#define rtems_rfs_block_map_block_offset(_m) ((_m)->bpos.boff) + +/** + * Set the size offset for the map. The map is tagged as dirty. + * + * @param map Pointer to the open map to set the offset in. + * @param offset The offset to set in the map's size. + */ +static inline void +rtems_rfs_block_map_set_size_offset (rtems_rfs_block_map* map, + rtems_rfs_block_off offset) +{ + map->size.offset = offset; + map->dirty = true; +} + +/** + * Set the map's size. The map is tagged as dirty. + * + * @param map Pointer to the open map to set the offset in. + * @param size The size to set in the map's size. + */ +static inline void +rtems_rfs_block_map_set_size (rtems_rfs_block_map* map, + rtems_rfs_block_size* size) +{ + rtems_rfs_block_copy_size (&map->size, size); + map->dirty = true; +} +/** + * Open a block map. The block map data in the inode is copied into the + * map. The buffer handles are opened. The block position is set to the start + * so a seek of offset 0 will return the first block. + * + * @param fs The file system data. + * @param inode The inode the map belongs to. + * @param map The map that is opened. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_block_map_open (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* inode, + rtems_rfs_block_map* map); + +/** + * Close the map. The buffer handles are closed and any help buffers are + * released. + * + * @param fs The file system data. + * @param map The map that is opened. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_block_map_close (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map); + +/** + * Find a block number in the map from the position provided. + * + * @param fs The file system data. + * @param map The map to search. + * @param bpos The block position to find. + * @param block Pointer to place the block in when found. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_block_map_find (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map, + rtems_rfs_block_pos* bpos, + rtems_rfs_buffer_block* block); + +/** + * Seek around the map. + * + * @param fs The file system data. + * @param map The map to search. + * @param offset The distance to seek. It is signed. + * @param block Pointer to place the block in when found. + * @retval ENXIO Failed to seek because it is outside the block map. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_block_map_seek (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map, + rtems_rfs_pos_rel offset, + rtems_rfs_buffer_block* block); + +/** + * Seek to the next block. + * + * @param fs The file system data. + * @param map The map to search. + * @param block Pointer to place the block in when found. + * @retval ENXIO Failed to seek because it is outside the block map. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_block_map_next_block (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map, + rtems_rfs_buffer_block* block); + +/** + * Grow the block map by the specified number of blocks. + * + * @param fs The file system data. + * @param map Pointer to the open map to grow. + * @param blocks The number of blocks to grow the map by. + * @param new_block The first of the blocks allocated to the map. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_block_map_grow (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map, + size_t blocks, + rtems_rfs_block_no* new_block); + +/** + * Grow the block map by the specified number of blocks. + * + * @param fs The file system data. + * @param map Pointer to the open map to shrink. + * @param blocks The number of blocks to shrink the map by. If more than the + * number of blocks the map is emptied. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_block_map_shrink (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map, + size_t blocks); + +/** + * Free all blocks in the map. + * + * @param fs The file system data. + * @param map Pointer to the open map to free all blocks from. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_block_map_free_all (rtems_rfs_file_system* fs, + rtems_rfs_block_map* map); + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-buffer-bdbuf.c b/cpukit/libfs/src/rfs/rtems-rfs-buffer-bdbuf.c new file mode 100644 index 0000000000..536b136ed6 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-buffer-bdbuf.c @@ -0,0 +1,92 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Buffer Routines for the RTEMS libblock BD buffer cache. + * + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> +#include <errno.h> + +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-file-system.h> + +#if RTEMS_RFS_USE_LIBBLOCK + +/** + * Show errors. + */ +#define RTEMS_RFS_BUFFER_ERRORS 0 + +int +rtems_rfs_buffer_bdbuf_request (rtems_rfs_file_system* fs, + rtems_rfs_buffer_block block, + bool read, + rtems_rfs_buffer** buffer) +{ + rtems_status_code sc; + int rc = 0; + + if (read) + sc = rtems_bdbuf_read (rtems_rfs_fs_device (fs), block, buffer); + else + sc = rtems_bdbuf_get (rtems_rfs_fs_device (fs), block, buffer); + + if (sc != RTEMS_SUCCESSFUL) + { +#if RTEMS_RFS_BUFFER_ERRORS + printf ("rtems-rfs: buffer-bdbuf-request: block=%lu: bdbuf-%s: %d: %s\n", + block, read ? "read" : "get", sc, rtems_status_text (sc)); +#endif + rc = EIO; + } + + return rc; +} + +int +rtems_rfs_buffer_bdbuf_release (rtems_rfs_buffer* buffer, + bool modified) +{ + rtems_status_code sc; + int rc = 0; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_RELEASE)) + printf ("rtems-rfs: bdbuf-release: block=%" PRIuPTR " bdbuf=%" PRIu32 " %s\n", + ((intptr_t) buffer->user), + buffer->block, modified ? "(modified)" : ""); + + if (modified) + sc = rtems_bdbuf_release_modified (buffer); + else + sc = rtems_bdbuf_release (buffer); + + if (sc != RTEMS_SUCCESSFUL) + { +#if RTEMS_RFS_BUFFER_ERRORS + printf ("rtems-rfs: buffer-release: bdbuf-%s: %s(%d)\n", + modified ? "modified" : "not-modified", + rtems_status_text (sc), sc); +#endif + rc = EIO; + } + + return rc; +} + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-buffer-devio.c b/cpukit/libfs/src/rfs/rtems-rfs-buffer-devio.c new file mode 100644 index 0000000000..714c2bf300 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-buffer-devio.c @@ -0,0 +1,61 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Buffer Routines. + * + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> + +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-file-system.h> + +#if !RTEMS_RFS_USE_LIBBLOCK + +/** + * Show errors. + */ +#define RTEMS_RFS_BUFFER_ERRORS 1 + +int +rtems_rfs_buffer_deviceio_request (rtems_rfs_buffer_handle* handle, + dev_t device, + rtems_rfs_buffer_block block, + bool read) +{ +} + +int +rtems_rfs_buffer_deviceio_release (rtems_rfs_buffer_handle* handle, + dev_t device) +{ +} + +int +rtems_rfs_buffer_deviceio_handle_open (rtems_rfs_buffer_handle* handle, + dev_t device) +{ +} + +int +rtems_rfs_buffer_device_handle_close (rtems_rfs_buffer_handle* handle, + dev_t device) +{ +} + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-buffer.c b/cpukit/libfs/src/rfs/rtems-rfs-buffer.c new file mode 100644 index 0000000000..43fb586895 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-buffer.c @@ -0,0 +1,484 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Buffer Routines. + * + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> +#include <errno.h> + +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-file-system.h> + +/** + * Scan the chain for a buffer that matches the block number. + * + * @param chain The chain to scan. + * @param count The number of items on the chain. + * @param block The block number to find. + * @return rtems_rfs_buffer* The buffer if found else NULL. + */ +static rtems_rfs_buffer* +rtems_rfs_scan_chain (rtems_chain_control* chain, + uint32_t* count, + rtems_rfs_buffer_block block) +{ + rtems_rfs_buffer* buffer; + rtems_chain_node* node; + + node = rtems_chain_last (chain); + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS)) + printf ("rtems-rfs: buffer-scan: count=%" PRIu32 ", block=%" PRIu32 ": ", *count, block); + + while (!rtems_chain_is_head (chain, node)) + { + buffer = (rtems_rfs_buffer*) node; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS)) + printf ("%" PRIuPTR " ", ((intptr_t) buffer->user)); + + if (((rtems_rfs_buffer_block) ((intptr_t)(buffer->user))) == block) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS)) + printf (": found block=%" PRIuPTR "\n", + ((intptr_t)(buffer->user))); + + (*count)--; + rtems_chain_extract (node); + rtems_chain_set_off_chain (node); + return buffer; + } + node = rtems_chain_previous (node); + } + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS)) + printf (": not found\n"); + + return NULL; +} + +int +rtems_rfs_buffer_handle_request (rtems_rfs_file_system* fs, + rtems_rfs_buffer_handle* handle, + rtems_rfs_buffer_block block, + bool read) +{ + int rc; + + /* + * If the handle has a buffer release it. This allows a handle to be reused + * without needing to close then open it again. + */ + if (rtems_rfs_buffer_handle_has_block (handle)) + { + /* + * Treat block 0 as special to handle the loading of the super block. + */ + if (block && (rtems_rfs_buffer_bnum (handle) == block)) + return 0; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_REQUEST)) + printf ("rtems-rfs: buffer-request: handle has buffer: %" PRIu32 "\n", + rtems_rfs_buffer_bnum (handle)); + + rc = rtems_rfs_buffer_handle_release (fs, handle); + if (rc > 0) + return rc; + handle->dirty = false; + handle->bnum = 0; + } + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_REQUEST)) + printf ("rtems-rfs: buffer-request: block=%" PRIu32 "\n", block); + + /* + * First check to see if the buffer has already been requested and is + * currently attached to a handle. If it is share the access. A buffer could + * be shared where different parts of the block have separate functions. An + * example is an inode block and the file system needs to handle 2 inodes in + * the same block at the same time. + */ + if (fs->buffers_count) + { + /* + * Check the active buffer list for shared buffers. + */ + handle->buffer = rtems_rfs_scan_chain (&fs->buffers, + &fs->buffers_count, + block); + if (rtems_rfs_buffer_handle_has_block (handle) && + rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_REQUEST)) + printf ("rtems-rfs: buffer-request: buffer shared: refs: %d\n", + rtems_rfs_buffer_refs (handle) + 1); + } + + /* + * If the buffer has not been found check the local cache of released + * buffers. There are release and released modified lists to preserve the + * state. + */ + if (!rtems_rfs_fs_no_local_cache (fs) && + !rtems_rfs_buffer_handle_has_block (handle)) + { + /* + * Check the local cache of released buffers. + */ + if (fs->release_count) + handle->buffer = rtems_rfs_scan_chain (&fs->release, + &fs->release_count, + block); + + if (!rtems_rfs_buffer_handle_has_block (handle) && + fs->release_modified_count) + { + handle->buffer = rtems_rfs_scan_chain (&fs->release_modified, + &fs->release_modified_count, + block); + /* + * If we found a buffer retain the dirty buffer state. + */ + if (rtems_rfs_buffer_handle_has_block (handle)) + rtems_rfs_buffer_mark_dirty (handle); + } + } + + /* + * If not located we request the buffer from the I/O layer. + */ + if (!rtems_rfs_buffer_handle_has_block (handle)) + { + rc = rtems_rfs_buffer_io_request (fs, block, read, &handle->buffer); + + rtems_chain_set_off_chain (rtems_rfs_buffer_link(handle)); + + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_REQUEST)) + printf ("rtems-rfs: buffer-request: block=%" PRIu32 ": bdbuf-%s: %d: %s\n", + block, read ? "read" : "get", rc, strerror (rc)); + return rc; + } + } + + /* + * Increase the reference count of the buffer. + */ + rtems_rfs_buffer_refs_up (handle); + rtems_chain_append (&fs->buffers, rtems_rfs_buffer_link (handle)); + fs->buffers_count++; + + handle->buffer->user = (void*) ((intptr_t) block); + handle->bnum = block; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_REQUEST)) + printf ("rtems-rfs: buffer-request: block=%" PRIu32 " bdbuf-%s=%" PRIu32 " refs=%d\n", + block, read ? "read" : "get", handle->buffer->block, + handle->buffer->references); + + return 0; +} + +int +rtems_rfs_buffer_handle_release (rtems_rfs_file_system* fs, + rtems_rfs_buffer_handle* handle) +{ + int rc = 0; + + if (rtems_rfs_buffer_handle_has_block (handle)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_RELEASE)) + printf ("rtems-rfs: buffer-release: block=%" PRIu32 " %s refs=%d %s\n", + rtems_rfs_buffer_bnum (handle), + rtems_rfs_buffer_dirty (handle) ? "(dirty)" : "", + rtems_rfs_buffer_refs (handle), + rtems_rfs_buffer_refs (handle) == 0 ? "BAD REF COUNT" : ""); + + if (rtems_rfs_buffer_refs (handle) > 0) + rtems_rfs_buffer_refs_down (handle); + + if (rtems_rfs_buffer_refs (handle) == 0) + { + rtems_chain_extract (rtems_rfs_buffer_link (handle)); + fs->buffers_count--; + + if (rtems_rfs_fs_no_local_cache (fs)) + { + handle->buffer->user = (void*) 0; + rc = rtems_rfs_buffer_io_release (handle->buffer, + rtems_rfs_buffer_dirty (handle)); + } + else + { + /* + * If the total number of held buffers is higher than the configured + * value remove a buffer from the queue with the most buffers and + * release. The buffers are held on the queues with the newest at the + * head. + * + * This code stops a large series of transactions causing all the + * buffers in the cache being held in queues of this file system. + */ + if ((fs->release_count + + fs->release_modified_count) >= fs->max_held_buffers) + { + rtems_rfs_buffer* buffer; + bool modified; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_RELEASE)) + printf ("rtems-rfs: buffer-release: local cache overflow:" + " %" PRIu32 "\n", fs->release_count + fs->release_modified_count); + + if (fs->release_count > fs->release_modified_count) + { + buffer = (rtems_rfs_buffer*) rtems_chain_get (&fs->release); + fs->release_count--; + modified = false; + } + else + { + buffer = + (rtems_rfs_buffer*) rtems_chain_get (&fs->release_modified); + fs->release_modified_count--; + modified = true; + } + buffer->user = (void*) 0; + rc = rtems_rfs_buffer_io_release (buffer, modified); + } + + if (rtems_rfs_buffer_dirty (handle)) + { + rtems_chain_append (&fs->release_modified, + rtems_rfs_buffer_link (handle)); + fs->release_modified_count++; + } + else + { + rtems_chain_append (&fs->release, rtems_rfs_buffer_link (handle)); + fs->release_count++; + } + } + } + handle->buffer = NULL; + } + + return rc; +} + +int +rtems_rfs_buffer_open (const char* name, rtems_rfs_file_system* fs) +{ + struct stat st; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SYNC)) + printf ("rtems-rfs: buffer-open: opening: %s\n", name); + + if (stat (name, &st) < 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_OPEN)) + printf ("rtems-rfs: buffer-open: stat '%s' failed: %s\n", + name, strerror (errno)); + return ENOENT; + } + +#if RTEMS_RFS_USE_LIBBLOCK + /* + * Is the device a block device ? + */ + if (!S_ISBLK (st.st_mode)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_OPEN)) + printf ("rtems-rfs: buffer-open: '%s' is not a block device\n", name); + return EIO; + } + + /* + * Check that device is registred as a block device and lock it. + */ + fs->disk = rtems_disk_obtain (st.st_rdev); + if (!fs->disk) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_OPEN)) + printf ("rtems-rfs: buffer-open: cannot obtain the disk\n"); + return EIO; + } +#else + fs->device = open (name, O_RDWR); + if (fs->device < 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_OPEN)) + printf ("rtems-rfs: buffer-open: cannot open file\n"); + } + fs->media_size = st.st_size; + strcat (fs->name, name); +#endif + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SYNC)) + printf ("rtems-rfs: buffer-open: blks=%" PRId32 ", blk-size=%" PRId32 "\n", + rtems_rfs_fs_media_blocks (fs), + rtems_rfs_fs_media_block_size (fs)); + + return 0; +} + +int +rtems_rfs_buffer_close (rtems_rfs_file_system* fs) +{ + int rc = 0; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CLOSE)) + printf ("rtems-rfs: buffer-close: closing\n"); + + /* + * Change the block size to the media device size. It will release and sync + * all buffers. + */ + rc = rtems_rfs_buffer_setblksize (fs, rtems_rfs_fs_media_block_size (fs)); + + if ((rc > 0) && rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CLOSE)) + printf ("rtems-rfs: buffer-close: set media block size failed: %d: %s\n", + rc, strerror (rc)); + +#if RTEMS_RFS_USE_LIBBLOCK + rtems_disk_release (fs->disk); +#else + if (close (fs->device) < 0) + { + rc = errno; + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CLOSE)) + printf ("rtems-rfs: buffer-close: file close failed: %d: %s\n", + rc, strerror (rc)); + } +#endif + + return rc; +} + +int +rtems_rfs_buffer_sync (rtems_rfs_file_system* fs) +{ + int result = 0; +#if RTEMS_RFS_USE_LIBBLOCK + rtems_status_code sc; +#endif + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SYNC)) + printf ("rtems-rfs: buffer-sync: syncing\n"); + + /* + * @todo Split in the separate files for each type. + */ +#if RTEMS_RFS_USE_LIBBLOCK + sc = rtems_bdbuf_syncdev (rtems_rfs_fs_device (fs)); + if (sc != RTEMS_SUCCESSFUL) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SYNC)) + printf ("rtems-rfs: buffer-sync: device sync failed: %s\n", + rtems_status_text (sc)); + result = EIO; + } + rtems_disk_release (fs->disk); +#else + if (fsync (fs->device) < 0) + { + result = errno; + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CLOSE)) + printf ("rtems-rfs: buffer-sync: file sync failed: %d: %s\n", + result, strerror (result)); + } +#endif + return result; +} + +int +rtems_rfs_buffer_setblksize (rtems_rfs_file_system* fs, size_t size) +{ + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SETBLKSIZE)) + printf ("rtems-rfs: buffer-setblksize: block size: %zu\n", size); + + rc = rtems_rfs_buffers_release (fs); + if ((rc > 0) && rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SETBLKSIZE)) + printf ("rtems-rfs: buffer-setblksize: buffer release failed: %d: %s\n", + rc, strerror (rc)); + + rc = rtems_rfs_buffer_sync (fs); + if ((rc > 0) && rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SETBLKSIZE)) + printf ("rtems-rfs: buffer-setblksize: device sync failed: %d: %s\n", + rc, strerror (rc)); + +#if RTEMS_RFS_USE_LIBBLOCK + rc = fs->disk->ioctl (fs->disk, RTEMS_BLKIO_SETBLKSIZE, &size); + if (rc < 0) + rc = errno; +#endif + return rc; +} + +static int +rtems_rfs_release_chain (rtems_chain_control* chain, + uint32_t* count, + bool modified) +{ + rtems_rfs_buffer* buffer; + int rrc = 0; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS)) + printf ("rtems-rfs: release-chain: count=%" PRIu32 "\n", *count); + + while (!rtems_chain_is_empty (chain)) + { + buffer = (rtems_rfs_buffer*) rtems_chain_get (chain); + (*count)--; + + buffer->user = (void*) 0; + + rc = rtems_rfs_buffer_io_release (buffer, modified); + if ((rc > 0) && (rrc == 0)) + rrc = rc; + } + return rrc; +} + +int +rtems_rfs_buffers_release (rtems_rfs_file_system* fs) +{ + int rrc = 0; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_RELEASE)) + printf ("rtems-rfs: buffers-release: active:%" PRIu32 " " + "release:%" PRIu32 " release-modified:%" PRIu32 "\n", + fs->buffers_count, fs->release_count, fs->release_modified_count); + + rc = rtems_rfs_release_chain (&fs->release, + &fs->release_count, + false); + if ((rc > 0) && (rrc == 0)) + rrc = rc; + rc = rtems_rfs_release_chain (&fs->release_modified, + &fs->release_modified_count, + true); + if ((rc > 0) && (rrc == 0)) + rrc = rc; + + return rrc; +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-buffer.h b/cpukit/libfs/src/rfs/rtems-rfs-buffer.h new file mode 100644 index 0000000000..e207f5c7d3 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-buffer.h @@ -0,0 +1,265 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Buffer Management. + * + * These functions map blocks to the media interface layers. + */ + +#if !defined (_RTEMS_RFS_BUFFER_H_) +#define _RTEMS_RFS_BUFFER_H_ + +#include <errno.h> + +#include <rtems/rfs/rtems-rfs-file-system-fwd.h> +#include <rtems/rfs/rtems-rfs-trace.h> + +/** + * Define the method used to interface to the buffers. It can be libblock or + * device I/O. The libblock interface is to the RTEMS cache and block devices + * and device I/O accesses the media via a device file handle. + */ +#if defined (__rtems__) +#define RTEMS_RFS_USE_LIBBLOCK 1 +#endif + +/** + * The RTEMS RFS I/O Layering. + */ +#if RTEMS_RFS_USE_LIBBLOCK +#include <rtems/bdbuf.h> +#include <rtems/error.h> + +typedef rtems_blkdev_bnum rtems_rfs_buffer_block; +typedef rtems_bdbuf_buffer rtems_rfs_buffer; +#define rtems_rfs_buffer_io_request rtems_rfs_buffer_bdbuf_request +#define rtems_rfs_buffer_io_release rtems_rfs_buffer_bdbuf_release + +/** + * Request a buffer from the RTEMS libblock BD buffer cache. + */ +int rtems_rfs_buffer_bdbuf_request (rtems_rfs_file_system* fs, + rtems_rfs_buffer_block block, + bool read, + rtems_rfs_buffer** buffer); +/** + * Release a buffer to the RTEMS libblock BD buffer cache. + */ +int rtems_rfs_buffer_bdbuf_release (rtems_rfs_buffer* handle, + bool modified); +#else /* Device I/O */ +typedef uint32_t rtems_rfs_buffer_block; +typedef struct _rtems_rfs_buffer +{ + rtems_chain_node link; + rtems_rfs_buffer_block user; + void* buffer; + size_t size; + uint32_t references; +} rtems_rfs_buffer; +#define rtems_rfs_buffer_io_request rtems_rfs_buffer_devceio_request +#define rtems_rfs_buffer_io_release rtems_rfs_uffer_deviceio_release + +/** + * Request a buffer from the device I/O. + */ +int rtems_rfs_buffer_deviceio_request (rtems_rfs_file_system* fs, + rtems_rfs_buffer_block block, + bool read, + rtems_rfs_buffer* buffer); +/** + * Release a buffer to the RTEMS libblock BD buffer cache. + */ +int rtems_rfs_buffer_deviceio_release (rtems_rfs_buffer* handle, + bool modified); +#endif + +/** + * RFS Buffer handle. + */ +typedef struct rtems_rfs_buffer_handle_t +{ + /** + * Has the buffer been modifed? + */ + bool dirty; + + /** + * Block number. The lower layer block number may be absolute and we maybe + * relative to an offset in the disk so hold locally. + */ + rtems_rfs_buffer_block bnum; + + /** + * Reference the buffer descriptor. + */ + rtems_rfs_buffer* buffer; + +} rtems_rfs_buffer_handle; + +/** + * The buffer linkage. + */ +#define rtems_rfs_buffer_link(_h) (&(_h)->buffer->link) + +/** + * Return the start of the data area of the buffer given a handle. + */ +#define rtems_rfs_buffer_data(_h) ((void*)((_h)->buffer->buffer)) + +/** + * Return the size of the buffer given a handle. + */ +#define rtems_rfs_buffer_size(_h) ((_h)->buffer->size) + +/** + * Return the block number. + */ +#define rtems_rfs_buffer_bnum(_h) ((_h)->bnum) + +/** + * Return the buffer dirty status. + */ +#define rtems_rfs_buffer_dirty(_h) ((_h)->dirty) + +/** + * Does the handle have a valid block attached ? + */ +#define rtems_rfs_buffer_handle_has_block(_h) ((_h)->buffer ? true : false) + +/** + * Mark the buffer as dirty. + */ +#define rtems_rfs_buffer_mark_dirty(_h) ((_h)->dirty = true) + +/** + * Return the reference count. + */ +#define rtems_rfs_buffer_refs(_h) ((_h)->buffer->references) + +/** + * Increment the reference count. + */ +#define rtems_rfs_buffer_refs_up(_h) ((_h)->buffer->references += 1) + +/** + * Decrement the reference count. + */ +#define rtems_rfs_buffer_refs_down(_h) ((_h)->buffer->references -= 1) + +/** + * Request a buffer. The buffer can be filled with data from the media (read == + * true) or you can request a buffer to fill with data. + * + * @param fs The file system data. + * @param handle The handle the requested buffer is attached to. + * @param block The block number. + * @param read Read the data from the disk. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_buffer_handle_request (rtems_rfs_file_system* fs, + rtems_rfs_buffer_handle* handle, + rtems_rfs_buffer_block block, + bool read); + +/** + * Release a buffer. If the buffer is dirty the buffer is written to disk. The + * result does not indicate if the data was successfully written to the disk as + * this operation may be performed in asynchronously to this release. + * + * @param fs The file system data. + * @param handle The handle the requested buffer is attached to. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_buffer_handle_release (rtems_rfs_file_system* fs, + rtems_rfs_buffer_handle* handle); + +/** + * Open a handle. + * + * @param fs The file system data. + * @param handle The buffer handle to open. + * @return int The error number (errno). No error if 0. + */ +static inline int +rtems_rfs_buffer_handle_open (rtems_rfs_file_system* fs, + rtems_rfs_buffer_handle* handle) +{ + handle->dirty = false; + handle->bnum = 0; + handle->buffer = NULL; + return 0; +} + +/** + * Close a handle. + * + * @param fs The file system data. + * @param handle The buffer handle to close. + * @return int The error number (errno). No error if 0. + */ +static inline int +rtems_rfs_buffer_handle_close (rtems_rfs_file_system* fs, + rtems_rfs_buffer_handle* handle) +{ + rtems_rfs_buffer_handle_release (fs, handle); + handle->dirty = false; + handle->bnum = 0; + handle->buffer = NULL; + return 0; +} + +/** + * Open the buffer interface. + * + * @param name The device name to the media. + * @param fs Pointer to the file system data. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_buffer_open (const char* name, rtems_rfs_file_system* fs); + +/** + * Close the buffer interface. + * + * @param fs Pointer to the file system data. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_buffer_close (rtems_rfs_file_system* fs); + +/** + * Sync all buffers to the media. + * + * @param fs Pointer to the file system data. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_buffer_sync (rtems_rfs_file_system* fs); + +/** + * Set the block size of the device. + * + * @param fs Pointer to the file system data. + * @param size The new block size. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_buffer_setblksize (rtems_rfs_file_system* fs, size_t size); + +/** + * Release any chained buffers. + * + * @param fs The file system data. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_buffers_release (rtems_rfs_file_system* fs); + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-data.h b/cpukit/libfs/src/rfs/rtems-rfs-data.h new file mode 100644 index 0000000000..343c0a9854 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-data.h @@ -0,0 +1,87 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Data. + * + * Access data in the correct byte order for the specific target we are running + * on. + * + * @todo Make direct access on matching byte ordered targets. + */ + +#if !defined (_RTEMS_RFS_DATA_H_) +#define _RTEMS_RFS_DATA_H_ + +#include <stdint.h> + +/** + * Helper function to make sure we have a byte pointer. + */ +#define rtems_rfs_data_ptr(_d) ((uint8_t*)(_d)) + +/** + * Helper function to get the data shifted in the correctly sized type. + */ +#define rtems_rfs_data_get(_d, _t, _o, _s) \ + (((_t)(rtems_rfs_data_ptr (_d)[_o])) << (_s)) + +/** + * RFS Read Unsigned 8bit Integer + */ +#define rtems_rfs_read_u8(_d) \ + (*rtems_rfs_data_ptr (_d)) + +/** + * RFS Read Unsigned 16bit Integer + */ +#define rtems_rfs_read_u16(_d) \ + (rtems_rfs_data_get (_d, uint16_t, 0, 8) | \ + rtems_rfs_data_get (_d, uint16_t, 1, 0)) + +/** + * RFS Read Unsigned 32bit Integer + */ +#define rtems_rfs_read_u32(_d) \ + (rtems_rfs_data_get (_d, uint32_t, 0, 24) | \ + rtems_rfs_data_get (_d, uint32_t, 1, 16) | \ + rtems_rfs_data_get (_d, uint32_t, 2, 8) | \ + rtems_rfs_data_get (_d, uint32_t, 3, 0)) + +/** + * RFS Write Unsigned 8bit Integer + */ +#define rtems_rfs_write_u8(_d, _v) \ + (*rtems_rfs_data_ptr (_d) = (uint8_t)(_v)) + +/** + * RFS Write Unsigned 16bit Integer + */ +#define rtems_rfs_write_u16(_d, _v) \ + do { \ + rtems_rfs_data_ptr (_d)[0] = (uint8_t)(((uint16_t)(_v)) >> 8); \ + rtems_rfs_data_ptr (_d)[1] = (uint8_t)((_v)); \ + } while (0) + +/** + * RFS Write Unsigned 32bit Integer + */ +#define rtems_rfs_write_u32(_d, _v) \ + do { \ + rtems_rfs_data_ptr (_d)[0] = (uint8_t)(((uint32_t)(_v)) >> 24); \ + rtems_rfs_data_ptr (_d)[1] = (uint8_t)(((uint32_t)(_v)) >> 16); \ + rtems_rfs_data_ptr (_d)[2] = (uint8_t)(((uint32_t)(_v)) >> 8); \ + rtems_rfs_data_ptr (_d)[3] = (uint8_t)((uint32_t)(_v)); \ + } while (0) + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-dir-hash.c b/cpukit/libfs/src/rfs/rtems-rfs-dir-hash.c new file mode 100644 index 0000000000..0aaf405213 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-dir-hash.c @@ -0,0 +1,341 @@ +/* + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Directory Hash function. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/rfs/rtems-rfs-dir-hash.h> + +#ifdef __rtems__ +# include <machine/endian.h> /* attempt to define endianness */ +#endif +#ifdef linux +# include <endian.h> /* attempt to define endianness */ +#endif + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* + ------------------------------------------------------------------------------- + mix -- mix 3 32-bit values reversibly. + + This is reversible, so any information in (a,b,c) before mix() is still in + (a,b,c) after mix(). + + If four pairs of (a,b,c) inputs are run through mix(), or through mix() in + reverse, there are at least 32 bits of the output that are sometimes the same + for one pair and different for another pair. This was tested for: + + * pairs that differed by one bit, by two bits, in any combination of top bits + of (a,b,c), or in any combination of bottom bits of (a,b,c). + + * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the + output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly + produced by subtraction) look like a single 1-bit difference. + + * the base values were pseudorandom, all zero but one bit set, or all zero + plus a counter that starts at zero. + + Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that satisfy this + are: + + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 + + Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing for "differ" defined + as + with a one-bit base and a two-bit delta. I used + http://burtleburtle.net/bob/hash/avalanche.html to choose the operations, + constants, and arrangements of the variables. + + This does not achieve avalanche. There are input bits of (a,b,c) that fail + to affect some output bits of (a,b,c), especially of a. The most thoroughly + mixed value is c, but it doesn't really even achieve avalanche in c. + + This allows some parallelism. Read-after-writes are good at doubling the + number of bits affected, so the goal of mixing pulls in the opposite + direction as the goal of parallelism. I did what I could. Rotates seem to + cost as much as shifts on every machine I could lay my hands on, and rotates + are much kinder to the top and bottom bits, so I used rotates. + ------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ + { \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ + } + +/* + ------------------------------------------------------------------------------- + final -- final mixing of 3 32-bit values (a,b,c) into c + + Pairs of (a,b,c) values differing in only a few bits will usually produce + values of c that look totally different. This was tested for + + * pairs that differed by one bit, by two bits, in any combination of top bits + of (a,b,c), or in any combination of bottom bits of (a,b,c). + + * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the + output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly + produced by subtraction) look like a single 1-bit difference. * the base + values were pseudorandom, all zero but one bit set, or all zero plus a + counter that starts at zero. + + These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 + and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 + ------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ + { \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ + } + +/** + * The follow is the documentation from Bob Jenkin's hash function: + * + * http://burtleburtle.net/bob/hash/doobs.html + * + * The function hashlittle() has been renamed. + * + * hashlittle() -- hash a variable-length key into a 32-bit value + * + * k : the key (the unaligned variable-length array of bytes) + * length : the length of the key, counting by bytes + * initval : can be any 4-byte value + * + * Returns a 32-bit value. Every bit of the key affects every bit of the + * return value. Two keys differing by one or two bits will have totally + * different hash values. + * + * The best hash table sizes are powers of 2. There is no need to do mod a + * prime (mod is sooo slow!). If you need less than 32 bits, use a bitmask. + * For example, if you need only 10 bits, do h = (h & hashmask(10)); In which + * case, the hash table should have hashsize(10) elements. + * + * If you are hashing n strings (uint8_t **)k, do it like this: for (i=0, h=0; + * i<n; ++i) h = hashlittle( k[i], len[i], h); + * + * By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this + * code any way you wish, private, educational, or commercial. It's free. + * + * Use for hash table lookup, or anything where one collision in 2^^32 is + * acceptable. Do NOT use for cryptographic purposes. +*/ + +#define initval (20010928) +uint32_t +rtems_rfs_dir_hash (const void *key, size_t length) +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + /*const uint8_t *k8;*/ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + diff --git a/cpukit/libfs/src/rfs/rtems-rfs-dir-hash.h b/cpukit/libfs/src/rfs/rtems-rfs-dir-hash.h new file mode 100644 index 0000000000..ca761dd0cc --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-dir-hash.h @@ -0,0 +1,34 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Directory Hash provides a 32bit hash of a string. This is + * used to search a directory. + */ + +#if !defined (_RTEMS_RFS_DIR_HASH_H_) +#define _RTEMS_RFS_DIR_HAS_H_ + +#include <stddef.h> +#include <stdint.h> + +/** + * Compute a hash of the key over the length of string. + * + * @param key The key to calculate the hash of. + * @param length The length of the key in bytes. + * @return uint32_t The hash. + */ +uint32_t rtems_rfs_dir_hash (const void *key, size_t length); + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-dir.c b/cpukit/libfs/src/rfs/rtems-rfs-dir.c new file mode 100644 index 0000000000..1f2e5fd518 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-dir.c @@ -0,0 +1,760 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Directory Routines. + * + * These functions manage blocks in the directory format. A directory entry is + * a variable length record in the block. The entry consists of a length, hash + * and the string. The length allows the next entry to be located and the hash + * allows a simple check to be performed wihtout a string compare. Directory + * entries do not span a block and removal of an entry results in the space in + * the block being compacted and the spare area being initialised to ones. + * + * The maximum length can be 1 or 2 bytes depending on the value in the + * superblock. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#if SIZEOF_OFF_T == 8 +#define PRIdoff_t PRIo64 +#elif SIZEOF_OFF_T == 4 +#define PRIdoff_t PRIo32 +#else +#error "unsupported size of off_t" +#endif + +#include <rtems/rfs/rtems-rfs-block.h> +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-trace.h> +#include <rtems/rfs/rtems-rfs-dir.h> +#include <rtems/rfs/rtems-rfs-dir-hash.h> + +/** + * Validate the directory entry data. + */ +#define rtems_rfs_dir_entry_valid(_f, _l, _i) \ + (((_l) <= RTEMS_RFS_DIR_ENTRY_SIZE) || ((_l) >= rtems_rfs_fs_max_name (_f)) \ + || (_i < RTEMS_RFS_ROOT_INO) || (_i > rtems_rfs_fs_inodes (_f))) + +int +rtems_rfs_dir_lookup_ino (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* inode, + const char* name, + int length, + rtems_rfs_ino* ino, + uint32_t* offset) +{ + rtems_rfs_block_map map; + rtems_rfs_buffer_handle entries; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + { + int c; + printf ("rtems-rfs: dir-lookup-ino: lookup ino: root=%" PRId32 ", path=", + inode->ino); + for (c = 0; c < length; c++) + printf ("%c", name[c]); + printf (", len=%d\n", length); + } + + *ino = RTEMS_RFS_EMPTY_INO; + *offset = 0; + + rc = rtems_rfs_block_map_open (fs, inode, &map); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: map open failed for ino %" PRIu32 ": %d: %s", + rtems_rfs_inode_ino (inode), rc, strerror (rc)); + return rc; + } + + rc = rtems_rfs_buffer_handle_open (fs, &entries); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: handle open failed for ino %" PRIu32 ": %d: %s", + rtems_rfs_inode_ino (inode), rc, strerror (rc)); + rtems_rfs_block_map_close (fs, &map); + return rc; + } + else + { + rtems_rfs_block_no block; + uint32_t hash; + + /* + * Calculate the hash of the look up string. + */ + hash = rtems_rfs_dir_hash (name, length); + + /* + * Locate the first block. The map points to the start after open so just + * seek 0. If an error the block will be 0. + */ + rc = rtems_rfs_block_map_seek (fs, &map, 0, &block); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: block map find failed: %d: %s\n", + rc, strerror (rc)); + if (rc == ENXIO) + rc = ENOENT; + rtems_rfs_buffer_handle_close (fs, &entries); + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + while ((rc == 0) && block) + { + uint8_t* entry; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: block read, ino=%" PRIu32 " bno=%" PRId32 "\n", + rtems_rfs_inode_ino (inode), map.bpos.bno); + + rc = rtems_rfs_buffer_handle_request (fs, &entries, block, true); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: block read, ino=%" PRIu32 " block=%" PRId32 ": %d: %s\n", + rtems_rfs_inode_ino (inode), block, rc, strerror (rc)); + break; + } + + /* + * Search the block to see if the name matches. A hash of 0xffff or 0x0 + * means the entry is empty. + */ + + entry = rtems_rfs_buffer_data (&entries); + + map.bpos.boff = 0; + + while (map.bpos.boff < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE)) + { + uint32_t ehash; + int elength; + + ehash = rtems_rfs_dir_entry_hash (entry); + elength = rtems_rfs_dir_entry_length (entry); + *ino = rtems_rfs_dir_entry_ino (entry); + + if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY) + break; + + if (rtems_rfs_dir_entry_valid (fs, elength, *ino)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: " + "bad length or ino for ino %" PRIu32 ": %u/%" PRId32 " @ %04" PRIx32 "\n", + rtems_rfs_inode_ino (inode), elength, *ino, map.bpos.boff); + rc = EIO; + break; + } + + if (ehash == hash) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO_CHECK)) + printf ("rtems-rfs: dir-lookup-ino: " + "checking entry for ino %" PRId32 ": bno=%04" PRIx32 "/off=%04" PRIx32 + " length:%d ino:%" PRId32 "\n", + rtems_rfs_inode_ino (inode), map.bpos.bno, map.bpos.boff, + elength, rtems_rfs_dir_entry_ino (entry)); + + if (memcmp (entry + RTEMS_RFS_DIR_ENTRY_SIZE, name, length) == 0) + { + *offset = rtems_rfs_block_map_pos (fs, &map); + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO_FOUND)) + printf ("rtems-rfs: dir-lookup-ino: " + "entry found in ino %" PRIu32 ", ino=%" PRIu32 " offset=%" PRIu32 "\n", + rtems_rfs_inode_ino (inode), *ino, *offset); + + rtems_rfs_buffer_handle_close (fs, &entries); + rtems_rfs_block_map_close (fs, &map); + return 0; + } + } + + map.bpos.boff += elength; + entry += elength; + } + + if (rc == 0) + { + rc = rtems_rfs_block_map_next_block (fs, &map, &block); + if ((rc > 0) && (rc != ENXIO)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: " + "block map next block failed in ino %" PRIu32 ": %d: %s\n", + rtems_rfs_inode_ino (inode), rc, strerror (rc)); + } + if (rc == ENXIO) + rc = ENOENT; + } + } + + if ((rc == 0) && (block == 0)) + { + rc = EIO; + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: block is 0 in ino %" PRIu32 ": %d: %s\n", + rtems_rfs_inode_ino (inode), rc, strerror (rc)); + } + } + + rtems_rfs_buffer_handle_close (fs, &entries); + rtems_rfs_block_map_close (fs, &map); + return rc; +} + +int +rtems_rfs_dir_add_entry (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* dir, + const char* name, + size_t length, + rtems_rfs_ino ino) +{ + rtems_rfs_block_map map; + rtems_rfs_block_pos bpos; + rtems_rfs_buffer_handle buffer; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY)) + { + int c; + printf ("rtems-rfs: dir-add-entry: dir=%" PRId32 ", name=", + rtems_rfs_inode_ino (dir)); + for (c = 0; c < length; c++) + printf ("%c", name[c]); + printf (", len=%zd\n", length); + } + + rc = rtems_rfs_block_map_open (fs, dir, &map); + if (rc > 0) + return rc; + + rc = rtems_rfs_buffer_handle_open (fs, &buffer); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + /* + * Search the map from the beginning to find any empty space. + */ + rtems_rfs_block_set_bpos_zero (&bpos); + + while (true) + { + rtems_rfs_block_no block; + uint8_t* entry; + int offset; + bool read = true; + + /* + * Locate the first block. If an error the block will be 0. If the map is + * empty which happens when creating a directory and adding the first entry + * the seek will return ENXIO. In this case we need to grow the directory. + */ + rc = rtems_rfs_block_map_find (fs, &map, &bpos, &block); + if (rc > 0) + { + if (rc != ENXIO) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY)) + printf ("rtems-rfs: dir-add-entry: " + "block map find failed for ino %" PRIu32 ": %d: %s\n", + rtems_rfs_inode_ino (dir), rc, strerror (rc)); + break; + } + + /* + * We have reached the end of the directory so add a block. + */ + rc = rtems_rfs_block_map_grow (fs, &map, 1, &block); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY)) + printf ("rtems-rfs: dir-add-entry: " + "block map grow failed for ino %" PRIu32 ": %d: %s\n", + rtems_rfs_inode_ino (dir), rc, strerror (rc)); + break; + } + + read = false; + } + + bpos.bno++; + + rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, read); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY)) + printf ("rtems-rfs: dir-add-entry: " + "block buffer req failed for ino %" PRIu32 ": %d: %s\n", + rtems_rfs_inode_ino (dir), rc, strerror (rc)); + break; + } + + entry = rtems_rfs_buffer_data (&buffer); + + if (!read) + memset (entry, 0xff, rtems_rfs_fs_block_size (fs)); + + offset = 0; + + while (offset < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE)) + { + rtems_rfs_ino eino; + int elength; + + elength = rtems_rfs_dir_entry_length (entry); + eino = rtems_rfs_dir_entry_ino (entry); + + if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY) + { + if ((length + RTEMS_RFS_DIR_ENTRY_SIZE) < + (rtems_rfs_fs_block_size (fs) - offset)) + { + uint32_t hash; + hash = rtems_rfs_dir_hash (name, length); + rtems_rfs_dir_set_entry_hash (entry, hash); + rtems_rfs_dir_set_entry_ino (entry, ino); + rtems_rfs_dir_set_entry_length (entry, + RTEMS_RFS_DIR_ENTRY_SIZE + length); + memcpy (entry + RTEMS_RFS_DIR_ENTRY_SIZE, name, length); + rtems_rfs_buffer_mark_dirty (&buffer); + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return 0; + } + + break; + } + + if (rtems_rfs_dir_entry_valid (fs, elength, eino)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY)) + printf ("rtems-rfs: dir-add-entry: " + "bad length or ino for ino %" PRIu32 ": %u/%" PRId32 " @ %04x\n", + rtems_rfs_inode_ino (dir), elength, eino, offset); + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return EIO; + } + + entry += elength; + offset += elength; + } + } + + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return rc; +} + +int +rtems_rfs_dir_del_entry (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* dir, + rtems_rfs_ino ino, + uint32_t offset) +{ + rtems_rfs_block_map map; + rtems_rfs_block_no block; + rtems_rfs_buffer_handle buffer; + bool search; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY)) + printf ("rtems-rfs: dir-del-entry: dir=%" PRId32 ", entry=%" PRId32 " offset=%" PRIu32 "\n", + rtems_rfs_inode_ino (dir), ino, offset); + + rc = rtems_rfs_block_map_open (fs, dir, &map); + if (rc > 0) + return rc; + + rc = rtems_rfs_block_map_seek (fs, &map, offset, &block); + if (rc > 0) + { + if (rc == ENXIO) + rc = ENOENT; + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + rc = rtems_rfs_buffer_handle_open (fs, &buffer); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + /* + * Only search if the offset is 0 else we are at that position. + */ + search = offset ? false : true; + + while (rc == 0) + { + uint8_t* entry; + int eoffset; + + rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, true); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY)) + printf ("rtems-rfs: dir-del-entry: " + "block buffer req failed for ino %" PRIu32 ": %d: %s\n", + rtems_rfs_inode_ino (dir), rc, strerror (rc)); + break; + } + + /* + * If we are searching start at the beginning of the block. If not searching + * skip to the offset in the block. + */ + if (search) + eoffset = 0; + else + eoffset = offset % rtems_rfs_fs_block_size (fs); + + entry = rtems_rfs_buffer_data (&buffer) + eoffset; + + while (eoffset < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE)) + { + rtems_rfs_ino eino; + int elength; + + elength = rtems_rfs_dir_entry_length (entry); + eino = rtems_rfs_dir_entry_ino (entry); + + if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY) + break; + + if (rtems_rfs_dir_entry_valid (fs, elength, eino)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY)) + printf ("rtems-rfs: dir-del-entry: " + "bad length or ino for ino %" PRIu32 ": %u/%" PRId32 + " @ %" PRIu32 ".%04x\n", + rtems_rfs_inode_ino (dir), elength, eino, block, eoffset); + rc = EIO; + break; + } + + if (ino == rtems_rfs_dir_entry_ino (entry)) + { + uint32_t remaining; + remaining = rtems_rfs_fs_block_size (fs) - (eoffset + elength); + memmove (entry, entry + elength, remaining); + memset (entry + remaining, 0xff, elength); + + /* + * If the remainder of the block is empty and this is the start of the + * block and it is the last block in the map shrink the map. + * + * @note We could check again to see if the new end block in the map is + * also empty. This way we could clean up an empty directory. + */ + elength = rtems_rfs_dir_entry_length (entry); + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY)) + printf ("rtems-rfs: dir-del-entry: " + "last block free for ino %" PRIu32 ": elength=%i block=%" PRIu32 + " offset=%d last=%s\n", + ino, elength, block, eoffset, + rtems_rfs_block_map_last (&map) ? "yes" : "no"); + + if ((elength == RTEMS_RFS_DIR_ENTRY_EMPTY) && + (eoffset == 0) && rtems_rfs_block_map_last (&map)) + { + rc = rtems_rfs_block_map_shrink (fs, &map, 1); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY)) + printf ("rtems-rfs: dir-del-entry: " + "block map shrink failed for ino %" PRIu32 ": %d: %s\n", + rtems_rfs_inode_ino (dir), rc, strerror (rc)); + } + } + + rtems_rfs_buffer_mark_dirty (&buffer); + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return 0; + } + + if (!search) + { + rc = EIO; + break; + } + + entry += elength; + eoffset += elength; + } + + if (rc == 0) + { + rc = rtems_rfs_block_map_next_block (fs, &map, &block); + if (rc == ENXIO) + rc = ENOENT; + } + } + + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return rc; +} + +int +rtems_rfs_dir_read (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* dir, + rtems_rfs_pos_rel offset, + struct dirent* dirent, + size_t* length) +{ + rtems_rfs_block_map map; + rtems_rfs_buffer_handle buffer; + rtems_rfs_block_no block; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ)) + printf ("rtems-rfs: dir-read: dir=%" PRId32 " offset=%" PRId64 "\n", + rtems_rfs_inode_ino (dir), offset); + + *length = 0; + + rc = rtems_rfs_block_map_open (fs, dir, &map); + if (rc > 0) + return rc; + + if (((rtems_rfs_fs_block_size (fs) - + (offset % rtems_rfs_fs_block_size (fs))) <= RTEMS_RFS_DIR_ENTRY_SIZE)) + offset = (((offset / rtems_rfs_fs_block_size (fs)) + 1) * + rtems_rfs_fs_block_size (fs)); + + rc = rtems_rfs_block_map_seek (fs, &map, offset, &block); + if (rc > 0) + { + if (rc == ENXIO) + rc = ENOENT; + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + rc = rtems_rfs_buffer_handle_open (fs, &buffer); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + /* + * Look for an empty entry and if this is the last block that is the end of + * the directory. + */ + while (rc == 0) + { + uint8_t* entry; + rtems_rfs_ino eino; + int elength; + int remaining; + + rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, true); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + entry = rtems_rfs_buffer_data (&buffer); + entry += map.bpos.boff; + + elength = rtems_rfs_dir_entry_length (entry); + eino = rtems_rfs_dir_entry_ino (entry); + + if (elength != RTEMS_RFS_DIR_ENTRY_EMPTY) + { + if (rtems_rfs_dir_entry_valid (fs, elength, eino)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ)) + printf ("rtems-rfs: dir-read: " + "bad length or ino for ino %" PRIu32 ": %u/%" PRId32 " @ %04" PRIx32 "\n", + rtems_rfs_inode_ino (dir), elength, eino, map.bpos.boff); + rc = EIO; + break; + } + + memset (dirent, 0, sizeof (struct dirent)); + dirent->d_off = offset; + dirent->d_reclen = sizeof (struct dirent); + + *length += elength; + + remaining = rtems_rfs_fs_block_size (fs) - (map.bpos.boff + elength); + + if (remaining <= RTEMS_RFS_DIR_ENTRY_SIZE) + *length += remaining; + + elength -= RTEMS_RFS_DIR_ENTRY_SIZE; + if (elength > NAME_MAX) + elength = NAME_MAX; + + memcpy (dirent->d_name, entry + RTEMS_RFS_DIR_ENTRY_SIZE, elength); + + dirent->d_ino = rtems_rfs_dir_entry_ino (entry); + dirent->d_namlen = elength; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ)) + printf ("rtems-rfs: dir-read: found off:%" PRIdoff_t " ino:%ld name=%s\n", + dirent->d_off, dirent->d_ino, dirent->d_name); + break; + } + + *length += rtems_rfs_fs_block_size (fs) - map.bpos.boff; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ)) + printf ("rtems-rfs: dir-read: next block: off:%" PRId64 " length:%zd\n", + offset, *length); + + rc = rtems_rfs_block_map_next_block (fs, &map, &block); + if (rc == ENXIO) + rc = ENOENT; + } + + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return rc; +} + +int +rtems_rfs_dir_empty (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* dir) +{ + rtems_rfs_block_map map; + rtems_rfs_buffer_handle buffer; + rtems_rfs_block_no block; + bool empty; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ)) + printf ("rtems-rfs: dir-empty: dir=%" PRId32 "\n", rtems_rfs_inode_ino (dir)); + + empty = true; + + rc = rtems_rfs_block_map_open (fs, dir, &map); + if (rc > 0) + return rc; + + rc = rtems_rfs_block_map_seek (fs, &map, 0, &block); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + rc = rtems_rfs_buffer_handle_open (fs, &buffer); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + /* + * Look for an empty entry and if this is the last block that is the end of + * the directory. + */ + while (empty) + { + uint8_t* entry; + int offset; + + rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, true); + if (rc > 0) + break; + + entry = rtems_rfs_buffer_data (&buffer); + offset = 0; + + while (offset < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE)) + { + rtems_rfs_ino eino; + int elength; + + elength = rtems_rfs_dir_entry_length (entry); + eino = rtems_rfs_dir_entry_ino (entry); + + if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY) + break; + + if (rtems_rfs_dir_entry_valid (fs, elength, eino)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_EMPTY)) + printf ("rtems-rfs: dir-empty: " + "bad length or ino for ino %" PRIu32 ": %u/%" PRIu32 " @ %04x\n", + rtems_rfs_inode_ino (dir), elength, eino, offset); + rc = EIO; + break; + } + + /* + * Ignore the current (.) and parent (..) entries. Anything else means + * the directory is not empty. + */ + if (((elength != (RTEMS_RFS_DIR_ENTRY_SIZE + 1)) || + (entry[RTEMS_RFS_DIR_ENTRY_SIZE] != '.')) && + ((elength != (RTEMS_RFS_DIR_ENTRY_SIZE + 2)) || + (entry[RTEMS_RFS_DIR_ENTRY_SIZE] != '.') || + (entry[RTEMS_RFS_DIR_ENTRY_SIZE + 1] != '.'))) + { + empty = false; + break; + } + + entry += elength; + offset += elength; + } + + if (empty) + { + rc = rtems_rfs_block_map_next_block (fs, &map, &block); + if (rc > 0) + { + if (rc == ENXIO) + rc = 0; + break; + } + } + } + + if ((rc == 0) && !empty) + rc = ENOTEMPTY; + + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return rc; +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-dir.h b/cpukit/libfs/src/rfs/rtems-rfs-dir.h new file mode 100644 index 0000000000..1f452460db --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-dir.h @@ -0,0 +1,206 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Directory Support + * + * This file provides the directory support functions. + */ + +#if !defined (_RTEMS_RFS_DIR_H_) +#define _RTEMS_RFS_DIR_H_ + +#include <dirent.h> + +#include <rtems/libio_.h> + +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> + +/** + * Test if the path provided is a current directory. + * + * @param _p Pointer to the path string. + * @return bool True if the path is a current directory. + */ +#define rtems_rfs_current_dir(_p) \ + ((_p[0] == '.') && ((_p[1] == '\0') || rtems_filesystem_is_separator (_p[1]))) + +/** + * Test if the path provided is a parent directory. + * + * @param _p Pointer to the path string. + * @return bool True if the path is a parent directory. + */ +#define rtems_rfs_parent_dir(_p) \ + ((_p[0] == '.') && (_p[1] == '.') && \ + ((_p[2] == '\0') || rtems_filesystem_is_separator (_p[2]))) + +/** + * Define the offsets of the fields of a directory entry. + */ +#define RTEMS_RFS_DIR_ENTRY_INO (0) /**< The ino offset in a directory + * entry. */ +#define RTEMS_RFS_DIR_ENTRY_HASH (4) /**< The hash offset in a directory + * entry. The hash is 32bits. We need at + * least 16bits and given the length and + * ino field are 4 the extra 2 bytes is + * not a big overhead.*/ +#define RTEMS_RFS_DIR_ENTRY_LEN (8) /**< The length offset in a directory + * entry. */ + +/** + * The length of the directory entry header. + */ +#define RTEMS_RFS_DIR_ENTRY_SIZE (4 + 4 + 2) + +/** + * The length when the remainder of the directory block is empty. + */ +#define RTEMS_RFS_DIR_ENTRY_EMPTY (0xffff) + +/** + * Return the hash of the entry. + * + * @param _e Pointer to the directory entry. + * @return uint32_t The hash. + */ +#define rtems_rfs_dir_entry_hash(_e) \ + rtems_rfs_read_u32 (_e + RTEMS_RFS_DIR_ENTRY_HASH) + +/** + * Set the hash of the entry. + * + * @param _e Pointer to the directory entry. + * @param _h The hash. + */ +#define rtems_rfs_dir_set_entry_hash(_e, _h) \ + rtems_rfs_write_u32 (_e + RTEMS_RFS_DIR_ENTRY_HASH, _h) + +/** + * Return the ino of the entry. + * + * @param _e Pointer to the directory entry. + * @return uint32_t The ino. + */ +#define rtems_rfs_dir_entry_ino(_e) \ + rtems_rfs_read_u32 (_e + RTEMS_RFS_DIR_ENTRY_INO) + +/** + * Set the ino of the entry. + * + * @param _e Pointer to the directory entry. + * @param _i The ino. + */ +#define rtems_rfs_dir_set_entry_ino(_e, _i) \ + rtems_rfs_write_u32 (_e + RTEMS_RFS_DIR_ENTRY_INO, _i) + +/** + * Return the length of the entry. + * + * @param _e Pointer to the directory entry. + * @return uint16_t The length. + */ +#define rtems_rfs_dir_entry_length(_e) \ + rtems_rfs_read_u16 (_e + RTEMS_RFS_DIR_ENTRY_LEN) + +/** + * Set the length of the entry. + * + * @param _e Pointer to the directory entry. + * @param _l The length. + */ +#define rtems_rfs_dir_set_entry_length(_e, _l) \ + rtems_rfs_write_u16 (_e + RTEMS_RFS_DIR_ENTRY_LEN, _l) + +/** + * Look up a directory entry in the directory pointed to by the inode. The look + * up is local to this directory. No need to decend. + * + * @param fs The file system. + * @param inode The inode of the directory to search. + * @param name The name to look up. The name may not be nul terminated. + * @param length The length of the name. + * @param ino The return inode number if there is no error. + * @param offset The offset in the directory for the entry. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_dir_lookup_ino (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* inode, + const char* name, + int length, + rtems_rfs_ino* ino, + uint32_t* offset); + +/** + * Add an entry to the directory returing the inode number allocated to the + * entry. + * + * @param fs The file system data. + * @param dir Pointer to the directory inode the entry is to be added too. + * @param name The name of the entry to be added. + * @param length The length of the name excluding a terminating 0. + * @param ino The ino of the entry. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_dir_add_entry (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* dir, + const char* name, + size_t length, + rtems_rfs_ino ino); + +/** + * Del an entry from the directory using an inode number as a key. + * + * @param fs The file system data. + * @param dir Pointer to the directory inode the entry is to be deleted from. + * @param ino The ino of the entry. + * @param offset The offset in the directory of the entry to delete. If 0 + * search from the start for the ino. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_dir_del_entry (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* dir, + rtems_rfs_ino ino, + uint32_t offset); + +/** + * Read the directory entry from offset into the directory entry buffer and + * return the length of space this entry uses in the directory table. + * + * @param fs The file system data. + * @param dir The direct inode handler. + * @param offset The offset in the directory to read from. + * @param dirent Pointer to the dirent structure the entry is written into. + * @param length Set the length this entry takes in the directory. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_dir_read (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* dir, + rtems_rfs_pos_rel offset, + struct dirent* dirent, + size_t* length); + +/** + * Check if the directory is empty. The current and parent directory entries + * are ignored. + * + * @param fs The file system data + * @param dir The directory inode to check. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_dir_empty (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* dir); + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-file-system-fwd.h b/cpukit/libfs/src/rfs/rtems-rfs-file-system-fwd.h new file mode 100644 index 0000000000..8c39944486 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-file-system-fwd.h @@ -0,0 +1,27 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Data forward decl. + */ + +#if !defined (_RTEMS_RFS_FILE_SYSTEM_FWD_H_) +#define _RTEMS_RFS_FILE_SYSTEM_FWD_H_ + +/** + * Forward reference to the file system data. + */ +struct _rtems_rfs_file_system; +typedef struct _rtems_rfs_file_system rtems_rfs_file_system; + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-file-system.c b/cpukit/libfs/src/rfs/rtems-rfs-file-system.c new file mode 100644 index 0000000000..c972e13247 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-file-system.c @@ -0,0 +1,320 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Open + * + * Open the file system by reading the superblock and then the group data. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> +#include <rtems/rfs/rtems-rfs-trace.h> + +uint64_t +rtems_rfs_fs_size (rtems_rfs_file_system* fs) +{ + uint64_t blocks = rtems_rfs_fs_blocks (fs); + uint64_t block_size = rtems_rfs_fs_block_size (fs); + return blocks * block_size; +} + +uint64_t +rtems_rfs_fs_media_size (rtems_rfs_file_system* fs) +{ + uint64_t media_blocks = (uint64_t) rtems_rfs_fs_media_blocks (fs); + uint64_t media_block_size = (uint64_t) rtems_rfs_fs_media_block_size (fs); + return media_blocks * media_block_size; +} + +static int +rtems_rfs_fs_read_superblock (rtems_rfs_file_system* fs) +{ + rtems_rfs_buffer_handle handle; + uint8_t* sb; + int group; + int rc; + + rc = rtems_rfs_buffer_handle_open (fs, &handle); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: read-superblock: handle open failed: %d: %s\n", + rc, strerror (rc)); + return rc; + } + + rc = rtems_rfs_buffer_handle_request (fs, &handle, 0, true); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: read-superblock: request failed%d: %s\n", + rc, strerror (rc)); + return rc; + } + + sb = rtems_rfs_buffer_data (&handle); + +#define read_sb(_o) rtems_rfs_read_u32 (sb + (_o)) + + if (read_sb (RTEMS_RFS_SB_OFFSET_MAGIC) != RTEMS_RFS_SB_MAGIC) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: read-superblock: invalid superblock, bad magic\n"); + rtems_rfs_buffer_handle_close (fs, &handle); + return EIO; + } + + fs->blocks = read_sb (RTEMS_RFS_SB_OFFSET_BLOCKS); + fs->block_size = read_sb (RTEMS_RFS_SB_OFFSET_BLOCK_SIZE); + + if (rtems_rfs_fs_size(fs) > rtems_rfs_fs_media_size (fs)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: read-superblock: invalid superblock block/size count\n"); + rtems_rfs_buffer_handle_close (fs, &handle); + return EIO; + } + + if ((read_sb (RTEMS_RFS_SB_OFFSET_VERSION) & RTEMS_RFS_VERSION_MASK) != + (RTEMS_RFS_VERSION * RTEMS_RFS_VERSION_MASK)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: read-superblock: incompatible version: %08" PRIx32 " (%08" PRIx32 ")\n", + read_sb (RTEMS_RFS_SB_OFFSET_VERSION), RTEMS_RFS_VERSION_MASK); + rtems_rfs_buffer_handle_close (fs, &handle); + return EIO; + } + + if (read_sb (RTEMS_RFS_SB_OFFSET_INODE_SIZE) != RTEMS_RFS_INODE_SIZE) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: read-superblock: inode size mismatch: fs:%" PRId32 " target:%" PRId32 "\n", + read_sb (RTEMS_RFS_SB_OFFSET_VERSION), RTEMS_RFS_VERSION_MASK); + rtems_rfs_buffer_handle_close (fs, &handle); + return EIO; + } + + fs->bad_blocks = read_sb (RTEMS_RFS_SB_OFFSET_BAD_BLOCKS); + fs->max_name_length = read_sb (RTEMS_RFS_SB_OFFSET_MAX_NAME_LENGTH); + fs->group_count = read_sb (RTEMS_RFS_SB_OFFSET_GROUPS); + fs->group_blocks = read_sb (RTEMS_RFS_SB_OFFSET_GROUP_BLOCKS); + fs->group_inodes = read_sb (RTEMS_RFS_SB_OFFSET_GROUP_INODES); + + fs->blocks_per_block = + rtems_rfs_fs_block_size (fs) / sizeof (rtems_rfs_inode_block); + + fs->block_map_singly_blocks = + fs->blocks_per_block * RTEMS_RFS_INODE_BLOCKS; + fs->block_map_doubly_blocks = + fs->blocks_per_block * fs->blocks_per_block * RTEMS_RFS_INODE_BLOCKS; + + fs->inodes = fs->group_count * fs->group_inodes; + + fs->inodes_per_block = fs->block_size / RTEMS_RFS_INODE_SIZE; + + if (fs->group_blocks > + rtems_rfs_bitmap_numof_bits (rtems_rfs_fs_block_size (fs))) + { + rtems_rfs_buffer_handle_close (fs, &handle); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: read-superblock: groups blocks larger than block bits\n"); + return EIO; + } + + rtems_rfs_buffer_handle_close (fs, &handle); + + /* + * Change the block size to the value in the superblock. + */ + rc = rtems_rfs_buffer_setblksize (fs, rtems_rfs_fs_block_size (fs)); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &handle); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: read-superblock: invalid superblock block size%d: %s\n", + rc, strerror (rc)); + return rc; + } + + fs->groups = calloc (fs->group_count, sizeof (rtems_rfs_group)); + + if (!fs->groups) + { + rtems_rfs_buffer_handle_close (fs, &handle); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: read-superblock: no memory for group table\n"); + return ENOMEM; + } + + /* + * Perform each phase of group initialisation at the same time. This way we + * know how far the initialisation has gone if an error occurs and we need to + * close everything. + */ + for (group = 0; group < fs->group_count; group++) + { + rc = rtems_rfs_group_open (fs, + rtems_rfs_fs_block (fs, group, 0), + fs->group_blocks, + fs->group_inodes, + &fs->groups[group]); + if (rc > 0) + { + int g; + for (g = 0; g < group; g++) + rtems_rfs_group_close (fs, &fs->groups[g]); + rtems_rfs_buffer_handle_close (fs, &handle); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: read-superblock: no memory for group table%d: %s\n", + rc, strerror (rc)); + return rc; + } + } + + return 0; +} + +int +rtems_rfs_fs_open (const char* name, + void* user, + uint32_t flags, + rtems_rfs_file_system** fs) +{ + rtems_rfs_group* group; + size_t group_base; + rtems_rfs_inode_handle inode; + uint16_t mode; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: open: %s\n", name); + + *fs = malloc (sizeof (rtems_rfs_file_system)); + if (!*fs) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: open: no memory for file system data\n"); + errno = ENOMEM; + return -1; + } + + memset (*fs, 0, sizeof (rtems_rfs_file_system)); + + (*fs)->user = user; + rtems_chain_initialize_empty (&(*fs)->buffers); + rtems_chain_initialize_empty (&(*fs)->release); + rtems_chain_initialize_empty (&(*fs)->release_modified); + rtems_chain_initialize_empty (&(*fs)->file_shares); + + (*fs)->max_held_buffers = RTEMS_RFS_FS_MAX_HELD_BUFFERS; + (*fs)->buffers_count = 0; + (*fs)->release_count = 0; + (*fs)->release_modified_count = 0; + (*fs)->flags = flags; + + group = &(*fs)->groups[0]; + group_base = 0; + + /* + * Open the buffer interface. + */ + rc = rtems_rfs_buffer_open (name, *fs); + if (rc > 0) + { + free (*fs); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: open: buffer open failed: %d: %s\n", + rc, strerror (rc)); + errno = rc; + return -1; + } + + rc = rtems_rfs_fs_read_superblock (*fs); + if (rc > 0) + { + rtems_rfs_buffer_close (*fs); + free (*fs); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: open: reading superblock: %d: %s\n", + rc, strerror (rc)); + errno = rc; + return -1; + } + + rc = rtems_rfs_inode_open (*fs, RTEMS_RFS_ROOT_INO, &inode, true); + if (rc > 0) + { + rtems_rfs_buffer_close (*fs); + free (*fs); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: open: reading root inode: %d: %s\n", + rc, strerror (rc)); + errno = rc; + return -1; + } + + if (((*fs)->flags & RTEMS_RFS_FS_FORCE_OPEN) == 0) + { + mode = rtems_rfs_inode_get_mode (&inode); + + if ((mode == 0xffff) || !RTEMS_RFS_S_ISDIR (mode)) + { + rtems_rfs_inode_close (*fs, &inode); + rtems_rfs_buffer_close (*fs); + free (*fs); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: open: invalid root inode mode\n"); + errno = EIO; + return -1; + } + } + + rc = rtems_rfs_inode_close (*fs, &inode); + if (rc > 0) + { + rtems_rfs_buffer_close (*fs); + free (*fs); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: open: closing root inode: %d: %s\n", rc, strerror (rc)); + errno = rc; + return -1; + } + + errno = 0; + return 0; +} + +int +rtems_rfs_fs_close (rtems_rfs_file_system* fs) +{ + int group; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_CLOSE)) + printf ("rtems-rfs: close\n"); + + for (group = 0; group < fs->group_count; group++) + rtems_rfs_group_close (fs, &fs->groups[group]); + + rtems_rfs_buffer_close (fs); + + free (fs); + return 0; +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-file-system.h b/cpukit/libfs/src/rfs/rtems-rfs-file-system.h new file mode 100644 index 0000000000..9ca0e4754f --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-file-system.h @@ -0,0 +1,402 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Data + * + * This file defines the file system data. + */ + +#if !defined (_RTEMS_RFS_FILE_SYSTEM_H_) +#define _RTEMS_RFS_FILE_SYSTEM_H_ + +#include <rtems/rfs/rtems-rfs-group.h> + +/** + * Superblock offsets and values. + */ +#define RTEMS_RFS_SB_OFFSET_MAGIC (0) +#define RTEMS_RFS_SB_MAGIC (0x28092001) +#define RTEMS_RFS_SB_OFFSET_VERSION (RTEMS_RFS_SB_OFFSET_MAGIC + 4) +#define RTEMS_RFS_SB_OFFSET_BLOCK_SIZE (RTEMS_RFS_SB_OFFSET_VERSION + 4) +#define RTEMS_RFS_SB_OFFSET_BLOCKS (RTEMS_RFS_SB_OFFSET_BLOCK_SIZE + 4) +#define RTEMS_RFS_SB_OFFSET_BAD_BLOCKS (RTEMS_RFS_SB_OFFSET_BLOCKS + 4) +#define RTEMS_RFS_SB_OFFSET_MAX_NAME_LENGTH (RTEMS_RFS_SB_OFFSET_BAD_BLOCKS + 4) +#define RTEMS_RFS_SB_OFFSET_GROUPS (RTEMS_RFS_SB_OFFSET_MAX_NAME_LENGTH + 4) +#define RTEMS_RFS_SB_OFFSET_GROUP_BLOCKS (RTEMS_RFS_SB_OFFSET_GROUPS + 4) +#define RTEMS_RFS_SB_OFFSET_GROUP_INODES (RTEMS_RFS_SB_OFFSET_GROUP_BLOCKS + 4) +#define RTEMS_RFS_SB_OFFSET_INODE_SIZE (RTEMS_RFS_SB_OFFSET_GROUP_INODES + 4) + +/** + * RFS Version Number. + */ +#define RTEMS_RFS_VERSION (0x00000000) + +/** + * RFS Version Number Mask. The mask determines which bits of the version + * number indicate compatility issues. + */ +#define RTEMS_RFS_VERSION_MASK INT32_C(0x00000000) + +/** + * The root inode number. Do not use 0 as this has special meaning in some Unix + * operating systems. + */ +#define RTEMS_RFS_ROOT_INO (1) + +/** + * Empty inode number. + */ +#define RTEMS_RFS_EMPTY_INO (0) + +/** + * The number of blocks in the inode. This number effects the size of the inode + * and that effects the overhead of the inode tables in a group. + */ +#define RTEMS_RFS_INODE_BLOCKS (5) + +/** + * The inode overhead is the percentage of space reserved for inodes. It is + * calculated as the percentage number of blocks in a group. The number of + * blocks in a group is the number of bits a block can hold. + */ +#define RTEMS_RFS_INODE_OVERHEAD_PERCENTAGE (1) + +/** + * Number of blocks in the superblock. Yes I know it is a superblock and not + * superblocks but if for any reason this needs to change it is handled. + */ +#define RTEMS_RFS_SUPERBLOCK_SIZE (1) + +/** + * The maximum number of buffers held by the file system at any one time. + */ +#define RTEMS_RFS_FS_MAX_HELD_BUFFERS (5) + +/** + * Absolute position. Make a 64bit value. + */ +typedef uint64_t rtems_rfs_pos; + +/** + * Relative position. Make a 64bit value. + */ +typedef int64_t rtems_rfs_pos_rel; + +/** + * Flags to control the file system. + */ +#define RTEMS_RFS_FS_BITMAPS_HOLD (1 << 0) /**< Do not release bitmaps + * when finished. Default is + * off so they are released. */ +#define RTEMS_RFS_FS_NO_LOCAL_CACHE (1 << 1) /**< Do not cache the buffers + * and release directly to the + * buffer support layer. The + * default is to hold buffers. */ +#define RTEMS_RFS_FS_FORCE_OPEN (1 << 2) /**< Force open and ignore any + * errors. */ +#define RTEMS_RFS_FS_READ_ONLY (1 << 3) /**< Make the mount + * read-only. Currently not + * supported. */ +/** + * RFS File System data. + */ +struct _rtems_rfs_file_system +{ + /** + * Flags to control the file system. Some can be controlled via the ioctl. + */ + uint32_t flags; + + /** + * The number of blocks in the disk. The size of the disk is the number of + * blocks by the block size. This should be within a block size of the size + * returned by the media driver. + */ + size_t blocks; + + /** + * The size of a block. This must be a multiple of the disk's media block + * size. + */ + size_t block_size; + +#if RTEMS_RFS_USE_LIBBLOCK + /** + * The disk device. This is the data about the block device this file system + * is mounted on. We access the data held in this structure rather than + * making an extra copy in this structure. + */ + rtems_disk_device* disk; +#else + /** + * The device number which is a the file handle for device I/O. + */ + dev_t device; + + /** + * The number of blocks in the file system. + */ + size_t size; +#endif + + /** + * Inode count. + */ + uint32_t inodes; + + /** + * Bad block blocks. This is a table of blocks that have been found to be + * bad. + */ + uint32_t bad_blocks; + + /** + * Maximum length of names supported by this file system. + */ + uint32_t max_name_length; + + /** + * A disk is broken down into a series of groups. + */ + rtems_rfs_group* groups; + + /** + * Number of groups. + */ + int group_count; + + /** + * Number of blocks in a group. + */ + size_t group_blocks; + + /** + * Number of inodes in a group. + */ + size_t group_inodes; + + /** + * Number of inodes in each block. + */ + size_t inodes_per_block; + + /** + * Number of block numbers in a block. + */ + size_t blocks_per_block; + + /** + * Block map single indirect count. This is the block number per block + * multiplied but the slots in the inode. + */ + size_t block_map_singly_blocks; + + /** + * Block map double indirect count. This is the block number per block + * squared and multiplied by the slots in the inode. It is the maximum + * number of blocks a map (file/directory) can have. + */ + size_t block_map_doubly_blocks; + + /** + * Number of buffers held before releasing back to the cache. + */ + uint32_t max_held_buffers; + + /** + * List of buffers attached to buffer handles. Allows sharing. + */ + rtems_chain_control buffers; + + /** + * Number of buffers held on the buffers list. + */ + uint32_t buffers_count; + + /** + * List of buffers that need to be released when the processing of a file + * system request has completed. + */ + rtems_chain_control release; + + /** + * Number of buffers held on the release list. + */ + uint32_t release_count; + + /** + * List of buffers that need to be released modified when the processing of a + * file system request has completed. + */ + rtems_chain_control release_modified; + + /** + * Number of buffers held on the release modified list. + */ + uint32_t release_modified_count; + + /** + * List of open shared file node data. The shared node data such as the inode + * and block map allows a single file to be open more than once. + */ + rtems_chain_control file_shares; + + /** + * Pointer to user data supplied when opening. + */ + void* user; +}; + +/** + * Return the flags. + * + * @param _fs Pointer to the file system. + */ +#define rtems_rfs_fs_flags(_f) ((_f)->flags) +/** + * Should bitmap buffers be released when finished ? + * + * @param _fs Pointer to the file system. + */ +#define rtems_rfs_fs_release_bitmaps(_f) (!((_f)->flags & RTEMS_RFS_FS_BITMAPS_HOLD)) + +/** + * Are the buffers locally cache or released back to the buffering layer ? + * + * @param _fs Pointer to the file system. + */ +#define rtems_rfs_fs_no_local_cache(_f) ((_f)->flags & RTEMS_RFS_FS_NO_LOCAL_CACHE) + +/** + * The disk device number. + * + * @param _fs Pointer to the file system. + */ +#if RTEMS_RFS_USE_LIBBLOCK +#define rtems_rfs_fs_device(_fs) ((_fs)->disk->dev) +#else +#define rtems_rfs_fs_device(_fs) ((_fs)->device) +#endif + +/** + * The size of the disk in blocks. + * + * @param _fs Pointer to the file system. + */ +#define rtems_rfs_fs_blocks(_fs) ((_fs)->blocks) + +/** + * The block size. + * + * @param _fs Pointer to the file system. + */ +#define rtems_rfs_fs_block_size(_fs) ((_fs)->block_size) + +/** + * The number of inodes. + * + * @param _fs Pointer to the file system. + */ +#define rtems_rfs_fs_inodes(_fs) ((_fs)->inodes) + +/** + * Calculate a block in the file system given the group and the block within + * the group. + * + * @param _fs Pointer to the file system. + * @param _grp The group. + * @param _blk The block within the group. + * @return The absolute block number. + */ +#define rtems_rfs_fs_block(_fs, _grp, _blk) \ + ((((_fs)->group_blocks) * (_grp)) + (_blk) + 1) + +/** + * The media size of the disk in media size blocks. + * + * @param _fs Pointer to the file system. + */ +#if RTEMS_RFS_USE_LIBBLOCK +#define rtems_rfs_fs_media_blocks(_fs) ((_fs)->disk->size) +#else +#define rtems_rfs_fs_media_blocks(_fs) ((_fs)->media_size) +#endif + +/** + * The media block size. This is the size of a block on disk. For a device I/O + * this value is 1. + * + * @param _fs Pointer to the file system. + */ +#if RTEMS_RFS_USE_LIBBLOCK +#define rtems_rfs_fs_media_block_size(_fs) ((_fs)->disk->media_block_size) +#else +#define rtems_rfs_fs_media_block_size(_fs) (1) +#endif + +/** + * The maximum length of a name supported by the file system. + */ +#define rtems_rfs_fs_max_name(_fs) ((_fs)->max_name_length) + +/** + * Return the maximum number of blocks in a block map. + * + * @return uint32_t The maximum number of blocks possible. + */ +#define rtems_rfs_fs_max_block_map_blocks(_fs) ((_fs)->block_map_doubly_blocks) + +/** + * Return the user pointer. + */ +#define rtems_rfs_fs_user(_fs) ((_fs)->user) + +/** + * Return the size of the disk in bytes. + * + * @param fs Pointer to the file system. + * @return uint64_t The size of the disk in bytes. + */ +uint64_t rtems_rfs_fs_size(rtems_rfs_file_system* fs); + +/** + * The size of the disk in bytes calculated from the media parameters.. + * + * @param fs Pointer to the file system. + * @return uint64_t The size of the disk in bytes. + */ +uint64_t rtems_rfs_fs_media_size (rtems_rfs_file_system* fs); + +/** + * Open the file system given a file path. + * + * @param name The device to open. + * @param fs The file system data filled in by this call. + * @param user A pointer to user data. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_fs_open (const char* name, + void* user, + uint32_t flags, + rtems_rfs_file_system** fs); + +/** + * Close the file system. + * + * @param fs The file system data. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_fs_close (rtems_rfs_file_system* fs); + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-file.c b/cpukit/libfs/src/rfs/rtems-rfs-file.c new file mode 100644 index 0000000000..777726a23a --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-file.c @@ -0,0 +1,596 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems File Routines. + * + * These functions manage files. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <rtems/rfs/rtems-rfs-block-pos.h> +#include <rtems/rfs/rtems-rfs-file.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-trace.h> + +int +rtems_rfs_file_open (rtems_rfs_file_system* fs, + rtems_rfs_ino ino, + uint32_t flags, + rtems_rfs_file_handle** file) +{ + rtems_rfs_file_handle* handle; + rtems_rfs_file_shared* shared; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN)) + printf ("rtems-rfs: file-open: ino=%" PRId32 "\n", ino); + + *file = NULL; + + /* + * Allocate a new handle and initialise it. Do this before we deal with the + * shared node data so we do not have to be concerned with reference + * counting. + */ + handle = malloc (sizeof (rtems_rfs_file_handle)); + if (!handle) + return ENOMEM; + + memset (handle, 0, sizeof (rtems_rfs_file_handle)); + + rc = rtems_rfs_buffer_handle_open (fs, &handle->buffer); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN)) + printf ("rtems-rfs: file-open: buffer handle open failed: %d: %s\n", + rc, strerror (rc)); + free (handle); + return rc; + } + + /* + * Scan the file system data list of open files for this ino. If found up + * the reference count and return the pointer to the data. + */ + shared = rtems_rfs_file_get_shared (fs, ino); + if (shared) + { + shared->references++; + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN)) + printf ("rtems-rfs: file-open: ino=%" PRId32 " shared\n", ino); + } + else + { + /* + * None exists so create. Copy in the shared parts of the inode we hold in + * memory. + */ + shared = malloc (sizeof (rtems_rfs_file_shared)); + if (!shared) + { + rtems_rfs_buffer_handle_close (fs, &handle->buffer); + free (handle); + return ENOMEM; + } + + memset (shared, 0, sizeof (rtems_rfs_file_shared)); + + rc = rtems_rfs_inode_open (fs, ino, &shared->inode, true); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN)) + printf ("rtems-rfs: file-open: inode open failed: %d: %s\n", + rc, strerror (rc)); + free (shared); + rtems_rfs_buffer_handle_close (fs, &handle->buffer); + free (handle); + return rc; + } + + rc = rtems_rfs_block_map_open (fs, &shared->inode, &shared->map); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN)) + printf ("rtems-rfs: file-open: block map open failed: %d: %s\n", + rc, strerror (rc)); + rtems_rfs_inode_close (fs, &shared->inode); + free (shared); + rtems_rfs_buffer_handle_close (fs, &handle->buffer); + free (handle); + return rc; + } + + shared->references = 1; + shared->size.count = rtems_rfs_inode_get_block_count (&shared->inode); + shared->size.offset = rtems_rfs_inode_get_block_offset (&shared->inode); + shared->atime = rtems_rfs_inode_get_atime (&shared->inode); + shared->mtime = rtems_rfs_inode_get_mtime (&shared->inode); + shared->ctime = rtems_rfs_inode_get_ctime (&shared->inode); + shared->fs = fs; + + rtems_chain_append (&fs->file_shares, &shared->link); + + rtems_rfs_inode_unload (fs, &shared->inode, false); + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN)) + printf ("rtems-rfs: file-open: ino=%" PRId32 " share created\n", ino); + } + + handle->flags = flags; + handle->shared = shared; + + *file = handle; + + return 0; +} + +int +rtems_rfs_file_close (rtems_rfs_file_system* fs, + rtems_rfs_file_handle* handle) +{ + int rrc; + int rc; + + rrc = 0; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_CLOSE)) + printf ("rtems-rfs: file-close: entry: ino=%" PRId32 "\n", + handle->shared->inode.ino); + + if (handle->shared->references > 0) + handle->shared->references--; + + if (handle->shared->references == 0) + { + if (!rtems_rfs_inode_is_loaded (&handle->shared->inode)) + rrc = rtems_rfs_inode_load (fs, &handle->shared->inode); + + if (rrc == 0) + { + /* + * @todo This could be clever and only update if different. + */ + rtems_rfs_inode_set_atime (&handle->shared->inode, + handle->shared->atime); + rtems_rfs_inode_set_mtime (&handle->shared->inode, + handle->shared->mtime); + rtems_rfs_inode_set_ctime (&handle->shared->inode, + handle->shared->ctime); + if (!rtems_rfs_block_size_equal (&handle->shared->size, + &handle->shared->map.size)) + rtems_rfs_block_map_set_size (&handle->shared->map, + &handle->shared->size); + } + + rc = rtems_rfs_block_map_close (fs, &handle->shared->map); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_CLOSE)) + printf ("rtems-rfs: file-close: map close error: ino=%" PRId32 ": %d: %s\n", + handle->shared->inode.ino, rc, strerror (rc)); + if (rrc == 0) + rrc = rc; + } + + rc = rtems_rfs_inode_close (fs, &handle->shared->inode); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_CLOSE)) + printf ("rtems-rfs: file-close: inode close error: ino=%" PRId32 ": %d: %s\n", + handle->shared->inode.ino, rc, strerror (rc)); + if (rrc == 0) + rrc = rc; + } + + rtems_chain_extract (&handle->shared->link); + free (handle->shared); + } + + rc = rtems_rfs_buffer_handle_close (fs, &handle->buffer); + if ((rrc == 0) && (rc > 0)) + rrc = rc; + + if (rrc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_CLOSE)) + printf ("rtems-rfs: file-close: result: %d: %s\n", rrc, strerror (rrc)); + } + + free (handle); + + return rrc; +} + +int +rtems_rfs_file_io_start (rtems_rfs_file_handle* handle, + size_t* available, + bool read) +{ + size_t size; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO)) + printf ("rtems-rfs: file-io: start: %s pos=%" PRIu32 ":%" PRIu32 "\n", + read ? "read" : "write", handle->bpos.bno, handle->bpos.boff); + + if (!rtems_rfs_buffer_handle_has_block (&handle->buffer)) + { + rtems_rfs_buffer_block block; + bool request_read; + int rc; + + request_read = read; + + rc = rtems_rfs_block_map_find (rtems_rfs_file_fs (handle), + rtems_rfs_file_map (handle), + rtems_rfs_file_bpos (handle), + &block); + if (rc > 0) + { + /* + * Has the read reached the EOF ? + */ + if (read && (rc == ENXIO)) + { + *available = 0; + return 0; + } + + if (rc != ENXIO) + return rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO)) + printf ("rtems-rfs: file-io: start: grow\n"); + + rc = rtems_rfs_block_map_grow (rtems_rfs_file_fs (handle), + rtems_rfs_file_map (handle), + 1, &block); + if (rc > 0) + return rc; + + request_read = false; + } + else + { + /* + * If this is a write check if the write starts within a block or the + * amount of data is less than a block size. If it is read the block + * rather than getting a block to fill. + */ + if (!read && + (rtems_rfs_file_block_offset (handle) || + (*available < rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle))))) + request_read = true; + } + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO)) + printf ("rtems-rfs: file-io: start: block=%" PRIu32 " request-read=%s\n", + block, request_read ? "yes" : "no"); + + rc = rtems_rfs_buffer_handle_request (rtems_rfs_file_fs (handle), + rtems_rfs_file_buffer (handle), + block, request_read); + if (rc > 0) + return rc; + } + + if (read + && rtems_rfs_block_map_last (rtems_rfs_file_map (handle)) + && rtems_rfs_block_map_size_offset (rtems_rfs_file_map (handle))) + size = rtems_rfs_block_map_size_offset (rtems_rfs_file_map (handle)); + else + size = rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle)); + + *available = size - rtems_rfs_file_block_offset (handle); + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO)) + printf ("rtems-rfs: file-io: start: available=%zu (%zu)\n", + *available, size); + + return 0; +} + +int +rtems_rfs_file_io_end (rtems_rfs_file_handle* handle, + size_t size, + bool read) +{ + bool atime; + bool mtime; + bool length; + int rc = 0; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO)) + printf ("rtems-rfs: file-io: end: %s size=%zu\n", + read ? "read" : "write", size); + + if (rtems_rfs_buffer_handle_has_block (&handle->buffer)) + { + if (!read) + rtems_rfs_buffer_mark_dirty (rtems_rfs_file_buffer (handle)); + rc = rtems_rfs_buffer_handle_release (rtems_rfs_file_fs (handle), + rtems_rfs_file_buffer (handle)); + if (rc > 0) + { + printf ( + "rtems-rfs: file-io: end: error on release: %s size=%zu: %d: %s\n", + read ? "read" : "write", size, rc, strerror (rc)); + + return rc; + } + } + + /* + * Update the handle's position. Only a block size can be handled at a time + * so no special maths is needed. If the offset is bigger than the block size + * increase the block number and adjust the offset. + * + * If we are the last block and the position is past the current size update + * the size with the new length. The map holds the block count. + */ + handle->bpos.boff += size; + + if (handle->bpos.boff >= + rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle))) + { + handle->bpos.bno++; + handle->bpos.boff -= rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle)); + } + + length = false; + mtime = false; + + if (!read && + rtems_rfs_block_map_past_end (rtems_rfs_file_map (handle), + rtems_rfs_file_bpos (handle))) + { + rtems_rfs_block_map_set_size_offset (rtems_rfs_file_map (handle), + handle->bpos.boff); + length = true; + mtime = true; + } + + atime = rtems_rfs_file_update_atime (handle); + mtime = rtems_rfs_file_update_mtime (handle) && mtime; + length = rtems_rfs_file_update_length (handle) && length; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO)) + printf ("rtems-rfs: file-io: end: pos=%" PRIu32 ":%" PRIu32 " %c %c %c\n", + handle->bpos.bno, handle->bpos.boff, + atime ? 'A' : '-', mtime ? 'M' : '-', length ? 'L' : '-'); + + if (atime || mtime) + { + time_t now = time (NULL); + if (read && atime) + handle->shared->atime = now; + if (!read && mtime) + handle->shared->mtime = now; + } + if (length) + { + handle->shared->size.count = + rtems_rfs_block_map_count (rtems_rfs_file_map (handle)); + handle->shared->size.offset = + rtems_rfs_block_map_size_offset (rtems_rfs_file_map (handle)); + } + + return rc; +} + +int +rtems_rfs_file_io_release (rtems_rfs_file_handle* handle) +{ + int rc = 0; + if (rtems_rfs_buffer_handle_has_block (&handle->buffer)) + rc = rtems_rfs_buffer_handle_release (rtems_rfs_file_fs (handle), + rtems_rfs_file_buffer (handle)); + return rc; +} + +int +rtems_rfs_file_seek (rtems_rfs_file_handle* handle, + rtems_rfs_pos pos, + rtems_rfs_pos* new_pos) +{ + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO)) + printf ("rtems-rfs: file-seek: new=%" PRIu64 "\n", pos); + + /* + * This call only sets the position if it is in a valid part of the file. The + * user can request past the end of the file then write to extend the + * file. The lseek entry states: + * + * "Although lseek() may position the file offset beyond the end of the + * file, this function does not itself extend the size of the file." + * + * This means the file needs to set the file size to the pos only when a + * write occurs. + */ + if (pos < rtems_rfs_file_shared_get_size (rtems_rfs_file_fs (handle), + handle->shared)) + rtems_rfs_file_set_bpos (handle, pos); + + *new_pos = pos; + return 0; +} + +int +rtems_rfs_file_set_size (rtems_rfs_file_handle* handle, + rtems_rfs_pos new_size) +{ + rtems_rfs_block_map* map = rtems_rfs_file_map (handle); + rtems_rfs_pos size; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO)) + printf ("rtems-rfs: file-set-size: size=%" PRIu64 "\n", new_size); + + /* + * Short cut for the common truncate on open call. + */ + if (new_size == 0) + { + rc = rtems_rfs_block_map_free_all (rtems_rfs_file_fs (handle), map); + if (rc > 0) + return rc; + } + else + { + size = rtems_rfs_file_size (handle); + + /* + * If the file is same size do nothing else grow or shrink it ? + */ + if (size != new_size) + { + if (size < new_size) + { + /* + * Grow. Fill with 0's. + */ + rtems_rfs_pos count; + uint32_t length; + bool read_block; + + count = new_size - size; + length = rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle)); + read_block = false; + + while (count) + { + rtems_rfs_buffer_block block; + rtems_rfs_block_pos bpos; + uint8_t* dst; + + /* + * Get the block position for the current end of the file as seen by + * the map. If not found and the EOF grow the map then fill the block + * with 0. + */ + rtems_rfs_block_size_get_bpos (rtems_rfs_block_map_size (map), &bpos); + rc = rtems_rfs_block_map_find (rtems_rfs_file_fs (handle), + map, &bpos, &block); + if (rc > 0) + { + /* + * Have we reached the EOF ? + */ + if (rc != ENXIO) + return rc; + + rc = rtems_rfs_block_map_grow (rtems_rfs_file_fs (handle), + map, 1, &block); + if (rc > 0) + return rc; + } + + if (count < (length - bpos.boff)) + { + length = count + bpos.boff; + read_block = true; + rtems_rfs_block_map_set_size_offset (map, length); + } + else + { + rtems_rfs_block_map_set_size_offset (map, 0); + } + + /* + * Only read the block if the length is not the block size. + */ + rc = rtems_rfs_buffer_handle_request (rtems_rfs_file_fs (handle), + rtems_rfs_file_buffer (handle), + block, read_block); + if (rc > 0) + return rc; + + dst = rtems_rfs_buffer_data (&handle->buffer); + memset (dst + bpos.boff, 0, length - bpos.boff); + + rtems_rfs_buffer_mark_dirty (rtems_rfs_file_buffer (handle)); + + rc = rtems_rfs_buffer_handle_release (rtems_rfs_file_fs (handle), + rtems_rfs_file_buffer (handle)); + if (rc > 0) + return rc; + + count -= length - bpos.boff; + } + } + else + { + /* + * Shrink + */ + rtems_rfs_block_no blocks; + uint32_t offset; + + blocks = + rtems_rfs_block_map_count (map) - + (((new_size - 1) / + rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle))) + 1); + + offset = + new_size % rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle)); + + if (blocks) + { + int rc; + rc = rtems_rfs_block_map_shrink (rtems_rfs_file_fs (handle), + rtems_rfs_file_map (handle), + blocks); + if (rc > 0) + return rc; + } + + rtems_rfs_block_map_set_size_offset (map, offset); + + if (rtems_rfs_block_pos_past_end (rtems_rfs_file_bpos (handle), + rtems_rfs_block_map_size (map))) + rtems_rfs_block_size_get_bpos (rtems_rfs_block_map_size (map), + rtems_rfs_file_bpos (handle)); + } + } + } + + handle->shared->size.count = rtems_rfs_block_map_count (map); + handle->shared->size.offset = rtems_rfs_block_map_size_offset (map); + + if (rtems_rfs_file_update_mtime (handle)) + handle->shared->mtime = time (NULL); + + return 0; +} + +rtems_rfs_file_shared* +rtems_rfs_file_get_shared (rtems_rfs_file_system* fs, + rtems_rfs_ino ino) +{ + rtems_chain_node* node; + node = rtems_chain_first (&fs->file_shares); + while (!rtems_chain_is_tail (&fs->file_shares, node)) + { + rtems_rfs_file_shared* shared; + shared = (rtems_rfs_file_shared*) node; + if (shared->inode.ino == ino) + return shared; + node = rtems_chain_next (node); + } + return NULL; +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-file.h b/cpukit/libfs/src/rfs/rtems-rfs-file.h new file mode 100644 index 0000000000..54a2187df3 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-file.h @@ -0,0 +1,393 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System File Support + * + * This file provides the support functions. + */ + +#if !defined (_RTEMS_RFS_FILE_H_) +#define _RTEMS_RFS_FILE_H_ + +#include <rtems/libio_.h> + +#include <rtems/rfs/rtems-rfs-block.h> +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> + +/** + * File data that is shared by various file handles accessing the same file. We + * hold various inode values common to the file that can change frequently so + * the inode is not thrashed yet we meet the requirements of the POSIX + * standard. The stat call needs to check the shared file data. + */ +typedef struct _rtems_rfs_file_shared +{ + /** + * The shared parts are maintained as a list. + */ + rtems_chain_node link; + + /** + * Reference count the users of this data. + */ + int references; + + /** + * The inode for the file. + */ + rtems_rfs_inode_handle inode; + + /** + * The block map for the file. The handle holds the file's position not the + * map. + */ + rtems_rfs_block_map map; + + /** + * The size of the file as taken from the inode. The map's size and + * this size should be the same. + */ + rtems_rfs_block_size size; + + /** + * The access time. The last time the file was read. + */ + rtems_rfs_time atime; + + /** + * The modified time. The last time the file was written too. + */ + rtems_rfs_time mtime; + + /** + * The change time. The last time the inode was written too. + */ + rtems_rfs_time ctime; + + /** + * Hold a pointer to the file system data so users can take the handle and + * use it without the needing to hold the file system data pointer. + */ + rtems_rfs_file_system* fs; + +} rtems_rfs_file_shared; + +/** + * Get the atime. + * + * @param shared The shared file data. + * @return rtems_rfs_time The atime. + */ +static inline rtems_rfs_time +rtems_rfs_file_shared_get_atime (rtems_rfs_file_shared* shared) +{ + return shared->atime; +} + +/** + * Get the mtime. + * + * @param shared The shared file data. + * @return rtems_rfs_time The mtime. + */ +static inline rtems_rfs_time +rtems_rfs_file_shared_get_mtime (rtems_rfs_file_shared* shared) +{ + return shared->mtime; +} + +/** + * Get the ctime. + * + * @param shared The shared file data. + * @return rtems_rfs_time The ctime. + */ +static inline rtems_rfs_time +rtems_rfs_file_shared_get_ctime (rtems_rfs_file_shared* shared) +{ + return shared->ctime; +} + +/** + * Get the block count. + * + * @param shared The shared file data. + * @return uint32_t The block count. + */ +static inline uint32_t +rtems_rfs_file_shared_get_block_count (rtems_rfs_file_shared* shared) +{ + return shared->size.count; +} + +/** + * Get the block offset. + * + * @param shared The shared file data. + * @return uint16_t The block offset. + */ +static inline uint16_t +rtems_rfs_file_shared_get_block_offset (rtems_rfs_file_shared* shared) +{ + return shared->size.offset; +} + +/** + * Calculate the size of data. + * + * @param fs The file system data. + * @oaram shared The shared file data. + * @return rtems_rfs_pos The data size in bytes. + */ +static inline rtems_rfs_pos +rtems_rfs_file_shared_get_size (rtems_rfs_file_system* fs, + rtems_rfs_file_shared* shared) +{ + return rtems_rfs_block_get_size (fs, &shared->size); +} + +/** + * File flags. + */ +#define RTEMS_RFS_FILE_NO_ATIME_UPDATE (1 << 0) /**< Do not update the atime + * field in the inode if + * set. */ +#define RTEMS_RFS_FILE_NO_MTIME_UPDATE (1 << 1) /**< Do not update the mtime + * field in the inode if + * set. */ +#define RTEMS_RFS_FILE_NO_LENGTH_UPDATE (1 << 2) /**< Do not update the position + * field in the inode if + * set. */ + +/** + * File data used to managed an open file. + */ +typedef struct _rtems_rfs_file_handle +{ + /** + * Special flags that can be controlled by the fctrl call. + */ + uint32_t flags; + + /** + * The buffer of data at the file's position. + */ + rtems_rfs_buffer_handle buffer; + + /** + * The block position of this file handle. + */ + rtems_rfs_block_pos bpos; + + /** + * Pointer to the shared file data. + */ + rtems_rfs_file_shared* shared; + +} rtems_rfs_file_handle; + +/** + * Access the data in the buffer. + */ +#define rtems_rfs_file_data(_f) \ + (rtems_rfs_buffer_data (&(_f)->buffer) + (_f)->bpos.boff) + +/** + * Return the file system data pointer given a file handle. + */ +#define rtems_rfs_file_fs(_f) ((_f)->shared->fs) + +/** + * Return the file's inode handle pointer given a file handle. + */ +#define rtems_rfs_file_inode(_f) (&(_f)->shared->inode) + +/** + * Return the file's block map pointer given a file handle. + */ +#define rtems_rfs_file_map(_f) (&(_f)->shared->map) + +/** + * Return the file's block position pointer given a file handle. + */ +#define rtems_rfs_file_bpos(_f) (&(_f)->bpos) + +/** + * Return the file's block number given a file handle. + */ +#define rtems_rfs_file_block(_f) ((_f)->bpos.bno) + +/** + * Return the file's block offset given a file handle. + */ +#define rtems_rfs_file_block_offset(_f) ((_f)->bpos.boff) + +/** + * Set the file's block position given a file position (absolute). + */ +#define rtems_rfs_file_set_bpos(_f, _p) \ + rtems_rfs_block_get_bpos (rtems_rfs_file_fs (_f), _p, (&(_f)->bpos)) + +/** + * Return the file's buffer handle pointer given a file handle. + */ +#define rtems_rfs_file_buffer(_f) (&(_f)->buffer) + +/** + * Update the access time field of the inode when reading if flagged to do so. + */ +#define rtems_rfs_file_update_atime(_f) \ + (((_f)->flags & RTEMS_RFS_FILE_NO_ATIME_UPDATE) == 0) + +/** + * Update the modified time field of the inode when writing if flagged to do so. + */ +#define rtems_rfs_file_update_mtime(_f) \ + (((_f)->flags & RTEMS_RFS_FILE_NO_MTIME_UPDATE) == 0) + +/** + * Update the length field of the inode. + */ +#define rtems_rfs_file_update_length(_f) \ + (((_f)->flags & RTEMS_RFS_FILE_NO_LENGTH_UPDATE) == 0) + +/** + * Return the shared size varable. + */ +#define rtems_rfs_file_get_size(_f) \ + (&(_f)->shared->size) + +/** + * Return the size of file. + */ +#define rtems_rfs_file_size(_f) \ + rtems_rfs_file_shared_get_size (rtems_rfs_file_fs (_f), (_f)->shared) + +/** + * Return the file block count. + */ +#define rtems_rfs_file_size_count(_f) \ + rtems_rfs_file_shared_get_block_count ((_f)->shared) + +/** + * Return the file block offset. + */ +#define rtems_rfs_file_size_offset(_f) \ + rtems_rfs_file_shared_get_block_offset ((_f)->shared) + +/** + * Open a file handle. + * + * @param fs The file system. + * @param ino The inode number of the file to be opened. + * @param handle Return the handle pointer in this handle. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_file_open (rtems_rfs_file_system* fs, + rtems_rfs_ino ino, + uint32_t flags, + rtems_rfs_file_handle** handle); + +/** + * Close an open file handle. + * + * @param fs The file system. + * @param handle The open file handle. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_file_close (rtems_rfs_file_system* fs, + rtems_rfs_file_handle* handle); + +/** + * Start I/O on a block of a file. This call only requests the block from the + * media if reading and makes the buffer available to you the via the + * rtems_rfs_file_data interface after the call. The available amount data is + * taken from the current file position until the end of the block. The file + * position is not adujsted until the I/O ends. An I/O request cannot perform + * I/O past the end of a block so the call returns the amount of data + * available. + * + * @param handle The file handle. + * @param available The amount of data available for I/O. + * @param read The I/O operation is a read so the block is read from the media. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_file_io_start (rtems_rfs_file_handle* handle, + size_t* available, + bool read); + +/** + * End the I/O. Any buffers held in the file handle and returned to the + * cache. If inode updating is not disable and the I/O is a read the atime + * field is updated and if a write I/O the mtime is updated. + * + * If the file's position is updated by the size amount. + * + * @param handle The file handle. + * @param size The amount of data read or written. + * @param read The I/O was a read if true else it was a write. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_file_io_end (rtems_rfs_file_handle* handle, + size_t size, + bool read); + +/** + * Release the I/O resources without any changes. If data has changed in the + * buffer and the buffer was not already released as modified the data will be + * lost. + * + * @param handle The file handle. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_file_io_release (rtems_rfs_file_handle* handle); + +/** + * The file to the position returning the old position. The position is + * abolute. + * + * @param handle The file handle. + * @param pos The position to seek to. + * @param new_pos The actual position. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_file_seek (rtems_rfs_file_handle* handle, + rtems_rfs_pos pos, + rtems_rfs_pos* new_pos); + +/** + * Set the size of the file to the new size. This can extend the file to a new + * size. + * + * @param handle The file handle. + * @param size The new size of the file. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_file_set_size (rtems_rfs_file_handle* handle, + rtems_rfs_pos size); + +/** + * Return the shared file data for an ino. + * + * @param fs The file system data. + * @param ino The inode number to locate the data for. + * @return rtems_rfs_file_shared* The shared data or NULL is not located. + */ +rtems_rfs_file_shared* rtems_rfs_file_get_shared (rtems_rfs_file_system* fs, + rtems_rfs_ino ino); + + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-format.c b/cpukit/libfs/src/rfs/rtems-rfs-format.c new file mode 100644 index 0000000000..8199466bfb --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-format.c @@ -0,0 +1,644 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Format + * + * Format the file system ready for use. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <inttypes.h> + +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> +#include <rtems/rtems-rfs-format.h> +#include <rtems/rfs/rtems-rfs-dir.h> + +/** + * Return the number of gigabytes. + */ +#define GIGS(_g) (((uint64_t)(_g)) * 1024 * 1024) + +/** + * Return the number of bits that fit in the block size. + */ +static int +rtems_rfs_bits_per_block (rtems_rfs_file_system* fs) +{ + return rtems_rfs_bitmap_numof_bits (rtems_rfs_fs_block_size (fs)); +} + +/** + * Return a rounded up integer quotient given a dividend and divisor. That is: + * "quotient = dividend / divisor" + */ +int +rtems_rfs_rup_quotient (uint32_t dividend, uint32_t divisor) +{ + if (dividend == 0) + return 1; + return ((dividend - 1) / divisor) + 1; +} + +/** + * Return the number of inodes as a percentage of the total number that can fit + * in a blocl. + */ +static int +rtems_rfs_inodes_from_percent (rtems_rfs_file_system* fs, + int percentage) +{ + int blocks; + blocks = ((rtems_rfs_fs_blocks (fs) - + RTEMS_RFS_SUPERBLOCK_SIZE) * percentage) / 100; + blocks = rtems_rfs_rup_quotient (blocks, fs->group_count); + return blocks * (rtems_rfs_fs_block_size (fs) / RTEMS_RFS_INODE_SIZE); +} + +/** + * Return the inode overhead given a number of inodes. + */ +static int +rtems_rfs_inode_overhead (rtems_rfs_file_system* fs) +{ + int blocks; + int bits_per_block; + blocks = rtems_rfs_rup_quotient(fs->group_inodes * RTEMS_RFS_INODE_SIZE, + rtems_rfs_fs_block_size (fs)); + bits_per_block = rtems_rfs_bits_per_block (fs); + /* + * There could be more bits than blocks, eg 512K disk with 512 blocks. + */ + if (bits_per_block > (rtems_rfs_fs_blocks (fs) - RTEMS_RFS_SUPERBLOCK_SIZE)) + bits_per_block = rtems_rfs_fs_blocks (fs) - RTEMS_RFS_SUPERBLOCK_SIZE; + return ((blocks + 1) * 100 * 10) / bits_per_block; +} + +static bool +rtems_rfs_check_config (rtems_rfs_file_system* fs, + const rtems_rfs_format_config* config) +{ + fs->block_size = config->block_size; + if (!fs->block_size) + { + uint64_t total_size = rtems_rfs_fs_media_size (fs); + + if (total_size >= GIGS (1)) + { + uint32_t gigs = (total_size + GIGS (1)) / GIGS (1); + int b; + for (b = 31; b > 0; b--) + if ((gigs & (1 << b)) != 0) + break; + fs->block_size = 1 << b; + } + + if (fs->block_size < 512) + fs->block_size = 512; + + if (fs->block_size > (4 * 1024)) + fs->block_size = (4 * 1024); + } + + if ((fs->block_size % rtems_rfs_fs_media_block_size (fs)) != 0) + { + printf ("block size (%zd) is not a multiple of media block size (%" PRId32 ")\n", + fs->block_size, rtems_rfs_fs_media_block_size (fs)); + return false; + } + + fs->group_blocks = config->group_blocks; + if (!fs->group_blocks) + { + /* + * The number of blocks per group is defined by the number of bits in a + * block. + */ + fs->group_blocks = rtems_rfs_bitmap_numof_bits (fs->block_size); + } + + if (fs->group_blocks > rtems_rfs_bitmap_numof_bits (fs->block_size)) + { + printf ("group block count is higher than bits in block\n"); + return false; + } + + fs->blocks = rtems_rfs_fs_media_size (fs) / fs->block_size; + + /* + * The bits per block sets the upper limit for the number of blocks in a + * group. The disk will be divided into groups which are the number of bits + * per block. + */ + fs->group_count = rtems_rfs_rup_quotient (rtems_rfs_fs_blocks (fs), + rtems_rfs_bits_per_block (fs)); + + fs->group_inodes = config->group_inodes; + if (!fs->group_inodes) + { + int inode_overhead = RTEMS_RFS_INODE_OVERHEAD_PERCENTAGE; + + /* + * The number of inodes per group is set as a percentage. + */ + if (config->inode_overhead) + inode_overhead = config->inode_overhead; + + fs->group_inodes = rtems_rfs_inodes_from_percent (fs, inode_overhead); + } + + /* + * Round up to fill a block because the minimum allocation unit is a block. + */ + fs->inodes_per_block = rtems_rfs_fs_block_size (fs) / RTEMS_RFS_INODE_SIZE; + fs->group_inodes = + rtems_rfs_rup_quotient (fs->group_inodes, + fs->inodes_per_block) * fs->inodes_per_block; + + if (fs->group_inodes > rtems_rfs_bitmap_numof_bits (fs->block_size)) + fs->group_inodes = rtems_rfs_bitmap_numof_bits (fs->block_size); + + fs->max_name_length = config->max_name_length; + if (!fs->max_name_length) + { + fs->max_name_length = 512; + } + + return true; +} + +static bool +rtems_rfs_write_group (rtems_rfs_file_system* fs, + int group, + bool initialise_inodes, + bool verbose) +{ + rtems_rfs_buffer_handle handle; + rtems_rfs_bitmap_control bitmap; + rtems_rfs_buffer_block group_base; + size_t group_size; + int blocks; + int b; + int rc; + + group_base = rtems_rfs_fs_block (fs, group, 0); + + if (group_base > rtems_rfs_fs_blocks (fs)) + { + printf ("rtems-rfs: write-group: group %d base beyond disk limit\n", + group); + return false; + } + + group_size = fs->group_blocks; + + /* + * Be nice to strange sizes of disks. These are embedded systems after all + * and nice numbers do not always work out. Let the last block pick up the + * remainder of the blocks. + */ + if ((group_base + group_size) > rtems_rfs_fs_blocks (fs)) + group_size = rtems_rfs_fs_blocks (fs) - group_base; + + if (verbose) + printf ("\rrtems-rfs: format: group %3d: base = %" PRId32 ", size = %zd", + group, group_base, group_size); + + /* + * Open a handle and request an empty buffer. + */ + rc = rtems_rfs_buffer_handle_open (fs, &handle); + if (rc > 0) + { + printf ("\nrtems-rfs: write-group: handle open failed: %d: %s\n", + rc, strerror (rc)); + return false; + } + + if (verbose) + printf (", blocks"); + + /* + * Open the block bitmap using the new buffer. + */ + rc = rtems_rfs_bitmap_open (&bitmap, fs, &handle, group_size, + group_base + RTEMS_RFS_GROUP_BLOCK_BITMAP_BLOCK); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &handle); + printf ("\nrtems-rfs: write-group: group %3d: open block bitmap failed: %d: %s\n", + group, rc, strerror (rc)); + return false; + } + + /* + * Force the whole buffer to a known state. The bit map may not occupy the + * whole block. + */ + memset (rtems_rfs_buffer_data (&handle), 0xff, rtems_rfs_fs_block_size (fs)); + + /* + * Clear the bitmap. + */ + rc = rtems_rfs_bitmap_map_clear_all (&bitmap); + if (rc > 0) + { + rtems_rfs_bitmap_close (&bitmap); + rtems_rfs_buffer_handle_close (fs, &handle); + printf ("\nrtems-rfs: write-group: group %3d: block bitmap clear all failed: %d: %s\n", + group, rc, strerror (rc)); + return false; + } + + /* + * Forced allocation of the block bitmap. + */ + rtems_rfs_bitmap_map_set (&bitmap, RTEMS_RFS_GROUP_BLOCK_BITMAP_BLOCK); + + /* + * Forced allocation of the inode bitmap. + */ + rtems_rfs_bitmap_map_set (&bitmap, RTEMS_RFS_GROUP_INODE_BITMAP_BLOCK); + + /* + * Determine the number of inodes blocks in the group. + */ + blocks = rtems_rfs_rup_quotient (fs->group_inodes, fs->inodes_per_block); + + /* + * Forced allocation of the inode blocks which follow the block bitmap. + */ + for (b = 0; b < blocks; b++) + rtems_rfs_bitmap_map_set (&bitmap, b + RTEMS_RFS_GROUP_INODE_BLOCK); + + /* + * Close the block bitmap. + */ + rc = rtems_rfs_bitmap_close (&bitmap); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &handle); + printf ("\nrtems-rfs: write-group: group %3d: close block bitmap failed: %d: %s\n", + group, rc, strerror (rc)); + return false; + } + + rtems_rfs_buffer_mark_dirty (&handle); + + if (verbose) + printf (", inodes"); + + /* + * Open the inode bitmap using the old buffer. Should release any changes. + */ + rc = rtems_rfs_bitmap_open (&bitmap, fs, &handle, group_size, + group_base + RTEMS_RFS_GROUP_INODE_BITMAP_BLOCK); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &handle); + printf ("\nrtems-rfs: write-group: group %3d: open inode bitmap failed: %d: %s\n", + group, rc, strerror (rc)); + return false; + } + + /* + * Force the whole buffer to a known state. The bit map may not occupy the + * whole block. + */ + memset (rtems_rfs_buffer_data (&handle), 0x00, rtems_rfs_fs_block_size (fs)); + + /* + * Clear the inode bitmap. + */ + rc = rtems_rfs_bitmap_map_clear_all (&bitmap); + if (rc > 0) + { + rtems_rfs_bitmap_close (&bitmap); + rtems_rfs_buffer_handle_close (fs, &handle); + printf ("\nrtems-rfs: write-group: group %3d: inode bitmap" \ + " clear all failed: %d: %s\n", group, rc, strerror (rc)); + return false; + } + + /* + * Close the inode bitmap. + */ + rc = rtems_rfs_bitmap_close (&bitmap); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &handle); + printf ("\nrtems-rfs: write-group: group %3d: close inode" \ + " bitmap failed: %d: %s\n", group, rc, strerror (rc)); + return false; + } + + rtems_rfs_buffer_mark_dirty (&handle); + + /* + * Initialise the inode tables if required to do so. + */ + if (initialise_inodes) + { + for (b = 0; b < blocks; b++) + { + rc = rtems_rfs_buffer_handle_request (fs, &handle, + group_base + b + RTEMS_RFS_GROUP_INODE_BLOCK, + false); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &handle); + printf ("\nrtems-rfs: write-group: group %3d: block %" PRId32 " request failed: %d: %s\n", + group, group_base + b + RTEMS_RFS_GROUP_INODE_BLOCK, + rc, strerror (rc)); + return false; + } + + /* + * Force the whole buffer to a known state. The bit map may not occupy the + * whole block. + */ + memset (rtems_rfs_buffer_data (&handle), 0xff, rtems_rfs_fs_block_size (fs)); + + rtems_rfs_buffer_mark_dirty (&handle); + } + } + + rc = rtems_rfs_buffer_handle_close (fs, &handle); + if (rc > 0) + { + printf ("\nrtems-rfs: write-group: buffer handle close failed: %d: %s\n", + rc, strerror (rc)); + return false; + } + + return true; +} + +static bool +rtems_rfs_write_superblock (rtems_rfs_file_system* fs) +{ + rtems_rfs_buffer_handle handle; + uint8_t* sb; + int rc; + + rc = rtems_rfs_buffer_handle_open (fs, &handle); + if (rc > 0) + { + printf ("rtems-rfs: write-superblock: handle open failed: %d: %s\n", + rc, strerror (rc)); + return false; + } + + rc = rtems_rfs_buffer_handle_request (fs, &handle, 0, false); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &handle); + printf ("rtems-rfs: write-superblock: request failed: %d: %s\n", + rc, strerror (rc)); + return false; + } + + sb = rtems_rfs_buffer_data (&handle); + +#define write_sb(_o, _d) rtems_rfs_write_u32(sb + (_o), _d) + + memset (sb, 0xff, rtems_rfs_fs_block_size (fs)); + + write_sb (RTEMS_RFS_SB_OFFSET_MAGIC, RTEMS_RFS_SB_MAGIC); + write_sb (RTEMS_RFS_SB_OFFSET_VERSION, RTEMS_RFS_VERSION); + write_sb (RTEMS_RFS_SB_OFFSET_BLOCKS, rtems_rfs_fs_blocks (fs)); + write_sb (RTEMS_RFS_SB_OFFSET_BLOCK_SIZE, rtems_rfs_fs_block_size (fs)); + write_sb (RTEMS_RFS_SB_OFFSET_BAD_BLOCKS, fs->bad_blocks); + write_sb (RTEMS_RFS_SB_OFFSET_MAX_NAME_LENGTH, fs->max_name_length); + write_sb (RTEMS_RFS_SB_OFFSET_GROUPS, fs->group_count); + write_sb (RTEMS_RFS_SB_OFFSET_GROUP_BLOCKS, fs->group_blocks); + write_sb (RTEMS_RFS_SB_OFFSET_GROUP_INODES, fs->group_inodes); + write_sb (RTEMS_RFS_SB_OFFSET_INODE_SIZE, RTEMS_RFS_INODE_SIZE); + + rtems_rfs_buffer_mark_dirty (&handle); + + rc = rtems_rfs_buffer_handle_release (fs, &handle); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &handle); + printf ("rtems-rfs: write-superblock: buffer release failed: %d: %s\n", + rc, strerror (rc)); + return false; + } + + rc = rtems_rfs_buffer_handle_close (fs, &handle); + if (rc > 0) + { + printf ("rtems-rfs: write-superblock: buffer handle close failed: %d: %s\n", + rc, strerror (rc)); + return false; + } + + return true; +} + +static int +rtems_rfs_write_root_dir (const char* name) +{ + rtems_rfs_file_system* fs; + rtems_rfs_inode_handle inode; + rtems_rfs_ino ino; + int rc; + + /* + * External API so returns -1. + */ + rc = rtems_rfs_fs_open (name, NULL, RTEMS_RFS_FS_FORCE_OPEN, &fs); + if (rc < 0) + { + printf ("rtems-rfs: format: file system open failed: %d: %s\n", + errno, strerror (errno)); + return -1; + } + + rc = rtems_rfs_inode_alloc (fs, RTEMS_RFS_ROOT_INO, &ino); + if (rc > 0) + { + printf ("rtems-rfs: format: inode allocation failed: %d: %s\n", + rc, strerror (rc)); + rtems_rfs_fs_close (fs); + return rc; + } + + if (ino != RTEMS_RFS_ROOT_INO) + { + printf ("rtems-rfs: format: allocated inode not root ino: %" PRId32 "\n", ino); + rtems_rfs_fs_close (fs); + return rc; + } + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + printf ("rtems-rfs: format: inode open failed: %d: %s\n", + rc, strerror (rc)); + rtems_rfs_group_bitmap_free (fs, true, ino); + rtems_rfs_fs_close (fs); + return rc; + } + + rc = rtems_rfs_inode_initialise (&inode, 0, + (RTEMS_RFS_S_IFDIR | RTEMS_RFS_S_IRWXU | + RTEMS_RFS_S_IXGRP | RTEMS_RFS_S_IXOTH), + 0, 0); + if (rc > 0) + printf ("rtems-rfs: format: inode initialise failed: %d: %s\n", + rc, strerror (rc)); + + rc = rtems_rfs_dir_add_entry (fs, &inode, ".", 1, ino); + if (rc > 0) + printf ("rtems-rfs: format: directory add failed: %d: %s\n", + rc, strerror (rc)); + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + printf ("rtems-rfs: format: inode close failed: %d: %s\n", + rc, strerror (rc)); + + rc = rtems_rfs_fs_close (fs); + if (rc < 0) + printf ("rtems-rfs: format: file system close failed: %d: %s\n", + errno, strerror (errno)); + + return rc; +} + +int +rtems_rfs_format (const char* name, const rtems_rfs_format_config* config) +{ + rtems_rfs_file_system fs; + int group; + int rc; + + if (config->verbose) + printf ("rtems-rfs: format: %s\n", name); + + memset (&fs, 0, sizeof (rtems_rfs_file_system)); + + rtems_chain_initialize_empty (&fs.buffers); + rtems_chain_initialize_empty (&fs.release); + rtems_chain_initialize_empty (&fs.release_modified); + rtems_chain_initialize_empty (&fs.file_shares); + + fs.max_held_buffers = RTEMS_RFS_FS_MAX_HELD_BUFFERS; + + fs.release_count = 0; + fs.release_modified_count = 0; + + fs.flags = RTEMS_RFS_FS_NO_LOCAL_CACHE; + + /* + * Open the buffer interface. + */ + rc = rtems_rfs_buffer_open (name, &fs); + if (rc > 0) + { + printf ("rtems-rfs: format: buffer open failed: %d: %s\n", + rc, strerror (rc)); + return -1; + } + + /* + * Check the media. + */ + if (rtems_rfs_fs_media_block_size (&fs) == 0) + { + printf ("rtems-rfs: media block is invalid: %" PRIu32 "\n", + rtems_rfs_fs_media_block_size (&fs)); + return -1; + } + + /* + * Check the configuration data. + */ + if (!rtems_rfs_check_config (&fs, config)) + return -1; + + if (config->verbose) + { + printf ("rtems-rfs: format: media size = %" PRIu64 "\n", + rtems_rfs_fs_media_size (&fs)); + printf ("rtems-rfs: format: media blocks = %" PRIu32 "\n", + rtems_rfs_fs_media_blocks (&fs)); + printf ("rtems-rfs: format: media block size = %" PRIu32 "\n", + rtems_rfs_fs_media_block_size (&fs)); + printf ("rtems-rfs: format: size = %" PRIu64 "\n", + rtems_rfs_fs_size (&fs)); + printf ("rtems-rfs: format: blocks = %zu\n", + rtems_rfs_fs_blocks (&fs)); + printf ("rtems-rfs: format: block size = %zu\n", + rtems_rfs_fs_block_size (&fs)); + printf ("rtems-rfs: format: bits per block = %u\n", + rtems_rfs_bits_per_block (&fs)); + printf ("rtems-rfs: format: inode size = %zu\n", RTEMS_RFS_INODE_SIZE); + printf ("rtems-rfs: format: inodes = %zu (%d.%d%%)\n", + fs.group_inodes * fs.group_count, + rtems_rfs_inode_overhead (&fs) / 10, + rtems_rfs_inode_overhead (&fs) % 10); + printf ("rtems-rfs: format: groups = %u\n", fs.group_count); + printf ("rtems-rfs: format: group blocks = %zu\n", fs.group_blocks); + printf ("rtems-rfs: format: group inodes = %zu\n", fs.group_inodes); + } + + rc = rtems_rfs_buffer_setblksize (&fs, rtems_rfs_fs_block_size (&fs)); + if (rc > 0) + { + printf ("rtems-rfs: format: setting block size failed: %d: %s\n", + rc, strerror (rc)); + return -1; + } + + if (!rtems_rfs_write_superblock (&fs)) + { + printf ("rtems-rfs: format: superblock write failed\n"); + return -1; + } + + for (group = 0; group < fs.group_count; group++) + if (!rtems_rfs_write_group (&fs, group, + config->initialise_inodes, config->verbose)) + return -1; + + if (config->verbose) + printf ("\n"); + + rc = rtems_rfs_buffer_close (&fs); + if (rc > 0) + { + printf ("rtems-rfs: format: buffer close failed: %d: %s\n", + rc, strerror (rc)); + return -1; + } + + rc = rtems_rfs_write_root_dir (name); + if (rc > 0) + { + printf ("rtems-rfs: format: writing root dir failed: %d: %s\n", + rc, strerror (rc)); + return -1; + } + + return 0; +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-format.h b/cpukit/libfs/src/rfs/rtems-rfs-format.h new file mode 100644 index 0000000000..48d65c9165 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-format.h @@ -0,0 +1,90 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Format. + * + * This function lets you format a disk in the RFS format. + */ + +#if !defined (_RTEMS_RFS_FORMAT_H_) +#define _RTEMS_RFS_FORMAT_H_ + +#include <stddef.h> +#include <stdbool.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rtems/rfs/rtems-rfs-trace.h> + +/** + * RFS File System Configuration data used to format the file system. For + * default values leave the field set to 0. + */ +typedef struct _rtems_rfs_format_config +{ + /** + * The size of a block. + */ + size_t block_size; + + /** + * The number of blocks in a group. + */ + size_t group_blocks; + + /** + * The number of inodes in a group. + */ + size_t group_inodes; + + /** + * The percentage overhead allocated to inodes. + */ + int inode_overhead; + + /** + * The maximum length of a name. + */ + size_t max_name_length; + + /** + * Initialise the inode tables to all ones. + */ + bool initialise_inodes; + + /** + * Is the format verbose. + */ + bool verbose; + +} rtems_rfs_format_config; + +/** + * RFS Format command. + * + * @param name The device name to format. + * @param config Pointer to a configuration table. + * @retval -1 Error. See errno. + * @retval 0 No error. Format successful. + */ +int rtems_rfs_format (const char* name, const rtems_rfs_format_config* config); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-group.c b/cpukit/libfs/src/rfs/rtems-rfs-group.c new file mode 100644 index 0000000000..a6c2fce1ba --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-group.c @@ -0,0 +1,361 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Group Routines. + * + * These functions open and close a group as well as manage bit allocations + * within a group. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-group.h> + +int +rtems_rfs_group_open (rtems_rfs_file_system* fs, + rtems_rfs_buffer_block base, + size_t size, + size_t inodes, + rtems_rfs_group* group) +{ + int rc; + + if (base >= rtems_rfs_fs_blocks (fs)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_OPEN)) + printf ("rtems-rfs: group-open: base outside file system range: %d: %s\n", + EIO, strerror (EIO)); + return EIO; + } + + if ((base + size) >= rtems_rfs_fs_blocks (fs)) + size = rtems_rfs_fs_blocks (fs) - base; + + /* + * Limit the inodes to the same size as the blocks. This is what the + * format does and if this is not done the accounting of inodes does + * not work. If we are so pushed for inodes that this makes a difference + * the format configuration needs reviewing. + */ + if (inodes > size) + inodes = size; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_OPEN)) + printf ("rtems-rfs: group-open: base=%" PRId32 ", blocks=%zd inodes=%zd\n", + base, size, inodes); + + group->base = base; + group->size = size; + + rc = rtems_rfs_buffer_handle_open (fs, &group->block_bitmap_buffer); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_OPEN)) + printf ("rtems-rfs: group-open: could not open block bitmap handle: %d: %s\n", + rc, strerror (rc)); + return rc; + } + + rc = rtems_rfs_bitmap_open (&group->block_bitmap, fs, + &group->block_bitmap_buffer, size, + group->base + RTEMS_RFS_GROUP_BLOCK_BITMAP_BLOCK); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &group->block_bitmap_buffer); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_OPEN)) + printf ("rtems-rfs: group-open: could not open block bitmap: %d: %s\n", + rc, strerror (rc)); + return rc; + } + + rc = rtems_rfs_buffer_handle_open (fs, &group->inode_bitmap_buffer); + if (rc > 0) + { + rtems_rfs_bitmap_close (&group->block_bitmap); + rtems_rfs_buffer_handle_close (fs, &group->block_bitmap_buffer); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_OPEN)) + printf ("rtems-rfs: group-open: could not open inode bitmap handle: %d: %s\n", + rc, strerror (rc)); + return rc; + } + + rc = rtems_rfs_bitmap_open (&group->inode_bitmap, fs, + &group->inode_bitmap_buffer, inodes, + group->base + RTEMS_RFS_GROUP_INODE_BITMAP_BLOCK); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &group->inode_bitmap_buffer); + rtems_rfs_bitmap_close (&group->block_bitmap); + rtems_rfs_buffer_handle_close (fs, &group->block_bitmap_buffer); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_OPEN)) + printf ("rtems-rfs: group-open: could not open inode bitmap: %d: %s\n", + rc, strerror (rc)); + return rc; + } + + if (rtems_rfs_fs_release_bitmaps (fs)) + { + rtems_rfs_bitmap_release_buffer (fs, &group->block_bitmap); + rtems_rfs_bitmap_release_buffer (fs, &group->inode_bitmap); + } + + return 0; +} + +int +rtems_rfs_group_close (rtems_rfs_file_system* fs, rtems_rfs_group* group) +{ + int result = 0; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_CLOSE)) + printf ("rtems-rfs: group-close: base=%" PRId32 "\n", group->base); + + /* + * We need to close as much as possible and also return any error if one + * occurs but this may result in one even more important error being lost but + * we cannot OR the errors together so this is a reasonable compromise. + */ + rc = rtems_rfs_bitmap_close (&group->inode_bitmap); + if (rc > 0) + result = rc; + rc = rtems_rfs_buffer_handle_close (fs, &group->inode_bitmap_buffer); + if (rc > 0) + result = rc; + rc = rtems_rfs_bitmap_close (&group->block_bitmap); + if (rc > 0) + result = rc; + rc = rtems_rfs_buffer_handle_close (fs, &group->block_bitmap_buffer); + if (rc > 0) + result = rc; + + return result; +} + +int +rtems_rfs_group_bitmap_alloc (rtems_rfs_file_system* fs, + rtems_rfs_bitmap_bit goal, + bool inode, + rtems_rfs_bitmap_bit* result) +{ + int group_start; + size_t size; + rtems_rfs_bitmap_bit bit; + int offset; + bool updown; + int direction; + + if (inode) + { + size = fs->group_inodes; + goal -= RTEMS_RFS_ROOT_INO; + } + else + size = fs->group_blocks; + + group_start = goal / size; + bit = (rtems_rfs_bitmap_bit) (goal % size); + offset = 0; + updown = true; + direction = 1; + + /* + * Try the goal group first and if that group fails try the groups either + * side until the whole file system has be tried. + */ + while (true) + { + rtems_rfs_bitmap_control* bitmap; + int group; + bool allocated = false; + int rc; + + /* + * We can start at any location and we move out from that point in each + * direction. The offset grows until we find a free bit or we hit an end. + */ + group = group_start + (direction * offset); + if (offset) + bit = direction > 0 ? 0 : size - 1; + + /* + * If we are still looking up and down and if the group is out of range we + * have reached one end. Stopping looking up and down and just move in the + * one direction one group at a time. + */ + if ((group < 0) || (group >= fs->group_count)) + { + if (!updown) + break; + direction = direction > 0 ? -1 : 1; + updown = false; + continue; + } + + if (inode) + bitmap = &fs->groups[group].inode_bitmap; + else + bitmap = &fs->groups[group].block_bitmap; + + rc = rtems_rfs_bitmap_map_alloc (bitmap, bit, &allocated, &bit); + if (rc > 0) + return rc; + + if (rtems_rfs_fs_release_bitmaps (fs)) + rtems_rfs_bitmap_release_buffer (fs, bitmap); + + if (allocated) + { + if (inode) + *result = rtems_rfs_group_inode (fs, group, bit); + else + *result = rtems_rfs_group_block (&fs->groups[group], bit); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_BITMAPS)) + printf ("rtems-rfs: group-bitmap-alloc: %s allocated: %" PRId32 "\n", + inode ? "inode" : "block", *result); + return 0; + } + + if (updown) + direction = direction > 0 ? -1 : 1; + + offset++; + } + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_BITMAPS)) + printf ("rtems-rfs: group-bitmap-alloc: no blocks available\n"); + + return ENOSPC; +} + +int +rtems_rfs_group_bitmap_free (rtems_rfs_file_system* fs, + bool inode, + rtems_rfs_bitmap_bit no) +{ + rtems_rfs_bitmap_control* bitmap; + unsigned int group; + rtems_rfs_bitmap_bit bit; + size_t size; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_BITMAPS)) + printf ("rtems-rfs: group-bitmap-free: %s free: %" PRId32 "\n", + inode ? "inode" : "block", no); + + if (inode) + { + no -= RTEMS_RFS_ROOT_INO; + size = fs->group_inodes; + } + else + { + no -= RTEMS_RFS_SUPERBLOCK_SIZE; + size = fs->group_blocks; + } + + group = no / size; + bit = (rtems_rfs_bitmap_bit) (no % size); + + if (inode) + bitmap = &fs->groups[group].inode_bitmap; + else + bitmap = &fs->groups[group].block_bitmap; + + rc = rtems_rfs_bitmap_map_clear (bitmap, bit); + + rtems_rfs_bitmap_release_buffer (fs, bitmap); + + return rc; +} + +int +rtems_rfs_group_bitmap_test (rtems_rfs_file_system* fs, + bool inode, + rtems_rfs_bitmap_bit no, + bool* state) +{ + rtems_rfs_bitmap_control* bitmap; + unsigned int group; + rtems_rfs_bitmap_bit bit; + size_t size; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_BITMAPS)) + printf ("rtems-rfs: group-bitmap-test: %s test: %" PRId32 "\n", + inode ? "inode" : "block", no); + + if (inode) + { + if ((no < RTEMS_RFS_ROOT_INO) || (no > rtems_rfs_fs_inodes (fs))) + return EINVAL; + no -= RTEMS_RFS_ROOT_INO; + size = fs->group_inodes; + } + else + { + if (no >= rtems_rfs_fs_blocks (fs)) + return EINVAL; + size = fs->group_blocks; + } + + group = no / size; + bit = (rtems_rfs_bitmap_bit) (no % size); + + if (inode) + bitmap = &fs->groups[group].inode_bitmap; + else + bitmap = &fs->groups[group].block_bitmap; + + rc = rtems_rfs_bitmap_map_test (bitmap, bit, state); + + rtems_rfs_bitmap_release_buffer (fs, bitmap); + + return rc; +} + +int +rtems_rfs_group_usage (rtems_rfs_file_system* fs, + size_t* blocks, + size_t* inodes) +{ + int g; + + *blocks = 0; + *inodes = 0; + + for (g = 0; g < fs->group_count; g++) + { + rtems_rfs_group* group = &fs->groups[g]; + *blocks += + rtems_rfs_bitmap_map_size(&group->block_bitmap) - + rtems_rfs_bitmap_map_free (&group->block_bitmap); + *inodes += + rtems_rfs_bitmap_map_size (&group->inode_bitmap) - + rtems_rfs_bitmap_map_free (&group->inode_bitmap); + } + + if (*blocks > rtems_rfs_fs_blocks (fs)) + *blocks = rtems_rfs_fs_blocks (fs); + if (*inodes > rtems_rfs_fs_inodes (fs)) + *inodes = rtems_rfs_fs_inodes (fs); + + return 0; +} + diff --git a/cpukit/libfs/src/rfs/rtems-rfs-group.h b/cpukit/libfs/src/rfs/rtems-rfs-group.h new file mode 100644 index 0000000000..1ec0107f89 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-group.h @@ -0,0 +1,163 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Group Management. + * + * These functions manage the groups used in the file system. + */ + +#if !defined (_RTEMS_RFS_GROUP_H_) +#define _RTEMS_RFS_GROUP_H_ + +#include <rtems/rfs/rtems-rfs-trace.h> +#include <rtems/rfs/rtems-rfs-bitmaps.h> +#include <rtems/rfs/rtems-rfs-buffer.h> + +/** + * Block allocations for a group on disk. + */ +#define RTEMS_RFS_GROUP_BLOCK_BITMAP_BLOCK (0) +#define RTEMS_RFS_GROUP_INODE_BITMAP_BLOCK (1) +#define RTEMS_RFS_GROUP_INODE_BLOCK (2) + +/** + * A group is a selection of blocks on the disk. Typically the number of blocks + * in a group is determined by the number of bits a block holds. This makes the + * bit allocator for blocks in the group simpler plus is allows a simple way to + * localise access to files and directories. + */ +typedef struct _rtems_rfs_group +{ + /** + * Base block number. + */ + rtems_rfs_buffer_block base; + + /** + * The number of blocks in the group. Groups may be different sizes. + */ + size_t size; + + /** + * The block bitmap control. + */ + rtems_rfs_bitmap_control block_bitmap; + + /** + * The handle to the block bitmap buffer. + */ + rtems_rfs_buffer_handle block_bitmap_buffer; + + /** + * The inode bitmap control. + */ + rtems_rfs_bitmap_control inode_bitmap; + + /** + * The handle to the inode bitmap buffer. + */ + rtems_rfs_buffer_handle inode_bitmap_buffer; + +} rtems_rfs_group; + +/** + * Return the disk's block for a block in a group. + */ +#define rtems_rfs_group_block(_g, _b) (((_g)->base) + (_b)) + +/** + * Return the file system inode for a inode in a group. + */ +#define rtems_rfs_group_inode(_f, _g, _i) \ + (((_f)->group_inodes * (_g)) + (_i) + RTEMS_RFS_ROOT_INO) + +/** + * Open a group. Allocate all the resources including the bitmaps. + * + * @param fs The file system. + * @param base The base block number. + * @param size The number of blocks in the group. + * @param group Reference to the group to open. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_group_open (rtems_rfs_file_system* fs, + rtems_rfs_buffer_block base, + size_t size, + size_t inodes, + rtems_rfs_group* group); + +/** + * Close a group. Release all resources the group holds. + * + * @param fs The file system. + * @param group The group to close. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_group_close (rtems_rfs_file_system* fs, + rtems_rfs_group* group); + +/** + * Allocate an inode or block. The groups are searched to find the next + * available inode or block. + * + * @param fs The file system data. + * @param goal The goal to seed the bitmap search. + * @param inode If true allocate an inode else allocate a block. + * @param result The allocated bit in the bitmap. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_group_bitmap_alloc (rtems_rfs_file_system* fs, + rtems_rfs_bitmap_bit goal, + bool inode, + rtems_rfs_bitmap_bit* result); + +/** + * Free the group allocated bit. + * + * @param fs The file system data. + * @param inode If true the number to free is an inode else it is a block. + * @param block The inode or block number to free. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_group_bitmap_free (rtems_rfs_file_system* fs, + bool inode, + rtems_rfs_bitmap_bit no); + +/** + * Test the group allocated bit. + * + * @param fs The file system data. + * @param inode If true the number to free is an inode else it is a block. + * @param block The inode or block number to free. + * @param state Return the state of the bit. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_group_bitmap_test (rtems_rfs_file_system* fs, + bool inode, + rtems_rfs_bitmap_bit no, + bool* state); + +/** + * Determine the number of blocks and inodes used. + * + * @param fs The file system data. + * @param blocks The number of blocks used. + * @param inodes The number of inodes used. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_group_usage (rtems_rfs_file_system* fs, + size_t* blocks, + size_t* inodes); + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-inode.c b/cpukit/libfs/src/rfs/rtems-rfs-inode.c new file mode 100644 index 0000000000..ab3f4b7e65 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-inode.c @@ -0,0 +1,402 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Inode Routines. + * + * These functions manage inodes in the RFS file system. An inode is part of a + * block that reside after the bitmaps in the group. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <rtems/rfs/rtems-rfs-block.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> +#include <rtems/rfs/rtems-rfs-dir.h> + +int +rtems_rfs_inode_alloc (rtems_rfs_file_system* fs, + rtems_rfs_bitmap_bit goal, + rtems_rfs_ino* ino) +{ + rtems_rfs_bitmap_bit bit; + int rc; + rc = rtems_rfs_group_bitmap_alloc (fs, goal, true, &bit); + *ino = bit; + return rc; +} + +int +rtems_rfs_inode_free (rtems_rfs_file_system* fs, + rtems_rfs_ino ino) +{ + rtems_rfs_bitmap_bit bit; + bit = ino; + return rtems_rfs_group_bitmap_free (fs, true, bit); +} + +int +rtems_rfs_inode_open (rtems_rfs_file_system* fs, + rtems_rfs_ino ino, + rtems_rfs_inode_handle* handle, + bool load) +{ + int group; + int gino; + int index; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_OPEN)) + printf ("rtems-rfs: inode-open: ino: %" PRIu32 "\n", ino); + + if (ino == RTEMS_RFS_EMPTY_INO) + return EINVAL; + + if ((ino - RTEMS_RFS_ROOT_INO) > rtems_rfs_fs_inodes (fs)) + return EINVAL; + + handle->ino = ino; + handle->node = NULL; + handle->loads = 0; + + gino = ino - RTEMS_RFS_ROOT_INO; + group = gino / fs->group_inodes; + gino = gino % fs->group_inodes; + index = (gino / fs->inodes_per_block) + RTEMS_RFS_GROUP_INODE_BLOCK; + + handle->offset = gino % fs->inodes_per_block; + handle->block = rtems_rfs_group_block (&fs->groups[group], index); + + rc = rtems_rfs_buffer_handle_open (fs, &handle->buffer); + if ((rc == 0) && load) + rc = rtems_rfs_inode_load (fs, handle); + return rc; +} + +int +rtems_rfs_inode_close (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* handle) +{ + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_CLOSE)) + printf ("rtems-rfs: inode-close: ino: %" PRIu32 "\n", handle->ino); + + rc = rtems_rfs_inode_unload (fs, handle, true); + + if ((rc == 0) && (handle->loads > 0)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_CLOSE)) + printf ("rtems-rfs: inode-close: bad loads number: %d\n", + handle->loads); + rc = EIO; + } + + handle->ino = 0; + return rc; +} + +int +rtems_rfs_inode_load (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* handle) +{ + if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_LOAD)) + printf ("rtems-rfs: inode-load: ino=%" PRIu32 " loads=%i loaded=%s\n", + handle->ino, handle->loads, + rtems_rfs_inode_is_loaded (handle) ? "yes" : "no"); + + /* + * An inode does not move so once loaded no need to do again. + */ + + if (!rtems_rfs_inode_is_loaded (handle)) + { + int rc; + + rc = rtems_rfs_buffer_handle_request (fs,&handle->buffer, + handle->block, true); + if (rc > 0) + return rc; + + handle->node = rtems_rfs_buffer_data (&handle->buffer); + handle->node += handle->offset; + } + + handle->loads++; + + return 0; +} + +int +rtems_rfs_inode_unload (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* handle, + bool update_ctime) +{ + int rc = 0; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_UNLOAD)) + printf ("rtems-rfs: inode-unload: ino=%" PRIu32 " loads=%i loaded=%s\n", + handle->ino, handle->loads, + rtems_rfs_inode_is_loaded (handle) ? "yes" : "no"); + + if (rtems_rfs_inode_is_loaded (handle)) + { + if (handle->loads == 0) + return EIO; + + handle->loads--; + + if (handle->loads == 0) + { + /* + * If the buffer is dirty it will be release. Also set the ctime. + */ + if (rtems_rfs_buffer_dirty (&handle->buffer) && update_ctime) + rtems_rfs_inode_set_ctime (handle, time (NULL)); + rc = rtems_rfs_buffer_handle_release (fs, &handle->buffer); + handle->node = NULL; + } + } + + return rc; +} + +int +rtems_rfs_inode_create (rtems_rfs_file_system* fs, + rtems_rfs_ino parent, + const char* name, + size_t length, + uint16_t mode, + uint16_t links, + uid_t uid, + gid_t gid, + rtems_rfs_ino* ino) +{ + rtems_rfs_inode_handle parent_inode; + rtems_rfs_inode_handle inode; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_CREATE)) + { + const char* type = "unknown"; + int c; + if (RTEMS_RFS_S_ISDIR (mode)) + type = "dir"; + else if (RTEMS_RFS_S_ISCHR (mode)) + type = "char"; + else if (RTEMS_RFS_S_ISBLK (mode)) + type = "block"; + else if (RTEMS_RFS_S_ISREG (mode)) + type = "file"; + else if (RTEMS_RFS_S_ISLNK (mode)) + type = "link"; + printf("rtems-rfs: inode-create: parent:%" PRIu32 " name:", parent); + for (c = 0; c < length; c++) + printf ("%c", name[c]); + printf (" type:%s mode:%04x (%03o)\n", type, mode, mode & ((1 << 10) - 1)); + } + + /* + * The file type is field within the mode. Check we have a sane mode set. + */ + switch (mode & RTEMS_RFS_S_IFMT) + { + case RTEMS_RFS_S_IFDIR: + case RTEMS_RFS_S_IFCHR: + case RTEMS_RFS_S_IFBLK: + case RTEMS_RFS_S_IFREG: + case RTEMS_RFS_S_IFLNK: + break; + default: + return EINVAL; + } + + rc = rtems_rfs_inode_alloc (fs, parent, ino); + if (rc > 0) + return rc; + + rc = rtems_rfs_inode_open (fs, *ino, &inode, true); + if (rc > 0) + { + rtems_rfs_inode_free (fs, *ino); + return rc; + } + + rc = rtems_rfs_inode_initialise (&inode, links, mode, uid, gid); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_inode_free (fs, *ino); + return rc; + } + + /* + * Only handle the specifics of a directory. Let caller handle the others. + * + * The inode delete will free the inode. + */ + if (RTEMS_RFS_S_ISDIR (mode)) + { + rc = rtems_rfs_dir_add_entry (fs, &inode, ".", 1, *ino); + if (rc == 0) + rc = rtems_rfs_dir_add_entry (fs, &inode, "..", 2, parent); + if (rc > 0) + { + rtems_rfs_inode_delete (fs, &inode); + rtems_rfs_inode_close (fs, &inode); + return rc; + } + } + + rc = rtems_rfs_inode_open (fs, parent, &parent_inode, true); + if (rc > 0) + { + rtems_rfs_inode_delete (fs, &inode); + rtems_rfs_inode_close (fs, &inode); + return rc; + } + + rc = rtems_rfs_dir_add_entry (fs, &parent_inode, name, length, *ino); + if (rc > 0) + { + rtems_rfs_inode_delete (fs, &inode); + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_inode_close (fs, &parent_inode); + return rc; + } + + /* + * If the node is a directory update the parent link count as the + * new directory has the '..' link that points to the parent. + */ + if (RTEMS_RFS_S_ISDIR (mode)) + rtems_rfs_inode_set_links (&parent_inode, + rtems_rfs_inode_get_links (&parent_inode) + 1); + + rc = rtems_rfs_inode_close (fs, &parent_inode); + if (rc > 0) + { + rtems_rfs_inode_delete (fs, &inode); + rtems_rfs_inode_close (fs, &inode); + return rc; + } + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_inode_free (fs, *ino); + return rc; + } + + return 0; +} + +int +rtems_rfs_inode_delete (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* handle) +{ + int rc = 0; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_DELETE)) + printf("rtems-rfs: inode-delete: ino:%" PRIu32 " loaded:%s\n", + rtems_rfs_inode_ino (handle), + rtems_rfs_inode_is_loaded (handle) ? "yes" : "no"); + + if (rtems_rfs_inode_is_loaded (handle)) + { + rtems_rfs_block_map map; + + /* + * Free the ino number. + */ + rc = rtems_rfs_inode_free (fs, handle->ino); + if (rc > 0) + return rc; + + /* + * Free the blocks the inode may have attached. + */ + rc = rtems_rfs_block_map_open (fs, handle, &map); + if (rc == 0) + { + int rrc; + rrc = rtems_rfs_block_map_free_all (fs, &map); + rc = rtems_rfs_block_map_close (fs, &map); + if (rc > 0) + rrc = rc; + memset (handle->node, 0xff, RTEMS_RFS_INODE_SIZE); + rtems_rfs_buffer_mark_dirty (&handle->buffer); + /* + * Do the release here to avoid the ctime field being set on a + * close. Also if there loads is greater then one then other loads + * active. Forcing the loads count to 0. + */ + rc = rtems_rfs_buffer_handle_release (fs, &handle->buffer); + handle->loads = 0; + handle->node = NULL; + } + } + return rc; +} + +int +rtems_rfs_inode_initialise (rtems_rfs_inode_handle* handle, + uint16_t links, + uint16_t mode, + uid_t uid, + gid_t gid) +{ + int b; + rtems_rfs_inode_set_links (handle, links); + rtems_rfs_inode_set_flags (handle, 0); + rtems_rfs_inode_set_mode (handle, mode); + rtems_rfs_inode_set_uid_gid (handle, uid, gid); + rtems_rfs_inode_set_block_offset (handle, 0); + rtems_rfs_inode_set_block_count (handle, 0); + for (b = 0; b < RTEMS_RFS_INODE_BLOCKS; b++) + rtems_rfs_inode_set_block (handle, b, 0); + rtems_rfs_inode_set_last_map_block (handle, 0); + rtems_rfs_inode_set_last_data_block (handle, 0); + return rtems_rfs_inode_time_stamp_now (handle, true, true); +} + +int +rtems_rfs_inode_time_stamp_now (rtems_rfs_inode_handle* handle, + bool atime, + bool mtime) +{ + time_t now; + if (!rtems_rfs_inode_is_loaded (handle)) + return ENXIO; + now = time (NULL); + if (atime) + rtems_rfs_inode_set_atime (handle, now); + if (mtime) + rtems_rfs_inode_set_mtime (handle, now); + return 0; +} + +rtems_rfs_pos +rtems_rfs_inode_get_size (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* handle) +{ + rtems_rfs_block_size size; + size.count = rtems_rfs_inode_get_block_count (handle); + size.offset = rtems_rfs_inode_get_block_offset (handle); + return rtems_rfs_block_get_size (fs, &size); +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-inode.h b/cpukit/libfs/src/rfs/rtems-rfs-inode.h new file mode 100644 index 0000000000..5ac2868859 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-inode.h @@ -0,0 +1,693 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Information Node. + * + * The information nodes hold the data about all nodes in the file system. + */ + +#if !defined (_RTEMS_RFS_INODE_H_) +#define _RTEMS_RFS_INODE_H_ + +#include <sys/stat.h> + +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-file-system.h> + +/** + * The RFS mode definitions. Currently map to the C library ones. + */ +#define RTEMS_RFS_S_ISUID S_ISUID /**< Set user id on execution */ +#define RTEMS_RFS_S_ISGID S_ISGID /**< Set group id on execution */ +#define RTEMS_RFS_S_ISVTX S_ISVTX /**< Save swapped text even after use */ +#define RTEMS_RFS_S_IREAD S_IREAD /**< Read permission, owner */ +#define RTEMS_RFS_S_IWRITE S_IWRITE /**< Write permission, owner */ +#define RTEMS_RFS_S_IEXEC S_IEXEC /**< Execute/search permission, owner */ +#define RTEMS_RFS_S_ENFMT S_ENFMT /**< Enforcement-mode locking */ +#define RTEMS_RFS_S_IFMT S_IFMT /**< Type of file */ +#define RTEMS_RFS_S_IFDIR S_IFDIR /**< Directory */ +#define RTEMS_RFS_S_IFCHR S_IFCHR /**< Character special */ +#define RTEMS_RFS_S_IFBLK S_IFBLK /**< Block special */ +#define RTEMS_RFS_S_IFREG S_IFREG /**< Regular */ +#define RTEMS_RFS_S_IFLNK S_IFLNK /**< Symbolic link */ +#define RTEMS_RFS_S_IFSOCK S_IFSOCK /**< Socket */ +#define RTEMS_RFS_S_IFIFO S_IFIFO /**< Fifo */ +#define RTEMS_RFS_S_IRWXU S_IRWXU +#define RTEMS_RFS_S_IRUSR S_IRUSR /**< Read permission, owner */ +#define RTEMS_RFS_S_IWUSR S_IWUSR /**< Write permission, owner */ +#define RTEMS_RFS_S_IXUSR S_IXUSR /**< Execute/search permission, owner */ +#define RTEMS_RFS_S_IRWXG S_IRWXG +#define RTEMS_RFS_S_IRGRP S_IRGRP /**< Read permission, group */ +#define RTEMS_RFS_S_IWGRP S_IWGRP /**< Write permission, grougroup */ +#define RTEMS_RFS_S_IXGRP S_IXGRP /**< Execute/search permission, group */ +#define RTEMS_RFS_S_IRWXO S_IRWXO +#define RTEMS_RFS_S_IROTH S_IROTH /**< Read permission, other */ +#define RTEMS_RFS_S_IWOTH S_IWOTH /**< Write permission, other */ +#define RTEMS_RFS_S_IXOTH S_IXOTH /**< Execute/search permission, other */ + +#define RTEMS_RFS_S_ISBLK(m) S_ISBLK(m) +#define RTEMS_RFS_S_ISCHR(m) S_ISCHR(m) +#define RTEMS_RFS_S_ISDIR(m) S_ISDIR(m) +#define RTEMS_RFS_S_ISFIFO(m) S_ISFIFO(m) +#define RTEMS_RFS_S_ISREG(m) S_ISREG(m) +#define RTEMS_RFS_S_ISLNK(m) S_ISLNK(m) +#define RTEMS_RFS_S_ISSOCK(m) S_ISSOCK(m) + +/** + * Permissions of a symlink. + */ +#define RTEMS_RFS_S_SYMLINK \ + RTEMS_RFS_S_IFLNK | RTEMS_RFS_S_IRWXU | RTEMS_RFS_S_IRWXG | RTEMS_RFS_S_IRWXO + +/** + * The inode number or ino. + */ +typedef uint32_t rtems_rfs_ino; + +/** + * The time in the file system. + */ +typedef uint32_t rtems_rfs_time; + +/** + * The size of a block value on disk. This include the inodes and indirect + * tables. + */ +typedef uint32_t rtems_rfs_inode_block; + +/** + * The size of the data name field in the inode. + */ +#define RTEMS_RFS_INODE_DATA_NAME_SIZE \ + (RTEMS_RFS_INODE_BLOCKS * sizeof (rtems_rfs_inode_block)) + +/** + * The inode. + */ +typedef struct _rtems_rfs_inode +{ + /** + * The number of links to the inode. + */ + uint16_t links; + + /** + * The mode of the node. + */ + uint16_t mode; + + /** + * The owner of the node. + */ + uint32_t owner; + + /** + * Reserved. + */ + uint16_t flags; + + /** + * Amount of data held in the last block data. + */ + uint16_t block_offset; + + /** + * Number of blocks held by this file. + */ + uint32_t block_count; + + /** + * The access time. The last time the file was read. + */ + rtems_rfs_time atime; + + /** + * The modified time. The last time the file was written too. + */ + rtems_rfs_time mtime; + + /** + * The change time. The last time the inode was written too. + */ + rtems_rfs_time ctime; + + /** + * Blocks. These are the block numbers used by the node or table of + * nodes. The flags indicate the mode the blocks are being held in. In the + * direct table mode the blocks are entries in this table. In the indirect + * mode the blocks point to blocks that hold the block numbers. The data can + * also be a name if it fits. For example a symbolic link. + */ + union + { + rtems_rfs_inode_block blocks[RTEMS_RFS_INODE_BLOCKS]; + uint8_t name[RTEMS_RFS_INODE_DATA_NAME_SIZE]; + } data; + + /** + * The last block map block. Used as the goal when allocating a new block for + * use in the map. + */ + rtems_rfs_inode_block last_map_block; + + /** + * The last data block. Used as the goal when allocating a new block. + */ + rtems_rfs_inode_block last_data_block; + +} rtems_rfs_inode; + +/** + * The size of an inode. + */ +#define RTEMS_RFS_INODE_SIZE (sizeof (rtems_rfs_inode)) + +/** + * RFS Inode Handle. + */ +typedef struct _rtems_rfs_inode_handle +{ + /** + * Handles can be linked as a list for easy processing. + */ + rtems_chain_node link; + + /** + * The ino for this handle. + */ + rtems_rfs_ino ino; + + /** + * The pointer to the inode. + */ + rtems_rfs_inode* node; + + /** + * The buffer that contains this inode. + */ + rtems_rfs_buffer_handle buffer; + + /** + * The block number that holds the inode. + */ + rtems_rfs_buffer_block block; + + /** + * The offset into the block for the inode. + */ + int offset; + + /** + * Number of load requests. + */ + int loads; + +} rtems_rfs_inode_handle; + +/** + * Is the inode loaded ? + */ +#define rtems_rfs_inode_is_loaded(_h) ((_h)->node) + +/** + * Get the inode ino for a handle. + */ +#define rtems_rfs_inode_ino(_h) ((_h)->ino) + +/** + * Get the link count. + * + * @param handle The inode handle. + * @return uint16_t The link count. + */ +static inline uint16_t +rtems_rfs_inode_get_links (rtems_rfs_inode_handle* handle) +{ + uint16_t links; + links = rtems_rfs_read_u16 (&handle->node->links); + if (links == 0xffff) + links = 0; + return links; +} + +/** + * Set the link count. + * + * @param handle The inode handle. + * @prarm links The links. + */ +static inline void +rtems_rfs_inode_set_links (rtems_rfs_inode_handle* handle, uint16_t links) +{ + rtems_rfs_write_u16 (&handle->node->links, links); + rtems_rfs_buffer_mark_dirty (&handle->buffer); +} + +/** + * Get the flags. + * + * @param handle The inode handle. + * @return uint16_t The flags. + */ +static inline uint16_t +rtems_rfs_inode_get_flags (rtems_rfs_inode_handle* handle) +{ + return rtems_rfs_read_u16 (&handle->node->flags); +} + +/** + * Set the flags. + * + * @param handle The inode handle. + * @prarm flags The flags. + */ +static inline void +rtems_rfs_inode_set_flags (rtems_rfs_inode_handle* handle, uint16_t flags) +{ + rtems_rfs_write_u16 (&handle->node->flags, flags); + rtems_rfs_buffer_mark_dirty (&handle->buffer); +} + +/** + * Get the mode. + * + * @param handle The inode handle. + * @return uint16_t The mode. + */ +static inline uint16_t +rtems_rfs_inode_get_mode (rtems_rfs_inode_handle* handle) +{ + return rtems_rfs_read_u16 (&handle->node->mode); +} + +/** + * Set the mode. + * + * @param handle The inode handle. + * @prarm mode The mode. + */ +static inline void +rtems_rfs_inode_set_mode (rtems_rfs_inode_handle* handle, uint16_t mode) +{ + rtems_rfs_write_u16 (&handle->node->mode, mode); + rtems_rfs_buffer_mark_dirty (&handle->buffer); +} + +/** + * Get the user id. + * + * @param handle The inode handle. + * @return uint16_t The user id (uid). + */ +static inline uint16_t +rtems_rfs_inode_get_uid (rtems_rfs_inode_handle* handle) +{ + return rtems_rfs_read_u32 (&handle->node->owner) & 0xffff; +} + +/** + * Get the group id. + * + * @param handle The inode handle. + * @return uint16_t The group id (gid). + */ +static inline uint16_t +rtems_rfs_inode_get_gid (rtems_rfs_inode_handle* handle) +{ + return (rtems_rfs_read_u32 (&handle->node->owner) >> 16) & 0xffff; +} + +/** + * Set the user id and group id. + * + * @param handle The inode handle. + * @param uid The user id (uid). + * @param gid The group id (gid). + */ +static inline void +rtems_rfs_inode_set_uid_gid (rtems_rfs_inode_handle* handle, + uint16_t uid, uint16_t gid) +{ + rtems_rfs_write_u32 (&handle->node->owner, (((uint32_t) gid) << 16) | uid); + rtems_rfs_buffer_mark_dirty (&handle->buffer); +} + +/** + * Get the block offset. + * + * @param handle The inode handle. + * @return uint32_t The block offset. + */ +static inline uint16_t +rtems_rfs_inode_get_block_offset (rtems_rfs_inode_handle* handle) +{ + return rtems_rfs_read_u16 (&handle->node->block_offset); +} + +/** + * Set the block offset. + * + * @param handle The inode handle. + * @param block_count The block offset. + */ +static inline void +rtems_rfs_inode_set_block_offset (rtems_rfs_inode_handle* handle, + uint16_t block_offset) +{ + rtems_rfs_write_u16 (&handle->node->block_offset, block_offset); + rtems_rfs_buffer_mark_dirty (&handle->buffer); +} + +/** + * Get the block count. + * + * @param handle The inode handle. + * @return uint32_t The block count. + */ +static inline uint32_t +rtems_rfs_inode_get_block_count (rtems_rfs_inode_handle* handle) +{ + return rtems_rfs_read_u32 (&handle->node->block_count); +} + +/** + * Set the block count. + * + * @param handle The inode handle. + * @param block_count The block count. + */ +static inline void +rtems_rfs_inode_set_block_count (rtems_rfs_inode_handle* handle, uint32_t block_count) +{ + rtems_rfs_write_u32 (&handle->node->block_count, block_count); + rtems_rfs_buffer_mark_dirty (&handle->buffer); +} + +/** + * Get the atime. + * + * @param handle The inode handle. + * @return rtems_rfs_time The atime. + */ +static inline rtems_rfs_time +rtems_rfs_inode_get_atime (rtems_rfs_inode_handle* handle) +{ + return rtems_rfs_read_u32 (&handle->node->atime); +} + +/** + * Set the atime. + * + * @param handle The inode handle. + * @prarm atime The atime. + */ +static inline void +rtems_rfs_inode_set_atime (rtems_rfs_inode_handle* handle, + rtems_rfs_time atime) +{ + rtems_rfs_write_u32 (&handle->node->atime, atime); + rtems_rfs_buffer_mark_dirty (&handle->buffer); +} + +/** + * Get the mtime. + * + * @param handle The inode handle. + * @return rtems_rfs_time The mtime. + */ +static inline rtems_rfs_time +rtems_rfs_inode_get_mtime (rtems_rfs_inode_handle* handle) +{ + return rtems_rfs_read_u32 (&handle->node->mtime); +} + +/** + * Set the mtime. + * + * @param handle The inode handle. + * @prarm atime The mtime. + */ +static inline void +rtems_rfs_inode_set_mtime (rtems_rfs_inode_handle* handle, + rtems_rfs_time mtime) +{ + rtems_rfs_write_u32 (&handle->node->mtime, mtime); + rtems_rfs_buffer_mark_dirty (&handle->buffer); +} + +/** + * Get the ctime. + * + * @param handle The inode handle. + * @return rtems_rfs_time The ctime. + */ +static inline rtems_rfs_time +rtems_rfs_inode_get_ctime (rtems_rfs_inode_handle* handle) +{ + return rtems_rfs_read_u32 (&handle->node->ctime); +} + +/** + * Set the ctime. + * + * @param handle The inode handle. + * @prarm atime The ctime. + */ +static inline void +rtems_rfs_inode_set_ctime (rtems_rfs_inode_handle* handle, + rtems_rfs_time ctime) +{ + rtems_rfs_write_u32 (&handle->node->ctime, ctime); + rtems_rfs_buffer_mark_dirty (&handle->buffer); +} + +/** + * Get the block number. + * + * @param handle The inode handle. + * @param block The block number to return. + * @return uint32_t The block number. + */ +static inline uint32_t +rtems_rfs_inode_get_block (rtems_rfs_inode_handle* handle, int block) +{ + return rtems_rfs_read_u32 (&handle->node->data.blocks[block]); +} + +/** + * Set the block number for a given block index. + * + * @param handle The inode handle. + * @param block The block index. + * @param bno The block number. + */ +static inline void +rtems_rfs_inode_set_block (rtems_rfs_inode_handle* handle, int block, uint32_t bno) +{ + rtems_rfs_write_u32 (&handle->node->data.blocks[block], bno); + rtems_rfs_buffer_mark_dirty (&handle->buffer); +} + +/** + * Get the last map block from the inode. + * + * @param handle The inode handle. + * @return uint32_t The last map block number. + */ +static inline uint32_t +rtems_rfs_inode_get_last_map_block (rtems_rfs_inode_handle* handle) +{ + return rtems_rfs_read_u32 (&handle->node->last_map_block); +} + +/** + * Set the last map block. + * + * @param handle The inode handle. + * @param block_count The last map block number. + */ +static inline void +rtems_rfs_inode_set_last_map_block (rtems_rfs_inode_handle* handle, uint32_t last_map_block) +{ + rtems_rfs_write_u32 (&handle->node->last_map_block, last_map_block); + rtems_rfs_buffer_mark_dirty (&handle->buffer); +} + +/** + * Get the last data block from the inode. + * + * @param handle The inode handle. + * @return uint32_t The last data block number. + */ +static inline uint32_t +rtems_rfs_inode_get_last_data_block (rtems_rfs_inode_handle* handle) +{ + return rtems_rfs_read_u32 (&handle->node->last_data_block); +} + +/** + * Set the last data block. + * + * @param handle The inode handle. + * @param block_count The last data block number. + */ +static inline void +rtems_rfs_inode_set_last_data_block (rtems_rfs_inode_handle* handle, uint32_t last_data_block) +{ + rtems_rfs_write_u32 (&handle->node->last_data_block, last_data_block); + rtems_rfs_buffer_mark_dirty (&handle->buffer); +} + +/** + * Allocate an inode number and return it. + * + * @param fs The file system data. + * @param ino Return the ino. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_inode_alloc (rtems_rfs_file_system* fs, + rtems_rfs_bitmap_bit goal, + rtems_rfs_ino* ino); + +/** + * Allocate an inode number and return it. + * + * @param fs The file system data. + * @param ino The ino too free. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_inode_free (rtems_rfs_file_system* fs, + rtems_rfs_ino ino); + +/** + * Open the inode handle. This reads the inode into the buffer and sets the + * data pointer. All data is in media byte order and needs to be accessed via + * the supporting calls. + * + * @param fs The file system. + * @param ino The inode number. + * @param handle The handle to the inode we are opening. + * @param load If true load the inode into memory from the media. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_inode_open (rtems_rfs_file_system* fs, + rtems_rfs_ino ino, + rtems_rfs_inode_handle* handle, + bool load); + +/** + * The close inode handle. All opened inodes need to be closed. + * + * @param fs The file system. + * @param handle The handle to close. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_inode_close (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* handle); + +/** + * Load the inode into memory. + * + * @param fs The file system. + * @param handle The inode handle to load. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_inode_load (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* handle); + +/** + * Unload the inode from memory. + * + * @param fs The file system. + * @param handle The inode handle to unload. + * @param update_ctime Update the ctime field of the inode. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_inode_unload (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* handle, + bool update_ctime); + +/** + * Create an inode allocating, initialising and adding an entry to the parent + * directory. + * + * @param fs The file system data. + * @param parent The parent inode number to add the directory entry to. + * @param name The name of the directory entryinode to create. + * + */ +int rtems_rfs_inode_create (rtems_rfs_file_system* fs, + rtems_rfs_ino parent, + const char* name, + size_t length, + uint16_t mode, + uint16_t links, + uid_t uid, + gid_t gid, + rtems_rfs_ino* ino); + +/** + * Delete the inode eraseing it and release the buffer to commit the write. You + * need to load the inode again if you wish to use it again. + * + * @param fs The file system. + * @param handle The inode handle to erase. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_inode_delete (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* handle); + +/** + * Initialise a new inode. + * + * @param handle The inode handle to initialise. + * @param links The number of links to the inode. + * @param mode The inode mode. + * @param uid The user id. + * @param gid The group id. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_inode_initialise (rtems_rfs_inode_handle* handle, + uint16_t links, + uint16_t mode, + uid_t uid, + gid_t gid); + +/** + * Time stamp the inode with the current time. The ctime field is hanlded + * automatically. + * + * @param handle The inode handle. + * @param atime Update the atime field. + * @param mtime UPdate the mtime field. + * @return int The error number (errno). No error if 0 and ENXIO if no inode + * loaded. + */ +int rtems_rfs_inode_time_stamp_now (rtems_rfs_inode_handle* handle, + bool atime, + bool mtime); + +/** + * Calculate the size of data attached to the inode. + * + * @param fs The file system data. + * @param handle The inode handle. + * @return rtems_rfs_pos The data size in bytes in the block map attched to the + * inode. + */ +rtems_rfs_pos rtems_rfs_inode_get_size (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* handle); + +#endif + diff --git a/cpukit/libfs/src/rfs/rtems-rfs-link.c b/cpukit/libfs/src/rfs/rtems-rfs-link.c new file mode 100644 index 0000000000..27a08153b9 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-link.c @@ -0,0 +1,458 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Link Routines. + * + * These functions manage links. A link is the addition of a directory entry in + * a parent directory and incrementing the links count in the inode. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <rtems/rfs/rtems-rfs-block.h> +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-trace.h> +#include <rtems/rfs/rtems-rfs-dir.h> +#include <rtems/rfs/rtems-rfs-dir-hash.h> +#include <rtems/rfs/rtems-rfs-link.h> + +int +rtems_rfs_link (rtems_rfs_file_system* fs, + const char* name, + int length, + rtems_rfs_ino parent, + rtems_rfs_ino target, + bool link_dir) +{ + rtems_rfs_inode_handle parent_inode; + rtems_rfs_inode_handle target_inode; + uint16_t links; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_LINK)) + { + int c; + printf ("rtems-rfs: link: parent(%" PRIu32 ") -> ", parent); + for (c = 0; c < length; c++) + printf ("%c", name[c]); + printf ("(%" PRIu32 ")\n", target); + } + + rc = rtems_rfs_inode_open (fs, target, &target_inode, true); + if (rc) + return rc; + + /* + * If the target inode is a directory and we cannot link directories + * return a not supported error code. + */ + if (!link_dir && S_ISDIR (rtems_rfs_inode_get_mode (&target_inode))) + { + rtems_rfs_inode_close (fs, &target_inode); + return ENOTSUP; + } + + rc = rtems_rfs_inode_open (fs, parent, &parent_inode, true); + if (rc) + { + rtems_rfs_inode_close (fs, &target_inode); + return rc; + } + + rc = rtems_rfs_dir_add_entry (fs, &parent_inode, name, length, target); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &parent_inode); + rtems_rfs_inode_close (fs, &target_inode); + return rc; + } + + links = rtems_rfs_inode_get_links (&target_inode) + 1; + rtems_rfs_inode_set_links (&target_inode, links); + + rc = rtems_rfs_inode_time_stamp_now (&parent_inode, true, true); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &parent_inode); + rtems_rfs_inode_close (fs, &target_inode); + return rc; + } + + rc = rtems_rfs_inode_close (fs, &parent_inode); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &target_inode); + return rc; + } + + rc = rtems_rfs_inode_close (fs, &target_inode); + + return rc; +} + +int +rtems_rfs_unlink (rtems_rfs_file_system* fs, + rtems_rfs_ino parent, + rtems_rfs_ino target, + uint32_t doff, + rtems_rfs_unlink_dir dir_mode) +{ + rtems_rfs_inode_handle parent_inode; + rtems_rfs_inode_handle target_inode; + uint16_t links; + bool dir; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK)) + printf ("rtems-rfs: unlink: parent(%" PRIu32 ") -X-> (%" PRIu32 ")\n", parent, target); + + rc = rtems_rfs_inode_open (fs, target, &target_inode, true); + if (rc) + return rc; + + /* + * If a directory process the unlink mode. + */ + + dir = RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&target_inode)); + if (dir) + { + switch (dir_mode) + { + case rtems_rfs_unlink_dir_denied: + if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK)) + printf ("rtems-rfs: link is a directory\n"); + rtems_rfs_inode_close (fs, &target_inode); + return EISDIR; + + case rtems_rfs_unlink_dir_if_empty: + rc = rtems_rfs_dir_empty (fs, &target_inode); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK)) + printf ("rtems-rfs: dir-empty: %d: %s\n", rc, strerror (rc)); + rtems_rfs_inode_close (fs, &target_inode); + return rc; + } + break; + + default: + break; + } + } + + rc = rtems_rfs_inode_open (fs, parent, &parent_inode, true); + if (rc) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK)) + printf ("rtems-rfs: link: inode-open failed: %d: %s\n", + rc, strerror (rc)); + rtems_rfs_inode_close (fs, &target_inode); + return rc; + } + + rc = rtems_rfs_dir_del_entry (fs, &parent_inode, target, doff); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK)) + printf ("rtems-rfs: unlink: dir-del failed: %d: %s\n", + rc, strerror (rc)); + rtems_rfs_inode_close (fs, &parent_inode); + rtems_rfs_inode_close (fs, &target_inode); + return rc; + } + + links = rtems_rfs_inode_get_links (&target_inode); + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK)) + printf ("rtems-rfs: unlink: target:%" PRIu32 " links:%u\n", target, links); + + if (links > 1) + { + links--; + rtems_rfs_inode_set_links (&target_inode, links); + } + else + { + /* + * Erasing the inode releases all blocks attached to it. + */ + rc = rtems_rfs_inode_delete (fs, &target_inode); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK)) + printf ("rtems-rfs: unlink: inode-del failed: %d: %s\n", + rc, strerror (rc)); + rtems_rfs_inode_close (fs, &parent_inode); + rtems_rfs_inode_close (fs, &target_inode); + return rc; + } + + if (dir) + { + links = rtems_rfs_inode_get_links (&parent_inode); + if (links > 1) + links--; + rtems_rfs_inode_set_links (&parent_inode, links); + } + } + + rc = rtems_rfs_inode_time_stamp_now (&parent_inode, true, true); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK)) + printf ("rtems-rfs: link: inode-time-stamp failed: %d: %s\n", + rc, strerror (rc)); + rtems_rfs_inode_close (fs, &parent_inode); + rtems_rfs_inode_close (fs, &target_inode); + return rc; + } + + rc = rtems_rfs_inode_close (fs, &parent_inode); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK)) + printf ("rtems-rfs: link: parent inode-close failed: %d: %s\n", + rc, strerror (rc)); + rtems_rfs_inode_close (fs, &target_inode); + return rc; + } + + rc = rtems_rfs_inode_close (fs, &target_inode); + + if ((rc > 0) && rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK)) + printf ("rtems-rfs: link: target inode-close failed: %d: %s\n", + rc, strerror (rc)); + + return rc; +} + +int +rtems_rfs_symlink (rtems_rfs_file_system* fs, + const char* name, + int length, + const char* link, + int link_length, + uid_t uid, + gid_t gid, + rtems_rfs_ino parent) +{ + rtems_rfs_inode_handle inode; + rtems_rfs_ino ino; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_SYMLINK)) + { + int c; + printf ("rtems-rfs: symlink: parent:%" PRIu32 " name:", parent); + for (c = 0; c < length; c++) + printf ("%c", name[c]); + printf (" link:"); + for (c = 0; c < link_length; c++) + printf ("%c", link[c]); + } + + if (link_length >= rtems_rfs_fs_block_size (fs)) + return ENAMETOOLONG; + + rc = rtems_rfs_inode_create (fs, parent, name, strlen (name), + RTEMS_RFS_S_SYMLINK, + 1, uid, gid, &ino); + if (rc > 0) + return rc; + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + return rc; + + /* + * If the link length is less than the length of data union in the inode + * place the link into the data area else allocate a block and write the link + * to that. + */ + if (link_length < RTEMS_RFS_INODE_DATA_NAME_SIZE) + { + memset (inode.node->data.name, 0, RTEMS_RFS_INODE_DATA_NAME_SIZE); + memcpy (inode.node->data.name, link, link_length); + rtems_rfs_inode_set_block_count (&inode, 0); + } + else + { + rtems_rfs_block_map map; + rtems_rfs_block_no block; + rtems_rfs_buffer_handle buffer; + uint8_t* data; + + rc = rtems_rfs_block_map_open (fs, &inode, &map); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + return rc; + } + + rc = rtems_rfs_block_map_grow (fs, &map, 1, &block); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + rtems_rfs_inode_close (fs, &inode); + return rc; + } + + rc = rtems_rfs_buffer_handle_open (fs, &buffer); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + rtems_rfs_inode_close (fs, &inode); + return rc; + } + + rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, false); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + rtems_rfs_inode_close (fs, &inode); + return rc; + } + + data = rtems_rfs_buffer_data (&buffer); + + memset (data, 0xff, rtems_rfs_fs_block_size (fs)); + memcpy (data, link, link_length); + + rc = rtems_rfs_buffer_handle_close (fs, &buffer); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + rtems_rfs_inode_close (fs, &inode); + return rc; + } + + rc = rtems_rfs_block_map_close (fs, &map); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + return rc; + } + } + + rtems_rfs_inode_set_block_offset (&inode, link_length); + + rc = rtems_rfs_inode_close (fs, &inode); + + return rc; +} + +int +rtems_rfs_symlink_read (rtems_rfs_file_system* fs, + rtems_rfs_ino link, + char* path, + size_t size, + size_t* length) +{ + rtems_rfs_inode_handle inode; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_SYMLINK_READ)) + printf ("rtems-rfs: symlink-read: link:%" PRIu32 "\n", link); + + rc = rtems_rfs_inode_open (fs, link, &inode, true); + if (rc) + return rc; + + if (!RTEMS_RFS_S_ISLNK (rtems_rfs_inode_get_mode (&inode))) + { + rtems_rfs_inode_close (fs, &inode); + return EINVAL; + } + + *length = rtems_rfs_inode_get_block_offset (&inode); + + if (size < *length) + { + rtems_rfs_inode_close (fs, &inode); + return EINVAL; + } + + if (rtems_rfs_inode_get_block_count (&inode) == 0) + { + memcpy (path, inode.node->data.name, *length); + } + else + { + rtems_rfs_block_map map; + rtems_rfs_block_no block; + rtems_rfs_buffer_handle buffer; + char* data; + + rc = rtems_rfs_block_map_open (fs, &inode, &map); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + return rc; + } + + rc = rtems_rfs_block_map_seek (fs, &map, 0, &block); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + rtems_rfs_inode_close (fs, &inode); + return rc; + } + + rc = rtems_rfs_buffer_handle_open (fs, &buffer); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + rtems_rfs_inode_close (fs, &inode); + return rc; + } + + rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, false); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + rtems_rfs_inode_close (fs, &inode); + return rc; + } + + data = rtems_rfs_buffer_data (&buffer); + memcpy (path, data, *length); + + rc = rtems_rfs_buffer_handle_close (fs, &buffer); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + rtems_rfs_inode_close (fs, &inode); + return rc; + } + + rc = rtems_rfs_block_map_close (fs, &map); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + return rc; + } + } + + path[*length] = '\0'; + + rc = rtems_rfs_inode_close (fs, &inode); + + return rc; +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-link.h b/cpukit/libfs/src/rfs/rtems-rfs-link.h new file mode 100644 index 0000000000..359360f0fd --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-link.h @@ -0,0 +1,112 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Link Support + * + * This file provides the link support functions. + */ + +#if !defined (_RTEMS_RFS_LINK_H_) +#define _RTEMS_RFS_LINK_H_ + +#include <dirent.h> + +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> + +/** + * Directory unlink modes. + */ +typedef enum rtems_rfs_unlink_dir_e +{ + rtems_rfs_unlink_dir_denied, /**< Not allowed to unlink a directory. */ + rtems_rfs_unlink_dir_if_empty, /**< Unlink if the directory is empty. */ + rtems_rfs_unlink_dir_allowed /**< Unlinking of directories is allowed. */ +} rtems_rfs_unlink_dir; + +/** + * Create a link. Do not link directories unless renaming or you will create + * loops in the file system. + * + * @param fs The file system. + * @param name The name of the link. + * @param length The length of the name. + * @param parent The inode number of the parent directory. + * @param target The inode of the target. + * @param link_dir If true directories can be linked. Useful when renaming. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_link (rtems_rfs_file_system* fs, + const char* name, + int length, + rtems_rfs_ino parent, + rtems_rfs_ino target, + bool link_dir); + +/** + * Unlink the node from the parent directory. A directory offset for the target + * entry is required because links cause a number of inode numbers to appear in + * a single directory so scanning does not work. + * + * @param fs The file system. + * @param parent The inode number of the parent directory. + * @param target The inode of the target. + * @param doff Parent directory entry offset for the target entry. + * @param dir_mode Directory unlink mode. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_unlink (rtems_rfs_file_system* fs, + rtems_rfs_ino parent, + rtems_rfs_ino target, + uint32_t doff, + rtems_rfs_unlink_dir dir_mode); + +/** + * Symbolic link is an inode that has a path attached. + * + * @param fs The file system data. + * @param name The name of the node. + * @param length The length of the name of the node. + * @param link The link path attached to the symlink inode. + * @param link_length The length of the link path. + * @param parent The parent inode number. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_symlink (rtems_rfs_file_system* fs, + const char* name, + int length, + const char* link, + int link_length, + uid_t uid, + gid_t gid, + rtems_rfs_ino parent); + +/** + * Read a symbolic link into the provided buffer returning the link of link + * name. + * + * @param fs The file system data. + * @param link The link inode number to read. + * @param path The buffer to write the link path into. + * @param size The size of the buffer. + * @param length Set to the length of the link path. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_symlink_read (rtems_rfs_file_system* fs, + rtems_rfs_ino link, + char* path, + size_t size, + size_t* length); + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-mutex.c b/cpukit/libfs/src/rfs/rtems-rfs-mutex.c new file mode 100644 index 0000000000..90a865fb76 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-mutex.c @@ -0,0 +1,72 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Mutex. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/rfs/rtems-rfs-mutex.h> + +#if __rtems__ +/** + * RTEMS_RFS Mutex Attributes + * + * @warning Do not configure as inherit priority. If a driver is in the driver + * initialisation table this locked semaphore will have the IDLE task + * as the holder and a blocking task will raise the priority of the + * IDLE task which can cause unsual side effects like not work. + */ +#define RTEMS_RFS_MUTEX_ATTRIBS \ + (RTEMS_PRIORITY | RTEMS_SIMPLE_BINARY_SEMAPHORE | \ + RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL) +#endif + +int +rtems_rfs_mutex_create (rtems_rfs_mutex* mutex) +{ +#if __rtems__ + rtems_status_code sc; + sc = rtems_semaphore_create (rtems_build_name ('R', 'F', 'S', 'm'), + 1, RTEMS_RFS_MUTEX_ATTRIBS, 0, + mutex); + if (sc != RTEMS_SUCCESSFUL) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_MUTEX)) + printf ("rtems-rfs: mutex: open failed: %s\n", + rtems_status_text (sc)); + return EIO; + } +#endif + return 0; +} + +int +rtems_rfs_mutex_destroy (rtems_rfs_mutex* mutex) +{ +#if __rtems__ + rtems_status_code sc; + sc = rtems_semaphore_delete (*mutex); + if (sc != RTEMS_SUCCESSFUL) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_MUTEX)) + printf ("rtems-rfs: mutex: close failed: %s\n", + rtems_status_text (sc)); + return EIO; + } +#endif + return 0; +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-mutex.h b/cpukit/libfs/src/rfs/rtems-rfs-mutex.h new file mode 100644 index 0000000000..12e7fee921 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-mutex.h @@ -0,0 +1,108 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Mutex. + * + * It may be suprising we abstract this for the RTEMS file system but this code + * is designed to be run on host operating systems. + */ + +#if !defined (_RTEMS_RFS_MUTEX_H_) +#define _RTEMS_RFS_MUTEX_H_ + +#include <errno.h> + +#include <rtems/rfs/rtems-rfs-trace.h> + +#if __rtems__ +#include <rtems.h> +#include <rtems/error.h> +#endif + +/** + * RFS Mutex type. + */ +#if __rtems__ +typedef rtems_id rtems_rfs_mutex; +#else +typedef uint32_t rtems_rfs_mutex; /* place holder */ +#endif + +/** + * Create the mutex. + * + * @param mutex Reference to the mutex handle returned to the caller. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_mutex_create (rtems_rfs_mutex* mutex); + +/** + * Create the mutex. + * + * @param mutex Reference to the mutex handle returned to the caller. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_mutex_destroy (rtems_rfs_mutex* mutex); + +/** + * Lock the mutex. + * + * @param mutex The mutex to lock. + * @retval true The mutex is locked. + * @retval false The mutex could not be locked. + */ +static inline int +rtems_rfs_mutex_lock (rtems_rfs_mutex* mutex) +{ +#if __rtems__ + rtems_status_code sc = rtems_semaphore_obtain (*mutex, RTEMS_WAIT, 0); + if (sc != RTEMS_SUCCESSFUL) + { +#if RTEMS_RFS_TRACE + if (rtems_rfs_trace (RTEMS_RFS_TRACE_MUTEX)) + printf ("rtems-rfs: mutex: obtain failed: %s\n", + rtems_status_text (sc)); +#endif + return EIO; + } +#endif + return 0; +} + +/** + * Unlock the mutex. + * + * @param mutex The mutex to unlock. + * @retval true The mutex is unlocked. + * @retval false The mutex could not be unlocked. + */ +static inline int +rtems_rfs_mutex_unlock (rtems_rfs_mutex* mutex) +{ +#if __rtems__ + rtems_status_code sc = rtems_semaphore_release (*mutex); + if (sc != RTEMS_SUCCESSFUL) + { +#if RTEMS_RFS_TRACE + if (rtems_rfs_trace (RTEMS_RFS_TRACE_MUTEX)) + printf ("rtems-rfs: mutex: release failed: %s\n", + rtems_status_text (sc)); +#endif + return EIO; + } +#endif + return 0; +} + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-rtems-dev.c b/cpukit/libfs/src/rfs/rtems-rfs-rtems-dev.c new file mode 100644 index 0000000000..e2b3eb207f --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems-dev.c @@ -0,0 +1,271 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS RFS Device Interface. + * + * This file contains the set of handlers used to map operations on RFS device + * nodes onto calls to the RTEMS Classic API IO Manager. + * + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "rtems-rfs-rtems.h" + +/* + * Convert RTEMS status to a UNIX errno + */ +extern int rtems_deviceio_errno (rtems_status_code code); + +/** + * This handler maps an open() operation onto rtems_io_open(). + * + * @param iop + * @param pathname + * @param flag + * @param mode + * @return int + */ +static int +rtems_rfs_rtems_device_open ( rtems_libio_t *iop, + const char *pathname, + uint32_t flag, + uint32_t mode) +{ + rtems_libio_open_close_args_t args; + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (&iop->pathinfo); + rtems_rfs_ino ino = rtems_rfs_rtems_get_iop_ino (iop); + rtems_rfs_inode_handle inode; + int major; + int minor; + rtems_status_code status; + int rc; + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("device_open: opening inode", rc); + } + + major = rtems_rfs_inode_get_block (&inode, 0); + minor = rtems_rfs_inode_get_block (&inode, 1); + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("device_open: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + + iop->data0 = major; + iop->data1 = (void*)((intptr_t) minor); + + args.iop = iop; + args.flags = iop->flags; + args.mode = mode; + + status = rtems_io_open (major, minor, (void *) &args); + + return rtems_deviceio_errno (status); +} + +/** + * This handler maps a close() operation onto rtems_io_close(). + * + * @param iop + * @return int + */ + +static int +rtems_rfs_rtems_device_close (rtems_libio_t* iop) +{ + rtems_libio_open_close_args_t args; + rtems_status_code status; + int major; + int minor; + + major = (int) iop->data0; + minor = (intptr_t) iop->data1; + + args.iop = iop; + args.flags = 0; + args.mode = 0; + + status = rtems_io_close (major, minor, (void *) &args); + + return rtems_deviceio_errno (status); +} + +/** + * This handler maps a read() operation onto rtems_io_read(). + * + * @param iop + * @param buffer + * @param count + * @return ssize_t + */ + +static ssize_t +rtems_rfs_rtems_device_read (rtems_libio_t* iop, void* buffer, size_t count) +{ + rtems_libio_rw_args_t args; + rtems_status_code status; + int major; + int minor; + + major = (int) iop->data0; + minor = (intptr_t) iop->data1; + + args.iop = iop; + args.offset = iop->offset; + args.buffer = buffer; + args.count = count; + args.flags = iop->flags; + args.bytes_moved = 0; + + status = rtems_io_read (major, minor, (void *) &args); + if (status) + return rtems_deviceio_errno (status); + + return (ssize_t) args.bytes_moved; +} + +/* + * This handler maps a write() operation onto rtems_io_write(). + * + * @param iop + * @param buffer + * @param count + * @return ssize_t + */ + +static ssize_t +rtems_rfs_rtems_device_write (rtems_libio_t* iop, + const void* buffer, + size_t count) +{ + rtems_libio_rw_args_t args; + rtems_status_code status; + int major; + int minor; + + major = (int) iop->data0; + minor = (intptr_t) iop->data1; + + args.iop = iop; + args.offset = iop->offset; + args.buffer = (void *) buffer; + args.count = count; + args.flags = iop->flags; + args.bytes_moved = 0; + + status = rtems_io_write (major, minor, (void *) &args); + if (status) + return rtems_deviceio_errno (status); + + return (ssize_t) args.bytes_moved; +} + +/** + * This handler maps an ioctl() operation onto rtems_io_ioctl(). + * + * @param iop + * @param command + * @param buffer + * @return int + */ + +static int +rtems_rfs_rtems_device_ioctl (rtems_libio_t* iop, + uint32_t command, + void* buffer) +{ + rtems_libio_ioctl_args_t args; + rtems_status_code status; + int major; + int minor; + + major = (int) iop->data0; + minor = (intptr_t) iop->data1; + + args.iop = iop; + args.command = command; + args.buffer = buffer; + + status = rtems_io_control (major, minor, (void *) &args); + if (status) + return rtems_deviceio_errno (status); + + return args.ioctl_return; +} + +/** + * This handler eats all lseek() operations and does not create an error. It + * assumes all devices can handle the seek. The writes fail. + * + * @param iop + * @param offset + * @param whence + * @return rtems_off64_t + */ + +static rtems_off64_t +rtems_rfs_rtems_device_lseek (rtems_libio_t* iop, + rtems_off64_t offset, + int whence) +{ + return offset; +} + +/** + * The consumes the truncate call. You cannot truncate device files. + * + * @param iop + * @param length + * @return int + */ + +static int +rtems_rfs_rtems_device_ftruncate (rtems_libio_t* iop, rtems_off64_t length) +{ + return 0; +} + +/* + * Handler table for RFS device nodes + */ + +const rtems_filesystem_file_handlers_r rtems_rfs_rtems_device_handlers = { + .open_h = rtems_rfs_rtems_device_open, + .close_h = rtems_rfs_rtems_device_close, + .read_h = rtems_rfs_rtems_device_read, + .write_h = rtems_rfs_rtems_device_write, + .ioctl_h = rtems_rfs_rtems_device_ioctl, + .lseek_h = rtems_rfs_rtems_device_lseek, + .fstat_h = rtems_rfs_rtems_fstat, + .fchmod_h = rtems_rfs_rtems_fchmod, + .ftruncate_h = rtems_rfs_rtems_device_ftruncate, + .fpathconf_h = rtems_filesystem_default_fpathconf, + .fsync_h = rtems_filesystem_default_fsync, + .fdatasync_h = rtems_filesystem_default_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .rmnod_h = rtems_rfs_rtems_rmnod +}; diff --git a/cpukit/libfs/src/rfs/rtems-rfs-rtems-dir.c b/cpukit/libfs/src/rfs/rtems-rfs-rtems-dir.c new file mode 100644 index 0000000000..bc3c4b85fa --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems-dir.c @@ -0,0 +1,245 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS RFS Directory Access Routines + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include <rtems/rfs/rtems-rfs-dir.h> +#include <rtems/rfs/rtems-rfs-link.h> +#include "rtems-rfs-rtems.h" + +/** + * This rountine will verify that the node being opened as a directory is in + * fact a directory node. If it is then the offset into the directory will be + * set to 0 to position to the first directory entry. + * + * @param iop + * @param pathname + * @param flag + * @param mode + * @@return int + */ +static int +rtems_rfs_rtems_dir_open (rtems_libio_t* iop, + const char* pathname, + uint32_t flag, + uint32_t mode) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (&iop->pathinfo); + rtems_rfs_ino ino = rtems_rfs_rtems_get_iop_ino (iop); + rtems_rfs_inode_handle inode; + int rc; + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("dir_open: opening inode", rc); + } + + if (!RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode))) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("dir_open: not dir", ENOTDIR); + } + + iop->offset = 0; + + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return 0; +} + +/** + * This routine will be called by the generic close routine to cleanup any + * resources that have been allocated for the management of the file + * + * @param iop + * @retval 0 Always no error. + */ +static int +rtems_rfs_rtems_dir_close (rtems_libio_t* iop) +{ + /* + * The RFS does not hold any resources. Nothing to do. + */ + return 0; +} + +/** + * 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. + */ +static ssize_t +rtems_rfs_rtems_dir_read (rtems_libio_t* iop, + void* buffer, + size_t count) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (&iop->pathinfo); + rtems_rfs_ino ino = rtems_rfs_rtems_get_iop_ino (iop); + rtems_rfs_inode_handle inode; + struct dirent* dirent; + ssize_t bytes_transferred; + int d; + int rc; + + count = count / sizeof (struct dirent); + dirent = buffer; + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("dir_read: read inode", rc); + } + + bytes_transferred = 0; + + for (d = 0; d < count; d++, dirent++) + { + size_t size; + rc = rtems_rfs_dir_read (fs, &inode, iop->offset, dirent, &size); + if (rc == ENOENT) + { + rc = 0; + break; + } + if (rc > 0) + { + bytes_transferred = rtems_rfs_rtems_error ("dir_read: dir read", rc); + break; + } + iop->offset += size; + bytes_transferred += sizeof (struct dirent); + } + + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + + return bytes_transferred; +} + +/** + * 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. + * + * @param iop + * @param offset + * @param whence + * return rtems_off64_t + */ +static rtems_off64_t +rtems_rfs_rtems_dir_lseek (rtems_libio_t* iop, + rtems_off64_t offset, + int whence) +{ + switch (whence) + { + case SEEK_SET: /* absolute move from the start of the file */ + case SEEK_CUR: /* relative move */ + break; + + case SEEK_END: /* Movement past the end of the directory via lseek */ + /* is not a permitted operation */ + default: + return rtems_rfs_rtems_error ("dir_lseek: bad whence", EINVAL); + break; + } + return 0; +} + +static int +rtems_rfs_rtems_dir_rmnod (rtems_filesystem_location_info_t* parent_pathloc, + rtems_filesystem_location_info_t* pathloc) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino parent = rtems_rfs_rtems_get_pathloc_ino (parent_pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + uint32_t doff = rtems_rfs_rtems_get_pathloc_doff (pathloc); + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_DIR_RMNOD)) + printf ("rtems-rfs: dir-rmnod: parent:%" PRId32 " doff:%" PRIu32 ", ino:%" PRId32 "\n", + parent, doff, ino); + + if (ino == RTEMS_RFS_ROOT_INO) + return rtems_rfs_rtems_error ("dir_rmnod: root inode", EBUSY); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_unlink (fs, parent, ino, doff, rtems_rfs_unlink_dir_if_empty); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("dir_rmnod: unlinking", rc); + } + + rtems_rfs_rtems_unlock (fs); + return 0; +} + +/* + * Set of operations handlers for operations on directories. + */ + +const rtems_filesystem_file_handlers_r rtems_rfs_rtems_dir_handlers = { + .open_h = rtems_rfs_rtems_dir_open, + .close_h = rtems_rfs_rtems_dir_close, + .read_h = rtems_rfs_rtems_dir_read, + .write_h = rtems_filesystem_default_write, + .ioctl_h = rtems_filesystem_default_ioctl, + .lseek_h = rtems_rfs_rtems_dir_lseek, + .fstat_h = rtems_rfs_rtems_fstat, + .fchmod_h = rtems_rfs_rtems_fchmod, + .ftruncate_h = rtems_filesystem_default_ftruncate, + .fpathconf_h = rtems_filesystem_default_fpathconf, + .fsync_h = rtems_filesystem_default_fsync, + .fdatasync_h = rtems_rfs_rtems_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .rmnod_h = rtems_rfs_rtems_dir_rmnod +}; diff --git a/cpukit/libfs/src/rfs/rtems-rfs-rtems-file.c b/cpukit/libfs/src/rfs/rtems-rfs-rtems-file.c new file mode 100644 index 0000000000..2f2df428dd --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems-file.c @@ -0,0 +1,354 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS RFS File Handlers + * + * This file contains the set of handlers used to process operations on + * RFS file nodes. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <rtems/rfs/rtems-rfs-file.h> +#include "rtems-rfs-rtems.h" + +/** + * This routine processes the open() system call. Note that there is nothing + * special to be done at open() time. + * + * @param iop + * @param pathname + * @param flag + * @param mode + * @return int + */ + +static int +rtems_rfs_rtems_file_open (rtems_libio_t* iop, + const char* pathname, + uint32_t flag, + uint32_t mode) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (&iop->pathinfo); + rtems_rfs_ino ino; + rtems_rfs_file_handle* file; + uint32_t flags; + int rc; + + flags = 0; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_FILE_OPEN)) + printf("rtems-rfs: file-open: path:%s ino:%" PRId32 " flags:%04" PRIx32 " mode:%04" PRIx32 "\n", + pathname, ino, flags, mode); + + rtems_rfs_rtems_lock (fs); + + ino = rtems_rfs_rtems_get_iop_ino (iop); + + rc = rtems_rfs_file_open (fs, ino, flags, &file); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("file-open: open", rc); + } + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_FILE_OPEN)) + printf("rtems-rfs: file-open: handle:%p\n", file); + + iop->size = rtems_rfs_file_size (file); + rtems_rfs_rtems_set_iop_file_handle (iop, file); + + rtems_rfs_rtems_unlock (fs); + return 0; +} + +/** + * This routine processes the close() system call. Note that there is nothing + * to flush at this point. + * + * @param iop + * @return int + */ +static int +rtems_rfs_rtems_file_close (rtems_libio_t* iop) +{ + rtems_rfs_file_handle* file = rtems_rfs_rtems_get_iop_file_handle (iop); + rtems_rfs_file_system* fs = rtems_rfs_file_fs (file); + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_FILE_CLOSE)) + printf("rtems-rfs: file-close: handle:%p\n", file); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_file_close (fs, file); + if (rc > 0) + rc = rtems_rfs_rtems_error ("file-close: file close", rc); + + rtems_rfs_rtems_unlock (fs); + return rc; +} + +/** + * This routine processes the read() system call. + * + * @param iop + * @param buffer + * @param count + * @return int + */ +static ssize_t +rtems_rfs_rtems_file_read (rtems_libio_t* iop, + void* buffer, + size_t count) +{ + rtems_rfs_file_handle* file = rtems_rfs_rtems_get_iop_file_handle (iop); + rtems_rfs_pos pos; + uint8_t* data = buffer; + ssize_t read = 0; + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_FILE_READ)) + printf("rtems-rfs: file-read: handle:%p count:%zd\n", file, count); + + rtems_rfs_rtems_lock (rtems_rfs_file_fs (file)); + + pos = iop->offset; + + if (pos < rtems_rfs_file_size (file)) + { + while (count) + { + size_t size; + + rc = rtems_rfs_file_io_start (file, &size, true); + if (rc > 0) + { + read = rtems_rfs_rtems_error ("file-read: read: io-start", rc); + break; + } + + if (size == 0) + break; + + if (size > count) + size = count; + + memcpy (data, rtems_rfs_file_data (file), size); + + data += size; + count -= size; + read += size; + + rc = rtems_rfs_file_io_end (file, size, true); + if (rc > 0) + { + read = rtems_rfs_rtems_error ("file-read: read: io-end", rc); + break; + } + } + } + + rtems_rfs_rtems_unlock (rtems_rfs_file_fs (file)); + + return read; +} + +/** + * This routine processes the write() system call. + * + * @param iop + * @param buffer + * @param count + * @return ssize_t + */ +static ssize_t +rtems_rfs_rtems_file_write (rtems_libio_t* iop, + const void* buffer, + size_t count) +{ + rtems_rfs_file_handle* file = rtems_rfs_rtems_get_iop_file_handle (iop); + rtems_rfs_pos pos; + const uint8_t* data = buffer; + ssize_t write = 0; + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_FILE_WRITE)) + printf("rtems-rfs: file-write: handle:%p count:%zd\n", file, count); + + rtems_rfs_rtems_lock (rtems_rfs_file_fs (file)); + + pos = iop->offset; + + /* + * If the iop position is past the physical end of the file we need to set + * the file size to the new length before writing. If the position equals the + * size of file we are still past the end of the file as positions number + * from 0. For a specific position we need a file that has a length of one + * more. + */ + + if (pos >= rtems_rfs_file_size (file)) + { + rc = rtems_rfs_file_set_size (file, pos + 1); + if (rc) + { + rtems_rfs_rtems_unlock (rtems_rfs_file_fs (file)); + return rtems_rfs_rtems_error ("file-write: write extend", rc); + } + } + + rtems_rfs_file_set_bpos (file, pos); + + while (count) + { + size_t size = count; + + rc = rtems_rfs_file_io_start (file, &size, false); + if (rc) + { + write = rtems_rfs_rtems_error ("file-write: write open", rc); + break; + } + + if (size > count) + size = count; + + memcpy (rtems_rfs_file_data (file), data, size); + + data += size; + count -= size; + write += size; + + rc = rtems_rfs_file_io_end (file, size, false); + if (rc) + { + write = rtems_rfs_rtems_error ("file-write: write close", rc); + break; + } + } + + iop->size = rtems_rfs_file_size (file); + + rtems_rfs_rtems_unlock (rtems_rfs_file_fs (file)); + + return write; +} + +/** + * This routine processes the ioctl() system call. + * + * @note No ioctl()'s are currently supported for RFS files. + * + * @param iop + * @param command + * @param buffer + */ + +static int +rtems_rfs_rtems_file_ioctl (rtems_libio_t* iop, uint32_t command, void* buffer) +{ + return 0; +} + +/** + * This routine processes the lseek() system call. + * + * @param iop + * @param offset + * @param whence + * @return rtems_off64_t + */ +static rtems_off64_t +rtems_rfs_rtems_file_lseek (rtems_libio_t* iop, + rtems_off64_t offset, + int whence) +{ + rtems_rfs_file_handle* file = rtems_rfs_rtems_get_iop_file_handle (iop); + rtems_rfs_pos pos; + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_FILE_LSEEK)) + printf("rtems-rfs: file-lseek: handle:%p offset:%Ld\n", file, offset); + + rtems_rfs_rtems_lock (rtems_rfs_file_fs (file)); + + pos = iop->offset; + + rc = rtems_rfs_file_seek (file, pos, &pos); + if (rc) + { + rtems_rfs_rtems_unlock (rtems_rfs_file_fs (file)); + return rtems_rfs_rtems_error ("file_lseek: lseek", rc); + } + + rtems_rfs_rtems_unlock (rtems_rfs_file_fs (file)); + + return iop->offset; +} + +/** + * This routine processes the ftruncate() system call. + * + * @param iop + * @param length + * @return int + */ +static int +rtems_rfs_rtems_file_ftruncate (rtems_libio_t* iop, + rtems_off64_t length) +{ + rtems_rfs_file_handle* file = rtems_rfs_rtems_get_iop_file_handle (iop); + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_FILE_FTRUNC)) + printf("rtems-rfs: file-ftrunc: handle:%p length:%Ld\n", file, length); + + rtems_rfs_rtems_lock (rtems_rfs_file_fs (file)); + + rc = rtems_rfs_file_set_size (file, length); + if (rc) + rc = rtems_rfs_rtems_error ("file_ftruncate: set size", rc); + + iop->size = rtems_rfs_file_size (file); + + rtems_rfs_rtems_unlock (rtems_rfs_file_fs (file)); + + return rc; +} + +/* + * Set of operations handlers for operations on RFS files. + */ + +const rtems_filesystem_file_handlers_r rtems_rfs_rtems_file_handlers = { + .open_h = rtems_rfs_rtems_file_open, + .close_h = rtems_rfs_rtems_file_close, + .read_h = rtems_rfs_rtems_file_read, + .write_h = rtems_rfs_rtems_file_write, + .ioctl_h = rtems_rfs_rtems_file_ioctl, + .lseek_h = rtems_rfs_rtems_file_lseek, + .fstat_h = rtems_rfs_rtems_fstat, + .fchmod_h = rtems_rfs_rtems_fchmod, + .ftruncate_h = rtems_rfs_rtems_file_ftruncate, + .fpathconf_h = rtems_filesystem_default_fpathconf, + .fsync_h = rtems_rfs_rtems_fdatasync, + .fdatasync_h = rtems_rfs_rtems_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .rmnod_h = rtems_rfs_rtems_rmnod +}; diff --git a/cpukit/libfs/src/rfs/rtems-rfs-rtems-utils.c b/cpukit/libfs/src/rfs/rtems-rfs-rtems-utils.c new file mode 100644 index 0000000000..2c8ba591f2 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems-utils.c @@ -0,0 +1,244 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * Set of utility functions to support RTEMS RFS on RTEMS. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> + +#include "rtems-rfs-rtems.h" + +bool +rtems_rfs_rtems_eval_perms (rtems_rfs_inode_handle* inode, int flags) +{ + uid_t st_uid; + gid_t st_gid; + uint16_t uid; + uint16_t gid; + uint16_t mode; + int flags_to_test; + + uid = rtems_rfs_inode_get_uid (inode); + gid = rtems_rfs_inode_get_gid (inode); + mode = rtems_rfs_inode_get_mode (inode); + +#if defined (RTEMS_POSIX_API) + st_uid = geteuid (); + st_gid = getegid (); +#else + st_uid = uid; + st_gid = gid; +#endif + + /* + * Check if I am owner or a group member or someone else. + */ + flags_to_test = flags; + + if ((st_uid == 0) || (st_uid == uid)) + flags_to_test |= flags << 6; + if ((st_uid == 0) || (st_gid == gid)) + flags_to_test |= flags << 3; + else + /* must be other - already set above */; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PERMS)) + printf ("rtems-rfs: eval-perms: uid=%d gid=%d iuid=%d igid=%d " \ + "flags=%o flags_to_test=%o mode=%o (%o)\n", + st_uid, st_gid, uid, gid, + flags, flags_to_test, mode & 0777, + flags_to_test & (mode & 0777)); + + /* + * If all of the flags are set we have permission + * to do this. + */ + if ((flags_to_test & (mode & 0777)) != 0) + return true; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PERMS)) + printf("rtems-rfs: eval-perms: perms failed\n"); + + return false; +} + +/* + * The following sets the handlers based on the type of inode. + */ + +bool +rtems_rfs_rtems_set_handlers (rtems_filesystem_location_info_t* loc, + rtems_rfs_inode_handle* inode) +{ + uint16_t mode = rtems_rfs_inode_get_mode (inode); + loc->handlers = NULL; + if (RTEMS_RFS_S_ISDIR (mode)) + loc->handlers = rtems_rfs_rtems_handlers (dir); + else if (RTEMS_RFS_S_ISCHR (mode) || RTEMS_RFS_S_ISBLK(mode)) + loc->handlers = rtems_rfs_rtems_handlers (device); + else if (RTEMS_RFS_S_ISLNK (mode)) + loc->handlers = rtems_rfs_rtems_handlers (link); + else if (RTEMS_RFS_S_ISREG (mode)) + loc->handlers = rtems_rfs_rtems_handlers (file); + else + { + printf ("rtems-rfs: mode type unknown: %04x\n", mode); + return false; + } + return true; +} + +uint16_t +rtems_rfs_rtems_imode (mode_t mode) +{ + /* + * Mapping matches RTEMS so no need to change. + */ + return mode; +} + +mode_t +rtems_rfs_rtems_mode (int imode) +{ + /* + * Mapping matches RTEMS so no need to change. + */ + return imode; +} + +/* + * Only provide if there is no macro version. + */ +#if !defined (rtems_rfs_rtems_error) +int +rtems_rfs_rtems_error (const char* mesg, int error) +{ + if (error) + printf ("rtems-rfs: %s: %d: %s\n", mesg, error, strerror (error)); + errno = error; + return error == 0 ? 0 : -1; +} +#endif + +#if RTEMS_RFS_RTEMS_TRACE +static uint32_t rtems_rfs_rtems_trace_mask; + +bool +rtems_rfs_rtems_trace (uint32_t mask) +{ + bool result = false; + if (mask & rtems_rfs_rtems_trace_mask) + result = true; + return result; +} + +void +rtems_rfs_trace_rtems_set_mask (uint32_t mask) +{ + rtems_rfs_rtems_trace_mask |= mask; +} + +void +rtems_rfs_trace_rtems_clear_mask (uint32_t mask) +{ + rtems_rfs_rtems_trace_mask &= ~mask; +} + +int +rtems_rfs_rtems_trace_shell_command (int argc, char *argv[]) +{ + const char* table[] = + { + "error-msgs", + "eval-path" + "eval-for-make", + "eval-perms", + "mknod", + "rmnod", + "link", + "unlink", + "chown", + "readlink", + "fchmod", + "stat", + "dir-rmnod", + "file-open", + "file-close", + "file-read", + "file-write", + "file-lseek", + "file-ftrunc" + }; + + bool set = true; + int arg; + int t; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + switch (argv[arg][1]) + { + case 'h': + printf ("usage: %s [-hl] [set/clear] [flags]\n", argv[0]); + return 0; + case 'l': + printf ("%s: valid flags to set or clear are:\n", argv[0]); + for (t = 0; t < (sizeof (table) / sizeof (const char*)); t++) + printf (" %s\n", table[t]); + return 0; + default: + printf ("error: unknown option\n"); + return 1; + } + } + else + { + uint32_t value = 0; + if (strcmp (argv[arg], "set") == 0) + set = true; + if (strcmp (argv[arg], "clear") == 0) + set = false; + else if (strcmp (argv[arg], "all") == 0) + value = RTEMS_RFS_RTEMS_DEBUG_ALL; + else + { + for (t = 0; t < (sizeof (table) / sizeof (const char*)); t++) + { + if (strcmp (argv[arg], table[t]) == 0) + { + value = 1 << t; + break; + } + } + } + + if (set) + rtems_rfs_rtems_trace_mask |= value; + else + rtems_rfs_rtems_trace_mask &= ~value; + } + } + + return 0; +} + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-rtems.c b/cpukit/libfs/src/rfs/rtems-rfs-rtems.c new file mode 100644 index 0000000000..bf688f0f67 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems.c @@ -0,0 +1,1286 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Interface for RTEMS. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> +#include <stdlib.h> + +#if SIZEOF_MODE_T == 8 +#define PRIomode_t PRIo64 +#elif SIZEOF_MODE_T == 4 +#define PRIomode_t PRIo32 +#else +#error "unsupport size of mode_t" +#endif + +#include <rtems/rfs/rtems-rfs-file.h> +#include <rtems/rfs/rtems-rfs-dir.h> +#include <rtems/rfs/rtems-rfs-link.h> +#include "rtems-rfs-rtems.h" + +/** + * The libio permissions for read/execute. + */ +#define RTEMS_LIBIO_PERMS_RX (RTEMS_LIBIO_PERMS_SEARCH | RTEMS_LIBIO_PERMS_READ) +/** + * The libio permissions for write/execute. + */ +#define RTEMS_LIBIO_PERMS_WX (RTEMS_LIBIO_PERMS_SEARCH | RTEMS_LIBIO_PERMS_WRITE) + +/** + * Evaluate the path to a node that wishes to be accessed. The pathloc is + * returned with the ino to the node to be accessed. + * + * The routine starts from the root stripping away any leading path separators + * breaking the path up into the node names and checking an inode exists for + * that node name. Permissions are checked to insure access to the node is + * allowed. A path to a node must be accessable all the way even if the end + * result is directly accessable. As a user on Linux try "ls /root/../tmp" and + * you will see if fails. + * + * The whole process is complicated by crossmount paths where we head down into + * this file system only to return to the top and out to a another mounted file + * system. For example we are mounted on '/e' and the user enters "ls + * /e/a/b/../../dev". We need to head down then back up. + * + * @param path + * @param pathlen + * @param flags + * @param pathloc + */ +static int +rtems_rfs_rtems_eval_path (const char* path, + size_t pathlen, + int flags, + rtems_filesystem_location_info_t* pathloc) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_inode_handle inode; + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + uint32_t doff = 0; + const char* node; + size_t node_len; + int stripped; + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH)) + printf ("rtems-rfs-rtems: eval-path: in: path:%s pathlen:%zi ino:%" PRId32 "\n", + path, pathlen, ino); + + /* + * Eat any separators at the start of the path. + */ + stripped = rtems_filesystem_prefix_separators (path, pathlen); + path += stripped; + pathlen -= stripped; + + rtems_rfs_rtems_lock (fs); + + while (true) + { + /* + * Open and load the inode. + */ + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_path: opening inode", rc); + } + + /* + * Is this the end of the pathname we where given ? + */ + if ((*path == '\0') || (pathlen == 0)) + break; + + /* + * If a directory the execute bit must be set for us to enter. + */ + if (RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode)) && + !rtems_rfs_rtems_eval_perms (&inode, RTEMS_LIBIO_PERMS_SEARCH)) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_path: eval perms", EACCES); + } + + /* + * Extract the node name we will look for this time around. + */ + node = path; + node_len = 0; + while (!rtems_filesystem_is_separator (*path) && + (*path != '\0') && pathlen && + ((node_len + 1) < rtems_rfs_fs_max_name (fs))) + { + path++; + pathlen--; + node_len++; + } + + /* + * Eat any separators at start of the path. + */ + stripped = rtems_filesystem_prefix_separators (path, pathlen); + path += stripped; + pathlen -= stripped; + node_len += stripped; + + /* + * If the node is the current directory and there is more path to come move + * on it else we are at the inode we want. + */ + if (rtems_rfs_current_dir (node)) + { + if (*path) + { + rtems_rfs_inode_close (fs, &inode); + continue; + } + break; + } + + /* + * If the node is a parent we must move up one directory. If the location + * is on another file system we have a crossmount so we call that file + * system to handle the remainder of the path. + */ + if (rtems_rfs_parent_dir (node)) + { + /* + * If we are at root inode of the file system we have a crossmount path. + */ + if (ino == RTEMS_RFS_ROOT_INO) + { + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH)) + printf("rtems-rfs-rtems: eval-path: crossmount: path:%s (%zd)\n", + path - node_len, pathlen + node_len); + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + *pathloc = pathloc->mt_entry->mt_point_node; + return (*pathloc->ops->evalpath_h)(path - node_len, pathlen + node_len, + flags, pathloc); + } + + /* + * We need to find the parent of this node. + */ + rc = rtems_rfs_dir_lookup_ino (fs, &inode, "..", 2, &ino, &doff); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_path: read parent inode", rc); + } + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH)) + printf("rtems-rfs-rtems: eval-path: parent: ino:%" PRId32 "\n", ino); + } + else + { + /* + * Look up the node name in this directory. If found drop through, close + * the current inode and let the loop open the inode so the mode can be + * read and handlers set. + */ + rc = rtems_rfs_dir_lookup_ino (fs, &inode, + node, node_len - stripped, &ino, &doff); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return ((errno = rc) == 0) ? 0 : -1; + } + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH)) + printf("rtems-rfs-rtems: eval-path: down: path:%s ino:%" PRId32 "\n", node, ino); + } + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_path: closing node", rc); + } + } + + rtems_rfs_rtems_set_pathloc_ino (pathloc, ino); + rtems_rfs_rtems_set_pathloc_doff (pathloc, doff); + + rc = rtems_rfs_rtems_set_handlers (pathloc, &inode) ? 0 : EIO; + + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH)) + printf("rtems-rfs-rtems: eval-path: ino:%" PRId32 "\n", ino); + + return rc; +} + +/** + * The following routine evaluates a path for a new node to be created. The + * pathloc is returned with a pointer to the parent of the new node. The name + * is returned with a pointer to the first character in the new node name. The + * parent node is verified to be a directory. + * + * @param path + * @param pathloc + * @param name + * @return int + */ +static int +rtems_rfs_rtems_eval_for_make (const char* path, + rtems_filesystem_location_info_t* pathloc, + const char** name) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_inode_handle inode; + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_rfs_ino node_ino; + uint32_t doff = 0; + const char* node; + int node_len; + int stripped; + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE)) + printf ("rtems-rfs-rtems: eval-for-make: path:%s ino:%" PRId32 "\n", path, ino); + + *name = path + strlen (path); + + while (*name != path) + { + (*name)--; + if (rtems_filesystem_is_separator (**name)) + { + (*name)++; + break; + } + } + + /* + * Eat any separators at start of the path. + */ + stripped = rtems_filesystem_prefix_separators (path, strlen(path)); + path += stripped; + + rtems_rfs_rtems_lock (fs); + + while (true) + { + /* + * Open and load the inode. + */ + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: read ino", rc); + } + + /* + * If a directory the execute bit must be set for us to enter. + */ + if (RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode)) && + !rtems_rfs_rtems_eval_perms (&inode, RTEMS_LIBIO_PERMS_SEARCH)) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: eval perms", EACCES); + } + + /* + * Is this the end of the pathname we where given ? + */ + if (path == *name) + break; + + /* + * Extract the node name we will look for this time around. + */ + node = path; + node_len = 0; + while (!rtems_filesystem_is_separator(*path) && + (*path != '\0') && + (node_len < (rtems_rfs_fs_max_name (fs) - 1))) + { + node_len++; + path++; + } + + /* + * Eat any separators at start of the new path. + */ + stripped = rtems_filesystem_prefix_separators (path, strlen (path)); + path += stripped; + node_len += stripped; + + /* + * If the node is the current directory and there is more path to come move + * on it else we are at the inode we want. + */ + if (rtems_rfs_current_dir (node)) + { + if (*path) + { + rtems_rfs_inode_close (fs, &inode); + continue; + } + break; + } + + /* + * If the node is a parent we must move up one directory. If the location + * is on another file system we have a crossmount so we call that file + * system to handle the remainder of the path. + */ + if (rtems_rfs_parent_dir (path)) + { + /* + * If we are at the root inode of the file system we have a crossmount + * path. + */ + if (ino == RTEMS_RFS_ROOT_INO) + { + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE)) + printf("rtems-rfs-rtems: eval-for-make: crossmount: path:%s\n", + path - node_len); + + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + *pathloc = pathloc->mt_entry->mt_point_node; + return (*pathloc->ops->evalformake_h)(path - node_len, pathloc, name); + } + + /* + * If not a directory give and up return. We cannot change dir from a + * regular file or device node. + */ + if (!RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode))) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: not dir", ENOTSUP); + } + + /* + * We need to find the parent of this node. + */ + rc = rtems_rfs_dir_lookup_ino (fs, &inode, "..", 2, &ino, &doff); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: read parent inode", rc); + } + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE)) + printf ("rtems-rfs-rtems: eval-for-make: parent: ino:%" PRId32 "\n", ino); + } + else + { + /* + * Read the inode so we know it exists and what type it is. + */ + rc = rtems_rfs_dir_lookup_ino (fs, &inode, + node, node_len - stripped, &ino, &doff); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: reading inode", rc); + } + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE)) + printf("rtems-rfs-rtems: eval-for-make: down: path:%s ino:%" PRId32 "\n", + node, ino); + } + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: closing node", rc); + } + } + + if (!RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode))) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: not dir", ENOTDIR); + } + + if (!rtems_rfs_rtems_eval_perms (&inode, RTEMS_LIBIO_PERMS_WX)) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: cannot write", EACCES); + } + + /* + * Make sure the name does not already exists in the directory. + */ + rc = rtems_rfs_dir_lookup_ino (fs, &inode, *name, strlen (*name), + &node_ino, &doff); + if (rc == 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: found name", EEXIST); + } + + if (rc != ENOENT) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: look up", rc); + } + + /* + * Set the parent ino in the path location. + */ + + rtems_rfs_rtems_set_pathloc_ino (pathloc, ino); + rtems_rfs_rtems_set_pathloc_doff (pathloc, doff); + + rc = rtems_rfs_rtems_set_handlers (pathloc, &inode) ? 0 : EIO; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE)) + printf("rtems-rfs-rtems: eval-for-make: parent ino:%" PRId32 " name:%s\n", + ino, *name); + + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + + return rc; +} + +/** + * The following rouine creates a new link node under parent with the name + * given in name. The link node is set to point to the node at to_loc. + * + * @param to_loc + * @param parent_loc + * @param name + * @return int + */ +static int +rtems_rfs_rtems_link (rtems_filesystem_location_info_t* to_loc, + rtems_filesystem_location_info_t* parent_loc, + const char* name) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (to_loc); + rtems_rfs_ino target = rtems_rfs_rtems_get_pathloc_ino (to_loc); + rtems_rfs_ino parent = rtems_rfs_rtems_get_pathloc_ino (parent_loc); + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_LINK)) + printf ("rtems-rfs-rtems: link: in: parent:%" PRId32 " target:%" PRId32 "\n", + parent, target); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_link (fs, name, strlen (name), parent, target, false); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("link: linking", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * Routine to remove a link node from the file system. + * + * @param parent_loc + * @param loc + * @return int + */ + +static int +rtems_rfs_rtems_unlink (rtems_filesystem_location_info_t* parent_loc, + rtems_filesystem_location_info_t* loc) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (parent_loc); + rtems_rfs_ino parent = rtems_rfs_rtems_get_pathloc_ino (parent_loc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (loc); + uint32_t doff = rtems_rfs_rtems_get_pathloc_doff (loc); + int rc; + + rtems_rfs_rtems_lock (fs); + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_UNLINK)) + printf("rtems-rfs-rtems: unlink: parent:%" PRId32 " doff:%" PRIu32 " ino:%" PRId32 "\n", + parent, doff, ino); + + rc = rtems_rfs_unlink (fs, parent, ino, doff, rtems_rfs_unlink_dir_denied); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("unlink: unlink inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * The following verifies that and returns the type of node that the loc refers + * to. + * + * @param pathloc + * @return rtems_filesystem_node_types_t + */ + +static rtems_filesystem_node_types_t +rtems_rfs_rtems_node_type (rtems_filesystem_location_info_t* pathloc) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_filesystem_node_types_t type; + rtems_rfs_inode_handle inode; + uint16_t mode; + int rc; + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("node_type: opening inode", rc); + } + + /* + * Do not return RTEMS_FILESYSTEM_HARD_LINK because this would result in an + * eval link which does not make sense in the case of the RFS file + * system. All directory entries are links to an inode. A link such as a HARD + * link is actually the normal path to a regular file, directory, device + * etc's inode. Links to inodes can be considered "the real" one, yet they + * are all links. + */ + mode = rtems_rfs_inode_get_mode (&inode); + if (RTEMS_RFS_S_ISDIR (mode)) + type = RTEMS_FILESYSTEM_DIRECTORY; + else if (RTEMS_RFS_S_ISLNK (mode)) + type = RTEMS_FILESYSTEM_SYM_LINK; + else if (RTEMS_RFS_S_ISBLK (mode) || RTEMS_RFS_S_ISCHR (mode)) + type = RTEMS_FILESYSTEM_DEVICE; + else + type = RTEMS_FILESYSTEM_MEMORY_FILE; + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("node_type: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return type; +} + +/** + * This routine is the implementation of the chown() system call for the + * RFS. + * + * @param pathloc + * @param owner + * @param group + * return int + */ + +static int +rtems_rfs_rtems_chown (rtems_filesystem_location_info_t *pathloc, + uid_t owner, + gid_t group) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_rfs_inode_handle inode; +#if defined (RTEMS_POSIX_API) + uid_t uid; +#endif + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_CHOWN)) + printf ("rtems-rfs-rtems: chown: in: ino:%" PRId32 " uid:%d gid:%d\n", + ino, owner, group); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("chown: opening inode", rc); + } + + /* + * Verify I am the owner of the node or the super user. + */ + +#if defined (RTEMS_POSIX_API) + uid = geteuid(); + + if ((uid != rtems_rfs_inode_get_uid (&inode)) && (uid != 0)) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("chown: not able", EPERM); + } +#endif + + rtems_rfs_inode_set_uid_gid (&inode, owner, group); + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("chown: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * This routine is the implementation of the utime() system call for the + * RFS. + * + * @param pathloc + * @param atime + * @param mtime + * return int + */ + +static int +rtems_rfs_rtems_utime(rtems_filesystem_location_info_t* pathloc, + time_t atime, + time_t mtime) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_rfs_inode_handle inode; + int rc; + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("utime: read inode", rc); + } + + rtems_rfs_inode_set_atime (&inode, atime); + rtems_rfs_inode_set_mtime (&inode, mtime); + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("utime: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * The following rouine creates a new symbolic link node under parent with the + * name given in name. The node is set to point to the node at to_loc. + * + * @param parent_loc + * @param link_name + * @param node_name + * return int + */ + +static int +rtems_rfs_rtems_symlink (rtems_filesystem_location_info_t* parent_loc, + const char* link_name, + const char* node_name) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (parent_loc); + rtems_rfs_ino parent = rtems_rfs_rtems_get_pathloc_ino (parent_loc); + uid_t uid; + gid_t gid; + int rc; + +#if defined(RTEMS_POSIX_API) + uid = geteuid (); + gid = getegid (); +#else + uid = 0; + gid = 0; +#endif + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_symlink (fs, node_name, strlen (node_name), + link_name, strlen (link_name), + uid, gid, parent); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("symlink: linking", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * The following rouine puts the symblic links destination name into buf. + * + * @param loc + * @param buf + * @param bufsize + * @return int + */ + +static ssize_t +rtems_rfs_rtems_readlink (rtems_filesystem_location_info_t* pathloc, + char* buf, + size_t bufsize) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + size_t length; + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_READLINK)) + printf ("rtems-rfs-rtems: readlink: in: ino:%" PRId32 "\n", ino); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_symlink_read (fs, ino, buf, bufsize, &length); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("readlink: reading link", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return (int) length; +} + +int +rtems_rfs_rtems_fchmod (rtems_filesystem_location_info_t* pathloc, + mode_t mode) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_rfs_inode_handle inode; + uint16_t imode; +#if defined (RTEMS_POSIX_API) + uid_t uid; +#endif + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_FCHMOD)) + printf ("rtems-rfs-rtems: fchmod: in: ino:%" PRId32 " mode:%06" PRIomode_t "\n", + ino, mode); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("fchmod: opening inode", rc); + } + + imode = rtems_rfs_inode_get_mode (&inode); + + /* + * Verify I am the owner of the node or the super user. + */ +#if defined (RTEMS_POSIX_API) + uid = geteuid(); + + if ((uid != rtems_rfs_inode_get_uid (&inode)) && (uid != 0)) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("fchmod: checking uid", EPERM); + } +#endif + + imode &= ~(S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); + imode |= mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); + + rtems_rfs_inode_set_mode (&inode, imode); + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("fchmod: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +int +rtems_rfs_rtems_fstat (rtems_filesystem_location_info_t* pathloc, + struct stat* buf) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_rfs_inode_handle inode; + rtems_rfs_file_shared* shared; + uint16_t mode; + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_STAT)) + printf ("rtems-rfs-rtems: stat: in: ino:%" PRId32 "\n", ino); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("stat: opening inode", rc); + } + + mode = rtems_rfs_inode_get_mode (&inode); + + if (RTEMS_RFS_S_ISCHR (mode) || RTEMS_RFS_S_ISBLK (mode)) + { + buf->st_rdev = + rtems_filesystem_make_dev_t (rtems_rfs_inode_get_block (&inode, 0), + rtems_rfs_inode_get_block (&inode, 1)); + } + + buf->st_dev = rtems_rfs_fs_device (fs); + buf->st_ino = rtems_rfs_inode_ino (&inode); + buf->st_mode = rtems_rfs_rtems_mode (mode); + buf->st_nlink = rtems_rfs_inode_get_links (&inode); + buf->st_uid = rtems_rfs_inode_get_uid (&inode); + buf->st_gid = rtems_rfs_inode_get_gid (&inode); + + /* + * Need to check is the ino is an open file. If so we take the values from + * the open file rather than the inode. + */ + shared = rtems_rfs_file_get_shared (fs, rtems_rfs_inode_ino (&inode)); + + if (shared) + { + buf->st_atime = rtems_rfs_file_shared_get_atime (shared); + buf->st_mtime = rtems_rfs_file_shared_get_mtime (shared); + buf->st_ctime = rtems_rfs_file_shared_get_ctime (shared); + buf->st_blocks = rtems_rfs_file_shared_get_block_count (shared); + + if (S_ISLNK (buf->st_mode)) + buf->st_size = rtems_rfs_file_shared_get_block_offset (shared); + else + buf->st_size = rtems_rfs_file_shared_get_size (fs, shared); + } + else + { + buf->st_atime = rtems_rfs_inode_get_atime (&inode); + buf->st_mtime = rtems_rfs_inode_get_mtime (&inode); + buf->st_ctime = rtems_rfs_inode_get_ctime (&inode); + buf->st_blocks = rtems_rfs_inode_get_block_count (&inode); + + if (S_ISLNK (buf->st_mode)) + buf->st_size = rtems_rfs_inode_get_block_offset (&inode); + else + buf->st_size = rtems_rfs_inode_get_size (fs, &inode); + } + + buf->st_blksize = rtems_rfs_fs_block_size (fs); + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("stat: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + return 0; +} + +/** + * Routine to create a node in the RFS file system. + * + * @param name + * @param mode + * @param dev + * @param pathloc + * @return int + */ + +static int +rtems_rfs_rtems_mknod (const char *name, + mode_t mode, + dev_t dev, + rtems_filesystem_location_info_t *pathloc) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino parent = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_rfs_ino ino; + rtems_rfs_inode_handle inode; + uid_t uid; + gid_t gid; + int rc; + +#if defined(RTEMS_POSIX_API) + uid = geteuid (); + gid = getegid (); +#else + uid = 0; + gid = 0; +#endif + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_create (fs, parent, name, strlen (name), + rtems_rfs_rtems_imode (mode), + 1, uid, gid, &ino); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("mknod: inode create", rc); + } + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("mknod: inode open", rc); + } + + if (S_ISDIR(mode) || S_ISREG(mode)) + { + } + else if (S_ISCHR (mode) || S_ISBLK (mode)) + { + int major; + int minor; + rtems_filesystem_split_dev_t (dev, major, minor); + rtems_rfs_inode_set_block (&inode, 0, major); + rtems_rfs_inode_set_block (&inode, 1, minor); + } + else + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("mknod: bad mode", EINVAL); + } + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("mknod: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + return 0; +} + +/** + * Routine to remove a node from the RFS file system. + * + * @param parent_pathloc + * @param pathloc + * @return int + */ +int +rtems_rfs_rtems_rmnod (rtems_filesystem_location_info_t* parent_pathloc, + rtems_filesystem_location_info_t* pathloc) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino parent = rtems_rfs_rtems_get_pathloc_ino (parent_pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + uint32_t doff = rtems_rfs_rtems_get_pathloc_doff (pathloc); + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_RMNOD)) + printf ("rtems-rfs: rmnod: parent:%" PRId32 " doff:%" PRIu32 ", ino:%" PRId32 "\n", + parent, doff, ino); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_unlink (fs, parent, ino, doff, rtems_rfs_unlink_dir_denied); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("rmnod: unlinking", rc); + } + + rtems_rfs_rtems_unlock (fs); + return 0; +} + +/** + * The following routine does a sync on an inode node. Currently it flushes + * everything related to this device. + * + * @param iop + * @return int + */ +int +rtems_rfs_rtems_fdatasync (rtems_libio_t* iop) +{ + int rc; + + rc = rtems_rfs_buffer_sync (rtems_rfs_rtems_pathloc_dev (&iop->pathinfo)); + if (rc) + return rtems_rfs_rtems_error ("fdatasync: sync", rc); + + return 0; +} + +/** + * Rename the node. + * + * @param old_parent_loc The old name's parent location. + * @param old_loc The old name's location. + * @param new_parent_loc The new name's parent location. + * @param new_name The new name. + * @return int + */ +static int +rtems_rfs_rtems_rename(rtems_filesystem_location_info_t* old_parent_loc, + rtems_filesystem_location_info_t* old_loc, + rtems_filesystem_location_info_t* new_parent_loc, + const char* new_name) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (old_loc); + rtems_rfs_ino old_parent; + rtems_rfs_ino new_parent; + rtems_rfs_ino ino; + uint32_t doff; + int rc; + + old_parent = rtems_rfs_rtems_get_pathloc_ino (old_parent_loc); + new_parent = rtems_rfs_rtems_get_pathloc_ino (new_parent_loc); + + ino = rtems_rfs_rtems_get_pathloc_ino (old_loc); + doff = rtems_rfs_rtems_get_pathloc_doff (old_loc); + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_RENAME)) + printf ("rtems-rfs: rename: ino:%" PRId32 " doff:%" PRIu32 ", new parent:%" PRId32 " new name:%s\n", + ino, doff, new_parent, new_name); + + rtems_rfs_rtems_lock (fs); + + /* + * Link to the inode before unlinking so the inode is not erased when + * unlinked. + */ + rc = rtems_rfs_link (fs, new_name, strlen (new_name), new_parent, ino, true); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("rename: linking", rc); + } + + /* + * Unlink all inodes even directories with the dir option as false because a + * directory may not be empty. + */ + rc = rtems_rfs_unlink (fs, old_parent, ino, doff, + rtems_rfs_unlink_dir_allowed); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("rename: unlinking", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * Return the file system stat data. + * + * @param pathloc + * @param sb + * @return int + */ +static int +rtems_rfs_rtems_statvfs (rtems_filesystem_location_info_t* pathloc, + struct statvfs* sb) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + size_t blocks; + size_t inodes; + + rtems_rfs_group_usage (fs, &blocks, &inodes); + + sb->f_bsize = rtems_rfs_fs_block_size (fs); + sb->f_frsize = rtems_rfs_fs_media_block_size (fs); + sb->f_blocks = rtems_rfs_fs_media_blocks (fs); + sb->f_bfree = rtems_rfs_fs_blocks (fs) - blocks; + sb->f_bavail = sb->f_bfree; + sb->f_files = rtems_rfs_fs_inodes (fs); + sb->f_ffree = rtems_rfs_fs_inodes (fs) - inodes; + sb->f_favail = sb->f_ffree; + sb->f_fsid = RTEMS_RFS_SB_MAGIC; + sb->f_flag = rtems_rfs_fs_flags (fs); + sb->f_namemax = rtems_rfs_fs_max_name (fs); + + return 0; +} + +/** + * Handler table for RFS link nodes + */ +const rtems_filesystem_file_handlers_r rtems_rfs_rtems_link_handlers = +{ + .open_h = rtems_filesystem_default_open, + .close_h = rtems_filesystem_default_close, + .read_h = rtems_filesystem_default_read, + .write_h = rtems_filesystem_default_write, + .ioctl_h = rtems_filesystem_default_ioctl, + .lseek_h = rtems_filesystem_default_lseek, + .fstat_h = rtems_rfs_rtems_fstat, + .fchmod_h = rtems_filesystem_default_fchmod, + .ftruncate_h = rtems_filesystem_default_ftruncate, + .fpathconf_h = rtems_filesystem_default_fpathconf, + .fsync_h = rtems_filesystem_default_fsync, + .fdatasync_h = rtems_filesystem_default_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .rmnod_h = rtems_rfs_rtems_rmnod +}; + +/** + * Forward decl for the ops table. + */ + +int rtems_rfs_rtems_initialise (rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data); +int rtems_rfs_rtems_shutdown (rtems_filesystem_mount_table_entry_t *mt_entry); + +/** + * RFS file system operations table. + */ +const rtems_filesystem_operations_table rtems_rfs_ops = +{ + .evalpath_h = rtems_rfs_rtems_eval_path, + .evalformake_h = rtems_rfs_rtems_eval_for_make, + .link_h = rtems_rfs_rtems_link, + .unlink_h = rtems_rfs_rtems_unlink, + .node_type_h = rtems_rfs_rtems_node_type, + .mknod_h = rtems_rfs_rtems_mknod, + .chown_h = rtems_rfs_rtems_chown, + .freenod_h = rtems_filesystem_default_freenode, + .mount_h = rtems_filesystem_default_mount, + .fsmount_me_h = rtems_rfs_rtems_initialise, + .unmount_h = rtems_filesystem_default_unmount, + .fsunmount_me_h = rtems_rfs_rtems_shutdown, + .utime_h = rtems_rfs_rtems_utime, + .eval_link_h = rtems_filesystem_default_evaluate_link, /* never called cause we lie in the node type */ + .symlink_h = rtems_rfs_rtems_symlink, + .readlink_h = rtems_rfs_rtems_readlink, + .rename_h = rtems_rfs_rtems_rename, + .statvfs_h = rtems_rfs_rtems_statvfs +}; + +/** + * Open the file system. + */ + +int +rtems_rfs_rtems_initialise (rtems_filesystem_mount_table_entry_t* mt_entry, + const void* data) +{ + rtems_rfs_rtems_private* rtems; + rtems_rfs_file_system* fs; + int rc; + + rtems = malloc (sizeof (rtems_rfs_rtems_private)); + if (!rtems) + return rtems_rfs_rtems_error ("initialise: local data", ENOMEM); + + memset (rtems, 0, sizeof (rtems_rfs_rtems_private)); + + rc = rtems_rfs_mutex_create (&rtems->access); + if (rc > 0) + { + free (rtems); + return rtems_rfs_rtems_error ("initialise: cannot create mutex", rc); + } + + rc = rtems_rfs_mutex_lock (&rtems->access); + if (rc > 0) + { + rtems_rfs_mutex_destroy (&rtems->access); + free (rtems); + return rtems_rfs_rtems_error ("initialise: cannot lock access mutex", rc); + } + + rc = rtems_rfs_fs_open (mt_entry->dev, rtems, 0, &fs); + if (rc) + { + free (rtems); + return rtems_rfs_rtems_error ("initialise: open", rc); + } + + mt_entry->fs_info = fs; + + mt_entry->mt_fs_root.node_access = (void*) RTEMS_RFS_ROOT_INO; + mt_entry->mt_fs_root.handlers = &rtems_rfs_rtems_dir_handlers; + mt_entry->mt_fs_root.ops = &rtems_rfs_ops; + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * Shutdown the file system. + */ +int +rtems_rfs_rtems_shutdown (rtems_filesystem_mount_table_entry_t* mt_entry) +{ + rtems_rfs_file_system* fs = mt_entry->fs_info; + rtems_rfs_rtems_private* rtems; + int rc; + + rtems = rtems_rfs_fs_user (fs); + + rc = rtems_rfs_fs_close(fs); + + rtems_rfs_mutex_destroy (&rtems->access); + free (rtems); + + return rtems_rfs_rtems_error ("shutdown: close", rc); +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-rtems.h b/cpukit/libfs/src/rfs/rtems-rfs-rtems.h new file mode 100644 index 0000000000..19f51da31c --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems.h @@ -0,0 +1,329 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System RTEMS Header file. + * + * This file is not to be installed. It binds the RFS file system to RTEMS. + */ + +#if !defined(RTEMS_RFS_RTEMS_DEFINED) +#define RTEMS_RFS_RTEMS_DEFINED + +#include <stdbool.h> +#include <stdint.h> +#include <errno.h> + +/** + * RTEMS RFS RTEMS Error Enable. Set to 1 to printing of errors. Default is off. + */ +#define RTEMS_RFS_RTEMS_ERROR 0 + +/** + * RTEMS RFS RTEMS Trace Enable. Set to 1 to printing of errors. Default is off. + */ +#define RTEMS_RFS_RTEMS_TRACE 0 + +/** + * If we are not handling errors provide a define that removes the strings from + * the code. + */ +#if !RTEMS_RFS_RTEMS_ERROR +#define rtems_rfs_rtems_error(_m, _e) \ + (((errno = (_e)) == 0) ? 0 : -1) +#else +/** + * Take the result code and set errno with it and if non-zero return -1 else + * return 0. + * + * @param what The message to print is the error is not zero. + * @param error The error code. + * @retval -1 An error has occured. + * @retval 0 No error. + */ +int rtems_rfs_rtems_error (const char* mesg, int error); +#endif + +/** + * Trace message defines the RTEMS bindings of the RTEMS RFS. This is a + * development tool where can edit the values below to control the various trace + * output. + */ +#define RTEMS_RFS_RTEMS_DEBUG_ALL (0xffffffff) +#define RTEMS_RFS_RTEMS_DEBUG_ERROR_MSGS (1 << 0) +#define RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH (1 << 1) +#define RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE (1 << 2) +#define RTEMS_RFS_RTEMS_DEBUG_EVAL_PERMS (1 << 3) +#define RTEMS_RFS_RTEMS_DEBUG_MKNOD (1 << 4) +#define RTEMS_RFS_RTEMS_DEBUG_RMNOD (1 << 5) +#define RTEMS_RFS_RTEMS_DEBUG_LINK (1 << 6) +#define RTEMS_RFS_RTEMS_DEBUG_UNLINK (1 << 7) +#define RTEMS_RFS_RTEMS_DEBUG_CHOWN (1 << 8) +#define RTEMS_RFS_RTEMS_DEBUG_READLINK (1 << 9) +#define RTEMS_RFS_RTEMS_DEBUG_FCHMOD (1 << 10) +#define RTEMS_RFS_RTEMS_DEBUG_STAT (1 << 11) +#define RTEMS_RFS_RTEMS_DEBUG_DIR_RMNOD (1 << 12) +#define RTEMS_RFS_RTEMS_DEBUG_FILE_OPEN (1 << 13) +#define RTEMS_RFS_RTEMS_DEBUG_FILE_CLOSE (1 << 14) +#define RTEMS_RFS_RTEMS_DEBUG_FILE_READ (1 << 15) +#define RTEMS_RFS_RTEMS_DEBUG_FILE_WRITE (1 << 16) +#define RTEMS_RFS_RTEMS_DEBUG_FILE_LSEEK (1 << 17) +#define RTEMS_RFS_RTEMS_DEBUG_FILE_FTRUNC (1 << 18) + +/** + * Call to check if this part is bring traced. If RTEMS_RFS_RTEMS_TRACE is + * defined to 0 the code is dead code elminiated when built with -Os, -O2, or + * higher. + * + * @param mask The part of the API to trace. + * @retval true Tracing is active for the mask. + * @retval false Do not trace. + */ +#if RTEMS_RFS_RTEMS_TRACE +bool rtems_rfs_rtems_trace (uint32_t mask); +#else +#define rtems_rfs_rtems_trace(_m) (0) +#endif + +/** + * Set the mask. + * + * @param mask The mask bits to set. + * @return The previous mask. + */ +#if RTEMS_RFS_RTEMS_TRACE +void rtems_rfs_rtems_trace_set_mask (uint32_t mask); +#else +#define rtems_rfs_rtems_trace_set_mask(_m) +#endif + +/** + * Clear the mask. + * + * @param mask The mask bits to clear. + * @return The previous mask. + */ +#if RTEMS_RFS_RTEMS_TRACE +void rtems_rfs_rtems_trace_clear_mask (uint32_t mask); +#else +#define rtems_rfs_rtems_trace_clear_mask(_m) +#endif + +/** + * Add shell trace shell command. + */ +#if RTEMS_RFS_RTEMS_TRACE +int rtems_rfs_rtems_trace_shell_command (int argc, char *argv[]); +#endif + +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> +#include <rtems/rfs/rtems-rfs-mutex.h> +#include <rtems/libio_.h> +#include <rtems/fs.h> + +/** + * Private RFS RTEMS Port data. + */ +typedef struct rtems_rfs_rtems_private +{ + /** + * The access lock. + */ + rtems_rfs_mutex access; +} rtems_rfs_rtems_private; +/** + * Return the file system structure given a path location. + * + * @param _loc Pointer to the path location. + * @return rtems_rfs_file_system* + */ +#define rtems_rfs_rtems_pathloc_dev(_loc) \ + ((rtems_rfs_file_system*)((_loc)->mt_entry->fs_info)) + +/** + * Set the inode number (ino) into the path location. + * + * @param _loc Pointer to the path location. + * @param _ino The ino to set in the path location. + */ +#define rtems_rfs_rtems_set_pathloc_ino(_loc, _ino) \ + (_loc)->node_access = (void*)((intptr_t)(_ino)) + +/** + * Get the inode number (ino) given a path location. + * + * @param _loc Pointer to the path location. + * @return rtems_rfs_ino The inode number in the path location. + */ +#define rtems_rfs_rtems_get_pathloc_ino(_loc) \ + ((rtems_rfs_ino) (intptr_t)((_loc)->node_access)) + +/** + * Set the directory offset (doff) into the path location. + * + * @param _loc Pointer to the path location. + * @param _doff The doff to set in the path location. + */ +#define rtems_rfs_rtems_set_pathloc_doff(_loc, _doff) \ + (_loc)->node_access_2 = (void*)((intptr_t)(_doff)) + +/** + * Get the directory offset (doff) given a path location. + * + * @param _loc Pointer to the path location. + * @return uin32_t The doff in the path location. + */ +#define rtems_rfs_rtems_get_pathloc_doff(_loc) \ + ((uint32_t) (intptr_t)((_loc)->node_access_2)) + +/** + * Get the ino from the I/O pointer. + * + * @param _iop The I/O pointer. + * @return ino + */ +#define rtems_rfs_rtems_get_iop_ino(_iop) \ + ((intptr_t)(_iop)->pathinfo.node_access) + +/** + * Get the file handle from the I/O pointer. + * + * @param _iop The I/O pointer. + * @return filehandle The file handle + */ +#define rtems_rfs_rtems_get_iop_file_handle(_iop) \ + ((rtems_rfs_file_handle*)(_iop)->pathinfo.node_access_2) + +/** + * Set the file handle in the I/O pointer. + * + * @param _iop The I/O pointer. + * @param _fh The file handle. + */ +#define rtems_rfs_rtems_set_iop_file_handle(_iop, _fh) \ + (_iop)->pathinfo.node_access_2 = (_fh) + +/** + * Create the name of the handler's table given the type of handlers. + * + * @param _h The name of the handlers. + * @return label The name of the handler's table. + */ +#define rtems_rfs_rtems_handlers(_h) \ + &rtems_rfs_rtems_ ## _h ## _handlers + +/** + * Evaluate the permissions of the inode's mode against the flags. + * + * @param inode The inode handler to check the mode, uid and gid. + * @param flags The flags to check permissions of. + * @retval true The permissions allow access to the inode. + * @retval false Access to the inode is not permitted. + */ +bool rtems_rfs_rtems_eval_perms (rtems_rfs_inode_handle* inode, int flags); + +/** + * Set the handlers in the path location based on the mode of the inode. + * + * @param loc Pointer to the path location to set the handlers in. + * @param inode The inode handle to check the mode of for the type of handlers. + * @retval true The handlers have been set. + * @retval false There are no handlers for the mode. + */ +bool rtems_rfs_rtems_set_handlers (rtems_filesystem_location_info_t* pathloc, + rtems_rfs_inode_handle* inode); + +/** + * Convert the system mode flags to inode mode flags. + * + * @param mode The system mode flags. + * @return uint16_t The inode mode flags. + */ +uint16_t rtems_rfs_rtems_imode (mode_t mode); + +/** + * Convert the inode mode flags to system mode flags. + * + * @param imode The inode mode flags + * @return mode_t The system mode flags. + */ +mode_t rtems_rfs_rtems_mode (int imode); + +/** + * Lock the RFS file system. + */ +static inline void + rtems_rfs_rtems_lock (rtems_rfs_file_system* fs) +{ + rtems_rfs_rtems_private* rtems = rtems_rfs_fs_user (fs); + rtems_rfs_mutex_lock (&rtems->access); +} + +/** + * Unlock the RFS file system. + */ +static inline void + rtems_rfs_rtems_unlock (rtems_rfs_file_system* fs) +{ + rtems_rfs_rtems_private* rtems = rtems_rfs_fs_user (fs); + rtems_rfs_buffers_release (fs); + rtems_rfs_mutex_unlock (&rtems->access); +} + +/** + * The handlers. + */ +extern const rtems_filesystem_file_handlers_r rtems_rfs_rtems_dir_handlers; +extern const rtems_filesystem_file_handlers_r rtems_rfs_rtems_device_handlers; +extern const rtems_filesystem_file_handlers_r rtems_rfs_rtems_link_handlers; +extern const rtems_filesystem_file_handlers_r rtems_rfs_rtems_file_handlers; + +/** + * The following routine does a stat on a node. + * + * @param iop + * @param buf + * @return int + */ +int rtems_rfs_rtems_fstat (rtems_filesystem_location_info_t* pathloc, + struct stat* buf); + +/** + * File change mode routine. + * + * @param iop + * @param mode + * @return int + */ +int rtems_rfs_rtems_fchmod (rtems_filesystem_location_info_t* pathloc, + mode_t mode); + +/** + * Routine to remove a node from the RFS file system. + * + * @param parent_pathloc + * @param pathloc + */ +int rtems_rfs_rtems_rmnod (rtems_filesystem_location_info_t* parent_pathloc, + rtems_filesystem_location_info_t* pathloc); + +/** + * The following routine does a sync on an inode node. Currently it flushes + * everything related to this device. + * + * @param iop + */ +int rtems_rfs_rtems_fdatasync (rtems_libio_t* iop); + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-shell.c b/cpukit/libfs/src/rfs/rtems-rfs-shell.c new file mode 100644 index 0000000000..63931752cf --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-shell.c @@ -0,0 +1,753 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Shell Commands Support + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> +#include <string.h> + +#include <rtems/rfs/rtems-rfs-block.h> +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-group.h> +#include <rtems/rfs/rtems-rfs-inode.h> +#include <rtems/rfs/rtems-rfs-dir.h> +#include <rtems/rtems-rfs-format.h> + +#include <sys/statvfs.h> + +#if __rtems__ +#include "rtems-rfs-rtems.h" +#endif + +/** + * The type of the shell handlers we have. + */ +typedef int (*rtems_rfs_shell_handler) (rtems_rfs_file_system* fs, int argc, char *argv[]); + +/** + * Table of handlers we parse to invoke the command. + */ +typedef struct +{ + const char* name; + rtems_rfs_shell_handler handler; + const char* help; +} rtems_rfs_shell_cmd; + +/** + * Lock the file system. + */ +static void +rtems_rfs_shell_lock_rfs (rtems_rfs_file_system* fs) +{ +#if __rtems__ + rtems_rfs_rtems_lock (fs); +#endif +} + +/** + * Unlock the file system. + */ +static void +rtems_rfs_shell_unlock_rfs (rtems_rfs_file_system* fs) +{ +#if __rtems__ + rtems_rfs_rtems_unlock (fs); +#endif +} + +/** + * Get the file system data from the specific path. Checks to make sure the path is + * pointing to a valid RFS file system. + */ +static int +rtems_rfs_get_fs (const char* path, rtems_rfs_file_system** fs) +{ + struct statvfs sb; + int rc; + + rc = statvfs (path, &sb); + if (rc < 0) + { + printf ("error: cannot statvfs path: %s: (%d) %s\n", + path, errno, strerror (errno)); + return -1; + } + + if (sb.f_fsid != RTEMS_RFS_SB_MAGIC) + { + printf ("error: path '%s' is not on an RFS file system\n", path); + return -1; + } + +#if __rtems__ + /* + * Now find the path location on the file system. This will give the file + * system data. + */ + { + rtems_filesystem_location_info_t pathloc; + rc = rtems_filesystem_evaluate_path (path, strlen (path), 0, &pathloc, true); + *fs = rtems_rfs_rtems_pathloc_dev (&pathloc); + rtems_filesystem_freenode (&pathloc); + } +#endif + + return rc; +} + +static int +rtems_rfs_shell_data (rtems_rfs_file_system* fs, int argc, char *argv[]) +{ + size_t blocks; + size_t inodes; + int bpcent; + int ipcent; + + printf ("RFS Filesystem Data\n"); + printf (" flags: %08" PRIx32 "\n", fs->flags); +#if 0 + printf (" device: %08lx\n", rtems_rfs_fs_device (fs)); +#endif + printf (" blocks: %zu\n", rtems_rfs_fs_blocks (fs)); + printf (" block size: %zu\n", rtems_rfs_fs_block_size (fs)); + printf (" size: %" PRIu64 "\n", rtems_rfs_fs_size (fs)); + printf (" media block size: %" PRIu32 "\n", rtems_rfs_fs_media_block_size (fs)); + printf (" media size: %" PRIu64 "\n", rtems_rfs_fs_media_size (fs)); + printf (" inodes: %" PRIu32 "\n", rtems_rfs_fs_inodes (fs)); + printf (" bad blocks: %" PRIu32 "\n", fs->bad_blocks); + printf (" max. name length: %" PRIu32 "\n", rtems_rfs_fs_max_name (fs)); + printf (" groups: %d\n", fs->group_count); + printf (" group blocks: %zd\n", fs->group_blocks); + printf (" group inodes: %zd\n", fs->group_inodes); + printf (" inodes per block: %zd\n", fs->inodes_per_block); + printf (" blocks per block: %zd\n", fs->blocks_per_block); + printf (" singly blocks: %zd\n", fs->block_map_singly_blocks); + printf (" doublly blocks: %zd\n", fs->block_map_doubly_blocks); + printf (" max. held buffers: %" PRId32 "\n", fs->max_held_buffers); + + rtems_rfs_shell_lock_rfs (fs); + + rtems_rfs_group_usage (fs, &blocks, &inodes); + + rtems_rfs_shell_unlock_rfs (fs); + + bpcent = (blocks * 1000) / rtems_rfs_fs_blocks (fs); + ipcent = (inodes * 1000) / rtems_rfs_fs_inodes (fs); + + printf (" blocks used: %zd (%d.%d%%)\n", + blocks, bpcent / 10, bpcent % 10); + printf (" inodes used: %zd (%d.%d%%)\n", + inodes, ipcent / 10, ipcent % 10); + + return 0; +} + +static int +rtems_rfs_shell_block (rtems_rfs_file_system* fs, int argc, char *argv[]) +{ + rtems_rfs_buffer_handle buffer; + rtems_rfs_block_no block; + uint8_t* data; + bool state; + int b; + int rc; + + if (argc <= 1) + { + printf ("error: no block number provided\n"); + return 1; + } + + block = strtoul (argv[1], 0, 0); + + rtems_rfs_shell_lock_rfs (fs); + + rc = rtems_rfs_group_bitmap_test (fs, false, block, &state); + if (rc > 0) + { + rtems_rfs_shell_unlock_rfs (fs); + printf ("error: testing block state: block=%" PRIu32 ": (%d) %s\n", + block, rc, strerror (rc)); + return 1; + } + + printf (" %5" PRIu32 ": block %s\n", block, state ? "allocated" : "free"); + + rc = rtems_rfs_buffer_handle_open (fs, &buffer); + if (rc > 0) + { + rtems_rfs_shell_unlock_rfs (fs); + printf ("error: opening buffer handle: block=%" PRIu32 ": (%d) %s\n", + block, rc, strerror (rc)); + return 1; + } + + rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, true); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_shell_unlock_rfs (fs); + printf ("error: requesting buffer handle: block=%" PRIu32 ": (%d) %s\n", + block, rc, strerror (rc)); + return 1; + } + + for (b = 0, data = rtems_rfs_buffer_data (&buffer); + b < rtems_rfs_fs_block_size (fs); + b++, data++) + { + int mod = b % 16; + if (mod == 0) + { + if (b) + printf ("\n"); + printf ("%04x ", b); + } + if (mod == 8) + printf (" "); + printf ("%02x ", *data); + } + + printf ("\n"); + + rc = rtems_rfs_buffer_handle_close (fs, &buffer); + if (rc > 0) + { + rtems_rfs_shell_unlock_rfs (fs); + printf ("error: closing buffer handle: block=%" PRIu32 ": (%d) %s\n", + block, rc, strerror (rc)); + return 1; + } + + rtems_rfs_shell_unlock_rfs (fs); + + return 0; +} + +static int +rtems_rfs_shell_inode (rtems_rfs_file_system* fs, int argc, char *argv[]) +{ + rtems_rfs_ino start; + rtems_rfs_ino end; + rtems_rfs_ino total; + rtems_rfs_ino ino; + bool show_all; + bool error_check_only; + bool forced; + bool have_start; + bool have_end; + int arg; + int b; + int rc; + + total = fs->group_inodes * fs->group_count; + start = RTEMS_RFS_ROOT_INO; + end = total - 1; + show_all = false; + error_check_only = false; + forced = false; + have_start = have_end = false; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + switch (argv[arg][1]) + { + case 'a': + show_all = true; + break; + case 'e': + error_check_only = true; + break; + case 'f': + forced = true; + break; + default: + printf ("warning: option ignored: %s\n", argv[arg]); + break; + } + } + else + { + if (have_end && have_start) + printf ("warning: option ignored: %s\n", argv[arg]); + else if (!have_start) + { + start = end = strtoul (argv[arg], 0, 0); + have_start = true; + } + else + { + end = strtoul (argv[arg], 0, 0); + have_end = true; + } + } + } + + if ((start >= total) || (end >= total)) + { + printf ("error: inode out of range (0->%" PRId32 ").\n", total - 1); + return 1; + } + + rtems_rfs_shell_lock_rfs (fs); + + for (ino = start; ino <= end; ino++) + { + rtems_rfs_inode_handle inode; + bool allocated; + + rc = rtems_rfs_group_bitmap_test (fs, true, ino, &allocated); + if (rc > 0) + { + rtems_rfs_shell_unlock_rfs (fs); + printf ("error: testing inode state: ino=%" PRIu32 ": (%d) %s\n", + ino, rc, strerror (rc)); + return 1; + } + + if (show_all || allocated) + { + uint16_t mode; + bool error; + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + rtems_rfs_shell_unlock_rfs (fs); + printf ("error: opening inode handle: ino=%" PRIu32 ": (%d) %s\n", + ino, rc, strerror (rc)); + return 1; + } + + error = false; + + mode = rtems_rfs_inode_get_mode (&inode); + + if (error_check_only) + { + if (!RTEMS_RFS_S_ISDIR (mode) && + !RTEMS_RFS_S_ISCHR (mode) && + !RTEMS_RFS_S_ISBLK (mode) && + !RTEMS_RFS_S_ISREG (mode) && + !RTEMS_RFS_S_ISLNK (mode)) + error = true; + else + { +#if NEED_TO_HANDLE_DIFFERENT_TYPES + int b; + for (b = 0; b < RTEMS_RFS_INODE_BLOCKS; b++) + { + uint32_t block; + block = rtems_rfs_inode_get_block (&inode, b); + if ((block <= RTEMS_RFS_SUPERBLOCK_SIZE) || + (block >= rtems_rfs_fs_blocks (fs))) + error = true; + } +#endif + } + } + + if (!error_check_only || error) + { + printf (" %5" PRIu32 ": pos=%06" PRIu32 ":%04zx %c ", + ino, rtems_rfs_buffer_bnum (&inode.buffer), + inode.offset * RTEMS_RFS_INODE_SIZE, + allocated ? 'A' : 'F'); + + if (!allocated && !forced) + printf (" --\n"); + else + { + const char* type; + type = "UKN"; + if (RTEMS_RFS_S_ISDIR (mode)) + type = "DIR"; + else if (RTEMS_RFS_S_ISCHR (mode)) + type = "CHR"; + else if (RTEMS_RFS_S_ISBLK (mode)) + type = "BLK"; + else if (RTEMS_RFS_S_ISREG (mode)) + type = "REG"; + else if (RTEMS_RFS_S_ISLNK (mode)) + type = "LNK"; + printf ("links=%03i mode=%04x (%s/%03o) bo=%04u bc=%04" PRIu32 " b=[", + rtems_rfs_inode_get_links (&inode), + mode, type, mode & ((1 << 10) - 1), + rtems_rfs_inode_get_block_offset (&inode), + rtems_rfs_inode_get_block_count (&inode)); + for (b = 0; b < (RTEMS_RFS_INODE_BLOCKS - 1); b++) + printf ("%" PRIu32 " ", rtems_rfs_inode_get_block (&inode, b)); + printf ("%" PRIu32 "]\n", rtems_rfs_inode_get_block (&inode, b)); + } + } + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_shell_unlock_rfs (fs); + printf ("error: closing inode handle: ino=%" PRIu32 ": (%d) %s\n", + ino, rc, strerror (rc)); + return 1; + } + } + } + + rtems_rfs_shell_unlock_rfs (fs); + + return 0; +} + +static int +rtems_rfs_shell_dir (rtems_rfs_file_system* fs, int argc, char *argv[]) +{ + rtems_rfs_buffer_handle buffer; + rtems_rfs_block_no block; + uint8_t* data; + bool state; + int entry; + int b; + int rc; + + if (argc <= 1) + { + printf ("error: no block number provided\n"); + return 1; + } + + block = strtoul (argv[1], 0, 0); + + rtems_rfs_shell_lock_rfs (fs); + + rc = rtems_rfs_group_bitmap_test (fs, false, block, &state); + if (rc > 0) + { + rtems_rfs_shell_unlock_rfs (fs); + printf ("error: testing block state: block=%" PRIu32 ": (%d) %s\n", + block, rc, strerror (rc)); + return 1; + } + + printf (" %5" PRIu32 ": block %s\n", block, state ? "allocated" : "free"); + + rc = rtems_rfs_buffer_handle_open (fs, &buffer); + if (rc > 0) + { + rtems_rfs_shell_unlock_rfs (fs); + printf ("error: opening buffer handle: block=%" PRIu32 ": (%d) %s\n", + block, rc, strerror (rc)); + return 1; + } + + rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, true); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_shell_unlock_rfs (fs); + printf ("error: requesting buffer handle: block=%" PRIu32 ": (%d) %s\n", + block, rc, strerror (rc)); + return 1; + } + + b = 0; + entry = 1; + data = rtems_rfs_buffer_data (&buffer); + + while (b < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE - 1)) + { + rtems_rfs_ino eino; + int elength; + int length; + int c; + + eino = rtems_rfs_dir_entry_ino (data); + elength = rtems_rfs_dir_entry_length (data); + + if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY) + break; + + if ((elength < RTEMS_RFS_DIR_ENTRY_SIZE) || + (elength >= rtems_rfs_fs_max_name (fs))) + { + printf (" %5d: entry length appears corrupt: %d\n", entry, elength); + break; + } + + if ((eino < RTEMS_RFS_ROOT_INO) || (eino >= rtems_rfs_fs_inodes (fs))) + { + printf (" %5d: entry ino appears corrupt: ino=%" PRId32 "\n", entry, eino); + break; + } + + length = elength - RTEMS_RFS_DIR_ENTRY_SIZE; + + printf (" %5d: %04x inode=%-6" PRIu32 " hash=%08" PRIx32 " name[%03u]=", + entry, b, + rtems_rfs_dir_entry_ino (data), + rtems_rfs_dir_entry_hash (data), + length); + + if (length > 50) + length = 50; + + for (c = 0; c < length; c++) + printf ("%c", data[RTEMS_RFS_DIR_ENTRY_SIZE + c]); + if (length < elength - RTEMS_RFS_DIR_ENTRY_SIZE) + printf ("..."); + printf ("\n"); + + b += elength; + data += elength; + entry++; + } + + rc = rtems_rfs_buffer_handle_close (fs, &buffer); + if (rc > 0) + { + rtems_rfs_shell_unlock_rfs (fs); + printf ("error: closing buffer handle: block=%" PRIu32 ": (%d) %s\n", + block, rc, strerror (rc)); + return 1; + } + + rtems_rfs_shell_unlock_rfs (fs); + + return 0; +} + +static int +rtems_rfs_shell_group (rtems_rfs_file_system* fs, int argc, char *argv[]) +{ + int start; + int end; + int g; + + start = 0; + end = fs->group_count - 1; + + switch (argc) + { + case 1: + break; + case 2: + start = end = strtoul (argv[1], 0, 0); + break; + case 3: + start = strtoul (argv[1], 0, 0); + end = strtoul (argv[2], 0, 0); + break; + default: + printf ("error: too many arguments.\n"); + return 1; + } + + if ((start < 0) || (end < 0) || + (start >= fs->group_count) || (end >= fs->group_count)) + { + printf ("error: group out of range (0->%d).\n", fs->group_count); + return 1; + } + + rtems_rfs_shell_lock_rfs (fs); + + for (g = start; g <= end; g++) + { + rtems_rfs_group* group = &fs->groups[g]; + size_t blocks; + size_t inodes; + blocks = group->size - rtems_rfs_bitmap_map_free (&group->block_bitmap); + inodes = fs->group_inodes - rtems_rfs_bitmap_map_free (&group->inode_bitmap); + printf (" %4d: base=%-7" PRIu32 " size=%-6zu blocks=%-5zu (%3zu%%) inode=%-5zu (%3zu%%)\n", + g, group->base, group->size, + blocks, (blocks * 100) / group->size, + inodes, (inodes * 100) / fs->group_inodes); + } + + rtems_rfs_shell_unlock_rfs (fs); + + return 0; +} + + +void +rtems_rfs_shell_usage (const char* arg) +{ + printf ("%s: RFS debugger\n", arg); + printf (" %s [-hl] <path> <command>\n", arg); + printf (" where:\n"); + printf (" path: Path to the mounted RFS file system\n"); + printf (" command: A debugger command. See -l for a list plus help.\n"); + printf (" -h: This help\n"); + printf (" -l: The debugger command list.\n"); +} + +int +rtems_shell_debugrfs (int argc, char *argv[]) +{ + const rtems_rfs_shell_cmd table[] = + { + { "block", rtems_rfs_shell_block, + "Display the contents of a block, block <bno>, block <bno>..<bno>" }, + { "data", rtems_rfs_shell_data, + "Display file system data, data" }, + { "dir", rtems_rfs_shell_dir, + "Display a block as a table for directory entrie, dir <bno>" }, + { "group", rtems_rfs_shell_group, + "Display the group data of a file system, group, group <group>, group <start> <end>" }, + { "inode", rtems_rfs_shell_inode, + "Display an inode, inode <ino>, inode> <ino>..<ino>" } + }; + + int arg; + int t; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] != '-') + break; + + switch (argv[arg][1]) + { + case 'h': + rtems_rfs_shell_usage (argv[0]); + return 0; + case 'l': + printf ("%s: commands are:\n", argv[0]); + for (t = 0; t < (sizeof (table) / sizeof (const rtems_rfs_shell_cmd)); t++) + printf (" %s\t\t%s\n", table[t].name, table[t].help); + return 0; + default: + printf ("error: unknown option: %s\n", argv[arg]); + return 1; + } + } + + if ((argc - arg) < 2) + printf ("error: you need at least a path and command, try %s -h\n", argv[0]); + else + { + rtems_rfs_file_system* fs; + if (rtems_rfs_get_fs (argv[arg], &fs) == 0) + { + for (t = 0; t < (sizeof (table) / sizeof (const rtems_rfs_shell_cmd)); t++) + if (strcmp (argv[arg + 1], table[t].name) == 0) + return table[t].handler (fs, argc - 2, argv + 2); + printf ("error: command not found: %s\n", argv[arg + 1]); + } + } + + return 1; +} + +int +rtems_shell_rfs_format (int argc, char* argv[]) +{ + rtems_rfs_format_config config; + const char* driver = NULL; + int arg; + + memset (&config, 0, sizeof (rtems_rfs_format_config)); + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + switch (argv[arg][1]) + { + case 'v': + config.verbose = true; + break; + + case 's': + arg++; + if (arg >= argc) + { + printf ("error: block size needs an argument\n"); + return 1; + } + config.block_size = strtoul (argv[arg], 0, 0); + break; + + case 'b': + arg++; + if (arg >= argc) + { + printf ("error: group block count needs an argument\n"); + return 1; + } + config.group_blocks = strtoul (argv[arg], 0, 0); + break; + + case 'i': + arg++; + if (arg >= argc) + { + printf ("error: group inode count needs an argument\n"); + return 1; + } + config.group_inodes = strtoul (argv[arg], 0, 0); + break; + + case 'I': + config.initialise_inodes = true; + break; + + case 'o': + arg++; + if (arg >= argc) + { + printf ("error: inode percentage overhead needs an argument\n"); + return 1; + } + config.inode_overhead = strtoul (argv[arg], 0, 0); + break; + + default: + printf ("error: invalid option: %s\n", argv[arg]); + return 1; + } + } + else + { + if (!driver) + driver = argv[arg]; + else + { + printf ("error: only one driver name allowed: %s\n", argv[arg]); + return 1; + } + } + } + + if (!driver) { + printf ("error: no driver name provided\n"); + return 1; + } + + if (rtems_rfs_format (driver, &config) < 0) + { + printf ("error: format of %s failed: %s\n", + driver, strerror (errno)); + return 1; + } + + return 0; +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-shell.h b/cpukit/libfs/src/rfs/rtems-rfs-shell.h new file mode 100644 index 0000000000..c07b7c3167 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-shell.h @@ -0,0 +1,44 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Shell commands provide a CLI interface to support and + * development od the RFS file system. + */ + +#if !defined (_RTEMS_RFS_SHELL_H_) +#define _RTEMS_RFS_SHELL_H_ + +#include <stddef.h> +#include <stdbool.h> +#include <stdint.h> + +/** + * The shell command for the RFS debugger. + * + * @param argc The argument count. + * @param argv The argument variables. + * @return int The exit code for the command. A 0 is no error. + */ +int rtems_shell_debugrfs (int argc, char *argv[]); + +/** + * The shell command for formatting an RFS file system. + * + * @param argc The argument count. + * @param argv The argument variables. + * @return int The exit code for the command. A 0 is no error. + */ +int rtems_shell_rfs_format (int argc, char* argv[]); + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-trace.c b/cpukit/libfs/src/rfs/rtems-rfs-trace.c new file mode 100644 index 0000000000..d96a63673b --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-trace.c @@ -0,0 +1,159 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Trace Support + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <rtems/rfs/rtems-rfs-trace.h> + +#if RTEMS_RFS_TRACE +static rtems_rfs_trace_mask rtems_rfs_trace_flags; + +bool +rtems_rfs_trace (rtems_rfs_trace_mask mask) +{ + bool result = false; + if (mask & rtems_rfs_trace_flags) + result = true; + return result; +} + +rtems_rfs_trace_mask +rtems_rfs_trace_set_mask (rtems_rfs_trace_mask mask) +{ + rtems_rfs_trace_mask state = rtems_rfs_trace_flags; + rtems_rfs_trace_flags |= mask; + return state; +} + +rtems_rfs_trace_mask +rtems_rfs_trace_clear_mask (rtems_rfs_trace_mask mask) +{ + rtems_rfs_trace_mask state = rtems_rfs_trace_flags; + rtems_rfs_trace_flags &= ~mask; + return state; +} + +int +rtems_rfs_trace_shell_command (int argc, char *argv[]) +{ + const char* table[] = + { + "open", + "close", + "mutex", + "buffer-open", + "buffer-close", + "buffer-sync", + "buffer-release", + "buffer-chains", + "buffer-handle-request", + "buffer-handle-release", + "buffer-setblksize", + "buffers-release", + "block-find", + "block-map-grow", + "block-map-shrink", + "group-open", + "group-close", + "group-bitmaps", + "inode-open", + "inode-close", + "inode-load", + "inode-unload", + "inode-create", + "inode-delete", + "link", + "unlink", + "dir-lookup-ino", + "dir-lookup-ino-check", + "dir-lookup-ino-found", + "dir-add-entry", + "dir-del-entry", + "dir-read", + "dir-empty", + "symlink", + "symlink-read", + "file-open", + "file-close", + "file-io" + }; + + rtems_rfs_trace_mask set_value = 0; + rtems_rfs_trace_mask clear_value = 0; + bool set = true; + int arg; + int t; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + switch (argv[arg][1]) + { + case 'h': + printf ("usage: %s [-hl] [set/clear] [flags]\n", argv[0]); + return 0; + case 'l': + printf ("%s: valid flags to set or clear are:\n", argv[0]); + for (t = 0; t < (sizeof (table) / sizeof (const char*)); t++) + printf (" %s\n", table[t]); + return 0; + default: + printf ("error: unknown option\n"); + return 1; + } + } + else + { + if (strcmp (argv[arg], "set") == 0) + set = true; + if (strcmp (argv[arg], "clear") == 0) + set = false; + else if (strcmp (argv[arg], "all") == 0) + { + if (set) + set_value = RTEMS_RFS_TRACE_ALL; + else + clear_value = RTEMS_RFS_TRACE_ALL; + } + else + { + for (t = 0; t < (sizeof (table) / sizeof (const char*)); t++) + { + if (strcmp (argv[arg], table[t]) == 0) + { + if (set) + set_value = 1 << t; + else + clear_value = 1 << t; + break; + } + } + } + + rtems_rfs_trace_flags |= set_value; + rtems_rfs_trace_flags &= ~clear_value; + } + } + + return 0; +} + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-trace.h b/cpukit/libfs/src/rfs/rtems-rfs-trace.h new file mode 100644 index 0000000000..05d9258c7d --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-trace.h @@ -0,0 +1,129 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Trace manages the trace and debugging features of the + * RTEMS RFS file system. The design allows all tracing code and strings to be + * removed from the target code for small footprint systems. + */ + +#if !defined (_RTEMS_RFS_TRACE_H_) +#define _RTEMS_RFS_TRACE_H_ + +#include <stddef.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> + +/** + * Is tracing enabled ? + */ +#if !defined (RTEMS_RFS_TRACE) +#define RTEMS_RFS_TRACE 1 +#endif + +/** + * The type of the mask. + */ +typedef uint64_t rtems_rfs_trace_mask; + +/** + * List of tracing bits for the various parts of the file system. + */ +#define RTEMS_RFS_TRACE_ALL (0xffffffffffffffffULL) +#define RTEMS_RFS_TRACE_OPEN (1ULL << 0) +#define RTEMS_RFS_TRACE_CLOSE (1ULL << 1) +#define RTEMS_RFS_TRACE_MUTEX (1ULL << 2) +#define RTEMS_RFS_TRACE_BUFFER_OPEN (1ULL << 3) +#define RTEMS_RFS_TRACE_BUFFER_CLOSE (1ULL << 4) +#define RTEMS_RFS_TRACE_BUFFER_SYNC (1ULL << 5) +#define RTEMS_RFS_TRACE_BUFFER_RELEASE (1ULL << 6) +#define RTEMS_RFS_TRACE_BUFFER_CHAINS (1ULL << 7) +#define RTEMS_RFS_TRACE_BUFFER_HANDLE_REQUEST (1ULL << 8) +#define RTEMS_RFS_TRACE_BUFFER_HANDLE_RELEASE (1ULL << 9) +#define RTEMS_RFS_TRACE_BUFFER_SETBLKSIZE (1ULL << 10) +#define RTEMS_RFS_TRACE_BUFFERS_RELEASE (1ULL << 11) +#define RTEMS_RFS_TRACE_BLOCK_FIND (1ULL << 12) +#define RTEMS_RFS_TRACE_BLOCK_MAP_GROW (1ULL << 13) +#define RTEMS_RFS_TRACE_BLOCK_MAP_SHRINK (1ULL << 14) +#define RTEMS_RFS_TRACE_GROUP_OPEN (1ULL << 15) +#define RTEMS_RFS_TRACE_GROUP_CLOSE (1ULL << 16) +#define RTEMS_RFS_TRACE_GROUP_BITMAPS (1ULL << 17) +#define RTEMS_RFS_TRACE_INODE_OPEN (1ULL << 18) +#define RTEMS_RFS_TRACE_INODE_CLOSE (1ULL << 19) +#define RTEMS_RFS_TRACE_INODE_LOAD (1ULL << 20) +#define RTEMS_RFS_TRACE_INODE_UNLOAD (1ULL << 21) +#define RTEMS_RFS_TRACE_INODE_CREATE (1ULL << 22) +#define RTEMS_RFS_TRACE_INODE_DELETE (1ULL << 23) +#define RTEMS_RFS_TRACE_LINK (1ULL << 24) +#define RTEMS_RFS_TRACE_UNLINK (1ULL << 25) +#define RTEMS_RFS_TRACE_DIR_LOOKUP_INO (1ULL << 26) +#define RTEMS_RFS_TRACE_DIR_LOOKUP_INO_CHECK (1ULL << 27) +#define RTEMS_RFS_TRACE_DIR_LOOKUP_INO_FOUND (1ULL << 28) +#define RTEMS_RFS_TRACE_DIR_ADD_ENTRY (1ULL << 29) +#define RTEMS_RFS_TRACE_DIR_DEL_ENTRY (1ULL << 30) +#define RTEMS_RFS_TRACE_DIR_READ (1ULL << 31) +#define RTEMS_RFS_TRACE_DIR_EMPTY (1ULL << 32) +#define RTEMS_RFS_TRACE_SYMLINK (1ULL << 33) +#define RTEMS_RFS_TRACE_SYMLINK_READ (1ULL << 34) +#define RTEMS_RFS_TRACE_FILE_OPEN (1ULL << 35) +#define RTEMS_RFS_TRACE_FILE_CLOSE (1ULL << 36) +#define RTEMS_RFS_TRACE_FILE_IO (1ULL << 37) +#define RTEMS_RFS_TRACE_FILE_SET (1ULL << 38) + +/** + * Call to check if this part is bring traced. If RTEMS_RFS_TRACE is defined to + * 0 the code is dead code elminiated when built with -Os, -O2, or higher. + * + * @param mask The part of the API to trace. + * @retval true Tracing is active for the mask. + * @retval false Do not trace. + */ +#if RTEMS_RFS_TRACE +bool rtems_rfs_trace (rtems_rfs_trace_mask mask); +#else +#define rtems_rfs_trace(_m) (0) +#endif + +/** + * Set the mask. + * + * @param mask The mask bits to set. + * @return The previous mask. + */ +#if RTEMS_RFS_TRACE +rtems_rfs_trace_mask rtems_rfs_trace_set_mask (rtems_rfs_trace_mask mask); +#else +#define rtems_rfs_trace_set_mask(_m) +#endif + +/** + * Clear the mask. + * + * @param mask The mask bits to clear. + * @return The previous mask. + */ +#if RTEMS_RFS_TRACE +rtems_rfs_trace_mask rtems_rfs_trace_clear_mask (rtems_rfs_trace_mask mask); +#else +#define rtems_rfs_trace_clear_mask(_m) +#endif + +/** + * Add shell trace shell command. + */ +#if RTEMS_RFS_TRACE +int rtems_rfs_trace_shell_command (int argc, char *argv[]); +#endif + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs.h b/cpukit/libfs/src/rfs/rtems-rfs.h new file mode 100644 index 0000000000..ca5a6f3051 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs.h @@ -0,0 +1,30 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System + * + */ + +#if !defined(RTEMS_RFS_DEFINED) +#define RTEMS_RFS_DEFINED + +#include <rtems.h> +#include <rtems/fs.h> + +/** + * Initialise the RFS File system. + */ +int rtems_rfs_rtems_initialise (rtems_filesystem_mount_table_entry_t *mt_entry); + +#endif |