+
+ if(dno->raw->i_flags & 1 << EXT4_INODE_EXTENTS)
+ {
+ FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_extent: inode %d uses extents\n"), dno->g.dnode_id));
+ return fsw_ext4_get_by_extent(vol, dno, extent);
+ }
+ else
+ {
+ FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_extent: inode %d uses direct/indirect block addressing\n"),
+ dno->g.dnode_id));
+ return fsw_ext4_get_by_blkaddr(vol, dno, extent);
+ }
+}
+
+/**
+ * New ext4 extents...
+ */
+static fsw_status_t fsw_ext4_get_by_extent(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno,
+ struct fsw_extent *extent)
+{
+ fsw_status_t status;
+ fsw_u32 bno, buf_offset;
+ int ext_cnt;
+ void *buffer;
+
+ struct ext4_extent_header *ext4_extent_header;
+ struct ext4_extent_idx *ext4_extent_idx;
+ struct ext4_extent *ext4_extent;
+
+ // Logical block requested by core...
+ bno = extent->log_start;
+
+ // First buffer is the i_block field from inode...
+ buffer = (void *)dno->raw->i_block;
+ buf_offset = 0;
+ while(1) {
+ ext4_extent_header = (struct ext4_extent_header *)((char *)buffer + buf_offset);
+ buf_offset += sizeof(struct ext4_extent_header);
+ FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: extent header with %d entries\n"),
+ ext4_extent_header->eh_entries));
+ if(ext4_extent_header->eh_magic != EXT4_EXT_MAGIC)
+ return FSW_VOLUME_CORRUPTED;
+
+ for(ext_cnt = 0;ext_cnt < ext4_extent_header->eh_entries;ext_cnt++)
+ {
+ if(ext4_extent_header->eh_depth == 0)
+ {
+ // Leaf node, the header follows actual extents
+ ext4_extent = (struct ext4_extent *)((char *)buffer + buf_offset);
+ buf_offset += sizeof(struct ext4_extent);
+ FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: extent node cover %d...\n"), ext4_extent->ee_block));
+
+ // Is the requested block in this extent?
+ if(bno >= ext4_extent->ee_block && bno < ext4_extent->ee_block + ext4_extent->ee_len)
+ {
+ extent->phys_start = ext4_extent->ee_start_lo + (bno - ext4_extent->ee_block);
+ extent->log_count = ext4_extent->ee_len - (bno - ext4_extent->ee_block);
+ return FSW_SUCCESS;
+ }
+ }
+ else
+ {
+ FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: index extents, depth %d\n"),
+ ext4_extent_header->eh_depth));
+ ext4_extent_idx = (struct ext4_extent_idx *)((char *)buffer + buf_offset);
+ buf_offset += sizeof(struct ext4_extent_idx);
+
+ FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: index node covers block %d...\n"),
+ ext4_extent_idx->ei_block));
+ if(bno >= ext4_extent_idx->ei_block)
+ {
+ // Follow extent tree...
+ status = fsw_block_get(vol, ext4_extent_idx->ei_leaf_lo, 1, (void **)&buffer);
+ if (status)
+ return status;
+ buf_offset = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ return FSW_NOT_FOUND;
+}
+
+/**
+ * The ext2/ext3 file system does not use extents, but stores a list of block numbers
+ * using the usual direct, indirect, double-indirect, triple-indirect scheme. To
+ * optimize access, this function checks if the following file blocks are mapped
+ * to consecutive disk blocks and returns a combined extent if possible.
+ */
+static fsw_status_t fsw_ext4_get_by_blkaddr(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno,
+ struct fsw_extent *extent)
+{
+ fsw_status_t status;
+ fsw_u32 bno, release_bno, buf_bcnt, file_bcnt;
+ int path[5], i;
+ fsw_u32 *buffer;