summaryrefslogtreecommitdiffstats
path: root/cpukit/libfs/src
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libfs/src')
-rw-r--r--cpukit/libfs/src/defaults/default_chown.c23
-rw-r--r--cpukit/libfs/src/defaults/default_close.c19
-rw-r--r--cpukit/libfs/src/defaults/default_evalformake.c32
-rw-r--r--cpukit/libfs/src/defaults/default_evalpath.c25
-rw-r--r--cpukit/libfs/src/defaults/default_evaluate_link.c23
-rw-r--r--cpukit/libfs/src/defaults/default_fchmod.c22
-rw-r--r--cpukit/libfs/src/defaults/default_fcntl.c22
-rw-r--r--cpukit/libfs/src/defaults/default_fdatasync.c21
-rw-r--r--cpukit/libfs/src/defaults/default_fpathconf.c22
-rw-r--r--cpukit/libfs/src/defaults/default_freenode.c22
-rw-r--r--cpukit/libfs/src/defaults/default_fsmount.c22
-rw-r--r--cpukit/libfs/src/defaults/default_fstat.c22
-rw-r--r--cpukit/libfs/src/defaults/default_fsunmount.c21
-rw-r--r--cpukit/libfs/src/defaults/default_fsync.c21
-rw-r--r--cpukit/libfs/src/defaults/default_ftruncate.c22
-rw-r--r--cpukit/libfs/src/defaults/default_handlers.c39
-rw-r--r--cpukit/libfs/src/defaults/default_ioctl.c23
-rw-r--r--cpukit/libfs/src/defaults/default_link.c24
-rw-r--r--cpukit/libfs/src/defaults/default_lseek.c23
-rw-r--r--cpukit/libfs/src/defaults/default_mknod.c24
-rw-r--r--cpukit/libfs/src/defaults/default_mount.c21
-rw-r--r--cpukit/libfs/src/defaults/default_node_type.c29
-rw-r--r--cpukit/libfs/src/defaults/default_open.c24
-rw-r--r--cpukit/libfs/src/defaults/default_ops.c43
-rw-r--r--cpukit/libfs/src/defaults/default_read.c23
-rw-r--r--cpukit/libfs/src/defaults/default_readlink.c23
-rw-r--r--cpukit/libfs/src/defaults/default_rename.c24
-rw-r--r--cpukit/libfs/src/defaults/default_rmnod.c22
-rw-r--r--cpukit/libfs/src/defaults/default_statvfs.c22
-rw-r--r--cpukit/libfs/src/defaults/default_symlink.c23
-rw-r--r--cpukit/libfs/src/defaults/default_unlink.c22
-rw-r--r--cpukit/libfs/src/defaults/default_unmount.c21
-rw-r--r--cpukit/libfs/src/defaults/default_utime.c24
-rw-r--r--cpukit/libfs/src/defaults/default_write.c23
-rw-r--r--cpukit/libfs/src/devfs/devclose.c41
-rw-r--r--cpukit/libfs/src/devfs/devfs.h280
-rw-r--r--cpukit/libfs/src/devfs/devfs_eval.c85
-rw-r--r--cpukit/libfs/src/devfs/devfs_init.c92
-rw-r--r--cpukit/libfs/src/devfs/devfs_mknod.c81
-rw-r--r--cpukit/libfs/src/devfs/devfs_node_type.c26
-rw-r--r--cpukit/libfs/src/devfs/devfs_show.c36
-rw-r--r--cpukit/libfs/src/devfs/devioctl.c45
-rw-r--r--cpukit/libfs/src/devfs/devopen.c42
-rw-r--r--cpukit/libfs/src/devfs/devread.c48
-rw-r--r--cpukit/libfs/src/devfs/devstat.c46
-rw-r--r--cpukit/libfs/src/devfs/devwrite.c48
-rw-r--r--cpukit/libfs/src/dosfs/.cvsignore4
-rw-r--r--cpukit/libfs/src/dosfs/dosfs.h88
-rw-r--r--cpukit/libfs/src/dosfs/fat.c863
-rw-r--r--cpukit/libfs/src/dosfs/fat.h517
-rw-r--r--cpukit/libfs/src/dosfs/fat_fat_operations.c436
-rw-r--r--cpukit/libfs/src/dosfs/fat_fat_operations.h59
-rw-r--r--cpukit/libfs/src/dosfs/fat_file.c993
-rw-r--r--cpukit/libfs/src/dosfs/fat_file.h192
-rw-r--r--cpukit/libfs/src/dosfs/msdos.h467
-rw-r--r--cpukit/libfs/src/dosfs/msdos_conv.c317
-rw-r--r--cpukit/libfs/src/dosfs/msdos_create.c267
-rw-r--r--cpukit/libfs/src/dosfs/msdos_dir.c706
-rw-r--r--cpukit/libfs/src/dosfs/msdos_eval.c437
-rw-r--r--cpukit/libfs/src/dosfs/msdos_file.c503
-rw-r--r--cpukit/libfs/src/dosfs/msdos_format.c1127
-rw-r--r--cpukit/libfs/src/dosfs/msdos_free.c56
-rw-r--r--cpukit/libfs/src/dosfs/msdos_fsunmount.c70
-rw-r--r--cpukit/libfs/src/dosfs/msdos_handlers_dir.c36
-rw-r--r--cpukit/libfs/src/dosfs/msdos_handlers_file.c36
-rw-r--r--cpukit/libfs/src/dosfs/msdos_init.c64
-rw-r--r--cpukit/libfs/src/dosfs/msdos_initsupp.c149
-rw-r--r--cpukit/libfs/src/dosfs/msdos_misc.c1731
-rw-r--r--cpukit/libfs/src/dosfs/msdos_mknod.c84
-rw-r--r--cpukit/libfs/src/dosfs/msdos_node_type.c58
-rw-r--r--cpukit/libfs/src/dosfs/msdos_rename.c92
-rw-r--r--cpukit/libfs/src/imfs/.cvsignore5
-rw-r--r--cpukit/libfs/src/imfs/deviceerrno.c73
-rw-r--r--cpukit/libfs/src/imfs/deviceio.c233
-rw-r--r--cpukit/libfs/src/imfs/fifoimfs_init.c61
-rw-r--r--cpukit/libfs/src/imfs/imfs.h574
-rw-r--r--cpukit/libfs/src/imfs/imfs_chown.c56
-rw-r--r--cpukit/libfs/src/imfs/imfs_config.c35
-rw-r--r--cpukit/libfs/src/imfs/imfs_creat.c170
-rw-r--r--cpukit/libfs/src/imfs/imfs_debug.c159
-rw-r--r--cpukit/libfs/src/imfs/imfs_directory.c317
-rw-r--r--cpukit/libfs/src/imfs/imfs_eval.c663
-rw-r--r--cpukit/libfs/src/imfs/imfs_fchmod.c56
-rw-r--r--cpukit/libfs/src/imfs/imfs_fdatasync.c29
-rw-r--r--cpukit/libfs/src/imfs/imfs_fifo.c148
-rw-r--r--cpukit/libfs/src/imfs/imfs_fsunmount.c94
-rw-r--r--cpukit/libfs/src/imfs/imfs_getchild.c66
-rw-r--r--cpukit/libfs/src/imfs/imfs_gtkn.c91
-rw-r--r--cpukit/libfs/src/imfs/imfs_handlers_device.c41
-rw-r--r--cpukit/libfs/src/imfs/imfs_handlers_directory.c41
-rw-r--r--cpukit/libfs/src/imfs/imfs_handlers_link.c41
-rw-r--r--cpukit/libfs/src/imfs/imfs_handlers_memfile.c41
-rw-r--r--cpukit/libfs/src/imfs/imfs_init.c61
-rw-r--r--cpukit/libfs/src/imfs/imfs_initsupp.c119
-rw-r--r--cpukit/libfs/src/imfs/imfs_link.c78
-rw-r--r--cpukit/libfs/src/imfs/imfs_load_tar.c186
-rw-r--r--cpukit/libfs/src/imfs/imfs_mknod.c76
-rw-r--r--cpukit/libfs/src/imfs/imfs_mount.c53
-rw-r--r--cpukit/libfs/src/imfs/imfs_ntype.c32
-rw-r--r--cpukit/libfs/src/imfs/imfs_readlink.c43
-rw-r--r--cpukit/libfs/src/imfs/imfs_rename.c54
-rw-r--r--cpukit/libfs/src/imfs/imfs_rmnod.c77
-rw-r--r--cpukit/libfs/src/imfs/imfs_stat.c81
-rw-r--r--cpukit/libfs/src/imfs/imfs_symlink.c77
-rw-r--r--cpukit/libfs/src/imfs/imfs_unlink.c82
-rw-r--r--cpukit/libfs/src/imfs/imfs_unmount.c62
-rw-r--r--cpukit/libfs/src/imfs/imfs_utime.c42
-rw-r--r--cpukit/libfs/src/imfs/ioman.c93
-rw-r--r--cpukit/libfs/src/imfs/memfile.c975
-rw-r--r--cpukit/libfs/src/imfs/miniimfs_init.c61
-rw-r--r--cpukit/libfs/src/nfsclient/.cvsignore2
-rw-r--r--cpukit/libfs/src/nfsclient/ChangeLog.slac112
-rw-r--r--cpukit/libfs/src/nfsclient/LICENSE44
-rw-r--r--cpukit/libfs/src/nfsclient/Makefile.am74
-rw-r--r--cpukit/libfs/src/nfsclient/README548
-rw-r--r--cpukit/libfs/src/nfsclient/preinstall.am51
-rw-r--r--cpukit/libfs/src/nfsclient/proto/mount_prot.h144
-rw-r--r--cpukit/libfs/src/nfsclient/proto/mount_prot.x161
-rw-r--r--cpukit/libfs/src/nfsclient/proto/mount_prot_xdr.c104
-rw-r--r--cpukit/libfs/src/nfsclient/proto/nfs_prot.h453
-rw-r--r--cpukit/libfs/src/nfsclient/proto/nfs_prot.x1268
-rw-r--r--cpukit/libfs/src/nfsclient/proto/nfs_prot_xdr.c621
-rw-r--r--cpukit/libfs/src/nfsclient/rfc1094.txt1258
-rw-r--r--cpukit/libfs/src/nfsclient/src/cexphelp.c20
-rw-r--r--cpukit/libfs/src/nfsclient/src/dirutils.c385
-rw-r--r--cpukit/libfs/src/nfsclient/src/librtemsNfs.h180
-rw-r--r--cpukit/libfs/src/nfsclient/src/nfs.c3354
-rw-r--r--cpukit/libfs/src/nfsclient/src/nfs.modini.c31
-rw-r--r--cpukit/libfs/src/nfsclient/src/nfsTest.c381
-rw-r--r--cpukit/libfs/src/nfsclient/src/rpcio.c1792
-rw-r--r--cpukit/libfs/src/nfsclient/src/rpcio.h209
-rw-r--r--cpukit/libfs/src/nfsclient/src/rpcio.modini.c19
-rw-r--r--cpukit/libfs/src/nfsclient/src/sock_mbuf.c283
-rw-r--r--cpukit/libfs/src/nfsclient/src/xdr_mbuf.c539
-rw-r--r--cpukit/libfs/src/pipe/fifo.c585
-rw-r--r--cpukit/libfs/src/pipe/pipe.c79
-rw-r--r--cpukit/libfs/src/pipe/pipe.h116
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-bitmaps-ut.c398
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-bitmaps.c646
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-bitmaps.h303
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-block-pos.h239
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-block.c800
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-block.h324
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-buffer-bdbuf.c92
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-buffer-devio.c61
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-buffer.c484
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-buffer.h265
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-data.h87
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-dir-hash.c341
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-dir-hash.h34
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-dir.c760
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-dir.h206
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-file-system-fwd.h27
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-file-system.c320
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-file-system.h402
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-file.c596
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-file.h393
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-format.c644
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-format.h90
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-group.c361
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-group.h163
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-inode.c402
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-inode.h693
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-link.c458
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-link.h112
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-mutex.c72
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-mutex.h108
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-rtems-dev.c271
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-rtems-dir.c245
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-rtems-file.c354
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-rtems-utils.c244
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-rtems.c1286
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-rtems.h329
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-shell.c753
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-shell.h44
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-trace.c159
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-trace.h129
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs.h30
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,
+ &sectors_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