diff --git a/tsk/fs/tsk_xfs.h b/tsk/fs/tsk_xfs.h index 3b154406914110ebe7e09b13b1d4e99bdc55c8d6..058f615e94177c6165bf52d6b6a76ea7e237027b 100644 --- a/tsk/fs/tsk_xfs.h +++ b/tsk/fs/tsk_xfs.h @@ -626,6 +626,15 @@ typedef struct xfs_dir2_leaf_entry { xfs_dir2_dataptr_t address; } xfs_dir2_leaf_entry_t; +// https://github.com/torvalds/linux/blob/master/fs/xfs/libxfs/xfs_da_format.h#L418 + +#define XFS_DIR2_DATA_ALIGN_LOG 3 /* i.e., 8 bytes */ +#define XFS_DIR2_SPACE_SIZE (1ULL << (32 + XFS_DIR2_DATA_ALIGN_LOG)) +#define XFS_DIR2_LEAF_SPACE 1 +#define XFS_DIR2_LEAF_OFFSET (XFS_DIR2_LEAF_SPACE * XFS_DIR2_SPACE_SIZE) +#define XFS_DIR2_FREE_SPACE 2 +#define XFS_DIR2_FREE_OFFSET (XFS_DIR2_FREE_SPACE * XFS_DIR2_SPACE_SIZE) + /* * Structure of an xfs file system handle. */ diff --git a/tsk/fs/xfs.cpp b/tsk/fs/xfs.cpp index 518d0c972d3b5d63fefcfbde09a8a44bb7a2db54..a9b65eadd4eedf099b30bf62013d69d2aeea0171 100644 --- a/tsk/fs/xfs.cpp +++ b/tsk/fs/xfs.cpp @@ -129,7 +129,7 @@ TSK_FS_META_FLAG_ENUM xfs_inode_getallocflag(XFSFS_INFO * xfsfs, TSK_INUM_T dino // found in a range, go one level down in b+tree, read the next found_key = true; - cur_block_num = (TSK_DADDR_T) ag_num * (TSK_DADDR_T) xfsfs->fs->sb_agblocks + (TSK_DADDR_T) tsk_getu32(TSK_BIG_ENDIAN, iptrs[cur_key]); + cur_block_num = (TSK_DADDR_T) ag_num * (TSK_DADDR_T) xfsfs->fs->sb_agblocks + (TSK_DADDR_T) tsk_getu32(TSK_BIG_ENDIAN, &iptrs[cur_key]); if (tsk_verbose) { tsk_fprintf(stderr, "go one level down in b+tree, cur_block_num = %u \n", cur_block_num); } @@ -1666,6 +1666,8 @@ xfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_RETVAL_ENUM retval = TSK_OK; TSK_FS_NAME *fs_name; + // Assuming fs_meta->type == TSK_FS_META_TYPE_DIR + if (tsk_verbose) { tsk_fprintf(stderr, "a_fs->first_inum = %d, a_fs->last_inum = %d \n", a_fs->first_inum, a_fs->last_inum); } if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) { @@ -1837,207 +1839,381 @@ xfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, xfs_bmbt_rec_t *extent_data_offset = (xfs_bmbt_rec_t *) fs_meta->content_ptr; uint32_t nextents = fs_meta->content_len / sizeof(xfs_bmbt_rec_t); - if (nextents == 0) - { - tsk_fprintf(stderr, "nextents == 0 \n", nextents); - } + tsk_fprintf(stderr, "nextents == %" PRId64 ", fs_meta->size = %" PRId64 " \n", nextents, fs_meta->size); - if (fs_meta->type == TSK_FS_META_TYPE_DIR && nextents > 1) + if (fs_meta->size <= xfs->fs_info.block_size /* nextents can be used too */) { - tsk_fprintf(stderr, "fs_meta->type == TSK_FS_META_TYPE_DIR and nextents = %d > 1. Directory blocks with multiple extents are unsupported now \n", nextents); - return TSK_ERR; - } - - // unpack extent - xfs_bmbt_irec_t irec; - memset(&irec, 0, sizeof(irec)); - xfs_bmbt_disk_get_all(extent_data_offset, &irec); - - if (tsk_verbose) { - tsk_fprintf(stderr, "extent_num = %d, adding br_startblock = %d / br_blockcount = %d \n", /* extent_num */ 0, irec.br_startblock, irec.br_blockcount); - } - - if ((dirbuf = (char*) tsk_malloc((size_t)a_fs->block_size)) == NULL) { - return TSK_ERR; - } + // unpack extent + xfs_bmbt_irec_t irec; + memset(&irec, 0, sizeof(irec)); + xfs_bmbt_disk_get_all(extent_data_offset, &irec); - size = irec.br_blockcount * a_fs->block_size; + if (tsk_verbose) { + tsk_fprintf(stderr, "extent_num = %d, adding br_startblock = %d / br_blockcount = %d \n", /* extent_num */ 0, irec.br_startblock, irec.br_blockcount); + } - xfs_agnumber_t ag_num = (TSK_OFF_T) irec.br_startblock >> xfs->fs->sb_agblklog; - uint64_t rel_blk_neg = 1 << (xfs->fs->sb_agblklog); - rel_blk_neg -= 1; - uint64_t rel_blk = (TSK_OFF_T) irec.br_startblock & rel_blk_neg; - TSK_OFF_T offset = ((TSK_OFF_T) ag_num * (TSK_OFF_T) xfs->fs->sb_agblocks + rel_blk) * (TSK_OFF_T) a_fs->block_size; + if ((dirbuf = (char*) tsk_malloc((size_t)a_fs->block_size)) == NULL) { + return TSK_ERR; + } - TSK_OFF_T offset_in_block = 0; + size = irec.br_blockcount * a_fs->block_size; - // read xfs_dir2_data_hdr (on a v5 filesystem this is xfs_dir3_data_hdr_t) + xfs_agnumber_t ag_num = (TSK_OFF_T) irec.br_startblock >> xfs->fs->sb_agblklog; + uint64_t rel_blk_neg = 1 << (xfs->fs->sb_agblklog); + rel_blk_neg -= 1; + uint64_t rel_blk = (TSK_OFF_T) irec.br_startblock & rel_blk_neg; + TSK_OFF_T offset = ((TSK_OFF_T) ag_num * (TSK_OFF_T) xfs->fs->sb_agblocks + rel_blk) * (TSK_OFF_T) a_fs->block_size; - ssize_t len = (size > a_fs->block_size) ? a_fs->block_size : size; - ssize_t cnt = tsk_fs_read(a_fs, offset, dirbuf, len); - if (cnt != len) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_FS_FWALK); - tsk_error_set_errstr - ("xfs_dir_open_meta: Error reading directory contents: %" - PRIuINUM "\n", a_addr); - free(dirbuf); - return TSK_COR; - } + TSK_OFF_T offset_in_block = 0; - xfs_dir2_data_hdr data_hdr; - memcpy(&data_hdr, dirbuf + offset_in_block, sizeof(data_hdr)); - offset_in_block += sizeof(data_hdr); - - data_hdr.bestfree[0].offset = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[0].offset); - data_hdr.bestfree[0].length = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[0].length); - data_hdr.bestfree[1].offset = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[1].offset); - data_hdr.bestfree[1].length = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[1].length); - data_hdr.bestfree[2].offset = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[2].offset); - data_hdr.bestfree[2].length = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[2].length); - - xfs_dir2_block_tail block_tail; - memcpy(&block_tail, dirbuf + size - sizeof(xfs_dir2_block_tail), sizeof(xfs_dir2_block_tail)); - block_tail.count = tsk_getu32(TSK_BIG_ENDIAN, &block_tail.count); - block_tail.stale = tsk_getu32(TSK_BIG_ENDIAN, &block_tail.stale); - uint32_t leaf_offset = size - sizeof(xfs_dir2_block_tail) - block_tail.count * sizeof(xfs_dir2_leaf_entry_t); - - if (leaf_offset >= len) { - tsk_fprintf(stderr, "leaf_offset = %d past len = %d \n", leaf_offset, len); - tsk_error_set_errno(TSK_ERR_FS_FWALK); - tsk_error_set_errstr - ("xfs_dir_open_meta: Error reading directory contents: %" - PRIuINUM "\n", a_addr); - return TSK_COR; - } + // read xfs_dir2_data_hdr (on a v5 filesystem this is xfs_dir3_data_hdr_t) - if (tsk_verbose) { - tsk_fprintf(stderr, "block_tail.count = %d, leaf_offset = %d (out of len = %d) \n", block_tail.count, leaf_offset, len); - } + ssize_t len = (size > a_fs->block_size) ? a_fs->block_size : size; + ssize_t cnt = tsk_fs_read(a_fs, offset, dirbuf, len); + if (cnt != len) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS_FWALK); + tsk_error_set_errstr + ("xfs_dir_open_meta: Error reading directory contents: %" + PRIuINUM "\n", a_addr); + free(dirbuf); + return TSK_COR; + } - size -= len; - offset += len; + xfs_dir2_data_hdr data_hdr; + memcpy(&data_hdr, dirbuf + offset_in_block, sizeof(data_hdr)); + offset_in_block += sizeof(data_hdr); + + data_hdr.bestfree[0].offset = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[0].offset); + data_hdr.bestfree[0].length = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[0].length); + data_hdr.bestfree[1].offset = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[1].offset); + data_hdr.bestfree[1].length = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[1].length); + data_hdr.bestfree[2].offset = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[2].offset); + data_hdr.bestfree[2].length = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[2].length); + + xfs_dir2_block_tail block_tail; + memcpy(&block_tail, dirbuf + size - sizeof(xfs_dir2_block_tail), sizeof(xfs_dir2_block_tail)); + block_tail.count = tsk_getu32(TSK_BIG_ENDIAN, &block_tail.count); + block_tail.stale = tsk_getu32(TSK_BIG_ENDIAN, &block_tail.stale); + uint32_t leaf_offset = size - sizeof(xfs_dir2_block_tail) - block_tail.count * sizeof(xfs_dir2_leaf_entry_t); + + if (leaf_offset >= len) { + tsk_fprintf(stderr, "leaf_offset = %d past len = %d \n", leaf_offset, len); + tsk_error_set_errno(TSK_ERR_FS_FWALK); + tsk_error_set_errstr + ("xfs_dir_open_meta: Error reading directory contents: %" + PRIuINUM "\n", a_addr); + return TSK_COR; + } - xfs_dir2_data_entry_t data_entry; + if (tsk_verbose) { + tsk_fprintf(stderr, "block_tail.count = %d, leaf_offset = %d (out of len = %d) \n", block_tail.count, leaf_offset, len); + } - while (offset_in_block < leaf_offset) - { - if (tsk_verbose) { tsk_fprintf(stderr, "offset_in_block = %d \n", offset_in_block); } + size -= len; + offset += len; - uint16_t *xfs_dir2_data_unused_freetag = (uint16_t*) (dirbuf + offset_in_block); + xfs_dir2_data_entry_t data_entry; - if (*xfs_dir2_data_unused_freetag == 0xffff) + while (offset_in_block < leaf_offset) { - xfs_dir2_data_unused *data_unused = (xfs_dir2_data_unused *) (dirbuf + offset_in_block); + if (tsk_verbose) { tsk_fprintf(stderr, "offset_in_block = %d \n", offset_in_block); } - if (tsk_verbose) { tsk_fprintf(stderr, "offset_in_block = % is a free space, shifting forward by tsk_getu32(TSK_BIG_ENDIAN, &data_unused->length)) = %d \n", offset_in_block, tsk_getu32(TSK_BIG_ENDIAN, &data_unused->length)); } - offset_in_block += tsk_getu32(TSK_BIG_ENDIAN, &data_unused->length); - } - else - { - if (offset_in_block + sizeof(uint64_t) + sizeof(uint8_t) >= leaf_offset) + uint16_t *xfs_dir2_data_unused_freetag = (uint16_t*) (dirbuf + offset_in_block); + + if (*xfs_dir2_data_unused_freetag == 0xffff) { - tsk_error_set_errno(TSK_ERR_FS_FWALK); - tsk_error_set_errstr - ("xfs_dir_open_meta: Error reading directory contents: %" - PRIuINUM "\n", a_addr); - return TSK_COR; + xfs_dir2_data_unused *data_unused = (xfs_dir2_data_unused *) (dirbuf + offset_in_block); + + if (tsk_verbose) { tsk_fprintf(stderr, "offset_in_block = % is a free space, shifting forward by tsk_getu32(TSK_BIG_ENDIAN, &data_unused->length)) = %d \n", offset_in_block, tsk_getu32(TSK_BIG_ENDIAN, &data_unused->length)); } + offset_in_block += tsk_getu32(TSK_BIG_ENDIAN, &data_unused->length); } + else + { + if (offset_in_block + sizeof(uint64_t) + sizeof(uint8_t) >= leaf_offset) + { + tsk_error_set_errno(TSK_ERR_FS_FWALK); + tsk_error_set_errstr + ("xfs_dir_open_meta: Error reading directory contents: %" + PRIuINUM "\n", a_addr); + return TSK_COR; + } - memcpy(&data_entry, dirbuf + offset_in_block, sizeof(uint64_t) + sizeof(uint8_t)); - offset_in_block += sizeof(uint64_t) + sizeof(uint8_t); + memcpy(&data_entry, dirbuf + offset_in_block, sizeof(uint64_t) + sizeof(uint8_t)); + offset_in_block += sizeof(uint64_t) + sizeof(uint8_t); - data_entry.inumber = tsk_getu64(TSK_BIG_ENDIAN, &data_entry.inumber); - fs_name->meta_addr = data_entry.inumber; + data_entry.inumber = tsk_getu64(TSK_BIG_ENDIAN, &data_entry.inumber); + fs_name->meta_addr = data_entry.inumber; - if (offset_in_block + data_entry.namelen + ftype_size >= leaf_offset) - { - tsk_error_set_errno(TSK_ERR_FS_FWALK); - tsk_error_set_errstr - ("xfs_dir_open_meta: Error reading directory contents: %" - PRIuINUM "\n", a_addr); - return TSK_COR; - } + if (offset_in_block + data_entry.namelen + ftype_size >= leaf_offset) + { + tsk_error_set_errno(TSK_ERR_FS_FWALK); + tsk_error_set_errstr + ("xfs_dir_open_meta: Error reading directory contents: %" + PRIuINUM "\n", a_addr); + return TSK_COR; + } - char *name = (char *) dirbuf + offset_in_block; - memcpy(fs_name->name, name, data_entry.namelen); - offset_in_block += data_entry.namelen; - fs_name->name[data_entry.namelen] = '\0'; + char *name = (char *) dirbuf + offset_in_block; + memcpy(fs_name->name, name, data_entry.namelen); + offset_in_block += data_entry.namelen; + fs_name->name[data_entry.namelen] = '\0'; - uint8_t ftype = 0; - if (ftype_size > 0) - { - ftype = * (uint8_t *) (name + data_entry.namelen); - } - else - { - xfs_dinode_t *dino_buf = NULL; - ssize_t dinodesize = - xfs->fs->sb_inodesize > - sizeof(xfs_dinode) ? xfs->fs->sb_inodesize : sizeof(xfs_dinode); - if ((dino_buf = (xfs_dinode_t *) tsk_malloc(dinodesize)) == NULL) { - return TSK_ERR; + uint8_t ftype = 0; + if (ftype_size > 0) + { + ftype = * (uint8_t *) (name + data_entry.namelen); + } + else + { + xfs_dinode_t *dino_buf = NULL; + ssize_t dinodesize = + xfs->fs->sb_inodesize > + sizeof(xfs_dinode) ? xfs->fs->sb_inodesize : sizeof(xfs_dinode); + if ((dino_buf = (xfs_dinode_t *) tsk_malloc(dinodesize)) == NULL) { + return TSK_ERR; + } + + if (xfs_dinode_load(xfs, fs_name->meta_addr, dino_buf)) { + free(dino_buf); + return TSK_ERR; + } + + ftype = dino_buf->di_core.di_mode & XFS_IN_FMT; + } + + uint32_t ftype32 = (uint32_t) ftype << 12; + switch (ftype32) { + case XFS_IN_REG: + fs_meta->type = TSK_FS_META_TYPE_REG; + break; + case XFS_IN_DIR: + fs_meta->type = TSK_FS_META_TYPE_DIR; + break; + case XFS_IN_SOCK: + fs_meta->type = TSK_FS_META_TYPE_SOCK; + break; + case XFS_IN_LNK: + fs_meta->type = TSK_FS_META_TYPE_LNK; + break; + case XFS_IN_BLK: + fs_meta->type = TSK_FS_META_TYPE_BLK; + break; + case XFS_IN_CHR: + fs_meta->type = TSK_FS_META_TYPE_CHR; + break; + case XFS_IN_FIFO: + fs_meta->type = TSK_FS_META_TYPE_FIFO; + break; + default: + fs_meta->type = TSK_FS_META_TYPE_UNDEF; + break; } - if (xfs_dinode_load(xfs, fs_name->meta_addr, dino_buf)) { - free(dino_buf); + // we iterate over allocated directories + fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; + + if (tsk_verbose) { tsk_fprintf(stderr, "namelen = %d, fs_name->name = %s, fs_meta->type = %d, fs_name->meta_addr = %" PRId64 " fs_name->flags = \n", data_entry.namelen, fs_name->name, fs_meta->type, fs_name->meta_addr, fs_name->flags); } + + if (tsk_fs_dir_add(fs_dir, fs_name)) { + tsk_fs_name_free(fs_name); return TSK_ERR; } - ftype = dino_buf->di_core.di_mode & XFS_IN_FMT; - } + // skipping xfs_dir2_data_off_t tag (and ftype, if present) + offset_in_block += sizeof(xfs_dir2_data_off_t) + ftype_size; - uint32_t ftype32 = (uint32_t) ftype << 12; - switch (ftype32) { - case XFS_IN_REG: - fs_meta->type = TSK_FS_META_TYPE_REG; - break; - case XFS_IN_DIR: - fs_meta->type = TSK_FS_META_TYPE_DIR; - break; - case XFS_IN_SOCK: - fs_meta->type = TSK_FS_META_TYPE_SOCK; - break; - case XFS_IN_LNK: - fs_meta->type = TSK_FS_META_TYPE_LNK; - break; - case XFS_IN_BLK: - fs_meta->type = TSK_FS_META_TYPE_BLK; - break; - case XFS_IN_CHR: - fs_meta->type = TSK_FS_META_TYPE_CHR; - break; - case XFS_IN_FIFO: - fs_meta->type = TSK_FS_META_TYPE_FIFO; - break; - default: - fs_meta->type = TSK_FS_META_TYPE_UNDEF; - break; + // x64 alignment + offset_in_block = roundup(offset_in_block, sizeof(uint64_t)); } + } + } + else + { + for(uint32_t extent_num = 0; extent_num < nextents; extent_num++) + { + // unpack extent + xfs_bmbt_irec_t irec; + memset(&irec, 0, sizeof(irec)); + xfs_bmbt_disk_get_all(&extent_data_offset[extent_num], &irec); - // we iterate over allocated directories - fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; + // skip it if that's not a data block + if (irec.br_startoff >= XFS_DIR2_LEAF_OFFSET / a_fs->block_size || irec.br_startoff >= XFS_DIR2_FREE_OFFSET / a_fs->block_size) + { + continue; + } - if (tsk_verbose) { tsk_fprintf(stderr, "namelen = %d, fs_name->name = %s, fs_meta->type = %d, fs_name->meta_addr = %" PRId64 " fs_name->flags = \n", data_entry.namelen, fs_name->name, fs_meta->type, fs_name->meta_addr, fs_name->flags); } + if (tsk_verbose) { + tsk_fprintf(stderr, "extent_num = %d, adding irec.br_startoff = %" PRId64 " br_startblock = %" PRId64 " / br_blockcount = %" PRId64 ", XFS_DIR2_LEAF_OFFSET = %" PRId64 ", XFS_DIR2_FREE_OFFSET = %" PRId64 "\n", extent_num, irec.br_startoff, irec.br_startblock, irec.br_blockcount, XFS_DIR2_LEAF_OFFSET, XFS_DIR2_FREE_OFFSET); + } - if (tsk_fs_dir_add(fs_dir, fs_name)) { - tsk_fs_name_free(fs_name); + size = (TSK_OFF_T) irec.br_blockcount * (TSK_OFF_T) a_fs->block_size; + + if ((dirbuf = (char*) tsk_malloc(size)) == NULL) { return TSK_ERR; } - // skipping xfs_dir2_data_off_t tag (and ftype, if present) - offset_in_block += sizeof(xfs_dir2_data_off_t) + ftype_size; + xfs_agnumber_t ag_num = (TSK_OFF_T) irec.br_startblock >> xfs->fs->sb_agblklog; + uint64_t rel_blk_neg = 1 << (xfs->fs->sb_agblklog); + rel_blk_neg -= 1; + uint64_t rel_blk = (TSK_OFF_T) irec.br_startblock & rel_blk_neg; + TSK_OFF_T offset = ((TSK_OFF_T) ag_num * (TSK_OFF_T) xfs->fs->sb_agblocks + rel_blk) * (TSK_OFF_T) a_fs->block_size; + + // read xfs_dir2_data_hdr (on a v5 filesystem this is xfs_dir3_data_hdr_t) + + // let's read the whole extent, but parse it block-by-block + ssize_t len = size; + ssize_t cnt = tsk_fs_read(a_fs, offset, dirbuf, len); + if (cnt != len) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS_FWALK); + tsk_error_set_errstr + ("xfs_dir_open_meta: Error reading directory contents: %" + PRIuINUM "\n", a_addr); + free(dirbuf); + return TSK_COR; + } - // x64 alignment - offset_in_block = roundup(offset_in_block, sizeof(uint64_t)); + for (uint16_t block_num = 0; block_num < irec.br_blockcount; block_num++) + { + TSK_OFF_T offset_in_block = (TSK_OFF_T) block_num * (TSK_OFF_T) a_fs->block_size; + TSK_OFF_T limit = (TSK_OFF_T) (block_num + 1) * (TSK_OFF_T) a_fs->block_size; + + xfs_dir2_data_hdr data_hdr; + memcpy(&data_hdr, dirbuf + offset_in_block, sizeof(data_hdr)); + offset_in_block += sizeof(data_hdr); + + data_hdr.bestfree[0].offset = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[0].offset); + data_hdr.bestfree[0].length = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[0].length); + data_hdr.bestfree[1].offset = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[1].offset); + data_hdr.bestfree[1].length = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[1].length); + data_hdr.bestfree[2].offset = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[2].offset); + data_hdr.bestfree[2].length = tsk_getu16(TSK_BIG_ENDIAN, &data_hdr.bestfree[2].length); + + xfs_dir2_data_entry_t data_entry; + + while (offset_in_block < limit) + { + if (tsk_verbose) { tsk_fprintf(stderr, "offset_in_block = %d \n", offset_in_block); } + + uint16_t *xfs_dir2_data_unused_freetag = (uint16_t*) (dirbuf + offset_in_block); + + if (*xfs_dir2_data_unused_freetag == 0xffff) + { + xfs_dir2_data_unused *data_unused = (xfs_dir2_data_unused *) (dirbuf + offset_in_block); + + if (tsk_verbose) { tsk_fprintf(stderr, "offset_in_block = % is a free space, shifting forward by tsk_getu32(TSK_BIG_ENDIAN, &data_unused->length)) = %d \n", offset_in_block, tsk_getu32(TSK_BIG_ENDIAN, &data_unused->length)); } + offset_in_block += tsk_getu32(TSK_BIG_ENDIAN, &data_unused->length); + } + else + { + if (offset_in_block + sizeof(uint64_t) + sizeof(uint8_t) >= limit) + { + tsk_error_set_errno(TSK_ERR_FS_FWALK); + tsk_error_set_errstr + ("xfs_dir_open_meta: Error reading directory contents: %" + PRIuINUM "\n", a_addr); + return TSK_COR; + } + + memcpy(&data_entry, dirbuf + offset_in_block, sizeof(uint64_t) + sizeof(uint8_t)); + offset_in_block += sizeof(uint64_t) + sizeof(uint8_t); + + data_entry.inumber = tsk_getu64(TSK_BIG_ENDIAN, &data_entry.inumber); + fs_name->meta_addr = data_entry.inumber; + + + if (offset_in_block + data_entry.namelen + ftype_size >= limit) + { + tsk_error_set_errno(TSK_ERR_FS_FWALK); + tsk_error_set_errstr + ("xfs_dir_open_meta: Error reading directory contents: %" + PRIuINUM "\n", a_addr); + return TSK_COR; + } + + char *name = (char *) dirbuf + offset_in_block; + memcpy(fs_name->name, name, data_entry.namelen); + offset_in_block += data_entry.namelen; + fs_name->name[data_entry.namelen] = '\0'; + + uint8_t ftype = 0; + if (ftype_size > 0) + { + ftype = * (uint8_t *) (name + data_entry.namelen); + } + else + { + xfs_dinode_t *dino_buf = NULL; + ssize_t dinodesize = + xfs->fs->sb_inodesize > + sizeof(xfs_dinode) ? xfs->fs->sb_inodesize : sizeof(xfs_dinode); + if ((dino_buf = (xfs_dinode_t *) tsk_malloc(dinodesize)) == NULL) { + return TSK_ERR; + } + + if (xfs_dinode_load(xfs, fs_name->meta_addr, dino_buf)) { + free(dino_buf); + return TSK_ERR; + } + + ftype = dino_buf->di_core.di_mode & XFS_IN_FMT; + } + + uint32_t ftype32 = (uint32_t) ftype << 12; + switch (ftype32) { + case XFS_IN_REG: + fs_meta->type = TSK_FS_META_TYPE_REG; + break; + case XFS_IN_DIR: + fs_meta->type = TSK_FS_META_TYPE_DIR; + break; + case XFS_IN_SOCK: + fs_meta->type = TSK_FS_META_TYPE_SOCK; + break; + case XFS_IN_LNK: + fs_meta->type = TSK_FS_META_TYPE_LNK; + break; + case XFS_IN_BLK: + fs_meta->type = TSK_FS_META_TYPE_BLK; + break; + case XFS_IN_CHR: + fs_meta->type = TSK_FS_META_TYPE_CHR; + break; + case XFS_IN_FIFO: + fs_meta->type = TSK_FS_META_TYPE_FIFO; + break; + default: + fs_meta->type = TSK_FS_META_TYPE_UNDEF; + break; + } + + // we iterate over allocated directories + fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; + + if (tsk_verbose) { tsk_fprintf(stderr, "namelen = %d, fs_name->name = %s, fs_meta->type = %d, fs_name->meta_addr = %" PRId64 " fs_name->flags = \n", data_entry.namelen, fs_name->name, fs_meta->type, fs_name->meta_addr, fs_name->flags); } + + if (tsk_fs_dir_add(fs_dir, fs_name)) { + tsk_fs_name_free(fs_name); + return TSK_ERR; + } + + // skipping xfs_dir2_data_off_t tag (and ftype, if present) + offset_in_block += sizeof(xfs_dir2_data_off_t) + ftype_size; + + // x64 alignment + offset_in_block = roundup(offset_in_block, sizeof(uint64_t)); + } + } + } } } free(dirbuf); } else if (fs_meta->content_type == TSK_FS_META_CONTENT_TYPE_XFS_FMT_BTREE) { - if (tsk_verbose) { tsk_fprintf(stderr, "fs_meta->content_type == TSK_FS_META_CONTENT_TYPE_XFS_FMT_BTREE is not supported yet"); } + tsk_fprintf(stderr, "fs_meta->content_type == XFS_DINODE_FMT_BTREE is not supported yet \n"); } return retval;