diff options
author | Chris Johns <chrisj@rtems.org> | 2010-02-18 00:24:25 +0000 |
---|---|---|
committer | Chris Johns <chrisj@rtems.org> | 2010-02-18 00:24:25 +0000 |
commit | a9fa9b765df38cc5319ae734b5148fd47ebbfd8d (patch) | |
tree | 55ee28ea4a162bfb26c7a2cb6106b057beda70ae /cpukit | |
parent | This commit was generated by cvs2svn to compensate for changes in r22658, (diff) | |
download | rtems-a9fa9b765df38cc5319ae734b5148fd47ebbfd8d.tar.bz2 |
2010-02-18 Chris Johns <chrisj@rtems.org>
* 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.
Diffstat (limited to '')
55 files changed, 13476 insertions, 54 deletions
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 <chrisj@rtems.org> + + * 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 <chrisj@rtems.org> * 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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Bitmap Unit Test.. + * + * This is a unit test module for the bit map functions. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <rtems/rfs/rtems-rfs-bitmaps.h> +#include <rtems/rfs/rtems-rfs-file-system.h> + +#define rtems_rfs_exit_on_error(_rc, _r, _c, _b) \ + if ((_rc > 0) || _r) { free (_b); rtems_rfs_bitmap_close (_c); return; } + +static bool +rtems_rfs_bitmap_ut_test_range (rtems_rfs_bitmap_control* control, + int test, + bool set, + rtems_rfs_bitmap_bit bit, + size_t size) +{ + unsigned int count; + bool result; + for (count = 0; count < size; count++) + { + int rc = rtems_rfs_bitmap_map_test (control, bit + count, &result); + if (rc > 0) + { + printf (" %2d. Test bit %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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Bitmap Routines. + * + * These functions manage bit maps. A bit map consists of the map of bit + * allocated in a block and a search map where a bit represents 32 actual + * bits. The search map allows for a faster search for an available bit as 32 + * search bits can checked in a test. + */ + +/** + * Set to 1 to enable warnings when developing. + */ +#define RTEMS_RFS_BITMAP_WARNINGS 0 + +#if RTEMS_RFS_BITMAP_WARNINGS +#include <stdio.h> +#endif +#include <stdlib.h> +#include <rtems/rfs/rtems-rfs-bitmaps.h> + +/** + * Test a bit in an element. If set return true else return false. + * + * @param target The target to test the bit in. + * @param bit The bit to test. + * @retval true The bit is set. + * @retval false The bit is clear. + */ +static bool +rtems_rfs_bitmap_test (rtems_rfs_bitmap_element target, + rtems_rfs_bitmap_bit bit) +{ + return RTEMS_RFS_BITMAP_TEST_BIT (target, bit); +} + +/** + * Set the bits in the element. Bits not set in the bit argument are left + * unchanged. + * + * @param target The target element bits are set. + * @param bits The bits in the target to set. A 1 in the bits will set the + * same bit in the target. + */ +static rtems_rfs_bitmap_element +rtems_rfs_bitmap_set (rtems_rfs_bitmap_element target, + rtems_rfs_bitmap_element bits) +{ + return RTEMS_RFS_BITMAP_SET_BITS (target, bits); +} + +/** + * Clear the bits in the element. Bits not set in the bit argument are left + * unchanged. + * + * @param target The target element to clear the bits in. + * @param bits The bits in the target to clear. A 1 in the bits will clear the + * bit in the target. + */ +static rtems_rfs_bitmap_element +rtems_rfs_bitmap_clear (rtems_rfs_bitmap_element target, + rtems_rfs_bitmap_element bits) +{ + return RTEMS_RFS_BITMAP_CLEAR_BITS (target, bits); +} + +/** + * Merge the bits in 2 variables based on the mask. A set bit in the mask will + * merge the bits from bits1 and a clear bit will merge the bits from bits2. + * The mask is always defined as 1 being set and 0 being clear. + */ +static rtems_rfs_bitmap_element +rtems_rfs_bitmap_merge (rtems_rfs_bitmap_element bits1, + rtems_rfs_bitmap_element bits2, + rtems_rfs_bitmap_element mask) +{ + /* + * Use the normal bit operators because we do not change the bits just merge + * the 2 separate parts. + */ + bits1 &= mask; + bits2 &= RTEMS_RFS_BITMAP_INVERT_MASK (mask); + return bits1 | bits2; +} + +/** + * Match the bits of 2 elements and return true if they match else return + * false. + * + * @param bits1 One set of bits to match. + * @param bits2 The second set of bits to match. + * @retval true The bits match. + * @retval false The bits do not match. + */ +static bool +rtems_rfs_bitmap_match (rtems_rfs_bitmap_element bits1, + rtems_rfs_bitmap_element bits2) +{ + return bits1 ^ bits2 ? false : true; +} + +#if RTEMS_NOT_USED_BUT_KEPT +/** + * Match the bits of 2 elements within the mask and return true if they match + * else return false. + * + * @param mask The mask over which the match occurs. A 1 is a valid mask bit. + * @param bits1 One set of bits to match. + * @param bits2 The second set of bits to match. + * @retval true The bits match. + * @retval false The bits do not match. + */ +static bool +rtems_rfs_bitmap_match_masked (rtems_rfs_bitmap_element mask, + rtems_rfs_bitmap_element bits1, + rtems_rfs_bitmap_element bits2) +{ + return (bits1 ^ bits2) & mask ? false : true; +} +#endif + +/** + * Return the map after loading from disk if not already loaded. + * + * @param control The bitmap control. + * @param rtems_rfs_bitmap_map* Pointer to the bitmap map data if no error. + * @return int The error number (errno). No error if 0. + */ +static int +rtems_rfs_bitmap_load_map (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_map* map) +{ + int rc; + + if (!control->buffer) + return ENXIO; + + *map = NULL; + + rc = rtems_rfs_buffer_handle_request (control->fs, + control->buffer, + control->block, + true); + if (rc) + return rc; + + *map = rtems_rfs_buffer_data (control->buffer); + return 0; +} + +rtems_rfs_bitmap_element +rtems_rfs_bitmap_mask (unsigned int size) +{ + rtems_rfs_bitmap_element mask = RTEMS_RFS_BITMAP_ELEMENT_FULL_MASK; + mask >>= (rtems_rfs_bitmap_element_bits () - size); + return mask; +} + +rtems_rfs_bitmap_element +rtems_rfs_bitmap_mask_section (unsigned int start, unsigned int end) +{ + rtems_rfs_bitmap_element mask = 0; + if (end > start) + mask = rtems_rfs_bitmap_mask (end - start) << start; + return mask; +} + +int +rtems_rfs_bitmap_map_set (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit bit) +{ + rtems_rfs_bitmap_map map; + rtems_rfs_bitmap_map search_map; + int index; + int offset; + int rc; + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + if (bit >= control->size) + return EINVAL; + search_map = control->search_bits; + index = rtems_rfs_bitmap_map_index (bit); + offset = rtems_rfs_bitmap_map_offset (bit); + map[index] = rtems_rfs_bitmap_set (map[index], 1 << offset); + if (rtems_rfs_bitmap_match(map[index], RTEMS_RFS_BITMAP_ELEMENT_SET)) + { + bit = index; + index = rtems_rfs_bitmap_map_index (bit); + offset = rtems_rfs_bitmap_map_offset (bit); + search_map[index] = rtems_rfs_bitmap_set (search_map[index], 1 << offset); + control->free--; + rtems_rfs_buffer_mark_dirty (control->buffer); + } + return 0; +} + +int +rtems_rfs_bitmap_map_clear (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit bit) +{ + rtems_rfs_bitmap_map map; + rtems_rfs_bitmap_map search_map; + int index; + int offset; + int rc; + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + if (bit >= control->size) + return EINVAL; + search_map = control->search_bits; + index = rtems_rfs_bitmap_map_index (bit); + offset = rtems_rfs_bitmap_map_offset (bit); + map[index] = rtems_rfs_bitmap_clear (map[index], 1 << offset); + bit = index; + index = rtems_rfs_bitmap_map_index (bit); + offset = rtems_rfs_bitmap_map_offset(bit); + search_map[index] = rtems_rfs_bitmap_clear (search_map[index], 1 << offset); + rtems_rfs_buffer_mark_dirty (control->buffer); + control->free++; + return 0; +} + +int +rtems_rfs_bitmap_map_test (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit bit, + bool* state) +{ + rtems_rfs_bitmap_map map; + int index; + int rc; + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + if (bit >= control->size) + return EINVAL; + index = rtems_rfs_bitmap_map_index (bit); + *state = rtems_rfs_bitmap_test (map[index], bit); + return 0; +} + +int +rtems_rfs_bitmap_map_set_all (rtems_rfs_bitmap_control* control) +{ + rtems_rfs_bitmap_map map; + size_t elements; + int e; + int rc; + + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + + elements = rtems_rfs_bitmap_elements (control->size); + + control->free = 0; + + for (e = 0; e < elements; e++) + map[e] = RTEMS_RFS_BITMAP_ELEMENT_SET; + + elements = rtems_rfs_bitmap_elements (elements); + + for (e = 0; e < elements; e++) + control->search_bits[e] = RTEMS_RFS_BITMAP_ELEMENT_SET; + + rtems_rfs_buffer_mark_dirty (control->buffer); + + return 0; +} + +int +rtems_rfs_bitmap_map_clear_all (rtems_rfs_bitmap_control* control) +{ + rtems_rfs_bitmap_map map; + rtems_rfs_bitmap_bit last_search_bit; + size_t elements; + int e; + int rc; + + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + + elements = rtems_rfs_bitmap_elements (control->size); + + control->free = elements; + + for (e = 0; e < elements; e++) + map[e] = RTEMS_RFS_BITMAP_ELEMENT_CLEAR; + + /* + * Set the un-mapped bits in the last search element so the available logic + * works. + */ + last_search_bit = rtems_rfs_bitmap_map_offset (elements); + + if (last_search_bit == 0) + last_search_bit = rtems_rfs_bitmap_element_bits (); + + elements = rtems_rfs_bitmap_elements (elements); + + for (e = 0; e < (elements - 1); e++) + control->search_bits[e] = RTEMS_RFS_BITMAP_ELEMENT_CLEAR; + + control->search_bits[elements - 1] = + rtems_rfs_bitmap_merge (RTEMS_RFS_BITMAP_ELEMENT_CLEAR, + RTEMS_RFS_BITMAP_ELEMENT_SET, + rtems_rfs_bitmap_mask (last_search_bit)); + + rtems_rfs_buffer_mark_dirty (control->buffer); + + return 0; +} + +static int +rtems_rfs_search_map_for_clear_bit (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit* bit, + bool* found, + size_t window, + int direction) +{ + rtems_rfs_bitmap_map map; + rtems_rfs_bitmap_bit test_bit; + rtems_rfs_bitmap_bit end_bit; + rtems_rfs_bitmap_element* search_bits; + int search_index; + int search_offset; + rtems_rfs_bitmap_element* map_bits; + int map_index; + int map_offset; + int rc; + + *found = false; + + /* + * Load the bitmap. + */ + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + + /* + * Calculate the bit we are testing plus the end point we search over. + */ + test_bit = *bit; + end_bit = test_bit + (window * direction); + + if (end_bit < 0) + end_bit = 0; + else if (end_bit >= control->size) + end_bit = control->size - 1; + + map_index = rtems_rfs_bitmap_map_index (test_bit); + map_offset = rtems_rfs_bitmap_map_offset (test_bit); + search_index = rtems_rfs_bitmap_map_index (map_index); + search_offset = rtems_rfs_bitmap_map_offset (map_index); + + search_bits = &control->search_bits[search_index]; + map_bits = &map[map_index]; + + /* + * Check each bit from the search map offset for a clear bit. + */ + do + { + /* + * If any bit is clear find that bit and then search the map element. If + * all bits are set there are no map bits so move to the next search + * element. + */ + if (!rtems_rfs_bitmap_match (*search_bits, RTEMS_RFS_BITMAP_ELEMENT_SET)) + { + while ((search_offset >= 0) + && (search_offset < rtems_rfs_bitmap_element_bits ())) + { + if (!rtems_rfs_bitmap_test (*search_bits, search_offset)) + { + /* + * Find the clear bit in the map. Update the search map and map if + * found. We may find none are spare if searching up from the seed. + */ + while ((map_offset >= 0) + && (map_offset < rtems_rfs_bitmap_element_bits ())) + { + if (!rtems_rfs_bitmap_test (*map_bits, map_offset)) + { + *map_bits = rtems_rfs_bitmap_set (*map_bits, 1 << map_offset); + if (rtems_rfs_bitmap_match(*map_bits, + RTEMS_RFS_BITMAP_ELEMENT_SET)) + *search_bits = rtems_rfs_bitmap_set (*search_bits, + 1 << search_offset); + control->free--; + *bit = test_bit; + *found = true; + rtems_rfs_buffer_mark_dirty (control->buffer); + return 0; + } + + if (test_bit == end_bit) + break; + + map_offset += direction; + test_bit += direction; + } + } + + map_bits += direction; + map_index += direction; + map_offset = direction > 0 ? 0 : rtems_rfs_bitmap_element_bits () - 1; + + test_bit = (map_index * rtems_rfs_bitmap_element_bits ()) + map_offset; + + search_offset += direction; + + if (((direction < 0) && (test_bit <= end_bit)) + || ((direction > 0) && (test_bit >= end_bit))) + break; + } + } + else + { + /* + * Move to the next search element. We need to determine the number of + * bits in the search offset that are being skipped so the map bits + * pointer can be updated. If we are moving down and we have a search + * offset of 0 then the search map adjustment is to the top bit of the + * pervious search bit's value. + * + * Align test_bit either up or down depending on the direction to next 32 + * bit boundary. + */ + rtems_rfs_bitmap_bit bits_skipped; + test_bit &= ~((1 << RTEMS_RFS_ELEMENT_BITS_POWER_2) - 1); + if (direction > 0) + { + bits_skipped = rtems_rfs_bitmap_element_bits () - search_offset; + test_bit += bits_skipped * rtems_rfs_bitmap_element_bits (); + map_offset = 0; + } + else + { + bits_skipped = search_offset + 1; + /* + * Need to remove 1 for the rounding up. The above rounds down and + * adds 1. Remember the logic is for subtraction. + */ + test_bit -= ((bits_skipped - 1) * rtems_rfs_bitmap_element_bits ()) + 1; + map_offset = rtems_rfs_bitmap_element_bits () - 1; + } + map_bits += direction * bits_skipped; + map_index += direction * bits_skipped; + } + + search_bits += direction; + search_offset = direction > 0 ? 0 : rtems_rfs_bitmap_element_bits () - 1; + } + while (((direction < 0) && (test_bit >= end_bit)) + || ((direction > 0) && (test_bit <= end_bit))); + + return 0; +} + +int +rtems_rfs_bitmap_map_alloc (rtems_rfs_bitmap_control* control, + rtems_rfs_bitmap_bit seed, + bool* allocated, + rtems_rfs_bitmap_bit* bit) +{ + rtems_rfs_bitmap_bit upper_seed; + rtems_rfs_bitmap_bit lower_seed; + rtems_rfs_bitmap_bit window; /* may become a parameter */ + int rc = 0; + + /* + * By default we assume the allocation failed. + */ + *allocated = false; + + /* + * The window is the number of bits we search over in either direction each + * time. + */ + window = RTEMS_RFS_BITMAP_SEARCH_WINDOW; + + /* + * Start from the seed and move in either direction. Search in window amounts + * of bits from the original seed above then below. That is search from the + * seed up then from the seed down a window number of bits, then repeat the + * process from the window distance from the seed, again above then + * below. Keep moving out until all bits have been searched. + */ + upper_seed = seed; + lower_seed = seed; + + /* + * If the upper and lower seed values have reached the limits of the bitmap + * we have searched all of the map. The seed may not be aligned to a window + * boundary so we may need to search a partial window and this may also not + * be balanced for the upper or lower seeds. We move to the limits, search + * then return false if no clear bits are found. + */ + while (((upper_seed >= 0) && (upper_seed < control->size)) + || ((lower_seed >= 0) && (lower_seed < control->size))) + { + /* + * Search up first so bits allocated in succession are grouped together. + */ + if (upper_seed < control->size) + { + *bit = upper_seed; + rc = rtems_rfs_search_map_for_clear_bit (control, bit, allocated, + window, 1); + if ((rc > 0) || *allocated) + break; + } + + if (lower_seed >= 0) + { + *bit = lower_seed; + rc = rtems_rfs_search_map_for_clear_bit (control, bit, allocated, + window, -1); + if ((rc > 0) || *allocated) + break; + } + + /* + * Do not bound the limits at the edges of the map. Do not update if an + * edge has been passed. + */ + if (upper_seed < control->size) + upper_seed += window; + if (lower_seed >= 0) + lower_seed -= window; + } + + return 0; +} + +int +rtems_rfs_bitmap_create_search (rtems_rfs_bitmap_control* control) +{ + rtems_rfs_bitmap_map search_map; + rtems_rfs_bitmap_map map; + size_t size; + rtems_rfs_bitmap_bit bit; + int rc; + + rc = rtems_rfs_bitmap_load_map (control, &map); + if (rc > 0) + return rc; + + control->free = 0; + search_map = control->search_bits; + size = control->size; + bit = 0; + + *search_map = RTEMS_RFS_BITMAP_ELEMENT_CLEAR; + while (size) + { + rtems_rfs_bitmap_element bits; + int available; + if (size < rtems_rfs_bitmap_element_bits ()) + { + bits = rtems_rfs_bitmap_merge (*map, + RTEMS_RFS_BITMAP_ELEMENT_SET, + rtems_rfs_bitmap_mask_section (0, size)); + available = size; + } + else + { + bits = *map; + available = rtems_rfs_bitmap_element_bits (); + } + + if (rtems_rfs_bitmap_match (bits, RTEMS_RFS_BITMAP_ELEMENT_SET)) + rtems_rfs_bitmap_set (*search_map, bit); + else + { + int b; + for (b = 0; b < available; b++) + if (!rtems_rfs_bitmap_test (bits, b)) + control->free++; + } + + size -= available; + + if (bit == rtems_rfs_bitmap_element_bits ()) + { + bit = 0; + search_map++; + *search_map = RTEMS_RFS_BITMAP_ELEMENT_CLEAR; + } + else + bit++; + map++; + } + + return 0; +} + +int +rtems_rfs_bitmap_open (rtems_rfs_bitmap_control* control, + rtems_rfs_file_system* fs, + rtems_rfs_buffer_handle* buffer, + size_t size, + rtems_rfs_buffer_block block) +{ + size_t elements = rtems_rfs_bitmap_elements (size); + + control->buffer = buffer; + control->fs = fs; + control->block = block; + control->size = size; + + elements = rtems_rfs_bitmap_elements (elements); + control->search_bits = malloc (elements * sizeof (rtems_rfs_bitmap_element)); + + if (!control->search_bits) + return ENOMEM; + + return rtems_rfs_bitmap_create_search (control); +} + +int +rtems_rfs_bitmap_close (rtems_rfs_bitmap_control* control) +{ + free (control->search_bits); + return 0; +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-bitmaps.h b/cpukit/libfs/src/rfs/rtems-rfs-bitmaps.h new file mode 100644 index 0000000000..8fe9ab7c9b --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-bitmaps.h @@ -0,0 +1,305 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Bitmap Routines. + * + * These functions manage bit maps. A bit map consists of the map of bit + * allocated in a block and a search map where a bit represents 32 actual + * bits. The search map allows for a faster search for an available bit as 32 + * search bits can checked in a test. + */ + +#if !defined (_RTEMS_RFS_BITMAPS_H_) +#define _RTEMS_RFS_BITMAPS_H_ + +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-file-system-fwd.h> +#include <rtems/rfs/rtems-rfs-trace.h> + +/** + * Define the way the bits are configured. We can have them configured as clear + * being 0 or clear being 1. This does not effect how masks are defined. A mask + * always has a 1 for set and 0 for clear. + */ +#define RTEMS_RFS_BITMAP_CLEAR_ZERO 0 + +#if RTEMS_RFS_BITMAP_CLEAR_ZERO +/* + * Bit set is a 1 and clear is 0. + */ +#define RTEMS_RFS_BITMAP_BIT_CLEAR 0 +#define RTEMS_RFS_BITMAP_BIT_SET 1 +#define RTEMS_RFS_BITMAP_ELEMENT_SET (RTEMS_RFS_BITMAP_ELEMENT_FULL_MASK) +#define RTEMS_RFS_BITMAP_ELEMENT_CLEAR (0) +#define RTEMS_RFS_BITMAP_SET_BITS(_t, _b) ((_t) | (_b)) +#define RTEMS_RFS_BITMAP_CLEAR_BITS(_t, _b) ((_t) & ~(_b)) +#define RTEMS_RFS_BITMAP_TEST_BIT(_t, _b) (((_t) & (1 << (_b))) != 0 ? true : false) +#else +/* + * Bit set is a 0 and clear is 1. + */ +#define RTEMS_RFS_BITMAP_BIT_CLEAR 1 +#define RTEMS_RFS_BITMAP_BIT_SET 0 +#define RTEMS_RFS_BITMAP_ELEMENT_SET (0) +#define RTEMS_RFS_BITMAP_ELEMENT_CLEAR (RTEMS_RFS_BITMAP_ELEMENT_FULL_MASK) +#define RTEMS_RFS_BITMAP_SET_BITS(_t, _b) ((_t) & ~(_b)) +#define RTEMS_RFS_BITMAP_CLEAR_BITS(_t, _b) ((_t) | (_b)) +#define RTEMS_RFS_BITMAP_TEST_BIT(_t, _b) (((_t) & (1 << (_b))) == 0 ? true : false) +#endif + +/** + * Invert a mask. Masks are always 1 for set and 0 for clear. + */ +#define RTEMS_RFS_BITMAP_INVERT_MASK(_mask) (~(_mask)) + +/** + * This is the full mask of the length of the element. A mask is always a 1 for + * set and 0 for clear. It is not effected by the state of + * RTEMS_RFS_BITMAP_CLEAR_ZERO. + */ +#define RTEMS_RFS_BITMAP_ELEMENT_FULL_MASK (0xffffffffUL) + +/** + * The bitmap search window. Searches occur around a seed in either direction + * for half the window. + */ +#define RTEMS_RFS_BITMAP_SEARCH_WINDOW (rtems_rfs_bitmap_element_bits () * 64) + +/** + * A bit in a map. + */ +typedef int32_t rtems_rfs_bitmap_bit; + +/** + * The basic element of a bitmap. A bitmap is manipulated by elements. + */ +typedef uint32_t rtems_rfs_bitmap_element; + +/** + * The power of 2 number of bits in the element. + */ +#define RTEMS_RFS_ELEMENT_BITS_POWER_2 (5) + +/** + * A bitmap or map is an array of bitmap elements. + */ +typedef rtems_rfs_bitmap_element* rtems_rfs_bitmap_map; + +/** + * The bitmap control is a simple way to manage the various parts of a bitmap. + */ +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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Block Position and Size Management. + * + * These functions manage the position in a block map as well as a size of data + * held in a block map. The position is the block count plus the offset into + * the last block where a block position of 0 and an offset of 0 is the start + * of a map. The size has a block count plus an offset, but the offset into the + * last block gives the actual size of the data in the map. This means a size + * will always have a block count greater than 0 when the file is not empty. A + * size offset of 0 and a non-zero block count means the length if aligned to + * the end of the block. For this reason there are 2 similar types so we know + * which set of rules are in use and the reason for this file. + */ + +#if !defined (_RTEMS_RFS_BLOCK_POS_H_) +#define _RTEMS_RFS_BLOCK_POS_H_ + +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> + +/** + * The block number is the same type as the inode block number. This makes sure + * the sizes of the types match. + */ +typedef rtems_rfs_inode_block rtems_rfs_block_no; + +/** + * The offset into a block. + */ +typedef uint32_t rtems_rfs_block_off; + +/** + * A block position is a block number times the block size plus the offset. The + * block field can be used hold a block number for the position as a look up + * cache. + */ +typedef struct rtems_rfs_block_pos_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Block Routines. + * + * These functions manage blocks in the RFS file system. A block is an area of + * the media and its size is set for a each specific media. The block size is + * set when the file system is set up and needs to be matched for it to be read + * correctly. + * + * Blocks are managed as groups. A block group or "group" is part of the total + * number of blocks being managed by the file system and exist to allow + * resources to localised. A file in a directory will be allocated blocks in + * the same group as the directory, and the blocks for the file will also be + * allocated in the same group. + * + * A group consist of a block bitmap, inodes and data blocks. The first block + * of the file system will hold the superblock. The block bitmap is a + * collection of blocks that hold a map of bits, one bit per block for each + * block in the group. When a file system is mounted the block bitmaps are read + * and a summary bit map is made. The summary bitmap has a single bit for 32 + * bits in the bitmap and is set when all 32 bits it maps to are set. This + * speeds up the search for a free block by a factor of 32. + */ + +#include <rtems/rfs/rtems-rfs-block.h> +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-group.h> +#include <rtems/rfs/rtems-rfs-inode.h> + +void +rtems_rfs_block_get_bpos (rtems_rfs_file_system* fs, + rtems_rfs_pos pos, + rtems_rfs_block_pos* bpos) +{ + bpos->bno = pos / rtems_rfs_fs_block_size (fs); + bpos->boff = pos % rtems_rfs_fs_block_size (fs); +} + +rtems_rfs_pos +rtems_rfs_block_get_pos (rtems_rfs_file_system* fs, + rtems_rfs_block_pos* bpos) +{ + 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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Block Management. + * + * These functions manage the blocks used in the file system. + */ + +#if !defined (_RTEMS_RFS_BLOCK_H_) +#define _RTEMS_RFS_BLOCK_H_ + +#include <rtems/rfs/rtems-rfs-block-pos.h> +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-file-system.h> + +/** + * Get a block number in the media format and return it in the host format. + * + * @param _h The buffer handle of the block. + * @param _b The block number index. + * @return uint32_t The block number. + */ +#define rtems_rfs_block_get_number(_h, _b) \ + ((rtems_rfs_block_no) \ + (rtems_rfs_read_u32 (rtems_rfs_buffer_data (_h) + \ + ((_b) * sizeof (rtems_rfs_block_no))))) + +/** + * Set a block number in the media format given a number in the host format. + * + * @param _h The buffer handle of the block. + * @param _b The block number index, ie the number of block number not the + * buffer offset. + * @param _n The block number. + */ +#define rtems_rfs_block_set_number(_h, _b, _n) \ + do { \ + rtems_rfs_write_u32 (rtems_rfs_buffer_data (_h) + \ + ((_b) * sizeof (rtems_rfs_block_no)), (_n)); \ + rtems_rfs_buffer_mark_dirty (_h); \ + } while (0) + +/** + * A block map manges the block lists that originate from an inode. The inode + * contains a number of block numbers. A block map takes those block numbers + * and manages them. + * + * The blocks cannot have all ones as a block number nor block 0. The block map + * is series of block numbers in a blocks. The size of the map determines the + * way the block numbers are stored. The map uses the following: + * + * @li @e Direct Access, + * @li @e Single Indirect Access, and + * @li @e Double Indirect Access. + * + * Direct access has the blocks numbers in the inode slots. The Single Indirect + * Access has block numbers in the inode slots that pointer to a table of block + * numbers that point to data blocks. The Double Indirect Access has block + * numbers in the inode that point to Single Indirect block tables. + * + * The inode can hold a number of Direct, Single Indirect, and Double Indirect + * block tables. The move from Direct to Single occurs then the block count in + * the map is above the number of slots in the inode. The move from Single to + * Double occurs when the map block count is greated than the block numbers per + * block multipled by the slots in the inode. The move from Single to Double + * occurs when the map block count is over the block numbers per block squared + * multipled by the number of slots in the inode. + * + * The block map can managed files of the follow size verses block size with 5 + * inode slots: + * + * @li 41,943,040 bytes for a 512 byte block size, + * @li 335,544,320 bytes for a 1024 byte block size, + * @li 2,684,354,560 bytes for a 2048 byte block size, and + * @li 21,474,836,480 bytes for a 4096 byte block size. + */ +typedef struct rtems_rfs_block_map_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Buffer Routines for the RTEMS libblock BD buffer cache. + * + */ + +#include <errno.h> + +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-file-system.h> + +#if RTEMS_RFS_USE_LIBBLOCK + +/** + * Show errors. + */ +#define RTEMS_RFS_BUFFER_ERRORS 0 + +int +rtems_rfs_buffer_bdbuf_request (rtems_rfs_file_system* fs, + rtems_rfs_buffer_block block, + bool read, + rtems_rfs_buffer** buffer) +{ + rtems_status_code sc; + int rc = 0; + + if (read) + sc = rtems_bdbuf_read (rtems_rfs_fs_device (fs), block, buffer); + else + sc = rtems_bdbuf_get (rtems_rfs_fs_device (fs), block, buffer); + + if (sc != RTEMS_SUCCESSFUL) + { +#if RTEMS_RFS_BUFFER_ERRORS + printf ("rtems-rfs: buffer-bdbuf-request: block=%lu: bdbuf-%s: %d: %s\n", + block, read ? "read" : "get", sc, rtems_status_text (sc)); +#endif + rc = EIO; + } + + return rc; +} + +int +rtems_rfs_buffer_bdbuf_release (rtems_rfs_buffer* buffer, + bool modified) +{ + rtems_status_code sc; + int rc = 0; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_RELEASE)) + printf ("rtems-rfs: bdbuf-release: block=%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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Buffer Routines. + * + */ + +#include <errno.h> + +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-file-system.h> + +#if !RTEMS_RFS_USE_LIBBLOCK + +/** + * Show errors. + */ +#define RTEMS_RFS_BUFFER_ERRORS 1 + +int +rtems_rfs_buffer_deviceio_request (rtems_rfs_buffer_handle* handle, + dev_t device, + rtems_rfs_buffer_block block, + bool read) +{ +} + +int +rtems_rfs_buffer_deviceio_release (rtems_rfs_buffer_handle* handle, + dev_t device) +{ +} + +int +rtems_rfs_buffer_deviceio_handle_open (rtems_rfs_buffer_handle* handle, + dev_t device) +{ +} + +int +rtems_rfs_buffer_device_handle_close (rtems_rfs_buffer_handle* handle, + dev_t device) +{ +} + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-buffer.c b/cpukit/libfs/src/rfs/rtems-rfs-buffer.c new file mode 100644 index 0000000000..ee1a7f6bde --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-buffer.c @@ -0,0 +1,478 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Buffer Routines. + * + */ + +#include <errno.h> + +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-file-system.h> + +/** + * Scan the chain for a buffer that matches the block number. + * + * @param chain The chain to scan. + * @param count The number of items on the chain. + * @param block The block number to find. + * @return rtems_rfs_buffer* The buffer if found else NULL. + */ +static rtems_rfs_buffer* +rtems_rfs_scan_chain (rtems_chain_control* chain, + uint32_t* count, + rtems_rfs_buffer_block block) +{ + rtems_rfs_buffer* buffer; + rtems_chain_node* node; + + node = rtems_chain_last (chain); + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS)) + printf ("rtems-rfs: buffer-scan: count=%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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Buffer Management. + * + * These functions map blocks to the media interface layers. + */ + +#if !defined (_RTEMS_RFS_BUFFER_H_) +#define _RTEMS_RFS_BUFFER_H_ + +#include <errno.h> + +#include <rtems/rfs/rtems-rfs-file-system-fwd.h> +#include <rtems/rfs/rtems-rfs-trace.h> + +/** + * Define the method used to interface to the buffers. It can be libblock or + * device I/O. The libblock interface is to the RTEMS cache and block devices + * and device I/O accesses the media via a device file handle. + */ +#if defined (__rtems__) +#define RTEMS_RFS_USE_LIBBLOCK 1 +#endif + +/** + * The RTEMS RFS I/O Layering. + */ +#if RTEMS_RFS_USE_LIBBLOCK +#include <rtems/bdbuf.h> +#include <rtems/error.h> + +typedef rtems_blkdev_bnum rtems_rfs_buffer_block; +typedef rtems_bdbuf_buffer rtems_rfs_buffer; +#define rtems_rfs_buffer_io_request rtems_rfs_buffer_bdbuf_request +#define rtems_rfs_buffer_io_release rtems_rfs_buffer_bdbuf_release + +/** + * Request a buffer from the RTEMS libblock BD buffer cache. + */ +int rtems_rfs_buffer_bdbuf_request (rtems_rfs_file_system* fs, + rtems_rfs_buffer_block block, + bool read, + rtems_rfs_buffer** buffer); +/** + * Release a buffer to the RTEMS libblock BD buffer cache. + */ +int rtems_rfs_buffer_bdbuf_release (rtems_rfs_buffer* handle, + bool modified); +#else /* Device I/O */ +typedef uint32_t rtems_rfs_buffer_block; +typedef struct rtems_rfs_buffer_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Data. + * + * Access data in the correct byte order for the specific target we are running + * on. + * + * @todo Make direct access on matching byte ordered targets. + */ + +#if !defined (_RTEMS_RFS_DATA_H_) +#define _RTEMS_RFS_DATA_H_ + +#include <stdint.h> + +/** + * Helper function to make sure we have a byte pointer. + */ +#define rtems_rfs_data_ptr(_d) ((uint8_t*)(_d)) + +/** + * 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 <rtems/rfs/rtems-rfs-dir-hash.h> + +#ifdef __rtems__ +# include <machine/endian.h> /* attempt to define endianness */ +#endif +#ifdef linux +# include <endian.h> /* attempt to define endianness */ +#endif + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* + ------------------------------------------------------------------------------- + mix -- mix 3 32-bit values reversibly. + + This is reversible, so any information in (a,b,c) before mix() is still in + (a,b,c) after mix(). + + If four pairs of (a,b,c) inputs are run through mix(), or through mix() in + reverse, there are at least 32 bits of the output that are sometimes the same + for one pair and different for another pair. This was tested for: + + * pairs that differed by one bit, by two bits, in any combination of top bits + of (a,b,c), or in any combination of bottom bits of (a,b,c). + + * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the + output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly + produced by subtraction) look like a single 1-bit difference. + + * the base values were pseudorandom, all zero but one bit set, or all zero + plus a counter that starts at zero. + + Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that satisfy this + are: + + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 + + Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing for "differ" defined + as + with a one-bit base and a two-bit delta. I used + http://burtleburtle.net/bob/hash/avalanche.html to choose the operations, + constants, and arrangements of the variables. + + This does not achieve avalanche. There are input bits of (a,b,c) that fail + to affect some output bits of (a,b,c), especially of a. The most thoroughly + mixed value is c, but it doesn't really even achieve avalanche in c. + + This allows some parallelism. Read-after-writes are good at doubling the + number of bits affected, so the goal of mixing pulls in the opposite + direction as the goal of parallelism. I did what I could. Rotates seem to + cost as much as shifts on every machine I could lay my hands on, and rotates + are much kinder to the top and bottom bits, so I used rotates. + ------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ + { \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ + } + +/* + ------------------------------------------------------------------------------- + final -- final mixing of 3 32-bit values (a,b,c) into c + + Pairs of (a,b,c) values differing in only a few bits will usually produce + values of c that look totally different. This was tested for + + * pairs that differed by one bit, by two bits, in any combination of top bits + of (a,b,c), or in any combination of bottom bits of (a,b,c). + + * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the + output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly + produced by subtraction) look like a single 1-bit difference. * the base + values were pseudorandom, all zero but one bit set, or all zero plus a + counter that starts at zero. + + These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 + and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 + ------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ + { \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ + } + +/** + * The follow is the documentation from Bob Jenkin's hash function: + * + * http://burtleburtle.net/bob/hash/doobs.html + * + * The function hashlittle() has been renamed. + * + * hashlittle() -- hash a variable-length key into a 32-bit value + * + * k : the key (the unaligned variable-length array of bytes) + * length : the length of the key, counting by bytes + * initval : can be any 4-byte value + * + * Returns a 32-bit value. Every bit of the key affects every bit of the + * return value. Two keys differing by one or two bits will have totally + * different hash values. + * + * The best hash table sizes are powers of 2. There is no need to do mod a + * prime (mod is sooo slow!). If you need less than 32 bits, use a bitmask. + * For example, if you need only 10 bits, do h = (h & hashmask(10)); In which + * case, the hash table should have hashsize(10) elements. + * + * If you are hashing n strings (uint8_t **)k, do it like this: for (i=0, h=0; + * i<n; ++i) h = hashlittle( k[i], len[i], h); + * + * By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this + * code any way you wish, private, educational, or commercial. It's free. + * + * Use for hash table lookup, or anything where one collision in 2^^32 is + * acceptable. Do NOT use for cryptographic purposes. +*/ + +#define initval (20010928) +uint32_t +rtems_rfs_dir_hash (const void *key, size_t length) +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + /*const uint8_t *k8;*/ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + diff --git a/cpukit/libfs/src/rfs/rtems-rfs-dir-hash.h b/cpukit/libfs/src/rfs/rtems-rfs-dir-hash.h new file mode 100644 index 0000000000..ca761dd0cc --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-dir-hash.h @@ -0,0 +1,34 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Directory Hash provides a 32bit hash of a string. This is + * used to search a directory. + */ + +#if !defined (_RTEMS_RFS_DIR_HASH_H_) +#define _RTEMS_RFS_DIR_HAS_H_ + +#include <stddef.h> +#include <stdint.h> + +/** + * Compute a hash of the key over the length of string. + * + * @param key The key to calculate the hash of. + * @param length The length of the key in bytes. + * @return uint32_t The hash. + */ +uint32_t rtems_rfs_dir_hash (const void *key, size_t length); + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-dir.c b/cpukit/libfs/src/rfs/rtems-rfs-dir.c new file mode 100644 index 0000000000..8813fb751e --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-dir.c @@ -0,0 +1,742 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Directory Routines. + * + * These functions manage blocks in the directory format. A directory entry is + * a variable length record in the block. The entry consists of a length, hash + * and the string. The length allows the next entry to be located and the hash + * allows a simple check to be performed wihtout a string compare. Directory + * entries do not span a block and removal of an entry results in the space in + * the block being compacted and the spare area being initialised to ones. + * + * The maximum length can be 1 or 2 bytes depending on the value in the + * superblock. + */ + +#include <rtems/rfs/rtems-rfs-block.h> +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-trace.h> +#include <rtems/rfs/rtems-rfs-dir.h> +#include <rtems/rfs/rtems-rfs-dir-hash.h> + +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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Directory Support + * + * This file provides the directory support functions. + */ + +#if !defined (_RTEMS_RFS_DIR_H_) +#define _RTEMS_RFS_DIR_H_ + +#include <dirent.h> + +#include <rtems/libio_.h> + +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> + +/** + * Test if the path provided is a current directory. + * + * @param _p Pointer to the path string. + * @return bool True if the path is a current directory. + */ +#define rtems_rfs_current_dir(_p) \ + ((_p[0] == '.') && ((_p[1] == '\0') || rtems_filesystem_is_separator (_p[1]))) + +/** + * Test if the path provided is a parent directory. + * + * @param _p Pointer to the path string. + * @return bool True if the path is a parent directory. + */ +#define rtems_rfs_parent_dir(_p) \ + ((_p[0] == '.') && (_p[1] == '.') && \ + ((_p[2] == '\0') || rtems_filesystem_is_separator (_p[2]))) + +/** + * Define the offsets of the fields of a directory entry. + */ +#define RTEMS_RFS_DIR_ENTRY_INO (0) /**< The ino offset in a directory + * entry. */ +#define RTEMS_RFS_DIR_ENTRY_HASH (4) /**< The hash offset in a directory + * entry. The hash is 32bits. We need at + * least 16bits and given the length and + * ino field are 4 the extra 2 bytes is + * not a big overhead.*/ +#define RTEMS_RFS_DIR_ENTRY_LEN (8) /**< The length offset in a directory + * entry. */ + +/** + * The length of the directory entry header. + */ +#define RTEMS_RFS_DIR_ENTRY_SIZE (4 + 4 + 2) + +/** + * The length when the remainder of the directory block is empty. + */ +#define RTEMS_RFS_DIR_ENTRY_EMPTY (0xffff) + +/** + * Return the hash of the entry. + * + * @param _e Pointer to the directory entry. + * @return uint32_t The hash. + */ +#define rtems_rfs_dir_entry_hash(_e) \ + rtems_rfs_read_u32 (_e + RTEMS_RFS_DIR_ENTRY_HASH) + +/** + * Set the hash of the entry. + * + * @param _e Pointer to the directory entry. + * @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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Data forward decl. + */ + +#if !defined (_RTEMS_RFS_FILE_SYSTEM_FWD_H_) +#define _RTEMS_RFS_FILE_SYSTEM_FWD_H_ + +/** + * Forward reference to the file system data. + */ +struct rtems_rfs_file_system_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Open + * + * Open the file system by reading the superblock and then the group data. + */ + +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> +#include <rtems/rfs/rtems-rfs-trace.h> + +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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Data + * + * This file defines the file system data. + */ + +#if !defined (_RTEMS_RFS_FILE_SYSTEM_H_) +#define _RTEMS_RFS_FILE_SYSTEM_H_ + +#include <rtems/rfs/rtems-rfs-group.h> + +/** + * Superblock offsets and values. + */ +#define RTEMS_RFS_SB_OFFSET_MAGIC (0) +#define RTEMS_RFS_SB_MAGIC (0x28092001) +#define RTEMS_RFS_SB_OFFSET_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems File Routines. + * + * These functions manage files. + */ + +#include <rtems/rfs/rtems-rfs-block-pos.h> +#include <rtems/rfs/rtems-rfs-file.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-trace.h> + +int +rtems_rfs_file_open (rtems_rfs_file_system* fs, + rtems_rfs_ino ino, + uint32_t flags, + rtems_rfs_file_handle** file) +{ + rtems_rfs_file_handle* handle; + rtems_rfs_file_shared* shared; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN)) + printf ("rtems-rfs: file-open: ino=%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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System File Support + * + * This file provides the support functions. + */ + +#if !defined (_RTEMS_RFS_FILE_H_) +#define _RTEMS_RFS_FILE_H_ + +#include <rtems/libio_.h> + +#include <rtems/rfs/rtems-rfs-block.h> +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> + +/** + * File data that is shared by various file handles accessing the same file. We + * hold various inode values common to the file that can change frequently so + * the inode is not thrashed yet we meet the requirements of the POSIX + * standard. The stat call needs to check the shared file data. + */ +typedef struct rtems_rfs_file_shared_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Format + * + * Format the file system ready for use. + */ + +#include <stdlib.h> +#include <stdio.h> + +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> +#include <rtems/rtems-rfs-format.h> +#include <rtems/rfs/rtems-rfs-dir.h> + +/** + * Return the number of gigabytes. + */ +#define GIGS(_g) ((_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Format. + * + * This function lets you format a disk in the RFS format. + */ + +#if !defined (_RTEMS_RFS_FORMAT_H_) +#define _RTEMS_RFS_FORMAT_H_ + +#include <stddef.h> +#include <stdbool.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rtems/rfs/rtems-rfs-trace.h> + +/** + * RFS File System Configuration data used to format the file system. For + * default values leave the field set to 0. + */ +typedef struct rtems_rfs_format_config_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Group Routines. + * + * These functions open and close a group as well as manage bit allocations + * within a group. + */ + +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-group.h> + +int +rtems_rfs_group_open (rtems_rfs_file_system* fs, + rtems_rfs_buffer_block base, + size_t size, + size_t inodes, + rtems_rfs_group* group) +{ + int rc; + + if (base >= rtems_rfs_fs_blocks (fs)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_GROUP_OPEN)) + printf ("rtems-rfs: group-open: base outside file system range: %d: %s\n", + EIO, strerror (EIO)); + return EIO; + } + + if ((base + size) >= rtems_rfs_fs_blocks (fs)) + size = rtems_rfs_fs_blocks (fs) - base; + + 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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Group Management. + * + * These functions manage the groups used in the file system. + */ + +#if !defined (_RTEMS_RFS_GROUP_H_) +#define _RTEMS_RFS_GROUP_H_ + +#include <rtems/rfs/rtems-rfs-trace.h> +#include <rtems/rfs/rtems-rfs-bitmaps.h> +#include <rtems/rfs/rtems-rfs-buffer.h> + +/** + * Block allocations for a group on disk. + */ +#define RTEMS_RFS_GROUP_BLOCK_BITMAP_BLOCK (0) +#define RTEMS_RFS_GROUP_INODE_BITMAP_BLOCK (1) +#define RTEMS_RFS_GROUP_INODE_BLOCK (2) + +/** + * A group is a selection of blocks on the disk. Typically the number of blocks + * in a group is determined by the number of bits a block holds. This makes the + * bit allocator for blocks in the group simpler plus is allows a simple way to + * localise access to files and directories. + */ +typedef struct rtems_rfs_group_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Inode Routines. + * + * These functions manage inodes in the RFS file system. An inode is part of a + * block that reside after the bitmaps in the group. + */ + +#include <rtems/rfs/rtems-rfs-block.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> +#include <rtems/rfs/rtems-rfs-dir.h> + +int +rtems_rfs_inode_alloc (rtems_rfs_file_system* fs, + rtems_rfs_bitmap_bit goal, + rtems_rfs_ino* ino) +{ + rtems_rfs_bitmap_bit bit; + int rc; + rc = rtems_rfs_group_bitmap_alloc (fs, goal, true, &bit); + *ino = bit; + return rc; +} + +int +rtems_rfs_inode_free (rtems_rfs_file_system* fs, + rtems_rfs_ino ino) +{ + rtems_rfs_bitmap_bit bit; + bit = ino; + return rtems_rfs_group_bitmap_free (fs, true, bit); +} + +int +rtems_rfs_inode_open (rtems_rfs_file_system* fs, + rtems_rfs_ino ino, + rtems_rfs_inode_handle* handle, + bool load) +{ + int group; + int gino; + int index; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_OPEN)) + printf ("rtems-rfs: inode-open: ino: %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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Information Node. + * + * The information nodes hold the data about all nodes in the file system. + */ + +#if !defined (_RTEMS_RFS_INODE_H_) +#define _RTEMS_RFS_INODE_H_ + +#include <sys/stat.h> + +#include <rtems/rfs/rtems-rfs-data.h> +#include <rtems/rfs/rtems-rfs-file-system.h> + +/** + * The RFS mode definitions. Currently map to the C library ones. + */ +#define RTEMS_RFS_S_ISUID S_ISUID /**< Set user id on execution */ +#define RTEMS_RFS_S_ISGID S_ISGID /**< Set group id on execution */ +#define RTEMS_RFS_S_ISVTX S_ISVTX /**< Save swapped text even after use */ +#define RTEMS_RFS_S_IREAD S_IREAD /**< Read permission, owner */ +#define RTEMS_RFS_S_IWRITE S_IWRITE /**< Write permission, owner */ +#define RTEMS_RFS_S_IEXEC S_IEXEC /**< Execute/search permission, owner */ +#define RTEMS_RFS_S_ENFMT S_ENFMT /**< Enforcement-mode locking */ +#define RTEMS_RFS_S_IFMT S_IFMT /**< Type of file */ +#define RTEMS_RFS_S_IFDIR S_IFDIR /**< Directory */ +#define RTEMS_RFS_S_IFCHR S_IFCHR /**< Character special */ +#define RTEMS_RFS_S_IFBLK S_IFBLK /**< Block special */ +#define RTEMS_RFS_S_IFREG S_IFREG /**< Regular */ +#define RTEMS_RFS_S_IFLNK S_IFLNK /**< Symbolic link */ +#define RTEMS_RFS_S_IFSOCK S_IFSOCK /**< Socket */ +#define RTEMS_RFS_S_IFIFO S_IFIFO /**< Fifo */ +#define RTEMS_RFS_S_IRWXU S_IRWXU +#define RTEMS_RFS_S_IRUSR S_IRUSR /**< Read permission, owner */ +#define RTEMS_RFS_S_IWUSR S_IWUSR /**< Write permission, owner */ +#define RTEMS_RFS_S_IXUSR S_IXUSR /**< Execute/search permission, owner */ +#define RTEMS_RFS_S_IRWXG S_IRWXG +#define RTEMS_RFS_S_IRGRP S_IRGRP /**< Read permission, group */ +#define RTEMS_RFS_S_IWGRP S_IWGRP /**< Write permission, grougroup */ +#define RTEMS_RFS_S_IXGRP S_IXGRP /**< Execute/search permission, group */ +#define RTEMS_RFS_S_IRWXO S_IRWXO +#define RTEMS_RFS_S_IROTH S_IROTH /**< Read permission, other */ +#define RTEMS_RFS_S_IWOTH S_IWOTH /**< Write permission, other */ +#define RTEMS_RFS_S_IXOTH S_IXOTH /**< Execute/search permission, other */ + +#define RTEMS_RFS_S_ISBLK(m) S_ISBLK(m) +#define RTEMS_RFS_S_ISCHR(m) S_ISCHR(m) +#define RTEMS_RFS_S_ISDIR(m) S_ISDIR(m) +#define RTEMS_RFS_S_ISFIFO(m) S_ISFIFO(m) +#define RTEMS_RFS_S_ISREG(m) S_ISREG(m) +#define RTEMS_RFS_S_ISLNK(m) S_ISLNK(m) +#define RTEMS_RFS_S_ISSOCK(m) S_ISSOCK(m) + +/** + * Permissions of a symlink. + */ +#define RTEMS_RFS_S_SYMLINK \ + RTEMS_RFS_S_IFLNK | RTEMS_RFS_S_IRWXU | RTEMS_RFS_S_IRWXG | RTEMS_RFS_S_IRWXO + +/** + * The inode number or ino. + */ +typedef uint32_t rtems_rfs_ino; + +/** + * The time in the file system. + */ +typedef uint32_t rtems_rfs_time; + +/** + * The size of a block value on disk. This include the inodes and indirect + * tables. + */ +typedef uint32_t rtems_rfs_inode_block; + +/** + * The size of the data name field in the inode. + */ +#define RTEMS_RFS_INODE_DATA_NAME_SIZE \ + (RTEMS_RFS_INODE_BLOCKS * sizeof (rtems_rfs_inode_block)) + +/** + * The inode. + */ +typedef struct rtems_rfs_inode_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Link Routines. + * + * These functions manage links. A link is the addition of a directory entry in + * a parent directory and incrementing the links count in the inode. + */ + +#include <rtems/rfs/rtems-rfs-block.h> +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-trace.h> +#include <rtems/rfs/rtems-rfs-dir.h> +#include <rtems/rfs/rtems-rfs-dir-hash.h> + +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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Link Support + * + * This file provides the link support functions. + */ + +#if !defined (_RTEMS_RFS_LINK_H_) +#define _RTEMS_RFS_LINK_H_ + +#include <dirent.h> + +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> + +/** + * 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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Mutex. + */ + +#include <rtems/rfs/rtems-rfs-mutex.h> + +#if __rtems__ +/** + * RTEMS_RFS Mutex Attributes + * + * @warning Do not configure as inherit priority. If a driver is in the driver + * initialisation table this locked semaphore will have the IDLE task + * as the holder and a blocking task will raise the priority of the + * IDLE task which can cause unsual side effects like not work. + */ +#define RTEMS_RFS_MUTEX_ATTRIBS \ + (RTEMS_PRIORITY | RTEMS_SIMPLE_BINARY_SEMAPHORE | \ + RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL) +#endif + +int +rtems_rfs_mutex_create (rtems_rfs_mutex* mutex) +{ +#if __rtems__ + rtems_status_code sc; + sc = rtems_semaphore_create (rtems_build_name ('R', 'F', 'S', 'm'), + 1, RTEMS_RFS_MUTEX_ATTRIBS, 0, + mutex); + if (sc != RTEMS_SUCCESSFUL) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_MUTEX)) + printf ("rtems-rfs: mutex: open failed: %s\n", + rtems_status_text (sc)); + return EIO; + } +#endif + return 0; +} + +int +rtems_rfs_mutex_destroy (rtems_rfs_mutex* mutex) +{ +#if __rtems__ + rtems_status_code sc; + sc = rtems_semaphore_delete (*mutex); + if (sc != RTEMS_SUCCESSFUL) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_MUTEX)) + printf ("rtems-rfs: mutex: close failed: %s\n", + rtems_status_text (sc)); + return EIO; + } +#endif + return 0; +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-mutex.h b/cpukit/libfs/src/rfs/rtems-rfs-mutex.h new file mode 100644 index 0000000000..12e7fee921 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-mutex.h @@ -0,0 +1,108 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Mutex. + * + * It may be suprising we abstract this for the RTEMS file system but this code + * is designed to be run on host operating systems. + */ + +#if !defined (_RTEMS_RFS_MUTEX_H_) +#define _RTEMS_RFS_MUTEX_H_ + +#include <errno.h> + +#include <rtems/rfs/rtems-rfs-trace.h> + +#if __rtems__ +#include <rtems.h> +#include <rtems/error.h> +#endif + +/** + * RFS Mutex type. + */ +#if __rtems__ +typedef rtems_id rtems_rfs_mutex; +#else +typedef uint32_t rtems_rfs_mutex; /* place holder */ +#endif + +/** + * Create the mutex. + * + * @param mutex Reference to the mutex handle returned to the caller. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_mutex_create (rtems_rfs_mutex* mutex); + +/** + * Create the mutex. + * + * @param mutex Reference to the mutex handle returned to the caller. + * @return int The error number (errno). No error if 0. + */ +int rtems_rfs_mutex_destroy (rtems_rfs_mutex* mutex); + +/** + * Lock the mutex. + * + * @param mutex The mutex to lock. + * @retval true The mutex is locked. + * @retval false The mutex could not be locked. + */ +static inline int +rtems_rfs_mutex_lock (rtems_rfs_mutex* mutex) +{ +#if __rtems__ + rtems_status_code sc = rtems_semaphore_obtain (*mutex, RTEMS_WAIT, 0); + if (sc != RTEMS_SUCCESSFUL) + { +#if RTEMS_RFS_TRACE + if (rtems_rfs_trace (RTEMS_RFS_TRACE_MUTEX)) + printf ("rtems-rfs: mutex: obtain failed: %s\n", + rtems_status_text (sc)); +#endif + return EIO; + } +#endif + return 0; +} + +/** + * Unlock the mutex. + * + * @param mutex The mutex to unlock. + * @retval true The mutex is unlocked. + * @retval false The mutex could not be unlocked. + */ +static inline int +rtems_rfs_mutex_unlock (rtems_rfs_mutex* mutex) +{ +#if __rtems__ + rtems_status_code sc = rtems_semaphore_release (*mutex); + if (sc != RTEMS_SUCCESSFUL) + { +#if RTEMS_RFS_TRACE + if (rtems_rfs_trace (RTEMS_RFS_TRACE_MUTEX)) + printf ("rtems-rfs: mutex: release failed: %s\n", + rtems_status_text (sc)); +#endif + return EIO; + } +#endif + return 0; +} + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-rtems-dev.c b/cpukit/libfs/src/rfs/rtems-rfs-rtems-dev.c new file mode 100644 index 0000000000..cebd730ed7 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems-dev.c @@ -0,0 +1,271 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS RFS Device Interface. + * + * This file contains the set of handlers used to map operations on RFS device + * nodes onto calls to the RTEMS Classic API IO Manager. + * + */ + +#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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS RFS Directory Access Routines + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> + +#include <rtems/rfs/rtems-rfs-dir.h> +#include <rtems/rfs/rtems-rfs-link.h> +#include "rtems-rfs-rtems.h" + +/** + * This rountine will verify that the node being opened as a directory is in + * fact a directory node. If it is then the offset into the directory will be + * set to 0 to position to the first directory entry. + * + * @param iop + * @param pathname + * @param flag + * @param mode + * @@return int + */ +static int +rtems_rfs_rtems_dir_open (rtems_libio_t* iop, + const char* pathname, + uint32_t flag, + uint32_t mode) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (&iop->pathinfo); + rtems_rfs_ino ino = rtems_rfs_rtems_get_iop_ino (iop); + rtems_rfs_inode_handle inode; + int rc; + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("dir_open: opening inode", rc); + } + + if (!RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode))) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("dir_open: not dir", ENOTDIR); + } + + iop->offset = 0; + + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return 0; +} + +/** + * This routine will be called by the generic close routine to cleanup any + * resources that have been allocated for the management of the file + * + * @param iop + * @retval 0 Always no error. + */ +static int +rtems_rfs_rtems_dir_close (rtems_libio_t* iop) +{ + /* + * The RFS does not hold any resources. Nothing to do. + */ + return 0; +} + +/** + * This routine will read the next directory entry based on the directory + * offset. The offset should be equal to -n- time the size of an individual + * dirent structure. If n is not an integer multiple of the sizeof a dirent + * structure, an integer division will be performed to determine directory + * entry that will be returned in the buffer. Count should reflect -m- times + * the sizeof dirent bytes to be placed in the buffer. If there are not -m- + * dirent elements from the current directory position to the end of the + * exisiting file, the remaining entries will be placed in the buffer and the + * returned value will be equal to -m actual- times the size of a directory + * entry. + */ +static ssize_t +rtems_rfs_rtems_dir_read (rtems_libio_t* iop, + void* buffer, + size_t count) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (&iop->pathinfo); + rtems_rfs_ino ino = rtems_rfs_rtems_get_iop_ino (iop); + rtems_rfs_inode_handle inode; + struct dirent* dirent; + 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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS RFS File Handlers + * + * This file contains the set of handlers used to process operations on + * RFS file nodes. + */ + +#include <rtems/rfs/rtems-rfs-file.h> +#include "rtems-rfs-rtems.h" + +/** + * This routine processes the open() system call. Note that there is nothing + * special to be done at open() time. + * + * @param iop + * @param pathname + * @param flag + * @param mode + * @return int + */ + +static int +rtems_rfs_rtems_file_open (rtems_libio_t* iop, + const char* pathname, + uint32_t flag, + uint32_t mode) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (&iop->pathinfo); + rtems_rfs_ino ino = rtems_rfs_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * Set of utility functions to support RTEMS RFS on RTEMS. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> + +#include "rtems-rfs-rtems.h" + +bool +rtems_rfs_rtems_eval_perms (rtems_rfs_inode_handle* inode, int flags) +{ + uid_t st_uid; + gid_t st_gid; + uint16_t uid; + uint16_t gid; + uint16_t mode; + int flags_to_test; + + uid = rtems_rfs_inode_get_uid (inode); + gid = rtems_rfs_inode_get_gid (inode); + mode = rtems_rfs_inode_get_mode (inode); + +#if defined (RTEMS_POSIX_API) + st_uid = geteuid (); + st_gid = getegid (); +#else + st_uid = uid; + st_gid = gid; +#endif + + /* + * Check if I am owner or a group member or someone else. + */ + flags_to_test = flags; + + if ((st_uid == 0) || (st_uid == uid)) + flags_to_test |= flags << 6; + if ((st_uid == 0) || (st_gid == gid)) + flags_to_test |= flags << 3; + else + /* must be other - already set above */; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PERMS)) + printf ("rtems-rfs: eval-perms: uid=%d gid=%d iuid=%d igid=%d " \ + "flags=%o flags_to_test=%o mode=%o (%o)\n", + st_uid, st_gid, uid, gid, + flags, flags_to_test, mode & 0777, + flags_to_test & (mode & 0777)); + + /* + * If all of the flags are set we have permission + * to do this. + */ + if ((flags_to_test & (mode & 0777)) != 0) + return true; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PERMS)) + printf("rtems-rfs: eval-perms: perms failed\n"); + + return false; +} + +/* + * The following sets the handlers based on the type of inode. + */ + +bool +rtems_rfs_rtems_set_handlers (rtems_filesystem_location_info_t* loc, + rtems_rfs_inode_handle* inode) +{ + uint16_t mode = rtems_rfs_inode_get_mode (inode); + loc->handlers = NULL; + if (RTEMS_RFS_S_ISDIR (mode)) + loc->handlers = rtems_rfs_rtems_handlers (dir); + else if (RTEMS_RFS_S_ISCHR (mode) || RTEMS_RFS_S_ISBLK(mode)) + loc->handlers = rtems_rfs_rtems_handlers (device); + else if (RTEMS_RFS_S_ISLNK (mode)) + loc->handlers = rtems_rfs_rtems_handlers (link); + else if (RTEMS_RFS_S_ISREG (mode)) + loc->handlers = rtems_rfs_rtems_handlers (file); + else + { + printf ("rtems-rfs: mode type unknown: %04x\n", mode); + return false; + } + return true; +} + +uint16_t +rtems_rfs_rtems_imode (mode_t mode) +{ + /* + * Mapping matches RTEMS so no need to change. + */ + return mode; +} + +mode_t +rtems_rfs_rtems_mode (int imode) +{ + /* + * Mapping matches RTEMS so no need to change. + */ + return imode; +} + +/* + * Only provide if there is no macro version. + */ +#if !defined (rtems_rfs_rtems_error) +int +rtems_rfs_rtems_error (const char* mesg, int error) +{ + if (error) + printf ("rtems-rfs: %s: %d: %s\n", mesg, error, strerror (error)); + errno = error; + return error == 0 ? 0 : -1; +} +#endif + +#if RTEMS_RFS_RTEMS_TRACE +static uint32_t rtems_rfs_rtems_trace_mask; + +bool +rtems_rfs_rtems_trace (uint32_t mask) +{ + bool result = false; + if (mask & rtems_rfs_rtems_trace_mask) + result = true; + return result; +} + +void +rtems_rfs_trace_rtems_set_mask (uint32_t mask) +{ + rtems_rfs_rtems_trace_mask |= mask; +} + +void +rtems_rfs_trace_rtems_clear_mask (uint32_t mask) +{ + rtems_rfs_rtems_trace_mask &= ~mask; +} + +int +rtems_rfs_rtems_trace_shell_command (int argc, char *argv[]) +{ + const char* table[] = + { + "error-msgs", + "eval-path" + "eval-for-make", + "eval-perms", + "mknod", + "rmnod", + "link", + "unlink", + "chown", + "readlink", + "fchmod", + "stat", + "dir-rmnod", + "file-open", + "file-close", + "file-read", + "file-write", + "file-lseek", + "file-ftrunc" + }; + + bool set = true; + int arg; + int t; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + switch (argv[arg][1]) + { + case 'h': + printf ("usage: %s [-hl] [set/clear] [flags]\n", argv[0]); + return 0; + case 'l': + printf ("%s: valid flags to set or clear are:\n", argv[0]); + for (t = 0; t < (sizeof (table) / sizeof (const char*)); t++) + printf (" %s\n", table[t]); + return 0; + default: + printf ("error: unknown option\n"); + return 1; + } + } + else + { + uint32_t value = 0; + if (strcmp (argv[arg], "set") == 0) + set = true; + if (strcmp (argv[arg], "clear") == 0) + set = false; + else if (strcmp (argv[arg], "all") == 0) + value = RTEMS_RFS_RTEMS_DEBUG_ALL; + else + { + for (t = 0; t < (sizeof (table) / sizeof (const char*)); t++) + { + if (strcmp (argv[arg], table[t]) == 0) + { + value = 1 << t; + break; + } + } + } + + if (set) + rtems_rfs_rtems_trace_mask |= value; + else + rtems_rfs_rtems_trace_mask &= ~value; + } + } + + return 0; +} + +#endif diff --git a/cpukit/libfs/src/rfs/rtems-rfs-rtems.c b/cpukit/libfs/src/rfs/rtems-rfs-rtems.c new file mode 100644 index 0000000000..60f3fdf429 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems.c @@ -0,0 +1,1235 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System Interface for RTEMS. + */ + +#include <stdlib.h> + +#include <rtems/rfs/rtems-rfs-file.h> +#include <rtems/rfs/rtems-rfs-dir.h> +#include <rtems/rfs/rtems-rfs-link.h> +#include "rtems-rfs-rtems.h" + +/** + * The libio permissions for read/execute. + */ +#define RTEMS_LIBIO_PERMS_RX (RTEMS_LIBIO_PERMS_SEARCH | RTEMS_LIBIO_PERMS_READ) +/** + * The libio permissions for write/execute. + */ +#define RTEMS_LIBIO_PERMS_WX (RTEMS_LIBIO_PERMS_SEARCH | RTEMS_LIBIO_PERMS_WRITE) + +/** + * Evaluate the path to a node that wishes to be accessed. The pathloc is + * returned with the ino to the node to be accessed. + * + * The routine starts from the root stripping away any leading path separators + * breaking the path up into the node names and checking an inode exists for + * that node name. Permissions are checked to insure access to the node is + * allowed. A path to a node must be accessable all the way even if the end + * result is directly accessable. As a user on Linux try "ls /root/../tmp" and + * you will see if fails. + * + * The whole process is complicated by crossmount paths where we head down into + * this file system only to return to the top and out to a another mounted file + * system. For example we are mounted on '/e' and the user enters "ls + * /e/a/b/../../dev". We need to head down then back up. + * + * @param path + * @param pathlen + * @param flags + * @param pathloc + */ +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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System RTEMS Header file. + * + * This file is not to be installed. It binds the RFS file system to RTEMS. + */ + +#if !defined(RTEMS_RFS_RTEMS_DEFINED) +#define RTEMS_RFS_RTEMS_DEFINED + +#include <stdbool.h> +#include <stdint.h> +#include <errno.h> + +/** + * RTEMS RFS RTEMS Error Enable. Set to 1 to printing of errors. Default is off. + */ +#define RTEMS_RFS_RTEMS_ERROR 0 + +/** + * RTEMS RFS RTEMS Trace Enable. Set to 1 to printing of errors. Default is off. + */ +#define RTEMS_RFS_RTEMS_TRACE 0 + +/** + * If we are not handling errors provide a define that removes the strings from + * the code. + */ +#if !RTEMS_RFS_RTEMS_ERROR +#define rtems_rfs_rtems_error(_m, _e) \ + (((errno = (_e)) == 0) ? 0 : -1) +#else +/** + * Take the result code and set errno with it and if non-zero return -1 else + * return 0. + * + * @param what The message to print is the error is not zero. + * @param error The error code. + * @retval -1 An error has occured. + * @retval 0 No error. + */ +int rtems_rfs_rtems_error (const char* mesg, int error); +#endif + +/** + * Trace message defines the RTEMS bindings of the RTEMS RFS. This is a + * development tool where can edit the values below to control the various trace + * output. + */ +#define RTEMS_RFS_RTEMS_DEBUG_ALL (0xffffffff) +#define RTEMS_RFS_RTEMS_DEBUG_ERROR_MSGS (1 << 0) +#define RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH (1 << 1) +#define RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE (1 << 2) +#define RTEMS_RFS_RTEMS_DEBUG_EVAL_PERMS (1 << 3) +#define RTEMS_RFS_RTEMS_DEBUG_MKNOD (1 << 4) +#define RTEMS_RFS_RTEMS_DEBUG_RMNOD (1 << 5) +#define RTEMS_RFS_RTEMS_DEBUG_LINK (1 << 6) +#define RTEMS_RFS_RTEMS_DEBUG_UNLINK (1 << 7) +#define RTEMS_RFS_RTEMS_DEBUG_CHOWN (1 << 8) +#define RTEMS_RFS_RTEMS_DEBUG_READLINK (1 << 9) +#define RTEMS_RFS_RTEMS_DEBUG_FCHMOD (1 << 10) +#define RTEMS_RFS_RTEMS_DEBUG_STAT (1 << 11) +#define RTEMS_RFS_RTEMS_DEBUG_DIR_RMNOD (1 << 12) +#define RTEMS_RFS_RTEMS_DEBUG_FILE_OPEN (1 << 13) +#define RTEMS_RFS_RTEMS_DEBUG_FILE_CLOSE (1 << 14) +#define RTEMS_RFS_RTEMS_DEBUG_FILE_READ (1 << 15) +#define RTEMS_RFS_RTEMS_DEBUG_FILE_WRITE (1 << 16) +#define RTEMS_RFS_RTEMS_DEBUG_FILE_LSEEK (1 << 17) +#define RTEMS_RFS_RTEMS_DEBUG_FILE_FTRUNC (1 << 18) + +/** + * Call to check if this part is bring traced. If RTEMS_RFS_RTEMS_TRACE is + * defined to 0 the code is dead code elminiated when built with -Os, -O2, or + * higher. + * + * @param mask The part of the API to trace. + * @retval true Tracing is active for the mask. + * @retval false Do not trace. + */ +#if RTEMS_RFS_RTEMS_TRACE +bool rtems_rfs_rtems_trace (uint32_t mask); +#else +#define rtems_rfs_rtems_trace(_m) (0) +#endif + +/** + * Set the mask. + * + * @param mask The mask bits to set. + * @return The previous mask. + */ +#if RTEMS_RFS_RTEMS_TRACE +void rtems_rfs_rtems_trace_set_mask (uint32_t mask); +#else +#define rtems_rfs_rtems_trace_set_mask(_m) +#endif + +/** + * Clear the mask. + * + * @param mask The mask bits to clear. + * @return The previous mask. + */ +#if RTEMS_RFS_RTEMS_TRACE +void rtems_rfs_rtems_trace_clear_mask (uint32_t mask); +#else +#define rtems_rfs_rtems_trace_clear_mask(_m) +#endif + +/** + * Add shell trace shell command. + */ +#if RTEMS_RFS_RTEMS_TRACE +int rtems_rfs_rtems_trace_shell_command (int argc, char *argv[]); +#endif + +#include <rtems/rfs/rtems-rfs-file-system.h> +#include <rtems/rfs/rtems-rfs-inode.h> +#include <rtems/rfs/rtems-rfs-mutex.h> +#include <rtems/libio_.h> +#include <rtems/fs.h> + +/** + * Private RFS RTEMS Port data. + */ +typedef struct rtems_rfs_rtems_private +{ + /** + * The access lock. + */ + rtems_rfs_mutex access; +} rtems_rfs_rtems_private; +/** + * Return the file system structure given a path location. + * + * @param _loc Pointer to the path location. + * @return rtems_rfs_file_system* + */ +#define rtems_rfs_rtems_pathloc_dev(_loc) \ + ((rtems_rfs_file_system*)((_loc)->mt_entry->fs_info)) + +/** + * Set the inode number (ino) into the path location. + * + * @param _loc Pointer to the path location. + * @param _ino The ino to set in the path location. + */ +#define rtems_rfs_rtems_set_pathloc_ino(_loc, _ino) \ + (_loc)->node_access = (void*)(_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Shell Commands Support + */ + +#include <string.h> + +#include <rtems/rfs/rtems-rfs-block.h> +#include <rtems/rfs/rtems-rfs-buffer.h> +#include <rtems/rfs/rtems-rfs-group.h> +#include <rtems/rfs/rtems-rfs-inode.h> +#include <rtems/rfs/rtems-rfs-dir.h> + +#include <sys/statvfs.h> + +#if __rtems__ +#include "rtems-rfs-rtems.h" +#endif + +/** + * The type of the shell handlers we have. + */ +typedef int (*rtems_rfs_shell_handler) (rtems_rfs_file_system* fs, int argc, char *argv[]); + +/** + * Table of handlers we parse to invoke the command. + */ +typedef struct 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] <path> <command>\n", arg); + printf (" where:\n"); + printf (" path: Path to the mounted RFS file system\n"); + printf (" command: A debugger command. See -l for a list plus help.\n"); + printf (" -h: This help\n"); + printf (" -l: The debugger command list.\n"); +} + +int +rtems_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 <bno>, block <bno>..<bno>" }, + { "data", rtems_rfs_shell_data, + "Display file system data, data" }, + { "dir", rtems_rfs_shell_dir, + "Display a block as a table for directory entrie, dir <bno>" }, + { "group", rtems_rfs_shell_group, + "Display the group data of a file system, group, group <group>, group <start> <end>" }, + { "inode", rtems_rfs_shell_inode, + "Display an inode, inode <ino>, inode> <ino>..<ino>" } + }; + + int arg; + int t; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] != '-') + break; + + switch (argv[arg][1]) + { + case 'h': + rtems_rfs_shell_usage (argv[0]); + return 0; + case 'l': + printf ("%s: commands are:\n", argv[0]); + for (t = 0; t < (sizeof (table) / sizeof (const rtems_rfs_shell_cmd)); t++) + printf (" %s\t\t%s\n", table[t].name, table[t].help); + return 0; + default: + printf ("error: unknown option: %s\n", argv[arg]); + return 1; + } + } + + if ((argc - arg) < 2) + printf ("error: you need at least a path and command, try %s -h\n", argv[0]); + else + { + rtems_rfs_file_system* fs; + if (rtems_rfs_get_fs (argv[arg], &fs) == 0) + { + for (t = 0; t < (sizeof (table) / sizeof (const rtems_rfs_shell_cmd)); t++) + if (strcmp (argv[arg + 1], table[t].name) == 0) + return table[t].handler (fs, argc - 2, argv + 2); + printf ("error: command not found: %s\n", argv[arg + 1]); + } + } + + return 1; +} 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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Shell commands provide a CLI interface to support and + * development od the RFS file system. + */ + +#if !defined (_RTEMS_RFS_SHELL_H_) +#define _RTEMS_RFS_SHELL_H_ + +#include <stddef.h> +#include <stdbool.h> +#include <stdint.h> + +/** + * The shell command for the RFS debugger. + * + * @param argc The argument count. + * @param argv The argument variables. + * @return int The exit code for the command. A 0 is no error. + */ +int rtems_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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Trace Support + */ + +#include <string.h> +#include <rtems/rfs/rtems-rfs-trace.h> + +#if RTEMS_RFS_TRACE +static rtems_rfs_trace_mask rtems_rfs_trace_flags; + +bool +rtems_rfs_trace (rtems_rfs_trace_mask mask) +{ + bool result = false; + if (mask & rtems_rfs_trace_flags) + result = true; + return result; +} + +rtems_rfs_trace_mask +rtems_rfs_trace_set_mask (rtems_rfs_trace_mask mask) +{ + rtems_rfs_trace_mask state = rtems_rfs_trace_flags; + rtems_rfs_trace_flags |= mask; + return state; +} + +rtems_rfs_trace_mask +rtems_rfs_trace_clear_mask (rtems_rfs_trace_mask mask) +{ + rtems_rfs_trace_mask state = rtems_rfs_trace_flags; + rtems_rfs_trace_flags &= ~mask; + return state; +} + +int +rtems_rfs_trace_shell_command (int argc, char *argv[]) +{ + const char* table[] = + { + "open", + "close", + "mutex", + "buffer-open", + "buffer-close", + "buffer-sync", + "buffer-release", + "buffer-chains", + "buffer-handle-request", + "buffer-handle-release", + "buffer-setblksize", + "buffers-release", + "block-find", + "block-map-grow", + "block-map-shrink", + "group-open", + "group-close", + "group-bitmaps", + "inode-open", + "inode-close", + "inode-load", + "inode-unload", + "inode-create", + "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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Trace manages the trace and debugging features of the + * RTEMS RFS file system. The design allows all tracing code and strings to be + * removed from the target code for small footprint systems. + */ + +#if !defined (_RTEMS_RFS_TRACE_H_) +#define _RTEMS_RFS_TRACE_H_ + +#include <stddef.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> + +/** + * Is tracing enabled ? + */ +#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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File System + * + */ + +#if !defined(RTEMS_RFS_DEFINED) +#define RTEMS_RFS_DEFINED + +#include <rtems.h> +#include <rtems/fs.h> + +/** + * 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 |