summaryrefslogtreecommitdiffstats
path: root/cpukit/libfs/src/rfs/rtems-rfs-dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libfs/src/rfs/rtems-rfs-dir.c')
-rw-r--r--cpukit/libfs/src/rfs/rtems-rfs-dir.c760
1 files changed, 760 insertions, 0 deletions
diff --git a/cpukit/libfs/src/rfs/rtems-rfs-dir.c b/cpukit/libfs/src/rfs/rtems-rfs-dir.c
new file mode 100644
index 0000000000..1f2e5fd518
--- /dev/null
+++ b/cpukit/libfs/src/rfs/rtems-rfs-dir.c
@@ -0,0 +1,760 @@
+/*
+ * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ *
+ * $Id$
+ */
+/**
+ * @file
+ *
+ * @ingroup rtems-rfs
+ *
+ * RTEMS File Systems Directory Routines.
+ *
+ * These functions manage blocks in the directory format. A directory entry is
+ * a variable length record in the block. The entry consists of a length, hash
+ * and the string. The length allows the next entry to be located and the hash
+ * allows a simple check to be performed wihtout a string compare. Directory
+ * entries do not span a block and removal of an entry results in the space in
+ * the block being compacted and the spare area being initialised to ones.
+ *
+ * The maximum length can be 1 or 2 bytes depending on the value in the
+ * superblock.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+
+#if SIZEOF_OFF_T == 8
+#define PRIdoff_t PRIo64
+#elif SIZEOF_OFF_T == 4
+#define PRIdoff_t PRIo32
+#else
+#error "unsupported size of off_t"
+#endif
+
+#include <rtems/rfs/rtems-rfs-block.h>
+#include <rtems/rfs/rtems-rfs-buffer.h>
+#include <rtems/rfs/rtems-rfs-file-system.h>
+#include <rtems/rfs/rtems-rfs-trace.h>
+#include <rtems/rfs/rtems-rfs-dir.h>
+#include <rtems/rfs/rtems-rfs-dir-hash.h>
+
+/**
+ * Validate the directory entry data.
+ */
+#define rtems_rfs_dir_entry_valid(_f, _l, _i) \
+ (((_l) <= RTEMS_RFS_DIR_ENTRY_SIZE) || ((_l) >= rtems_rfs_fs_max_name (_f)) \
+ || (_i < RTEMS_RFS_ROOT_INO) || (_i > rtems_rfs_fs_inodes (_f)))
+
+int
+rtems_rfs_dir_lookup_ino (rtems_rfs_file_system* fs,
+ rtems_rfs_inode_handle* inode,
+ const char* name,
+ int length,
+ rtems_rfs_ino* ino,
+ uint32_t* offset)
+{
+ rtems_rfs_block_map map;
+ rtems_rfs_buffer_handle entries;
+ int rc;
+
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO))
+ {
+ int c;
+ printf ("rtems-rfs: dir-lookup-ino: lookup ino: root=%" PRId32 ", path=",
+ inode->ino);
+ for (c = 0; c < length; c++)
+ printf ("%c", name[c]);
+ printf (", len=%d\n", length);
+ }
+
+ *ino = RTEMS_RFS_EMPTY_INO;
+ *offset = 0;
+
+ rc = rtems_rfs_block_map_open (fs, inode, &map);
+ if (rc > 0)
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO))
+ printf ("rtems-rfs: dir-lookup-ino: map open failed for ino %" PRIu32 ": %d: %s",
+ rtems_rfs_inode_ino (inode), rc, strerror (rc));
+ return rc;
+ }
+
+ rc = rtems_rfs_buffer_handle_open (fs, &entries);
+ if (rc > 0)
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO))
+ printf ("rtems-rfs: dir-lookup-ino: handle open failed for ino %" PRIu32 ": %d: %s",
+ rtems_rfs_inode_ino (inode), rc, strerror (rc));
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+ }
+ else
+ {
+ rtems_rfs_block_no block;
+ uint32_t hash;
+
+ /*
+ * Calculate the hash of the look up string.
+ */
+ hash = rtems_rfs_dir_hash (name, length);
+
+ /*
+ * Locate the first block. The map points to the start after open so just
+ * seek 0. If an error the block will be 0.
+ */
+ rc = rtems_rfs_block_map_seek (fs, &map, 0, &block);
+ if (rc > 0)
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO))
+ printf ("rtems-rfs: dir-lookup-ino: block map find failed: %d: %s\n",
+ rc, strerror (rc));
+ if (rc == ENXIO)
+ rc = ENOENT;
+ rtems_rfs_buffer_handle_close (fs, &entries);
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+ }
+
+ while ((rc == 0) && block)
+ {
+ uint8_t* entry;
+
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO))
+ printf ("rtems-rfs: dir-lookup-ino: block read, ino=%" PRIu32 " bno=%" PRId32 "\n",
+ rtems_rfs_inode_ino (inode), map.bpos.bno);
+
+ rc = rtems_rfs_buffer_handle_request (fs, &entries, block, true);
+ if (rc > 0)
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO))
+ printf ("rtems-rfs: dir-lookup-ino: block read, ino=%" PRIu32 " block=%" PRId32 ": %d: %s\n",
+ rtems_rfs_inode_ino (inode), block, rc, strerror (rc));
+ break;
+ }
+
+ /*
+ * Search the block to see if the name matches. A hash of 0xffff or 0x0
+ * means the entry is empty.
+ */
+
+ entry = rtems_rfs_buffer_data (&entries);
+
+ map.bpos.boff = 0;
+
+ while (map.bpos.boff < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE))
+ {
+ uint32_t ehash;
+ int elength;
+
+ ehash = rtems_rfs_dir_entry_hash (entry);
+ elength = rtems_rfs_dir_entry_length (entry);
+ *ino = rtems_rfs_dir_entry_ino (entry);
+
+ if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY)
+ break;
+
+ if (rtems_rfs_dir_entry_valid (fs, elength, *ino))
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO))
+ printf ("rtems-rfs: dir-lookup-ino: "
+ "bad length or ino for ino %" PRIu32 ": %u/%" PRId32 " @ %04" PRIx32 "\n",
+ rtems_rfs_inode_ino (inode), elength, *ino, map.bpos.boff);
+ rc = EIO;
+ break;
+ }
+
+ if (ehash == hash)
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO_CHECK))
+ printf ("rtems-rfs: dir-lookup-ino: "
+ "checking entry for ino %" PRId32 ": bno=%04" PRIx32 "/off=%04" PRIx32
+ " length:%d ino:%" PRId32 "\n",
+ rtems_rfs_inode_ino (inode), map.bpos.bno, map.bpos.boff,
+ elength, rtems_rfs_dir_entry_ino (entry));
+
+ if (memcmp (entry + RTEMS_RFS_DIR_ENTRY_SIZE, name, length) == 0)
+ {
+ *offset = rtems_rfs_block_map_pos (fs, &map);
+
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO_FOUND))
+ printf ("rtems-rfs: dir-lookup-ino: "
+ "entry found in ino %" PRIu32 ", ino=%" PRIu32 " offset=%" PRIu32 "\n",
+ rtems_rfs_inode_ino (inode), *ino, *offset);
+
+ rtems_rfs_buffer_handle_close (fs, &entries);
+ rtems_rfs_block_map_close (fs, &map);
+ return 0;
+ }
+ }
+
+ map.bpos.boff += elength;
+ entry += elength;
+ }
+
+ if (rc == 0)
+ {
+ rc = rtems_rfs_block_map_next_block (fs, &map, &block);
+ if ((rc > 0) && (rc != ENXIO))
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO))
+ printf ("rtems-rfs: dir-lookup-ino: "
+ "block map next block failed in ino %" PRIu32 ": %d: %s\n",
+ rtems_rfs_inode_ino (inode), rc, strerror (rc));
+ }
+ if (rc == ENXIO)
+ rc = ENOENT;
+ }
+ }
+
+ if ((rc == 0) && (block == 0))
+ {
+ rc = EIO;
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO))
+ printf ("rtems-rfs: dir-lookup-ino: block is 0 in ino %" PRIu32 ": %d: %s\n",
+ rtems_rfs_inode_ino (inode), rc, strerror (rc));
+ }
+ }
+
+ rtems_rfs_buffer_handle_close (fs, &entries);
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+}
+
+int
+rtems_rfs_dir_add_entry (rtems_rfs_file_system* fs,
+ rtems_rfs_inode_handle* dir,
+ const char* name,
+ size_t length,
+ rtems_rfs_ino ino)
+{
+ rtems_rfs_block_map map;
+ rtems_rfs_block_pos bpos;
+ rtems_rfs_buffer_handle buffer;
+ int rc;
+
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY))
+ {
+ int c;
+ printf ("rtems-rfs: dir-add-entry: dir=%" PRId32 ", name=",
+ rtems_rfs_inode_ino (dir));
+ for (c = 0; c < length; c++)
+ printf ("%c", name[c]);
+ printf (", len=%zd\n", length);
+ }
+
+ rc = rtems_rfs_block_map_open (fs, dir, &map);
+ if (rc > 0)
+ return rc;
+
+ rc = rtems_rfs_buffer_handle_open (fs, &buffer);
+ if (rc > 0)
+ {
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+ }
+
+ /*
+ * Search the map from the beginning to find any empty space.
+ */
+ rtems_rfs_block_set_bpos_zero (&bpos);
+
+ while (true)
+ {
+ rtems_rfs_block_no block;
+ uint8_t* entry;
+ int offset;
+ bool read = true;
+
+ /*
+ * Locate the first block. If an error the block will be 0. If the map is
+ * empty which happens when creating a directory and adding the first entry
+ * the seek will return ENXIO. In this case we need to grow the directory.
+ */
+ rc = rtems_rfs_block_map_find (fs, &map, &bpos, &block);
+ if (rc > 0)
+ {
+ if (rc != ENXIO)
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY))
+ printf ("rtems-rfs: dir-add-entry: "
+ "block map find failed for ino %" PRIu32 ": %d: %s\n",
+ rtems_rfs_inode_ino (dir), rc, strerror (rc));
+ break;
+ }
+
+ /*
+ * We have reached the end of the directory so add a block.
+ */
+ rc = rtems_rfs_block_map_grow (fs, &map, 1, &block);
+ if (rc > 0)
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY))
+ printf ("rtems-rfs: dir-add-entry: "
+ "block map grow failed for ino %" PRIu32 ": %d: %s\n",
+ rtems_rfs_inode_ino (dir), rc, strerror (rc));
+ break;
+ }
+
+ read = false;
+ }
+
+ bpos.bno++;
+
+ rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, read);
+ if (rc > 0)
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY))
+ printf ("rtems-rfs: dir-add-entry: "
+ "block buffer req failed for ino %" PRIu32 ": %d: %s\n",
+ rtems_rfs_inode_ino (dir), rc, strerror (rc));
+ break;
+ }
+
+ entry = rtems_rfs_buffer_data (&buffer);
+
+ if (!read)
+ memset (entry, 0xff, rtems_rfs_fs_block_size (fs));
+
+ offset = 0;
+
+ while (offset < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE))
+ {
+ rtems_rfs_ino eino;
+ int elength;
+
+ elength = rtems_rfs_dir_entry_length (entry);
+ eino = rtems_rfs_dir_entry_ino (entry);
+
+ if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY)
+ {
+ if ((length + RTEMS_RFS_DIR_ENTRY_SIZE) <
+ (rtems_rfs_fs_block_size (fs) - offset))
+ {
+ uint32_t hash;
+ hash = rtems_rfs_dir_hash (name, length);
+ rtems_rfs_dir_set_entry_hash (entry, hash);
+ rtems_rfs_dir_set_entry_ino (entry, ino);
+ rtems_rfs_dir_set_entry_length (entry,
+ RTEMS_RFS_DIR_ENTRY_SIZE + length);
+ memcpy (entry + RTEMS_RFS_DIR_ENTRY_SIZE, name, length);
+ rtems_rfs_buffer_mark_dirty (&buffer);
+ rtems_rfs_buffer_handle_close (fs, &buffer);
+ rtems_rfs_block_map_close (fs, &map);
+ return 0;
+ }
+
+ break;
+ }
+
+ if (rtems_rfs_dir_entry_valid (fs, elength, eino))
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY))
+ printf ("rtems-rfs: dir-add-entry: "
+ "bad length or ino for ino %" PRIu32 ": %u/%" PRId32 " @ %04x\n",
+ rtems_rfs_inode_ino (dir), elength, eino, offset);
+ rtems_rfs_buffer_handle_close (fs, &buffer);
+ rtems_rfs_block_map_close (fs, &map);
+ return EIO;
+ }
+
+ entry += elength;
+ offset += elength;
+ }
+ }
+
+ rtems_rfs_buffer_handle_close (fs, &buffer);
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+}
+
+int
+rtems_rfs_dir_del_entry (rtems_rfs_file_system* fs,
+ rtems_rfs_inode_handle* dir,
+ rtems_rfs_ino ino,
+ uint32_t offset)
+{
+ rtems_rfs_block_map map;
+ rtems_rfs_block_no block;
+ rtems_rfs_buffer_handle buffer;
+ bool search;
+ int rc;
+
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY))
+ printf ("rtems-rfs: dir-del-entry: dir=%" PRId32 ", entry=%" PRId32 " offset=%" PRIu32 "\n",
+ rtems_rfs_inode_ino (dir), ino, offset);
+
+ rc = rtems_rfs_block_map_open (fs, dir, &map);
+ if (rc > 0)
+ return rc;
+
+ rc = rtems_rfs_block_map_seek (fs, &map, offset, &block);
+ if (rc > 0)
+ {
+ if (rc == ENXIO)
+ rc = ENOENT;
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+ }
+
+ rc = rtems_rfs_buffer_handle_open (fs, &buffer);
+ if (rc > 0)
+ {
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+ }
+
+ /*
+ * Only search if the offset is 0 else we are at that position.
+ */
+ search = offset ? false : true;
+
+ while (rc == 0)
+ {
+ uint8_t* entry;
+ int eoffset;
+
+ rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, true);
+ if (rc > 0)
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY))
+ printf ("rtems-rfs: dir-del-entry: "
+ "block buffer req failed for ino %" PRIu32 ": %d: %s\n",
+ rtems_rfs_inode_ino (dir), rc, strerror (rc));
+ break;
+ }
+
+ /*
+ * If we are searching start at the beginning of the block. If not searching
+ * skip to the offset in the block.
+ */
+ if (search)
+ eoffset = 0;
+ else
+ eoffset = offset % rtems_rfs_fs_block_size (fs);
+
+ entry = rtems_rfs_buffer_data (&buffer) + eoffset;
+
+ while (eoffset < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE))
+ {
+ rtems_rfs_ino eino;
+ int elength;
+
+ elength = rtems_rfs_dir_entry_length (entry);
+ eino = rtems_rfs_dir_entry_ino (entry);
+
+ if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY)
+ break;
+
+ if (rtems_rfs_dir_entry_valid (fs, elength, eino))
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY))
+ printf ("rtems-rfs: dir-del-entry: "
+ "bad length or ino for ino %" PRIu32 ": %u/%" PRId32
+ " @ %" PRIu32 ".%04x\n",
+ rtems_rfs_inode_ino (dir), elength, eino, block, eoffset);
+ rc = EIO;
+ break;
+ }
+
+ if (ino == rtems_rfs_dir_entry_ino (entry))
+ {
+ uint32_t remaining;
+ remaining = rtems_rfs_fs_block_size (fs) - (eoffset + elength);
+ memmove (entry, entry + elength, remaining);
+ memset (entry + remaining, 0xff, elength);
+
+ /*
+ * If the remainder of the block is empty and this is the start of the
+ * block and it is the last block in the map shrink the map.
+ *
+ * @note We could check again to see if the new end block in the map is
+ * also empty. This way we could clean up an empty directory.
+ */
+ elength = rtems_rfs_dir_entry_length (entry);
+
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY))
+ printf ("rtems-rfs: dir-del-entry: "
+ "last block free for ino %" PRIu32 ": elength=%i block=%" PRIu32
+ " offset=%d last=%s\n",
+ ino, elength, block, eoffset,
+ rtems_rfs_block_map_last (&map) ? "yes" : "no");
+
+ if ((elength == RTEMS_RFS_DIR_ENTRY_EMPTY) &&
+ (eoffset == 0) && rtems_rfs_block_map_last (&map))
+ {
+ rc = rtems_rfs_block_map_shrink (fs, &map, 1);
+ if (rc > 0)
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY))
+ printf ("rtems-rfs: dir-del-entry: "
+ "block map shrink failed for ino %" PRIu32 ": %d: %s\n",
+ rtems_rfs_inode_ino (dir), rc, strerror (rc));
+ }
+ }
+
+ rtems_rfs_buffer_mark_dirty (&buffer);
+ rtems_rfs_buffer_handle_close (fs, &buffer);
+ rtems_rfs_block_map_close (fs, &map);
+ return 0;
+ }
+
+ if (!search)
+ {
+ rc = EIO;
+ break;
+ }
+
+ entry += elength;
+ eoffset += elength;
+ }
+
+ if (rc == 0)
+ {
+ rc = rtems_rfs_block_map_next_block (fs, &map, &block);
+ if (rc == ENXIO)
+ rc = ENOENT;
+ }
+ }
+
+ rtems_rfs_buffer_handle_close (fs, &buffer);
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+}
+
+int
+rtems_rfs_dir_read (rtems_rfs_file_system* fs,
+ rtems_rfs_inode_handle* dir,
+ rtems_rfs_pos_rel offset,
+ struct dirent* dirent,
+ size_t* length)
+{
+ rtems_rfs_block_map map;
+ rtems_rfs_buffer_handle buffer;
+ rtems_rfs_block_no block;
+ int rc;
+
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ))
+ printf ("rtems-rfs: dir-read: dir=%" PRId32 " offset=%" PRId64 "\n",
+ rtems_rfs_inode_ino (dir), offset);
+
+ *length = 0;
+
+ rc = rtems_rfs_block_map_open (fs, dir, &map);
+ if (rc > 0)
+ return rc;
+
+ if (((rtems_rfs_fs_block_size (fs) -
+ (offset % rtems_rfs_fs_block_size (fs))) <= RTEMS_RFS_DIR_ENTRY_SIZE))
+ offset = (((offset / rtems_rfs_fs_block_size (fs)) + 1) *
+ rtems_rfs_fs_block_size (fs));
+
+ rc = rtems_rfs_block_map_seek (fs, &map, offset, &block);
+ if (rc > 0)
+ {
+ if (rc == ENXIO)
+ rc = ENOENT;
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+ }
+
+ rc = rtems_rfs_buffer_handle_open (fs, &buffer);
+ if (rc > 0)
+ {
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+ }
+
+ /*
+ * Look for an empty entry and if this is the last block that is the end of
+ * the directory.
+ */
+ while (rc == 0)
+ {
+ uint8_t* entry;
+ rtems_rfs_ino eino;
+ int elength;
+ int remaining;
+
+ rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, true);
+ if (rc > 0)
+ {
+ rtems_rfs_buffer_handle_close (fs, &buffer);
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+ }
+
+ entry = rtems_rfs_buffer_data (&buffer);
+ entry += map.bpos.boff;
+
+ elength = rtems_rfs_dir_entry_length (entry);
+ eino = rtems_rfs_dir_entry_ino (entry);
+
+ if (elength != RTEMS_RFS_DIR_ENTRY_EMPTY)
+ {
+ if (rtems_rfs_dir_entry_valid (fs, elength, eino))
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ))
+ printf ("rtems-rfs: dir-read: "
+ "bad length or ino for ino %" PRIu32 ": %u/%" PRId32 " @ %04" PRIx32 "\n",
+ rtems_rfs_inode_ino (dir), elength, eino, map.bpos.boff);
+ rc = EIO;
+ break;
+ }
+
+ memset (dirent, 0, sizeof (struct dirent));
+ dirent->d_off = offset;
+ dirent->d_reclen = sizeof (struct dirent);
+
+ *length += elength;
+
+ remaining = rtems_rfs_fs_block_size (fs) - (map.bpos.boff + elength);
+
+ if (remaining <= RTEMS_RFS_DIR_ENTRY_SIZE)
+ *length += remaining;
+
+ elength -= RTEMS_RFS_DIR_ENTRY_SIZE;
+ if (elength > NAME_MAX)
+ elength = NAME_MAX;
+
+ memcpy (dirent->d_name, entry + RTEMS_RFS_DIR_ENTRY_SIZE, elength);
+
+ dirent->d_ino = rtems_rfs_dir_entry_ino (entry);
+ dirent->d_namlen = elength;
+
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ))
+ printf ("rtems-rfs: dir-read: found off:%" PRIdoff_t " ino:%ld name=%s\n",
+ dirent->d_off, dirent->d_ino, dirent->d_name);
+ break;
+ }
+
+ *length += rtems_rfs_fs_block_size (fs) - map.bpos.boff;
+
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ))
+ printf ("rtems-rfs: dir-read: next block: off:%" PRId64 " length:%zd\n",
+ offset, *length);
+
+ rc = rtems_rfs_block_map_next_block (fs, &map, &block);
+ if (rc == ENXIO)
+ rc = ENOENT;
+ }
+
+ rtems_rfs_buffer_handle_close (fs, &buffer);
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+}
+
+int
+rtems_rfs_dir_empty (rtems_rfs_file_system* fs,
+ rtems_rfs_inode_handle* dir)
+{
+ rtems_rfs_block_map map;
+ rtems_rfs_buffer_handle buffer;
+ rtems_rfs_block_no block;
+ bool empty;
+ int rc;
+
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ))
+ printf ("rtems-rfs: dir-empty: dir=%" PRId32 "\n", rtems_rfs_inode_ino (dir));
+
+ empty = true;
+
+ rc = rtems_rfs_block_map_open (fs, dir, &map);
+ if (rc > 0)
+ return rc;
+
+ rc = rtems_rfs_block_map_seek (fs, &map, 0, &block);
+ if (rc > 0)
+ {
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+ }
+
+ rc = rtems_rfs_buffer_handle_open (fs, &buffer);
+ if (rc > 0)
+ {
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+ }
+
+ /*
+ * Look for an empty entry and if this is the last block that is the end of
+ * the directory.
+ */
+ while (empty)
+ {
+ uint8_t* entry;
+ int offset;
+
+ rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, true);
+ if (rc > 0)
+ break;
+
+ entry = rtems_rfs_buffer_data (&buffer);
+ offset = 0;
+
+ while (offset < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE))
+ {
+ rtems_rfs_ino eino;
+ int elength;
+
+ elength = rtems_rfs_dir_entry_length (entry);
+ eino = rtems_rfs_dir_entry_ino (entry);
+
+ if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY)
+ break;
+
+ if (rtems_rfs_dir_entry_valid (fs, elength, eino))
+ {
+ if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_EMPTY))
+ printf ("rtems-rfs: dir-empty: "
+ "bad length or ino for ino %" PRIu32 ": %u/%" PRIu32 " @ %04x\n",
+ rtems_rfs_inode_ino (dir), elength, eino, offset);
+ rc = EIO;
+ break;
+ }
+
+ /*
+ * Ignore the current (.) and parent (..) entries. Anything else means
+ * the directory is not empty.
+ */
+ if (((elength != (RTEMS_RFS_DIR_ENTRY_SIZE + 1)) ||
+ (entry[RTEMS_RFS_DIR_ENTRY_SIZE] != '.')) &&
+ ((elength != (RTEMS_RFS_DIR_ENTRY_SIZE + 2)) ||
+ (entry[RTEMS_RFS_DIR_ENTRY_SIZE] != '.') ||
+ (entry[RTEMS_RFS_DIR_ENTRY_SIZE + 1] != '.')))
+ {
+ empty = false;
+ break;
+ }
+
+ entry += elength;
+ offset += elength;
+ }
+
+ if (empty)
+ {
+ rc = rtems_rfs_block_map_next_block (fs, &map, &block);
+ if (rc > 0)
+ {
+ if (rc == ENXIO)
+ rc = 0;
+ break;
+ }
+ }
+ }
+
+ if ((rc == 0) && !empty)
+ rc = ENOTEMPTY;
+
+ rtems_rfs_buffer_handle_close (fs, &buffer);
+ rtems_rfs_block_map_close (fs, &map);
+ return rc;
+}