diff --git a/tsk/fs/ext2fs.c b/tsk/fs/ext2fs.c index f36a193d86221b4ae3b3e96db87bac50f20b1d22..cc42ff5f51c61976053dad83a9fb5bfdaa321d37 100755 --- a/tsk/fs/ext2fs.c +++ b/tsk/fs/ext2fs.c @@ -452,13 +452,14 @@ static uint8_t * @param ext2fs A ext2fs file system information structure * @param dino_inum Metadata address * @param dino_buf The buffer to store the block in (must be size of ext2fs->inode_size or larger) + * @param ea_buf The buffer to hold the extended attribute data * * return 1 on error and 0 on success * */ static uint8_t ext2fs_dinode_load(EXT2FS_INFO * ext2fs, TSK_INUM_T dino_inum, - ext2fs_inode * dino_buf) + ext2fs_inode * dino_buf, uint8_t ** ea_buf, size_t * ea_buf_len) { EXT2_GRPNUM_T grp_num; TSK_OFF_T addr; @@ -551,6 +552,16 @@ ext2fs_dinode_load(EXT2FS_INFO * ext2fs, TSK_INUM_T dino_inum, //DEBUG printf("Inode Size: %d, %d, %d, %d\n", sizeof(ext2fs_inode), *ext2fs->fs->s_inode_size, ext2fs->inode_size, *ext2fs->fs->s_want_extra_isize); //DEBUG debug_print_buf((char *)dino_buf, ext2fs->inode_size); + // Check if we have an extended attribute in the inode + if (ext2fs->inode_size > EXT2_EA_INODE_OFFSET) { + // The extended attribute data immediatly follows the standard inode data + *ea_buf = (char*)dino_buf + EXT2_EA_INODE_OFFSET; + *ea_buf_len = ext2fs->inode_size - EXT2_EA_INODE_OFFSET; + } + else { + *ea_buf = NULL; + } + if (tsk_verbose) { tsk_fprintf(stderr, "%" PRIuINUM " m/l/s=%o/%d/%" PRIu32 @@ -575,21 +586,132 @@ ext2fs_dinode_load(EXT2FS_INFO * ext2fs, TSK_INUM_T dino_inum, return 0; } +/** +* \internal +* Loads attribute for Ext4 inline storage method. +* @param fs_file File to load attrs +* @param ea_buf Extended attribute buffer +* @param ea_buf_len Extended attribute buffer length +* @returns 0 on success, 1 otherwise +*/ +static uint8_t +ext4_load_attrs_inline(TSK_FS_FILE *fs_file, const uint8_t * ea_buf, size_t ea_buf_len) +{ + TSK_FS_META *fs_meta = fs_file->meta; + TSK_FS_ATTR *fs_attr; + + // see if we have already loaded the attr + if ((fs_meta->attr != NULL) + && (fs_meta->attr_state == TSK_FS_META_ATTR_STUDIED)) { + return 0; + } + + if (fs_meta->attr_state == TSK_FS_META_ATTR_ERROR) { + return 1; + } + + // First load the data from the extended attr (if present) + const char *ea_inline_data = NULL; + uint32_t ea_inline_data_len = 0; + if ((ea_buf != NULL) && (ea_buf_len > 4 + sizeof(ext2fs_ea_entry)) + && (tsk_getu32(fs_file->fs_info->endian, ea_buf) == EXT2_EA_MAGIC)) { + + // First entry starts after the four byte header + size_t index = 4; + ext2fs_ea_entry *ea_entry = (ext2fs_ea_entry*) &(ea_buf[index]); + + // The end of the list of entries is marked by two null bytes + while ((ea_entry->nlen != 0) || (ea_entry->nidx != 0)) { + + // It looks like the continuation of inline data is stored in system.data. + // Make sure we have room to read the attr name 'data'. + if ((ea_entry->nidx == EXT2_EA_IDX_SYSTEM) + && (ea_entry->nlen == 4) + && (index + sizeof(ext2fs_ea_entry) + strlen("data") < ea_buf_len) + && (strncmp(&(ea_entry->name), "data", 4)) == 0) { + + // This is the right attribute. Check that the length and offset are valid. + // The offset is from the beginning of the entries, i.e., four bytes into the buffer. + uint32_t offset = tsk_getu32(fs_file->fs_info->endian, ea_entry->val_off); + uint32_t size = tsk_getu32(fs_file->fs_info->endian, ea_entry->val_size); + if (4 + offset + size <= ea_buf_len) { + ea_inline_data = &(ea_buf[4 + offset]); + ea_inline_data_len = size; + break; + } + } + + // Prepare to load the next entry. + // The entry size is the size of the struct plus the length of the name, minus one + // because the struct contains the first character of the name. + index += sizeof(ext2fs_ea_entry) + ea_entry->nlen - 1; + + // Make sure there's room for the next entry plus the 'data' name we're looking for. + if (index + sizeof(ext2fs_ea_entry) + strlen("data") > ea_buf_len) { + break; + } + ext2fs_ea_entry *ea_entry = (ext2fs_ea_entry*) &(ea_buf[index]); + } + } + + // Combine the two parts of the inline data for the resident attribute. For now, make a + // buffer for the full file size - this may be different than the length of the data + // from the inode if we have sparse data. + uint8_t *resident_data; + if ((resident_data = (uint8_t*)tsk_malloc(fs_meta->size)) == NULL) { + return 1; + } + memset(resident_data, 0, fs_meta->size); + + // Copy the data from the inode. + size_t inode_data_len = (fs_meta->size < EXT2_INLINE_MAX_DATA_LEN) ? fs_meta->size : EXT2_INLINE_MAX_DATA_LEN; + memcpy(resident_data, fs_meta->content_ptr, inode_data_len); + + // If we need more data and found an extended attribute, append that data + if ((fs_meta->size > EXT2_INLINE_MAX_DATA_LEN) && (ea_inline_data_len > 0)) { + // Don't go beyond the size of the file + size_t ea_data_len = (inode_data_len + ea_inline_data_len < (uint64_t)fs_meta->size) ? inode_data_len + ea_inline_data_len : fs_meta->size - inode_data_len; + memcpy(resident_data + inode_data_len, ea_inline_data, ea_data_len); + } + + fs_meta->attr = tsk_fs_attrlist_alloc(); + if ((fs_attr = + tsk_fs_attrlist_getnew(fs_meta->attr, + TSK_FS_ATTR_RES)) == NULL) { + free(resident_data); + return 1; + } + + // Set the details in the fs_attr structure + if (tsk_fs_attr_set_str(fs_file, fs_attr, "DATA", + TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT, + (void*)resident_data, + fs_meta->size)) { + free(resident_data); + fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; + return 1; + } + + free(resident_data); + fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED; + return 0; +} + /* ext2fs_dinode_copy - copy cached disk inode into generic inode * * returns 1 on error and 0 on success * */ static uint8_t -ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta, - TSK_INUM_T inum, const ext2fs_inode * dino_buf) +ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_FILE * fs_file, + TSK_INUM_T inum, const ext2fs_inode * dino_buf, const uint8_t * ea_buf, size_t ea_buf_len) { int i; TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info; + TSK_FS_META * fs_meta = fs_file->meta; ext2fs_sb *sb = ext2fs->fs; EXT2_GRPNUM_T grp_num; TSK_INUM_T ibase = 0; - if (dino_buf == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); @@ -728,9 +850,20 @@ ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta, /* NOTE TSK_DADDR_T != uint32_t, so lets make sure we use uint32_t */ addr_ptr = (uint32_t *) fs_meta->content_ptr; for (i = 0; i < EXT2FS_NDADDR + EXT2FS_NIADDR; i++) { - addr_ptr[i] = tsk_gets32(fs->endian, dino_buf->i_block[i]);; + addr_ptr[i] = tsk_gets32(fs->endian, dino_buf->i_block[i]); } } + else if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_INLINE_DATA) { + uint32_t *addr_ptr; + fs_meta->content_type = TSK_FS_META_CONTENT_TYPE_EXT4_INLINE; + addr_ptr = (uint32_t *)fs_meta->content_ptr; + for (i = 0; i < EXT2FS_NDADDR + EXT2FS_NIADDR; i++) { + addr_ptr[i] = tsk_gets32(fs->endian, dino_buf->i_block[i]); + } + + // For inline data we create the default attribute now + ext4_load_attrs_inline(fs_file, ea_buf, ea_buf_len); + } else { TSK_DADDR_T *addr_ptr; addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr; @@ -887,6 +1020,8 @@ ext2fs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file, { EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; ext2fs_inode *dino_buf = NULL; + uint8_t *ea_buf = NULL; + size_t ea_buf_len = 0; unsigned int size = 0; if (a_fs_file == NULL) { @@ -919,12 +1054,12 @@ ext2fs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file, return 1; } - if (ext2fs_dinode_load(ext2fs, inum, dino_buf)) { + if (ext2fs_dinode_load(ext2fs, inum, dino_buf, &ea_buf, &ea_buf_len)) { free(dino_buf); return 1; } - if (ext2fs_dinode_copy(ext2fs, a_fs_file->meta, inum, dino_buf)) { + if (ext2fs_dinode_copy(ext2fs, a_fs_file, inum, dino_buf, ea_buf, ea_buf_len)) { free(dino_buf); return 1; } @@ -956,6 +1091,8 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum, TSK_FS_FILE *fs_file; unsigned int myflags; ext2fs_inode *dino_buf = NULL; + uint8_t *ea_buf = NULL; + size_t ea_buf_len = 0; unsigned int size = 0; // clean up any error messages that are lying around @@ -1089,7 +1226,7 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum, if ((flags & myflags) != myflags) continue; - if (ext2fs_dinode_load(ext2fs, inum, dino_buf)) { + if (ext2fs_dinode_load(ext2fs, inum, dino_buf, &ea_buf, &ea_buf_len)) { tsk_fs_file_close(fs_file); free(dino_buf); return 1; @@ -1119,7 +1256,7 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum, * Fill in a file system-independent inode structure and pass control * to the application. */ - if (ext2fs_dinode_copy(ext2fs, fs_file->meta, inum, dino_buf)) { + if (ext2fs_dinode_copy(ext2fs, fs_file, inum, dino_buf, ea_buf, ea_buf_len)) { tsk_fs_meta_close(fs_file->meta); free(dino_buf); return 1; @@ -1694,7 +1831,7 @@ ext4_load_attrs_extents(TSK_FS_FILE *fs_file) } length = roundup(fs_meta->size, fs_info->block_size); - + if ((fs_attr = tsk_fs_attrlist_getnew(fs_meta->attr, TSK_FS_ATTR_NONRES)) == NULL) { @@ -1755,7 +1892,7 @@ ext4_load_attrs_extents(TSK_FS_FILE *fs_file) ("ext2fs_load_attr: Inode reports too many extent indices"); return 1; } - + if ((fs_attr_extent = tsk_fs_attrlist_getnew(fs_meta->attr, TSK_FS_ATTR_NONRES)) == NULL) { @@ -1815,6 +1952,10 @@ ext2fs_load_attrs(TSK_FS_FILE * fs_file) if (fs_file->meta->content_type == TSK_FS_META_CONTENT_TYPE_EXT4_EXTENTS) { return ext4_load_attrs_extents(fs_file); } + else if (fs_file->meta->content_type == TSK_FS_META_CONTENT_TYPE_EXT4_INLINE) { + // Inline attributes are loaded in dinode_copy + return 0; + } else { return tsk_fs_unix_make_data_run(fs_file); } @@ -2683,6 +2824,8 @@ ext2fs_istat(TSK_FS_INFO * fs, TSK_FS_ISTAT_FLAG_ENUM istat_flags, FILE * hFile, EXT2FS_PRINT_ADDR print; const TSK_FS_ATTR *fs_attr_indir; ext2fs_inode *dino_buf = NULL; + uint8_t *ea_buf = NULL; + size_t ea_buf_len = 0; char timeBuf[128]; unsigned int size; unsigned int large_inodes; @@ -2703,7 +2846,7 @@ ext2fs_istat(TSK_FS_INFO * fs, TSK_FS_ISTAT_FLAG_ENUM istat_flags, FILE * hFile, return 1; } - if (ext2fs_dinode_load(ext2fs, inum, dino_buf)) { + if (ext2fs_dinode_load(ext2fs, inum, dino_buf, &ea_buf, &ea_buf_len)) { free(dino_buf); return 1; } @@ -2810,6 +2953,21 @@ ext2fs_istat(TSK_FS_INFO * fs, TSK_FS_ISTAT_FLAG_ENUM istat_flags, FILE * hFile, if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_EOFBLOCKS) tsk_fprintf(hFile, "Blocks Allocated Beyond EOF, "); + if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_SNAPFILE) + tsk_fprintf(hFile, "Snapshot, "); + + if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_SNAPFILE_DELETED) + tsk_fprintf(hFile, "Deleted Snapshot, "); + + if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_SNAPFILE_SHRUNK) + tsk_fprintf(hFile, "Shrunk Snapshot, "); + + if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_INLINE_DATA) + tsk_fprintf(hFile, "Inline Data, "); + + if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_PROJINHERIT) + tsk_fprintf(hFile, "Inherited project ID, "); + tsk_fprintf(hFile, "\n"); } @@ -3135,87 +3293,89 @@ ext2fs_istat(TSK_FS_INFO * fs, TSK_FS_ISTAT_FLAG_ENUM istat_flags, FILE * hFile, if (numblock > 0) fs_meta->size = numblock * fs->block_size; - tsk_fprintf(hFile, "\nDirect Blocks:\n"); - - if (istat_flags & TSK_FS_ISTAT_RUNLIST) { - const TSK_FS_ATTR *fs_attr_default = - tsk_fs_file_attr_get_type(fs_file, - TSK_FS_ATTR_TYPE_DEFAULT, 0, 0); - if (fs_attr_default && (fs_attr_default->flags & TSK_FS_ATTR_NONRES)) { - if (tsk_fs_attr_print(fs_attr_default, hFile)) { - tsk_fprintf(hFile, "\nError creating run lists\n"); - tsk_error_print(hFile); - tsk_error_reset(); - } - } - } - else { - print.idx = 0; - print.hFile = hFile; - - if (tsk_fs_file_walk(fs_file, TSK_FS_FILE_WALK_FLAG_AONLY, - print_addr_act, (void *)&print)) { - tsk_fprintf(hFile, "\nError reading file: "); - tsk_error_print(hFile); - tsk_error_reset(); - } - else if (print.idx != 0) { - tsk_fprintf(hFile, "\n"); - } - } + if (fs_meta->content_type != TSK_FS_META_CONTENT_TYPE_EXT4_INLINE) { + tsk_fprintf(hFile, "\nDirect Blocks:\n"); - if (fs_meta->content_type == TSK_FS_META_CONTENT_TYPE_EXT4_EXTENTS) { - const TSK_FS_ATTR *fs_attr_extent = - tsk_fs_file_attr_get_type(fs_file, - TSK_FS_ATTR_TYPE_UNIX_EXTENT, 0, 0); - if (fs_attr_extent) { - tsk_fprintf(hFile, "\nExtent Blocks:\n"); - - if (istat_flags & TSK_FS_ISTAT_RUNLIST) { - if (tsk_fs_attr_print(fs_attr_extent, hFile)) { + if (istat_flags & TSK_FS_ISTAT_RUNLIST) { + const TSK_FS_ATTR *fs_attr_default = + tsk_fs_file_attr_get_type(fs_file, + TSK_FS_ATTR_TYPE_DEFAULT, 0, 0); + if (fs_attr_default && (fs_attr_default->flags & TSK_FS_ATTR_NONRES)) { + if (tsk_fs_attr_print(fs_attr_default, hFile)) { tsk_fprintf(hFile, "\nError creating run lists\n"); tsk_error_print(hFile); tsk_error_reset(); } } - else { - print.idx = 0; + } + else { + print.idx = 0; + print.hFile = hFile; - if (tsk_fs_attr_walk(fs_attr_extent, - TSK_FS_FILE_WALK_FLAG_AONLY, print_addr_act, - (void *)&print)) { - tsk_fprintf(hFile, - "\nError reading indirect attribute: "); - tsk_error_print(hFile); - tsk_error_reset(); + if (tsk_fs_file_walk(fs_file, TSK_FS_FILE_WALK_FLAG_AONLY, + print_addr_act, (void *)&print)) { + tsk_fprintf(hFile, "\nError reading file: "); + tsk_error_print(hFile); + tsk_error_reset(); + } + else if (print.idx != 0) { + tsk_fprintf(hFile, "\n"); + } + } + + if (fs_meta->content_type == TSK_FS_META_CONTENT_TYPE_EXT4_EXTENTS) { + const TSK_FS_ATTR *fs_attr_extent = + tsk_fs_file_attr_get_type(fs_file, + TSK_FS_ATTR_TYPE_UNIX_EXTENT, 0, 0); + if (fs_attr_extent) { + tsk_fprintf(hFile, "\nExtent Blocks:\n"); + + if (istat_flags & TSK_FS_ISTAT_RUNLIST) { + if (tsk_fs_attr_print(fs_attr_extent, hFile)) { + tsk_fprintf(hFile, "\nError creating run lists\n"); + tsk_error_print(hFile); + tsk_error_reset(); + } } - else if (print.idx != 0) { - tsk_fprintf(hFile, "\n"); + else { + print.idx = 0; + + if (tsk_fs_attr_walk(fs_attr_extent, + TSK_FS_FILE_WALK_FLAG_AONLY, print_addr_act, + (void *)&print)) { + tsk_fprintf(hFile, + "\nError reading indirect attribute: "); + tsk_error_print(hFile); + tsk_error_reset(); + } + else if (print.idx != 0) { + tsk_fprintf(hFile, "\n"); + } } } } - } - else { - fs_attr_indir = tsk_fs_file_attr_get_type(fs_file, - TSK_FS_ATTR_TYPE_UNIX_INDIR, 0, 0); - if (fs_attr_indir) { - tsk_fprintf(hFile, "\nIndirect Blocks:\n"); - if (istat_flags & TSK_FS_ISTAT_RUNLIST) { - tsk_fs_attr_print(fs_attr_indir, hFile); - } - else { - print.idx = 0; - - if (tsk_fs_attr_walk(fs_attr_indir, - TSK_FS_FILE_WALK_FLAG_AONLY, print_addr_act, - (void *)&print)) { - tsk_fprintf(hFile, - "\nError reading indirect attribute: "); - tsk_error_print(hFile); - tsk_error_reset(); + else { + fs_attr_indir = tsk_fs_file_attr_get_type(fs_file, + TSK_FS_ATTR_TYPE_UNIX_INDIR, 0, 0); + if (fs_attr_indir) { + tsk_fprintf(hFile, "\nIndirect Blocks:\n"); + if (istat_flags & TSK_FS_ISTAT_RUNLIST) { + tsk_fs_attr_print(fs_attr_indir, hFile); } - else if (print.idx != 0) { - tsk_fprintf(hFile, "\n"); + else { + print.idx = 0; + + if (tsk_fs_attr_walk(fs_attr_indir, + TSK_FS_FILE_WALK_FLAG_AONLY, print_addr_act, + (void *)&print)) { + tsk_fprintf(hFile, + "\nError reading indirect attribute: "); + tsk_error_print(hFile); + tsk_error_reset(); + } + else if (print.idx != 0) { + tsk_fprintf(hFile, "\n"); + } } } } diff --git a/tsk/fs/ext2fs_dent.c b/tsk/fs/ext2fs_dent.c index f590bd07bf88997626096fac0631f204bf4830cb..95e8b256961bdd504dcc91b34a2d4abeb2140e30 100644 --- a/tsk/fs/ext2fs_dent.c +++ b/tsk/fs/ext2fs_dent.c @@ -317,7 +317,13 @@ ext2fs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, return TSK_ERR; } - size = roundup(fs_dir->fs_file->meta->size, a_fs->block_size); + if (fs_dir->fs_file->meta->content_type == TSK_FS_META_CONTENT_TYPE_EXT4_INLINE) { + // For inline dirs, don't try to read past the end of the data + size = fs_dir->fs_file->meta->size; + } + else { + size = roundup(fs_dir->fs_file->meta->size, a_fs->block_size); + } TSK_OFF_T offset = 0; while (size > 0) { diff --git a/tsk/fs/tsk_ext2fs.h b/tsk/fs/tsk_ext2fs.h index 382a9a5312f323046ca256c45484e3895e379a73..71640f5fd67ffbb68815568886a7bd8263e17adc 100644 --- a/tsk/fs/tsk_ext2fs.h +++ b/tsk/fs/tsk_ext2fs.h @@ -413,10 +413,16 @@ extern "C" { #define EXT2_IN_EXTENTS 0x00080000 /* Inode uses extents */ #define EXT2_IN_EA_INODE 0x00200000 /* Inode used for large EA */ #define EXT2_IN_EOFBLOCKS 0x00400000 /* Blocks allocated beyond EOF */ +#define EXT2_SNAPFILE 0x01000000 /* Inode is a snapshot */ +#define EXT2_SNAPFILE_DELETED 0x04000000 /* Snapshot is being deleted */ +#define EXT2_SNAPFILE_SHRUNK 0x08000000 /* Snapshot shrink has completed */ +#define EXT2_INLINE_DATA 0x10000000 /* Inode has inline data */ +#define EXT2_PROJINHERIT 0x20000000 /* Create children with the same project ID */ #define EXT2_IN_RESERVED 0x80000000 /* reserved for ext4 lib */ #define EXT2_IN_USER_VISIBLE 0x004BDFFF /* User visible flags */ #define EXT2_IN_USER_MODIFIABLE 0x004B80FF /* User modifiable flags */ +#define EXT2_INLINE_MAX_DATA_LEN 60 /* Max length for inline data in inode (not counting extended attribute) */ /* * directory entries @@ -463,6 +469,7 @@ extern "C" { */ #define EXT2_EA_MAGIC 0xEA020000 +#define EXT2_EA_INODE_OFFSET 160 typedef struct { uint8_t magic[4]; @@ -480,6 +487,8 @@ extern "C" { #define EXT2_EA_IDX_TRUSTED 4 #define EXT2_EA_IDX_LUSTRE 5 #define EXT2_EA_IDX_SECURITY 6 +#define EXT2_EA_IDX_SYSTEM 7 // Possibly only valid for inline data +#define EXT2_EA_IDX_SYSTEM_RICHACL 8 /* Entries follow the header and are aligned to 4-byte boundaries * the value of the attribute is stored at the bottom of the block diff --git a/tsk/fs/tsk_fs.h b/tsk/fs/tsk_fs.h index 2158584563104b5158975835cc73a11ccfdf243b..d0b5d1dc59697222a59fc889a8e3de8248a17e45 100644 --- a/tsk/fs/tsk_fs.h +++ b/tsk/fs/tsk_fs.h @@ -437,7 +437,8 @@ extern "C" { typedef enum TSK_FS_META_CONTENT_TYPE_ENUM { TSK_FS_META_CONTENT_TYPE_DEFAULT = 0x0, - TSK_FS_META_CONTENT_TYPE_EXT4_EXTENTS = 0x1 ///< Ext4 with extents instead of individual pointers + TSK_FS_META_CONTENT_TYPE_EXT4_EXTENTS = 0x1, ///< Ext4 with extents instead of individual pointers + TSK_FS_META_CONTENT_TYPE_EXT4_INLINE = 0x02 ///< Ext4 with inline data } TSK_FS_META_CONTENT_TYPE_ENUM;