From a9fa9b765df38cc5319ae734b5148fd47ebbfd8d Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Thu, 18 Feb 2010 00:24:25 +0000 Subject: 2010-02-18 Chris Johns * libfs/src/rfs/rtems-rfs-bitmaps.c, libfs/src/rfs/rtems-rfs-bitmaps.h, libfs/src/rfs/rtems-rfs-bitmaps-ut.c, libfs/src/rfs/rtems-rfs-block.c, libfs/src/rfs/rtems-rfs-block.h, libfs/src/rfs/rtems-rfs-block-pos.h, libfs/src/rfs/rtems-rfs-buffer-bdbuf.c, libfs/src/rfs/rtems-rfs-buffer.c, libfs/src/rfs/rtems-rfs-buffer-devio.c, libfs/src/rfs/rtems-rfs-buffer.h, libfs/src/rfs/rtems-rfs-data.h, libfs/src/rfs/rtems-rfs-dir.c, libfs/src/rfs/rtems-rfs-dir.h, libfs/src/rfs/rtems-rfs-dir-hash.c, libfs/src/rfs/rtems-rfs-dir-hash.h, libfs/src/rfs/rtems-rfs-file.c, libfs/src/rfs/rtems-rfs-file.h, libfs/src/rfs/rtems-rfs-file-system.c, libfs/src/rfs/rtems-rfs-file-system-fwd.h, libfs/src/rfs/rtems-rfs-file-system.h, libfs/src/rfs/rtems-rfs-format.c, libfs/src/rfs/rtems-rfs-format.h, libfs/src/rfs/rtems-rfs-group.c, libfs/src/rfs/rtems-rfs-group.h, libfs/src/rfs/rtems-rfs.h, libfs/src/rfs/rtems-rfs-inode.c, libfs/src/rfs/rtems-rfs-inode.h, libfs/src/rfs/rtems-rfs-link.c, libfs/src/rfs/rtems-rfs-link.h, libfs/src/rfs/rtems-rfs-mutex.c, libfs/src/rfs/rtems-rfs-mutex.h, libfs/src/rfs/rtems-rfs-rtems.c, libfs/src/rfs/rtems-rfs-rtems-dev.c, libfs/src/rfs/rtems-rfs-rtems-dir.c, libfs/src/rfs/rtems-rfs-rtems-file.c, libfs/src/rfs/rtems-rfs-rtems.h, libfs/src/rfs/rtems-rfs-rtems-utils.c, libfs/src/rfs/rtems-rfs-shell.c, libfs/src/rfs/rtems-rfs-shell.h, libfs/src/rfs/rtems-rfs-trace.c, libfs/src/rfs/rtems-rfs-trace.h: New. * Makefile.am, preinstall.am, libfs/Makefile.am, wrapup/Makefile.am: Updated with the RFS support. * libfs/README: Updated after 10 years. * libblock/src/flashdisk.c, libblock/src/nvdisk.c, libblock/src/ramdisk-driver.c: Updated to the new error reporting in libblock. * libmisc/shell/main_ls.c, libmisc/shell/print-ls.c: Fix printing the size in long mode. * libnetworking/nfs/bootp_subr.c, libnetworking/rtems/rtems_bootp.c, libnetworking/rtems/rtems_bsdnet_internal.h: Return the BOOTP/DHCP to the forever behaviour of 4.9 with the ability to call BOOTP and control the process if required. --- cpukit/libfs/src/rfs/rtems-rfs-dir.c | 742 +++++++++++++++++++++++++++++++++++ 1 file changed, 742 insertions(+) create mode 100644 cpukit/libfs/src/rfs/rtems-rfs-dir.c (limited to 'cpukit/libfs/src/rfs/rtems-rfs-dir.c') diff --git a/cpukit/libfs/src/rfs/rtems-rfs-dir.c b/cpukit/libfs/src/rfs/rtems-rfs-dir.c new file mode 100644 index 0000000000..8813fb751e --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-dir.c @@ -0,0 +1,742 @@ +/* + * COPYRIGHT (c) 2010 Chris Johns + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ +/** + * @file + * + * @ingroup rtems-rfs + * + * RTEMS File Systems Directory Routines. + * + * These functions manage blocks in the directory format. A directory entry is + * a variable length record in the block. The entry consists of a length, hash + * and the string. The length allows the next entry to be located and the hash + * allows a simple check to be performed wihtout a string compare. Directory + * entries do not span a block and removal of an entry results in the space in + * the block being compacted and the spare area being initialised to ones. + * + * The maximum length can be 1 or 2 bytes depending on the value in the + * superblock. + */ + +#include +#include +#include +#include +#include +#include + +int +rtems_rfs_dir_lookup_ino (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* inode, + const char* name, + int length, + rtems_rfs_ino* ino, + uint32_t* offset) +{ + rtems_rfs_block_map map; + rtems_rfs_buffer_handle entries; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + { + int c; + printf ("rtems-rfs: dir-lookup-ino: lookup ino: root=%ld, path=", + inode->ino); + for (c = 0; c < length; c++) + printf ("%c", name[c]); + printf (", len=%d\n", length); + } + + *ino = RTEMS_RFS_EMPTY_INO; + *offset = 0; + + rc = rtems_rfs_block_map_open (fs, inode, &map); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: map open failed for ino %lu: %d: %s", + rtems_rfs_inode_ino (inode), rc, strerror (rc)); + return rc; + } + + rc = rtems_rfs_buffer_handle_open (fs, &entries); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: handle open failed for ino %lu: %d: %s", + rtems_rfs_inode_ino (inode), rc, strerror (rc)); + rtems_rfs_block_map_close (fs, &map); + return rc; + } + else + { + rtems_rfs_block_no block; + uint32_t hash; + + /* + * Calculate the hash of the look up string. + */ + hash = rtems_rfs_dir_hash (name, length); + + /* + * Locate the first block. The map points to the start after open so just + * seek 0. If an error the block will be 0. + */ + rc = rtems_rfs_block_map_seek (fs, &map, 0, &block); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: block map find failed: %d: %s\n", + rc, strerror (rc)); + if (rc == ENXIO) + rc = ENOENT; + rtems_rfs_buffer_handle_close (fs, &entries); + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + while ((rc == 0) && block) + { + uint8_t* entry; + + rc = rtems_rfs_buffer_handle_request (fs, &entries, block, true); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: block read, ino=%lu block=%ld: %d: %s\n", + rtems_rfs_inode_ino (inode), block, rc, strerror (rc)); + break; + } + + /* + * Search the block to see if the name matches. A hash of 0xffff or 0x0 + * means the entry is empty. + */ + + entry = rtems_rfs_buffer_data (&entries); + + map.bpos.boff = 0; + + while (map.bpos.boff < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE)) + { + uint32_t ehash; + int elength; + + ehash = rtems_rfs_dir_entry_hash (entry); + elength = rtems_rfs_dir_entry_length (entry); + *ino = rtems_rfs_dir_entry_ino (entry); + + if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY) + break; + + if ((elength <= RTEMS_RFS_DIR_ENTRY_SIZE) + || (elength >= rtems_rfs_fs_max_name (fs)) + || (*ino < RTEMS_RFS_ROOT_INO) + || (*ino >= rtems_rfs_fs_inodes (fs))) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: " \ + "bad length or ino for ino %lu: %u/%ld @ %04lx\n", + rtems_rfs_inode_ino (inode), elength, *ino, map.bpos.boff); + rc = EIO; + break; + } + + if (ehash == hash) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO_CHECK)) + printf ("rtems-rfs: dir-lookup-ino: " \ + "checking entry for ino %ld: off=%04lx length:%d ino:%d\n", + rtems_rfs_inode_ino (inode), map.bpos.boff, + elength, rtems_rfs_dir_entry_ino (entry)); + + if (memcmp (entry + RTEMS_RFS_DIR_ENTRY_SIZE, name, length) == 0) + { + *offset = rtems_rfs_block_map_pos (fs, &map); + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO_FOUND)) + printf ("rtems-rfs: dir-lookup-ino: " \ + "entry found in ino %lu, ino=%lu offset=%lu\n", + rtems_rfs_inode_ino (inode), *ino, *offset); + + rtems_rfs_buffer_handle_close (fs, &entries); + rtems_rfs_block_map_close (fs, &map); + return 0; + } + } + + map.bpos.boff += elength; + entry += elength; + } + + if (rc == 0) + { + rc = rtems_rfs_block_map_next_block (fs, &map, &block); + if ((rc > 0) && (rc != ENXIO)) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: " \ + "block map next block failed in ino %lu: %d: %s\n", + rtems_rfs_inode_ino (inode), rc, strerror (rc)); + } + if (rc == ENXIO) + rc = ENOENT; + } + } + + if ((rc == 0) && (block == 0)) + { + rc = EIO; + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_LOOKUP_INO)) + printf ("rtems-rfs: dir-lookup-ino: block is 0 in ino %lu: %d: %s\n", + rtems_rfs_inode_ino (inode), rc, strerror (rc)); + } + } + + rtems_rfs_buffer_handle_close (fs, &entries); + rtems_rfs_block_map_close (fs, &map); + return rc; +} + +int +rtems_rfs_dir_add_entry (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* dir, + const char* name, + size_t length, + rtems_rfs_ino ino) +{ + rtems_rfs_block_map map; + rtems_rfs_block_pos bpos; + rtems_rfs_buffer_handle buffer; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY)) + { + int c; + printf ("rtems-rfs: dir-add-entry: dir=%ld, name=", + rtems_rfs_inode_ino (dir)); + for (c = 0; c < length; c++) + printf ("%c", name[c]); + printf (", len=%ld\n", length); + } + + rc = rtems_rfs_block_map_open (fs, dir, &map); + if (rc > 0) + return rc; + + rc = rtems_rfs_buffer_handle_open (fs, &buffer); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + /* + * Search the map from the beginning to find any empty space. + */ + rtems_rfs_block_set_bpos_zero (&bpos); + + while (true) + { + rtems_rfs_block_no block; + uint8_t* entry; + int offset; + bool read = true; + + /* + * Locate the first block. If an error the block will be 0. If the map is + * empty which happens when creating a directory and adding the first entry + * the seek will return ENXIO. In this case we need to grow the directory. + */ + rc = rtems_rfs_block_map_find (fs, &map, &bpos, &block); + if (rc > 0) + { + if (rc != ENXIO) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY)) + printf ("rtems-rfs: dir-add-entry: " \ + "block map find failed for ino %lu: %d: %s\n", + rtems_rfs_inode_ino (dir), rc, strerror (rc)); + break; + } + + /* + * We have reached the end of the directory so add a block. + */ + rc = rtems_rfs_block_map_grow (fs, &map, 1, &block); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY)) + printf ("rtems-rfs: dir-add-entry: " \ + "block map grow failed for ino %lu: %d: %s\n", + rtems_rfs_inode_ino (dir), rc, strerror (rc)); + break; + } + + read = false; + } + + bpos.bno++; + + rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, read); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY)) + printf ("rtems-rfs: dir-add-entry: " \ + "block buffer req failed for ino %lu: %d: %s\n", + rtems_rfs_inode_ino (dir), rc, strerror (rc)); + break; + } + + entry = rtems_rfs_buffer_data (&buffer); + + if (!read) + memset (entry, 0xff, rtems_rfs_fs_block_size (fs)); + + offset = 0; + + while (offset < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE)) + { + rtems_rfs_ino eino; + int elength; + + elength = rtems_rfs_dir_entry_length (entry); + eino = rtems_rfs_dir_entry_ino (entry); + + if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY) + { + if ((length + RTEMS_RFS_DIR_ENTRY_SIZE) < + (rtems_rfs_fs_block_size (fs) - offset)) + { + uint32_t hash; + hash = rtems_rfs_dir_hash (name, length); + rtems_rfs_dir_set_entry_hash (entry, hash); + rtems_rfs_dir_set_entry_ino (entry, ino); + rtems_rfs_dir_set_entry_length (entry, + RTEMS_RFS_DIR_ENTRY_SIZE + length); + memcpy (entry + RTEMS_RFS_DIR_ENTRY_SIZE, name, length); + rtems_rfs_buffer_mark_dirty (&buffer); + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return 0; + } + + break; + } + + if ((elength <= RTEMS_RFS_DIR_ENTRY_SIZE) + || (elength >= rtems_rfs_fs_max_name (fs)) + || (eino < RTEMS_RFS_ROOT_INO) + || (eino >= rtems_rfs_fs_inodes (fs))) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_ADD_ENTRY)) + printf ("rtems-rfs: dir-add-entry: " \ + "bad length or ino for ino %lu: %u/%ld @ %04x\n", + rtems_rfs_inode_ino (dir), elength, eino, offset); + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return EIO; + } + + entry += elength; + offset += elength; + } + } + + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return rc; +} + +int +rtems_rfs_dir_del_entry (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* dir, + rtems_rfs_ino ino, + uint32_t offset) +{ + rtems_rfs_block_map map; + rtems_rfs_block_no block; + rtems_rfs_buffer_handle buffer; + bool search; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY)) + printf ("rtems-rfs: dir-del-entry: dir=%ld, entry=%ld offset=%lu\n", + rtems_rfs_inode_ino (dir), ino, offset); + + rc = rtems_rfs_block_map_open (fs, dir, &map); + if (rc > 0) + return rc; + + rc = rtems_rfs_block_map_seek (fs, &map, offset, &block); + if (rc > 0) + { + if (rc == ENXIO) + rc = ENOENT; + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + rc = rtems_rfs_buffer_handle_open (fs, &buffer); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + /* + * Only search if the offset is 0 else we are at that position. + */ + search = offset ? false : true; + + while (rc == 0) + { + uint8_t* entry; + int offset; + + rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, true); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY)) + printf ("rtems-rfs: dir-del-entry: " \ + "block buffer req failed for ino %lu: %d: %s\n", + rtems_rfs_inode_ino (dir), rc, strerror (rc)); + break; + } + + entry = rtems_rfs_buffer_data (&buffer); + offset = 0; + + while (offset < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE)) + { + rtems_rfs_ino eino; + int elength; + + elength = rtems_rfs_dir_entry_length (entry); + eino = rtems_rfs_dir_entry_ino (entry); + + if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY) + break; + + if ((elength <= RTEMS_RFS_DIR_ENTRY_SIZE) + || (elength >= rtems_rfs_fs_max_name (fs)) + || (eino < RTEMS_RFS_ROOT_INO) + || (eino >= rtems_rfs_fs_inodes (fs))) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY)) + printf ("rtems-rfs: dir-del-entry: " \ + "bad length or ino for ino %lu: %u/%ld @ %04x\n", + rtems_rfs_inode_ino (dir), elength, eino, offset); + rc = EIO; + break; + } + + if (ino == rtems_rfs_dir_entry_ino (entry)) + { + uint32_t remaining; + remaining = rtems_rfs_fs_block_size (fs) - (offset + elength); + memmove (entry, entry + elength, remaining); + memset (entry + remaining, 0xff, elength); + + /* + * If the remainder of the block is empty and this is the start of the + * block and it is the last block in the map shrink the map. + * + * @note We could check again to see if the new end block in the map is + * also empty. This way we could clean up an empty directory. + */ + elength = rtems_rfs_dir_entry_length (entry); + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY)) + printf ("rtems-rfs: dir-del-entry: " \ + "last block free for ino %lu: elength=%i offset=%d last=%s\n", + ino, elength, offset, + rtems_rfs_block_map_last (&map) ? "yes" : "no"); + + if ((elength == RTEMS_RFS_DIR_ENTRY_EMPTY) && + (offset == 0) && rtems_rfs_block_map_last (&map)) + { + rc = rtems_rfs_block_map_shrink (fs, &map, 1); + if (rc > 0) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_DEL_ENTRY)) + printf ("rtems-rfs: dir-del-entry: " \ + "block map shrink failed for ino %lu: %d: %s\n", + rtems_rfs_inode_ino (dir), rc, strerror (rc)); + } + } + + rtems_rfs_buffer_mark_dirty (&buffer); + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return 0; + } + + if (!search) + { + rc = EIO; + break; + } + + entry += elength; + offset += elength; + } + + if (rc == 0) + { + rc = rtems_rfs_block_map_next_block (fs, &map, &block); + if (rc == ENXIO) + rc = ENOENT; + } + } + + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return rc; +} + +int +rtems_rfs_dir_read (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* dir, + rtems_rfs_pos_rel offset, + struct dirent* dirent, + size_t* length) +{ + rtems_rfs_block_map map; + rtems_rfs_buffer_handle buffer; + rtems_rfs_block_no block; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ)) + printf ("rtems-rfs: dir-read: dir=%ld offset=%Ld\n", + rtems_rfs_inode_ino (dir), offset); + + *length = 0; + + rc = rtems_rfs_block_map_open (fs, dir, &map); + if (rc > 0) + return rc; + + if (((rtems_rfs_fs_block_size (fs) - + (offset % rtems_rfs_fs_block_size (fs))) <= RTEMS_RFS_DIR_ENTRY_SIZE)) + offset = (((offset / rtems_rfs_fs_block_size (fs)) + 1) * + rtems_rfs_fs_block_size (fs)); + + rc = rtems_rfs_block_map_seek (fs, &map, offset, &block); + if (rc > 0) + { + if (rc == ENXIO) + rc = ENOENT; + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + rc = rtems_rfs_buffer_handle_open (fs, &buffer); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + /* + * Look for an empty entry and if this is the last block that is the end of + * the directory. + */ + while (rc == 0) + { + uint8_t* entry; + rtems_rfs_ino eino; + int elength; + int remaining; + + rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, true); + if (rc > 0) + { + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + entry = rtems_rfs_buffer_data (&buffer); + entry += map.bpos.boff; + + elength = rtems_rfs_dir_entry_length (entry); + eino = rtems_rfs_dir_entry_ino (entry); + + if (elength != RTEMS_RFS_DIR_ENTRY_EMPTY) + { + if ((elength <= RTEMS_RFS_DIR_ENTRY_SIZE) + || (elength >= rtems_rfs_fs_max_name (fs)) + || (eino < RTEMS_RFS_ROOT_INO) + || (eino >= rtems_rfs_fs_inodes (fs))) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ)) + printf ("rtems-rfs: dir-read: " \ + "bad length or ino for ino %lu: %u/%ld @ %04lx\n", + rtems_rfs_inode_ino (dir), elength, eino, map.bpos.boff); + rc = EIO; + break; + } + + memset (dirent, 0, sizeof (struct dirent)); + dirent->d_off = offset; + dirent->d_reclen = sizeof (struct dirent); + + *length += elength; + + remaining = rtems_rfs_fs_block_size (fs) - (map.bpos.boff + elength); + + if (remaining <= RTEMS_RFS_DIR_ENTRY_SIZE) + *length += remaining; + + if (elength < RTEMS_RFS_DIR_ENTRY_SIZE) + elength = 0; + else + elength -= RTEMS_RFS_DIR_ENTRY_SIZE; + if (elength > NAME_MAX) + elength = NAME_MAX; + + memcpy (dirent->d_name, entry + RTEMS_RFS_DIR_ENTRY_SIZE, elength); + + dirent->d_ino = rtems_rfs_dir_entry_ino (entry); + dirent->d_namlen = elength; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ)) + printf ("rtems-rfs: dir-read: found off:%ld ino:%ld name=%s\n", + dirent->d_off, dirent->d_ino, dirent->d_name); + break; + } + + *length += rtems_rfs_fs_block_size (fs) - map.bpos.boff; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ)) + printf ("rtems-rfs: dir-read: next block: off:%ld length:%ld\n", + (uint32_t) offset, *length); + + rc = rtems_rfs_block_map_next_block (fs, &map, &block); + if (rc == ENXIO) + rc = ENOENT; + } + + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return rc; +} + +int +rtems_rfs_dir_empty (rtems_rfs_file_system* fs, + rtems_rfs_inode_handle* dir) +{ + rtems_rfs_block_map map; + rtems_rfs_buffer_handle buffer; + rtems_rfs_block_no block; + bool empty; + int rc; + + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_READ)) + printf ("rtems-rfs: dir-empty: dir=%ld\n", rtems_rfs_inode_ino (dir)); + + empty = true; + + rc = rtems_rfs_block_map_open (fs, dir, &map); + if (rc > 0) + return rc; + + rc = rtems_rfs_block_map_seek (fs, &map, 0, &block); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + rc = rtems_rfs_buffer_handle_open (fs, &buffer); + if (rc > 0) + { + rtems_rfs_block_map_close (fs, &map); + return rc; + } + + /* + * Look for an empty entry and if this is the last block that is the end of + * the directory. + */ + while (empty) + { + uint8_t* entry; + int offset; + + rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, true); + if (rc > 0) + break; + + entry = rtems_rfs_buffer_data (&buffer); + offset = 0; + + while (offset < (rtems_rfs_fs_block_size (fs) - RTEMS_RFS_DIR_ENTRY_SIZE)) + { + rtems_rfs_ino eino; + int elength; + + elength = rtems_rfs_dir_entry_length (entry); + eino = rtems_rfs_dir_entry_ino (entry); + + if (elength == RTEMS_RFS_DIR_ENTRY_EMPTY) + break; + + if ((elength <= RTEMS_RFS_DIR_ENTRY_SIZE) + || (elength >= rtems_rfs_fs_max_name (fs)) + || (eino < RTEMS_RFS_ROOT_INO) + || (eino >= rtems_rfs_fs_inodes (fs))) + { + if (rtems_rfs_trace (RTEMS_RFS_TRACE_DIR_EMPTY)) + printf ("rtems-rfs: dir-empty: " \ + "bad length or ino for ino %lu: %u/%ld @ %04x\n", + rtems_rfs_inode_ino (dir), elength, eino, offset); + rc = EIO; + break; + } + + /* + * Ignore the current (.) and parent (..) entries. Anything else means + * the directory is not empty. + */ + if (((elength != (RTEMS_RFS_DIR_ENTRY_SIZE + 1)) || + (entry[RTEMS_RFS_DIR_ENTRY_SIZE] != '.')) && + ((elength != (RTEMS_RFS_DIR_ENTRY_SIZE + 2)) || + (entry[RTEMS_RFS_DIR_ENTRY_SIZE] != '.') || + (entry[RTEMS_RFS_DIR_ENTRY_SIZE + 1] != '.'))) + { + empty = false; + break; + } + + entry += elength; + offset += elength; + } + + if (empty) + { + rc = rtems_rfs_block_map_next_block (fs, &map, &block); + if (rc > 0) + { + if (rc == ENXIO) + rc = 0; + break; + } + } + } + + if ((rc == 0) && !empty) + rc = ENOTEMPTY; + + rtems_rfs_buffer_handle_close (fs, &buffer); + rtems_rfs_block_map_close (fs, &map); + return rc; +} -- cgit v1.2.3