From a9fa9b765df38cc5319ae734b5148fd47ebbfd8d Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Thu, 18 Feb 2010 00:24:25 +0000 Subject: 2010-02-18 Chris Johns * libfs/src/rfs/rtems-rfs-bitmaps.c, libfs/src/rfs/rtems-rfs-bitmaps.h, libfs/src/rfs/rtems-rfs-bitmaps-ut.c, libfs/src/rfs/rtems-rfs-block.c, libfs/src/rfs/rtems-rfs-block.h, libfs/src/rfs/rtems-rfs-block-pos.h, libfs/src/rfs/rtems-rfs-buffer-bdbuf.c, libfs/src/rfs/rtems-rfs-buffer.c, libfs/src/rfs/rtems-rfs-buffer-devio.c, libfs/src/rfs/rtems-rfs-buffer.h, libfs/src/rfs/rtems-rfs-data.h, libfs/src/rfs/rtems-rfs-dir.c, libfs/src/rfs/rtems-rfs-dir.h, libfs/src/rfs/rtems-rfs-dir-hash.c, libfs/src/rfs/rtems-rfs-dir-hash.h, libfs/src/rfs/rtems-rfs-file.c, libfs/src/rfs/rtems-rfs-file.h, libfs/src/rfs/rtems-rfs-file-system.c, libfs/src/rfs/rtems-rfs-file-system-fwd.h, libfs/src/rfs/rtems-rfs-file-system.h, libfs/src/rfs/rtems-rfs-format.c, libfs/src/rfs/rtems-rfs-format.h, libfs/src/rfs/rtems-rfs-group.c, libfs/src/rfs/rtems-rfs-group.h, libfs/src/rfs/rtems-rfs.h, libfs/src/rfs/rtems-rfs-inode.c, libfs/src/rfs/rtems-rfs-inode.h, libfs/src/rfs/rtems-rfs-link.c, libfs/src/rfs/rtems-rfs-link.h, libfs/src/rfs/rtems-rfs-mutex.c, libfs/src/rfs/rtems-rfs-mutex.h, libfs/src/rfs/rtems-rfs-rtems.c, libfs/src/rfs/rtems-rfs-rtems-dev.c, libfs/src/rfs/rtems-rfs-rtems-dir.c, libfs/src/rfs/rtems-rfs-rtems-file.c, libfs/src/rfs/rtems-rfs-rtems.h, libfs/src/rfs/rtems-rfs-rtems-utils.c, libfs/src/rfs/rtems-rfs-shell.c, libfs/src/rfs/rtems-rfs-shell.h, libfs/src/rfs/rtems-rfs-trace.c, libfs/src/rfs/rtems-rfs-trace.h: New. * Makefile.am, preinstall.am, libfs/Makefile.am, wrapup/Makefile.am: Updated with the RFS support. * libfs/README: Updated after 10 years. * libblock/src/flashdisk.c, libblock/src/nvdisk.c, libblock/src/ramdisk-driver.c: Updated to the new error reporting in libblock. * libmisc/shell/main_ls.c, libmisc/shell/print-ls.c: Fix printing the size in long mode. * libnetworking/nfs/bootp_subr.c, libnetworking/rtems/rtems_bootp.c, libnetworking/rtems/rtems_bsdnet_internal.h: Return the BOOTP/DHCP to the forever behaviour of 4.9 with the ability to call BOOTP and control the process if required. --- cpukit/ChangeLog | 52 + cpukit/Makefile.am | 23 + cpukit/libblock/src/flashdisk.c | 14 +- cpukit/libblock/src/nvdisk.c | 6 +- cpukit/libblock/src/ramdisk-driver.c | 2 + cpukit/libfs/Makefile.am | 15 + cpukit/libfs/README | 24 +- cpukit/libfs/src/rfs/rtems-rfs-bitmaps-ut.c | 389 ++++++ cpukit/libfs/src/rfs/rtems-rfs-bitmaps.c | 642 ++++++++++ cpukit/libfs/src/rfs/rtems-rfs-bitmaps.h | 305 +++++ cpukit/libfs/src/rfs/rtems-rfs-block-pos.h | 233 ++++ cpukit/libfs/src/rfs/rtems-rfs-block.c | 800 +++++++++++++ cpukit/libfs/src/rfs/rtems-rfs-block.h | 302 +++++ cpukit/libfs/src/rfs/rtems-rfs-buffer-bdbuf.c | 87 ++ cpukit/libfs/src/rfs/rtems-rfs-buffer-devio.c | 57 + cpukit/libfs/src/rfs/rtems-rfs-buffer.c | 478 ++++++++ cpukit/libfs/src/rfs/rtems-rfs-buffer.h | 262 +++++ cpukit/libfs/src/rfs/rtems-rfs-data.h | 78 ++ cpukit/libfs/src/rfs/rtems-rfs-dir-hash.c | 337 ++++++ cpukit/libfs/src/rfs/rtems-rfs-dir-hash.h | 34 + cpukit/libfs/src/rfs/rtems-rfs-dir.c | 742 ++++++++++++ cpukit/libfs/src/rfs/rtems-rfs-dir.h | 206 ++++ cpukit/libfs/src/rfs/rtems-rfs-file-system-fwd.h | 27 + cpukit/libfs/src/rfs/rtems-rfs-file-system.c | 278 +++++ cpukit/libfs/src/rfs/rtems-rfs-file-system.h | 383 ++++++ cpukit/libfs/src/rfs/rtems-rfs-file.c | 573 +++++++++ cpukit/libfs/src/rfs/rtems-rfs-file.h | 393 +++++++ cpukit/libfs/src/rfs/rtems-rfs-format.c | 627 ++++++++++ cpukit/libfs/src/rfs/rtems-rfs-format.h | 90 ++ cpukit/libfs/src/rfs/rtems-rfs-group.c | 316 +++++ cpukit/libfs/src/rfs/rtems-rfs-group.h | 151 +++ cpukit/libfs/src/rfs/rtems-rfs-inode.c | 370 ++++++ cpukit/libfs/src/rfs/rtems-rfs-inode.h | 688 +++++++++++ cpukit/libfs/src/rfs/rtems-rfs-link.c | 439 +++++++ cpukit/libfs/src/rfs/rtems-rfs-link.h | 99 ++ cpukit/libfs/src/rfs/rtems-rfs-mutex.c | 68 ++ cpukit/libfs/src/rfs/rtems-rfs-mutex.h | 108 ++ cpukit/libfs/src/rfs/rtems-rfs-rtems-dev.c | 271 +++++ cpukit/libfs/src/rfs/rtems-rfs-rtems-dir.c | 238 ++++ cpukit/libfs/src/rfs/rtems-rfs-rtems-file.c | 334 ++++++ cpukit/libfs/src/rfs/rtems-rfs-rtems-utils.c | 240 ++++ cpukit/libfs/src/rfs/rtems-rfs-rtems.c | 1235 ++++++++++++++++++++ cpukit/libfs/src/rfs/rtems-rfs-rtems.h | 318 +++++ cpukit/libfs/src/rfs/rtems-rfs-shell.c | 666 +++++++++++ cpukit/libfs/src/rfs/rtems-rfs-shell.h | 35 + cpukit/libfs/src/rfs/rtems-rfs-trace.c | 154 +++ cpukit/libfs/src/rfs/rtems-rfs-trace.h | 126 ++ cpukit/libfs/src/rfs/rtems-rfs.h | 35 + cpukit/libmisc/shell/main_ls.c | 5 +- cpukit/libmisc/shell/print-ls.c | 7 +- cpukit/libnetworking/nfs/bootp_subr.c | 78 +- cpukit/libnetworking/rtems/rtems_bootp.c | 10 +- cpukit/libnetworking/rtems/rtems_bsdnet_internal.h | 2 +- cpukit/preinstall.am | 77 ++ cpukit/wrapup/Makefile.am | 1 + 55 files changed, 13476 insertions(+), 54 deletions(-) create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-bitmaps-ut.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-bitmaps.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-bitmaps.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-block-pos.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-block.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-block.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-buffer-bdbuf.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-buffer-devio.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-buffer.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-buffer.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-data.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-dir-hash.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-dir-hash.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-dir.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-dir.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-file-system-fwd.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-file-system.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-file-system.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-file.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-file.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-format.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-format.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-group.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-group.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-inode.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-inode.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-link.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-link.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-mutex.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-mutex.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-rtems-dev.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-rtems-dir.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-rtems-file.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-rtems-utils.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-rtems.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-rtems.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-shell.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-shell.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-trace.c create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-trace.h create mode 100644 cpukit/libfs/src/rfs/rtems-rfs.h (limited to 'cpukit') diff --git a/cpukit/ChangeLog b/cpukit/ChangeLog index 5f1856004c..db91adf397 100644 --- a/cpukit/ChangeLog +++ b/cpukit/ChangeLog @@ -1,3 +1,55 @@ +2010-02-18 Chris Johns + + * libfs/src/rfs/rtems-rfs-bitmaps.c, + libfs/src/rfs/rtems-rfs-bitmaps.h, + libfs/src/rfs/rtems-rfs-bitmaps-ut.c, + libfs/src/rfs/rtems-rfs-block.c, libfs/src/rfs/rtems-rfs-block.h, + libfs/src/rfs/rtems-rfs-block-pos.h, + libfs/src/rfs/rtems-rfs-buffer-bdbuf.c, + libfs/src/rfs/rtems-rfs-buffer.c, + libfs/src/rfs/rtems-rfs-buffer-devio.c, + libfs/src/rfs/rtems-rfs-buffer.h, libfs/src/rfs/rtems-rfs-data.h, + libfs/src/rfs/rtems-rfs-dir.c, libfs/src/rfs/rtems-rfs-dir.h, + libfs/src/rfs/rtems-rfs-dir-hash.c, + libfs/src/rfs/rtems-rfs-dir-hash.h, + libfs/src/rfs/rtems-rfs-file.c, libfs/src/rfs/rtems-rfs-file.h, + libfs/src/rfs/rtems-rfs-file-system.c, + libfs/src/rfs/rtems-rfs-file-system-fwd.h, + libfs/src/rfs/rtems-rfs-file-system.h, + libfs/src/rfs/rtems-rfs-format.c, + libfs/src/rfs/rtems-rfs-format.h, libfs/src/rfs/rtems-rfs-group.c, + libfs/src/rfs/rtems-rfs-group.h, libfs/src/rfs/rtems-rfs.h, + libfs/src/rfs/rtems-rfs-inode.c, libfs/src/rfs/rtems-rfs-inode.h, + libfs/src/rfs/rtems-rfs-link.c, libfs/src/rfs/rtems-rfs-link.h, + libfs/src/rfs/rtems-rfs-mutex.c, libfs/src/rfs/rtems-rfs-mutex.h, + libfs/src/rfs/rtems-rfs-rtems.c, + libfs/src/rfs/rtems-rfs-rtems-dev.c, + libfs/src/rfs/rtems-rfs-rtems-dir.c, + libfs/src/rfs/rtems-rfs-rtems-file.c, + libfs/src/rfs/rtems-rfs-rtems.h, + libfs/src/rfs/rtems-rfs-rtems-utils.c, + libfs/src/rfs/rtems-rfs-shell.c, libfs/src/rfs/rtems-rfs-shell.h, + libfs/src/rfs/rtems-rfs-trace.c, libfs/src/rfs/rtems-rfs-trace.h: + New. + + * Makefile.am, preinstall.am, libfs/Makefile.am, + wrapup/Makefile.am: Updated with the RFS support. + + * libfs/README: Updated after 10 years. + + * libblock/src/flashdisk.c, libblock/src/nvdisk.c, + libblock/src/ramdisk-driver.c: Updated to the new error reporting + in libblock. + + * libmisc/shell/main_ls.c, libmisc/shell/print-ls.c: Fix printing + the size in long mode. + + * libnetworking/nfs/bootp_subr.c, + libnetworking/rtems/rtems_bootp.c, + libnetworking/rtems/rtems_bsdnet_internal.h: Return the BOOTP/DHCP + to the forever behaviour of 4.9 with the ability to call BOOTP and + control the process if required. + 2010-02-16 Chris Johns * libcsupport/src/open.c: Tighten the open handler check. diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am index 4a0885fd6b..11df9c0c93 100644 --- a/cpukit/Makefile.am +++ b/cpukit/Makefile.am @@ -95,6 +95,29 @@ if LIBDOSFS include_rtems_HEADERS += libfs/src/dosfs/dosfs.h endif +# RFS +include_rtems_HEADERS += \ + libfs/src/rfs/rtems-rfs.h \ + libfs/src/rfs/rtems-rfs-format.h \ + libfs/src/rfs/rtems-rfs-shell.h +include_rtems_rfsdir = $(include_rtemsdir)/rfs +include_rtems_rfs_HEADERS = \ + libfs/src/rfs/rtems-rfs-bitmaps.h \ + libfs/src/rfs/rtems-rfs-block-pos.h \ + libfs/src/rfs/rtems-rfs-block.h \ + libfs/src/rfs/rtems-rfs-buffer.h \ + libfs/src/rfs/rtems-rfs-data.h \ + libfs/src/rfs/rtems-rfs-dir.h \ + libfs/src/rfs/rtems-rfs-dir-hash.h \ + libfs/src/rfs/rtems-rfs-file.h \ + libfs/src/rfs/rtems-rfs-file-system-fwd.h \ + libfs/src/rfs/rtems-rfs-file-system.h \ + libfs/src/rfs/rtems-rfs-group.h \ + libfs/src/rfs/rtems-rfs-inode.h \ + libfs/src/rfs/rtems-rfs-link.h \ + libfs/src/rfs/rtems-rfs-mutex.h \ + libfs/src/rfs/rtems-rfs-trace.h + ## libblock include_rtems_HEADERS += libblock/include/rtems/bdbuf.h \ libblock/include/rtems/blkdev.h libblock/include/rtems/diskdevs.h \ diff --git a/cpukit/libblock/src/flashdisk.c b/cpukit/libblock/src/flashdisk.c index a1c8ea5036..322e21c0b7 100644 --- a/cpukit/libblock/src/flashdisk.c +++ b/cpukit/libblock/src/flashdisk.c @@ -134,7 +134,7 @@ * @todo A sequence number for the block could be added. This would * mean a larger descriptor size. Need to make the sequence * large like 20+ bits so a large file system would not have - * more blocks available then the sequence number. + * more blocks available than the sequence number. */ typedef struct rtems_fdisk_page_desc { @@ -1304,9 +1304,9 @@ rtems_fdisk_queue_segment (rtems_flashdisk* fd, rtems_fdisk_segment_ctl* sc) /** * Compact the used segments to free what is available. Find the segment - * with the most avalable number of pages and see if the we have + * with the most avalable number of pages and see if we have * used segments that will fit. The used queue is sorted on the least - * number active pages. + * number of active pages. */ static int rtems_fdisk_compact (rtems_flashdisk* fd) @@ -1701,7 +1701,7 @@ rtems_fdisk_recover_block_mappings (rtems_flashdisk* fd) * @return EIO Invalid block size, block number, segment pointer, crc, * page flags. */ -static int +static bool rtems_fdisk_read_block (rtems_flashdisk* fd, uint32_t block, uint8_t* buffer) @@ -2071,7 +2071,8 @@ rtems_fdisk_read (rtems_flashdisk* fd, rtems_blkdev_request* req) } } - req->req_done (req->done_arg, ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR); + req->status = ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL; + req->req_done (req->done_arg, req->status); return ret; } @@ -2106,7 +2107,8 @@ rtems_fdisk_write (rtems_flashdisk* fd, rtems_blkdev_request* req) } } - req->req_done (req->done_arg, ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR); + req->status = ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL; + req->req_done (req->done_arg, req->status); return 0; } diff --git a/cpukit/libblock/src/nvdisk.c b/cpukit/libblock/src/nvdisk.c index fde4a025d9..0d41b6e803 100644 --- a/cpukit/libblock/src/nvdisk.c +++ b/cpukit/libblock/src/nvdisk.c @@ -595,7 +595,8 @@ rtems_nvdisk_read (rtems_nvdisk* nvd, rtems_blkdev_request* req) } } - req->req_done (req->done_arg, ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR); + req->status = ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL; + req->req_done (req->done_arg, req->status); return ret; } @@ -634,7 +635,8 @@ rtems_nvdisk_write (rtems_nvdisk* nvd, rtems_blkdev_request* req) } } - req->req_done (req->done_arg, ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR); + req->status = ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL; + req->req_done (req->done_arg, req->status); return 0; } diff --git a/cpukit/libblock/src/ramdisk-driver.c b/cpukit/libblock/src/ramdisk-driver.c index 1e078fdf2a..ce3b444da2 100644 --- a/cpukit/libblock/src/ramdisk-driver.c +++ b/cpukit/libblock/src/ramdisk-driver.c @@ -65,6 +65,7 @@ ramdisk_read(struct ramdisk *rd, rtems_blkdev_request *req) #endif memcpy(sg->buffer, from + (sg->block * rd->block_size), sg->length); } + req->status = RTEMS_SUCCESSFUL; req->req_done(req->done_arg, RTEMS_SUCCESSFUL); return 0; } @@ -89,6 +90,7 @@ ramdisk_write(struct ramdisk *rd, rtems_blkdev_request *req) #endif memcpy(to + (sg->block * rd->block_size), sg->buffer, sg->length); } + req->status = RTEMS_SUCCESSFUL; req->req_done(req->done_arg, RTEMS_SUCCESSFUL); return 0; } diff --git a/cpukit/libfs/Makefile.am b/cpukit/libfs/Makefile.am index 6eb3ead8e2..56cdaa0052 100644 --- a/cpukit/libfs/Makefile.am +++ b/cpukit/libfs/Makefile.am @@ -62,6 +62,21 @@ libdosfs_a_SOURCES += src/dosfs/msdos_create.c src/dosfs/msdos_dir.c \ src/dosfs/dosfs.h endif +# RFS +noinst_LIBRARIES += librfs.a +librfs_a_SOURCES = \ + src/rfs/rtems-rfs-bitmaps.c src/rfs/rtems-rfs-block.c \ + src/rfs/rtems-rfs-buffer-bdbuf.c src/rfs/rtems-rfs-buffer.c \ + src/rfs/rtems-rfs-dir-hash.c src/rfs/rtems-rfs-file.c \ + src/rfs/rtems-rfs-group.c src/rfs/rtems-rfs-inode.c \ + src/rfs/rtems-rfs-rtems-dev.c src/rfs/rtems-rfs-rtems-utils.c \ + src/rfs/rtems-rfs-rtems.c src/rfs/rtems-rfs-shell.c \ + src/rfs/rtems-rfs-bitmaps-ut.c src/rfs/rtems-rfs-dir.c \ + src/rfs/rtems-rfs-file-system.c src/rfs/rtems-rfs-format.c \ + src/rfs/rtems-rfs-link.c src/rfs/rtems-rfs-mutex.c \ + src/rfs/rtems-rfs-rtems-dir.c src/rfs/rtems-rfs-rtems-file.c \ + src/rfs/rtems-rfs-trace.c + # --- include $(srcdir)/preinstall.am include $(top_srcdir)/automake/local.am diff --git a/cpukit/libfs/README b/cpukit/libfs/README index 7409b93dca..dfd7682099 100644 --- a/cpukit/libfs/README +++ b/cpukit/libfs/README @@ -5,9 +5,25 @@ This directory contains for the "file system" library. All supported file systems live under this tree. -Currently the only supported file systems in this library are the IMFS -and miniIMFS. The TFTP client filesystem is part of the libnetworking -library. +Currently the supported file systems in this library are: + +- IMFS or In Memory File System + + This is the only root file system on RTEMS at the moment. It supports + files, directories, device nodes and mount points. It can also be + configured to be the miniIMFS. + +- TFTP and FTP filesystem are part of the libnetworking library. + +- DEVFS or Device File system + +- DOSFS, a FAT 12/16/32 MSDOS compatible file system. + +- NFS Client, can mount NFS exported file systems. + +- PIPE, a pipe file system. + +- RFS, The RTEMS File System. --Chris Johns and Joel Sherrill -18 October 2000 + 17 Feb 2010 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..ad571af1bb --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-bitmaps-ut.c @@ -0,0 +1,389 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include +#include + +#include +#include + +#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 %ld in range (%ld,%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 %ld in range (%ld,%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 (%ld,%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 (%ld, %ld] (%ld)\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 = %ld\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 = %ld: %s: bit = %ld\n", + test, seed, result ? "pass" : "FAIL", bit); + return false; + } + } + + printf (" %2d. Alloc'ed all bits (%ld, %ld] (%ld)\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 = %ld (%ld)\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 = %ld\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) (%ld): %s (%s): bit = %ld\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) (%ld): %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 %ld: %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 = %ld\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 = %ld\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 = %ld\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) (%ld): %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 = %ld\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) (%ld): %s (%s): bit = %ld\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) (%ld): %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) (%ld): %s (%s): bit = %ld\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) (%ld): %s (%s): bit = %ld\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) (%ld): %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) (%ld): %s (%s): bit = %ld\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 %ld: %s (%s)\n", + bit, rc == 0 ? "PASS" : "FAIL", strerror (rc)); + rtems_rfs_exit_on_error (rc, false, &control, buffer.buffer); + } + } + + printf (" 26. Clear bit (%ld, %ld]: %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 %ld: %ld: %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 %ld: %ld: %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 : %ld\n", rtems_rfs_bitmap_element_bits ()); + + srand (0x23984237); + + 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..4de86342b0 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-bitmaps.c @@ -0,0 +1,642 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +/** + * Set to 1 to enable warnings when developing. + */ +#define RTEMS_RFS_BITMAP_WARNINGS 0 + +#if RTEMS_RFS_BITMAP_WARNINGS +#include +#endif +#include +#include + +/** + * 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..8fe9ab7c9b --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-bitmaps.h @@ -0,0 +1,305 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 +#include +#include + +/** + * 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. + */ +struct rtems_rfs_bitmap_control_t +{ + 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. +}; + +typedef struct rtems_rfs_bitmap_control_t 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..899e915695 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-block-pos.h @@ -0,0 +1,233 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 +#include + +/** + * 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_t +{ + /** + * 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. + * @parma _rhs The right handl 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_t +{ + /** + * 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. + * @parma _rhs The right handl 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) + +/** + * 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..e8542d6685 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-block.c @@ -0,0 +1,800 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include +#include +#include +#include + +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) +{ + rtems_rfs_pos pos = 0; + if (bpos->bno) + { + pos = bpos->boff; + if (pos == 0) + pos = rtems_rfs_fs_block_size (fs); + pos += (bpos->bno - 1) * rtems_rfs_fs_block_size (fs); + } + return pos; +} + +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; + if (size->count == 0) + return 0; + if (size->offset == 0) + offset = rtems_rfs_fs_block_size (fs); + else + offset = size->offset; + return (((uint64_t) (size->count - 1)) * rtems_rfs_fs_block_size (fs)) + 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=%ld, indirect=%ld/%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=%ld\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=%ld count=%lu\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=%ld count=%lu\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..a7ae8e3974 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-block.h @@ -0,0 +1,302 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 +#include +#include +#include + +/** + * 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_t +{ + /** + * 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) + +/** + * Set the size offset for the map. + */ +#define rtems_rfs_block_map_set_size_offset(_m, _o) ((_m)->size.offset = (_o)) + +/** + * 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) + +/** + * 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. + * @prarm 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..bed5ff8c8e --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-buffer-bdbuf.c @@ -0,0 +1,87 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + * + */ + +#include + +#include +#include + +#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=%lu bdbuf=%lu %s\n", + (rtems_rfs_buffer_block) 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..157fc0305a --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-buffer-devio.c @@ -0,0 +1,57 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + * + */ + +#include + +#include +#include + +#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..ee1a7f6bde --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-buffer.c @@ -0,0 +1,478 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + * + */ + +#include + +#include +#include + +/** + * 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=%lu, block=%lu: ", *count, block); + + while (!rtems_chain_is_head (chain, node)) + { + buffer = (rtems_rfs_buffer*) node; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS)) + printf ("%lu ", (rtems_rfs_buffer_block) buffer->user); + + if (((rtems_rfs_buffer_block) buffer->user) == block) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS)) + printf (": found block=%lu\n", (rtems_rfs_buffer_block) 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: %lu\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=%lu\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=%lu: 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*) block; + handle->bnum = block; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_REQUEST)) + printf ("rtems-rfs: buffer-request: block=%lu bdbuf-%s=%lu 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=%lu %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:" \ + " %lu\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_ISCHR (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=%ld, blk-size=%ld\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: %lu\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=%lu\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:%lu " \ + "release:%lu release-modified:%lu\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..480bce360d --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-buffer.h @@ -0,0 +1,262 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 + +#include +#include + +/** + * 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 +#include + +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_t +{ + 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); + 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..67e820faac --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-data.h @@ -0,0 +1,78 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 + +/** + * Helper function to make sure we have a byte pointer. + */ +#define rtems_rfs_data_ptr(_d) ((uint8_t*)(_d)) + +/** + * 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_ptr (_d)[0] << 8) | (rtems_rfs_data_ptr (_d)[1])) + +/** + * RFS Read Unsigned 32bit Integer + */ +#define rtems_rfs_read_u32(_d) \ + ((rtems_rfs_data_ptr (_d)[0] << 24) | (rtems_rfs_data_ptr (_d)[1] << 16) | \ + (rtems_rfs_data_ptr (_d)[2] << 8) | (rtems_rfs_data_ptr (_d)[3])) + +/** + * 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)((_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)((_v) >> 24); \ + rtems_rfs_data_ptr (_d)[1] = (uint8_t)((_v) >> 16); \ + rtems_rfs_data_ptr (_d)[2] = (uint8_t)((_v) >> 8); \ + rtems_rfs_data_ptr (_d)[3] = (uint8_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..791883390e --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-dir-hash.c @@ -0,0 +1,337 @@ +/* + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Directory Hash function. + */ + +#include + +#ifdef __rtems__ +# include /* attempt to define endianness */ +#endif +#ifdef linux +# include /* 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 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 + * + * The license and 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 +#include + +/** + * 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..8813fb751e --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-dir.c @@ -0,0 +1,742 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include +#include +#include +#include +#include +#include + +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=%ld, 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 %lu: %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 %lu: %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; + + 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=%lu block=%ld: %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 ((elength <= RTEMS_RFS_DIR_ENTRY_SIZE) + || (elength >= rtems_rfs_fs_max_name (fs)) + || (*ino < RTEMS_RFS_ROOT_INO) + || (*ino >= rtems_rfs_fs_inodes (fs))) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: " \ + "bad length or ino for ino %lu: %u/%ld @ %04lx\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 %ld: off=%04lx length:%d ino:%d\n", + rtems_rfs_inode_ino (inode), 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 %lu, ino=%lu offset=%lu\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 %lu: %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 %lu: %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=%ld, name=", + rtems_rfs_inode_ino (dir)); + for (c = 0; c < length; c++) + printf ("%c", name[c]); + printf (", len=%ld\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 %lu: %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 %lu: %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 %lu: %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 ((elength <= RTEMS_RFS_DIR_ENTRY_SIZE) + || (elength >= rtems_rfs_fs_max_name (fs)) + || (eino < RTEMS_RFS_ROOT_INO) + || (eino >= rtems_rfs_fs_inodes (fs))) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY)) + printf ("rtems-rfs: dir-add-entry: " \ + "bad length or ino for ino %lu: %u/%ld @ %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=%ld, entry=%ld offset=%lu\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 offset; + + 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 %lu: %d: %s\n", + rtems_rfs_inode_ino (dir), rc, strerror (rc)); + 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 ((elength <= RTEMS_RFS_DIR_ENTRY_SIZE) + || (elength >= rtems_rfs_fs_max_name (fs)) + || (eino < RTEMS_RFS_ROOT_INO) + || (eino >= rtems_rfs_fs_inodes (fs))) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY)) + printf ("rtems-rfs: dir-del-entry: " \ + "bad length or ino for ino %lu: %u/%ld @ %04x\n", + rtems_rfs_inode_ino (dir), elength, eino, offset); + rc = EIO; + break; + } + + if (ino == rtems_rfs_dir_entry_ino (entry)) + { + uint32_t remaining; + remaining = rtems_rfs_fs_block_size (fs) - (offset + 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 %lu: elength=%i offset=%d last=%s\n", + ino, elength, offset, + rtems_rfs_block_map_last (&map) ? "yes" : "no"); + + if ((elength == RTEMS_RFS_DIR_ENTRY_EMPTY) && + (offset == 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 %lu: %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; + offset += 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=%ld offset=%Ld\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 ((elength <= RTEMS_RFS_DIR_ENTRY_SIZE) + || (elength >= rtems_rfs_fs_max_name (fs)) + || (eino < RTEMS_RFS_ROOT_INO) + || (eino >= rtems_rfs_fs_inodes (fs))) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ)) + printf ("rtems-rfs: dir-read: " \ + "bad length or ino for ino %lu: %u/%ld @ %04lx\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; + + if (elength < RTEMS_RFS_DIR_ENTRY_SIZE) + elength = 0; + else + 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:%ld 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:%ld length:%ld\n", + (uint32_t) 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=%ld\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 ((elength <= RTEMS_RFS_DIR_ENTRY_SIZE) + || (elength >= rtems_rfs_fs_max_name (fs)) + || (eino < RTEMS_RFS_ROOT_INO) + || (eino >= rtems_rfs_fs_inodes (fs))) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_EMPTY)) + printf ("rtems-rfs: dir-empty: " \ + "bad length or ino for ino %lu: %u/%ld @ %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..2852908ea7 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-dir.h @@ -0,0 +1,206 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 + +#include + +#include +#include +#include + +/** + * 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. + * @aparam _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* path, + 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..7309c60cfd --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-file-system-fwd.h @@ -0,0 +1,27 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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_t; +typedef struct rtems_rfs_file_system_t 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..ce355be824 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-file-system.c @@ -0,0 +1,278 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include +#include +#include +#include + +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) + { + rtems_rfs_buffer_handle_close (fs, &handle); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: read-superblock: invalid superblock, bad magic\n"); + 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)) + { + rtems_rfs_buffer_handle_close (fs, &handle); + if (rtems_rfs_trace (RTEMS_RFS_TRACE_OPEN)) + printf ("rtems-rfs: read-superblock: invalid superblock block/size count\n"); + 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 / sizeof (rtems_rfs_inode); + + 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..7f307a2cd5 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-file-system.h @@ -0,0 +1,383 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 + +/** + * Superblock offsets and values. + */ +#define RTEMS_RFS_SB_OFFSET_MAGIC (0) +#define RTEMS_RFS_SB_MAGIC (0x28092001) +#define RTEMS_RFS_SB_OFFSET_BLOCK_SIZE (4) +#define RTEMS_RFS_SB_OFFSET_BLOCKS (8) +#define RTEMS_RFS_SB_OFFSET_BAD_BLOCKS (12) +#define RTEMS_RFS_SB_OFFSET_MAX_NAME_LENGTH (16) +#define RTEMS_RFS_SB_OFFSET_GROUPS (20) +#define RTEMS_RFS_SB_OFFSET_GROUP_BLOCKS (24) +#define RTEMS_RFS_SB_OFFSET_GROUP_INODES (28) + +/** + * 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_t +{ + /** + * 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; +}; + +/** + * 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 size of the disk in bytes. + * + * @param _fs Pointer to the file system. + */ +#define rtems_rfs_fs_size(_fs) (((uint64_t) rtems_rfs_fs_blocks (_fs)) * \ + rtems_rfs_fs_block_size (_fs)) + +/** + * 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 size of the disk in bytes. + * + * @param _fs Pointer to the file system. + */ +#define rtems_rfs_fs_media_size(_fs) (((uint64_t) rtems_rfs_fs_media_blocks (_fs)) * \ + rtems_rfs_fs_media_block_size (_fs)) + +/** + * 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) + +/** + * 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..33c9196a39 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-file.c @@ -0,0 +1,573 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include +#include +#include +#include + +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=%ld\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=%ld 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=%ld 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=%ld\n", + handle->shared->inode.ino); + + if (handle->shared->references > 0) + handle->shared->references--; + + if (handle->shared->references == 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); + handle->shared->map.size.count = handle->shared->size.count; + handle->shared->map.size.offset = handle->shared->size.offset; + + 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=%ld: %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=%ld: %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=%lu:%lu\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=%lu 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))) + 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=%lu (%lu)\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=%lu\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=%lu: %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=%lu:%lu %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=%Lu\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; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO)) + printf ("rtems-rfs: file-set-size: size=%Lu\n", new_size); + + /* + * Short cut for the common truncate on open call. + */ + if (new_size == 0) + return rtems_rfs_block_map_free_all (rtems_rfs_file_fs (handle), map); + + 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; + int rc; + + /* + * 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..8467bc951b --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-file.h @@ -0,0 +1,393 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 + +#include +#include +#include +#include + +/** + * 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_t +{ + /** + * 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_t +{ + /** + * 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..e15905b2f0 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-format.c @@ -0,0 +1,627 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include +#include + +#include +#include +#include +#include +#include + +/** + * Return the number of gigabytes. + */ +#define GIGS(_g) ((_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_bits_per_block (fs) * percentage) / 100; + return blocks * (rtems_rfs_fs_block_size (fs) / sizeof (rtems_rfs_inode)); +} + +/** + * Return the inode overhead given a number of inodes. + */ +static int +rtems_rfs_inode_overhead (rtems_rfs_file_system* fs) +{ + int blocks; + blocks = + (fs->group_inodes * sizeof (rtems_rfs_inode)) / rtems_rfs_fs_block_size (fs); + return ((blocks + 1) * 100 * 10) / rtems_rfs_bits_per_block (fs); +} + +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 (2)) + { + 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 < 1024) + fs->block_size = 1024; + + 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 (%ld) is not a multiple of media block size (%ld)\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->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) / sizeof (rtems_rfs_inode); + 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 = %ld, size = %ld", + 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 rerquired 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 %ld 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_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); + + 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: %ld\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.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: %lu\n", + rtems_rfs_fs_media_block_size (&fs)); + return -1; + } + + /* + * Check the configuration data. + */ + if (!rtems_rfs_check_config (&fs, config)) + return -1; + + 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_fs_blocks (&fs) - 1) / rtems_rfs_bits_per_block (&fs)) + 1; + + if (config->verbose) + { + printf ("rtems-rfs: format: media size = %llu\n", + rtems_rfs_fs_media_size (&fs)); + printf ("rtems-rfs: format: media blocks = %ld\n", + rtems_rfs_fs_media_blocks (&fs)); + printf ("rtems-rfs: format: media block size = %lu\n", + rtems_rfs_fs_media_block_size (&fs)); + printf ("rtems-rfs: format: size = %llu\n", + rtems_rfs_fs_size (&fs)); + printf ("rtems-rfs: format: blocks = %lu\n", + rtems_rfs_fs_blocks (&fs)); + printf ("rtems-rfs: format: block size = %lu\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 = %lu\n", sizeof (rtems_rfs_inode)); + printf ("rtems-rfs: format: inodes = %lu (%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 = %lu\n", fs.group_blocks); + printf ("rtems-rfs: format: group inodes = %lu\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..41da50781e --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-format.h @@ -0,0 +1,90 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * 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_t +{ + /** + * 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..ef60d320c5 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-group.c @@ -0,0 +1,316 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include +#include + +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; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_OPEN)) + printf ("rtems-rfs: group-open: base=%ld, blocks=%ld inodes=%ld\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=%ld\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: %ld\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: %ld\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: %ld\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; +} 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..3adf736e2f --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-group.h @@ -0,0 +1,151 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 +#include +#include + +/** + * 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_t +{ + /** + * 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. + * @prarm 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. + * @prarm block The inode or block number to free. + * @prarm 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); + +#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..b23f09994d --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-inode.c @@ -0,0 +1,370 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include +#include +#include +#include + +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: %lu\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: %lu\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=%lu 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=%lu 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:%lu 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)); + } + + 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) + 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. + */ + 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_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, sizeof (rtems_rfs_inode)); + 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..0a6226bd8c --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-inode.h @@ -0,0 +1,688 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 + +#include +#include + +/** + * 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_t +{ + /** + * 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; + +/** + * RFS Inode Handle. + */ +typedef struct rtems_rfs_inode_handle_t +{ + /** + * 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, (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. + * @parma 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. + * @oaram 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..bfbcae66e7 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-link.c @@ -0,0 +1,439 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include +#include +#include +#include +#include +#include + +int +rtems_rfs_link (rtems_rfs_file_system* fs, + const char* name, + int length, + rtems_rfs_ino parent, + rtems_rfs_ino target) +{ + 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(%lu) -> ", parent); + for (c = 0; c < length; c++) + printf ("%c", name[c]); + printf ("(%lu)\n", target); + } + + rc = rtems_rfs_inode_open (fs, target, &target_inode, true); + if (rc) + return rc; + + if (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, + bool 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_UNLINK)) + printf ("rtems-rfs: unlink: parent(%lu) -X-> (%lu)\n", parent, target); + + rc = rtems_rfs_inode_open (fs, target, &target_inode, true); + if (rc) + return rc; + + if (dir) + { + 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; + } + } + else + { + /* + * Directories not allowed and the target is a directory. + */ + if (RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&target_inode))) + { + 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; + } + } + + 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:%lu 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:%lu 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:%lu\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..d80db0195c --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-link.h @@ -0,0 +1,99 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 + +#include +#include + +/** + * Create a link. + * + * @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. + * @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); + +/** + * 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 If true unlinking of directory nodes is allowed. + * @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, + bool dir); + +/** + * 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..e67651d269 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-mutex.c @@ -0,0 +1,68 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include + +#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 + * + * The license and 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 + +#include + +#if __rtems__ +#include +#include +#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..cebd730ed7 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems-dev.c @@ -0,0 +1,271 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + * + */ + +#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); + if (status) + return rtems_deviceio_errno(status); + + return 0; +} + +/** + * 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); + if (status) + return rtems_deviceio_errno (status); + + return 0; +} + +/** + * 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_stat, + .fchmod_h = rtems_rfs_rtems_fchmod, + .ftruncate_h = rtems_rfs_rtems_device_ftruncate, + .fpathconf_h = NULL, + .fsync_h = NULL, + .fdatasync_h = NULL, + .fcntl_h = NULL, + .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..f22e3d6913 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems-dir.c @@ -0,0 +1,238 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 + */ + +#include +#include +#include +#include +#include + +#include +#include +#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; + size_t bytes_transfered; + 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_transfered = 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_transfered = rtems_rfs_rtems_error ("dir_read: dir read", rc); + break; + } + iop->offset += size; + bytes_transfered += sizeof (struct dirent); + } + + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + + return (ssize_t) bytes_transfered; +} + +/** + * 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:%ld doff:%lu, ino:%ld\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, true); + 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 = NULL, + .ioctl_h = NULL, + .lseek_h = rtems_rfs_rtems_dir_lseek, + .fstat_h = rtems_rfs_rtems_stat, + .fchmod_h = rtems_rfs_rtems_fchmod, + .ftruncate_h = NULL, + .fpathconf_h = NULL, + .fsync_h = NULL, + .fdatasync_h = rtems_rfs_rtems_fdatasync, + .fcntl_h = rtems_rfs_rtems_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..18ef2d2d77 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems-file.c @@ -0,0 +1,334 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include +#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_rtems_get_iop_ino (iop); + 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:%ld flags:%04lx mode:%04lx\n", + pathname, ino, flags, mode); + + rtems_rfs_rtems_lock (fs); + + 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->file_info = 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 = iop->file_info; + 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 0; +} + +/** + * This routine processes the read() system call. + * + * @param iop + * @param buffer + * @param count + * @return int + */ +ssize_t +rtems_rfs_rtems_file_read (rtems_libio_t* iop, + void* buffer, + size_t count) +{ + rtems_rfs_file_handle* file = iop->file_info; + rtems_rfs_pos pos = iop->offset; + 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:%ld\n", file, count); + + rtems_rfs_rtems_lock (rtems_rfs_file_fs (file)); + + 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 + */ +ssize_t +rtems_rfs_rtems_file_write (rtems_libio_t* iop, + const void* buffer, + size_t count) +{ + rtems_rfs_file_handle* file = iop->file_info; + rtems_rfs_pos pos = iop->offset; + 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:%ld\n", file, count); + + rtems_rfs_rtems_lock (rtems_rfs_file_fs (file)); + + /* + * 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 (pos > rtems_rfs_file_size (file)) + { + rc = rtems_rfs_file_set_size (file, pos); + if (rc) + 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 + */ + +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 + */ +rtems_off64_t +rtems_rfs_rtems_file_lseek (rtems_libio_t* iop, + rtems_off64_t offset, + int whence) +{ + rtems_rfs_file_handle* file = iop->file_info; + 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); + + pos = iop->offset; + + rtems_rfs_rtems_lock (rtems_rfs_file_fs (file)); + + 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 + */ +int +rtems_rfs_rtems_file_ftruncate (rtems_libio_t* iop, + rtems_off64_t length) +{ + rtems_rfs_file_handle* file = iop->file_info; + 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_stat, + .fchmod_h = rtems_rfs_rtems_fchmod, + .ftruncate_h = rtems_rfs_rtems_file_ftruncate, + .fpathconf_h = NULL, + .fsync_h = rtems_rfs_rtems_fdatasync, + .fdatasync_h = rtems_rfs_rtems_fdatasync, + .fcntl_h = rtems_rfs_rtems_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..f14cbd8af7 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems-utils.c @@ -0,0 +1,240 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include +#include +#include + +#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..60f3fdf429 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems.c @@ -0,0 +1,1235 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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. + */ + +#include + +#include +#include +#include +#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 + */ +int +rtems_rfs_rtems_eval_path (const char* path, + int 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; + int 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:%i ino:%ld\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 < (rtems_rfs_fs_max_name (fs) - 1))) + { + 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) + 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 (%d)\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:%ld\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:%ld\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:%ld\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 + */ +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:%ld\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) + 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:%ld\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:%ld\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:%ld 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 + */ +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:%ld target:%ld\n", + parent, target); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_link (fs, name, strlen (name), parent, target); + 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 + */ + +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:%ld doff:%lu ino:%ld\n", + parent, doff, ino); + + rc = rtems_rfs_unlink (fs, parent, ino, doff, false); + 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 + */ + +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:%ld 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 RFS free node handler for the file system operations + * table. The RFS does not need to free anything. + * + * @param pathloc + * @retval 0 Always returned. + */ + +int +rtems_rfs_rtems_freenodinfo (rtems_filesystem_location_info_t* pathloc) +{ + return 0; +} + +/** + * This routine is the implementation of the utime() system call for the + * RFS. + * + * @param pathloc + * @param atime + * @param mtime + * return int + */ + +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 + */ + +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 + */ + +int +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:%ld\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; +} + +/** + * File change mode routine. + * + * @param pathloc + * @param mode + * @return int + */ +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:%ld mode:%06o\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: not owner", 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; +} + +/** + * The following routine does a fcntl on an node. + * + * @param cmd + * @param iop + * @return int + */ + +int +rtems_rfs_rtems_fcntl (int cmd, + rtems_libio_t* iop) +{ + return 0; +} + +/** + * The following routine does a stat on a node. + * + * @param pathloc + * @param buf + * @return int + */ + +int +rtems_rfs_rtems_stat (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:%ld\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 = ino; + 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, ino); + + 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 + */ + +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 + */ +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:%ld doff:%lu, ino:%ld\n", + parent, doff, ino); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_unlink (fs, parent, ino, doff, false); + 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 + */ +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; +} + +/** + * Return the file system stat data. + * + * @param pathloc + * @param sb + * @return int + */ +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); + + 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_fsid = RTEMS_RFS_SB_MAGIC; + 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 = NULL, + .close_h = NULL, + .read_h = NULL, + .write_h = NULL, + .ioctl_h = NULL, + .lseek_h = NULL, + .fstat_h = rtems_rfs_rtems_stat, + .fchmod_h = NULL, + .ftruncate_h = NULL, + .fpathconf_h = NULL, + .fsync_h = NULL, + .fdatasync_h = NULL, + .fcntl_h = NULL, + .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); +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_rfs_rtems_freenodinfo, + .mount_h = NULL, + .fsmount_me_h = rtems_rfs_rtems_initialise, + .unmount_h = NULL, + .fsunmount_me_h = rtems_rfs_rtems_shutdown, + .utime_h = rtems_rfs_rtems_utime, + .eval_link_h = NULL, /* never called cause we lie in the node type */ + .symlink_h = rtems_rfs_rtems_symlink, + .readlink_h = rtems_rfs_rtems_readlink, + .statvfs_h = rtems_rfs_rtems_statvfs +}; + +/** + * Open the file system. + */ + +int +rtems_rfs_rtems_initialise (rtems_filesystem_mount_table_entry_t* mt_entry) +{ + 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..2c294aaebd --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems.h @@ -0,0 +1,318 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 +#include +#include + +/** + * 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 +#include +#include +#include +#include + +/** + * 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*)(_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*)(_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 + */ +#define rtems_rfs_rtems_get_iop_ino(_iop) \ + ((intptr_t)(_iop)->file_info) + +/** + * 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 pathloc + * @param buf + * @return int + */ +int rtems_rfs_rtems_stat (rtems_filesystem_location_info_t* loc, struct stat* buf); + +/** + * File change mode routine. + * + * @param pathloc + * @param mode + * @return int + */ +int rtems_rfs_rtems_fchmod (rtems_filesystem_location_info_t* loc, 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); + +/** + * The following routine does a fcntl on an node. + * + * @param cmd + * @param iop + * @return int + */ +int rtems_rfs_rtems_fcntl (int cmd, 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..b1d25c2c67 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-shell.c @@ -0,0 +1,666 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#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 rtems_rfs_shell_cmd_t +{ + 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; + int g; + + printf ("RFS Filesystem Data\n"); + printf (" flags: %08lx\n", fs->flags); +#if 0 + printf (" device: %08lx\n", rtems_rfs_fs_device (fs)); +#endif + printf (" blocks: %lu\n", rtems_rfs_fs_blocks (fs)); + printf (" block size: %lu\n", rtems_rfs_fs_block_size (fs)); + printf (" size: %llu\n", rtems_rfs_fs_size (fs)); + printf (" media block size: %lu\n", rtems_rfs_fs_media_block_size (fs)); + printf (" media size: %llu\n", rtems_rfs_fs_media_size (fs)); + printf (" inodes: %lu\n", rtems_rfs_fs_inodes (fs)); + printf (" bad blocks: %lu\n", fs->bad_blocks); + printf (" max. name length: %lu\n", rtems_rfs_fs_max_name (fs)); + printf (" groups: %d\n", fs->group_count); + printf (" group blocks: %ld\n", fs->group_blocks); + printf (" group inodes: %ld\n", fs->group_inodes); + printf (" inodes per block: %ld\n", fs->inodes_per_block); + printf (" blocks per block: %ld\n", fs->blocks_per_block); + printf (" singly blocks: %ld\n", fs->block_map_singly_blocks); + printf (" doublly blocks: %ld\n", fs->block_map_doubly_blocks); + printf (" max. held buffers: %ld\n", fs->max_held_buffers); + + rtems_rfs_shell_lock_rfs (fs); + + 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); + } + + 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: %ld (%d.%d%%)\n", + blocks, bpcent / 10, bpcent % 10); + printf (" inodes used: %ld (%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=%lu: (%d) %s\n", + block, rc, strerror (rc)); + return 1; + } + + printf (" %5lu: 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=%lu: (%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=%lu: (%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=%lu: (%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 < 0) || (end < 0) || + (start >= total) || (end >= total)) + { + printf ("error: inode out of range (0->%ld).\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=%lu: (%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=%lu: (%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 (" %5lu: pos=%06lu:%04lx %c ", + ino, rtems_rfs_buffer_bnum (&inode.buffer), + inode.offset * sizeof (rtems_rfs_inode), + 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=%04lu 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 ("%lu ", rtems_rfs_inode_get_block (&inode, b)); + printf ("%lu]\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=%lu: (%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=%lu: (%d) %s\n", + block, rc, strerror (rc)); + return 1; + } + + printf (" %5lu: 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=%lu: (%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=%lu: (%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=%ld\n", entry, eino); + break; + } + + length = elength - RTEMS_RFS_DIR_ENTRY_SIZE; + + printf (" %5d: %04x inode=%-6u hash=%08x 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=%lu: (%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=%-7lu size=%-6lu blocks=%-5lu (%3lu%%) inode=%-5lu (%3lu%%)\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] \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_rfs_shell_debugrfs (int argc, char *argv[]) +{ + const rtems_rfs_shell_cmd table[] = + { + { "block", rtems_rfs_shell_block, + "Display the contents of a block, block , block .." }, + { "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 " }, + { "group", rtems_rfs_shell_group, + "Display the group data of a file system, group, group , group " }, + { "inode", rtems_rfs_shell_inode, + "Display an inode, inode , inode> .." } + }; + + 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; +} 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..31a3dc8266 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-shell.h @@ -0,0 +1,35 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 +#include +#include + +/** + * 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_rfs_shell_debugrfs (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..f344ac9ec6 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-trace.c @@ -0,0 +1,154 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 + */ + +#include +#include + +#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", + "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..516bfe8e8f --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-trace.h @@ -0,0 +1,126 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 +#include +#include +#include + +/** + * Is tracing enabled ? + */ +#define RTEMS_RFS_TRACE 0 + +/** + * 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_LINK (1ULL << 23) +#define RTEMS_RFS_TRACE_UNLINK (1ULL << 24) +#define RTEMS_RFS_TRACE_DIR_LOOKUP_INO (1ULL << 25) +#define RTEMS_RFS_TRACE_DIR_LOOKUP_INO_CHECK (1ULL << 26) +#define RTEMS_RFS_TRACE_DIR_LOOKUP_INO_FOUND (1ULL << 27) +#define RTEMS_RFS_TRACE_DIR_ADD_ENTRY (1ULL << 28) +#define RTEMS_RFS_TRACE_DIR_DEL_ENTRY (1ULL << 29) +#define RTEMS_RFS_TRACE_DIR_READ (1ULL << 30) +#define RTEMS_RFS_TRACE_DIR_EMPTY (1ULL << 31) +#define RTEMS_RFS_TRACE_SYMLINK (1ULL << 32) +#define RTEMS_RFS_TRACE_SYMLINK_READ (1ULL << 33) +#define RTEMS_RFS_TRACE_FILE_OPEN (1ULL << 34) +#define RTEMS_RFS_TRACE_FILE_CLOSE (1ULL << 35) +#define RTEMS_RFS_TRACE_FILE_IO (1ULL << 36) +#define RTEMS_RFS_TRACE_FILE_SET (1ULL << 37) + +/** + * 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..b0e5a6c5e2 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs.h @@ -0,0 +1,35 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and 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 +#include + +/** + * File ops table for the RFS file system. + */ +const rtems_filesystem_operations_table rtems_rfs_ops; + +/** + * Initialise the RFS File system. + */ +int rtems_rfs_initialise (rtems_filesystem_mount_table_entry_t *mt_entry); + +#endif diff --git a/cpukit/libmisc/shell/main_ls.c b/cpukit/libmisc/shell/main_ls.c index b60477ff79..08072fb723 100644 --- a/cpukit/libmisc/shell/main_ls.c +++ b/cpukit/libmisc/shell/main_ls.c @@ -573,8 +573,9 @@ display(rtems_shell_ls_globals* globals, FTSENT *p, FTSENT *list) maxlen = cur->fts_namelen; if (needstats) { sp = cur->fts_statp; - size = ((uint64_t) sp->st_blocks) * sp->st_blksize; - if (size < 0x100000000ULL) + if (sp->st_size < 0) + size = sp->st_size * -1; + else size = sp->st_size; if (sp->st_blocks > maxblock) maxblock = sp->st_blocks; diff --git a/cpukit/libmisc/shell/print-ls.c b/cpukit/libmisc/shell/print-ls.c index 192d8f11b8..56e7b149d7 100644 --- a/cpukit/libmisc/shell/print-ls.c +++ b/cpukit/libmisc/shell/print-ls.c @@ -162,9 +162,10 @@ printlong(rtems_shell_ls_globals* globals, DISPLAY *dp) } else { #endif { - unsigned long long size = sp->st_blocks; - size *= sp->st_blksize; - if (size < 0x100000000ULL) + unsigned long long size; + if (sp->st_size < 0) + size = sp->st_size * -1; + else size = sp->st_size; (void)printf("%*llu ", dp->s_size, size); } diff --git a/cpukit/libnetworking/nfs/bootp_subr.c b/cpukit/libnetworking/nfs/bootp_subr.c index 7a0e8901f6..34754ac77a 100644 --- a/cpukit/libnetworking/nfs/bootp_subr.c +++ b/cpukit/libnetworking/nfs/bootp_subr.c @@ -152,8 +152,6 @@ bootpc_adjust_interface(struct ifreq *ireq,struct socket *so, struct sockaddr_in *gw, struct proc *procp); -void bootpc_init(int update_files); - #ifdef BOOTP_DEBUG void bootpboot_p_sa(struct sockaddr *sa, struct sockaddr *ma) @@ -463,7 +461,7 @@ bootpc_call( goto gotreply; /* break two levels */ } /* while secs */ - } /* forever send/receive */ + } /* send/receive a number of times then return an error */ { uint32_t addr = ntohl(sin->sin_addr.s_addr); printf("BOOTP timeout for server %lu.%lu.%lu.%lu\n", @@ -943,8 +941,8 @@ processOptions (unsigned char *optbuf, int optbufSize) #define EALEN 6 -void -bootpc_init(int update_files) +bool +bootpc_init(bool update_files, bool forever) { struct bootp_packet call; struct bootp_packet reply; @@ -965,7 +963,7 @@ bootpc_init(int update_files) * If already filled in, don't touch it here */ if (nfs_diskless_valid) - return; + return true; /* * If we are to update the files create the root @@ -991,7 +989,7 @@ bootpc_init(int update_files) break; if (ifp == NULL) { printf("bootpc_init: no suitable interface\n"); - return; + return false; } bzero(&ireq,sizeof(ireq)); sprintf(ireq.ifr_name, "%s%d", ifp->if_name,ifp->if_unit); @@ -1000,28 +998,31 @@ bootpc_init(int update_files) if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0,procp)) != 0) { printf("bootpc_init: socreate, error=%d", error); - return; + return false; } if (bootpc_fakeup_interface(&ireq,so,procp) != 0) { - return; + soclose(so); + return false; } /* Get HW address */ for (ifa = ifp->if_addrlist;ifa; ifa = ifa->ifa_next) if (ifa->ifa_addr->sa_family == AF_LINK && - (sdl = ((struct sockaddr_dl *) ifa->ifa_addr)) && - sdl->sdl_type == IFT_ETHER) + (sdl = ((struct sockaddr_dl *) ifa->ifa_addr)) && + sdl->sdl_type == IFT_ETHER) break; if (!sdl) { printf("bootpc: Unable to find HW address\n"); - return; + soclose(so); + return false; } if (sdl->sdl_alen != EALEN ) { printf("bootpc: HW address len is %d, expected value is %d\n", sdl->sdl_alen,EALEN); - return; + soclose(so); + return false; } printf("bootpc hw address is "); @@ -1036,32 +1037,39 @@ bootpc_init(int update_files) bootpboot_p_iflist(); bootpboot_p_rtlist(); #endif + + while (true) { + bzero((caddr_t) &call, sizeof(call)); + + /* bootpc part */ + call.op = 1; /* BOOTREQUEST */ + call.htype= 1; /* 10mb ethernet */ + call.hlen=sdl->sdl_alen; /* Hardware address length */ + call.hops=0; + xid++; + call.xid = txdr_unsigned(xid); + bcopy(LLADDR(sdl),&call.chaddr,sdl->sdl_alen); - bzero((caddr_t) &call, sizeof(call)); - - /* bootpc part */ - call.op = 1; /* BOOTREQUEST */ - call.htype= 1; /* 10mb ethernet */ - call.hlen=sdl->sdl_alen; /* Hardware address length */ - call.hops=0; - xid++; - call.xid = txdr_unsigned(xid); - bcopy(LLADDR(sdl),&call.chaddr,sdl->sdl_alen); - - call.vend[0]=99; - call.vend[1]=130; - call.vend[2]=83; - call.vend[3]=99; - call.vend[4]=255; + call.vend[0]=99; + call.vend[1]=130; + call.vend[2]=83; + call.vend[3]=99; + call.vend[4]=255; - call.secs = 0; - call.flags = htons(0x8000); /* We need an broadcast answer */ + call.secs = 0; + call.flags = htons(0x8000); /* We need an broadcast answer */ - error = bootpc_call(&call,&reply,procp); + error = bootpc_call(&call,&reply,procp); - if (error) { + if (!error) + break; + printf("BOOTP call failed -- error %d", error); - return; + + if (!forever) { + soclose(so); + return false; + } } /* @@ -1200,4 +1208,6 @@ bootpc_init(int update_files) error = bootpc_adjust_interface(&ireq,so, &myaddr,&dhcp_netmask,&dhcp_gw,procp); soclose(so); + + return true; } diff --git a/cpukit/libnetworking/rtems/rtems_bootp.c b/cpukit/libnetworking/rtems/rtems_bootp.c index 23c6801757..caf2faa6e2 100644 --- a/cpukit/libnetworking/rtems/rtems_bootp.c +++ b/cpukit/libnetworking/rtems/rtems_bootp.c @@ -18,9 +18,12 @@ void rtems_bsdnet_do_bootp (void) { + bool ok; rtems_bsdnet_semaphore_obtain (); - bootpc_init (FALSE); + ok = bootpc_init (false, true); rtems_bsdnet_semaphore_release (); + if (!ok) + panic ("rtems_bsdnet_do_bootp: bootp failed"); } /* @@ -30,7 +33,10 @@ rtems_bsdnet_do_bootp (void) void rtems_bsdnet_do_bootp_and_rootfs (void) { + bool ok; rtems_bsdnet_semaphore_obtain (); - bootpc_init (TRUE); + ok = bootpc_init (true, true); rtems_bsdnet_semaphore_release (); + if (!ok) + panic ("rtems_bsdnet_do_bootp_and_rootfs: bootp failed"); } diff --git a/cpukit/libnetworking/rtems/rtems_bsdnet_internal.h b/cpukit/libnetworking/rtems/rtems_bsdnet_internal.h index bb3633813a..a22a22391c 100644 --- a/cpukit/libnetworking/rtems/rtems_bsdnet_internal.h +++ b/cpukit/libnetworking/rtems/rtems_bsdnet_internal.h @@ -179,7 +179,7 @@ void domaininit (void *); void ifinit (void *); void ipintr (void); void arpintr (void); -void bootpc_init(int ); +bool bootpc_init(bool, bool); int socket (int, int, int); int ioctl (int, ioctl_command_t, ...); diff --git a/cpukit/preinstall.am b/cpukit/preinstall.am index a3bd6a9b31..c1be034fab 100644 --- a/cpukit/preinstall.am +++ b/cpukit/preinstall.am @@ -172,6 +172,83 @@ $(PROJECT_INCLUDE)/rtems/dosfs.h: libfs/src/dosfs/dosfs.h $(PROJECT_INCLUDE)/rte $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/dosfs.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/dosfs.h endif +$(PROJECT_INCLUDE)/rtems/rtems-rfs.h: libfs/src/rfs/rtems-rfs.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rtems-rfs.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rtems-rfs.h + +$(PROJECT_INCLUDE)/rtems/rtems-rfs-format.h: libfs/src/rfs/rtems-rfs-format.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rtems-rfs-format.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rtems-rfs-format.h + +$(PROJECT_INCLUDE)/rtems/rtems-rfs-shell.h: libfs/src/rfs/rtems-rfs-shell.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rtems-rfs-shell.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rtems-rfs-shell.h + +$(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp): + @$(MKDIR_P) $(PROJECT_INCLUDE)/rtems/rfs + @: > $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-bitmaps.h: libfs/src/rfs/rtems-rfs-bitmaps.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-bitmaps.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-bitmaps.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-block-pos.h: libfs/src/rfs/rtems-rfs-block-pos.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-block-pos.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-block-pos.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-block.h: libfs/src/rfs/rtems-rfs-block.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-block.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-block.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-buffer.h: libfs/src/rfs/rtems-rfs-buffer.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-buffer.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-buffer.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-data.h: libfs/src/rfs/rtems-rfs-data.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-data.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-data.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-dir.h: libfs/src/rfs/rtems-rfs-dir.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-dir.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-dir.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-dir-hash.h: libfs/src/rfs/rtems-rfs-dir-hash.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-dir-hash.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-dir-hash.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-file.h: libfs/src/rfs/rtems-rfs-file.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-file.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-file.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-file-system-fwd.h: libfs/src/rfs/rtems-rfs-file-system-fwd.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-file-system-fwd.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-file-system-fwd.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-file-system.h: libfs/src/rfs/rtems-rfs-file-system.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-file-system.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-file-system.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-group.h: libfs/src/rfs/rtems-rfs-group.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-group.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-group.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-inode.h: libfs/src/rfs/rtems-rfs-inode.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-inode.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-inode.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-link.h: libfs/src/rfs/rtems-rfs-link.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-link.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-link.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-mutex.h: libfs/src/rfs/rtems-rfs-mutex.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-mutex.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-mutex.h + +$(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-trace.h: libfs/src/rfs/rtems-rfs-trace.h $(PROJECT_INCLUDE)/rtems/rfs/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-trace.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rfs/rtems-rfs-trace.h + $(PROJECT_INCLUDE)/rtems/bdbuf.h: libblock/include/rtems/bdbuf.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/bdbuf.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/bdbuf.h diff --git a/cpukit/wrapup/Makefile.am b/cpukit/wrapup/Makefile.am index 1aef744fb3..9c60f8eee3 100644 --- a/cpukit/wrapup/Makefile.am +++ b/cpukit/wrapup/Makefile.am @@ -32,6 +32,7 @@ if LIBDOSFS TMP_LIBS += ../libfs/libdosfs.a endif TMP_LIBS += ../libfs/libimfs.a +TMP_LIBS += ../libfs/librfs.a TMP_LIBS += ../libmisc/libmonitor.a TMP_LIBS += ../libmisc/libuntar.a -- cgit v1.2.3