From d3276f49bab70327e16c57c2870af3de540231e2 Mon Sep 17 00:00:00 2001
From: Brian Carrier <carrier@sleuthkit.org>
Date: Fri, 25 Oct 2013 21:56:41 -0400
Subject: [PATCH] fixed line endings

---
 tsk/fs/exfatfs.c                    | 2048 +++++++--------
 tsk/fs/exfatfs_dent.c               | 1402 +++++------
 tsk/fs/exfatfs_meta.c               | 3644 +++++++++++++--------------
 tsk/fs/fatfs.c                      | 1634 ++++++------
 tsk/fs/fatfs_meta.c                 | 3050 +++++++++++-----------
 tsk/fs/fatfs_utils.c                |  656 ++---
 tsk/fs/fatxxfs.c                    | 1718 ++++++-------
 tsk/fs/fatxxfs_dent.c               |  860 +++----
 tsk/fs/fatxxfs_meta.c               | 1714 ++++++-------
 win32/libtsk/libtsk.vcxproj         |  482 ++--
 win32/libtsk/libtsk.vcxproj.filters |  796 +++---
 11 files changed, 9002 insertions(+), 9002 deletions(-)

diff --git a/tsk/fs/exfatfs.c b/tsk/fs/exfatfs.c
index 308d3ac36..90ec6a208 100755
--- a/tsk/fs/exfatfs.c
+++ b/tsk/fs/exfatfs.c
@@ -1,1025 +1,1025 @@
-/*
-** The Sleuth Kit
-**
-** Copyright (c) 2013 Basis Technology Corp.  All rights reserved
-** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
-**
-** This software is distributed under the Common Public License 1.0
-**
-*/
-
-/*
- * This code makes use of research presented in the following paper:
- * "Reverse Engineering the exFAT File System" by Robert Shullich
- * Retrieved May 2013 from:
- * http://www.sans.org/reading_room/whitepapers/forensics/reverse-engineering-microsoft-exfat-file-system_33274
- *
- * Some additional details concerning TexFAT were obtained in May 2013
- * from:
- * http://msdn.microsoft.com/en-us/library/ee490643(v=winembedded.60).aspx
- */
-
-/**
- * \file exfatfs.c
- * Contains the internal TSK exFAT file system code to "open" an exFAT file
- * system found in a device image and do the equivalent of a UNIX "stat" call 
- * on the file system.
- */
-
-#include "tsk_exfatfs.h"
-#include "tsk_fs_i.h"
-#include "tsk_fatfs.h"
-#include <assert.h>
-
-// RJCTODO: It may be worthwhile to determine and save the bounds of the root directory when opening the file
-// system. If this is done, then the code in exfatfs_get_alloc_bitmap() and exfatfs_find_volume_label_dentry() could
-// restrict searching to the root directory. This would have the benefit of acting as another file system type validation
-// mechnaism.
-
-// RJCTODO: It would be nice to combine the searches in exfatfs_get_alloc_bitmap() and exfatfs_find_volume_label_dentry(),
-// storing the volume label in the exFAT part of the FATFS_INFO struct. This would make fsstat a little more zippy, but the
-// main benefits would be reduced code duplication and another bit of file system type validation.
-
-/**
- * \internal
- * Parses the MBR of an exFAT file system to obtain file system size 
- * information - bytes per sector, sectors per cluster, and sectors per FAT -
- * to add to a FATFS_INFO object.
- *
- * @param [in, out] a_fatfs Generic FAT file system info structure.
- * @return 0 on success, 1 otherwise, per TSK convention.
- */
-static uint8_t 
-exfatfs_get_fs_size_params(FATFS_INFO *a_fatfs)
-{
-    const char *func_name = "exfatfs_get_fs_size_params";
-	TSK_FS_INFO *fs = &(a_fatfs->fs_info);
-    EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
-
-    assert(a_fatfs != NULL);
-    
-    /* Get bytes per sector.
-     * Bytes per sector is a base 2 logarithm, defining a range of sizes with 
-     * a min of 512 bytes and a max of 4096 bytes. */ 
-    exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer);
-    a_fatfs->ssize_sh = (uint16_t)exfatbs->bytes_per_sector;
-    if ((a_fatfs->ssize_sh < 9) || (a_fatfs->ssize_sh > 12))
-    {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-        tsk_error_set_errstr("Not an FATFS file system (invalid sector size)");
-        if (tsk_verbose) {
-            fprintf(stderr, "%s: Invalid sector size base 2 logarithm (%d), not in range (9 - 12)\n", func_name, a_fatfs->ssize);
-        }
-        return FATFS_FAIL;
-    }
-    a_fatfs->ssize = (1 << a_fatfs->ssize_sh);
-
-    /* Get sectors per cluster. 
-     * Sectors per cluster is a base 2 logarithm. The max cluster size is 
-     * 32 MiB, so the sum of the bytes per sector and sectors per cluster
-     * logs cannot exceed 25. */
-    if ((a_fatfs->ssize_sh + exfatbs->sectors_per_cluster) > 25) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-        tsk_error_set_errstr("Not an exFAT file system (invalid cluster size)");
-        if (tsk_verbose) {
-            fprintf(stderr, "%s: Invalid cluster size (%d)\n", func_name, a_fatfs->csize);
-        }
-        return FATFS_FAIL;
-    }
-    a_fatfs->csize = (1 << exfatbs->sectors_per_cluster);
-
-    /* Get sectors per FAT. 
-     * It will at least be non-zero. */
-    a_fatfs->sectperfat = tsk_getu32(fs->endian, exfatbs->fat_len_in_sectors);
-    if (a_fatfs->sectperfat == 0) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-        tsk_error_set_errstr("Not an exFAT file system (invalid sectors per FAT)");
-        if (tsk_verbose) {
-            fprintf(stderr, "%s: Invalid number of sectors per FAT (%d)\n", func_name, a_fatfs->sectperfat);
-        }
-        return FATFS_FAIL;
-    }
-
-    return FATFS_OK;
-}
-
-/**
- * \internal
- * Parses the MBR of an exFAT file system to obtain file system layout 
- * information to add to a FATFS_INFO object.
- *
- * @param [in, out] a_fatfs Generic FAT file system info structure.
- * @return 0 on success, 1 otherwise, per TSK convention.
- */
-static uint8_t 
-exfatfs_get_fs_layout(FATFS_INFO *a_fatfs)
-{
-    const char *func_name = "exfatfs_get_fs_layout";
-	TSK_FS_INFO *fs = &(a_fatfs->fs_info);
-	EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
-    uint64_t vol_len_in_sectors = 0;
-    uint64_t last_sector_of_cluster_heap = 0;
-
-    assert(a_fatfs != NULL);
-    
-    /* Get the size of the volume. It should be non-zero. */
-	exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer);
-    vol_len_in_sectors = tsk_getu64(fs->endian, exfatbs->vol_len_in_sectors);
-    if (vol_len_in_sectors == 0) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-        tsk_error_set_errstr("Not an exFAT file system (invalid volume length)");
-        if (tsk_verbose) {
-            fprintf(stderr, "%s: Invalid volume length in sectors (%d)\n", func_name, vol_len_in_sectors);
-        }
-        return FATFS_FAIL;
-    }
-
-    /* Get the number of FATs. There will be one FAT for regular exFAT and two 
-     * FATs for TexFAT (transactional exFAT). */
-    a_fatfs->numfat = exfatbs->num_fats;
-    if ((a_fatfs->numfat != 1) && (a_fatfs->numfat != 2)) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-        tsk_error_set_errstr("Not an exFAT file system (number of FATs)");
-        if (tsk_verbose) {
-            fprintf(stderr, "%s: Invalid number of FATs (%d)\n", func_name, a_fatfs->numfat);
-        }
-        return FATFS_FAIL;
-    }
-
-    /* Get the sector address of the first FAT (FAT0). 
-     * It should be non-zero and within the boundaries of the volume.
-     * Note that if the file system is TexFAT, FAT1 will be the working copy
-     * of the FAT and FAT0 will be the stable copy of the last known good FAT. 
-     * Therefore, the Sleuthkit should use FAT0. */
-    a_fatfs->firstfatsect = tsk_getu32(fs->endian, exfatbs->fat_offset);
-    if ((a_fatfs->firstfatsect == 0) || ((uint64_t)a_fatfs->firstfatsect >= vol_len_in_sectors)) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
-        tsk_error_set_errstr("Not an exFAT file system (invalid first FAT sector)");
-        if (tsk_verbose) {
-            fprintf(stderr, "%s: Invalid first FAT sector (%" PRIuDADDR ")\n", func_name, a_fatfs->firstfatsect);
-        }
-        return FATFS_FAIL;
-    }
-
-    /* Get the sector address of the cluster heap (data area). It should be 
-     * after the FATs and within the boundaries of the volume. */
-    a_fatfs->firstdatasect = tsk_getu32(fs->endian, exfatbs->cluster_heap_offset);  
-    if ((a_fatfs->firstdatasect <= (a_fatfs->firstfatsect + (a_fatfs->sectperfat * a_fatfs->numfat) - 1)) ||
-        ((uint64_t)a_fatfs->firstdatasect >= vol_len_in_sectors)) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
-        tsk_error_set_errstr("Not an exFAT file system (invalid first data sector");
-        if (tsk_verbose) {
-            fprintf(stderr, "%s: Invalid first data sector (%" PRIuDADDR ")\n", func_name, a_fatfs->firstdatasect);
-        }
-        return FATFS_FAIL;
-    }
-
-    /* Unlike FAT12 and FAT16, but like FAT32, the sector address of the first
-     * cluster (cluster #2, there is no cluster #0 or cluster #1) is the same
-     * as the sector address of the cluster heap (data area). */
-    a_fatfs->firstclustsect = a_fatfs->firstdatasect;
-
-    /* Get the total number of clusters. It should be non-zero, and should 
-     * define a cluster heap (data area) that is within the boundaries of the
-     * volume. */
-    a_fatfs->clustcnt = tsk_getu32(fs->endian, exfatbs->cluster_cnt);
-    last_sector_of_cluster_heap = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1;
-    if ((a_fatfs->clustcnt == 0) || 
-        (last_sector_of_cluster_heap >= vol_len_in_sectors)) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
-        tsk_error_set_errstr("Not an exFAT file system (invalid cluster count)");
-        if (tsk_verbose) {
-            fprintf(stderr, "%s: Invalid cluster count (%" PRIuDADDR ")\n", func_name, a_fatfs->clustcnt);
-        }
-        return FATFS_FAIL;
-    }
-
-    /* The first cluster is #2, so the final cluster is: */
-     a_fatfs->lastclust = 1 + a_fatfs->clustcnt;
-
-     /* This bit mask is required to make the FATFS_CLUST_2_SECT macro work
-      * for exFAT. It is the same as the FAT32 mask. */
-     a_fatfs->mask = EXFATFS_MASK;
-
-    /* Get the sector address of the root directory. It should be within the
-     * cluster heap (data area). */
-    a_fatfs->rootsect = FATFS_CLUST_2_SECT(a_fatfs, tsk_getu32(fs->endian, exfatbs->root_dir_cluster));
-    if ((a_fatfs->rootsect < a_fatfs->firstdatasect) ||
-        ((uint64_t)a_fatfs->rootsect > last_sector_of_cluster_heap)) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
-        tsk_error_set_errstr("Not an exFAT file system (invalid root directory sector address)");
-        if (tsk_verbose) {
-            fprintf(stderr, "%s: Invalid root directory sector address (%d)\n", func_name, a_fatfs->rootsect);
-        }
-        return FATFS_FAIL;
-    }
-
-    /* The number of directory entries in the root directory is not specified
-     * in the exFAT boot sector. */
-    a_fatfs->numroot = 0;
-
-    return FATFS_OK;
-}
-
-/**
- * \internal
- * Searches the root directory of an exFAT file system for an allocation bitmap
- * directory entry. If the entry is found, data from the entry is saved to a
- * FATFS_INFO object.
- *
- * @param [in, out] a_fatfs Generic FAT file system info structure.
- * @return 0 on success, 1 otherwise, per TSK convention.
- */
-static uint8_t
-exfatfs_get_alloc_bitmap(FATFS_INFO *a_fatfs)
-{
-    const char *func_name = "exfatfs_get_alloc_bitmap";
-	TSK_FS_INFO *fs = &(a_fatfs->fs_info);
-    TSK_DADDR_T current_sector = 0;
-    TSK_DADDR_T last_sector_of_data_area = 0;
-    char *sector_buf = NULL;
-    ssize_t bytes_read = 0;
-    EXFATFS_ALLOC_BITMAP_DIR_ENTRY *dentry = NULL;
-    uint64_t i = 0;
-    uint64_t first_sector_of_alloc_bitmap = 0;
-    uint64_t alloc_bitmap_length_in_bytes = 0;
-    uint64_t last_sector_of_alloc_bitmap = 0;
-
-    assert(a_fatfs != NULL);
-    
-    if ((sector_buf = (char*)tsk_malloc(a_fatfs->ssize)) == NULL) {
-        return FATFS_FAIL;
-    }
-
-    current_sector = a_fatfs->rootsect;
-    last_sector_of_data_area = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1;
-    while (current_sector < last_sector_of_data_area) {
-        /* Read in a sector from the root directory. The allocation bitmap
-         * directory entries will probably be near the beginning of the 
-         * directory, probably in the first sector. */
-        bytes_read = tsk_fs_read_block(fs, current_sector, sector_buf, a_fatfs->ssize);
-        if (bytes_read != a_fatfs->ssize) {
-            if (bytes_read >= 0) {
-                tsk_error_reset();
-                tsk_error_set_errno(TSK_ERR_FS_READ);
-            }
-            tsk_error_set_errstr2("%s: sector: %" PRIuDADDR, func_name, current_sector);
-            free(sector_buf);
-            return FATFS_FAIL;
-        }
-
-        /* Read the directory entries in the sector, looking for allocation
-         * bitmap entries. There will be one entry unless the file system is 
-         * TexFAT (transactional exFAT), in which case there will be two. */
-        for (i = 0; i < a_fatfs->ssize; i += sizeof(FATFS_DENTRY)) {
-            dentry = (EXFATFS_ALLOC_BITMAP_DIR_ENTRY*)&(sector_buf[i]); 
-
-            /* The type of the directory entry is encoded in the first byte 
-             * of the entry. See EXFATFS_DIR_ENTRY_TYPE_ENUM. */ 
-            if (dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP) {
-                /* Do an in-depth test. */
-                if (!exfatfs_is_alloc_bitmap_dentry((FATFS_DENTRY*)dentry, FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN, a_fatfs)) { // RJCTODO: The second argument would change if searching only the root directory. See remarks at beginning of file.
-                    continue;
-                }
-
-                /* The first bit of the flags byte is 0 for the first 
-                 * allocation bitmap directory entry and 1 for the second 
-                 * bitmap directory entry. If TexFAT is in use and there are
-                 * two allocation bitmaps, the first bitmap should be the
-                 * stable copy of the last known good allocation bitmap. 
-                 * Therefore, the SleuthKit will use the first bitmap to 
-                 * determine which clusters are allocated. */
-                if (~(dentry->flags & 0x01)) {
-                    first_sector_of_alloc_bitmap = FATFS_CLUST_2_SECT(a_fatfs, tsk_getu32(fs->endian, dentry->first_cluster_of_bitmap));
-                    alloc_bitmap_length_in_bytes = tsk_getu64(fs->endian, dentry->length_of_alloc_bitmap_in_bytes);
-                    last_sector_of_alloc_bitmap = first_sector_of_alloc_bitmap + (roundup(alloc_bitmap_length_in_bytes, a_fatfs->ssize) / a_fatfs->ssize) - 1;
-
-                    /* The allocation bitmap must lie within the boundaries of the data area. 
-                     * It also must be big enough for the number of clusters reported in the VBR. */
-                    if ((first_sector_of_alloc_bitmap >= a_fatfs->firstdatasect) &&
-                        (last_sector_of_alloc_bitmap <= last_sector_of_data_area) &&
-                        (alloc_bitmap_length_in_bytes >= (a_fatfs->clustcnt + 7) / 8))
-                    {
-                        a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap = first_sector_of_alloc_bitmap; 
-                        a_fatfs->EXFATFS_INFO.length_of_alloc_bitmap_in_bytes = alloc_bitmap_length_in_bytes;
-                        free(sector_buf);
-                        return FATFS_OK;
-                    }
-                }
-            }
-        }
-    }
-    free(sector_buf);
-
-    return FATFS_FAIL;
-}
-
-/**
- * \internal
- * Parses the MBR of an exFAT file system to obtain a volume serial number to
- * add to a FATFS_INFO object.
- *
- * @param [in, out] a_fatfs Generic FAT file system info structure.
- */
-static void 
-exfatfs_get_volume_id(FATFS_INFO *a_fatfs)
-{
-	TSK_FS_INFO *fs = &(a_fatfs->fs_info);
-	EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
-
-    assert(a_fatfs != NULL);
-    
-	exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer);
-    for (fs->fs_id_used = 0; fs->fs_id_used < 4; fs->fs_id_used++) {
-        fs->fs_id[fs->fs_id_used] = exfatbs->vol_serial_no[fs->fs_id_used];
-    }
-}
-
-/**
- * \internal
- * Sets the file system layout members of a FATFS_INFO object for an exFAT file
- * system. Note that there are no "block" or "inode" concepts in exFAT. So, to 
- * conform to the SleuthKit generic file system model, sectors are treated as 
- * blocks, directory entries are treated as inodes, and inode addresses (inode 
- * numbers) are assigned to every directory-entry-sized chunk of the file 
- * system. This is the same mapping previously established for TSK treatment of
- * the other FAT file systems. As with those sister file systems, any given 
- * inode address may or may not point to a conceptual exFAT inode.
- *
- * @param [in, out] a_fatfs Generic FAT file system info structure.
- */
-static void
-exfatfs_setup_fs_layout_model(FATFS_INFO *a_fatfs)
-{
-	TSK_FS_INFO *fs = &(a_fatfs->fs_info);
-	EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
-
-    assert(a_fatfs != NULL);
-
-    fs->duname = "Sector";
-
-    fs->block_size = a_fatfs->ssize;    
-    
-    exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer);
-    fs->block_count = tsk_getu64(fs->endian, exfatbs->vol_len_in_sectors);
-    
-    fs->first_block = 0;
-    fs->last_block = fs->last_block_act = fs->block_count - 1;
-
-    /* Determine the last block actually included in the image, since the 
-     * end of the file system could be "cut off." */
-    if ((TSK_DADDR_T) ((fs->img_info->size - fs->offset) / fs->block_size) <
-        fs->block_count) {
-        fs->last_block_act = (fs->img_info->size - fs->offset) / fs->block_size - 1;
-    }
-
-    /* Calculate the maximum number of directory entries that will fit in a 
-     * sector and a cluster. */
-    a_fatfs->dentry_cnt_se = a_fatfs->ssize / sizeof(FATFS_DENTRY);
-    a_fatfs->dentry_cnt_cl = a_fatfs->dentry_cnt_se * a_fatfs->csize;
-
-    /* The first entry in an exFAT FAT is a media type indicator.
-     * The second entry is simply a meaningless 0xFFFFFFFF. 
-     * The first inode address is therefore 2. */
-    fs->first_inum = FATFS_FIRSTINO;
-
-    fs->root_inum = FATFS_ROOTINO;
-
-    /* Calculate inode addresses for the virtual files (MBR, one or two FATS) 
-     * and the virtual orphan files directory. */
-    fs->last_inum = (FATFS_SECT_2_INODE(a_fatfs, fs->last_block_act + 1) - 1) + FATFS_NUM_VIRT_FILES(a_fatfs);
-    a_fatfs->mbr_virt_inum = fs->last_inum - FATFS_NUM_VIRT_FILES(a_fatfs) + 1;
-    a_fatfs->fat1_virt_inum = a_fatfs->mbr_virt_inum + 1;
-    if (a_fatfs->numfat == 2) {
-        a_fatfs->fat2_virt_inum = a_fatfs->fat1_virt_inum + 1;
-    }
-    else {
-        a_fatfs->fat2_virt_inum = a_fatfs->fat1_virt_inum;
-    }
-    
-    /* Calculate the total number of inodes. */
-    fs->inum_count = fs->last_inum - fs->first_inum + 1;
-}
-
-/**
- * \internal
- * Initializes the data structures used to cache the cluster addresses that 
- * make up FAT chains in an exFAT file system, and the lock used to make the
- * data structures thread-safe. 
- *
- * @param [in, out] a_fatfs Generic FAT file system info structure.
- */
-static void 
-exfatfs_init_fat_cache(FATFS_INFO *a_fatfs)
-{
-    uint32_t i = 0;
-
-    assert(a_fatfs != NULL);
-
-    for (i = 0; i < FATFS_FAT_CACHE_N; i++) {
-        a_fatfs->fatc_addr[i] = 0;
-        a_fatfs->fatc_ttl[i] = 0;
-    }
-
-    tsk_init_lock(&a_fatfs->cache_lock);
-    tsk_init_lock(&a_fatfs->dir_lock);
-    a_fatfs->inum2par = NULL;
-}
-
-/**
- * \internal
- * Initializes the data structure used to map inode addresses to parent inode
- * addresses in an exFAT file system, and the lock used to make the data 
- * structure thread-safe. 
- *
- * @param [in, out] a_fatfs Generic FAT file system info structure.
- */
-static void 
-exfatfs_init_inums_map(FATFS_INFO *a_fatfs)
-{
-    assert(a_fatfs != NULL);
-
-    tsk_init_lock(&a_fatfs->dir_lock);
-    a_fatfs->inum2par = NULL;
-}
-
-/**
- * \internal
- * 
- * Sets the function pointers in a FATFS_INFO object for an exFAT file system
- * to point to either generic FAT file system functions or to exFAT file system
- * functions.
- *
- * @param [in, out] a_fatfs Generic FAT file system info structure.
- */
-static void 
-exfatfs_set_func_ptrs(FATFS_INFO *a_fatfs)
-{
-	TSK_FS_INFO *fs = &(a_fatfs->fs_info);
-
-    assert(a_fatfs != NULL);
-
-    fs->close = fatfs_close;
-
-    /* File system category functions. */
-    fs->fsstat = exfatfs_fsstat;
-    fs->fscheck = fatfs_fscheck;
-
-    /* Content category functions. */
-    fs->block_walk = fatfs_block_walk;
-    fs->block_getflags = fatfs_block_getflags;
-
-    /* Meta data category functions. */
-    fs->inode_walk = fatfs_inode_walk;
-    fs->istat = fatfs_istat;
-    fs->file_add_meta = fatfs_inode_lookup;
-    fs->get_default_attr_type = fatfs_get_default_attr_type;
-    fs->load_attrs = fatfs_make_data_runs;
-
-    /* Name category functions. */
-    fs->dir_open_meta = fatfs_dir_open_meta;
-    fs->name_cmp = fatfs_name_cmp;
-
-    /* NOP journal functions - exFAT has no file system journal. */
-    fs->jblk_walk = fatfs_jblk_walk;
-    fs->jentry_walk = fatfs_jentry_walk;
-    fs->jopen = fatfs_jopen;
-
-    /* Specialization for exFAT functions. */
-    a_fatfs->is_cluster_alloc = exfatfs_is_cluster_alloc;
-    a_fatfs->is_dentry = exfatfs_is_dentry;
-    a_fatfs->dinode_copy =  exfatfs_dinode_copy;
-    a_fatfs->inode_lookup = exfatfs_inode_lookup;
-    a_fatfs->inode_walk_should_skip_dentry = exfatfs_inode_walk_should_skip_dentry;
-    a_fatfs->istat_attr_flags = exfatfs_istat_attr_flags;
-    a_fatfs->dent_parse_buf = exfatfs_dent_parse_buf;
-}
-
-/**
- * Open an exFAT file system in an image file. 
- *
- * @param [in, out] a_fatfs Generic FAT file system info structure.
- * @return 0 on success, 1 otherwise, per TSK convention.
- */
-uint8_t
-exfatfs_open(FATFS_INFO *a_fatfs)
-{
-    const char *func_name = "exfatfs_open";
-    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
-
-    assert(a_fatfs != NULL);    
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) {
-        return FATFS_FAIL; 
-    }
-
-    if (exfatfs_get_fs_size_params(a_fatfs) == FATFS_FAIL ||
-        exfatfs_get_fs_layout(a_fatfs) == FATFS_FAIL) {
-        return FATFS_FAIL; 
-    }
-
-    if (exfatfs_get_fs_layout(a_fatfs) == FATFS_OK) {
-        exfatfs_setup_fs_layout_model(a_fatfs);
-    }
-    else {
-        return FATFS_FAIL;
-    }
-
-    if (exfatfs_get_alloc_bitmap(a_fatfs) == FATFS_FAIL) {
-        return FATFS_FAIL;
-    }
-
-    exfatfs_get_volume_id(a_fatfs);
-    exfatfs_init_inums_map(a_fatfs);
-    exfatfs_init_fat_cache(a_fatfs);
-    exfatfs_set_func_ptrs(a_fatfs);
-
-    fs->ftype = TSK_FS_TYPE_EXFAT;
-
-	return FATFS_OK;
-}
-
-/**
- * \internal
- * Searches an exFAT file system for its volume label directory entry, which 
- * should be in the root directory of the file system. If the entry is found, 
- * its metadata is copied into the TSK_FS_META object of a TSK_FS_FILE object.
- *
- * @param [in] a_fatfs Generic FAT file system info structure.
- * @param [out] a_fatfs Generic file system file structure.
- * @return 0 on success, 1 otherwise, per TSK convention.
- */
-static uint8_t
-exfatfs_find_volume_label_dentry(FATFS_INFO *a_fatfs, TSK_FS_FILE *a_fs_file)
-{
-    const char *func_name = "exfatfs_find_volume_label_dentry";
-    TSK_FS_INFO *fs = (TSK_FS_INFO *)a_fatfs;
-    TSK_DADDR_T current_sector = 0;
-    TSK_DADDR_T last_sector_of_data_area = 0;
-    int8_t sector_is_alloc = 0;
-    char *sector_buf = NULL;
-    ssize_t bytes_read = 0;
-    TSK_INUM_T current_inum = 0;
-    FATFS_DENTRY *dentry = NULL;
-    uint64_t i = 0;
-
-    assert(a_fatfs != NULL);
-    assert(a_fs_file != NULL);
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
-        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name)) {
-        return FATFS_FAIL; 
-    }
-
-    /* Allocate or reset the TSK_FS_META object. */
-    if (a_fs_file->meta == NULL) {
-        if ((a_fs_file->meta =
-                tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) {
-            return FATFS_FAIL;
-        }
-    }
-    else {
-        tsk_fs_meta_reset(a_fs_file->meta);
-    }
-
-    /* Allocate a buffer for reading in sector-size chunks of the image. */
-    if ((sector_buf = (char*)tsk_malloc(a_fatfs->ssize)) == NULL) {
-        return FATFS_FAIL;
-    }
-
-    current_sector = a_fatfs->rootsect;
-    last_sector_of_data_area = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1;
-    while (current_sector < last_sector_of_data_area) {
-        /* Read in a sector from the root directory. The volume label
-         * directory entry will probably be near the beginning of the 
-         * directory, probably in the first sector. */
-        bytes_read = tsk_fs_read_block(fs, current_sector, sector_buf, a_fatfs->ssize);
-        if (bytes_read != a_fatfs->ssize) {
-            if (bytes_read >= 0) {
-                tsk_error_reset();
-                tsk_error_set_errno(TSK_ERR_FS_READ);
-            }
-            tsk_error_set_errstr2("%s: error reading sector: %" PRIuDADDR, func_name, current_sector);
-            free(sector_buf);
-            return FATFS_FAIL;
-        }
-
-        /* Get the allocation status of the sector (yes, it should be allocated). */
-        sector_is_alloc = fatfs_is_sectalloc(a_fatfs, current_sector);
-        if (sector_is_alloc == -1) {
-            return FATFS_FAIL;
-        }
-
-        /* Get the inode address of the first directory entry of the sector. */
-        current_inum = FATFS_SECT_2_INODE(a_fatfs, current_sector);
-
-        /* Loop through the putative directory entries in the sector, 
-         * until the volume label entry is found.  */
-        for (i = 0; i < a_fatfs->ssize; i += sizeof(FATFS_DENTRY)) {
-            dentry = (FATFS_DENTRY*)&(sector_buf[i]); 
-
-            /* The type of the directory entry is encoded in the first byte 
-             * of the entry. See EXFATFS_DIR_ENTRY_TYPE_ENUM. */ 
-            if (dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL ||
-                dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL) {
-                if (!exfatfs_is_vol_label_dentry(dentry, FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN)) { // RJCTODO: The second argument would change if searching only the root directory. See remarks at beginning of file.
-                    continue;
-                }
-
-                /* Found it, save it to the TSK_FS_META object of the 
-                 * TSK_FS_FILE object and exit. */ 
-                if (exfatfs_dinode_copy(a_fatfs, current_inum, dentry, 
-                    sector_is_alloc, a_fs_file) == TSK_OK) {
-                        return FATFS_OK;
-                }
-                else {
-                    return FATFS_FAIL;
-                }
-            }
-        }
-    }
-
-    free(sector_buf);
-    return FATFS_OK;
-}
-
-/**
- * \internal
- * Prints file system category data for an exFAT file system to a file 
- * handle. 
- *
- * @param [in] a_fs Generic file system info structure for the file system.
- * @param [in] a_hFile The file handle.
- * @return 0 on success, 1 otherwise, per TSK convention.
- */
-static uint8_t
-exfatfs_fsstat_fs_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
-{
-    FATFS_INFO *fatfs = NULL;
-    EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
-    TSK_FS_FILE *fs_file = NULL;
-
-    assert(a_fs != NULL);
-    assert(a_hFile != NULL);
-
-    fatfs = (FATFS_INFO*)a_fs;
-    exfatbs = (EXFATFS_MASTER_BOOT_REC*)&(fatfs->boot_sector_buffer);
-
-    if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) {
-        return FATFS_FAIL;
-    }
-
-    if ((fs_file->meta = tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) {
-        return FATFS_FAIL;
-    }
-
-    tsk_fprintf(a_hFile, "FILE SYSTEM INFORMATION\n");
-    tsk_fprintf(a_hFile, "--------------------------------------------\n");
-
-    tsk_fprintf(a_hFile, "File System Type: exFAT\n");
-
-    tsk_fprintf(a_hFile, "\nVolume Serial Number: %x%x-%x%x\n", 
-        exfatbs->vol_serial_no[3], exfatbs->vol_serial_no[2], 
-        exfatbs->vol_serial_no[1], exfatbs->vol_serial_no[0]);
-
-    if (exfatfs_find_volume_label_dentry(fatfs, fs_file) == 0) {
-        tsk_fprintf(a_hFile, "Volume Label (from root directory): %s\n", fs_file->meta->name2->name);
-    }
-    else {
-        tsk_fprintf(a_hFile, "Volume Label:\n");
-    }
-
-    tsk_fprintf(a_hFile, "File System Name (from MBR): %s\n", exfatbs->fs_name);
-
-    tsk_fprintf(a_hFile, "File System Revision: %x.%x\n", 
-        exfatbs->fs_revision[1], exfatbs->fs_revision[0]);
-
-    tsk_fprintf(a_hFile, "Partition Offset: %" PRIuDADDR "\n", 
-        tsk_getu64(a_fs->endian, exfatbs->partition_offset));
-
-    tsk_fprintf(a_hFile, "Number of FATs: %d\n", fatfs->numfat);
-
-    tsk_fs_file_close(fs_file);
-
-    return FATFS_OK;
-}
-
-/**
- * \internal
- * Prints file system layout data for an exFAT file system to a file 
- * handle. 
- *
- * @param [in] a_fs Generic file system info structure for the file system.
- * @param [in] a_hFile The file handle.
- * @return 0 on success, 1 otherwise, per TSK convention.
- */
-static uint8_t
-exfatfs_fsstat_fs_layout_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
-{
-    const char *func_name = "exfatfs_fsstat_fs_layout_info";
-    FATFS_INFO *fatfs = NULL;
-    uint64_t i = 0;
-    TSK_DADDR_T fat_base_sect = 0;
-    TSK_DADDR_T clust_heap_len = 0;
-    TSK_LIST *root_dir_clusters_seen = NULL;
-    TSK_DADDR_T current_cluster;
-    TSK_DADDR_T next_cluster = 0; 
-
-    assert(a_fs != NULL);
-    assert(a_hFile != NULL);
-
-    fatfs = (FATFS_INFO*)a_fs;
-
-    tsk_fprintf(a_hFile, "\nFile System Layout (in sectors):\n");
-
-    tsk_fprintf(a_hFile, "Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
-        a_fs->first_block, a_fs->last_block);
-
-    if (a_fs->last_block != a_fs->last_block_act)
-        tsk_fprintf(a_hFile,
-            "Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n",
-            a_fs->first_block, a_fs->last_block_act);
-
-    tsk_fprintf(a_hFile, "* Reserved: 0 - %" PRIuDADDR "\n",
-        fatfs->firstfatsect - 1);
-
-    tsk_fprintf(a_hFile, "** Volume Boot Record (VBR): 0 - 11\n");
-
-    tsk_fprintf(a_hFile, "*** Boot Sector (MBR): 0\n");
-
-    tsk_fprintf(a_hFile, "** Backup Volume Boot Record (VBR): 12 - 23\n");
-
-    tsk_fprintf(a_hFile, "*** Backup Boot Sector (MBR): 12\n");
-
-    tsk_fprintf(a_hFile, "** FAT alignment space: 24 - %" PRIuDADDR "\n", 
-        fatfs->firstfatsect - 1);
-
-    for (i = 0; i < fatfs->numfat; i++) {
-        fat_base_sect = fatfs->firstfatsect + i * (fatfs->sectperfat);
-        tsk_fprintf(a_hFile, "* FAT %" PRIuDADDR ": %" PRIuDADDR " - %" PRIuDADDR "\n",
-            i + 1, fat_base_sect, (fat_base_sect + fatfs->sectperfat - 1));
-    }
-
-    if (fat_base_sect + fatfs->sectperfat < fatfs->firstdatasect) {
-        tsk_fprintf(a_hFile, "* Data Area alignment space: %" PRIuDADDR " - %" PRIuDADDR "\n", 
-            fat_base_sect + fatfs->sectperfat, fatfs->firstdatasect - 1);
-    }
-
-    tsk_fprintf(a_hFile, "* Data Area: %" PRIuDADDR " - %" PRIuDADDR "\n",
-        fatfs->firstdatasect, a_fs->last_block);
-
-    clust_heap_len = fatfs->csize * (fatfs->lastclust - 1);
-    tsk_fprintf(a_hFile,
-        "** Cluster Heap: %" PRIuDADDR " - %" PRIuDADDR "\n",
-        fatfs->firstclustsect, (fatfs->firstclustsect + clust_heap_len - 1));
-
-    /* Walk the FAT chain for the root directory. */
-    current_cluster = fatfs->rootsect;
-    next_cluster = FATFS_SECT_2_CLUST(fatfs, fatfs->rootsect);
-    while ((next_cluster) && (0 == FATFS_ISEOF(next_cluster, FATFS_32_MASK))) {
-        TSK_DADDR_T nxt;
-        current_cluster = next_cluster;
-
-        /* Make sure we do not get into an infinite loop */
-        if (tsk_list_find(root_dir_clusters_seen, next_cluster)) {
-            if (tsk_verbose) {
-                tsk_fprintf(stderr,
-                    "%s : Loop found while determining root directory size\n",
-                    func_name);
-            }
-            break;
-        }
-
-        if (tsk_list_add(&root_dir_clusters_seen, next_cluster)) {
-            tsk_list_free(root_dir_clusters_seen);
-            root_dir_clusters_seen = NULL;
-            return FATFS_FAIL;
-        }
-
-        if (fatfs_getFAT(fatfs, next_cluster, &nxt)) {
-            break;
-        }
-
-        next_cluster = nxt;
-    }
-    tsk_list_free(root_dir_clusters_seen);
-    root_dir_clusters_seen = NULL;
-
-    tsk_fprintf(a_hFile,
-        "*** Root Directory: %" PRIuDADDR " - %" PRIuDADDR "\n",
-        fatfs->rootsect, (FATFS_CLUST_2_SECT(fatfs, current_cluster + 1) - 1));
-
-    if ((fatfs->firstclustsect + clust_heap_len - 1) != a_fs->last_block) {
-        tsk_fprintf(a_hFile,
-            "** Non-clustered: %" PRIuDADDR " - %" PRIuDADDR "\n",
-            (fatfs->firstclustsect + clust_heap_len), a_fs->last_block);
-    }
-
-    return FATFS_OK;
-}
-
-/**
- * \internal
- * Prints metadata category data for an exFAT file system to a file 
- * handle. 
- *
- * @param [in] a_fs Generic file system info structure for the file system.
- * @param [in] a_hFile The file handle.
- */
-static void
-exfatfs_fsstat_fs_metadata_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
-{
-    assert(a_fs != NULL);
-    assert(a_hFile != NULL);
-
-    tsk_fprintf(a_hFile, "\nMETADATA INFORMATION\n");
-    tsk_fprintf(a_hFile, "--------------------------------------------\n");
-
-    tsk_fprintf(a_hFile, "Metadata Layout (in virtual inodes):\n");
-
-    tsk_fprintf(a_hFile, "Range: %" PRIuINUM " - %" PRIuINUM "\n",
-        a_fs->first_inum, a_fs->last_inum);
-    
-    tsk_fprintf(a_hFile, "* Root Directory: %" PRIuINUM "\n", a_fs->root_inum);
-}
-
-/**
- * \internal
- * Prints metadata category data for an exFAT file system to a file 
- * handle. 
- *
- * @param [in] a_fs Generic file system info structure for the file system.
- * @param [in] a_hFile The file handle.
- */
-static void
-exfatfs_fsstat_fs_content_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
-{
-    FATFS_INFO *fatfs = NULL;
-    uint64_t i = 0;
-    ssize_t bad_sector_cnt = 0;
-
-    assert(a_fs != NULL);
-    assert(a_hFile != NULL);
-
-    fatfs = (FATFS_INFO*)a_fs;
-
-    tsk_fprintf(a_hFile, "\nCONTENT INFORMATION\n");
-    tsk_fprintf(a_hFile, "--------------------------------------------\n");
-    tsk_fprintf(a_hFile, "Sector Size: %" PRIu16 "\n", fatfs->ssize);
-    tsk_fprintf(a_hFile, "Cluster Size: %" PRIu32 "\n",
-        (uint32_t) fatfs->csize << fatfs->ssize_sh);
-
-    tsk_fprintf(a_hFile, "Cluster Range: 2 - %" PRIuDADDR "\n",
-        fatfs->lastclust);
-
-    // RJCTODO: Consider eliminating the code duplication between this function and
-    // and the corresponding FATXX code. 
-    /* Check each cluster of the data area to see if it is marked as bad in the
-     * FAT. If the cluster is bad, list the bad sectors. */
-    bad_sector_cnt = 0;
-    for (i = 2; i <= fatfs->lastclust; ++i) {
-        TSK_DADDR_T entry;
-        TSK_DADDR_T sect;
-
-        /* Get the FAT table entry */
-        if (fatfs_getFAT(fatfs, i, &entry)) {
-            break;
-        }
-
-        if (FATFS_ISBAD(entry, fatfs->mask) == 0) {
-            continue;
-        }
-
-        if (bad_sector_cnt == 0) {
-            tsk_fprintf(a_hFile, "Bad Sectors: ");
-        }
-
-        sect = FATFS_CLUST_2_SECT(fatfs, i);
-        for (i = 0; i < fatfs->csize; ++i) {
-            tsk_fprintf(a_hFile, "%" PRIuDADDR " ", sect + i);
-            if ((++bad_sector_cnt % 8) == 0) {
-                tsk_fprintf(a_hFile, "\n");
-            }
-        }
-    }
-    if ((bad_sector_cnt > 0) && ((bad_sector_cnt % 8) != 0)) {
-        tsk_fprintf(a_hFile, "\n");
-    }
-}
-
-/**
- * \internal
- * Prints FAT chains data for an exFAT file system to a file 
- * handle. 
- *
- * @param [in] a_fs Generic file system info structure for the file system.
- * @param [in] a_hFile The file handle.
- */
-static void
-exfatfs_fsstat_fs_fat_chains_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
-{
-    FATFS_INFO *fatfs = NULL;
-    uint64_t i = 0;
-    TSK_DADDR_T sect_run_start = 0;
-    TSK_DADDR_T sect_run_end = 0;
-    TSK_DADDR_T next_cluster = 0; 
-    TSK_DADDR_T next_sector = 0;
-
-    assert(a_fs != NULL);
-    assert(a_hFile != NULL);
-
-    fatfs = (FATFS_INFO*)a_fs;
-
-    tsk_fprintf(a_hFile, "\nFAT CHAINS (in sectors)\n");
-    tsk_fprintf(a_hFile, "--------------------------------------------\n");
-
-    // RJCTODO: Consider eliminating the code duplication between this function and
-    // and the corresponding FATXX code.
-    /* Check each cluster of the data area to see if it has a FAT chain. 
-     * If so, print out the sectors tha make up the chain. Note that exFAT file 
-     * systems only use FAT chains for the root directory, the allocation
-     * bitmap, the upcase table, and fragmented files. 
-     */
-    sect_run_start = fatfs->firstclustsect;
-    for (i = 2; i <= fatfs->lastclust; i++) {
-        sect_run_end = FATFS_CLUST_2_SECT(fatfs, i + 1) - 1;
-
-        if (fatfs_getFAT(fatfs, i, &next_cluster)) {
-            break;
-        }
-
-        next_sector = FATFS_CLUST_2_SECT(fatfs, next_cluster);
-
-        if ((next_cluster & fatfs->mask) == (i + 1)) {
-            continue;
-        }
-        else if ((next_cluster & fatfs->mask)) {
-            if (FATFS_ISEOF(next_cluster, fatfs->mask)) {
-                tsk_fprintf(a_hFile,
-                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
-                    ") -> EOF\n", sect_run_start, sect_run_end, sect_run_end - sect_run_start + 1);
-            }
-            else if (FATFS_ISBAD(next_cluster, fatfs->mask)) {
-                tsk_fprintf(a_hFile,
-                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
-                    ") -> BAD\n", sect_run_start, sect_run_end, sect_run_end - sect_run_start + 1);
-            }
-            else {
-                tsk_fprintf(a_hFile,
-                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
-                    ") -> %" PRIuDADDR "\n", sect_run_start, sect_run_end,
-                    sect_run_end - sect_run_start + 1, next_sector);
-            }
-        }
-
-        sect_run_start = sect_run_end + 1;
-    }
-}
-
-/**
- * \internal
- * Print details about an exFAT file system to a file handle. 
- *
- * @param [in] a_fs Generic file system info structure for the file system.
- * @param [in] a_hFile The file handle.
- * @return 0 on success, 1 otherwise, per TSK convention.
- */
-uint8_t
-exfatfs_fsstat(TSK_FS_INFO *a_fs, FILE *a_hFile)
-{
-    const char *func_name = "exfatfs_fsstat";
-
-    assert(a_fs != NULL);
-    assert(a_hFile != NULL);
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fs, "a_fs", func_name) ||
-        fatfs_ptr_arg_is_null(a_hFile, "a_hFile", func_name)) {
-        return FATFS_FAIL; 
-    }
-
-    if (exfatfs_fsstat_fs_info(a_fs, a_hFile)) {
-        return FATFS_FAIL;
-    }
-
-    if (exfatfs_fsstat_fs_layout_info(a_fs, a_hFile)) {
-        return FATFS_FAIL;
-    }
-
-    exfatfs_fsstat_fs_metadata_info(a_fs, a_hFile);
-    exfatfs_fsstat_fs_content_info(a_fs, a_hFile);
-    exfatfs_fsstat_fs_fat_chains_info(a_fs, a_hFile);
-
-    return FATFS_OK;
+/*
+** The Sleuth Kit
+**
+** Copyright (c) 2013 Basis Technology Corp.  All rights reserved
+** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
+**
+** This software is distributed under the Common Public License 1.0
+**
+*/
+
+/*
+ * This code makes use of research presented in the following paper:
+ * "Reverse Engineering the exFAT File System" by Robert Shullich
+ * Retrieved May 2013 from:
+ * http://www.sans.org/reading_room/whitepapers/forensics/reverse-engineering-microsoft-exfat-file-system_33274
+ *
+ * Some additional details concerning TexFAT were obtained in May 2013
+ * from:
+ * http://msdn.microsoft.com/en-us/library/ee490643(v=winembedded.60).aspx
+ */
+
+/**
+ * \file exfatfs.c
+ * Contains the internal TSK exFAT file system code to "open" an exFAT file
+ * system found in a device image and do the equivalent of a UNIX "stat" call 
+ * on the file system.
+ */
+
+#include "tsk_exfatfs.h"
+#include "tsk_fs_i.h"
+#include "tsk_fatfs.h"
+#include <assert.h>
+
+// RJCTODO: It may be worthwhile to determine and save the bounds of the root directory when opening the file
+// system. If this is done, then the code in exfatfs_get_alloc_bitmap() and exfatfs_find_volume_label_dentry() could
+// restrict searching to the root directory. This would have the benefit of acting as another file system type validation
+// mechnaism.
+
+// RJCTODO: It would be nice to combine the searches in exfatfs_get_alloc_bitmap() and exfatfs_find_volume_label_dentry(),
+// storing the volume label in the exFAT part of the FATFS_INFO struct. This would make fsstat a little more zippy, but the
+// main benefits would be reduced code duplication and another bit of file system type validation.
+
+/**
+ * \internal
+ * Parses the MBR of an exFAT file system to obtain file system size 
+ * information - bytes per sector, sectors per cluster, and sectors per FAT -
+ * to add to a FATFS_INFO object.
+ *
+ * @param [in, out] a_fatfs Generic FAT file system info structure.
+ * @return 0 on success, 1 otherwise, per TSK convention.
+ */
+static uint8_t 
+exfatfs_get_fs_size_params(FATFS_INFO *a_fatfs)
+{
+    const char *func_name = "exfatfs_get_fs_size_params";
+	TSK_FS_INFO *fs = &(a_fatfs->fs_info);
+    EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
+
+    assert(a_fatfs != NULL);
+    
+    /* Get bytes per sector.
+     * Bytes per sector is a base 2 logarithm, defining a range of sizes with 
+     * a min of 512 bytes and a max of 4096 bytes. */ 
+    exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer);
+    a_fatfs->ssize_sh = (uint16_t)exfatbs->bytes_per_sector;
+    if ((a_fatfs->ssize_sh < 9) || (a_fatfs->ssize_sh > 12))
+    {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr("Not an FATFS file system (invalid sector size)");
+        if (tsk_verbose) {
+            fprintf(stderr, "%s: Invalid sector size base 2 logarithm (%d), not in range (9 - 12)\n", func_name, a_fatfs->ssize);
+        }
+        return FATFS_FAIL;
+    }
+    a_fatfs->ssize = (1 << a_fatfs->ssize_sh);
+
+    /* Get sectors per cluster. 
+     * Sectors per cluster is a base 2 logarithm. The max cluster size is 
+     * 32 MiB, so the sum of the bytes per sector and sectors per cluster
+     * logs cannot exceed 25. */
+    if ((a_fatfs->ssize_sh + exfatbs->sectors_per_cluster) > 25) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr("Not an exFAT file system (invalid cluster size)");
+        if (tsk_verbose) {
+            fprintf(stderr, "%s: Invalid cluster size (%d)\n", func_name, a_fatfs->csize);
+        }
+        return FATFS_FAIL;
+    }
+    a_fatfs->csize = (1 << exfatbs->sectors_per_cluster);
+
+    /* Get sectors per FAT. 
+     * It will at least be non-zero. */
+    a_fatfs->sectperfat = tsk_getu32(fs->endian, exfatbs->fat_len_in_sectors);
+    if (a_fatfs->sectperfat == 0) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr("Not an exFAT file system (invalid sectors per FAT)");
+        if (tsk_verbose) {
+            fprintf(stderr, "%s: Invalid number of sectors per FAT (%d)\n", func_name, a_fatfs->sectperfat);
+        }
+        return FATFS_FAIL;
+    }
+
+    return FATFS_OK;
+}
+
+/**
+ * \internal
+ * Parses the MBR of an exFAT file system to obtain file system layout 
+ * information to add to a FATFS_INFO object.
+ *
+ * @param [in, out] a_fatfs Generic FAT file system info structure.
+ * @return 0 on success, 1 otherwise, per TSK convention.
+ */
+static uint8_t 
+exfatfs_get_fs_layout(FATFS_INFO *a_fatfs)
+{
+    const char *func_name = "exfatfs_get_fs_layout";
+	TSK_FS_INFO *fs = &(a_fatfs->fs_info);
+	EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
+    uint64_t vol_len_in_sectors = 0;
+    uint64_t last_sector_of_cluster_heap = 0;
+
+    assert(a_fatfs != NULL);
+    
+    /* Get the size of the volume. It should be non-zero. */
+	exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer);
+    vol_len_in_sectors = tsk_getu64(fs->endian, exfatbs->vol_len_in_sectors);
+    if (vol_len_in_sectors == 0) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr("Not an exFAT file system (invalid volume length)");
+        if (tsk_verbose) {
+            fprintf(stderr, "%s: Invalid volume length in sectors (%d)\n", func_name, vol_len_in_sectors);
+        }
+        return FATFS_FAIL;
+    }
+
+    /* Get the number of FATs. There will be one FAT for regular exFAT and two 
+     * FATs for TexFAT (transactional exFAT). */
+    a_fatfs->numfat = exfatbs->num_fats;
+    if ((a_fatfs->numfat != 1) && (a_fatfs->numfat != 2)) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr("Not an exFAT file system (number of FATs)");
+        if (tsk_verbose) {
+            fprintf(stderr, "%s: Invalid number of FATs (%d)\n", func_name, a_fatfs->numfat);
+        }
+        return FATFS_FAIL;
+    }
+
+    /* Get the sector address of the first FAT (FAT0). 
+     * It should be non-zero and within the boundaries of the volume.
+     * Note that if the file system is TexFAT, FAT1 will be the working copy
+     * of the FAT and FAT0 will be the stable copy of the last known good FAT. 
+     * Therefore, the Sleuthkit should use FAT0. */
+    a_fatfs->firstfatsect = tsk_getu32(fs->endian, exfatbs->fat_offset);
+    if ((a_fatfs->firstfatsect == 0) || ((uint64_t)a_fatfs->firstfatsect >= vol_len_in_sectors)) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr("Not an exFAT file system (invalid first FAT sector)");
+        if (tsk_verbose) {
+            fprintf(stderr, "%s: Invalid first FAT sector (%" PRIuDADDR ")\n", func_name, a_fatfs->firstfatsect);
+        }
+        return FATFS_FAIL;
+    }
+
+    /* Get the sector address of the cluster heap (data area). It should be 
+     * after the FATs and within the boundaries of the volume. */
+    a_fatfs->firstdatasect = tsk_getu32(fs->endian, exfatbs->cluster_heap_offset);  
+    if ((a_fatfs->firstdatasect <= (a_fatfs->firstfatsect + (a_fatfs->sectperfat * a_fatfs->numfat) - 1)) ||
+        ((uint64_t)a_fatfs->firstdatasect >= vol_len_in_sectors)) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr("Not an exFAT file system (invalid first data sector");
+        if (tsk_verbose) {
+            fprintf(stderr, "%s: Invalid first data sector (%" PRIuDADDR ")\n", func_name, a_fatfs->firstdatasect);
+        }
+        return FATFS_FAIL;
+    }
+
+    /* Unlike FAT12 and FAT16, but like FAT32, the sector address of the first
+     * cluster (cluster #2, there is no cluster #0 or cluster #1) is the same
+     * as the sector address of the cluster heap (data area). */
+    a_fatfs->firstclustsect = a_fatfs->firstdatasect;
+
+    /* Get the total number of clusters. It should be non-zero, and should 
+     * define a cluster heap (data area) that is within the boundaries of the
+     * volume. */
+    a_fatfs->clustcnt = tsk_getu32(fs->endian, exfatbs->cluster_cnt);
+    last_sector_of_cluster_heap = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1;
+    if ((a_fatfs->clustcnt == 0) || 
+        (last_sector_of_cluster_heap >= vol_len_in_sectors)) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr("Not an exFAT file system (invalid cluster count)");
+        if (tsk_verbose) {
+            fprintf(stderr, "%s: Invalid cluster count (%" PRIuDADDR ")\n", func_name, a_fatfs->clustcnt);
+        }
+        return FATFS_FAIL;
+    }
+
+    /* The first cluster is #2, so the final cluster is: */
+     a_fatfs->lastclust = 1 + a_fatfs->clustcnt;
+
+     /* This bit mask is required to make the FATFS_CLUST_2_SECT macro work
+      * for exFAT. It is the same as the FAT32 mask. */
+     a_fatfs->mask = EXFATFS_MASK;
+
+    /* Get the sector address of the root directory. It should be within the
+     * cluster heap (data area). */
+    a_fatfs->rootsect = FATFS_CLUST_2_SECT(a_fatfs, tsk_getu32(fs->endian, exfatbs->root_dir_cluster));
+    if ((a_fatfs->rootsect < a_fatfs->firstdatasect) ||
+        ((uint64_t)a_fatfs->rootsect > last_sector_of_cluster_heap)) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr("Not an exFAT file system (invalid root directory sector address)");
+        if (tsk_verbose) {
+            fprintf(stderr, "%s: Invalid root directory sector address (%d)\n", func_name, a_fatfs->rootsect);
+        }
+        return FATFS_FAIL;
+    }
+
+    /* The number of directory entries in the root directory is not specified
+     * in the exFAT boot sector. */
+    a_fatfs->numroot = 0;
+
+    return FATFS_OK;
+}
+
+/**
+ * \internal
+ * Searches the root directory of an exFAT file system for an allocation bitmap
+ * directory entry. If the entry is found, data from the entry is saved to a
+ * FATFS_INFO object.
+ *
+ * @param [in, out] a_fatfs Generic FAT file system info structure.
+ * @return 0 on success, 1 otherwise, per TSK convention.
+ */
+static uint8_t
+exfatfs_get_alloc_bitmap(FATFS_INFO *a_fatfs)
+{
+    const char *func_name = "exfatfs_get_alloc_bitmap";
+	TSK_FS_INFO *fs = &(a_fatfs->fs_info);
+    TSK_DADDR_T current_sector = 0;
+    TSK_DADDR_T last_sector_of_data_area = 0;
+    char *sector_buf = NULL;
+    ssize_t bytes_read = 0;
+    EXFATFS_ALLOC_BITMAP_DIR_ENTRY *dentry = NULL;
+    uint64_t i = 0;
+    uint64_t first_sector_of_alloc_bitmap = 0;
+    uint64_t alloc_bitmap_length_in_bytes = 0;
+    uint64_t last_sector_of_alloc_bitmap = 0;
+
+    assert(a_fatfs != NULL);
+    
+    if ((sector_buf = (char*)tsk_malloc(a_fatfs->ssize)) == NULL) {
+        return FATFS_FAIL;
+    }
+
+    current_sector = a_fatfs->rootsect;
+    last_sector_of_data_area = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1;
+    while (current_sector < last_sector_of_data_area) {
+        /* Read in a sector from the root directory. The allocation bitmap
+         * directory entries will probably be near the beginning of the 
+         * directory, probably in the first sector. */
+        bytes_read = tsk_fs_read_block(fs, current_sector, sector_buf, a_fatfs->ssize);
+        if (bytes_read != a_fatfs->ssize) {
+            if (bytes_read >= 0) {
+                tsk_error_reset();
+                tsk_error_set_errno(TSK_ERR_FS_READ);
+            }
+            tsk_error_set_errstr2("%s: sector: %" PRIuDADDR, func_name, current_sector);
+            free(sector_buf);
+            return FATFS_FAIL;
+        }
+
+        /* Read the directory entries in the sector, looking for allocation
+         * bitmap entries. There will be one entry unless the file system is 
+         * TexFAT (transactional exFAT), in which case there will be two. */
+        for (i = 0; i < a_fatfs->ssize; i += sizeof(FATFS_DENTRY)) {
+            dentry = (EXFATFS_ALLOC_BITMAP_DIR_ENTRY*)&(sector_buf[i]); 
+
+            /* The type of the directory entry is encoded in the first byte 
+             * of the entry. See EXFATFS_DIR_ENTRY_TYPE_ENUM. */ 
+            if (dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP) {
+                /* Do an in-depth test. */
+                if (!exfatfs_is_alloc_bitmap_dentry((FATFS_DENTRY*)dentry, FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN, a_fatfs)) { // RJCTODO: The second argument would change if searching only the root directory. See remarks at beginning of file.
+                    continue;
+                }
+
+                /* The first bit of the flags byte is 0 for the first 
+                 * allocation bitmap directory entry and 1 for the second 
+                 * bitmap directory entry. If TexFAT is in use and there are
+                 * two allocation bitmaps, the first bitmap should be the
+                 * stable copy of the last known good allocation bitmap. 
+                 * Therefore, the SleuthKit will use the first bitmap to 
+                 * determine which clusters are allocated. */
+                if (~(dentry->flags & 0x01)) {
+                    first_sector_of_alloc_bitmap = FATFS_CLUST_2_SECT(a_fatfs, tsk_getu32(fs->endian, dentry->first_cluster_of_bitmap));
+                    alloc_bitmap_length_in_bytes = tsk_getu64(fs->endian, dentry->length_of_alloc_bitmap_in_bytes);
+                    last_sector_of_alloc_bitmap = first_sector_of_alloc_bitmap + (roundup(alloc_bitmap_length_in_bytes, a_fatfs->ssize) / a_fatfs->ssize) - 1;
+
+                    /* The allocation bitmap must lie within the boundaries of the data area. 
+                     * It also must be big enough for the number of clusters reported in the VBR. */
+                    if ((first_sector_of_alloc_bitmap >= a_fatfs->firstdatasect) &&
+                        (last_sector_of_alloc_bitmap <= last_sector_of_data_area) &&
+                        (alloc_bitmap_length_in_bytes >= (a_fatfs->clustcnt + 7) / 8))
+                    {
+                        a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap = first_sector_of_alloc_bitmap; 
+                        a_fatfs->EXFATFS_INFO.length_of_alloc_bitmap_in_bytes = alloc_bitmap_length_in_bytes;
+                        free(sector_buf);
+                        return FATFS_OK;
+                    }
+                }
+            }
+        }
+    }
+    free(sector_buf);
+
+    return FATFS_FAIL;
+}
+
+/**
+ * \internal
+ * Parses the MBR of an exFAT file system to obtain a volume serial number to
+ * add to a FATFS_INFO object.
+ *
+ * @param [in, out] a_fatfs Generic FAT file system info structure.
+ */
+static void 
+exfatfs_get_volume_id(FATFS_INFO *a_fatfs)
+{
+	TSK_FS_INFO *fs = &(a_fatfs->fs_info);
+	EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
+
+    assert(a_fatfs != NULL);
+    
+	exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer);
+    for (fs->fs_id_used = 0; fs->fs_id_used < 4; fs->fs_id_used++) {
+        fs->fs_id[fs->fs_id_used] = exfatbs->vol_serial_no[fs->fs_id_used];
+    }
+}
+
+/**
+ * \internal
+ * Sets the file system layout members of a FATFS_INFO object for an exFAT file
+ * system. Note that there are no "block" or "inode" concepts in exFAT. So, to 
+ * conform to the SleuthKit generic file system model, sectors are treated as 
+ * blocks, directory entries are treated as inodes, and inode addresses (inode 
+ * numbers) are assigned to every directory-entry-sized chunk of the file 
+ * system. This is the same mapping previously established for TSK treatment of
+ * the other FAT file systems. As with those sister file systems, any given 
+ * inode address may or may not point to a conceptual exFAT inode.
+ *
+ * @param [in, out] a_fatfs Generic FAT file system info structure.
+ */
+static void
+exfatfs_setup_fs_layout_model(FATFS_INFO *a_fatfs)
+{
+	TSK_FS_INFO *fs = &(a_fatfs->fs_info);
+	EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
+
+    assert(a_fatfs != NULL);
+
+    fs->duname = "Sector";
+
+    fs->block_size = a_fatfs->ssize;    
+    
+    exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer);
+    fs->block_count = tsk_getu64(fs->endian, exfatbs->vol_len_in_sectors);
+    
+    fs->first_block = 0;
+    fs->last_block = fs->last_block_act = fs->block_count - 1;
+
+    /* Determine the last block actually included in the image, since the 
+     * end of the file system could be "cut off." */
+    if ((TSK_DADDR_T) ((fs->img_info->size - fs->offset) / fs->block_size) <
+        fs->block_count) {
+        fs->last_block_act = (fs->img_info->size - fs->offset) / fs->block_size - 1;
+    }
+
+    /* Calculate the maximum number of directory entries that will fit in a 
+     * sector and a cluster. */
+    a_fatfs->dentry_cnt_se = a_fatfs->ssize / sizeof(FATFS_DENTRY);
+    a_fatfs->dentry_cnt_cl = a_fatfs->dentry_cnt_se * a_fatfs->csize;
+
+    /* The first entry in an exFAT FAT is a media type indicator.
+     * The second entry is simply a meaningless 0xFFFFFFFF. 
+     * The first inode address is therefore 2. */
+    fs->first_inum = FATFS_FIRSTINO;
+
+    fs->root_inum = FATFS_ROOTINO;
+
+    /* Calculate inode addresses for the virtual files (MBR, one or two FATS) 
+     * and the virtual orphan files directory. */
+    fs->last_inum = (FATFS_SECT_2_INODE(a_fatfs, fs->last_block_act + 1) - 1) + FATFS_NUM_VIRT_FILES(a_fatfs);
+    a_fatfs->mbr_virt_inum = fs->last_inum - FATFS_NUM_VIRT_FILES(a_fatfs) + 1;
+    a_fatfs->fat1_virt_inum = a_fatfs->mbr_virt_inum + 1;
+    if (a_fatfs->numfat == 2) {
+        a_fatfs->fat2_virt_inum = a_fatfs->fat1_virt_inum + 1;
+    }
+    else {
+        a_fatfs->fat2_virt_inum = a_fatfs->fat1_virt_inum;
+    }
+    
+    /* Calculate the total number of inodes. */
+    fs->inum_count = fs->last_inum - fs->first_inum + 1;
+}
+
+/**
+ * \internal
+ * Initializes the data structures used to cache the cluster addresses that 
+ * make up FAT chains in an exFAT file system, and the lock used to make the
+ * data structures thread-safe. 
+ *
+ * @param [in, out] a_fatfs Generic FAT file system info structure.
+ */
+static void 
+exfatfs_init_fat_cache(FATFS_INFO *a_fatfs)
+{
+    uint32_t i = 0;
+
+    assert(a_fatfs != NULL);
+
+    for (i = 0; i < FATFS_FAT_CACHE_N; i++) {
+        a_fatfs->fatc_addr[i] = 0;
+        a_fatfs->fatc_ttl[i] = 0;
+    }
+
+    tsk_init_lock(&a_fatfs->cache_lock);
+    tsk_init_lock(&a_fatfs->dir_lock);
+    a_fatfs->inum2par = NULL;
+}
+
+/**
+ * \internal
+ * Initializes the data structure used to map inode addresses to parent inode
+ * addresses in an exFAT file system, and the lock used to make the data 
+ * structure thread-safe. 
+ *
+ * @param [in, out] a_fatfs Generic FAT file system info structure.
+ */
+static void 
+exfatfs_init_inums_map(FATFS_INFO *a_fatfs)
+{
+    assert(a_fatfs != NULL);
+
+    tsk_init_lock(&a_fatfs->dir_lock);
+    a_fatfs->inum2par = NULL;
+}
+
+/**
+ * \internal
+ * 
+ * Sets the function pointers in a FATFS_INFO object for an exFAT file system
+ * to point to either generic FAT file system functions or to exFAT file system
+ * functions.
+ *
+ * @param [in, out] a_fatfs Generic FAT file system info structure.
+ */
+static void 
+exfatfs_set_func_ptrs(FATFS_INFO *a_fatfs)
+{
+	TSK_FS_INFO *fs = &(a_fatfs->fs_info);
+
+    assert(a_fatfs != NULL);
+
+    fs->close = fatfs_close;
+
+    /* File system category functions. */
+    fs->fsstat = exfatfs_fsstat;
+    fs->fscheck = fatfs_fscheck;
+
+    /* Content category functions. */
+    fs->block_walk = fatfs_block_walk;
+    fs->block_getflags = fatfs_block_getflags;
+
+    /* Meta data category functions. */
+    fs->inode_walk = fatfs_inode_walk;
+    fs->istat = fatfs_istat;
+    fs->file_add_meta = fatfs_inode_lookup;
+    fs->get_default_attr_type = fatfs_get_default_attr_type;
+    fs->load_attrs = fatfs_make_data_runs;
+
+    /* Name category functions. */
+    fs->dir_open_meta = fatfs_dir_open_meta;
+    fs->name_cmp = fatfs_name_cmp;
+
+    /* NOP journal functions - exFAT has no file system journal. */
+    fs->jblk_walk = fatfs_jblk_walk;
+    fs->jentry_walk = fatfs_jentry_walk;
+    fs->jopen = fatfs_jopen;
+
+    /* Specialization for exFAT functions. */
+    a_fatfs->is_cluster_alloc = exfatfs_is_cluster_alloc;
+    a_fatfs->is_dentry = exfatfs_is_dentry;
+    a_fatfs->dinode_copy =  exfatfs_dinode_copy;
+    a_fatfs->inode_lookup = exfatfs_inode_lookup;
+    a_fatfs->inode_walk_should_skip_dentry = exfatfs_inode_walk_should_skip_dentry;
+    a_fatfs->istat_attr_flags = exfatfs_istat_attr_flags;
+    a_fatfs->dent_parse_buf = exfatfs_dent_parse_buf;
+}
+
+/**
+ * Open an exFAT file system in an image file. 
+ *
+ * @param [in, out] a_fatfs Generic FAT file system info structure.
+ * @return 0 on success, 1 otherwise, per TSK convention.
+ */
+uint8_t
+exfatfs_open(FATFS_INFO *a_fatfs)
+{
+    const char *func_name = "exfatfs_open";
+    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
+
+    assert(a_fatfs != NULL);    
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) {
+        return FATFS_FAIL; 
+    }
+
+    if (exfatfs_get_fs_size_params(a_fatfs) == FATFS_FAIL ||
+        exfatfs_get_fs_layout(a_fatfs) == FATFS_FAIL) {
+        return FATFS_FAIL; 
+    }
+
+    if (exfatfs_get_fs_layout(a_fatfs) == FATFS_OK) {
+        exfatfs_setup_fs_layout_model(a_fatfs);
+    }
+    else {
+        return FATFS_FAIL;
+    }
+
+    if (exfatfs_get_alloc_bitmap(a_fatfs) == FATFS_FAIL) {
+        return FATFS_FAIL;
+    }
+
+    exfatfs_get_volume_id(a_fatfs);
+    exfatfs_init_inums_map(a_fatfs);
+    exfatfs_init_fat_cache(a_fatfs);
+    exfatfs_set_func_ptrs(a_fatfs);
+
+    fs->ftype = TSK_FS_TYPE_EXFAT;
+
+	return FATFS_OK;
+}
+
+/**
+ * \internal
+ * Searches an exFAT file system for its volume label directory entry, which 
+ * should be in the root directory of the file system. If the entry is found, 
+ * its metadata is copied into the TSK_FS_META object of a TSK_FS_FILE object.
+ *
+ * @param [in] a_fatfs Generic FAT file system info structure.
+ * @param [out] a_fatfs Generic file system file structure.
+ * @return 0 on success, 1 otherwise, per TSK convention.
+ */
+static uint8_t
+exfatfs_find_volume_label_dentry(FATFS_INFO *a_fatfs, TSK_FS_FILE *a_fs_file)
+{
+    const char *func_name = "exfatfs_find_volume_label_dentry";
+    TSK_FS_INFO *fs = (TSK_FS_INFO *)a_fatfs;
+    TSK_DADDR_T current_sector = 0;
+    TSK_DADDR_T last_sector_of_data_area = 0;
+    int8_t sector_is_alloc = 0;
+    char *sector_buf = NULL;
+    ssize_t bytes_read = 0;
+    TSK_INUM_T current_inum = 0;
+    FATFS_DENTRY *dentry = NULL;
+    uint64_t i = 0;
+
+    assert(a_fatfs != NULL);
+    assert(a_fs_file != NULL);
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
+        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name)) {
+        return FATFS_FAIL; 
+    }
+
+    /* Allocate or reset the TSK_FS_META object. */
+    if (a_fs_file->meta == NULL) {
+        if ((a_fs_file->meta =
+                tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) {
+            return FATFS_FAIL;
+        }
+    }
+    else {
+        tsk_fs_meta_reset(a_fs_file->meta);
+    }
+
+    /* Allocate a buffer for reading in sector-size chunks of the image. */
+    if ((sector_buf = (char*)tsk_malloc(a_fatfs->ssize)) == NULL) {
+        return FATFS_FAIL;
+    }
+
+    current_sector = a_fatfs->rootsect;
+    last_sector_of_data_area = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1;
+    while (current_sector < last_sector_of_data_area) {
+        /* Read in a sector from the root directory. The volume label
+         * directory entry will probably be near the beginning of the 
+         * directory, probably in the first sector. */
+        bytes_read = tsk_fs_read_block(fs, current_sector, sector_buf, a_fatfs->ssize);
+        if (bytes_read != a_fatfs->ssize) {
+            if (bytes_read >= 0) {
+                tsk_error_reset();
+                tsk_error_set_errno(TSK_ERR_FS_READ);
+            }
+            tsk_error_set_errstr2("%s: error reading sector: %" PRIuDADDR, func_name, current_sector);
+            free(sector_buf);
+            return FATFS_FAIL;
+        }
+
+        /* Get the allocation status of the sector (yes, it should be allocated). */
+        sector_is_alloc = fatfs_is_sectalloc(a_fatfs, current_sector);
+        if (sector_is_alloc == -1) {
+            return FATFS_FAIL;
+        }
+
+        /* Get the inode address of the first directory entry of the sector. */
+        current_inum = FATFS_SECT_2_INODE(a_fatfs, current_sector);
+
+        /* Loop through the putative directory entries in the sector, 
+         * until the volume label entry is found.  */
+        for (i = 0; i < a_fatfs->ssize; i += sizeof(FATFS_DENTRY)) {
+            dentry = (FATFS_DENTRY*)&(sector_buf[i]); 
+
+            /* The type of the directory entry is encoded in the first byte 
+             * of the entry. See EXFATFS_DIR_ENTRY_TYPE_ENUM. */ 
+            if (dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL ||
+                dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL) {
+                if (!exfatfs_is_vol_label_dentry(dentry, FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN)) { // RJCTODO: The second argument would change if searching only the root directory. See remarks at beginning of file.
+                    continue;
+                }
+
+                /* Found it, save it to the TSK_FS_META object of the 
+                 * TSK_FS_FILE object and exit. */ 
+                if (exfatfs_dinode_copy(a_fatfs, current_inum, dentry, 
+                    sector_is_alloc, a_fs_file) == TSK_OK) {
+                        return FATFS_OK;
+                }
+                else {
+                    return FATFS_FAIL;
+                }
+            }
+        }
+    }
+
+    free(sector_buf);
+    return FATFS_OK;
+}
+
+/**
+ * \internal
+ * Prints file system category data for an exFAT file system to a file 
+ * handle. 
+ *
+ * @param [in] a_fs Generic file system info structure for the file system.
+ * @param [in] a_hFile The file handle.
+ * @return 0 on success, 1 otherwise, per TSK convention.
+ */
+static uint8_t
+exfatfs_fsstat_fs_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
+{
+    FATFS_INFO *fatfs = NULL;
+    EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
+    TSK_FS_FILE *fs_file = NULL;
+
+    assert(a_fs != NULL);
+    assert(a_hFile != NULL);
+
+    fatfs = (FATFS_INFO*)a_fs;
+    exfatbs = (EXFATFS_MASTER_BOOT_REC*)&(fatfs->boot_sector_buffer);
+
+    if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) {
+        return FATFS_FAIL;
+    }
+
+    if ((fs_file->meta = tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) {
+        return FATFS_FAIL;
+    }
+
+    tsk_fprintf(a_hFile, "FILE SYSTEM INFORMATION\n");
+    tsk_fprintf(a_hFile, "--------------------------------------------\n");
+
+    tsk_fprintf(a_hFile, "File System Type: exFAT\n");
+
+    tsk_fprintf(a_hFile, "\nVolume Serial Number: %x%x-%x%x\n", 
+        exfatbs->vol_serial_no[3], exfatbs->vol_serial_no[2], 
+        exfatbs->vol_serial_no[1], exfatbs->vol_serial_no[0]);
+
+    if (exfatfs_find_volume_label_dentry(fatfs, fs_file) == 0) {
+        tsk_fprintf(a_hFile, "Volume Label (from root directory): %s\n", fs_file->meta->name2->name);
+    }
+    else {
+        tsk_fprintf(a_hFile, "Volume Label:\n");
+    }
+
+    tsk_fprintf(a_hFile, "File System Name (from MBR): %s\n", exfatbs->fs_name);
+
+    tsk_fprintf(a_hFile, "File System Revision: %x.%x\n", 
+        exfatbs->fs_revision[1], exfatbs->fs_revision[0]);
+
+    tsk_fprintf(a_hFile, "Partition Offset: %" PRIuDADDR "\n", 
+        tsk_getu64(a_fs->endian, exfatbs->partition_offset));
+
+    tsk_fprintf(a_hFile, "Number of FATs: %d\n", fatfs->numfat);
+
+    tsk_fs_file_close(fs_file);
+
+    return FATFS_OK;
+}
+
+/**
+ * \internal
+ * Prints file system layout data for an exFAT file system to a file 
+ * handle. 
+ *
+ * @param [in] a_fs Generic file system info structure for the file system.
+ * @param [in] a_hFile The file handle.
+ * @return 0 on success, 1 otherwise, per TSK convention.
+ */
+static uint8_t
+exfatfs_fsstat_fs_layout_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
+{
+    const char *func_name = "exfatfs_fsstat_fs_layout_info";
+    FATFS_INFO *fatfs = NULL;
+    uint64_t i = 0;
+    TSK_DADDR_T fat_base_sect = 0;
+    TSK_DADDR_T clust_heap_len = 0;
+    TSK_LIST *root_dir_clusters_seen = NULL;
+    TSK_DADDR_T current_cluster;
+    TSK_DADDR_T next_cluster = 0; 
+
+    assert(a_fs != NULL);
+    assert(a_hFile != NULL);
+
+    fatfs = (FATFS_INFO*)a_fs;
+
+    tsk_fprintf(a_hFile, "\nFile System Layout (in sectors):\n");
+
+    tsk_fprintf(a_hFile, "Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
+        a_fs->first_block, a_fs->last_block);
+
+    if (a_fs->last_block != a_fs->last_block_act)
+        tsk_fprintf(a_hFile,
+            "Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n",
+            a_fs->first_block, a_fs->last_block_act);
+
+    tsk_fprintf(a_hFile, "* Reserved: 0 - %" PRIuDADDR "\n",
+        fatfs->firstfatsect - 1);
+
+    tsk_fprintf(a_hFile, "** Volume Boot Record (VBR): 0 - 11\n");
+
+    tsk_fprintf(a_hFile, "*** Boot Sector (MBR): 0\n");
+
+    tsk_fprintf(a_hFile, "** Backup Volume Boot Record (VBR): 12 - 23\n");
+
+    tsk_fprintf(a_hFile, "*** Backup Boot Sector (MBR): 12\n");
+
+    tsk_fprintf(a_hFile, "** FAT alignment space: 24 - %" PRIuDADDR "\n", 
+        fatfs->firstfatsect - 1);
+
+    for (i = 0; i < fatfs->numfat; i++) {
+        fat_base_sect = fatfs->firstfatsect + i * (fatfs->sectperfat);
+        tsk_fprintf(a_hFile, "* FAT %" PRIuDADDR ": %" PRIuDADDR " - %" PRIuDADDR "\n",
+            i + 1, fat_base_sect, (fat_base_sect + fatfs->sectperfat - 1));
+    }
+
+    if (fat_base_sect + fatfs->sectperfat < fatfs->firstdatasect) {
+        tsk_fprintf(a_hFile, "* Data Area alignment space: %" PRIuDADDR " - %" PRIuDADDR "\n", 
+            fat_base_sect + fatfs->sectperfat, fatfs->firstdatasect - 1);
+    }
+
+    tsk_fprintf(a_hFile, "* Data Area: %" PRIuDADDR " - %" PRIuDADDR "\n",
+        fatfs->firstdatasect, a_fs->last_block);
+
+    clust_heap_len = fatfs->csize * (fatfs->lastclust - 1);
+    tsk_fprintf(a_hFile,
+        "** Cluster Heap: %" PRIuDADDR " - %" PRIuDADDR "\n",
+        fatfs->firstclustsect, (fatfs->firstclustsect + clust_heap_len - 1));
+
+    /* Walk the FAT chain for the root directory. */
+    current_cluster = fatfs->rootsect;
+    next_cluster = FATFS_SECT_2_CLUST(fatfs, fatfs->rootsect);
+    while ((next_cluster) && (0 == FATFS_ISEOF(next_cluster, FATFS_32_MASK))) {
+        TSK_DADDR_T nxt;
+        current_cluster = next_cluster;
+
+        /* Make sure we do not get into an infinite loop */
+        if (tsk_list_find(root_dir_clusters_seen, next_cluster)) {
+            if (tsk_verbose) {
+                tsk_fprintf(stderr,
+                    "%s : Loop found while determining root directory size\n",
+                    func_name);
+            }
+            break;
+        }
+
+        if (tsk_list_add(&root_dir_clusters_seen, next_cluster)) {
+            tsk_list_free(root_dir_clusters_seen);
+            root_dir_clusters_seen = NULL;
+            return FATFS_FAIL;
+        }
+
+        if (fatfs_getFAT(fatfs, next_cluster, &nxt)) {
+            break;
+        }
+
+        next_cluster = nxt;
+    }
+    tsk_list_free(root_dir_clusters_seen);
+    root_dir_clusters_seen = NULL;
+
+    tsk_fprintf(a_hFile,
+        "*** Root Directory: %" PRIuDADDR " - %" PRIuDADDR "\n",
+        fatfs->rootsect, (FATFS_CLUST_2_SECT(fatfs, current_cluster + 1) - 1));
+
+    if ((fatfs->firstclustsect + clust_heap_len - 1) != a_fs->last_block) {
+        tsk_fprintf(a_hFile,
+            "** Non-clustered: %" PRIuDADDR " - %" PRIuDADDR "\n",
+            (fatfs->firstclustsect + clust_heap_len), a_fs->last_block);
+    }
+
+    return FATFS_OK;
+}
+
+/**
+ * \internal
+ * Prints metadata category data for an exFAT file system to a file 
+ * handle. 
+ *
+ * @param [in] a_fs Generic file system info structure for the file system.
+ * @param [in] a_hFile The file handle.
+ */
+static void
+exfatfs_fsstat_fs_metadata_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
+{
+    assert(a_fs != NULL);
+    assert(a_hFile != NULL);
+
+    tsk_fprintf(a_hFile, "\nMETADATA INFORMATION\n");
+    tsk_fprintf(a_hFile, "--------------------------------------------\n");
+
+    tsk_fprintf(a_hFile, "Metadata Layout (in virtual inodes):\n");
+
+    tsk_fprintf(a_hFile, "Range: %" PRIuINUM " - %" PRIuINUM "\n",
+        a_fs->first_inum, a_fs->last_inum);
+    
+    tsk_fprintf(a_hFile, "* Root Directory: %" PRIuINUM "\n", a_fs->root_inum);
+}
+
+/**
+ * \internal
+ * Prints metadata category data for an exFAT file system to a file 
+ * handle. 
+ *
+ * @param [in] a_fs Generic file system info structure for the file system.
+ * @param [in] a_hFile The file handle.
+ */
+static void
+exfatfs_fsstat_fs_content_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
+{
+    FATFS_INFO *fatfs = NULL;
+    uint64_t i = 0;
+    ssize_t bad_sector_cnt = 0;
+
+    assert(a_fs != NULL);
+    assert(a_hFile != NULL);
+
+    fatfs = (FATFS_INFO*)a_fs;
+
+    tsk_fprintf(a_hFile, "\nCONTENT INFORMATION\n");
+    tsk_fprintf(a_hFile, "--------------------------------------------\n");
+    tsk_fprintf(a_hFile, "Sector Size: %" PRIu16 "\n", fatfs->ssize);
+    tsk_fprintf(a_hFile, "Cluster Size: %" PRIu32 "\n",
+        (uint32_t) fatfs->csize << fatfs->ssize_sh);
+
+    tsk_fprintf(a_hFile, "Cluster Range: 2 - %" PRIuDADDR "\n",
+        fatfs->lastclust);
+
+    // RJCTODO: Consider eliminating the code duplication between this function and
+    // and the corresponding FATXX code. 
+    /* Check each cluster of the data area to see if it is marked as bad in the
+     * FAT. If the cluster is bad, list the bad sectors. */
+    bad_sector_cnt = 0;
+    for (i = 2; i <= fatfs->lastclust; ++i) {
+        TSK_DADDR_T entry;
+        TSK_DADDR_T sect;
+
+        /* Get the FAT table entry */
+        if (fatfs_getFAT(fatfs, i, &entry)) {
+            break;
+        }
+
+        if (FATFS_ISBAD(entry, fatfs->mask) == 0) {
+            continue;
+        }
+
+        if (bad_sector_cnt == 0) {
+            tsk_fprintf(a_hFile, "Bad Sectors: ");
+        }
+
+        sect = FATFS_CLUST_2_SECT(fatfs, i);
+        for (i = 0; i < fatfs->csize; ++i) {
+            tsk_fprintf(a_hFile, "%" PRIuDADDR " ", sect + i);
+            if ((++bad_sector_cnt % 8) == 0) {
+                tsk_fprintf(a_hFile, "\n");
+            }
+        }
+    }
+    if ((bad_sector_cnt > 0) && ((bad_sector_cnt % 8) != 0)) {
+        tsk_fprintf(a_hFile, "\n");
+    }
+}
+
+/**
+ * \internal
+ * Prints FAT chains data for an exFAT file system to a file 
+ * handle. 
+ *
+ * @param [in] a_fs Generic file system info structure for the file system.
+ * @param [in] a_hFile The file handle.
+ */
+static void
+exfatfs_fsstat_fs_fat_chains_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
+{
+    FATFS_INFO *fatfs = NULL;
+    uint64_t i = 0;
+    TSK_DADDR_T sect_run_start = 0;
+    TSK_DADDR_T sect_run_end = 0;
+    TSK_DADDR_T next_cluster = 0; 
+    TSK_DADDR_T next_sector = 0;
+
+    assert(a_fs != NULL);
+    assert(a_hFile != NULL);
+
+    fatfs = (FATFS_INFO*)a_fs;
+
+    tsk_fprintf(a_hFile, "\nFAT CHAINS (in sectors)\n");
+    tsk_fprintf(a_hFile, "--------------------------------------------\n");
+
+    // RJCTODO: Consider eliminating the code duplication between this function and
+    // and the corresponding FATXX code.
+    /* Check each cluster of the data area to see if it has a FAT chain. 
+     * If so, print out the sectors tha make up the chain. Note that exFAT file 
+     * systems only use FAT chains for the root directory, the allocation
+     * bitmap, the upcase table, and fragmented files. 
+     */
+    sect_run_start = fatfs->firstclustsect;
+    for (i = 2; i <= fatfs->lastclust; i++) {
+        sect_run_end = FATFS_CLUST_2_SECT(fatfs, i + 1) - 1;
+
+        if (fatfs_getFAT(fatfs, i, &next_cluster)) {
+            break;
+        }
+
+        next_sector = FATFS_CLUST_2_SECT(fatfs, next_cluster);
+
+        if ((next_cluster & fatfs->mask) == (i + 1)) {
+            continue;
+        }
+        else if ((next_cluster & fatfs->mask)) {
+            if (FATFS_ISEOF(next_cluster, fatfs->mask)) {
+                tsk_fprintf(a_hFile,
+                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
+                    ") -> EOF\n", sect_run_start, sect_run_end, sect_run_end - sect_run_start + 1);
+            }
+            else if (FATFS_ISBAD(next_cluster, fatfs->mask)) {
+                tsk_fprintf(a_hFile,
+                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
+                    ") -> BAD\n", sect_run_start, sect_run_end, sect_run_end - sect_run_start + 1);
+            }
+            else {
+                tsk_fprintf(a_hFile,
+                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
+                    ") -> %" PRIuDADDR "\n", sect_run_start, sect_run_end,
+                    sect_run_end - sect_run_start + 1, next_sector);
+            }
+        }
+
+        sect_run_start = sect_run_end + 1;
+    }
+}
+
+/**
+ * \internal
+ * Print details about an exFAT file system to a file handle. 
+ *
+ * @param [in] a_fs Generic file system info structure for the file system.
+ * @param [in] a_hFile The file handle.
+ * @return 0 on success, 1 otherwise, per TSK convention.
+ */
+uint8_t
+exfatfs_fsstat(TSK_FS_INFO *a_fs, FILE *a_hFile)
+{
+    const char *func_name = "exfatfs_fsstat";
+
+    assert(a_fs != NULL);
+    assert(a_hFile != NULL);
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fs, "a_fs", func_name) ||
+        fatfs_ptr_arg_is_null(a_hFile, "a_hFile", func_name)) {
+        return FATFS_FAIL; 
+    }
+
+    if (exfatfs_fsstat_fs_info(a_fs, a_hFile)) {
+        return FATFS_FAIL;
+    }
+
+    if (exfatfs_fsstat_fs_layout_info(a_fs, a_hFile)) {
+        return FATFS_FAIL;
+    }
+
+    exfatfs_fsstat_fs_metadata_info(a_fs, a_hFile);
+    exfatfs_fsstat_fs_content_info(a_fs, a_hFile);
+    exfatfs_fsstat_fs_fat_chains_info(a_fs, a_hFile);
+
+    return FATFS_OK;
 }
\ No newline at end of file
diff --git a/tsk/fs/exfatfs_dent.c b/tsk/fs/exfatfs_dent.c
index e66fafa2c..f222e37b6 100755
--- a/tsk/fs/exfatfs_dent.c
+++ b/tsk/fs/exfatfs_dent.c
@@ -1,702 +1,702 @@
-/*
-** The Sleuth Kit
-**
-** Copyright (c) 2013 Basis Technology Corp.  All rights reserved
-** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
-**
-** This software is distributed under the Common Public License 1.0
-**
-*/
-
-/*
- * This code makes use of research presented in the following paper:
- * "Reverse Engineering the exFAT File System" by Robert Shullich
- * Retrieved May 2013 from: 
- * http://www.sans.org/reading_room/whitepapers/forensics/reverse-engineering-microsoft-exfat-file-system_33274
- *
- * Some additional details concerning TexFAT were obtained in May 2013
- * from:
- * http://msdn.microsoft.com/en-us/library/ee490643(v=winembedded.60).aspx
-*/
-
-/**
- * \file exfatfs_dent.c
- * Contains the internal TSK exFAT file system code to handle name category 
- * processing. 
- */
-
-#include "tsk_exfatfs.h" /* Include first to make sure it stands alone. */
-#include "tsk_fs_i.h"
-#include "tsk_fatfs.h"
-#include <assert.h>
-
-/**
- * \internal
- * \struct
- * Bundles a TSK_FS_NAME object and a TSK_FS_DIR object with additional data 
- * required when assembling a name from file directory entry set. If the
- * TSK_FS_NAME is successfully populated, it is added to the TSK_FS_DIR.
- */
-typedef struct {
-    FATFS_INFO *fatfs;
-    int8_t sector_is_allocated;
-    EXFATFS_DIR_ENTRY_TYPE_ENUM last_dentry_type;
-    uint8_t expected_secondary_entry_count;
-    uint8_t actual_secondary_entry_count;
-    uint16_t expected_check_sum;
-    uint16_t actual_check_sum;
-    uint8_t expected_name_length;
-    uint8_t actual_name_length;
-    TSK_FS_NAME *fs_name;
-    TSK_FS_DIR *fs_dir;
-} EXFATFS_FS_NAME_INFO;
-
-/**
- * \internal
- * Adds the bytes of a directory entry from a file directory entry set to the 
- * the entry set check sum stored in a EXFATFS_FS_NAME_INFO object.
- *
- * @param a_name_info The name info object.
- * @param a_dentry A buffer containing a file directory entry.
- */
-static void
-exfatfs_update_file_entry_set_checksum(EXFATFS_FS_NAME_INFO *a_name_info, 
-    FATFS_DENTRY *a_dentry)
-{
-    EXFATFS_DIR_ENTRY_TYPE_ENUM dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
-    uint8_t index = 0;
-    uint16_t byte_to_add = 0; /* uint16_t is data type of check sum. */
-
-    assert(a_name_info != NULL);
-    assert(a_dentry != NULL);
-    
-    dentry_type = (EXFATFS_DIR_ENTRY_TYPE_ENUM)a_dentry->data[0];
-    assert(dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE ||
-           dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE ||
-           dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM ||
-           dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM ||
-           dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME ||
-           dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME);
-
-    for (index = 0; index < sizeof(a_dentry->data); ++index) {
-        /* Skip the expected check sum, found in the file entry. */
-        if ((dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE ||
-             dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE) &&
-            (index == 2 || index == 3)) {
-            continue;
-        }
-
-        // RJCTODO: Confirm this. Or discard the check sum calculation code.
-        /* The file system does not update the check sum when an entry set is 
-         * marked as no longer in use. Compensate for this. */
-        if (index == 0) {
-            switch (dentry_type) {
-            case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE:
-                byte_to_add = (uint16_t)EXFATFS_DIR_ENTRY_TYPE_FILE;
-                break;
-            case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM:
-                byte_to_add = (uint16_t)EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM;
-                break;
-            case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME:
-                byte_to_add = (uint16_t)EXFATFS_DIR_ENTRY_TYPE_FILE_NAME;
-                break;
-            default:
-                byte_to_add = (uint16_t)dentry_type;
-                break;
-            }
-        }
-        else {
-            byte_to_add = (uint16_t)a_dentry->data[index];
-        }
-        
-        a_name_info->actual_check_sum = 
-            ((a_name_info->actual_check_sum << 15) | 
-             (a_name_info->actual_check_sum >> 1)) + 
-             byte_to_add;
-    }
-}
-
-/**
- * \internal
- * Reset the fields of a EXFATFS_FS_NAME_INFO to their initialized state. This
- * allows for reuse of the object.
- *
- * @param a_name_info The name info object.
- */
-static void
-exfatfs_reset_name_info(EXFATFS_FS_NAME_INFO *a_name_info)
-{
-    assert(a_name_info != NULL);
-    assert(a_name_info->fs_name != NULL);
-    assert(a_name_info->fs_name->name != NULL);
-    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
-
-    a_name_info->last_dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
-    a_name_info->expected_secondary_entry_count = 0;
-    a_name_info->actual_secondary_entry_count = 0;
-    a_name_info->expected_check_sum = 0;
-    a_name_info->actual_check_sum = 0;
-    a_name_info->expected_name_length = 0;
-    a_name_info->actual_name_length = 0;
-    a_name_info->fs_name->name[0] = '\0';
-    a_name_info->fs_name->meta_addr = 0;
-    a_name_info->fs_name->type = TSK_FS_NAME_TYPE_UNDEF;
-    a_name_info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
-}
-
-/**
- * \internal
- * Add the TSK_FS_NAME object of an EXFATFS_FS_NAME_INFO object to its
- * TSK_FS_DIR object and reset the fields of a EXFATFS_FS_NAME_INFO to their
- * initialized state. This allows for reuse of the object.
- *
- * @param a_name_info The name info object.
- */
-static void
-exfatfs_add_name_to_dir_and_reset_info(EXFATFS_FS_NAME_INFO *a_name_info)
-{
-    assert(a_name_info != NULL);
-    assert(a_name_info->fs_name != NULL);
-    assert(a_name_info->fs_name->name != NULL);
-    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
-    assert(a_name_info->fs_dir != NULL);
-
-    /* If the parsing of the directory entry or directory entry set produced
-     * a name, add the TSK_FS_NAME object to the TSK_FS_DIR object. */
-    if (strlen(a_name_info->fs_name->name) > 0) {
-        tsk_fs_dir_add(a_name_info->fs_dir, a_name_info->fs_name);
-    }
-
-    exfatfs_reset_name_info(a_name_info);
-}
-
-/**
- * \internal
- * Populates an EXFATFS_FS_NAME_INFO object with data parsed from a file
- * directory entry. Since this is the beginning of a new name, the name
- * previously stored on the EXFATFS_FS_NAME_INFO, if any, is saved.
- *
- * @param a_name_info The name info object.
- * @param a_dentry A buffer containing a file directory entry.
- * @param a_inum The inode address associated with the directory entry.
- */
-static void
-exfats_parse_file_dentry(EXFATFS_FS_NAME_INFO *a_name_info, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum)
-{
-    EXFATFS_FILE_DIR_ENTRY *dentry = (EXFATFS_FILE_DIR_ENTRY*)a_dentry;
-
-    assert(a_name_info != NULL);
-    assert(a_name_info->fatfs != NULL);
-    assert(a_name_info->fs_name != NULL);
-    assert(a_name_info->fs_name->name != NULL);
-    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
-    assert(a_name_info->fs_dir != NULL);
-    assert(dentry != NULL);
-    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE ||
-           dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE);
-    assert(fatfs_inum_is_in_range(a_name_info->fatfs, a_inum));
-    
-    /* Starting parse of a new name, so save the current name, if any. */
-    exfatfs_add_name_to_dir_and_reset_info(a_name_info);
-
-    /* Set the current entry type. This is used to check the sequence and 
-     * in-use state of the entries in the set. */
-    a_name_info->last_dentry_type = (EXFATFS_DIR_ENTRY_TYPE_ENUM)dentry->entry_type;
-
-    /* The number of secondary entries and the check sum for the entry set are
-     * stored in the file entry. */
-    a_name_info->expected_secondary_entry_count = 
-        dentry->secondary_entries_count;
-    a_name_info->expected_check_sum = 
-        tsk_getu16(a_name_info->fatfs->fs_info.endian, dentry->check_sum);
-    
-    /* The file type (regular file, directory) is stored in the file entry. */
-    if (dentry->attrs[0] & FATFS_ATTR_DIRECTORY) {
-        a_name_info->fs_name->type = TSK_FS_NAME_TYPE_DIR;
-    }
-    else {
-        a_name_info->fs_name->type = TSK_FS_NAME_TYPE_REG;
-    }
-   
-    /* If the in-use bit of the type byte is not set, the entry set is for a 
-     * deleted or renamed file. However, trust and verify - to be marked as 
-     * allocated, the inode must also be in an allocated sector. */
-    if (a_name_info->sector_is_allocated && dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE) {
-        a_name_info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;    
-    }
-    else {
-        a_name_info->fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;    
-    }
-
-    /* Make the inum of the file entry the inode address for the entry set. */
-    a_name_info->fs_name->meta_addr = a_inum;
-
-    /* Add the file entry bytes to the entry set check sum. */
-    exfatfs_update_file_entry_set_checksum(a_name_info, a_dentry);
-}
-
-/**
- * \internal
- * Populates an EXFATFS_FS_NAME_INFO object with data parsed from a file
- * stream directory entry. 
- *
- * @param a_name_info The name info object.
- * @param a_dentry A buffer containing a file stream directory entry.
- * @param a_inum The inode address associated with the directory entry.
- */
-static void
-exfats_parse_file_stream_dentry(EXFATFS_FS_NAME_INFO *a_name_info, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum)
-{
-    EXFATFS_FILE_STREAM_DIR_ENTRY *dentry = (EXFATFS_FILE_STREAM_DIR_ENTRY*)a_dentry;
-
-    assert(a_name_info != NULL);
-    assert(a_name_info->fatfs != NULL);
-    assert(a_name_info->fs_name != NULL);
-    assert(a_name_info->fs_name->name != NULL);
-    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
-    assert(a_name_info->fs_dir != NULL);
-    assert(dentry != NULL);
-    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM ||
-           dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM);
-    assert(fatfs_inum_is_in_range(a_name_info->fatfs, a_inum));
-
-    if ((a_name_info->last_dentry_type != EXFATFS_DIR_ENTRY_TYPE_FILE) && 
-        (a_name_info->last_dentry_type != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE)) {
-        /* A file stream entry must follow a file entry, so this entry is a
-         * false positive or there is corruption. Save the current name, 
-         * if any, and ignore this buffer. */ 
-        exfatfs_add_name_to_dir_and_reset_info(a_name_info);
-        return;
-    }
-
-    if ((a_name_info->last_dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE &&
-         dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM) || 
-        (a_name_info->last_dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE &&
-         dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM)) {
-        /* The in-use bits of all of the entries in an entry set should be 
-         * same, so this entry is a false positive or there is corruption. 
-         * Save the current name, if any, and ignore this buffer. */ 
-        exfatfs_add_name_to_dir_and_reset_info(a_name_info);
-        return;
-    }
-
-    /* Set the current entry type. This is used to check the sequence and 
-     * in-use state of the entries in the set. */
-    a_name_info->last_dentry_type = 
-        (EXFATFS_DIR_ENTRY_TYPE_ENUM)dentry->entry_type;
-
-    /* The file stream entry contains the length of the file name. */
-    a_name_info->expected_name_length = dentry->file_name_length;
-
-    /* Add the stream entry bytes to the entry set check sum. */
-    exfatfs_update_file_entry_set_checksum(a_name_info, a_dentry);
-
-    /* If all of the secondary entries for the set are present, save the name,
-     * if any. Note that if this condition is satisfied here, the directory is
-     * corrupted or this is a degenerate case - there should be at least one 
-     * file name entry in a directory entry set. */
-    // RJCTODO: Verify the check sum?
-    ++a_name_info->actual_secondary_entry_count;
-    if (a_name_info->actual_secondary_entry_count == 
-        a_name_info->expected_secondary_entry_count) {
-        exfatfs_add_name_to_dir_and_reset_info(a_name_info);
-    }
-}
-
-/**
- * \internal
- * Populates an EXFATFS_FS_NAME_INFO object with data parsed from a file
- * name directory entry. 
- *
- * @param a_name_info The name info object.
- * @param a_dentry A buffer containing a file name directory entry.
- * @param a_inum The inode address associated with the directory entry.
- */
-static void
-exfats_parse_file_name_dentry(EXFATFS_FS_NAME_INFO *a_name_info, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum)
-{
-    EXFATFS_FILE_NAME_DIR_ENTRY *dentry = (EXFATFS_FILE_NAME_DIR_ENTRY*)a_dentry;
-    size_t num_chars_to_copy = 0;
-
-    assert(a_name_info != NULL);
-    assert(a_name_info->fatfs != NULL);
-    assert(a_name_info->fs_name != NULL);
-    assert(a_name_info->fs_name->name != NULL);
-    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
-    assert(a_name_info->fs_dir != NULL);
-    assert(dentry != NULL);
-    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME ||
-           dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME);
-    assert(fatfs_inum_is_in_range(a_name_info->fatfs, a_inum));
-
-    if (a_name_info->last_dentry_type != EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM && 
-        a_name_info->last_dentry_type != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM &&
-        a_name_info->last_dentry_type != EXFATFS_DIR_ENTRY_TYPE_FILE_NAME &&
-        a_name_info->last_dentry_type != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME) {
-        /* A file name entry must follow a stream or name entry, so this entry is
-         * is a false positive or there is corruption. Save the current name, 
-         * if any, and ignore this buffer. */ 
-        exfatfs_add_name_to_dir_and_reset_info(a_name_info);
-        return;
-    }
-
-    if (((a_name_info->last_dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM || 
-          a_name_info->last_dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME) && 
-         dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME) ||
-        ((a_name_info->last_dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM || 
-         a_name_info->last_dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME) &&
-         dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME)) {
-        /* The in-use bits of all of the entries in an entry set should be 
-         * same, so this entry is a false positive or there is corruption. 
-         * Save the current name, if any, and ignore this buffer. */ 
-        exfatfs_add_name_to_dir_and_reset_info(a_name_info);
-        return;
-    }
-
-    /* Set the current entry type. This is used to check the sequence and 
-     * in-use state of the entries in the set. */
-    a_name_info->last_dentry_type = 
-        (EXFATFS_DIR_ENTRY_TYPE_ENUM)dentry->entry_type;
-
-    /* Determine how many name chars remain according to the name length from
-     * the file stream entry and how many chars can be obtained from this
-     * name entry. */
-    num_chars_to_copy = a_name_info->expected_name_length - a_name_info->actual_name_length;
-    if (num_chars_to_copy > EXFATFS_MAX_FILE_NAME_SEGMENT_LENGTH) {
-        num_chars_to_copy = EXFATFS_MAX_FILE_NAME_SEGMENT_LENGTH;
-    }
-
-    /* If there is enough space remaining in the name object, convert the
-     * name chars to UTF-8 and save them. */
-    if ((size_t)(a_name_info->actual_name_length + num_chars_to_copy) < 
-        a_name_info->fs_name->name_size - 1) {
-        if (fatfs_utf16_inode_str_2_utf8(a_name_info->fatfs, 
-            (UTF16*)dentry->utf16_name_chars, num_chars_to_copy,
-            (UTF8*)&(a_name_info->fs_name->name[a_name_info->actual_name_length]), 
-            a_name_info->fs_name->name_size,
-            a_inum, "file name segment") != TSKconversionOK) {
-            /* Discard whatever was written by the failed conversion and save
-             * whatever has been found to this point, if anything. */
-            a_name_info->fs_name->name[a_name_info->actual_name_length] = '\0';
-            exfatfs_add_name_to_dir_and_reset_info(a_name_info);
-            return;
-        }
-
-        /* Update the actual name length and null-terminate the name so far. */
-        a_name_info->actual_name_length += num_chars_to_copy;
-        a_name_info->fs_name->name[a_name_info->actual_name_length] = '\0';
-    }
-
-    /* If all of the secondary entries for the set are present, save the name,
-     * if any. */
-    // RJCTODO: Verify the check sum?
-    ++a_name_info->actual_secondary_entry_count;
-    if (a_name_info->actual_secondary_entry_count == 
-        a_name_info->expected_secondary_entry_count) {
-        exfatfs_add_name_to_dir_and_reset_info(a_name_info);
-    }
-}
-
-/**
- * \internal
- * Populates an EXFATFS_FS_NAME_INFO object with data parsed from a volume
- * label directory entry. 
- *
- * @param a_name_info The name info object.
- * @param a_dentry A buffer containing a volume label directory entry.
- * @param a_inum The inode address associated with the directory entry.
- */
-static void
-exfats_parse_vol_label_dentry(EXFATFS_FS_NAME_INFO *a_name_info, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum)
-{
-    EXFATFS_VOL_LABEL_DIR_ENTRY *dentry = (EXFATFS_VOL_LABEL_DIR_ENTRY*)a_dentry;
-    const char *tag = " (Volume Label Entry)";
-    size_t tag_length = 0;
-
-    assert(a_name_info != NULL);
-    assert(a_name_info->fatfs != NULL);
-    assert(a_name_info->fs_name != NULL);
-    assert(a_name_info->fs_name->name != NULL);
-    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
-    assert(a_name_info->fs_dir != NULL);
-    assert(dentry != NULL);
-    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL ||
-           dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL);
-    assert(fatfs_inum_is_in_range(a_name_info->fatfs, a_inum));
-
-    /* Starting parse of a new name, save the previous name, if any. */
-    exfatfs_add_name_to_dir_and_reset_info(a_name_info);
-
-    /* Set the current entry type. This is used to check the sequence and 
-     * in-use state of the entries in the set. */
-    a_name_info->last_dentry_type = 
-        (EXFATFS_DIR_ENTRY_TYPE_ENUM)dentry->entry_type;
-
-    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL) {
-        if (fatfs_utf16_inode_str_2_utf8(a_name_info->fatfs, 
-            (UTF16*)dentry->volume_label, (size_t)dentry->utf16_char_count + 1, 
-            (UTF8*)a_name_info->fs_name->name, a_name_info->fs_name->name_size,
-            a_inum, "volume label") != TSKconversionOK) {
-            /* Discard whatever was written by the failed conversion. */
-            exfatfs_reset_name_info(a_name_info);
-            return;
-        }
-    }
-    else {
-        strcpy(a_name_info->fs_name->name, EXFATFS_EMPTY_VOLUME_LABEL_DENTRY_NAME);
-    }
-
-    a_name_info->actual_name_length += dentry->utf16_char_count;
-    a_name_info->fs_name->name[a_name_info->actual_name_length] = '\0';
-
-    tag_length = strlen(tag);
-    if ((size_t)a_name_info->actual_name_length + tag_length < 
-        FATFS_MAXNAMLEN_UTF8) {
-        strcat(a_name_info->fs_name->name, tag);
-    }
-
-    /* Record the inum associated with this name. */
-    a_name_info->fs_name->meta_addr =  a_inum;
-
-    /* Not a directory. */
-    a_name_info->fs_name->type = TSK_FS_NAME_TYPE_REG;
-
-    if (a_name_info->sector_is_allocated) {
-        a_name_info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;    
-    }
-
-    /* Save the volume label. */
-    exfatfs_add_name_to_dir_and_reset_info(a_name_info);
-}
-
-/**
- * \internal
- * Populates an EXFATFS_FS_NAME_INFO object with data parsed from a 
- * special file directory entry. 
- *
- * @param a_name_info The name info object.
- * @param a_dentry A buffer containing a special file directory entry.
- * @param a_inum The inode address associated with the directory entry.
- */
-static void
-exfats_parse_special_file_dentry(EXFATFS_FS_NAME_INFO *a_name_info, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum)
-{
-    assert(a_name_info != NULL);
-    assert(a_name_info->fatfs != NULL);
-    assert(a_name_info->fs_name != NULL);
-    assert(a_name_info->fs_name->name != NULL);
-    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
-    assert(a_name_info->fs_dir != NULL);
-    assert(a_dentry != NULL);
-    assert(a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID ||
-           a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP ||
-           a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE ||
-           a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_ACT);
-    assert(fatfs_inum_is_in_range(a_name_info->fatfs, a_inum));
-
-    /* Starting parse of a new name, save the previous name, if any. */
-    exfatfs_add_name_to_dir_and_reset_info(a_name_info);
-
-    /* Record the inum associated with this name. */
-    a_name_info->fs_name->meta_addr = a_inum;
-
-    /* Set the current entry type. This is used to check the sequence and 
-     * in-use state of the entries in the set. */
-    a_name_info->last_dentry_type = 
-        (EXFATFS_DIR_ENTRY_TYPE_ENUM)a_dentry->data[0];
-
-    switch (a_dentry->data[0]) {
-        case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
-            strcpy(a_name_info->fs_name->name, EXFATFS_VOLUME_GUID_DENTRY_NAME);
-            break;
-        case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:
-            strcpy(a_name_info->fs_name->name, EXFATFS_ALLOC_BITMAP_DENTRY_NAME);
-            break;
-        case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:
-            strcpy(a_name_info->fs_name->name, EXFATFS_UPCASE_TABLE_DENTRY_NAME);
-            break;
-        case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:
-            strcpy(a_name_info->fs_name->name, EXFATFS_TEX_FAT_DENTRY_NAME);
-            break;
-        case EXFATFS_DIR_ENTRY_TYPE_ACT:
-            strcpy(a_name_info->fs_name->name, EXFATFS_ACT_DENTRY_NAME);
-            break;
-    }
-
-    /* Not a directory. */
-    a_name_info->fs_name->type = TSK_FS_NAME_TYPE_REG;
-
-    if (a_name_info->sector_is_allocated) {
-        a_name_info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;    
-    }
-
-    /* Save the virtual file name. */
-    exfatfs_add_name_to_dir_and_reset_info(a_name_info);
-}
-
-/**
- * /internal
- * Parse a buffer containing the contents of a directory and add TSK_FS_NAME 
- * objects for each named file found to the TSK_FS_DIR representation of the 
- * directory.
- *
- * @param a_fatfs File system information structure for file system that
- * contains the directory.
- * @param a_fs_dir Directory structure into to which parsed file metadata will
- * be added.
- * @param a_buf Buffer that contains the directory contents.
- * @param a_buf_len Length of buffer in bytes (must be a multiple of sector
-*  size).
- * @param a_sector_addrs Array where each element is the original address of
- * the corresponding sector in a_buf (size of array is number of sectors in
- * the directory).
- * @return TSK_RETVAL_ENUM
-*/
-TSK_RETVAL_ENUM
-exfatfs_dent_parse_buf(FATFS_INFO *a_fatfs, TSK_FS_DIR *a_fs_dir, char *a_buf,
-    TSK_OFF_T a_buf_len, TSK_DADDR_T *a_sector_addrs)
-{
-    const char *func_name = "exfatfs_parse_directory_buf";
-    TSK_FS_INFO *fs = NULL;
-    TSK_OFF_T num_sectors = 0;
-    TSK_OFF_T sector_index = 0;
-    TSK_INUM_T base_inum_of_sector = 0;
-    EXFATFS_FS_NAME_INFO name_info;
-    TSK_OFF_T dentry_index = 0;
-    FATFS_DENTRY *dentry = NULL;
-    int entries_count = 0;
-    int invalid_entries_count = 0;
-    uint8_t is_corrupt_dir = 0;
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
-        fatfs_ptr_arg_is_null(a_fs_dir, "a_fs_dir", func_name) ||
-        fatfs_ptr_arg_is_null(a_buf, "a_buf", func_name) ||
-        fatfs_ptr_arg_is_null(a_sector_addrs, "a_sector_addrs", func_name)) {
-        return TSK_ERR; 
-    }
-
-    assert(a_buf_len > 0);
-    if (a_buf_len < 0) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_ARG);
-        tsk_error_set_errstr("%s: invalid buffer length", func_name);
-        return TSK_ERR; 
-    }
-
-    fs = (TSK_FS_INFO*)a_fatfs;
-
-    memset((void*)&name_info, 0, sizeof(EXFATFS_FS_NAME_INFO));
-    name_info.fatfs = a_fatfs;
-    if ((name_info.fs_name = tsk_fs_name_alloc(FATFS_MAXNAMLEN_UTF8, 0)) == NULL) {
-        return TSK_ERR;
-    }
-    name_info.fs_name->name[0] = '\0';
-    name_info.fs_dir = a_fs_dir;
-
-    /* Loop through the sectors in the buffer. */ 
-    dentry = (FATFS_DENTRY*)a_buf;
-    num_sectors = a_buf_len / a_fatfs->ssize;
-    for (sector_index = 0; sector_index < num_sectors; ++sector_index) {
-        /* Convert the address of the current sector into an inode address. */
-        base_inum_of_sector = 
-            FATFS_SECT_2_INODE(a_fatfs, a_sector_addrs[sector_index]);
-        if (base_inum_of_sector > fs->last_inum) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_ARG);
-            tsk_error_set_errstr("%s: inode address for sector address %" 
-                PRIuDADDR " at addresses array index %" PRIuDADDR 
-                " is too large", func_name, base_inum_of_sector, sector_index);
-            tsk_fs_name_free(name_info.fs_name);
-            return TSK_COR;
-        }
-
-        if (tsk_verbose) {
-            tsk_fprintf(stderr,"%s: Parsing sector %" PRIuDADDR " for dir %" 
-                PRIuINUM "\n", func_name, a_sector_addrs[sector_index], a_fs_dir->addr);
-        }
-
-        /* Get the allocation status of the current sector. */
-        if ((name_info.sector_is_allocated = 
-            fatfs_is_sectalloc(a_fatfs, a_sector_addrs[sector_index])) == -1) {
-            if (tsk_verbose) {
-                tsk_fprintf(stderr, 
-                    "%s: Error looking up allocation status of sector : %"
-                    PRIuDADDR "\n", func_name, a_sector_addrs[sector_index]);
-                tsk_error_print(stderr);
-            }
-            tsk_error_reset();
-            continue;
-        }
-
-        /* Loop through the putative directory entries in the current sector. */
-        for (dentry_index = 0; dentry_index < a_fatfs->dentry_cnt_se; ++dentry_index, ++dentry) {
-            FATFS_DENTRY *current_dentry = dentry;
-            TSK_INUM_T current_inum = base_inum_of_sector + dentry_index;
-            EXFATFS_DIR_ENTRY_TYPE_ENUM dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
-
-            ++entries_count; // RJCTODO: Should this be reset for each iteration of this loop?
-
-            if (!fatfs_inum_is_in_range(a_fatfs, current_inum)) {
-                tsk_fs_name_free(name_info.fs_name);
-                return TSK_ERR;
-            }
-
-            if (exfatfs_is_dentry(a_fatfs, current_dentry, 
-                (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)name_info.sector_is_allocated, 
-                (uint8_t)(!is_corrupt_dir && name_info.sector_is_allocated))) {
-                dentry_type = (EXFATFS_DIR_ENTRY_TYPE_ENUM)current_dentry->data[0];
-            }
-            else {
-                dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
-            }
-
-            switch (dentry_type) {
-            case EXFATFS_DIR_ENTRY_TYPE_FILE:
-            case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE:
-                exfats_parse_file_dentry(&name_info, current_dentry, current_inum);                 
-                break;
-            case EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM:
-            case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM:
-                exfats_parse_file_stream_dentry(&name_info, current_dentry, current_inum);                 
-                break;
-            case EXFATFS_DIR_ENTRY_TYPE_FILE_NAME:
-            case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME:
-                exfats_parse_file_name_dentry(&name_info, current_dentry, current_inum);                 
-                break;
-            case EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL:
-            case EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL:
-                exfats_parse_vol_label_dentry(&name_info, current_dentry, current_inum);
-                break;
-            case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
-            case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:
-            case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:
-            case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:
-            case EXFATFS_DIR_ENTRY_TYPE_ACT:
-                exfats_parse_special_file_dentry(&name_info, current_dentry, current_inum);                 
-                break;
-            case EXFATFS_DIR_ENTRY_TYPE_NONE:
-            default:
-                ++invalid_entries_count;
-                if (entries_count == 4 && invalid_entries_count == 4) {
-                    /* If the first four putative entries in the buffer are not
-                     * entries, set the corrupt directory flag to make entry tests
-                     * more in-depth, even for allocated sectors. */
-                    is_corrupt_dir = 1;
-                }
-
-                /* Starting parse of a new name, save the previous name, 
-                 * if any. */
-                exfatfs_add_name_to_dir_and_reset_info(&name_info);
-
-                break;
-            }
-        }
-    }
-
-     /* Save the last parsed name, if any. */
-    exfatfs_add_name_to_dir_and_reset_info(&name_info);
-    tsk_fs_name_free(name_info.fs_name);
-
-    return TSK_OK;
+/*
+** The Sleuth Kit
+**
+** Copyright (c) 2013 Basis Technology Corp.  All rights reserved
+** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
+**
+** This software is distributed under the Common Public License 1.0
+**
+*/
+
+/*
+ * This code makes use of research presented in the following paper:
+ * "Reverse Engineering the exFAT File System" by Robert Shullich
+ * Retrieved May 2013 from: 
+ * http://www.sans.org/reading_room/whitepapers/forensics/reverse-engineering-microsoft-exfat-file-system_33274
+ *
+ * Some additional details concerning TexFAT were obtained in May 2013
+ * from:
+ * http://msdn.microsoft.com/en-us/library/ee490643(v=winembedded.60).aspx
+*/
+
+/**
+ * \file exfatfs_dent.c
+ * Contains the internal TSK exFAT file system code to handle name category 
+ * processing. 
+ */
+
+#include "tsk_exfatfs.h" /* Include first to make sure it stands alone. */
+#include "tsk_fs_i.h"
+#include "tsk_fatfs.h"
+#include <assert.h>
+
+/**
+ * \internal
+ * \struct
+ * Bundles a TSK_FS_NAME object and a TSK_FS_DIR object with additional data 
+ * required when assembling a name from file directory entry set. If the
+ * TSK_FS_NAME is successfully populated, it is added to the TSK_FS_DIR.
+ */
+typedef struct {
+    FATFS_INFO *fatfs;
+    int8_t sector_is_allocated;
+    EXFATFS_DIR_ENTRY_TYPE_ENUM last_dentry_type;
+    uint8_t expected_secondary_entry_count;
+    uint8_t actual_secondary_entry_count;
+    uint16_t expected_check_sum;
+    uint16_t actual_check_sum;
+    uint8_t expected_name_length;
+    uint8_t actual_name_length;
+    TSK_FS_NAME *fs_name;
+    TSK_FS_DIR *fs_dir;
+} EXFATFS_FS_NAME_INFO;
+
+/**
+ * \internal
+ * Adds the bytes of a directory entry from a file directory entry set to the 
+ * the entry set check sum stored in a EXFATFS_FS_NAME_INFO object.
+ *
+ * @param a_name_info The name info object.
+ * @param a_dentry A buffer containing a file directory entry.
+ */
+static void
+exfatfs_update_file_entry_set_checksum(EXFATFS_FS_NAME_INFO *a_name_info, 
+    FATFS_DENTRY *a_dentry)
+{
+    EXFATFS_DIR_ENTRY_TYPE_ENUM dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
+    uint8_t index = 0;
+    uint16_t byte_to_add = 0; /* uint16_t is data type of check sum. */
+
+    assert(a_name_info != NULL);
+    assert(a_dentry != NULL);
+    
+    dentry_type = (EXFATFS_DIR_ENTRY_TYPE_ENUM)a_dentry->data[0];
+    assert(dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE ||
+           dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE ||
+           dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM ||
+           dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM ||
+           dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME ||
+           dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME);
+
+    for (index = 0; index < sizeof(a_dentry->data); ++index) {
+        /* Skip the expected check sum, found in the file entry. */
+        if ((dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE ||
+             dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE) &&
+            (index == 2 || index == 3)) {
+            continue;
+        }
+
+        // RJCTODO: Confirm this. Or discard the check sum calculation code.
+        /* The file system does not update the check sum when an entry set is 
+         * marked as no longer in use. Compensate for this. */
+        if (index == 0) {
+            switch (dentry_type) {
+            case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE:
+                byte_to_add = (uint16_t)EXFATFS_DIR_ENTRY_TYPE_FILE;
+                break;
+            case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM:
+                byte_to_add = (uint16_t)EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM;
+                break;
+            case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME:
+                byte_to_add = (uint16_t)EXFATFS_DIR_ENTRY_TYPE_FILE_NAME;
+                break;
+            default:
+                byte_to_add = (uint16_t)dentry_type;
+                break;
+            }
+        }
+        else {
+            byte_to_add = (uint16_t)a_dentry->data[index];
+        }
+        
+        a_name_info->actual_check_sum = 
+            ((a_name_info->actual_check_sum << 15) | 
+             (a_name_info->actual_check_sum >> 1)) + 
+             byte_to_add;
+    }
+}
+
+/**
+ * \internal
+ * Reset the fields of a EXFATFS_FS_NAME_INFO to their initialized state. This
+ * allows for reuse of the object.
+ *
+ * @param a_name_info The name info object.
+ */
+static void
+exfatfs_reset_name_info(EXFATFS_FS_NAME_INFO *a_name_info)
+{
+    assert(a_name_info != NULL);
+    assert(a_name_info->fs_name != NULL);
+    assert(a_name_info->fs_name->name != NULL);
+    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
+
+    a_name_info->last_dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
+    a_name_info->expected_secondary_entry_count = 0;
+    a_name_info->actual_secondary_entry_count = 0;
+    a_name_info->expected_check_sum = 0;
+    a_name_info->actual_check_sum = 0;
+    a_name_info->expected_name_length = 0;
+    a_name_info->actual_name_length = 0;
+    a_name_info->fs_name->name[0] = '\0';
+    a_name_info->fs_name->meta_addr = 0;
+    a_name_info->fs_name->type = TSK_FS_NAME_TYPE_UNDEF;
+    a_name_info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
+}
+
+/**
+ * \internal
+ * Add the TSK_FS_NAME object of an EXFATFS_FS_NAME_INFO object to its
+ * TSK_FS_DIR object and reset the fields of a EXFATFS_FS_NAME_INFO to their
+ * initialized state. This allows for reuse of the object.
+ *
+ * @param a_name_info The name info object.
+ */
+static void
+exfatfs_add_name_to_dir_and_reset_info(EXFATFS_FS_NAME_INFO *a_name_info)
+{
+    assert(a_name_info != NULL);
+    assert(a_name_info->fs_name != NULL);
+    assert(a_name_info->fs_name->name != NULL);
+    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
+    assert(a_name_info->fs_dir != NULL);
+
+    /* If the parsing of the directory entry or directory entry set produced
+     * a name, add the TSK_FS_NAME object to the TSK_FS_DIR object. */
+    if (strlen(a_name_info->fs_name->name) > 0) {
+        tsk_fs_dir_add(a_name_info->fs_dir, a_name_info->fs_name);
+    }
+
+    exfatfs_reset_name_info(a_name_info);
+}
+
+/**
+ * \internal
+ * Populates an EXFATFS_FS_NAME_INFO object with data parsed from a file
+ * directory entry. Since this is the beginning of a new name, the name
+ * previously stored on the EXFATFS_FS_NAME_INFO, if any, is saved.
+ *
+ * @param a_name_info The name info object.
+ * @param a_dentry A buffer containing a file directory entry.
+ * @param a_inum The inode address associated with the directory entry.
+ */
+static void
+exfats_parse_file_dentry(EXFATFS_FS_NAME_INFO *a_name_info, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum)
+{
+    EXFATFS_FILE_DIR_ENTRY *dentry = (EXFATFS_FILE_DIR_ENTRY*)a_dentry;
+
+    assert(a_name_info != NULL);
+    assert(a_name_info->fatfs != NULL);
+    assert(a_name_info->fs_name != NULL);
+    assert(a_name_info->fs_name->name != NULL);
+    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
+    assert(a_name_info->fs_dir != NULL);
+    assert(dentry != NULL);
+    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE ||
+           dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE);
+    assert(fatfs_inum_is_in_range(a_name_info->fatfs, a_inum));
+    
+    /* Starting parse of a new name, so save the current name, if any. */
+    exfatfs_add_name_to_dir_and_reset_info(a_name_info);
+
+    /* Set the current entry type. This is used to check the sequence and 
+     * in-use state of the entries in the set. */
+    a_name_info->last_dentry_type = (EXFATFS_DIR_ENTRY_TYPE_ENUM)dentry->entry_type;
+
+    /* The number of secondary entries and the check sum for the entry set are
+     * stored in the file entry. */
+    a_name_info->expected_secondary_entry_count = 
+        dentry->secondary_entries_count;
+    a_name_info->expected_check_sum = 
+        tsk_getu16(a_name_info->fatfs->fs_info.endian, dentry->check_sum);
+    
+    /* The file type (regular file, directory) is stored in the file entry. */
+    if (dentry->attrs[0] & FATFS_ATTR_DIRECTORY) {
+        a_name_info->fs_name->type = TSK_FS_NAME_TYPE_DIR;
+    }
+    else {
+        a_name_info->fs_name->type = TSK_FS_NAME_TYPE_REG;
+    }
+   
+    /* If the in-use bit of the type byte is not set, the entry set is for a 
+     * deleted or renamed file. However, trust and verify - to be marked as 
+     * allocated, the inode must also be in an allocated sector. */
+    if (a_name_info->sector_is_allocated && dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE) {
+        a_name_info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;    
+    }
+    else {
+        a_name_info->fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;    
+    }
+
+    /* Make the inum of the file entry the inode address for the entry set. */
+    a_name_info->fs_name->meta_addr = a_inum;
+
+    /* Add the file entry bytes to the entry set check sum. */
+    exfatfs_update_file_entry_set_checksum(a_name_info, a_dentry);
+}
+
+/**
+ * \internal
+ * Populates an EXFATFS_FS_NAME_INFO object with data parsed from a file
+ * stream directory entry. 
+ *
+ * @param a_name_info The name info object.
+ * @param a_dentry A buffer containing a file stream directory entry.
+ * @param a_inum The inode address associated with the directory entry.
+ */
+static void
+exfats_parse_file_stream_dentry(EXFATFS_FS_NAME_INFO *a_name_info, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum)
+{
+    EXFATFS_FILE_STREAM_DIR_ENTRY *dentry = (EXFATFS_FILE_STREAM_DIR_ENTRY*)a_dentry;
+
+    assert(a_name_info != NULL);
+    assert(a_name_info->fatfs != NULL);
+    assert(a_name_info->fs_name != NULL);
+    assert(a_name_info->fs_name->name != NULL);
+    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
+    assert(a_name_info->fs_dir != NULL);
+    assert(dentry != NULL);
+    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM ||
+           dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM);
+    assert(fatfs_inum_is_in_range(a_name_info->fatfs, a_inum));
+
+    if ((a_name_info->last_dentry_type != EXFATFS_DIR_ENTRY_TYPE_FILE) && 
+        (a_name_info->last_dentry_type != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE)) {
+        /* A file stream entry must follow a file entry, so this entry is a
+         * false positive or there is corruption. Save the current name, 
+         * if any, and ignore this buffer. */ 
+        exfatfs_add_name_to_dir_and_reset_info(a_name_info);
+        return;
+    }
+
+    if ((a_name_info->last_dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE &&
+         dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM) || 
+        (a_name_info->last_dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE &&
+         dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM)) {
+        /* The in-use bits of all of the entries in an entry set should be 
+         * same, so this entry is a false positive or there is corruption. 
+         * Save the current name, if any, and ignore this buffer. */ 
+        exfatfs_add_name_to_dir_and_reset_info(a_name_info);
+        return;
+    }
+
+    /* Set the current entry type. This is used to check the sequence and 
+     * in-use state of the entries in the set. */
+    a_name_info->last_dentry_type = 
+        (EXFATFS_DIR_ENTRY_TYPE_ENUM)dentry->entry_type;
+
+    /* The file stream entry contains the length of the file name. */
+    a_name_info->expected_name_length = dentry->file_name_length;
+
+    /* Add the stream entry bytes to the entry set check sum. */
+    exfatfs_update_file_entry_set_checksum(a_name_info, a_dentry);
+
+    /* If all of the secondary entries for the set are present, save the name,
+     * if any. Note that if this condition is satisfied here, the directory is
+     * corrupted or this is a degenerate case - there should be at least one 
+     * file name entry in a directory entry set. */
+    // RJCTODO: Verify the check sum?
+    ++a_name_info->actual_secondary_entry_count;
+    if (a_name_info->actual_secondary_entry_count == 
+        a_name_info->expected_secondary_entry_count) {
+        exfatfs_add_name_to_dir_and_reset_info(a_name_info);
+    }
+}
+
+/**
+ * \internal
+ * Populates an EXFATFS_FS_NAME_INFO object with data parsed from a file
+ * name directory entry. 
+ *
+ * @param a_name_info The name info object.
+ * @param a_dentry A buffer containing a file name directory entry.
+ * @param a_inum The inode address associated with the directory entry.
+ */
+static void
+exfats_parse_file_name_dentry(EXFATFS_FS_NAME_INFO *a_name_info, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum)
+{
+    EXFATFS_FILE_NAME_DIR_ENTRY *dentry = (EXFATFS_FILE_NAME_DIR_ENTRY*)a_dentry;
+    size_t num_chars_to_copy = 0;
+
+    assert(a_name_info != NULL);
+    assert(a_name_info->fatfs != NULL);
+    assert(a_name_info->fs_name != NULL);
+    assert(a_name_info->fs_name->name != NULL);
+    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
+    assert(a_name_info->fs_dir != NULL);
+    assert(dentry != NULL);
+    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME ||
+           dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME);
+    assert(fatfs_inum_is_in_range(a_name_info->fatfs, a_inum));
+
+    if (a_name_info->last_dentry_type != EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM && 
+        a_name_info->last_dentry_type != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM &&
+        a_name_info->last_dentry_type != EXFATFS_DIR_ENTRY_TYPE_FILE_NAME &&
+        a_name_info->last_dentry_type != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME) {
+        /* A file name entry must follow a stream or name entry, so this entry is
+         * is a false positive or there is corruption. Save the current name, 
+         * if any, and ignore this buffer. */ 
+        exfatfs_add_name_to_dir_and_reset_info(a_name_info);
+        return;
+    }
+
+    if (((a_name_info->last_dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM || 
+          a_name_info->last_dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME) && 
+         dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME) ||
+        ((a_name_info->last_dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM || 
+         a_name_info->last_dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME) &&
+         dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME)) {
+        /* The in-use bits of all of the entries in an entry set should be 
+         * same, so this entry is a false positive or there is corruption. 
+         * Save the current name, if any, and ignore this buffer. */ 
+        exfatfs_add_name_to_dir_and_reset_info(a_name_info);
+        return;
+    }
+
+    /* Set the current entry type. This is used to check the sequence and 
+     * in-use state of the entries in the set. */
+    a_name_info->last_dentry_type = 
+        (EXFATFS_DIR_ENTRY_TYPE_ENUM)dentry->entry_type;
+
+    /* Determine how many name chars remain according to the name length from
+     * the file stream entry and how many chars can be obtained from this
+     * name entry. */
+    num_chars_to_copy = a_name_info->expected_name_length - a_name_info->actual_name_length;
+    if (num_chars_to_copy > EXFATFS_MAX_FILE_NAME_SEGMENT_LENGTH) {
+        num_chars_to_copy = EXFATFS_MAX_FILE_NAME_SEGMENT_LENGTH;
+    }
+
+    /* If there is enough space remaining in the name object, convert the
+     * name chars to UTF-8 and save them. */
+    if ((size_t)(a_name_info->actual_name_length + num_chars_to_copy) < 
+        a_name_info->fs_name->name_size - 1) {
+        if (fatfs_utf16_inode_str_2_utf8(a_name_info->fatfs, 
+            (UTF16*)dentry->utf16_name_chars, num_chars_to_copy,
+            (UTF8*)&(a_name_info->fs_name->name[a_name_info->actual_name_length]), 
+            a_name_info->fs_name->name_size,
+            a_inum, "file name segment") != TSKconversionOK) {
+            /* Discard whatever was written by the failed conversion and save
+             * whatever has been found to this point, if anything. */
+            a_name_info->fs_name->name[a_name_info->actual_name_length] = '\0';
+            exfatfs_add_name_to_dir_and_reset_info(a_name_info);
+            return;
+        }
+
+        /* Update the actual name length and null-terminate the name so far. */
+        a_name_info->actual_name_length += num_chars_to_copy;
+        a_name_info->fs_name->name[a_name_info->actual_name_length] = '\0';
+    }
+
+    /* If all of the secondary entries for the set are present, save the name,
+     * if any. */
+    // RJCTODO: Verify the check sum?
+    ++a_name_info->actual_secondary_entry_count;
+    if (a_name_info->actual_secondary_entry_count == 
+        a_name_info->expected_secondary_entry_count) {
+        exfatfs_add_name_to_dir_and_reset_info(a_name_info);
+    }
+}
+
+/**
+ * \internal
+ * Populates an EXFATFS_FS_NAME_INFO object with data parsed from a volume
+ * label directory entry. 
+ *
+ * @param a_name_info The name info object.
+ * @param a_dentry A buffer containing a volume label directory entry.
+ * @param a_inum The inode address associated with the directory entry.
+ */
+static void
+exfats_parse_vol_label_dentry(EXFATFS_FS_NAME_INFO *a_name_info, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum)
+{
+    EXFATFS_VOL_LABEL_DIR_ENTRY *dentry = (EXFATFS_VOL_LABEL_DIR_ENTRY*)a_dentry;
+    const char *tag = " (Volume Label Entry)";
+    size_t tag_length = 0;
+
+    assert(a_name_info != NULL);
+    assert(a_name_info->fatfs != NULL);
+    assert(a_name_info->fs_name != NULL);
+    assert(a_name_info->fs_name->name != NULL);
+    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
+    assert(a_name_info->fs_dir != NULL);
+    assert(dentry != NULL);
+    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL ||
+           dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL);
+    assert(fatfs_inum_is_in_range(a_name_info->fatfs, a_inum));
+
+    /* Starting parse of a new name, save the previous name, if any. */
+    exfatfs_add_name_to_dir_and_reset_info(a_name_info);
+
+    /* Set the current entry type. This is used to check the sequence and 
+     * in-use state of the entries in the set. */
+    a_name_info->last_dentry_type = 
+        (EXFATFS_DIR_ENTRY_TYPE_ENUM)dentry->entry_type;
+
+    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL) {
+        if (fatfs_utf16_inode_str_2_utf8(a_name_info->fatfs, 
+            (UTF16*)dentry->volume_label, (size_t)dentry->utf16_char_count + 1, 
+            (UTF8*)a_name_info->fs_name->name, a_name_info->fs_name->name_size,
+            a_inum, "volume label") != TSKconversionOK) {
+            /* Discard whatever was written by the failed conversion. */
+            exfatfs_reset_name_info(a_name_info);
+            return;
+        }
+    }
+    else {
+        strcpy(a_name_info->fs_name->name, EXFATFS_EMPTY_VOLUME_LABEL_DENTRY_NAME);
+    }
+
+    a_name_info->actual_name_length += dentry->utf16_char_count;
+    a_name_info->fs_name->name[a_name_info->actual_name_length] = '\0';
+
+    tag_length = strlen(tag);
+    if ((size_t)a_name_info->actual_name_length + tag_length < 
+        FATFS_MAXNAMLEN_UTF8) {
+        strcat(a_name_info->fs_name->name, tag);
+    }
+
+    /* Record the inum associated with this name. */
+    a_name_info->fs_name->meta_addr =  a_inum;
+
+    /* Not a directory. */
+    a_name_info->fs_name->type = TSK_FS_NAME_TYPE_REG;
+
+    if (a_name_info->sector_is_allocated) {
+        a_name_info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;    
+    }
+
+    /* Save the volume label. */
+    exfatfs_add_name_to_dir_and_reset_info(a_name_info);
+}
+
+/**
+ * \internal
+ * Populates an EXFATFS_FS_NAME_INFO object with data parsed from a 
+ * special file directory entry. 
+ *
+ * @param a_name_info The name info object.
+ * @param a_dentry A buffer containing a special file directory entry.
+ * @param a_inum The inode address associated with the directory entry.
+ */
+static void
+exfats_parse_special_file_dentry(EXFATFS_FS_NAME_INFO *a_name_info, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum)
+{
+    assert(a_name_info != NULL);
+    assert(a_name_info->fatfs != NULL);
+    assert(a_name_info->fs_name != NULL);
+    assert(a_name_info->fs_name->name != NULL);
+    assert(a_name_info->fs_name->name_size == FATFS_MAXNAMLEN_UTF8);
+    assert(a_name_info->fs_dir != NULL);
+    assert(a_dentry != NULL);
+    assert(a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID ||
+           a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP ||
+           a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE ||
+           a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_ACT);
+    assert(fatfs_inum_is_in_range(a_name_info->fatfs, a_inum));
+
+    /* Starting parse of a new name, save the previous name, if any. */
+    exfatfs_add_name_to_dir_and_reset_info(a_name_info);
+
+    /* Record the inum associated with this name. */
+    a_name_info->fs_name->meta_addr = a_inum;
+
+    /* Set the current entry type. This is used to check the sequence and 
+     * in-use state of the entries in the set. */
+    a_name_info->last_dentry_type = 
+        (EXFATFS_DIR_ENTRY_TYPE_ENUM)a_dentry->data[0];
+
+    switch (a_dentry->data[0]) {
+        case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
+            strcpy(a_name_info->fs_name->name, EXFATFS_VOLUME_GUID_DENTRY_NAME);
+            break;
+        case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:
+            strcpy(a_name_info->fs_name->name, EXFATFS_ALLOC_BITMAP_DENTRY_NAME);
+            break;
+        case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:
+            strcpy(a_name_info->fs_name->name, EXFATFS_UPCASE_TABLE_DENTRY_NAME);
+            break;
+        case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:
+            strcpy(a_name_info->fs_name->name, EXFATFS_TEX_FAT_DENTRY_NAME);
+            break;
+        case EXFATFS_DIR_ENTRY_TYPE_ACT:
+            strcpy(a_name_info->fs_name->name, EXFATFS_ACT_DENTRY_NAME);
+            break;
+    }
+
+    /* Not a directory. */
+    a_name_info->fs_name->type = TSK_FS_NAME_TYPE_REG;
+
+    if (a_name_info->sector_is_allocated) {
+        a_name_info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;    
+    }
+
+    /* Save the virtual file name. */
+    exfatfs_add_name_to_dir_and_reset_info(a_name_info);
+}
+
+/**
+ * /internal
+ * Parse a buffer containing the contents of a directory and add TSK_FS_NAME 
+ * objects for each named file found to the TSK_FS_DIR representation of the 
+ * directory.
+ *
+ * @param a_fatfs File system information structure for file system that
+ * contains the directory.
+ * @param a_fs_dir Directory structure into to which parsed file metadata will
+ * be added.
+ * @param a_buf Buffer that contains the directory contents.
+ * @param a_buf_len Length of buffer in bytes (must be a multiple of sector
+*  size).
+ * @param a_sector_addrs Array where each element is the original address of
+ * the corresponding sector in a_buf (size of array is number of sectors in
+ * the directory).
+ * @return TSK_RETVAL_ENUM
+*/
+TSK_RETVAL_ENUM
+exfatfs_dent_parse_buf(FATFS_INFO *a_fatfs, TSK_FS_DIR *a_fs_dir, char *a_buf,
+    TSK_OFF_T a_buf_len, TSK_DADDR_T *a_sector_addrs)
+{
+    const char *func_name = "exfatfs_parse_directory_buf";
+    TSK_FS_INFO *fs = NULL;
+    TSK_OFF_T num_sectors = 0;
+    TSK_OFF_T sector_index = 0;
+    TSK_INUM_T base_inum_of_sector = 0;
+    EXFATFS_FS_NAME_INFO name_info;
+    TSK_OFF_T dentry_index = 0;
+    FATFS_DENTRY *dentry = NULL;
+    int entries_count = 0;
+    int invalid_entries_count = 0;
+    uint8_t is_corrupt_dir = 0;
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
+        fatfs_ptr_arg_is_null(a_fs_dir, "a_fs_dir", func_name) ||
+        fatfs_ptr_arg_is_null(a_buf, "a_buf", func_name) ||
+        fatfs_ptr_arg_is_null(a_sector_addrs, "a_sector_addrs", func_name)) {
+        return TSK_ERR; 
+    }
+
+    assert(a_buf_len > 0);
+    if (a_buf_len < 0) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("%s: invalid buffer length", func_name);
+        return TSK_ERR; 
+    }
+
+    fs = (TSK_FS_INFO*)a_fatfs;
+
+    memset((void*)&name_info, 0, sizeof(EXFATFS_FS_NAME_INFO));
+    name_info.fatfs = a_fatfs;
+    if ((name_info.fs_name = tsk_fs_name_alloc(FATFS_MAXNAMLEN_UTF8, 0)) == NULL) {
+        return TSK_ERR;
+    }
+    name_info.fs_name->name[0] = '\0';
+    name_info.fs_dir = a_fs_dir;
+
+    /* Loop through the sectors in the buffer. */ 
+    dentry = (FATFS_DENTRY*)a_buf;
+    num_sectors = a_buf_len / a_fatfs->ssize;
+    for (sector_index = 0; sector_index < num_sectors; ++sector_index) {
+        /* Convert the address of the current sector into an inode address. */
+        base_inum_of_sector = 
+            FATFS_SECT_2_INODE(a_fatfs, a_sector_addrs[sector_index]);
+        if (base_inum_of_sector > fs->last_inum) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_ARG);
+            tsk_error_set_errstr("%s: inode address for sector address %" 
+                PRIuDADDR " at addresses array index %" PRIuDADDR 
+                " is too large", func_name, base_inum_of_sector, sector_index);
+            tsk_fs_name_free(name_info.fs_name);
+            return TSK_COR;
+        }
+
+        if (tsk_verbose) {
+            tsk_fprintf(stderr,"%s: Parsing sector %" PRIuDADDR " for dir %" 
+                PRIuINUM "\n", func_name, a_sector_addrs[sector_index], a_fs_dir->addr);
+        }
+
+        /* Get the allocation status of the current sector. */
+        if ((name_info.sector_is_allocated = 
+            fatfs_is_sectalloc(a_fatfs, a_sector_addrs[sector_index])) == -1) {
+            if (tsk_verbose) {
+                tsk_fprintf(stderr, 
+                    "%s: Error looking up allocation status of sector : %"
+                    PRIuDADDR "\n", func_name, a_sector_addrs[sector_index]);
+                tsk_error_print(stderr);
+            }
+            tsk_error_reset();
+            continue;
+        }
+
+        /* Loop through the putative directory entries in the current sector. */
+        for (dentry_index = 0; dentry_index < a_fatfs->dentry_cnt_se; ++dentry_index, ++dentry) {
+            FATFS_DENTRY *current_dentry = dentry;
+            TSK_INUM_T current_inum = base_inum_of_sector + dentry_index;
+            EXFATFS_DIR_ENTRY_TYPE_ENUM dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
+
+            ++entries_count; // RJCTODO: Should this be reset for each iteration of this loop?
+
+            if (!fatfs_inum_is_in_range(a_fatfs, current_inum)) {
+                tsk_fs_name_free(name_info.fs_name);
+                return TSK_ERR;
+            }
+
+            if (exfatfs_is_dentry(a_fatfs, current_dentry, 
+                (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)name_info.sector_is_allocated, 
+                (uint8_t)(!is_corrupt_dir && name_info.sector_is_allocated))) {
+                dentry_type = (EXFATFS_DIR_ENTRY_TYPE_ENUM)current_dentry->data[0];
+            }
+            else {
+                dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
+            }
+
+            switch (dentry_type) {
+            case EXFATFS_DIR_ENTRY_TYPE_FILE:
+            case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE:
+                exfats_parse_file_dentry(&name_info, current_dentry, current_inum);                 
+                break;
+            case EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM:
+            case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM:
+                exfats_parse_file_stream_dentry(&name_info, current_dentry, current_inum);                 
+                break;
+            case EXFATFS_DIR_ENTRY_TYPE_FILE_NAME:
+            case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME:
+                exfats_parse_file_name_dentry(&name_info, current_dentry, current_inum);                 
+                break;
+            case EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL:
+            case EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL:
+                exfats_parse_vol_label_dentry(&name_info, current_dentry, current_inum);
+                break;
+            case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
+            case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:
+            case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:
+            case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:
+            case EXFATFS_DIR_ENTRY_TYPE_ACT:
+                exfats_parse_special_file_dentry(&name_info, current_dentry, current_inum);                 
+                break;
+            case EXFATFS_DIR_ENTRY_TYPE_NONE:
+            default:
+                ++invalid_entries_count;
+                if (entries_count == 4 && invalid_entries_count == 4) {
+                    /* If the first four putative entries in the buffer are not
+                     * entries, set the corrupt directory flag to make entry tests
+                     * more in-depth, even for allocated sectors. */
+                    is_corrupt_dir = 1;
+                }
+
+                /* Starting parse of a new name, save the previous name, 
+                 * if any. */
+                exfatfs_add_name_to_dir_and_reset_info(&name_info);
+
+                break;
+            }
+        }
+    }
+
+     /* Save the last parsed name, if any. */
+    exfatfs_add_name_to_dir_and_reset_info(&name_info);
+    tsk_fs_name_free(name_info.fs_name);
+
+    return TSK_OK;
 }
\ No newline at end of file
diff --git a/tsk/fs/exfatfs_meta.c b/tsk/fs/exfatfs_meta.c
index 381a380a8..544e663bf 100755
--- a/tsk/fs/exfatfs_meta.c
+++ b/tsk/fs/exfatfs_meta.c
@@ -1,1823 +1,1823 @@
-/*
-** The Sleuth Kit
-**
-** Copyright (c) 2013 Basis Technology Corp.  All rights reserved
-** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
-**
-** This software is distributed under the Common Public License 1.0
-**
-*/
-
-/*
- * This code makes use of research presented in the following paper:
- * "Reverse Engineering the exFAT File System" by Robert Shullich
- * Retrieved May 2013 from: 
- * http://www.sans.org/reading_room/whitepapers/forensics/reverse-engineering-microsoft-exfat-file-system_33274
- *
- * Some additional details concerning TexFAT were obtained in May 2013
- * from:
- * http://msdn.microsoft.com/en-us/library/ee490643(v=winembedded.60).aspx
- */
-
-/**
- * \file exfatfs_meta.c
- * Contains the internal TSK exFAT file system code to access the data in the 
- * metadata data category as defined in the book "File System Forensic 
- * Analysis" by Brian Carrier (pp. 174-175). 
- */
-
-#include "tsk_exfatfs.h" /* Included first to make sure it stands alone. */
-#include "tsk_fs_i.h"
-#include "tsk_fatfs.h"
-#include <assert.h>
-
-// RJCTODO: This code does not follow the lazy load paradigm for file attributes, i.e., data runs. The reason for this is that unlike FATXX,
-// exFAT only uses the FAT for fragmented files. The valid FAT chain flag in file stream entries is the indicator for regular files. For
-// other "special" files (e.g., allocation bitmap, upcase table) there will always be contiguous data runs. In all cases, the data for deciding
-// whether a run should be contiguous is in the directory entry. We could do away with the eager load of the data runs for exFAT if we allocated another 
-// byte of file-system-specific content in the TSK_FS_META structure to indicate whether or not there was a valid FAT chain for the file. 
-// This would also allow us to have a somewhat smarter recovery algorithm for deleted exFAT files without FAT chains - we would know the maximum 
-// number of clusters from which we could hope to recover any authentic file content more precisely than we do by using file size alone. This would also
-// support a uniform inode_copy function signature for FATXX and exFAY - it would no longer be necessary to pass the entire file object through, just the
-// meta object. On the other hand, the fatxxfs_copy_dinode function could be changed to take a TSK_FS_FILE instead of a TSK_FS_META object parameter.
-
-// RJCTODO: Adding yet another byte to the file system specific content (see above for the first proposed new byte) would allow us to save the directory 
-// entry type as well. An immediate benefit of this would be the way we would be able to skip over the recovery code for the file name entries of deleted files - 
-// currently we don't add a data run to the file name entries of allocated files, yet we tack a "one cluster run" on to each file name entry for a deleted file 
-// (see lines 2015-2034 of fatfs_meta.c). This is a bug as far as I'm concerned.
-
-// RJCTODO: Enum variables display nicely in IntelliSense. Use them instead of the integral type flags I put in to deal with compiler warnings
-// and use casts instead.
-
-// RJCTODO: Consider standardizing on the use of tsk_error_reset() throughout the FAT code.
-
-// RJCTODO: Make sure all exFAT code is Doxygen commented. It would be better to also include shared FAT code in this. Even better would be
-// to make sure the FATXX code is documented as well.
-
-// RJCTODO: It would be good to obtain real world exFAT images and a Windows CE exFAT image for further testing.
-
-/**
- * \internal
- * Checks whether a specified cluster is allocated according to the allocation 
- * bitmap of an exFAT file system. 
- *
- * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system.
- * @param [in] a_cluster_addr The cluster address of the cluster to check. 
- * @return 1 if the cluster is allocated, 0 if the cluster is not allocated, 
- * or -1 if an error occurs.
- */
-int8_t 
-exfatfs_is_cluster_alloc(FATFS_INFO *a_fatfs, TSK_DADDR_T a_cluster_addr)
-{
-    const char *func_name = "exfatfs_is_clust_alloc";
-    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
-    TSK_DADDR_T bitmap_byte_offset = 0;
-    uint8_t bitmap_byte;
-    ssize_t bytes_read = 0;
-
-    assert(a_fatfs != NULL);
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) {
-        return -1;
-    }
-
-    assert((a_cluster_addr >= FATFS_FIRST_CLUSTER_ADDR) && (a_cluster_addr <= a_fatfs->lastclust));
-    if ((a_cluster_addr < FATFS_FIRST_CLUSTER_ADDR) || (a_cluster_addr > a_fatfs->lastclust)) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_ARG);
-        tsk_error_set_errstr("%s: cluster address %" PRIuINUM " out of range", func_name, a_cluster_addr);
-        return -1;
-    }
-
-     /* Normalize the cluster address. */
-    a_cluster_addr = a_cluster_addr - FATFS_FIRST_CLUSTER_ADDR;
-
-    /* Determine the offset of the byte in the allocation bitmap that contains
-     * the bit for the specified cluster. */
-    bitmap_byte_offset = (a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap * a_fatfs->ssize) + (a_cluster_addr / 8);
-
-    /* Read the byte. */
-    bytes_read = tsk_fs_read(fs, bitmap_byte_offset, (char*)&bitmap_byte, 1);
-    if (bytes_read != 1) {
-        if (bytes_read >= 0) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_READ);
-        }
-        tsk_error_set_errstr2("%s: failed to read bitmap byte at offset %" PRIuINUM "", func_name, bitmap_byte_offset); 
-        return -1;
-    }
-
-    /* Check the bit that corresponds to the specified cluster. Note that this
-     * computation does not yield 0 or 1. */
-    if (bitmap_byte & (1 << (a_cluster_addr % 8))) {
-        return 1;
-    }
-    else {
-        return 0;
-    }
-}
-
-/**
- * \internal
- * Determine whether the contents of a buffer may be an exFAT volume label
- * directory entry.
- *
- * @param [in] a_dentry A directory entry buffer.
- * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
- * cluster from which the buffer was filled. 
- * @returns 1 if the directory entry buffer likely contains a volume label 
- * directory entry, 0 otherwise. 
- */
-uint8_t
-exfatfs_is_vol_label_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_cluster_is_alloc)
-{
-    const char *func_name = "exfatfs_is_vol_label_dentry";
-    EXFATFS_VOL_LABEL_DIR_ENTRY *dentry = (EXFATFS_VOL_LABEL_DIR_ENTRY*)a_dentry;
-    uint8_t i = 0;
-    
-    assert(a_dentry != NULL);
-    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
-        return 0;
-    }
-
-    /* Check the entry type byte. */
-    if ((dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL) && 
-        (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL)) {
-        return 0;
-    }
-
-    /* There should be a single volume label directory entry at the
-     * beginning of the root directory, so check the allocation status, if 
-     * known, of the cluster from which the buffer was filled. */
-    if (a_cluster_is_alloc == FATFS_DATA_UNIT_ALLOC_STATUS_UNALLOC) {
-        return 0;
-    }
-
-    if (dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL) {
-        /* There is supposed to be a label, check its length. */
-        if ((dentry->utf16_char_count < 1) || (dentry->utf16_char_count > EXFATFS_MAX_VOLUME_LABEL_LEN)) {
-            if (tsk_verbose) {
-                fprintf(stderr, "%s: incorrect volume label length\n", func_name);
-            }
-            return 0;
-        }
-    }
-    else {
-        // RJCTODO: I think I verified that these tests are valid, but check again to be sure.
-        /* There is supposed to be no label, check for a zero in the length
-         * field. */
-        if (dentry->utf16_char_count != 0x00) {
-            if (tsk_verbose) {
-                fprintf(stderr, "%s: volume label length non-zero for no label entry\n", func_name);
-            }
-            return 0;
-        }
-
-        /* Every byte of the UTF-16 volume label string should be 0. */
-        for (i = 0; i < EXFATFS_MAX_VOLUME_LABEL_LEN * 2; ++i) {
-            if (dentry->volume_label[i] != 0x00) {
-                if (tsk_verbose) {
-                    fprintf(stderr, "%s: non-zero byte in label for no label entry\n", func_name);
-                }
-                return 0;
-            }
-        }
-    }
-
-    return 1;
-}
-
-/**
- * \internal
- * Determine whether the contents of a buffer may be an exFAT volume GUID
- * directory entry.
- *
- * @param [in] a_dentry A directory entry buffer.
- * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
- * cluster from which the buffer was filled. 
- * @returns 1 if the directory entry buffer likely contains a volume GUID 
- * directory entry, 0 otherwise. 
- */
-uint8_t
-exfatfs_is_vol_guid_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status)
-{
-    const char *func_name = "exfatfs_is_vol_guid_dentry";
-    EXFATFS_VOL_GUID_DIR_ENTRY *dentry = (EXFATFS_VOL_GUID_DIR_ENTRY*)a_dentry;
-    
-    assert(a_dentry != NULL);
-    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
-        return 0;
-    }
-
-    /* Check the entry type byte. */
-    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID) {
-        return 0;
-    }
-
-    /* There is not enough data in a volume GUID directory entry to test
-     * anything but the entry type byte. However, a volume GUID directory 
-     * entry should be in allocated space, so check the allocation status, if
-     * known, of the cluster from which the buffer was filled to reduce false
-     * positives. */
-    return ((a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_ALLOC) ||
-            (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN));
-}
-
-/**
- * \internal
- * Determine whether the contents of a buffer may be an exFAT allocation bitmap
- * directory entry. The test will be more reliable if an optional FATFS_INFO 
- * struct representing the file system is provided.
- *
- * @param [in] a_dentry A directory entry buffer.
- * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
- * cluster from which the buffer was filled. 
- * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system,
- * may be NULL.
- * @returns 1 if the directory entry buffer likely contains an allocation 
- * bitmap directory entry, 0 otherwise. 
- */
-uint8_t
-exfatfs_is_alloc_bitmap_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status, FATFS_INFO *a_fatfs)
-{
-    const char *func_name = "exfatfs_is_alloc_bitmap_dentry";
-    EXFATFS_ALLOC_BITMAP_DIR_ENTRY *dentry = (EXFATFS_ALLOC_BITMAP_DIR_ENTRY*)a_dentry;
-    uint32_t first_cluster_of_bitmap = 0;
-    uint64_t length_of_alloc_bitmap_in_bytes = 0;
-
-    assert(a_dentry != NULL);
-    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
-        return 0;
-    }
-
-    /* Check the entry type byte. */
-    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP) {
-        return 0;
-    }
-
-    /* There should be a single allocation bitmap directory entry near the the
-     * beginning of the root directory, so check the allocation status, if 
-     * known, of the cluster from which the buffer was filled. */
-    if (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNALLOC) {
-        return 0;
-    }
-
-    if (a_fatfs != NULL) {
-        /* The length of the allocation bitmap should be consistent with the 
-         * number of clusters in the data area as specified in the volume boot
-         * record. */
-        length_of_alloc_bitmap_in_bytes = tsk_getu64(a_fatfs->fs_info.endian, dentry->length_of_alloc_bitmap_in_bytes);
-        if (length_of_alloc_bitmap_in_bytes != (a_fatfs->clustcnt + 7) / 8) {
-            if (tsk_verbose) {
-                fprintf(stderr, "%s: bitmap length incorrect\n", func_name);
-            }
-            return 0;
-        }
-
-        /* The first cluster of the bit map should be within the data area.
-         * It is usually in the first cluster. */
-        first_cluster_of_bitmap = tsk_getu32(a_fatfs->fs_info.endian, dentry->first_cluster_of_bitmap);
-        if ((first_cluster_of_bitmap < EXFATFS_FIRST_CLUSTER) ||
-            (first_cluster_of_bitmap > a_fatfs->lastclust)) {
-            if (tsk_verbose) {
-                fprintf(stderr, "%s: first cluster not in cluster heap\n", func_name);
-            }
-            return 0;
-        }
-        
-        /* The first cluster of the allocation bitmap should be allocated (the 
-         * other conditions allow this function to be safely used to look for
-         * the allocation bitmap during FATFS_INFO initialization, before a 
-         * cluster allocation is possible). */
-        if ((a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap > 0) &&
-            (a_fatfs->EXFATFS_INFO.length_of_alloc_bitmap_in_bytes > 0) &&
-            (exfatfs_is_cluster_alloc(a_fatfs, (TSK_DADDR_T)first_cluster_of_bitmap) != 1)) {
-            if (tsk_verbose) {
-                fprintf(stderr, 
-                    "%s: first cluster of allocation bitmap not allocated\n", func_name);
-            }
-            return 0;
-        }
-    }
-
-    return 1;
-}
-
-/**
- * \internal
- * Determine whether the contents of a buffer may be an exFAT upcase table
- * directory entry. The test will be more reliable if an optional FATFS_INFO 
- * struct representing the file system is provided.
- *
- * @param [in] a_dentry A directory entry buffer.
- * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
- * cluster from which the buffer was filled. 
- * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system,
- * may be NULL.
- * @returns 1 if the directory entry buffer likely contains an upcase table 
- * directory entry, 0 otherwise. 
- */
-uint8_t
-exfatfs_is_upcase_table_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status, FATFS_INFO *a_fatfs)
-{
-    const char *func_name = "exfatfs_is_upcase_table_dentry";
-    EXFATFS_UPCASE_TABLE_DIR_ENTRY *dentry = (EXFATFS_UPCASE_TABLE_DIR_ENTRY*)a_dentry;
-    uint64_t table_size = 0;
-    uint32_t first_cluster_of_table = 0;
-
-    assert(a_dentry != NULL);
-    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
-        return 0;
-    }
-
-    /* Check the entry type byte. */
-    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE) {
-        return 0;
-    }
-
-    /* There should be a single upcase table directory entry near the the
-     * beginning of the root directory, so check the allocation status, if 
-     * known, of the cluster from which the buffer was filled. */
-    if (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNALLOC) {
-        return 0;
-    }
-
-    if (a_fatfs != NULL) {
-        /* Check the size of the table. */
-        table_size = tsk_getu64(a_fatfs->fs_info.endian, dentry->table_length_in_bytes);
-        if (table_size == 0) {
-            if (tsk_verbose) {
-                fprintf(stderr, "%s: table size is zero\n", func_name);
-            }
-            return 0;
-        }
-
-        /* Is the table size less than the size of the cluster heap 
-         * (data area)? The cluster heap size is computed by multiplying the
-         * cluster size by the number of sectors in a cluster and then 
-         * multiplying by the number of bytes in a sector (the last operation 
-         * is optimized as a left shift by the base 2 log of sector size). */
-        if (table_size > (a_fatfs->clustcnt * a_fatfs->csize) << a_fatfs->ssize_sh) {
-            if (tsk_verbose) {
-                fprintf(stderr, "%s: table size too big\n", func_name);
-            }
-            return 0;
-        }
-
-        /* Is the address of the first cluster in range? */
-        first_cluster_of_table = tsk_getu32(a_fatfs->fs_info.endian, dentry->first_cluster_of_table);
-        if ((first_cluster_of_table < EXFATFS_FIRST_CLUSTER) ||
-            (first_cluster_of_table > a_fatfs->lastclust)) {
-            if (tsk_verbose) {
-                fprintf(stderr, 
-                    "%s: first cluster not in cluster heap\n", func_name);
-            }
-            return 0;
-        }
-
-        /* The first cluster of the table should be allocated. */
-        if (exfatfs_is_cluster_alloc(a_fatfs, (TSK_DADDR_T)first_cluster_of_table) != 1) {
-            if (tsk_verbose) {
-                fprintf(stderr, 
-                    "%s: first cluster of table not allocated\n", func_name);
-            }
-            return 0;
-        }
-    }
-
-    return 1;
-}
-
-/**
- * \internal
- * Determine whether the contents of a buffer may be an exFAT TexFAT directory
- * entry.
- *
- * @param [in] a_dentry A directory entry buffer.
- * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
- * cluster from which the buffer was filled. 
- * @returns 1 if the directory entry buffer likely contains a TexFAT directory
- * entry, 0 otherwise. 
- */
-uint8_t
-exfatfs_is_texfat_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status)
-{
-    const char *func_name = "exfatfs_is_texfat_dentry";
-    EXFATFS_TEXFAT_DIR_ENTRY *dentry = (EXFATFS_TEXFAT_DIR_ENTRY*)a_dentry;
-    
-    assert(a_dentry != NULL);
-    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
-        return 0;
-    }
-
-    /* Check the entry type byte. */
-    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_TEXFAT) {
-        return 0;
-    }
-
-    /* There is not enough data in a TexFAT directory entry to test anything
-     * but the entry type byte. However, a TexFAT directory entry should be in 
-     * allocated space, so check the allocation status, if known, of the 
-     * cluster from which the buffer was filled to reduce false positives. */
-    return ((a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_ALLOC) ||
-            (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN));
-}
-
-/**
- * \internal
- * Determine whether the contents of a buffer may be an exFAT access control 
- * table directory entry.
- *
- * @param [in] a_dentry A directory entry buffer.
- * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
- * cluster from which the buffer was filled. 
- * @returns 1 if the directory entry buffer likely contains an access control
- * table entry, 0 otherwise. 
- */
-uint8_t
-exfatfs_is_access_ctrl_table_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status)
-{
-    const char *func_name = "exfatfs_is_texfat_dentry";
-    EXFATFS_TEXFAT_DIR_ENTRY *dentry = (EXFATFS_TEXFAT_DIR_ENTRY*)a_dentry;
-    
-    assert(a_dentry != NULL);
-    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
-        return 0;
-    }
-
-    /* Check the entry type byte. */
-    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_TEXFAT) {
-        return 0;
-    }
-
-    /* There is not enough data in an access control table directory entry to 
-     * test anything but the entry type byte. However, an access control table
-     * directory entry should be in allocated space, so check the allocation 
-     * status, if known, of the cluster from which the buffer was filled to 
-     * reduce false positives. */
-    return ((a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_ALLOC) ||
-            (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN));
-}
-
-
-/**
- * \internal
- * Determine whether the contents of a buffer may be an exFAT file directory 
- * entry. The test will be more reliable if an optional FATFS_INFO struct 
- * representing the file system is provided.
- *
- * @param [in] a_dentry A directory entry buffer.
- * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system,
- * may be NULL.
- * @returns 1 if the directory entry buffer likely contains a file directory 
- * entry, 0 otherwise. 
- */
-uint8_t
-exfatfs_is_file_dentry(FATFS_DENTRY *a_dentry, FATFS_INFO *a_fatfs)
-{
-    const char *func_name = "exfatfs_is_file_dentry";
-    TSK_FS_INFO *fs = NULL;
-    EXFATFS_FILE_DIR_ENTRY *dentry = (EXFATFS_FILE_DIR_ENTRY*)a_dentry;
-
-    assert(a_dentry != NULL);
-    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
-        return 0;
-    }
-
-    /* Check the entry type byte. */
-    if ((dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_FILE) && 
-        (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE)) {
-        return 0;
-    }
-
-    /* A file directory entry is the first entry of a file directory entry set
-     * consisting of a file directory entry followed by a file stream directory
-     * entry and from 1 to 17 file name directory entries. The file stream and
-     * file name entries are called secondary entries. */
-    if (dentry->secondary_entries_count < EXFATFS_MIN_FILE_SECONDARY_DENTRIES_COUNT ||
-        dentry->secondary_entries_count > EXFATFS_MAX_FILE_SECONDARY_DENTRIES_COUNT) {
-        if (tsk_verbose) {
-            fprintf(stderr, "%s: secondary entries count out of range\n", 
-                func_name);
-        }
-        return 0;
-    }
-
-    if (a_fatfs != NULL) {
-        fs = &(a_fatfs->fs_info);   
-
-        /* Make sure the time stamps aren't all zeros. */
-        if ((tsk_getu16(fs->endian, dentry->modified_date) == 0) &&
-            (tsk_getu16(fs->endian, dentry->modified_time) == 0) &&
-            (dentry->modified_time_tenths_of_sec == 0) && 
-            (tsk_getu16(fs->endian, dentry->created_date) == 0) &&
-            (tsk_getu16(fs->endian, dentry->created_time) == 0) &&
-            (dentry->created_time_tenths_of_sec == 0) && 
-            (tsk_getu16(fs->endian, dentry->accessed_date) == 0) &&
-            (tsk_getu16(fs->endian, dentry->accessed_time) == 0)) {
-            if (tsk_verbose) {
-                fprintf(stderr, "%s: time stamps all zero\n", 
-                    func_name);
-            }
-            return 0;
-        }
-    }
-
-    return 1;
-}
-
-/**
- * \internal
- * Determine whether the contents of a buffer may be an exFAT file stream 
- * directory entry. The test will be more reliable if an optional FATFS_INFO 
- * struct representing the file system is provided.
- *
- * @param [in] a_dentry A directory entry buffer.
- * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
- * cluster from which the buffer was filled. 
- * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system,
- * may be NULL.
- * @returns 1 if the directory entry buffer likely contains a file stream 
- * directory entry, 0 otherwise. 
- */
-uint8_t
-exfatfs_is_file_stream_dentry(FATFS_DENTRY *a_dentry, FATFS_INFO *a_fatfs)
-{
-    const char *func_name = "exfatfs_is_file_stream_dentry";
-    TSK_FS_INFO *fs = NULL;
-    EXFATFS_FILE_STREAM_DIR_ENTRY *dentry = (EXFATFS_FILE_STREAM_DIR_ENTRY*)a_dentry;
-    uint64_t file_size = 0;
-    uint32_t first_cluster = 0;
-
-    assert(a_dentry != NULL);
-    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
-        return 0;
-    }
-
-    /* Check the entry type byte. */
-    if ((dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM) && 
-        (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM)) {
-        return 0;
-    }
-
-    if (a_fatfs != NULL) {
-        fs = &(a_fatfs->fs_info);   
-
-        /* Check the size. */
-        file_size = tsk_getu64(fs->endian, dentry->data_length); // RJCTODO: How does this relate to the valid data length field? Is one or the other to be preferred?
-        if (file_size > 0) {
-            /* Is the file size less than the size of the cluster heap 
-             * (data area)? The cluster heap size is computed by multiplying the
-             * cluster size by the number of sectors in a cluster and then 
-             * multiplying by the number of bytes in a sector (the last operation 
-             * is optimized as a left shift by the base 2 log of sector size). */
-            if (file_size > (a_fatfs->clustcnt * a_fatfs->csize) << a_fatfs->ssize_sh) {
-                if (tsk_verbose) {
-                    fprintf(stderr, "%s: file size too big\n", func_name);
-                }
-                return 0;
-            }
-
-            /* Is the address of the first cluster in range? */
-            first_cluster = tsk_getu32(fs->endian, dentry->first_cluster_addr);
-            if ((first_cluster < EXFATFS_FIRST_CLUSTER) ||
-                (first_cluster > a_fatfs->lastclust)) {
-                if (tsk_verbose) {
-                    fprintf(stderr, 
-                        "%s: first cluster not in cluster heap\n", func_name);
-                }
-                return 0;
-            }
-
-            /* If the file is not marked as unallocated and has non-zero size, is its
-             * first cluster allocated? */
-            if ((dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM) && 
-                (exfatfs_is_cluster_alloc(a_fatfs, (TSK_DADDR_T)first_cluster) != 1)) {
-                if (tsk_verbose) {
-                    fprintf(stderr, 
-                        "%s: file not deleted, first cluster not allocated\n", func_name);
-                }
-                return 0;
-            }
-        }
-    }
-
-    // RJCTODO: Are there any more checks that could be done?
-
-    return 1;
-}
-
-/**
- * \internal
- * Determine whether the contents of a buffer may be an exFAT file name 
- * directory entry.
- *
- * @param [in] a_dentry A directory entry buffer.
- * @returns 1 if the directory entry buffer likely contains an file name
- * directory entry, 0 otherwise. 
- */
-uint8_t
-exfatfs_is_file_name_dentry(FATFS_DENTRY *a_dentry)
-{
-    const char *func_name = "exfatfs_is_file_name_dentry";
-    EXFATFS_FILE_NAME_DIR_ENTRY *dentry = (EXFATFS_FILE_NAME_DIR_ENTRY*)a_dentry;
-    
-    assert(a_dentry != NULL);
-    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
-        return 0;
-    }
-
-    /* There is not enough data in a file name directory entry
-     * to test anything but the entry type byte. */
-    return ((dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME) || 
-            (dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME));
-}
-
-
-/**
- * \internal
- * Determine whether a buffer likely contains a directory entry.
- * For the most reliable results, request the in-depth test.
- *
- * @param [in] a_fatfs Source file system for the directory entry.
- * @param [in] a_dentry Buffer that may contain a directory entry.
- * @param [in] a_cluster_is_alloc The allocation status, possibly unknown, of the 
- * cluster from which the buffer was filled. 
- * @param [in] a_do_basic_tests_only Whether to do basic or in-depth testing. 
- * @return 1 if the buffer likely contains a direcotry entry, 0 otherwise
- */
-uint8_t
-exfatfs_is_dentry(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_cluster_is_alloc, uint8_t a_do_basic_tests_only)
-{
-    const char *func_name = "exfatfs_is_dentry";
-
-    assert(a_dentry != NULL);
-    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
-        return 0;
-    }
-
-    switch (a_dentry->data[0])
-    {
-    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL:
-    case EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL:
-        return exfatfs_is_vol_label_dentry(a_dentry, a_cluster_is_alloc);
-    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
-        return exfatfs_is_vol_guid_dentry(a_dentry, a_cluster_is_alloc);
-    case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:
-        return exfatfs_is_alloc_bitmap_dentry(a_dentry, a_cluster_is_alloc, a_fatfs);
-    case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:
-        return exfatfs_is_upcase_table_dentry(a_dentry, a_cluster_is_alloc, a_fatfs);
-    case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:
-        return exfatfs_is_texfat_dentry(a_dentry, a_cluster_is_alloc);
-    case EXFATFS_DIR_ENTRY_TYPE_ACT:
-        return exfatfs_is_access_ctrl_table_dentry(a_dentry, a_cluster_is_alloc);
-    case EXFATFS_DIR_ENTRY_TYPE_FILE:
-    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE:
-        return exfatfs_is_file_dentry(a_dentry, a_fatfs);
-    case EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM:
-    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM:
-        return exfatfs_is_file_stream_dentry(a_dentry, a_fatfs);
-    case EXFATFS_DIR_ENTRY_TYPE_FILE_NAME:
-    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME:
-        return exfatfs_is_file_name_dentry(a_dentry);
-    default:
-        return 0;
-    }
-}
-
-/**
- * \internal
- * Construct a single, non-resident data run for the TSK_FS_META object of a 
- * TSK_FS_FILE object.  
- *
- * @param [in, out] a_fs_file Generic file with generic inode structure (TSK_FS_META).
- * @return 0 on success, 1 on failure, per TSK convention
- */
-static uint8_t
-exfatfs_make_contiguous_data_run(TSK_FS_FILE *a_fs_file)
-{
-    const char *func_name = "exfatfs_make_contiguous_data_run";
-    TSK_FS_META *fs_meta = NULL;
-    TSK_FS_INFO *fs = NULL;
-    FATFS_INFO *fatfs = NULL;
-    TSK_DADDR_T first_cluster = 0;
-    TSK_FS_ATTR_RUN *data_run;
-    TSK_FS_ATTR *fs_attr = NULL;
-    TSK_OFF_T alloc_size = 0;
-
-    assert(a_fs_file != NULL);
-    assert(a_fs_file->meta != NULL);
-    assert(a_fs_file->fs_info != NULL);
-
-    fs_meta = a_fs_file->meta;
-    fs = (TSK_FS_INFO*)a_fs_file->fs_info;
-    fatfs = (FATFS_INFO*)fs;
-
-    if (tsk_verbose) {
-        tsk_fprintf(stderr,
-            "%s: Loading attrs for inode: %" PRIuINUM
-            "\n", func_name, a_fs_file->meta->addr);
-    }
-
-    /* Get the stashed first cluster address of the file. If the address does
-     * not make sense, set the attribute state to TSK_FS_META_ATTR_ERROR so
-     * that there is no subsequent attempt to load a data run for this 
-     * file object. */
-    first_cluster = ((TSK_DADDR_T*)fs_meta->content_ptr)[0];
-    if ((first_cluster > (fatfs->lastclust)) &&
-        (FATFS_ISEOF(first_cluster, fatfs->mask) == 0)) {
-        fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-        tsk_error_reset();
-        if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC) {
-            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
-        }
-        else {
-            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
-        }
-        tsk_error_set_errstr
-            ("%s: Starting cluster address too large: %"
-            PRIuDADDR, func_name, first_cluster);
-        return 1;
-    }
-
-    /* Figure out the allocated size of the file. The minimum allocation unit
-     * for exFAT is a cluster, so the the roundup() function is used to round 
-     * up the file size in bytes to a multiple of cluser size in bytes. */
-    alloc_size = roundup(fs_meta->size, (fatfs->csize * fs->block_size));
-
-    /* Allocate an attribute list for the file. */
-    fs_meta->attr = tsk_fs_attrlist_alloc();
-
-    /* Allocate a non-resident attribute for the file and add it to the
-     * attribute list. */
-    if ((fs_attr = tsk_fs_attrlist_getnew(fs_meta->attr, 
-        TSK_FS_ATTR_NONRES)) == NULL) {
-        return 1;
-    }
-
-    /* Allocate a single data run for the attribute. For exFAT, a data run is 
-     * a contiguous run of sectors. */
-    data_run = tsk_fs_attr_run_alloc();
-    if (data_run == NULL) {
-        return 1;
-    }
-
-    /* Set the starting sector address of the run and the length of the run 
-     * in sectors. */
-    data_run->addr = FATFS_CLUST_2_SECT(fatfs, first_cluster);
-    data_run->len = roundup(fs_meta->size, 
-        (fatfs->csize * fs->block_size)) / fs->block_size;  
-
-    /* Add the data run to the attribute and add the attribute to the 
-     * attribute list. Note that the initial size and the allocation
-     * size are the same for exFAT. */
-    if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run, NULL,
-            TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
-            fs_meta->size,
-            data_run->len * fs->block_size,
-            data_run->len * fs->block_size, 
-            TSK_FS_ATTR_FLAG_NONE, 0)) {
-        return 1;
-    }
-
-    /* Mark the attribute list as loaded. */
-    fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
-
-    return 0;
-}
-
-/**
- * \internal
- * Use a volume label directory entry corresponding to the exFAT 
- * equivalent of an inode to populate the TSK_FS_META object of a 
- * TSK_FS_FILE object.
- *
- * @param [in] a_fatfs Source file system for the directory entry.
- * @param [in] a_inum Address of the inode.
- * @param [in] a_dentry A volume label directory entry.
- * @param a_fs_file Generic file with generic inode structure (TSK_FS_META).
- * @return TSK_RETVAL_ENUM.  
- */
-static TSK_RETVAL_ENUM 
-exfatfs_copy_vol_label_inode(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, FATFS_DENTRY *a_dentry, TSK_FS_FILE *a_fs_file)
-{
-    const char *func_name = "exfatfs_copy_vol_label_inode";
-    EXFATFS_VOL_LABEL_DIR_ENTRY *dentry = NULL;
-
-    assert(a_fatfs != NULL);
-    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
-    assert(a_dentry != NULL);
-    assert(a_fs_file != NULL);
-    assert(a_fs_file->meta != NULL);
-
-    dentry = (EXFATFS_VOL_LABEL_DIR_ENTRY*)a_dentry;
-    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL ||
-           dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL);
-
-    /* If there is a volume label, copy it to the name field of the 
-     * TSK_FS_META structure. */
-    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL) {
-        if (fatfs_utf16_inode_str_2_utf8(a_fatfs, (UTF16*)dentry->volume_label, (size_t)dentry->utf16_char_count + 1, 
-            (UTF8*)a_fs_file->meta->name2->name, sizeof(a_fs_file->meta->name2->name), a_inum, "volume label") != TSKconversionOK) {
-            return TSK_COR;
-        }
-    }
-    else {
-        strcpy(a_fs_file->meta->name2->name, EXFATFS_EMPTY_VOLUME_LABEL_DENTRY_NAME);
-    }
-
-    return TSK_OK;
-}
-
-/**
- * \internal
- * Use an allocation bitmap directory entry corresponding to the exFAT 
- * equivalent of an inode to populate the TSK_FS_META object of a 
- * TSK_FS_FILE object.
- *
- * @param a_fatfs [in] Source file system for the directory entries.
- * @param [in] a_dentry An allocation bitmap directory entry.
- * @param a_fs_file [in, out] Generic file with generic inode structure (TSK_FS_META).
- * @return TSK_RETVAL_ENUM.  
- */
-static TSK_RETVAL_ENUM 
-exfatfs_copy_alloc_bitmap_inode(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, TSK_FS_FILE *a_fs_file)
-{
-    const char *func_name = "exfatfs_copy_alloc_bitmap_inode";
-    EXFATFS_ALLOC_BITMAP_DIR_ENTRY *dentry = NULL;
-
-    assert(a_fatfs != NULL);
-    assert(a_dentry != NULL);
-    assert(a_fs_file != NULL);
-    assert(a_fs_file->meta != NULL);
-
-    dentry = (EXFATFS_ALLOC_BITMAP_DIR_ENTRY*)a_dentry;
-    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP);
-
-    /* Set the file name to a descriptive pseudo file name. */
-    strcpy(a_fs_file->meta->name2->name, EXFATFS_ALLOC_BITMAP_DENTRY_NAME);
-
-    /* Set the size of the allocation bitmap and the address of its 
-     * first cluster. */
-    ((TSK_DADDR_T*)a_fs_file->meta->content_ptr)[0] = FATFS_SECT_2_CLUST(a_fatfs, a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap);
-    a_fs_file->meta->size = a_fatfs->EXFATFS_INFO.length_of_alloc_bitmap_in_bytes;
-    
-    /* There is no FAT chain walk for the allocation bitmap. Do an eager
-     * load instead of a lazy load of its data run. */
-    if (exfatfs_make_contiguous_data_run(a_fs_file)) {
-        return TSK_ERR;
-    }
-
-    return TSK_OK;
-}
-
-/**
- * \internal
- * Use an UP-Case table directory entry corresponding to the exFAT equivalent
- * of an inode to populate the TSK_FS_META object of a TSK_FS_FILE object.
- *
- * @param a_fatfs [in] Source file system for the directory entries.
- * @param [in] a_dentry An upcase table directory entry.
- * @param a_fs_file [in, out] Generic file with generic inode structure (TSK_FS_META).
- * @return TSK_RETVAL_ENUM.  
- */
-static TSK_RETVAL_ENUM 
-exfatfs_copy_upcase_table_inode(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, TSK_FS_FILE *a_fs_file)
-{
-    const char *func_name = "exfatfs_copy_upcase_table_inode";
-    EXFATFS_UPCASE_TABLE_DIR_ENTRY *dentry = NULL;
-
-    assert(a_fatfs != NULL);
-    assert(a_dentry != NULL);
-    assert(a_fs_file != NULL);
-    assert(a_fs_file->meta != NULL);
-
-    dentry = (EXFATFS_UPCASE_TABLE_DIR_ENTRY*)a_dentry;
-    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE);
-
-    strcpy(a_fs_file->meta->name2->name, EXFATFS_UPCASE_TABLE_DENTRY_NAME);
-
-    /* Set the size of the Up-Case table and the address of its 
-     * first cluster. */((TSK_DADDR_T*)a_fs_file->meta->content_ptr)[0] = tsk_getu32(a_fatfs->fs_info.endian, dentry->first_cluster_of_table);
-    a_fs_file->meta->size = tsk_getu64(a_fatfs->fs_info.endian, dentry->table_length_in_bytes);
-
-    /* There is no FAT chain walk for the upcase table. Do an eager
-     * load instead of a lazy load of its data run. */
-    if (exfatfs_make_contiguous_data_run(a_fs_file)) {
-        return TSK_ERR;
-    }
-
-    return TSK_OK;
-}
-
-/**
- * \internal
- * Given an inode address, load the corresponding directory entry and test
- * to see if it's an exFAT file stream directory entry.
- *
- * @param a_fatfs [in] Source file system for the directory entries.
- * @param a_stream_entry_inum [in] The inode address associated with the 
- * supposed file stream entry.
- * @param a_sector_is_alloc [in] The allocation status of the sector that
- * contains the supposed file stream entry.
- * @param a_file_dentry_type [in] The companion file entry type, 
- * i.e., deleted or not.
- * @param a_dentry [in, out] A directory entry structure. The stream 
- * entry, if found, will be loaded into it.
- * @return 0 on success, 1 on failure, per TSK convention
- */
-static uint8_t
-exfatfs_load_file_stream_dentry(FATFS_INFO *a_fatfs, 
-    TSK_INUM_T a_stream_entry_inum, uint8_t a_sector_is_alloc, 
-    EXFATFS_DIR_ENTRY_TYPE_ENUM a_file_dentry_type, // RJCTODO: Consider sending the desired type of stream entry in, or passing in the file entry.
-    FATFS_DENTRY *a_dentry)
-{
-    assert(a_fatfs != NULL);
-    assert(fatfs_inum_is_in_range(a_fatfs, a_stream_entry_inum));
-    assert(a_dentry != NULL);
-
-    if (fatfs_dentry_load(a_fatfs, a_dentry, a_stream_entry_inum) == 0 &&
-        exfatfs_is_dentry(a_fatfs, a_dentry, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)a_sector_is_alloc, a_sector_is_alloc)) {
-        /* If the bytes at the specified inode address are a file stream entry
-         * with the same allocation status as the file entry, report success. */
-        if ((a_file_dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE && 
-             a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM) ||
-            (a_file_dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE &&
-             a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM)) {
-            return 0;
-        }
-    }
-
-    memset((void*)a_dentry, 0, sizeof(FATFS_DENTRY));
-    return 1;
-}
-
-/**
- * \internal
- * Given an exFAT file directory entry, try to find the corresponding file
- * stream directory entry.
- *
- * @param [in] a_fatfs Source file system for the directory entries.
- * @param [in] a_file_entry_inum The inode address associated with the file 
- * entry.
- * @param [in] a_sector The address of the sector where the file entry was 
- * found.
- * @param [in] a_sector_is_alloc The allocation status of the sector.
- * @param [in] a_file_dentry_type The file entry type, deleted or not.
- * @param [in, out] The stream entry, if found, will be loaded into the
- * this generic directory entry structure.
- * @return 0 on success, 1 on failure, per TSK convention
- */
-static uint8_t
-exfatfs_new_find_file_stream_dentry(FATFS_INFO *a_fatfs, TSK_INUM_T a_file_entry_inum, 
-    EXFATFS_FILE_DIR_ENTRY *a_file_dentry, EXFATFS_FILE_STREAM_DIR_ENTRY *a_stream_dentry)
-{
-    const char *func_name = "exfatfs_new_find_file_stream_dentry";
-    TSK_INUM_T stream_entry_inum = 0;
-    int8_t alloc_check_ret_val = 0;
-    uint8_t cluster_is_alloc = 0;
-    TSK_DADDR_T sector = 0; // RJCTODO: Improve name
-    TSK_DADDR_T cluster = 0; // RJCTODO: Improve name
-    TSK_DADDR_T cluster_base_sector = 0;
-    TSK_DADDR_T last_entry_offset = 0;
-    TSK_DADDR_T file_entry_offset = 0;
-    EXFATFS_DIR_ENTRY_TYPE_ENUM dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
-    TSK_DADDR_T next_cluster = 0;
-
-    assert(a_fatfs != NULL);
-    assert(fatfs_inum_is_in_range(a_fatfs, a_file_entry_inum));
-    assert(a_file_dentry != NULL);
-    assert(a_stream_dentry != NULL);
-        
-    sector = FATFS_INODE_2_SECT(a_fatfs, a_file_entry_inum);
-    cluster = FATFS_SECT_2_CLUST(a_fatfs, sector);
-    alloc_check_ret_val = exfatfs_is_cluster_alloc(a_fatfs, cluster);
-    if (alloc_check_ret_val != -1) {
-        cluster_is_alloc = (uint8_t)alloc_check_ret_val;
-    }
-    else {
-        return FATFS_FAIL;
-    }
-
-    /* Check for the most common case first - the file stream entry is located
-     * immediately after the file entry. This should always be true for any 
-     * in-use file entry in an allocated cluster that is not the last entry in
-     * the cluster. It will also be true if the file entry is the last entry in 
-     * the cluster and the directory that contains the file is not fragmented - 
-     * the stream entry will simply be the first entry of the next cluster. 
-     * Finally, if the file entry is not in-use and was found in an unallocated 
-     * sector, the only viable place to look for the stream entry is in the 
-     * bytes following the file entry, since there is no FAT chain to 
-     * consult. */
-    stream_entry_inum = a_file_entry_inum + 1;
-    if (fatfs_inum_is_in_range(a_fatfs, stream_entry_inum)) {
-        if (exfatfs_load_file_stream_dentry(a_fatfs, 
-            stream_entry_inum, cluster_is_alloc, 
-            (EXFATFS_DIR_ENTRY_TYPE_ENUM)a_file_dentry->entry_type, // RJCTODO: Messy casts
-            (FATFS_DENTRY*)a_stream_dentry) == 0) {
-            /* Found it. */
-            return FATFS_OK;
-        }
-    }
-
-    /* If the stream entry was not found immediately following the file entry
-     * and the cluster is allocated, it is possible that the file entry was the
-     * last entry of a cluster in a fragmented directory. In this
-     * case, the FAT can be consulted to see if there is a next cluster. If 
-     * so, the stream entry may be the first entry of that cluster. */
-    if (cluster_is_alloc) {
-        /* Calculate the byte offset of the last possible directory entry in 
-         * the current cluster. */
-        cluster_base_sector = FATFS_CLUST_2_SECT(a_fatfs, cluster); 
-        last_entry_offset = (cluster_base_sector * a_fatfs->ssize) + 
-            (a_fatfs->csize * a_fatfs->ssize) - sizeof(FATFS_DENTRY);   
-
-        /* Get the byte offset of the file entry. Note that FATFS_INODE_2_OFF
-         * gices the offset relative to start of a sector. */
-        file_entry_offset = (sector * a_fatfs->ssize) + 
-            FATFS_INODE_2_OFF(a_fatfs, a_file_entry_inum);
-
-        if (file_entry_offset == last_entry_offset) {
-            /* The file entry is the last in its cluster. Look up the next
-             * cluster. */
-            if ((fatfs_getFAT(a_fatfs, cluster, &next_cluster) == 0) &&
-                (next_cluster != 0)) {
-                /* Found the next cluster in the FAT, so get its first sector
-                 * and the inode address of the first entry of the sector. */
-                cluster_base_sector = FATFS_CLUST_2_SECT(a_fatfs, next_cluster); 
-                stream_entry_inum = FATFS_SECT_2_INODE(a_fatfs, 
-                    cluster_base_sector);
-
-                if (fatfs_inum_is_in_range(a_fatfs, stream_entry_inum)) {
-                    if (exfatfs_load_file_stream_dentry(a_fatfs, 
-                        stream_entry_inum, cluster_is_alloc, 
-                        (EXFATFS_DIR_ENTRY_TYPE_ENUM)a_file_dentry->entry_type, // RJCTODO: Messy casts
-                        (FATFS_DENTRY*)a_stream_dentry) == 0) {
-                        /* Found it. */
-                        return FATFS_OK;
-                    }
-                }
-            }
-        }
-    }
-
-    /* Did not find the file stream entry. */
-    return FATFS_FAIL;
-}
-
-/**
- * \internal
- * Use a a file and a file stream directory entry corresponding to the exFAT 
- * equivalent of an inode to populate the TSK_FS_META object of a TSK_FS_FILE 
- * object.
- *
- * @param a_fatfs [in] Source file system for the directory entries.
- * @param [in] a_inum Address of the inode. 
- * @param [in] a_dentry A file directory entry.
- * @param [in] a_is_alloc Allocation status of the sector that contains the
- * file directory entry.
- * @param a_fs_file [in, out] Generic file with generic inode structure (TSK_FS_META).
- * @return TSK_RETVAL_ENUM.  
- */
-static TSK_RETVAL_ENUM 
-exfatfs_copy_file_inode(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
-    FATFS_DENTRY *a_file_dentry, uint8_t a_is_alloc, TSK_FS_FILE *a_fs_file)
-{
-    const char *func_name = "exfatfs_copy_file_inode";
-    TSK_FS_INFO *fs = NULL;
-    TSK_FS_META *fs_meta =  NULL;
-    EXFATFS_FILE_DIR_ENTRY *file_dentry = (EXFATFS_FILE_DIR_ENTRY*)a_file_dentry;
-    EXFATFS_FILE_STREAM_DIR_ENTRY stream_dentry;
-    uint32_t mode = 0;
-
-    assert(a_fatfs != NULL);
-    assert(a_file_dentry != NULL);
-    assert(a_fs_file != NULL);
-    assert(a_fs_file->meta != NULL);
-    assert(file_dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE ||
-           file_dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE);
-
-    fs = &(a_fatfs->fs_info);
-    fs_meta = a_fs_file->meta;
-
-    /* Determine whether the file is a regular file or directory. */
-    if (file_dentry->attrs[0] & FATFS_ATTR_DIRECTORY) {
-        fs_meta->type = TSK_FS_META_TYPE_DIR;
-    }
-    else {
-        fs_meta->type = TSK_FS_META_TYPE_REG;
-    }
-
-    /* Add mode flags corresponding to file attribute flags. */
-    mode = fs_meta->mode; 
-    if ((file_dentry->attrs[0] & FATFS_ATTR_READONLY) == 0) {
-        mode |=
-            (TSK_FS_META_MODE_IRUSR | TSK_FS_META_MODE_IRGRP |
-            TSK_FS_META_MODE_IROTH);
-    }
-    if ((file_dentry->attrs[0] & FATFS_ATTR_HIDDEN) == 0) {
-        mode |=
-            (TSK_FS_META_MODE_IWUSR | TSK_FS_META_MODE_IWGRP |
-            TSK_FS_META_MODE_IWOTH);
-    }
-    fs_meta->mode = (TSK_FS_META_MODE_ENUM)mode;
-
-    /* There is no notion of links in exFAT, just deleted or not deleted. 
-     * If the file is not deleted, treat this as having one link. */
-    fs_meta->nlink = (file_dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE) ? 0 : 1;
-
-    /* Copy the last modified time, converted to UNIX date format. */
-    if (FATFS_ISDATE(tsk_getu16(fs->endian, file_dentry->modified_date))) {
-        fs_meta->mtime =
-            fatfs_dos_2_unix_time(tsk_getu16(fs->endian, file_dentry->modified_date),
-                tsk_getu16(fs->endian, file_dentry->modified_time), 
-                file_dentry->modified_time_tenths_of_sec);
-        fs_meta->mtime_nano = fatfs_dos_2_nanosec(file_dentry->modified_time_tenths_of_sec);
-    }
-    else {
-        fs_meta->mtime = 0;
-        fs_meta->mtime_nano = 0;
-    }
-
-    /* Copy the last accessed time, converted to UNIX date format. */
-    if (FATFS_ISDATE(tsk_getu16(fs->endian, file_dentry->accessed_date))) {
-        fs_meta->atime =
-            fatfs_dos_2_unix_time(tsk_getu16(fs->endian, file_dentry->accessed_date), 
-                tsk_getu16(fs->endian, file_dentry->accessed_time), 0);
-    }
-    else {
-        fs_meta->atime = 0;
-    }
-    fs_meta->atime_nano = 0;
-
-    /* exFAT does not have a last changed time. */
-    fs_meta->ctime = 0;
-    fs_meta->ctime_nano = 0;
-
-    /* Copy the created time, converted to UNIX date format. */
-    if (FATFS_ISDATE(tsk_getu16(fs->endian, file_dentry->created_date))) {
-        fs_meta->crtime =
-            fatfs_dos_2_unix_time(tsk_getu16(fs->endian, file_dentry->created_date),
-                tsk_getu16(fs->endian, file_dentry->created_time), 
-                file_dentry->created_time_tenths_of_sec);
-        fs_meta->crtime_nano = fatfs_dos_2_nanosec(file_dentry->created_time_tenths_of_sec);
-    }
-    else {
-        fs_meta->crtime = 0;
-        fs_meta->crtime_nano = 0;
-    }
-
-    // RJCTODO: Do we want to do anything with the time zone offsets?
-
-    /* Attempt to load the file stream entry that goes with this file entry. 
-     * If not successful, at least the file entry meta data will be returned. */
-    if (exfatfs_new_find_file_stream_dentry(a_fatfs, a_inum, file_dentry, &stream_dentry)) { // RJCTODO: COuld pass allocation status through.
-        return TSK_OK;
-    }
-
-    /* Set the size of the file and the address of its first cluster. */
-    ((TSK_DADDR_T*)a_fs_file->meta->content_ptr)[0] = 
-        tsk_getu32(a_fatfs->fs_info.endian, stream_dentry.first_cluster_addr);
-    fs_meta->size = tsk_getu64(fs->endian, stream_dentry.data_length); // RJCTODO: How does this relate to the valid data length field? Is one or the other to be preferred?
-
-    // RJCTODO: What if a cluster boundary is crossed? Probably need to check the allocation status of all the clusters
-    // involved in a file directory entry set. This is another argument for moving the duplicated code to find stream entries out of inode lookup
-    // and walk to here.
-    /* Set the allocation status using both the allocation status of the 
-     * sector that contains the directory entries and the entry type 
-     * settings - essentially a "belt and suspenders" check. */
-    if ((a_is_alloc) &&
-        (file_dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE) &&
-        (stream_dentry.entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM)) {
-        a_fs_file->meta->flags = TSK_FS_META_FLAG_ALLOC;
-
-        /* If the FAT chain bit of the secondary flags of the stream entry is set,
-         * the file is not fragmented and there is no FAT chain to walk. If the 
-         * file is not deleted, do an eager load instead of a lazy load of its 
-         * data run. */
-        if ((stream_dentry.flags & EXFATFS_INVALID_FAT_CHAIN_MASK) &&
-            (exfatfs_make_contiguous_data_run(a_fs_file))) {
-            return TSK_ERR;
-        }
-    }
-    else {
-        a_fs_file->meta->flags = TSK_FS_META_FLAG_UNALLOC;
-    }
-
-    return TSK_OK;
-}
-
-/**
- * \internal
- * Use a file name directory entry corresponding to the exFAT equivalent of
- * an inode to populate the TSK_FS_META object of a TSK_FS_FILE object.
- *
- * @param [in] a_fatfs Source file system for the directory entry.
- * @param [in] a_inum Address of the inode.
- * @param [in] a_is_alloc Allocation status of the sector that contains the
- * inode.
- * @param [in] a_dentry A file name directory entry.
- * @param a_fs_file Generic file with generic inode structure (TSK_FS_META).
- * @return TSK_RETVAL_ENUM.  
- */
-static TSK_RETVAL_ENUM 
-exfatfs_copy_file_name_inode(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
-    FATFS_DENTRY *a_dentry, uint8_t a_is_alloc, TSK_FS_FILE *a_fs_file)
-{
-    const char *func_name = "exfatfs_copy_file_name_inode";
-    EXFATFS_FILE_NAME_DIR_ENTRY *dentry = NULL;
-
-    assert(a_fatfs != NULL);
-    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
-    assert(a_dentry != NULL);
-    assert(a_fs_file != NULL);
-    assert(a_fs_file->meta != NULL);
-
-    dentry = (EXFATFS_FILE_NAME_DIR_ENTRY*)a_dentry;
-    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME ||
-           dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME);
-
-    // RJCTODO: This leads to attempts to "recover" a file with a single cluster
-    // run for a deleted file name entry. See remarks at the beginning of this file.
-    /* Set the allocation status using both the allocation status of the 
-     * sector that contains the directory entries and the entry type 
-     * settings - essentially a "belt and suspenders" check. */
-    if ((a_is_alloc) &&
-        (dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME)) {
-        a_fs_file->meta->flags = TSK_FS_META_FLAG_ALLOC;
-    }
-    else {
-        a_fs_file->meta->flags = TSK_FS_META_FLAG_UNALLOC;
-    }
-
-    /* Copy the file name segment. */
-    // RJCTODO: Is there a problem here because we don't know how many of the chars are valid?
-    if (fatfs_utf16_inode_str_2_utf8(a_fatfs, (UTF16*)dentry->utf16_name_chars, EXFATFS_MAX_FILE_NAME_SEGMENT_LENGTH, 
-        (UTF8*)a_fs_file->meta->name2->name, sizeof(a_fs_file->meta->name2->name), a_inum, "file name segment") != TSKconversionOK) {
-        return TSK_COR;
-    }
-
-    return TSK_OK;
-}
-
-/**
- * \internal
- * Initialize the members of a TSK_FS_META object before copying the contents
- * of an an inode consisting of one or more raw exFAT directry entries into it. 
- *
- * @param [in] a_fatfs Source file system for the directory entries.
- * @param [in] a_inum Address of the inode.
- * @param [in] a_is_alloc Allocation status of the sector that contains the
- * inode.
- * @param [in, out] a_fs_file Generic file with generic inode structure to 
- * initialize.
- * @return 0 on success, 1 on failure, per TSK convention
- */
-static uint8_t
-exfatfs_inode_copy_init(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
-    uint8_t a_is_alloc, TSK_FS_FILE *a_fs_file)
-{
-    const char *func_name = "exfatfs_inode_copy_init";
-    TSK_FS_META *fs_meta = NULL;
-    int8_t ret_val = 0;
-
-    assert(a_fatfs != NULL);
-    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
-    assert(a_fs_file != NULL);
-    assert(a_fs_file->meta != NULL);
-
-    fs_meta = a_fs_file->meta;
-    fs_meta->addr = a_inum;
-
-    /* Set the allocation status based on the cluster allocation status. File 
-     * entry set entries may change this. */
-    a_fs_file->meta->flags = a_is_alloc ? TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC;
-
-    /* As for FATXX, make regular file the default type. */
-    fs_meta->type = TSK_FS_META_TYPE_REG;
-
-    /* As for FATXX, mark everything as executable. */
-    fs_meta->mode = (TSK_FS_META_MODE_ENUM)(TSK_FS_META_MODE_IXUSR | TSK_FS_META_MODE_IXGRP |
-        TSK_FS_META_MODE_IXOTH);
-
-    /* There is no notion of links in exFAT, just deleted or not deleted. 
-     * With not deleted being equivalent to having one link, set nlink to 1
-     * here so that it will be set for static things like the allocation 
-     * bitmap. The code for file inodes can reset or unset it appropriately. */
-    fs_meta->nlink = 1;
-
-    /* Initialize size to zero. The code for particular inode types will 
-     * fill in another value, if appropriate. */
-    fs_meta->size = 0;
-
-    /* Default values for time stamp metadata. The code for file inodes will 
-     * fill in actual time stamp data. */
-    fs_meta->mtime = 0;
-    fs_meta->mtime_nano = 0;
-    fs_meta->atime = 0;
-    fs_meta->atime_nano = 0;
-    fs_meta->ctime = 0;
-    fs_meta->ctime_nano = 0;
-    fs_meta->crtime = 0;
-    fs_meta->crtime_nano = 0;
-
-    /* Metadata that does not exist in exFAT. */
-    fs_meta->uid = 0;
-    fs_meta->gid = 0;
-    fs_meta->seq = 0;
-
-    /* Allocate space for a name. */
-    if (fs_meta->name2 == NULL) {
-        if ((fs_meta->name2 = (TSK_FS_META_NAME_LIST*)tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) {
-            return 1;
-        }
-        fs_meta->name2->next = NULL;
-    }
-    fs_meta->name2->name[0] = '\0';
-
-    /* Allocate space for saving the cluster address of the first cluster 
-     * of file inodes, including allocation bitmaps and upcase tables. */
-    if (fs_meta->content_len < FATFS_FILE_CONTENT_LEN) {
-        if ((fs_meta =
-                tsk_fs_meta_realloc(fs_meta,
-                    FATFS_FILE_CONTENT_LEN)) == NULL) {
-            return 1;
-        }
-    }
-
-    /* Mark the generic attribute list as not in use (in the generic file model
-     * attributes are containers for data or metadata). Population of this 
-     * stuff is done on demand (lazy look up). */
-    fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
-    if (fs_meta->attr) {
-        tsk_fs_attrlist_markunused(fs_meta->attr);
-    }
-
-    return 0;
-}
-
-/**
- * \internal
- * Use one or more directory entries corresponding to the exFAT equivalent of
- * an inode to populate the TSK_FS_META object of a TSK_FS_FILE object.
- *
- * @param [in] a_fatfs Source file system for the directory entries.
- * @param [in] a_inum Address of the inode.
- * @param [in] a_dentry A directory entry.
- * @param [in] a_is_alloc Allocation status of the inode.
- * @param [in, out] a_fs_file Generic file object with a generic inode 
- * metadata structure.
- * @return TSK_RETVAL_ENUM.  
- */
-TSK_RETVAL_ENUM
-exfatfs_dinode_copy(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
-    FATFS_DENTRY *a_dentry, uint8_t a_is_alloc, TSK_FS_FILE *a_fs_file)
-{
-    const char *func_name = "exfatfs_dinode_copy";
-
-    assert(a_fatfs != NULL);
-    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
-    assert(a_dentry != NULL);
-    assert(a_fs_file != NULL);
-    assert(a_fs_file->meta != NULL);
-    assert(a_fs_file->fs_info != NULL);
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
-        fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name) ||
-        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name) ||
-        fatfs_ptr_arg_is_null(a_fs_file->meta, "a_fs_file->meta", func_name) ||
-        fatfs_ptr_arg_is_null(a_fs_file->fs_info, "a_fs_file->fs_info", func_name) ||
-        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
-        return TSK_ERR;
-    }
-
-    if (exfatfs_inode_copy_init(a_fatfs, a_inum, a_is_alloc, a_fs_file)) {
-        return TSK_ERR;
-    }
-
-    switch (a_dentry->data[0])
-    {
-    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL:
-    case EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL:
-        return exfatfs_copy_vol_label_inode(a_fatfs, a_inum, a_dentry, a_fs_file);
-    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
-        strcpy(a_fs_file->meta->name2->name, EXFATFS_VOLUME_GUID_DENTRY_NAME);
-        return TSK_OK;
-    case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:
-        return exfatfs_copy_alloc_bitmap_inode(a_fatfs, a_dentry, a_fs_file);
-    case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:
-        return exfatfs_copy_upcase_table_inode(a_fatfs, a_dentry, a_fs_file);
-    case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:
-        strcpy(a_fs_file->meta->name2->name, EXFATFS_TEX_FAT_DENTRY_NAME);
-        return TSK_OK;
-    case EXFATFS_DIR_ENTRY_TYPE_ACT:
-        strcpy(a_fs_file->meta->name2->name, EXFATFS_ACT_DENTRY_NAME);
-        return TSK_OK;
-    case EXFATFS_DIR_ENTRY_TYPE_FILE:
-    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE:
-        return exfatfs_copy_file_inode(a_fatfs, a_inum, a_dentry, a_is_alloc, a_fs_file);
-    case EXFATFS_DIR_ENTRY_TYPE_FILE_NAME:
-    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME:
-        return exfatfs_copy_file_name_inode(a_fatfs, a_inum, a_dentry, a_is_alloc, a_fs_file);
-    case EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM:
-    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM:
-    default:
-        /* Stream entries are copied in tandem with the corresponding file entry. */
-        return TSK_ERR;
-    }
-
-    return TSK_OK;
-}
-
-/**
- * \internal
- * Given an exFAT file directory entry, try to find the corresponding file
- * stream directory entry.
- *
- * @param [in] a_fatfs Source file system for the directory entries.
- * @param [in] a_file_entry_inum The inode address associated with the file 
- * entry.
- * @param [in] a_sector The address of the sector where the file entry was 
- * found.
- * @param [in] a_sector_is_alloc The allocation status of the sector.
- * @param [in] a_file_dentry_type The file entry type, deleted or not.
- * @param [in, out] The stream entry, if found, will be loaded into the
- * this generic directory entry structure.
- * @return 0 on success, 1 on failure, per TSK convention
- */
-uint8_t
-exfatfs_find_file_stream_dentry(FATFS_INFO *a_fatfs, TSK_INUM_T a_file_entry_inum, 
-    TSK_DADDR_T a_sector, uint8_t a_sector_is_alloc,  
-    EXFATFS_DIR_ENTRY_TYPE_ENUM a_file_dentry_type,
-    FATFS_DENTRY *a_stream_dentry)
-{
-    const char *func_name = "exfatfs_find_file_stream_dentry";
-    TSK_INUM_T stream_entry_inum = 0;
-    TSK_DADDR_T cluster = 0;
-    TSK_DADDR_T cluster_base_sector = 0;
-    TSK_DADDR_T last_entry_offset = 0;
-    TSK_DADDR_T file_entry_offset = 0;
-    EXFATFS_DIR_ENTRY_TYPE_ENUM dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
-    TSK_DADDR_T next_cluster = 0;
-
-    assert(a_fatfs != NULL);
-    assert(fatfs_inum_is_in_range(a_fatfs, a_file_entry_inum));
-    assert(a_stream_dentry != NULL);
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
-        fatfs_ptr_arg_is_null(a_stream_dentry, "a_stream_dentry", func_name) ||
-        !fatfs_inum_arg_is_in_range(a_fatfs, a_file_entry_inum, func_name)) {
-        return FATFS_FAIL;
-    }
-        
-    /* Check for the most common case first - the file stream entry is located
-     * immediately after the file entry. This should always be true for any 
-     * in-use file entry in an allocated cluster that is not the last entry in
-     * the cluster. It will also be true if the file entry is the last entry in 
-     * the cluster and the directory that contains the file is not fragmented - 
-     * the stream entry will simply be the first entry of the next cluster. 
-     * Finally, if the file entry is not in-use and was found in an unallocated 
-     * sector, the only viable place to look for the stream entry is in the 
-     * bytes following the file entry, since there is no FAT chain to 
-     * consult. */
-    stream_entry_inum = a_file_entry_inum + 1;
-    if (fatfs_inum_is_in_range(a_fatfs, stream_entry_inum)) {
-        if (exfatfs_load_file_stream_dentry(a_fatfs, 
-            stream_entry_inum, a_sector_is_alloc, 
-            a_file_dentry_type, 
-            a_stream_dentry) == 0) {
-            /* Found it. */
-            return FATFS_OK;
-        }
-    }
-
-    /* If the stream entry was not found immediately following the file entry
-     * and the cluster is allocated, it is possible that the file entry was the
-     * last entry of a cluster in a fragmented directory. In this
-     * case, the FAT can be consulted to see if there is a next cluster. If 
-     * so, the stream entry may be the first entry of that cluster. */
-    if (a_sector_is_alloc) {
-        /* Calculate the byte offset of the last possible directory entry in 
-         * the current cluster. */
-        cluster = FATFS_SECT_2_CLUST(a_fatfs, a_sector);
-        cluster_base_sector = FATFS_CLUST_2_SECT(a_fatfs, cluster); 
-        last_entry_offset = (cluster_base_sector * a_fatfs->ssize) + 
-            (a_fatfs->csize * a_fatfs->ssize) - sizeof(FATFS_DENTRY);   
-
-        /* Get the byte offset of the file entry. Note that FATFS_INODE_2_OFF
-         * gices the offset relative to start of a sector. */
-        file_entry_offset = (a_sector * a_fatfs->ssize) + 
-            FATFS_INODE_2_OFF(a_fatfs, a_file_entry_inum);
-
-        if (file_entry_offset == last_entry_offset) {
-            /* The file entry is the last in its cluster. Look up the next
-             * cluster. */
-            if ((fatfs_getFAT(a_fatfs, cluster, &next_cluster) == 0) &&
-                (next_cluster != 0)) {
-                /* Found the next cluster in the FAT, so get its first sector
-                 * and the inode address of the first entry of the sector. */
-                cluster_base_sector = FATFS_CLUST_2_SECT(a_fatfs, next_cluster); 
-                stream_entry_inum = FATFS_SECT_2_INODE(a_fatfs, 
-                    cluster_base_sector);
-
-                if (fatfs_inum_is_in_range(a_fatfs, stream_entry_inum)) {
-                    if (exfatfs_load_file_stream_dentry(a_fatfs, 
-                        stream_entry_inum, a_sector_is_alloc, 
-                        a_file_dentry_type, 
-                        a_stream_dentry) == 0) {
-                        /* Found it. */
-                        return FATFS_OK;
-                    }
-                }
-            }
-        }
-    }
-
-    /* Did not find the file stream entry. */
-    return FATFS_FAIL;
-}
-
-/**
- * \internal
- * Read in the bytes from an exFAT file system that correspond to the exFAT 
- * equivalent of an inode and use them to populate the TSK_FS_META object of
- * a TSK_FS_FILE object.
- *
- * @param [in] a_fatfs Source file system for the directory entries.
- * @param [in, out] a_fs_file The TSK_FS_FILE object.
- * @param [in] a_inum Inode address.
- * @return 0 on success, 1 on failure, per TSK convention
- */
-uint8_t
-exfatfs_inode_lookup(FATFS_INFO *a_fatfs, TSK_FS_FILE *a_fs_file, 
-    TSK_INUM_T a_inum)
-{
-    const char *func_name = "exfatfs_inode_lookup";
-    TSK_DADDR_T sector = 0;
-    int8_t sect_is_alloc = 0;
-    FATFS_DENTRY dentry;
-    //FATFS_DENTRY stream_dentry;
-    //FATFS_DENTRY *secondary_dentry = NULL;
-    uint64_t inode_offset = 0;
-    TSK_DADDR_T cluster_base_sector = 0;
-    TSK_INUM_T next_inum = 0;
-    EXFATFS_DIR_ENTRY_TYPE_ENUM dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
-    TSK_RETVAL_ENUM copy_result = TSK_OK;
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
-        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name) ||
-        fatfs_ptr_arg_is_null(a_fs_file->meta, "a_fs_file->meta", func_name) ||
-        fatfs_ptr_arg_is_null(a_fs_file->fs_info, "a_fs_file->fs_info", func_name) ||
-        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
-        return 1;
-    }
-
-    /* Map the inode address to a sector. */ 
-    sector = FATFS_INODE_2_SECT(a_fatfs, a_inum);
-    if (sector > a_fatfs->fs_info.last_block) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
-        tsk_error_set_errstr("%s: Inode %" PRIuINUM
-            " in sector too big for image: %" PRIuDADDR, func_name, a_inum, sector);
-        return 1;
-    }
-
-    /* Check the allocation status of the sector. This status will be used
-     * not only as meta data to be reported, but also as a way to choose
-     * between the basic or in-depth version of the tests (below) that 
-     * determine whether or not the bytes corrresponding to the inode are 
-     * likely to be a directory entry. Note that in other places in the code 
-     * information about whether or not the sector that contains the inode is
-     * part of a folder is used to select the test. Here, that information is 
-     * not available, so the test here is less reliable and may result in some 
-     * false positives. */
-    sect_is_alloc = fatfs_is_sectalloc(a_fatfs, sector);
-    if (sect_is_alloc == -1) {
-        return 1;
-    }
-
-    /* Load the bytes at the specified inode address. */
-    memset((void*)&dentry, 0, sizeof(FATFS_DENTRY));
-    if (fatfs_dentry_load(a_fatfs, &dentry, a_inum)) {
-        return 1;
-    }
-
-    /* Try typing the bytes as a directory entry.*/
-    if (exfatfs_is_dentry(a_fatfs, &dentry, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)sect_is_alloc, sect_is_alloc)) {
-        dentry_type = (EXFATFS_DIR_ENTRY_TYPE_ENUM)dentry.data[0];
-    }
-    else {
-        return 1;
-    }
-
-    /* For the purposes of inode lookup, the file and file stream entries 
-     * that begin a file entry set are mapped to a single inode. Thus,  
-     * file stream entries are not treated as independent inodes. */
-    if (dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM ||
-        dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
-        tsk_error_set_errstr("%s: %" PRIuINUM " is not an inode", func_name, 
-            a_inum);
-        return 1;
-    }
-
-    /* Populate the TSK_FS_META object of the TSK_FS_FILE object. */
-    copy_result = exfatfs_dinode_copy(a_fatfs, a_inum, &dentry, sect_is_alloc, a_fs_file); 
-    if (copy_result == TSK_OK) {
-        return 0;
-    }
-    else if (copy_result == TSK_COR) {
-        /* There was a Unicode conversion error on a string, but the rest 
-         * of the inode meta data is probably o.k., so report the error (if in 
-         * verbose mode), but also report a successful look up.*/
-        if (tsk_verbose) {
-            tsk_error_print(stderr);
-        }
-        tsk_error_reset();
-        return 0;
-    }
-    else {
-        return 1;
-    }
-}
-
-/**
- * \internal
- * Outputs file attributes for an exFAT directory entry/inode in 
- * human-readable form.
- *
- * @param [in] a_fatfs Source file system for the directory entry.
- * @param [in] a_inum Inode address associated with the directory entry.
- * @param [in] a_hFile Handle of a file to which to write.
- * @return 0 on success, 1 on failure, per TSK convention
- */
-uint8_t
-exfatfs_istat_attr_flags(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum,  FILE *a_hFile)
-{
-    const char *func_name = "exfatfs_istat_attr_flags";
-    FATFS_DENTRY dentry;
-    EXFATFS_FILE_DIR_ENTRY *file_dentry = NULL;
-    uint16_t attr_flags = 0;
-
-    assert(a_fatfs != NULL);
-    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
-    assert(a_hFile != NULL);
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
-        fatfs_ptr_arg_is_null(a_hFile, "a_hFile", func_name) ||
-        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
-        return FATFS_FAIL; 
-    }
-
-    /* Load the bytes at the given inode address. */
-    if (fatfs_dentry_load(a_fatfs, (FATFS_DENTRY*)(&dentry), a_inum)) {
-        return FATFS_FAIL; 
-    }
-
-    /* Print the attributes. */
-    switch (dentry.data[0])
-    {
-    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL:
-    case EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL:
-        tsk_fprintf(a_hFile, "Volume Label\n");
-        break;
-    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
-        tsk_fprintf(a_hFile, "Volume GUID\n");
-        break;
-    case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:     
-        tsk_fprintf(a_hFile, "Allocation Bitmap\n");
-        break;
-    case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:     
-        tsk_fprintf(a_hFile, "Up-Case Table\n");
-        break;
-    case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:     
-        tsk_fprintf(a_hFile, "TexFAT\n");
-        break;
-    case EXFATFS_DIR_ENTRY_TYPE_ACT:
-        tsk_fprintf(a_hFile, "Access Control Table\n");
-        break;
-    case EXFATFS_DIR_ENTRY_TYPE_FILE:
-    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE:
-        file_dentry = (EXFATFS_FILE_DIR_ENTRY*)&dentry;
-        attr_flags = tsk_getu16(a_fatfs->fs_info.endian, file_dentry->attrs);
-
-        if (attr_flags & FATFS_ATTR_DIRECTORY) {
-            tsk_fprintf(a_hFile, "Directory");
-        }
-        else {
-            tsk_fprintf(a_hFile, "File");
-        }
-
-        if (attr_flags & FATFS_ATTR_READONLY) {
-            tsk_fprintf(a_hFile, ", Read Only");
-        }
-
-        if (attr_flags & FATFS_ATTR_HIDDEN) {
-            tsk_fprintf(a_hFile, ", Hidden");
-        }
-
-        if (attr_flags & FATFS_ATTR_SYSTEM) {
-            tsk_fprintf(a_hFile, ", System");
-        }
-
-        if (attr_flags & FATFS_ATTR_ARCHIVE) {
-            tsk_fprintf(a_hFile, ", Archive");
-        }
-
-        tsk_fprintf(a_hFile, "\n");
-
-        break;
-    case EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM:
-    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM:
-        tsk_fprintf(a_hFile, "File Stream\n"); 
-        break;
-    case EXFATFS_DIR_ENTRY_TYPE_FILE_NAME:
-    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME:
-        tsk_fprintf(a_hFile, "File Name\n");
-        break;
-    default:
-        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
-        tsk_error_set_errstr("%s: Inode %" PRIuINUM
-            " is not an exFAT directory entry", func_name, a_inum);
-        return FATFS_FAIL;
-    }
-
-    return FATFS_OK;
-}
-
-/**
- * \internal
- * Determine whether an exFAT directory entry should be included in an inode
- *  walk.
- *
- * @param [in] a_fatfs Source file system for the directory entry.
- * @param [in] a_inum Inode address associated with the directory entry.
- * @param [in] a_dentry A directory entry buffer.
- * @param [in] a_selection_flags The inode selection falgs for the inode walk.
- * @param [in] a_cluster_is_alloc The allocation status of the cluster that
- * contains the directory entry.
- * @return 1 if the entry should be skipped, 0 otherwise
- */
-uint8_t
-exfatfs_inode_walk_should_skip_dentry(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
-    FATFS_DENTRY *a_dentry, unsigned int a_selection_flags, 
-    int a_cluster_is_alloc)
-{
-    const char *func_name = "exfatfs_inode_walk_should_skip_dentry";
-    unsigned int dentry_flags = 0;
-    uint8_t i = 0;
-
-    assert(a_fatfs != NULL);
-    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
-    assert(a_dentry != NULL);
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
-        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name) ||
-        fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
-        return 1; 
-    }
-
-    /* Skip file stream and file name entries. For inode walks, these entries
-     * are handled with the file entry with which they are associated in a file
-     * entry set. */
-    if (a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM ||
-        a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM ||
-        a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME ||
-        a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME) {
-        return 1;
-    }
-
-    // RJCTODO: This code probably needs to change. See explanatory TODO comment
-    // associated with the EXFATFS_DIR_ENTRY_TYPE_ENUM definition.
-    /* Assign an allocation status to the entry. Allocation status is 
-     * determined first by the allocation status of the cluster that contains
-     * the entry, then by the allocated status of the entry. */
-    if ((a_cluster_is_alloc) && (a_dentry->data[0] != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE)) {
-        dentry_flags = TSK_FS_META_FLAG_ALLOC;
-    }
-    else {
-        dentry_flags = TSK_FS_META_FLAG_UNALLOC;
-    }
-
-    /* Does the allocation status of the entry match that of the inode 
-     * selection flags? */
-    if ((a_selection_flags & dentry_flags) != dentry_flags) {
-        return 1;
-    }
-
-    /* If the inode selection flags call for only processing orphan files, 
-     * check whether or not this inode is in list of non-orphan files found via
-     * name walk. */
-    if ((dentry_flags & TSK_FS_META_FLAG_UNALLOC) &&
-        (a_selection_flags & TSK_FS_META_FLAG_ORPHAN) &&
-        (tsk_fs_dir_find_inum_named(&(a_fatfs->fs_info), a_inum))) {
-        return 1;
-    }
-
-    return 0;
+/*
+** The Sleuth Kit
+**
+** Copyright (c) 2013 Basis Technology Corp.  All rights reserved
+** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
+**
+** This software is distributed under the Common Public License 1.0
+**
+*/
+
+/*
+ * This code makes use of research presented in the following paper:
+ * "Reverse Engineering the exFAT File System" by Robert Shullich
+ * Retrieved May 2013 from: 
+ * http://www.sans.org/reading_room/whitepapers/forensics/reverse-engineering-microsoft-exfat-file-system_33274
+ *
+ * Some additional details concerning TexFAT were obtained in May 2013
+ * from:
+ * http://msdn.microsoft.com/en-us/library/ee490643(v=winembedded.60).aspx
+ */
+
+/**
+ * \file exfatfs_meta.c
+ * Contains the internal TSK exFAT file system code to access the data in the 
+ * metadata data category as defined in the book "File System Forensic 
+ * Analysis" by Brian Carrier (pp. 174-175). 
+ */
+
+#include "tsk_exfatfs.h" /* Included first to make sure it stands alone. */
+#include "tsk_fs_i.h"
+#include "tsk_fatfs.h"
+#include <assert.h>
+
+// RJCTODO: This code does not follow the lazy load paradigm for file attributes, i.e., data runs. The reason for this is that unlike FATXX,
+// exFAT only uses the FAT for fragmented files. The valid FAT chain flag in file stream entries is the indicator for regular files. For
+// other "special" files (e.g., allocation bitmap, upcase table) there will always be contiguous data runs. In all cases, the data for deciding
+// whether a run should be contiguous is in the directory entry. We could do away with the eager load of the data runs for exFAT if we allocated another 
+// byte of file-system-specific content in the TSK_FS_META structure to indicate whether or not there was a valid FAT chain for the file. 
+// This would also allow us to have a somewhat smarter recovery algorithm for deleted exFAT files without FAT chains - we would know the maximum 
+// number of clusters from which we could hope to recover any authentic file content more precisely than we do by using file size alone. This would also
+// support a uniform inode_copy function signature for FATXX and exFAY - it would no longer be necessary to pass the entire file object through, just the
+// meta object. On the other hand, the fatxxfs_copy_dinode function could be changed to take a TSK_FS_FILE instead of a TSK_FS_META object parameter.
+
+// RJCTODO: Adding yet another byte to the file system specific content (see above for the first proposed new byte) would allow us to save the directory 
+// entry type as well. An immediate benefit of this would be the way we would be able to skip over the recovery code for the file name entries of deleted files - 
+// currently we don't add a data run to the file name entries of allocated files, yet we tack a "one cluster run" on to each file name entry for a deleted file 
+// (see lines 2015-2034 of fatfs_meta.c). This is a bug as far as I'm concerned.
+
+// RJCTODO: Enum variables display nicely in IntelliSense. Use them instead of the integral type flags I put in to deal with compiler warnings
+// and use casts instead.
+
+// RJCTODO: Consider standardizing on the use of tsk_error_reset() throughout the FAT code.
+
+// RJCTODO: Make sure all exFAT code is Doxygen commented. It would be better to also include shared FAT code in this. Even better would be
+// to make sure the FATXX code is documented as well.
+
+// RJCTODO: It would be good to obtain real world exFAT images and a Windows CE exFAT image for further testing.
+
+/**
+ * \internal
+ * Checks whether a specified cluster is allocated according to the allocation 
+ * bitmap of an exFAT file system. 
+ *
+ * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system.
+ * @param [in] a_cluster_addr The cluster address of the cluster to check. 
+ * @return 1 if the cluster is allocated, 0 if the cluster is not allocated, 
+ * or -1 if an error occurs.
+ */
+int8_t 
+exfatfs_is_cluster_alloc(FATFS_INFO *a_fatfs, TSK_DADDR_T a_cluster_addr)
+{
+    const char *func_name = "exfatfs_is_clust_alloc";
+    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
+    TSK_DADDR_T bitmap_byte_offset = 0;
+    uint8_t bitmap_byte;
+    ssize_t bytes_read = 0;
+
+    assert(a_fatfs != NULL);
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) {
+        return -1;
+    }
+
+    assert((a_cluster_addr >= FATFS_FIRST_CLUSTER_ADDR) && (a_cluster_addr <= a_fatfs->lastclust));
+    if ((a_cluster_addr < FATFS_FIRST_CLUSTER_ADDR) || (a_cluster_addr > a_fatfs->lastclust)) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("%s: cluster address %" PRIuINUM " out of range", func_name, a_cluster_addr);
+        return -1;
+    }
+
+     /* Normalize the cluster address. */
+    a_cluster_addr = a_cluster_addr - FATFS_FIRST_CLUSTER_ADDR;
+
+    /* Determine the offset of the byte in the allocation bitmap that contains
+     * the bit for the specified cluster. */
+    bitmap_byte_offset = (a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap * a_fatfs->ssize) + (a_cluster_addr / 8);
+
+    /* Read the byte. */
+    bytes_read = tsk_fs_read(fs, bitmap_byte_offset, (char*)&bitmap_byte, 1);
+    if (bytes_read != 1) {
+        if (bytes_read >= 0) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_READ);
+        }
+        tsk_error_set_errstr2("%s: failed to read bitmap byte at offset %" PRIuINUM "", func_name, bitmap_byte_offset); 
+        return -1;
+    }
+
+    /* Check the bit that corresponds to the specified cluster. Note that this
+     * computation does not yield 0 or 1. */
+    if (bitmap_byte & (1 << (a_cluster_addr % 8))) {
+        return 1;
+    }
+    else {
+        return 0;
+    }
+}
+
+/**
+ * \internal
+ * Determine whether the contents of a buffer may be an exFAT volume label
+ * directory entry.
+ *
+ * @param [in] a_dentry A directory entry buffer.
+ * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
+ * cluster from which the buffer was filled. 
+ * @returns 1 if the directory entry buffer likely contains a volume label 
+ * directory entry, 0 otherwise. 
+ */
+uint8_t
+exfatfs_is_vol_label_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_cluster_is_alloc)
+{
+    const char *func_name = "exfatfs_is_vol_label_dentry";
+    EXFATFS_VOL_LABEL_DIR_ENTRY *dentry = (EXFATFS_VOL_LABEL_DIR_ENTRY*)a_dentry;
+    uint8_t i = 0;
+    
+    assert(a_dentry != NULL);
+    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
+        return 0;
+    }
+
+    /* Check the entry type byte. */
+    if ((dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL) && 
+        (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL)) {
+        return 0;
+    }
+
+    /* There should be a single volume label directory entry at the
+     * beginning of the root directory, so check the allocation status, if 
+     * known, of the cluster from which the buffer was filled. */
+    if (a_cluster_is_alloc == FATFS_DATA_UNIT_ALLOC_STATUS_UNALLOC) {
+        return 0;
+    }
+
+    if (dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL) {
+        /* There is supposed to be a label, check its length. */
+        if ((dentry->utf16_char_count < 1) || (dentry->utf16_char_count > EXFATFS_MAX_VOLUME_LABEL_LEN)) {
+            if (tsk_verbose) {
+                fprintf(stderr, "%s: incorrect volume label length\n", func_name);
+            }
+            return 0;
+        }
+    }
+    else {
+        // RJCTODO: I think I verified that these tests are valid, but check again to be sure.
+        /* There is supposed to be no label, check for a zero in the length
+         * field. */
+        if (dentry->utf16_char_count != 0x00) {
+            if (tsk_verbose) {
+                fprintf(stderr, "%s: volume label length non-zero for no label entry\n", func_name);
+            }
+            return 0;
+        }
+
+        /* Every byte of the UTF-16 volume label string should be 0. */
+        for (i = 0; i < EXFATFS_MAX_VOLUME_LABEL_LEN * 2; ++i) {
+            if (dentry->volume_label[i] != 0x00) {
+                if (tsk_verbose) {
+                    fprintf(stderr, "%s: non-zero byte in label for no label entry\n", func_name);
+                }
+                return 0;
+            }
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * \internal
+ * Determine whether the contents of a buffer may be an exFAT volume GUID
+ * directory entry.
+ *
+ * @param [in] a_dentry A directory entry buffer.
+ * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
+ * cluster from which the buffer was filled. 
+ * @returns 1 if the directory entry buffer likely contains a volume GUID 
+ * directory entry, 0 otherwise. 
+ */
+uint8_t
+exfatfs_is_vol_guid_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status)
+{
+    const char *func_name = "exfatfs_is_vol_guid_dentry";
+    EXFATFS_VOL_GUID_DIR_ENTRY *dentry = (EXFATFS_VOL_GUID_DIR_ENTRY*)a_dentry;
+    
+    assert(a_dentry != NULL);
+    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
+        return 0;
+    }
+
+    /* Check the entry type byte. */
+    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID) {
+        return 0;
+    }
+
+    /* There is not enough data in a volume GUID directory entry to test
+     * anything but the entry type byte. However, a volume GUID directory 
+     * entry should be in allocated space, so check the allocation status, if
+     * known, of the cluster from which the buffer was filled to reduce false
+     * positives. */
+    return ((a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_ALLOC) ||
+            (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN));
+}
+
+/**
+ * \internal
+ * Determine whether the contents of a buffer may be an exFAT allocation bitmap
+ * directory entry. The test will be more reliable if an optional FATFS_INFO 
+ * struct representing the file system is provided.
+ *
+ * @param [in] a_dentry A directory entry buffer.
+ * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
+ * cluster from which the buffer was filled. 
+ * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system,
+ * may be NULL.
+ * @returns 1 if the directory entry buffer likely contains an allocation 
+ * bitmap directory entry, 0 otherwise. 
+ */
+uint8_t
+exfatfs_is_alloc_bitmap_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status, FATFS_INFO *a_fatfs)
+{
+    const char *func_name = "exfatfs_is_alloc_bitmap_dentry";
+    EXFATFS_ALLOC_BITMAP_DIR_ENTRY *dentry = (EXFATFS_ALLOC_BITMAP_DIR_ENTRY*)a_dentry;
+    uint32_t first_cluster_of_bitmap = 0;
+    uint64_t length_of_alloc_bitmap_in_bytes = 0;
+
+    assert(a_dentry != NULL);
+    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
+        return 0;
+    }
+
+    /* Check the entry type byte. */
+    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP) {
+        return 0;
+    }
+
+    /* There should be a single allocation bitmap directory entry near the the
+     * beginning of the root directory, so check the allocation status, if 
+     * known, of the cluster from which the buffer was filled. */
+    if (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNALLOC) {
+        return 0;
+    }
+
+    if (a_fatfs != NULL) {
+        /* The length of the allocation bitmap should be consistent with the 
+         * number of clusters in the data area as specified in the volume boot
+         * record. */
+        length_of_alloc_bitmap_in_bytes = tsk_getu64(a_fatfs->fs_info.endian, dentry->length_of_alloc_bitmap_in_bytes);
+        if (length_of_alloc_bitmap_in_bytes != (a_fatfs->clustcnt + 7) / 8) {
+            if (tsk_verbose) {
+                fprintf(stderr, "%s: bitmap length incorrect\n", func_name);
+            }
+            return 0;
+        }
+
+        /* The first cluster of the bit map should be within the data area.
+         * It is usually in the first cluster. */
+        first_cluster_of_bitmap = tsk_getu32(a_fatfs->fs_info.endian, dentry->first_cluster_of_bitmap);
+        if ((first_cluster_of_bitmap < EXFATFS_FIRST_CLUSTER) ||
+            (first_cluster_of_bitmap > a_fatfs->lastclust)) {
+            if (tsk_verbose) {
+                fprintf(stderr, "%s: first cluster not in cluster heap\n", func_name);
+            }
+            return 0;
+        }
+        
+        /* The first cluster of the allocation bitmap should be allocated (the 
+         * other conditions allow this function to be safely used to look for
+         * the allocation bitmap during FATFS_INFO initialization, before a 
+         * cluster allocation is possible). */
+        if ((a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap > 0) &&
+            (a_fatfs->EXFATFS_INFO.length_of_alloc_bitmap_in_bytes > 0) &&
+            (exfatfs_is_cluster_alloc(a_fatfs, (TSK_DADDR_T)first_cluster_of_bitmap) != 1)) {
+            if (tsk_verbose) {
+                fprintf(stderr, 
+                    "%s: first cluster of allocation bitmap not allocated\n", func_name);
+            }
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * \internal
+ * Determine whether the contents of a buffer may be an exFAT upcase table
+ * directory entry. The test will be more reliable if an optional FATFS_INFO 
+ * struct representing the file system is provided.
+ *
+ * @param [in] a_dentry A directory entry buffer.
+ * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
+ * cluster from which the buffer was filled. 
+ * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system,
+ * may be NULL.
+ * @returns 1 if the directory entry buffer likely contains an upcase table 
+ * directory entry, 0 otherwise. 
+ */
+uint8_t
+exfatfs_is_upcase_table_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status, FATFS_INFO *a_fatfs)
+{
+    const char *func_name = "exfatfs_is_upcase_table_dentry";
+    EXFATFS_UPCASE_TABLE_DIR_ENTRY *dentry = (EXFATFS_UPCASE_TABLE_DIR_ENTRY*)a_dentry;
+    uint64_t table_size = 0;
+    uint32_t first_cluster_of_table = 0;
+
+    assert(a_dentry != NULL);
+    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
+        return 0;
+    }
+
+    /* Check the entry type byte. */
+    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE) {
+        return 0;
+    }
+
+    /* There should be a single upcase table directory entry near the the
+     * beginning of the root directory, so check the allocation status, if 
+     * known, of the cluster from which the buffer was filled. */
+    if (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNALLOC) {
+        return 0;
+    }
+
+    if (a_fatfs != NULL) {
+        /* Check the size of the table. */
+        table_size = tsk_getu64(a_fatfs->fs_info.endian, dentry->table_length_in_bytes);
+        if (table_size == 0) {
+            if (tsk_verbose) {
+                fprintf(stderr, "%s: table size is zero\n", func_name);
+            }
+            return 0;
+        }
+
+        /* Is the table size less than the size of the cluster heap 
+         * (data area)? The cluster heap size is computed by multiplying the
+         * cluster size by the number of sectors in a cluster and then 
+         * multiplying by the number of bytes in a sector (the last operation 
+         * is optimized as a left shift by the base 2 log of sector size). */
+        if (table_size > (a_fatfs->clustcnt * a_fatfs->csize) << a_fatfs->ssize_sh) {
+            if (tsk_verbose) {
+                fprintf(stderr, "%s: table size too big\n", func_name);
+            }
+            return 0;
+        }
+
+        /* Is the address of the first cluster in range? */
+        first_cluster_of_table = tsk_getu32(a_fatfs->fs_info.endian, dentry->first_cluster_of_table);
+        if ((first_cluster_of_table < EXFATFS_FIRST_CLUSTER) ||
+            (first_cluster_of_table > a_fatfs->lastclust)) {
+            if (tsk_verbose) {
+                fprintf(stderr, 
+                    "%s: first cluster not in cluster heap\n", func_name);
+            }
+            return 0;
+        }
+
+        /* The first cluster of the table should be allocated. */
+        if (exfatfs_is_cluster_alloc(a_fatfs, (TSK_DADDR_T)first_cluster_of_table) != 1) {
+            if (tsk_verbose) {
+                fprintf(stderr, 
+                    "%s: first cluster of table not allocated\n", func_name);
+            }
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * \internal
+ * Determine whether the contents of a buffer may be an exFAT TexFAT directory
+ * entry.
+ *
+ * @param [in] a_dentry A directory entry buffer.
+ * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
+ * cluster from which the buffer was filled. 
+ * @returns 1 if the directory entry buffer likely contains a TexFAT directory
+ * entry, 0 otherwise. 
+ */
+uint8_t
+exfatfs_is_texfat_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status)
+{
+    const char *func_name = "exfatfs_is_texfat_dentry";
+    EXFATFS_TEXFAT_DIR_ENTRY *dentry = (EXFATFS_TEXFAT_DIR_ENTRY*)a_dentry;
+    
+    assert(a_dentry != NULL);
+    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
+        return 0;
+    }
+
+    /* Check the entry type byte. */
+    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_TEXFAT) {
+        return 0;
+    }
+
+    /* There is not enough data in a TexFAT directory entry to test anything
+     * but the entry type byte. However, a TexFAT directory entry should be in 
+     * allocated space, so check the allocation status, if known, of the 
+     * cluster from which the buffer was filled to reduce false positives. */
+    return ((a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_ALLOC) ||
+            (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN));
+}
+
+/**
+ * \internal
+ * Determine whether the contents of a buffer may be an exFAT access control 
+ * table directory entry.
+ *
+ * @param [in] a_dentry A directory entry buffer.
+ * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
+ * cluster from which the buffer was filled. 
+ * @returns 1 if the directory entry buffer likely contains an access control
+ * table entry, 0 otherwise. 
+ */
+uint8_t
+exfatfs_is_access_ctrl_table_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status)
+{
+    const char *func_name = "exfatfs_is_texfat_dentry";
+    EXFATFS_TEXFAT_DIR_ENTRY *dentry = (EXFATFS_TEXFAT_DIR_ENTRY*)a_dentry;
+    
+    assert(a_dentry != NULL);
+    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
+        return 0;
+    }
+
+    /* Check the entry type byte. */
+    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_TEXFAT) {
+        return 0;
+    }
+
+    /* There is not enough data in an access control table directory entry to 
+     * test anything but the entry type byte. However, an access control table
+     * directory entry should be in allocated space, so check the allocation 
+     * status, if known, of the cluster from which the buffer was filled to 
+     * reduce false positives. */
+    return ((a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_ALLOC) ||
+            (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN));
+}
+
+
+/**
+ * \internal
+ * Determine whether the contents of a buffer may be an exFAT file directory 
+ * entry. The test will be more reliable if an optional FATFS_INFO struct 
+ * representing the file system is provided.
+ *
+ * @param [in] a_dentry A directory entry buffer.
+ * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system,
+ * may be NULL.
+ * @returns 1 if the directory entry buffer likely contains a file directory 
+ * entry, 0 otherwise. 
+ */
+uint8_t
+exfatfs_is_file_dentry(FATFS_DENTRY *a_dentry, FATFS_INFO *a_fatfs)
+{
+    const char *func_name = "exfatfs_is_file_dentry";
+    TSK_FS_INFO *fs = NULL;
+    EXFATFS_FILE_DIR_ENTRY *dentry = (EXFATFS_FILE_DIR_ENTRY*)a_dentry;
+
+    assert(a_dentry != NULL);
+    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
+        return 0;
+    }
+
+    /* Check the entry type byte. */
+    if ((dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_FILE) && 
+        (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE)) {
+        return 0;
+    }
+
+    /* A file directory entry is the first entry of a file directory entry set
+     * consisting of a file directory entry followed by a file stream directory
+     * entry and from 1 to 17 file name directory entries. The file stream and
+     * file name entries are called secondary entries. */
+    if (dentry->secondary_entries_count < EXFATFS_MIN_FILE_SECONDARY_DENTRIES_COUNT ||
+        dentry->secondary_entries_count > EXFATFS_MAX_FILE_SECONDARY_DENTRIES_COUNT) {
+        if (tsk_verbose) {
+            fprintf(stderr, "%s: secondary entries count out of range\n", 
+                func_name);
+        }
+        return 0;
+    }
+
+    if (a_fatfs != NULL) {
+        fs = &(a_fatfs->fs_info);   
+
+        /* Make sure the time stamps aren't all zeros. */
+        if ((tsk_getu16(fs->endian, dentry->modified_date) == 0) &&
+            (tsk_getu16(fs->endian, dentry->modified_time) == 0) &&
+            (dentry->modified_time_tenths_of_sec == 0) && 
+            (tsk_getu16(fs->endian, dentry->created_date) == 0) &&
+            (tsk_getu16(fs->endian, dentry->created_time) == 0) &&
+            (dentry->created_time_tenths_of_sec == 0) && 
+            (tsk_getu16(fs->endian, dentry->accessed_date) == 0) &&
+            (tsk_getu16(fs->endian, dentry->accessed_time) == 0)) {
+            if (tsk_verbose) {
+                fprintf(stderr, "%s: time stamps all zero\n", 
+                    func_name);
+            }
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * \internal
+ * Determine whether the contents of a buffer may be an exFAT file stream 
+ * directory entry. The test will be more reliable if an optional FATFS_INFO 
+ * struct representing the file system is provided.
+ *
+ * @param [in] a_dentry A directory entry buffer.
+ * @param [in] a_alloc_status The allocation status, possibly unknown, of the 
+ * cluster from which the buffer was filled. 
+ * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system,
+ * may be NULL.
+ * @returns 1 if the directory entry buffer likely contains a file stream 
+ * directory entry, 0 otherwise. 
+ */
+uint8_t
+exfatfs_is_file_stream_dentry(FATFS_DENTRY *a_dentry, FATFS_INFO *a_fatfs)
+{
+    const char *func_name = "exfatfs_is_file_stream_dentry";
+    TSK_FS_INFO *fs = NULL;
+    EXFATFS_FILE_STREAM_DIR_ENTRY *dentry = (EXFATFS_FILE_STREAM_DIR_ENTRY*)a_dentry;
+    uint64_t file_size = 0;
+    uint32_t first_cluster = 0;
+
+    assert(a_dentry != NULL);
+    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
+        return 0;
+    }
+
+    /* Check the entry type byte. */
+    if ((dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM) && 
+        (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM)) {
+        return 0;
+    }
+
+    if (a_fatfs != NULL) {
+        fs = &(a_fatfs->fs_info);   
+
+        /* Check the size. */
+        file_size = tsk_getu64(fs->endian, dentry->data_length); // RJCTODO: How does this relate to the valid data length field? Is one or the other to be preferred?
+        if (file_size > 0) {
+            /* Is the file size less than the size of the cluster heap 
+             * (data area)? The cluster heap size is computed by multiplying the
+             * cluster size by the number of sectors in a cluster and then 
+             * multiplying by the number of bytes in a sector (the last operation 
+             * is optimized as a left shift by the base 2 log of sector size). */
+            if (file_size > (a_fatfs->clustcnt * a_fatfs->csize) << a_fatfs->ssize_sh) {
+                if (tsk_verbose) {
+                    fprintf(stderr, "%s: file size too big\n", func_name);
+                }
+                return 0;
+            }
+
+            /* Is the address of the first cluster in range? */
+            first_cluster = tsk_getu32(fs->endian, dentry->first_cluster_addr);
+            if ((first_cluster < EXFATFS_FIRST_CLUSTER) ||
+                (first_cluster > a_fatfs->lastclust)) {
+                if (tsk_verbose) {
+                    fprintf(stderr, 
+                        "%s: first cluster not in cluster heap\n", func_name);
+                }
+                return 0;
+            }
+
+            /* If the file is not marked as unallocated and has non-zero size, is its
+             * first cluster allocated? */
+            if ((dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM) && 
+                (exfatfs_is_cluster_alloc(a_fatfs, (TSK_DADDR_T)first_cluster) != 1)) {
+                if (tsk_verbose) {
+                    fprintf(stderr, 
+                        "%s: file not deleted, first cluster not allocated\n", func_name);
+                }
+                return 0;
+            }
+        }
+    }
+
+    // RJCTODO: Are there any more checks that could be done?
+
+    return 1;
+}
+
+/**
+ * \internal
+ * Determine whether the contents of a buffer may be an exFAT file name 
+ * directory entry.
+ *
+ * @param [in] a_dentry A directory entry buffer.
+ * @returns 1 if the directory entry buffer likely contains an file name
+ * directory entry, 0 otherwise. 
+ */
+uint8_t
+exfatfs_is_file_name_dentry(FATFS_DENTRY *a_dentry)
+{
+    const char *func_name = "exfatfs_is_file_name_dentry";
+    EXFATFS_FILE_NAME_DIR_ENTRY *dentry = (EXFATFS_FILE_NAME_DIR_ENTRY*)a_dentry;
+    
+    assert(a_dentry != NULL);
+    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
+        return 0;
+    }
+
+    /* There is not enough data in a file name directory entry
+     * to test anything but the entry type byte. */
+    return ((dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME) || 
+            (dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME));
+}
+
+
+/**
+ * \internal
+ * Determine whether a buffer likely contains a directory entry.
+ * For the most reliable results, request the in-depth test.
+ *
+ * @param [in] a_fatfs Source file system for the directory entry.
+ * @param [in] a_dentry Buffer that may contain a directory entry.
+ * @param [in] a_cluster_is_alloc The allocation status, possibly unknown, of the 
+ * cluster from which the buffer was filled. 
+ * @param [in] a_do_basic_tests_only Whether to do basic or in-depth testing. 
+ * @return 1 if the buffer likely contains a direcotry entry, 0 otherwise
+ */
+uint8_t
+exfatfs_is_dentry(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_cluster_is_alloc, uint8_t a_do_basic_tests_only)
+{
+    const char *func_name = "exfatfs_is_dentry";
+
+    assert(a_dentry != NULL);
+    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
+        return 0;
+    }
+
+    switch (a_dentry->data[0])
+    {
+    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL:
+    case EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL:
+        return exfatfs_is_vol_label_dentry(a_dentry, a_cluster_is_alloc);
+    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
+        return exfatfs_is_vol_guid_dentry(a_dentry, a_cluster_is_alloc);
+    case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:
+        return exfatfs_is_alloc_bitmap_dentry(a_dentry, a_cluster_is_alloc, a_fatfs);
+    case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:
+        return exfatfs_is_upcase_table_dentry(a_dentry, a_cluster_is_alloc, a_fatfs);
+    case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:
+        return exfatfs_is_texfat_dentry(a_dentry, a_cluster_is_alloc);
+    case EXFATFS_DIR_ENTRY_TYPE_ACT:
+        return exfatfs_is_access_ctrl_table_dentry(a_dentry, a_cluster_is_alloc);
+    case EXFATFS_DIR_ENTRY_TYPE_FILE:
+    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE:
+        return exfatfs_is_file_dentry(a_dentry, a_fatfs);
+    case EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM:
+    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM:
+        return exfatfs_is_file_stream_dentry(a_dentry, a_fatfs);
+    case EXFATFS_DIR_ENTRY_TYPE_FILE_NAME:
+    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME:
+        return exfatfs_is_file_name_dentry(a_dentry);
+    default:
+        return 0;
+    }
+}
+
+/**
+ * \internal
+ * Construct a single, non-resident data run for the TSK_FS_META object of a 
+ * TSK_FS_FILE object.  
+ *
+ * @param [in, out] a_fs_file Generic file with generic inode structure (TSK_FS_META).
+ * @return 0 on success, 1 on failure, per TSK convention
+ */
+static uint8_t
+exfatfs_make_contiguous_data_run(TSK_FS_FILE *a_fs_file)
+{
+    const char *func_name = "exfatfs_make_contiguous_data_run";
+    TSK_FS_META *fs_meta = NULL;
+    TSK_FS_INFO *fs = NULL;
+    FATFS_INFO *fatfs = NULL;
+    TSK_DADDR_T first_cluster = 0;
+    TSK_FS_ATTR_RUN *data_run;
+    TSK_FS_ATTR *fs_attr = NULL;
+    TSK_OFF_T alloc_size = 0;
+
+    assert(a_fs_file != NULL);
+    assert(a_fs_file->meta != NULL);
+    assert(a_fs_file->fs_info != NULL);
+
+    fs_meta = a_fs_file->meta;
+    fs = (TSK_FS_INFO*)a_fs_file->fs_info;
+    fatfs = (FATFS_INFO*)fs;
+
+    if (tsk_verbose) {
+        tsk_fprintf(stderr,
+            "%s: Loading attrs for inode: %" PRIuINUM
+            "\n", func_name, a_fs_file->meta->addr);
+    }
+
+    /* Get the stashed first cluster address of the file. If the address does
+     * not make sense, set the attribute state to TSK_FS_META_ATTR_ERROR so
+     * that there is no subsequent attempt to load a data run for this 
+     * file object. */
+    first_cluster = ((TSK_DADDR_T*)fs_meta->content_ptr)[0];
+    if ((first_cluster > (fatfs->lastclust)) &&
+        (FATFS_ISEOF(first_cluster, fatfs->mask) == 0)) {
+        fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+        tsk_error_reset();
+        if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC) {
+            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
+        }
+        else {
+            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        }
+        tsk_error_set_errstr
+            ("%s: Starting cluster address too large: %"
+            PRIuDADDR, func_name, first_cluster);
+        return 1;
+    }
+
+    /* Figure out the allocated size of the file. The minimum allocation unit
+     * for exFAT is a cluster, so the the roundup() function is used to round 
+     * up the file size in bytes to a multiple of cluser size in bytes. */
+    alloc_size = roundup(fs_meta->size, (fatfs->csize * fs->block_size));
+
+    /* Allocate an attribute list for the file. */
+    fs_meta->attr = tsk_fs_attrlist_alloc();
+
+    /* Allocate a non-resident attribute for the file and add it to the
+     * attribute list. */
+    if ((fs_attr = tsk_fs_attrlist_getnew(fs_meta->attr, 
+        TSK_FS_ATTR_NONRES)) == NULL) {
+        return 1;
+    }
+
+    /* Allocate a single data run for the attribute. For exFAT, a data run is 
+     * a contiguous run of sectors. */
+    data_run = tsk_fs_attr_run_alloc();
+    if (data_run == NULL) {
+        return 1;
+    }
+
+    /* Set the starting sector address of the run and the length of the run 
+     * in sectors. */
+    data_run->addr = FATFS_CLUST_2_SECT(fatfs, first_cluster);
+    data_run->len = roundup(fs_meta->size, 
+        (fatfs->csize * fs->block_size)) / fs->block_size;  
+
+    /* Add the data run to the attribute and add the attribute to the 
+     * attribute list. Note that the initial size and the allocation
+     * size are the same for exFAT. */
+    if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run, NULL,
+            TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
+            fs_meta->size,
+            data_run->len * fs->block_size,
+            data_run->len * fs->block_size, 
+            TSK_FS_ATTR_FLAG_NONE, 0)) {
+        return 1;
+    }
+
+    /* Mark the attribute list as loaded. */
+    fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
+
+    return 0;
+}
+
+/**
+ * \internal
+ * Use a volume label directory entry corresponding to the exFAT 
+ * equivalent of an inode to populate the TSK_FS_META object of a 
+ * TSK_FS_FILE object.
+ *
+ * @param [in] a_fatfs Source file system for the directory entry.
+ * @param [in] a_inum Address of the inode.
+ * @param [in] a_dentry A volume label directory entry.
+ * @param a_fs_file Generic file with generic inode structure (TSK_FS_META).
+ * @return TSK_RETVAL_ENUM.  
+ */
+static TSK_RETVAL_ENUM 
+exfatfs_copy_vol_label_inode(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, FATFS_DENTRY *a_dentry, TSK_FS_FILE *a_fs_file)
+{
+    const char *func_name = "exfatfs_copy_vol_label_inode";
+    EXFATFS_VOL_LABEL_DIR_ENTRY *dentry = NULL;
+
+    assert(a_fatfs != NULL);
+    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
+    assert(a_dentry != NULL);
+    assert(a_fs_file != NULL);
+    assert(a_fs_file->meta != NULL);
+
+    dentry = (EXFATFS_VOL_LABEL_DIR_ENTRY*)a_dentry;
+    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL ||
+           dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL);
+
+    /* If there is a volume label, copy it to the name field of the 
+     * TSK_FS_META structure. */
+    if (dentry->entry_type != EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL) {
+        if (fatfs_utf16_inode_str_2_utf8(a_fatfs, (UTF16*)dentry->volume_label, (size_t)dentry->utf16_char_count + 1, 
+            (UTF8*)a_fs_file->meta->name2->name, sizeof(a_fs_file->meta->name2->name), a_inum, "volume label") != TSKconversionOK) {
+            return TSK_COR;
+        }
+    }
+    else {
+        strcpy(a_fs_file->meta->name2->name, EXFATFS_EMPTY_VOLUME_LABEL_DENTRY_NAME);
+    }
+
+    return TSK_OK;
+}
+
+/**
+ * \internal
+ * Use an allocation bitmap directory entry corresponding to the exFAT 
+ * equivalent of an inode to populate the TSK_FS_META object of a 
+ * TSK_FS_FILE object.
+ *
+ * @param a_fatfs [in] Source file system for the directory entries.
+ * @param [in] a_dentry An allocation bitmap directory entry.
+ * @param a_fs_file [in, out] Generic file with generic inode structure (TSK_FS_META).
+ * @return TSK_RETVAL_ENUM.  
+ */
+static TSK_RETVAL_ENUM 
+exfatfs_copy_alloc_bitmap_inode(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, TSK_FS_FILE *a_fs_file)
+{
+    const char *func_name = "exfatfs_copy_alloc_bitmap_inode";
+    EXFATFS_ALLOC_BITMAP_DIR_ENTRY *dentry = NULL;
+
+    assert(a_fatfs != NULL);
+    assert(a_dentry != NULL);
+    assert(a_fs_file != NULL);
+    assert(a_fs_file->meta != NULL);
+
+    dentry = (EXFATFS_ALLOC_BITMAP_DIR_ENTRY*)a_dentry;
+    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP);
+
+    /* Set the file name to a descriptive pseudo file name. */
+    strcpy(a_fs_file->meta->name2->name, EXFATFS_ALLOC_BITMAP_DENTRY_NAME);
+
+    /* Set the size of the allocation bitmap and the address of its 
+     * first cluster. */
+    ((TSK_DADDR_T*)a_fs_file->meta->content_ptr)[0] = FATFS_SECT_2_CLUST(a_fatfs, a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap);
+    a_fs_file->meta->size = a_fatfs->EXFATFS_INFO.length_of_alloc_bitmap_in_bytes;
+    
+    /* There is no FAT chain walk for the allocation bitmap. Do an eager
+     * load instead of a lazy load of its data run. */
+    if (exfatfs_make_contiguous_data_run(a_fs_file)) {
+        return TSK_ERR;
+    }
+
+    return TSK_OK;
+}
+
+/**
+ * \internal
+ * Use an UP-Case table directory entry corresponding to the exFAT equivalent
+ * of an inode to populate the TSK_FS_META object of a TSK_FS_FILE object.
+ *
+ * @param a_fatfs [in] Source file system for the directory entries.
+ * @param [in] a_dentry An upcase table directory entry.
+ * @param a_fs_file [in, out] Generic file with generic inode structure (TSK_FS_META).
+ * @return TSK_RETVAL_ENUM.  
+ */
+static TSK_RETVAL_ENUM 
+exfatfs_copy_upcase_table_inode(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, TSK_FS_FILE *a_fs_file)
+{
+    const char *func_name = "exfatfs_copy_upcase_table_inode";
+    EXFATFS_UPCASE_TABLE_DIR_ENTRY *dentry = NULL;
+
+    assert(a_fatfs != NULL);
+    assert(a_dentry != NULL);
+    assert(a_fs_file != NULL);
+    assert(a_fs_file->meta != NULL);
+
+    dentry = (EXFATFS_UPCASE_TABLE_DIR_ENTRY*)a_dentry;
+    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE);
+
+    strcpy(a_fs_file->meta->name2->name, EXFATFS_UPCASE_TABLE_DENTRY_NAME);
+
+    /* Set the size of the Up-Case table and the address of its 
+     * first cluster. */((TSK_DADDR_T*)a_fs_file->meta->content_ptr)[0] = tsk_getu32(a_fatfs->fs_info.endian, dentry->first_cluster_of_table);
+    a_fs_file->meta->size = tsk_getu64(a_fatfs->fs_info.endian, dentry->table_length_in_bytes);
+
+    /* There is no FAT chain walk for the upcase table. Do an eager
+     * load instead of a lazy load of its data run. */
+    if (exfatfs_make_contiguous_data_run(a_fs_file)) {
+        return TSK_ERR;
+    }
+
+    return TSK_OK;
+}
+
+/**
+ * \internal
+ * Given an inode address, load the corresponding directory entry and test
+ * to see if it's an exFAT file stream directory entry.
+ *
+ * @param a_fatfs [in] Source file system for the directory entries.
+ * @param a_stream_entry_inum [in] The inode address associated with the 
+ * supposed file stream entry.
+ * @param a_sector_is_alloc [in] The allocation status of the sector that
+ * contains the supposed file stream entry.
+ * @param a_file_dentry_type [in] The companion file entry type, 
+ * i.e., deleted or not.
+ * @param a_dentry [in, out] A directory entry structure. The stream 
+ * entry, if found, will be loaded into it.
+ * @return 0 on success, 1 on failure, per TSK convention
+ */
+static uint8_t
+exfatfs_load_file_stream_dentry(FATFS_INFO *a_fatfs, 
+    TSK_INUM_T a_stream_entry_inum, uint8_t a_sector_is_alloc, 
+    EXFATFS_DIR_ENTRY_TYPE_ENUM a_file_dentry_type, // RJCTODO: Consider sending the desired type of stream entry in, or passing in the file entry.
+    FATFS_DENTRY *a_dentry)
+{
+    assert(a_fatfs != NULL);
+    assert(fatfs_inum_is_in_range(a_fatfs, a_stream_entry_inum));
+    assert(a_dentry != NULL);
+
+    if (fatfs_dentry_load(a_fatfs, a_dentry, a_stream_entry_inum) == 0 &&
+        exfatfs_is_dentry(a_fatfs, a_dentry, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)a_sector_is_alloc, a_sector_is_alloc)) {
+        /* If the bytes at the specified inode address are a file stream entry
+         * with the same allocation status as the file entry, report success. */
+        if ((a_file_dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE && 
+             a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM) ||
+            (a_file_dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE &&
+             a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM)) {
+            return 0;
+        }
+    }
+
+    memset((void*)a_dentry, 0, sizeof(FATFS_DENTRY));
+    return 1;
+}
+
+/**
+ * \internal
+ * Given an exFAT file directory entry, try to find the corresponding file
+ * stream directory entry.
+ *
+ * @param [in] a_fatfs Source file system for the directory entries.
+ * @param [in] a_file_entry_inum The inode address associated with the file 
+ * entry.
+ * @param [in] a_sector The address of the sector where the file entry was 
+ * found.
+ * @param [in] a_sector_is_alloc The allocation status of the sector.
+ * @param [in] a_file_dentry_type The file entry type, deleted or not.
+ * @param [in, out] The stream entry, if found, will be loaded into the
+ * this generic directory entry structure.
+ * @return 0 on success, 1 on failure, per TSK convention
+ */
+static uint8_t
+exfatfs_new_find_file_stream_dentry(FATFS_INFO *a_fatfs, TSK_INUM_T a_file_entry_inum, 
+    EXFATFS_FILE_DIR_ENTRY *a_file_dentry, EXFATFS_FILE_STREAM_DIR_ENTRY *a_stream_dentry)
+{
+    const char *func_name = "exfatfs_new_find_file_stream_dentry";
+    TSK_INUM_T stream_entry_inum = 0;
+    int8_t alloc_check_ret_val = 0;
+    uint8_t cluster_is_alloc = 0;
+    TSK_DADDR_T sector = 0; // RJCTODO: Improve name
+    TSK_DADDR_T cluster = 0; // RJCTODO: Improve name
+    TSK_DADDR_T cluster_base_sector = 0;
+    TSK_DADDR_T last_entry_offset = 0;
+    TSK_DADDR_T file_entry_offset = 0;
+    EXFATFS_DIR_ENTRY_TYPE_ENUM dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
+    TSK_DADDR_T next_cluster = 0;
+
+    assert(a_fatfs != NULL);
+    assert(fatfs_inum_is_in_range(a_fatfs, a_file_entry_inum));
+    assert(a_file_dentry != NULL);
+    assert(a_stream_dentry != NULL);
+        
+    sector = FATFS_INODE_2_SECT(a_fatfs, a_file_entry_inum);
+    cluster = FATFS_SECT_2_CLUST(a_fatfs, sector);
+    alloc_check_ret_val = exfatfs_is_cluster_alloc(a_fatfs, cluster);
+    if (alloc_check_ret_val != -1) {
+        cluster_is_alloc = (uint8_t)alloc_check_ret_val;
+    }
+    else {
+        return FATFS_FAIL;
+    }
+
+    /* Check for the most common case first - the file stream entry is located
+     * immediately after the file entry. This should always be true for any 
+     * in-use file entry in an allocated cluster that is not the last entry in
+     * the cluster. It will also be true if the file entry is the last entry in 
+     * the cluster and the directory that contains the file is not fragmented - 
+     * the stream entry will simply be the first entry of the next cluster. 
+     * Finally, if the file entry is not in-use and was found in an unallocated 
+     * sector, the only viable place to look for the stream entry is in the 
+     * bytes following the file entry, since there is no FAT chain to 
+     * consult. */
+    stream_entry_inum = a_file_entry_inum + 1;
+    if (fatfs_inum_is_in_range(a_fatfs, stream_entry_inum)) {
+        if (exfatfs_load_file_stream_dentry(a_fatfs, 
+            stream_entry_inum, cluster_is_alloc, 
+            (EXFATFS_DIR_ENTRY_TYPE_ENUM)a_file_dentry->entry_type, // RJCTODO: Messy casts
+            (FATFS_DENTRY*)a_stream_dentry) == 0) {
+            /* Found it. */
+            return FATFS_OK;
+        }
+    }
+
+    /* If the stream entry was not found immediately following the file entry
+     * and the cluster is allocated, it is possible that the file entry was the
+     * last entry of a cluster in a fragmented directory. In this
+     * case, the FAT can be consulted to see if there is a next cluster. If 
+     * so, the stream entry may be the first entry of that cluster. */
+    if (cluster_is_alloc) {
+        /* Calculate the byte offset of the last possible directory entry in 
+         * the current cluster. */
+        cluster_base_sector = FATFS_CLUST_2_SECT(a_fatfs, cluster); 
+        last_entry_offset = (cluster_base_sector * a_fatfs->ssize) + 
+            (a_fatfs->csize * a_fatfs->ssize) - sizeof(FATFS_DENTRY);   
+
+        /* Get the byte offset of the file entry. Note that FATFS_INODE_2_OFF
+         * gices the offset relative to start of a sector. */
+        file_entry_offset = (sector * a_fatfs->ssize) + 
+            FATFS_INODE_2_OFF(a_fatfs, a_file_entry_inum);
+
+        if (file_entry_offset == last_entry_offset) {
+            /* The file entry is the last in its cluster. Look up the next
+             * cluster. */
+            if ((fatfs_getFAT(a_fatfs, cluster, &next_cluster) == 0) &&
+                (next_cluster != 0)) {
+                /* Found the next cluster in the FAT, so get its first sector
+                 * and the inode address of the first entry of the sector. */
+                cluster_base_sector = FATFS_CLUST_2_SECT(a_fatfs, next_cluster); 
+                stream_entry_inum = FATFS_SECT_2_INODE(a_fatfs, 
+                    cluster_base_sector);
+
+                if (fatfs_inum_is_in_range(a_fatfs, stream_entry_inum)) {
+                    if (exfatfs_load_file_stream_dentry(a_fatfs, 
+                        stream_entry_inum, cluster_is_alloc, 
+                        (EXFATFS_DIR_ENTRY_TYPE_ENUM)a_file_dentry->entry_type, // RJCTODO: Messy casts
+                        (FATFS_DENTRY*)a_stream_dentry) == 0) {
+                        /* Found it. */
+                        return FATFS_OK;
+                    }
+                }
+            }
+        }
+    }
+
+    /* Did not find the file stream entry. */
+    return FATFS_FAIL;
+}
+
+/**
+ * \internal
+ * Use a a file and a file stream directory entry corresponding to the exFAT 
+ * equivalent of an inode to populate the TSK_FS_META object of a TSK_FS_FILE 
+ * object.
+ *
+ * @param a_fatfs [in] Source file system for the directory entries.
+ * @param [in] a_inum Address of the inode. 
+ * @param [in] a_dentry A file directory entry.
+ * @param [in] a_is_alloc Allocation status of the sector that contains the
+ * file directory entry.
+ * @param a_fs_file [in, out] Generic file with generic inode structure (TSK_FS_META).
+ * @return TSK_RETVAL_ENUM.  
+ */
+static TSK_RETVAL_ENUM 
+exfatfs_copy_file_inode(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
+    FATFS_DENTRY *a_file_dentry, uint8_t a_is_alloc, TSK_FS_FILE *a_fs_file)
+{
+    const char *func_name = "exfatfs_copy_file_inode";
+    TSK_FS_INFO *fs = NULL;
+    TSK_FS_META *fs_meta =  NULL;
+    EXFATFS_FILE_DIR_ENTRY *file_dentry = (EXFATFS_FILE_DIR_ENTRY*)a_file_dentry;
+    EXFATFS_FILE_STREAM_DIR_ENTRY stream_dentry;
+    uint32_t mode = 0;
+
+    assert(a_fatfs != NULL);
+    assert(a_file_dentry != NULL);
+    assert(a_fs_file != NULL);
+    assert(a_fs_file->meta != NULL);
+    assert(file_dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE ||
+           file_dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE);
+
+    fs = &(a_fatfs->fs_info);
+    fs_meta = a_fs_file->meta;
+
+    /* Determine whether the file is a regular file or directory. */
+    if (file_dentry->attrs[0] & FATFS_ATTR_DIRECTORY) {
+        fs_meta->type = TSK_FS_META_TYPE_DIR;
+    }
+    else {
+        fs_meta->type = TSK_FS_META_TYPE_REG;
+    }
+
+    /* Add mode flags corresponding to file attribute flags. */
+    mode = fs_meta->mode; 
+    if ((file_dentry->attrs[0] & FATFS_ATTR_READONLY) == 0) {
+        mode |=
+            (TSK_FS_META_MODE_IRUSR | TSK_FS_META_MODE_IRGRP |
+            TSK_FS_META_MODE_IROTH);
+    }
+    if ((file_dentry->attrs[0] & FATFS_ATTR_HIDDEN) == 0) {
+        mode |=
+            (TSK_FS_META_MODE_IWUSR | TSK_FS_META_MODE_IWGRP |
+            TSK_FS_META_MODE_IWOTH);
+    }
+    fs_meta->mode = (TSK_FS_META_MODE_ENUM)mode;
+
+    /* There is no notion of links in exFAT, just deleted or not deleted. 
+     * If the file is not deleted, treat this as having one link. */
+    fs_meta->nlink = (file_dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE) ? 0 : 1;
+
+    /* Copy the last modified time, converted to UNIX date format. */
+    if (FATFS_ISDATE(tsk_getu16(fs->endian, file_dentry->modified_date))) {
+        fs_meta->mtime =
+            fatfs_dos_2_unix_time(tsk_getu16(fs->endian, file_dentry->modified_date),
+                tsk_getu16(fs->endian, file_dentry->modified_time), 
+                file_dentry->modified_time_tenths_of_sec);
+        fs_meta->mtime_nano = fatfs_dos_2_nanosec(file_dentry->modified_time_tenths_of_sec);
+    }
+    else {
+        fs_meta->mtime = 0;
+        fs_meta->mtime_nano = 0;
+    }
+
+    /* Copy the last accessed time, converted to UNIX date format. */
+    if (FATFS_ISDATE(tsk_getu16(fs->endian, file_dentry->accessed_date))) {
+        fs_meta->atime =
+            fatfs_dos_2_unix_time(tsk_getu16(fs->endian, file_dentry->accessed_date), 
+                tsk_getu16(fs->endian, file_dentry->accessed_time), 0);
+    }
+    else {
+        fs_meta->atime = 0;
+    }
+    fs_meta->atime_nano = 0;
+
+    /* exFAT does not have a last changed time. */
+    fs_meta->ctime = 0;
+    fs_meta->ctime_nano = 0;
+
+    /* Copy the created time, converted to UNIX date format. */
+    if (FATFS_ISDATE(tsk_getu16(fs->endian, file_dentry->created_date))) {
+        fs_meta->crtime =
+            fatfs_dos_2_unix_time(tsk_getu16(fs->endian, file_dentry->created_date),
+                tsk_getu16(fs->endian, file_dentry->created_time), 
+                file_dentry->created_time_tenths_of_sec);
+        fs_meta->crtime_nano = fatfs_dos_2_nanosec(file_dentry->created_time_tenths_of_sec);
+    }
+    else {
+        fs_meta->crtime = 0;
+        fs_meta->crtime_nano = 0;
+    }
+
+    // RJCTODO: Do we want to do anything with the time zone offsets?
+
+    /* Attempt to load the file stream entry that goes with this file entry. 
+     * If not successful, at least the file entry meta data will be returned. */
+    if (exfatfs_new_find_file_stream_dentry(a_fatfs, a_inum, file_dentry, &stream_dentry)) { // RJCTODO: COuld pass allocation status through.
+        return TSK_OK;
+    }
+
+    /* Set the size of the file and the address of its first cluster. */
+    ((TSK_DADDR_T*)a_fs_file->meta->content_ptr)[0] = 
+        tsk_getu32(a_fatfs->fs_info.endian, stream_dentry.first_cluster_addr);
+    fs_meta->size = tsk_getu64(fs->endian, stream_dentry.data_length); // RJCTODO: How does this relate to the valid data length field? Is one or the other to be preferred?
+
+    // RJCTODO: What if a cluster boundary is crossed? Probably need to check the allocation status of all the clusters
+    // involved in a file directory entry set. This is another argument for moving the duplicated code to find stream entries out of inode lookup
+    // and walk to here.
+    /* Set the allocation status using both the allocation status of the 
+     * sector that contains the directory entries and the entry type 
+     * settings - essentially a "belt and suspenders" check. */
+    if ((a_is_alloc) &&
+        (file_dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE) &&
+        (stream_dentry.entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM)) {
+        a_fs_file->meta->flags = TSK_FS_META_FLAG_ALLOC;
+
+        /* If the FAT chain bit of the secondary flags of the stream entry is set,
+         * the file is not fragmented and there is no FAT chain to walk. If the 
+         * file is not deleted, do an eager load instead of a lazy load of its 
+         * data run. */
+        if ((stream_dentry.flags & EXFATFS_INVALID_FAT_CHAIN_MASK) &&
+            (exfatfs_make_contiguous_data_run(a_fs_file))) {
+            return TSK_ERR;
+        }
+    }
+    else {
+        a_fs_file->meta->flags = TSK_FS_META_FLAG_UNALLOC;
+    }
+
+    return TSK_OK;
+}
+
+/**
+ * \internal
+ * Use a file name directory entry corresponding to the exFAT equivalent of
+ * an inode to populate the TSK_FS_META object of a TSK_FS_FILE object.
+ *
+ * @param [in] a_fatfs Source file system for the directory entry.
+ * @param [in] a_inum Address of the inode.
+ * @param [in] a_is_alloc Allocation status of the sector that contains the
+ * inode.
+ * @param [in] a_dentry A file name directory entry.
+ * @param a_fs_file Generic file with generic inode structure (TSK_FS_META).
+ * @return TSK_RETVAL_ENUM.  
+ */
+static TSK_RETVAL_ENUM 
+exfatfs_copy_file_name_inode(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
+    FATFS_DENTRY *a_dentry, uint8_t a_is_alloc, TSK_FS_FILE *a_fs_file)
+{
+    const char *func_name = "exfatfs_copy_file_name_inode";
+    EXFATFS_FILE_NAME_DIR_ENTRY *dentry = NULL;
+
+    assert(a_fatfs != NULL);
+    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
+    assert(a_dentry != NULL);
+    assert(a_fs_file != NULL);
+    assert(a_fs_file->meta != NULL);
+
+    dentry = (EXFATFS_FILE_NAME_DIR_ENTRY*)a_dentry;
+    assert(dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME ||
+           dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME);
+
+    // RJCTODO: This leads to attempts to "recover" a file with a single cluster
+    // run for a deleted file name entry. See remarks at the beginning of this file.
+    /* Set the allocation status using both the allocation status of the 
+     * sector that contains the directory entries and the entry type 
+     * settings - essentially a "belt and suspenders" check. */
+    if ((a_is_alloc) &&
+        (dentry->entry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME)) {
+        a_fs_file->meta->flags = TSK_FS_META_FLAG_ALLOC;
+    }
+    else {
+        a_fs_file->meta->flags = TSK_FS_META_FLAG_UNALLOC;
+    }
+
+    /* Copy the file name segment. */
+    // RJCTODO: Is there a problem here because we don't know how many of the chars are valid?
+    if (fatfs_utf16_inode_str_2_utf8(a_fatfs, (UTF16*)dentry->utf16_name_chars, EXFATFS_MAX_FILE_NAME_SEGMENT_LENGTH, 
+        (UTF8*)a_fs_file->meta->name2->name, sizeof(a_fs_file->meta->name2->name), a_inum, "file name segment") != TSKconversionOK) {
+        return TSK_COR;
+    }
+
+    return TSK_OK;
+}
+
+/**
+ * \internal
+ * Initialize the members of a TSK_FS_META object before copying the contents
+ * of an an inode consisting of one or more raw exFAT directry entries into it. 
+ *
+ * @param [in] a_fatfs Source file system for the directory entries.
+ * @param [in] a_inum Address of the inode.
+ * @param [in] a_is_alloc Allocation status of the sector that contains the
+ * inode.
+ * @param [in, out] a_fs_file Generic file with generic inode structure to 
+ * initialize.
+ * @return 0 on success, 1 on failure, per TSK convention
+ */
+static uint8_t
+exfatfs_inode_copy_init(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
+    uint8_t a_is_alloc, TSK_FS_FILE *a_fs_file)
+{
+    const char *func_name = "exfatfs_inode_copy_init";
+    TSK_FS_META *fs_meta = NULL;
+    int8_t ret_val = 0;
+
+    assert(a_fatfs != NULL);
+    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
+    assert(a_fs_file != NULL);
+    assert(a_fs_file->meta != NULL);
+
+    fs_meta = a_fs_file->meta;
+    fs_meta->addr = a_inum;
+
+    /* Set the allocation status based on the cluster allocation status. File 
+     * entry set entries may change this. */
+    a_fs_file->meta->flags = a_is_alloc ? TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC;
+
+    /* As for FATXX, make regular file the default type. */
+    fs_meta->type = TSK_FS_META_TYPE_REG;
+
+    /* As for FATXX, mark everything as executable. */
+    fs_meta->mode = (TSK_FS_META_MODE_ENUM)(TSK_FS_META_MODE_IXUSR | TSK_FS_META_MODE_IXGRP |
+        TSK_FS_META_MODE_IXOTH);
+
+    /* There is no notion of links in exFAT, just deleted or not deleted. 
+     * With not deleted being equivalent to having one link, set nlink to 1
+     * here so that it will be set for static things like the allocation 
+     * bitmap. The code for file inodes can reset or unset it appropriately. */
+    fs_meta->nlink = 1;
+
+    /* Initialize size to zero. The code for particular inode types will 
+     * fill in another value, if appropriate. */
+    fs_meta->size = 0;
+
+    /* Default values for time stamp metadata. The code for file inodes will 
+     * fill in actual time stamp data. */
+    fs_meta->mtime = 0;
+    fs_meta->mtime_nano = 0;
+    fs_meta->atime = 0;
+    fs_meta->atime_nano = 0;
+    fs_meta->ctime = 0;
+    fs_meta->ctime_nano = 0;
+    fs_meta->crtime = 0;
+    fs_meta->crtime_nano = 0;
+
+    /* Metadata that does not exist in exFAT. */
+    fs_meta->uid = 0;
+    fs_meta->gid = 0;
+    fs_meta->seq = 0;
+
+    /* Allocate space for a name. */
+    if (fs_meta->name2 == NULL) {
+        if ((fs_meta->name2 = (TSK_FS_META_NAME_LIST*)tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) {
+            return 1;
+        }
+        fs_meta->name2->next = NULL;
+    }
+    fs_meta->name2->name[0] = '\0';
+
+    /* Allocate space for saving the cluster address of the first cluster 
+     * of file inodes, including allocation bitmaps and upcase tables. */
+    if (fs_meta->content_len < FATFS_FILE_CONTENT_LEN) {
+        if ((fs_meta =
+                tsk_fs_meta_realloc(fs_meta,
+                    FATFS_FILE_CONTENT_LEN)) == NULL) {
+            return 1;
+        }
+    }
+
+    /* Mark the generic attribute list as not in use (in the generic file model
+     * attributes are containers for data or metadata). Population of this 
+     * stuff is done on demand (lazy look up). */
+    fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
+    if (fs_meta->attr) {
+        tsk_fs_attrlist_markunused(fs_meta->attr);
+    }
+
+    return 0;
+}
+
+/**
+ * \internal
+ * Use one or more directory entries corresponding to the exFAT equivalent of
+ * an inode to populate the TSK_FS_META object of a TSK_FS_FILE object.
+ *
+ * @param [in] a_fatfs Source file system for the directory entries.
+ * @param [in] a_inum Address of the inode.
+ * @param [in] a_dentry A directory entry.
+ * @param [in] a_is_alloc Allocation status of the inode.
+ * @param [in, out] a_fs_file Generic file object with a generic inode 
+ * metadata structure.
+ * @return TSK_RETVAL_ENUM.  
+ */
+TSK_RETVAL_ENUM
+exfatfs_dinode_copy(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
+    FATFS_DENTRY *a_dentry, uint8_t a_is_alloc, TSK_FS_FILE *a_fs_file)
+{
+    const char *func_name = "exfatfs_dinode_copy";
+
+    assert(a_fatfs != NULL);
+    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
+    assert(a_dentry != NULL);
+    assert(a_fs_file != NULL);
+    assert(a_fs_file->meta != NULL);
+    assert(a_fs_file->fs_info != NULL);
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
+        fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name) ||
+        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name) ||
+        fatfs_ptr_arg_is_null(a_fs_file->meta, "a_fs_file->meta", func_name) ||
+        fatfs_ptr_arg_is_null(a_fs_file->fs_info, "a_fs_file->fs_info", func_name) ||
+        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
+        return TSK_ERR;
+    }
+
+    if (exfatfs_inode_copy_init(a_fatfs, a_inum, a_is_alloc, a_fs_file)) {
+        return TSK_ERR;
+    }
+
+    switch (a_dentry->data[0])
+    {
+    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL:
+    case EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL:
+        return exfatfs_copy_vol_label_inode(a_fatfs, a_inum, a_dentry, a_fs_file);
+    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
+        strcpy(a_fs_file->meta->name2->name, EXFATFS_VOLUME_GUID_DENTRY_NAME);
+        return TSK_OK;
+    case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:
+        return exfatfs_copy_alloc_bitmap_inode(a_fatfs, a_dentry, a_fs_file);
+    case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:
+        return exfatfs_copy_upcase_table_inode(a_fatfs, a_dentry, a_fs_file);
+    case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:
+        strcpy(a_fs_file->meta->name2->name, EXFATFS_TEX_FAT_DENTRY_NAME);
+        return TSK_OK;
+    case EXFATFS_DIR_ENTRY_TYPE_ACT:
+        strcpy(a_fs_file->meta->name2->name, EXFATFS_ACT_DENTRY_NAME);
+        return TSK_OK;
+    case EXFATFS_DIR_ENTRY_TYPE_FILE:
+    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE:
+        return exfatfs_copy_file_inode(a_fatfs, a_inum, a_dentry, a_is_alloc, a_fs_file);
+    case EXFATFS_DIR_ENTRY_TYPE_FILE_NAME:
+    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME:
+        return exfatfs_copy_file_name_inode(a_fatfs, a_inum, a_dentry, a_is_alloc, a_fs_file);
+    case EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM:
+    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM:
+    default:
+        /* Stream entries are copied in tandem with the corresponding file entry. */
+        return TSK_ERR;
+    }
+
+    return TSK_OK;
+}
+
+/**
+ * \internal
+ * Given an exFAT file directory entry, try to find the corresponding file
+ * stream directory entry.
+ *
+ * @param [in] a_fatfs Source file system for the directory entries.
+ * @param [in] a_file_entry_inum The inode address associated with the file 
+ * entry.
+ * @param [in] a_sector The address of the sector where the file entry was 
+ * found.
+ * @param [in] a_sector_is_alloc The allocation status of the sector.
+ * @param [in] a_file_dentry_type The file entry type, deleted or not.
+ * @param [in, out] The stream entry, if found, will be loaded into the
+ * this generic directory entry structure.
+ * @return 0 on success, 1 on failure, per TSK convention
+ */
+uint8_t
+exfatfs_find_file_stream_dentry(FATFS_INFO *a_fatfs, TSK_INUM_T a_file_entry_inum, 
+    TSK_DADDR_T a_sector, uint8_t a_sector_is_alloc,  
+    EXFATFS_DIR_ENTRY_TYPE_ENUM a_file_dentry_type,
+    FATFS_DENTRY *a_stream_dentry)
+{
+    const char *func_name = "exfatfs_find_file_stream_dentry";
+    TSK_INUM_T stream_entry_inum = 0;
+    TSK_DADDR_T cluster = 0;
+    TSK_DADDR_T cluster_base_sector = 0;
+    TSK_DADDR_T last_entry_offset = 0;
+    TSK_DADDR_T file_entry_offset = 0;
+    EXFATFS_DIR_ENTRY_TYPE_ENUM dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
+    TSK_DADDR_T next_cluster = 0;
+
+    assert(a_fatfs != NULL);
+    assert(fatfs_inum_is_in_range(a_fatfs, a_file_entry_inum));
+    assert(a_stream_dentry != NULL);
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
+        fatfs_ptr_arg_is_null(a_stream_dentry, "a_stream_dentry", func_name) ||
+        !fatfs_inum_arg_is_in_range(a_fatfs, a_file_entry_inum, func_name)) {
+        return FATFS_FAIL;
+    }
+        
+    /* Check for the most common case first - the file stream entry is located
+     * immediately after the file entry. This should always be true for any 
+     * in-use file entry in an allocated cluster that is not the last entry in
+     * the cluster. It will also be true if the file entry is the last entry in 
+     * the cluster and the directory that contains the file is not fragmented - 
+     * the stream entry will simply be the first entry of the next cluster. 
+     * Finally, if the file entry is not in-use and was found in an unallocated 
+     * sector, the only viable place to look for the stream entry is in the 
+     * bytes following the file entry, since there is no FAT chain to 
+     * consult. */
+    stream_entry_inum = a_file_entry_inum + 1;
+    if (fatfs_inum_is_in_range(a_fatfs, stream_entry_inum)) {
+        if (exfatfs_load_file_stream_dentry(a_fatfs, 
+            stream_entry_inum, a_sector_is_alloc, 
+            a_file_dentry_type, 
+            a_stream_dentry) == 0) {
+            /* Found it. */
+            return FATFS_OK;
+        }
+    }
+
+    /* If the stream entry was not found immediately following the file entry
+     * and the cluster is allocated, it is possible that the file entry was the
+     * last entry of a cluster in a fragmented directory. In this
+     * case, the FAT can be consulted to see if there is a next cluster. If 
+     * so, the stream entry may be the first entry of that cluster. */
+    if (a_sector_is_alloc) {
+        /* Calculate the byte offset of the last possible directory entry in 
+         * the current cluster. */
+        cluster = FATFS_SECT_2_CLUST(a_fatfs, a_sector);
+        cluster_base_sector = FATFS_CLUST_2_SECT(a_fatfs, cluster); 
+        last_entry_offset = (cluster_base_sector * a_fatfs->ssize) + 
+            (a_fatfs->csize * a_fatfs->ssize) - sizeof(FATFS_DENTRY);   
+
+        /* Get the byte offset of the file entry. Note that FATFS_INODE_2_OFF
+         * gices the offset relative to start of a sector. */
+        file_entry_offset = (a_sector * a_fatfs->ssize) + 
+            FATFS_INODE_2_OFF(a_fatfs, a_file_entry_inum);
+
+        if (file_entry_offset == last_entry_offset) {
+            /* The file entry is the last in its cluster. Look up the next
+             * cluster. */
+            if ((fatfs_getFAT(a_fatfs, cluster, &next_cluster) == 0) &&
+                (next_cluster != 0)) {
+                /* Found the next cluster in the FAT, so get its first sector
+                 * and the inode address of the first entry of the sector. */
+                cluster_base_sector = FATFS_CLUST_2_SECT(a_fatfs, next_cluster); 
+                stream_entry_inum = FATFS_SECT_2_INODE(a_fatfs, 
+                    cluster_base_sector);
+
+                if (fatfs_inum_is_in_range(a_fatfs, stream_entry_inum)) {
+                    if (exfatfs_load_file_stream_dentry(a_fatfs, 
+                        stream_entry_inum, a_sector_is_alloc, 
+                        a_file_dentry_type, 
+                        a_stream_dentry) == 0) {
+                        /* Found it. */
+                        return FATFS_OK;
+                    }
+                }
+            }
+        }
+    }
+
+    /* Did not find the file stream entry. */
+    return FATFS_FAIL;
+}
+
+/**
+ * \internal
+ * Read in the bytes from an exFAT file system that correspond to the exFAT 
+ * equivalent of an inode and use them to populate the TSK_FS_META object of
+ * a TSK_FS_FILE object.
+ *
+ * @param [in] a_fatfs Source file system for the directory entries.
+ * @param [in, out] a_fs_file The TSK_FS_FILE object.
+ * @param [in] a_inum Inode address.
+ * @return 0 on success, 1 on failure, per TSK convention
+ */
+uint8_t
+exfatfs_inode_lookup(FATFS_INFO *a_fatfs, TSK_FS_FILE *a_fs_file, 
+    TSK_INUM_T a_inum)
+{
+    const char *func_name = "exfatfs_inode_lookup";
+    TSK_DADDR_T sector = 0;
+    int8_t sect_is_alloc = 0;
+    FATFS_DENTRY dentry;
+    //FATFS_DENTRY stream_dentry;
+    //FATFS_DENTRY *secondary_dentry = NULL;
+    uint64_t inode_offset = 0;
+    TSK_DADDR_T cluster_base_sector = 0;
+    TSK_INUM_T next_inum = 0;
+    EXFATFS_DIR_ENTRY_TYPE_ENUM dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
+    TSK_RETVAL_ENUM copy_result = TSK_OK;
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
+        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name) ||
+        fatfs_ptr_arg_is_null(a_fs_file->meta, "a_fs_file->meta", func_name) ||
+        fatfs_ptr_arg_is_null(a_fs_file->fs_info, "a_fs_file->fs_info", func_name) ||
+        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
+        return 1;
+    }
+
+    /* Map the inode address to a sector. */ 
+    sector = FATFS_INODE_2_SECT(a_fatfs, a_inum);
+    if (sector > a_fatfs->fs_info.last_block) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr("%s: Inode %" PRIuINUM
+            " in sector too big for image: %" PRIuDADDR, func_name, a_inum, sector);
+        return 1;
+    }
+
+    /* Check the allocation status of the sector. This status will be used
+     * not only as meta data to be reported, but also as a way to choose
+     * between the basic or in-depth version of the tests (below) that 
+     * determine whether or not the bytes corrresponding to the inode are 
+     * likely to be a directory entry. Note that in other places in the code 
+     * information about whether or not the sector that contains the inode is
+     * part of a folder is used to select the test. Here, that information is 
+     * not available, so the test here is less reliable and may result in some 
+     * false positives. */
+    sect_is_alloc = fatfs_is_sectalloc(a_fatfs, sector);
+    if (sect_is_alloc == -1) {
+        return 1;
+    }
+
+    /* Load the bytes at the specified inode address. */
+    memset((void*)&dentry, 0, sizeof(FATFS_DENTRY));
+    if (fatfs_dentry_load(a_fatfs, &dentry, a_inum)) {
+        return 1;
+    }
+
+    /* Try typing the bytes as a directory entry.*/
+    if (exfatfs_is_dentry(a_fatfs, &dentry, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)sect_is_alloc, sect_is_alloc)) {
+        dentry_type = (EXFATFS_DIR_ENTRY_TYPE_ENUM)dentry.data[0];
+    }
+    else {
+        return 1;
+    }
+
+    /* For the purposes of inode lookup, the file and file stream entries 
+     * that begin a file entry set are mapped to a single inode. Thus,  
+     * file stream entries are not treated as independent inodes. */
+    if (dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM ||
+        dentry_type == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr("%s: %" PRIuINUM " is not an inode", func_name, 
+            a_inum);
+        return 1;
+    }
+
+    /* Populate the TSK_FS_META object of the TSK_FS_FILE object. */
+    copy_result = exfatfs_dinode_copy(a_fatfs, a_inum, &dentry, sect_is_alloc, a_fs_file); 
+    if (copy_result == TSK_OK) {
+        return 0;
+    }
+    else if (copy_result == TSK_COR) {
+        /* There was a Unicode conversion error on a string, but the rest 
+         * of the inode meta data is probably o.k., so report the error (if in 
+         * verbose mode), but also report a successful look up.*/
+        if (tsk_verbose) {
+            tsk_error_print(stderr);
+        }
+        tsk_error_reset();
+        return 0;
+    }
+    else {
+        return 1;
+    }
+}
+
+/**
+ * \internal
+ * Outputs file attributes for an exFAT directory entry/inode in 
+ * human-readable form.
+ *
+ * @param [in] a_fatfs Source file system for the directory entry.
+ * @param [in] a_inum Inode address associated with the directory entry.
+ * @param [in] a_hFile Handle of a file to which to write.
+ * @return 0 on success, 1 on failure, per TSK convention
+ */
+uint8_t
+exfatfs_istat_attr_flags(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum,  FILE *a_hFile)
+{
+    const char *func_name = "exfatfs_istat_attr_flags";
+    FATFS_DENTRY dentry;
+    EXFATFS_FILE_DIR_ENTRY *file_dentry = NULL;
+    uint16_t attr_flags = 0;
+
+    assert(a_fatfs != NULL);
+    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
+    assert(a_hFile != NULL);
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
+        fatfs_ptr_arg_is_null(a_hFile, "a_hFile", func_name) ||
+        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
+        return FATFS_FAIL; 
+    }
+
+    /* Load the bytes at the given inode address. */
+    if (fatfs_dentry_load(a_fatfs, (FATFS_DENTRY*)(&dentry), a_inum)) {
+        return FATFS_FAIL; 
+    }
+
+    /* Print the attributes. */
+    switch (dentry.data[0])
+    {
+    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL:
+    case EXFATFS_DIR_ENTRY_TYPE_EMPTY_VOLUME_LABEL:
+        tsk_fprintf(a_hFile, "Volume Label\n");
+        break;
+    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
+        tsk_fprintf(a_hFile, "Volume GUID\n");
+        break;
+    case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:     
+        tsk_fprintf(a_hFile, "Allocation Bitmap\n");
+        break;
+    case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:     
+        tsk_fprintf(a_hFile, "Up-Case Table\n");
+        break;
+    case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:     
+        tsk_fprintf(a_hFile, "TexFAT\n");
+        break;
+    case EXFATFS_DIR_ENTRY_TYPE_ACT:
+        tsk_fprintf(a_hFile, "Access Control Table\n");
+        break;
+    case EXFATFS_DIR_ENTRY_TYPE_FILE:
+    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE:
+        file_dentry = (EXFATFS_FILE_DIR_ENTRY*)&dentry;
+        attr_flags = tsk_getu16(a_fatfs->fs_info.endian, file_dentry->attrs);
+
+        if (attr_flags & FATFS_ATTR_DIRECTORY) {
+            tsk_fprintf(a_hFile, "Directory");
+        }
+        else {
+            tsk_fprintf(a_hFile, "File");
+        }
+
+        if (attr_flags & FATFS_ATTR_READONLY) {
+            tsk_fprintf(a_hFile, ", Read Only");
+        }
+
+        if (attr_flags & FATFS_ATTR_HIDDEN) {
+            tsk_fprintf(a_hFile, ", Hidden");
+        }
+
+        if (attr_flags & FATFS_ATTR_SYSTEM) {
+            tsk_fprintf(a_hFile, ", System");
+        }
+
+        if (attr_flags & FATFS_ATTR_ARCHIVE) {
+            tsk_fprintf(a_hFile, ", Archive");
+        }
+
+        tsk_fprintf(a_hFile, "\n");
+
+        break;
+    case EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM:
+    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM:
+        tsk_fprintf(a_hFile, "File Stream\n"); 
+        break;
+    case EXFATFS_DIR_ENTRY_TYPE_FILE_NAME:
+    case EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME:
+        tsk_fprintf(a_hFile, "File Name\n");
+        break;
+    default:
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr("%s: Inode %" PRIuINUM
+            " is not an exFAT directory entry", func_name, a_inum);
+        return FATFS_FAIL;
+    }
+
+    return FATFS_OK;
+}
+
+/**
+ * \internal
+ * Determine whether an exFAT directory entry should be included in an inode
+ *  walk.
+ *
+ * @param [in] a_fatfs Source file system for the directory entry.
+ * @param [in] a_inum Inode address associated with the directory entry.
+ * @param [in] a_dentry A directory entry buffer.
+ * @param [in] a_selection_flags The inode selection falgs for the inode walk.
+ * @param [in] a_cluster_is_alloc The allocation status of the cluster that
+ * contains the directory entry.
+ * @return 1 if the entry should be skipped, 0 otherwise
+ */
+uint8_t
+exfatfs_inode_walk_should_skip_dentry(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
+    FATFS_DENTRY *a_dentry, unsigned int a_selection_flags, 
+    int a_cluster_is_alloc)
+{
+    const char *func_name = "exfatfs_inode_walk_should_skip_dentry";
+    unsigned int dentry_flags = 0;
+    uint8_t i = 0;
+
+    assert(a_fatfs != NULL);
+    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
+    assert(a_dentry != NULL);
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
+        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name) ||
+        fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
+        return 1; 
+    }
+
+    /* Skip file stream and file name entries. For inode walks, these entries
+     * are handled with the file entry with which they are associated in a file
+     * entry set. */
+    if (a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM ||
+        a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_STREAM ||
+        a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME ||
+        a_dentry->data[0] == EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE_NAME) {
+        return 1;
+    }
+
+    // RJCTODO: This code probably needs to change. See explanatory TODO comment
+    // associated with the EXFATFS_DIR_ENTRY_TYPE_ENUM definition.
+    /* Assign an allocation status to the entry. Allocation status is 
+     * determined first by the allocation status of the cluster that contains
+     * the entry, then by the allocated status of the entry. */
+    if ((a_cluster_is_alloc) && (a_dentry->data[0] != EXFATFS_DIR_ENTRY_TYPE_UNALLOC_FILE)) {
+        dentry_flags = TSK_FS_META_FLAG_ALLOC;
+    }
+    else {
+        dentry_flags = TSK_FS_META_FLAG_UNALLOC;
+    }
+
+    /* Does the allocation status of the entry match that of the inode 
+     * selection flags? */
+    if ((a_selection_flags & dentry_flags) != dentry_flags) {
+        return 1;
+    }
+
+    /* If the inode selection flags call for only processing orphan files, 
+     * check whether or not this inode is in list of non-orphan files found via
+     * name walk. */
+    if ((dentry_flags & TSK_FS_META_FLAG_UNALLOC) &&
+        (a_selection_flags & TSK_FS_META_FLAG_ORPHAN) &&
+        (tsk_fs_dir_find_inum_named(&(a_fatfs->fs_info), a_inum))) {
+        return 1;
+    }
+
+    return 0;
 }
\ No newline at end of file
diff --git a/tsk/fs/fatfs.c b/tsk/fs/fatfs.c
index 1bfa3794a..a150f4fa4 100644
--- a/tsk/fs/fatfs.c
+++ b/tsk/fs/fatfs.c
@@ -1,817 +1,817 @@
-/*
-** The Sleuth Kit
-**
-** Copyright (c) 2013 Basis Technology Corp.  All rights reserved
-** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
-**
-** This software is distributed under the Common Public License 1.0
-**
-*/
-
-/**
- * \file fatfs.c
- * Contains the internal TSK FAT file system code to handle basic file system 
- * processing for opening file system, processing sectors, and directory entries. 
- */
-
-#include "tsk_fs_i.h"
-#include "tsk_fatfs.h"
-#include "tsk_fatxxfs.h"
-#include "tsk_exfatfs.h"
-
-/**
- * \internal
- * Open part of a disk image as a FAT file system. 
- *
- * @param a_img_info Disk image to analyze
- * @param a_offset Byte offset where FAT file system starts
- * @param a_ftype Specific type of FAT file system
- * @param a_test NOT USED
- * @returns NULL on error or if data is not a FAT file system
- */
-TSK_FS_INFO *
-fatfs_open(TSK_IMG_INFO *a_img_info, TSK_OFF_T a_offset, TSK_FS_TYPE_ENUM a_ftype, uint8_t a_test)
-{
-    const char *func_name = "fatfs_open";
-    FATFS_INFO *fatfs = NULL;
-    TSK_FS_INFO *fs = NULL;
-    TSK_OFF_T boot_sector_offset = 0;
-	int find_boot_sector_attempt = 0;
-    ssize_t bytes_read = 0;
-    FATFS_MASTER_BOOT_RECORD *bootSector;
-
-    tsk_error_reset();
-
-    if (TSK_FS_TYPE_ISFAT(a_ftype) == 0) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_ARG);
-        tsk_error_set_errstr("%s: Invalid FS Type", func_name);
-        return NULL;
-    }
-
-	// RJCTODO: Add validation of other parameters?
-
-	// Allocate an FATFS_INFO and initialize its generic TSK_FS_INFO members. 
-    if ((fatfs = (FATFS_INFO*)tsk_fs_malloc(sizeof(FATFS_INFO))) == NULL) {
-        return NULL;
-	}
-    fs = &(fatfs->fs_info);
-    fs->ftype = a_ftype;
-    fs->img_info = a_img_info;
-    fs->offset = a_offset;
-    fs->dev_bsize = a_img_info->sector_size;
-    fs->journ_inum = 0;
-    fs->tag = TSK_FS_INFO_TAG;
-
-	// Look for a FAT boot sector. Try up to three times because FAT32 and exFAT file systems have backup boot sectors.
-    for (find_boot_sector_attempt = 0; find_boot_sector_attempt < 3; ++find_boot_sector_attempt) {
-        if (find_boot_sector_attempt == 1) {
-			// The FATXX backup boot sector is located in sector 6, look there.
-            boot_sector_offset = 6 * fs->img_info->sector_size; 
-		}
-        else if (find_boot_sector_attempt == 2) {
-			// The exFAT backup boot sector is located in sector 12, look there.
-            boot_sector_offset = 12 * fs->img_info->sector_size;
-		}
-
-        // Read in the prospective boot sector. 
-        bytes_read = tsk_fs_read(fs, boot_sector_offset, fatfs->boot_sector_buffer, FATFS_MASTER_BOOT_RECORD_SIZE);
-        if (bytes_read != FATFS_MASTER_BOOT_RECORD_SIZE) {
-            if (bytes_read >= 0) {
-                tsk_error_reset();
-                tsk_error_set_errno(TSK_ERR_FS_READ);
-            }
-            tsk_error_set_errstr2("%s: boot sector", func_name); // RJCTODO: Is this a helpful error message?
-			free(fatfs);
-			return NULL;
-        }
-
-        // Check it out...
-        bootSector = (FATFS_MASTER_BOOT_RECORD*)fatfs->boot_sector_buffer;
-        if (tsk_fs_guessu16(fs, bootSector->magic, FATFS_FS_MAGIC) != 0) {
-            // No magic, look for a backup boot sector. 
-            if ((tsk_getu16(TSK_LIT_ENDIAN, bootSector->magic) == 0) && (find_boot_sector_attempt < 3)) {
-                continue;
-            }
-            else {
-                tsk_error_reset();
-                tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-                tsk_error_set_errstr("Not a FATFS file system (magic)");
-                if (tsk_verbose) {
-                    fprintf(stderr, "%s: Incorrect FATFS magic\n", func_name);
-				}
-				free(fatfs);
-				return NULL;
-            }
-        }
-        else {
-            // Found the magic.
-            fatfs->using_backup_boot_sector = boot_sector_offset > 0;
-            if (fatfs->using_backup_boot_sector && tsk_verbose) {
-				fprintf(stderr, "%s: Using backup boot sector\n", func_name);
-            }
-            break;
-        }
-    }
-
-	// Attempt to open the file system as one of the FAT types.
-    if ((a_ftype == TSK_FS_TYPE_FAT_DETECT && (fatxxfs_open(fatfs) == 0 || exfatfs_open(fatfs) == 0)) ||
-		(a_ftype == TSK_FS_TYPE_EXFAT && exfatfs_open(fatfs) == 0) ||
-		(fatxxfs_open(fatfs) == 0)) {
-    	return (TSK_FS_INFO*)fatfs;
-	} else {
-        free(fatfs);
-		return NULL;
-    }
-}
-
-/* TTL is 0 if the entry has not been used.  TTL of 1 means it was the
- * most recently used, and TTL of FATFS_FAT_CACHE_N means it was the least 
- * recently used.  This function has a LRU replacement algo
- *
- * Note: This routine assumes &fatfs->cache_lock is locked by the caller.
- */
-// return -1 on error, or cache index on success (0 to FATFS_FAT_CACHE_N)
-
-static int
-getFATCacheIdx(FATFS_INFO * fatfs, TSK_DADDR_T sect)
-{
-    int i, cidx;
-    ssize_t cnt;
-    TSK_FS_INFO *fs = (TSK_FS_INFO *) & fatfs->fs_info;
-
-    // see if we already have it in the cache
-    for (i = 0; i < FATFS_FAT_CACHE_N; i++) {
-        if ((fatfs->fatc_ttl[i] > 0) &&
-            (sect >= fatfs->fatc_addr[i]) &&
-            (sect < (fatfs->fatc_addr[i] + FATFS_FAT_CACHE_S))) {
-            int a;
-
-            // update the TTLs to push i to the front
-            for (a = 0; a < FATFS_FAT_CACHE_N; a++) {
-                if (fatfs->fatc_ttl[a] == 0)
-                    continue;
-
-                if (fatfs->fatc_ttl[a] < fatfs->fatc_ttl[i])
-                    fatfs->fatc_ttl[a]++;
-            }
-            fatfs->fatc_ttl[i] = 1;
-//          fprintf(stdout, "FAT Hit: %d\n", sect);
-//          fflush(stdout);
-            return i;
-        }
-    }
-
-//    fprintf(stdout, "FAT Miss: %d\n", (int)sect);
-//    fflush(stdout);
-
-    // Look for an unused entry or an entry with a TTL of FATFS_FAT_CACHE_N
-    cidx = 0;
-    for (i = 0; i < FATFS_FAT_CACHE_N; i++) {
-        if ((fatfs->fatc_ttl[i] == 0) ||
-            (fatfs->fatc_ttl[i] >= FATFS_FAT_CACHE_N)) {
-            cidx = i;
-        }
-    }
-//    fprintf(stdout, "FAT Removing: %d\n", (int)fatfs->fatc_addr[cidx]);
-    //   fflush(stdout);
-
-    // read the data
-    cnt =
-        tsk_fs_read(fs, sect * fs->block_size, fatfs->fatc_buf[cidx],
-        FATFS_FAT_CACHE_B);
-    if (cnt != FATFS_FAT_CACHE_B) {
-        if (cnt >= 0) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_READ);
-        }
-        tsk_error_set_errstr2("getFATCacheIdx: FAT: %" PRIuDADDR, sect);
-        return -1;
-    }
-
-    // update the TTLs
-    if (fatfs->fatc_ttl[cidx] == 0)     // special case for unused entry
-        fatfs->fatc_ttl[cidx] = FATFS_FAT_CACHE_N + 1;
-
-    for (i = 0; i < FATFS_FAT_CACHE_N; i++) {
-        if (fatfs->fatc_ttl[i] == 0)
-            continue;
-
-        if (fatfs->fatc_ttl[i] < fatfs->fatc_ttl[cidx])
-            fatfs->fatc_ttl[i]++;
-    }
-
-    fatfs->fatc_ttl[cidx] = 1;
-    fatfs->fatc_addr[cidx] = sect;
-
-    return cidx;
-}
-
-/*
- * Set *value to the entry in the File Allocation Table (FAT) 
- * for the given cluster
- *
- * *value is in clusters and may need to be coverted to
- * sectors by the calling function
- *
- * Invalid values in the FAT (i.e. greater than the largest
- * cluster have a value of 0 returned and a 0 return value.
- *
- * Return 1 on error and 0 on success
- */
-uint8_t
-fatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value)
-{
-    uint8_t *a_ptr;
-    uint16_t tmp16;
-    TSK_FS_INFO *fs = (TSK_FS_INFO *) & fatfs->fs_info;
-    TSK_DADDR_T sect, offs;
-    ssize_t cnt;
-    int cidx;
-
-    /* Sanity Check */
-    if (clust > fatfs->lastclust) {
-        /* silently ignore requests for the unclustered sectors... */
-        if ((clust == fatfs->lastclust + 1) &&
-            ((fatfs->firstclustsect + fatfs->csize * fatfs->clustcnt -
-                    1) != fs->last_block)) {
-            if (tsk_verbose)
-                tsk_fprintf(stderr,
-                    "fatfs_getFAT: Ignoring request for non-clustered sector\n");
-            return 0;
-        }
-
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_ARG);
-        tsk_error_set_errstr("fatfs_getFAT: invalid cluster address: %"
-            PRIuDADDR, clust);
-        return 1;
-    }
-
-    switch (fatfs->fs_info.ftype) {
-    case TSK_FS_TYPE_FAT12:
-        if (clust & 0xf000) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_ARG);
-            tsk_error_set_errstr
-                ("fatfs_getFAT: TSK_FS_TYPE_FAT12 Cluster %" PRIuDADDR
-                " too large", clust);
-            return 1;
-        }
-
-        /* id the sector in the FAT */
-        sect = fatfs->firstfatsect +
-            ((clust + (clust >> 1)) >> fatfs->ssize_sh);
-
-        tsk_take_lock(&fatfs->cache_lock);
-
-        /* Load the FAT if we don't have it */
-        // see if it is in the cache
-        if (-1 == (cidx = getFATCacheIdx(fatfs, sect))) {
-            tsk_release_lock(&fatfs->cache_lock);
-            return 1;
-        }
-
-        /* get the offset into the cache */
-        offs = ((sect - fatfs->fatc_addr[cidx]) << fatfs->ssize_sh) +
-            (clust + (clust >> 1)) % fatfs->ssize;
-
-        /* special case when the 12-bit value goes across the cache
-         * we load the cache to start at this sect.  The cache
-         * size must therefore be at least 2 sectors large 
-         */
-        if (offs == (FATFS_FAT_CACHE_B - 1)) {
-
-            // read the data -- TTLs will already have been updated
-            cnt =
-                tsk_fs_read(fs, sect * fs->block_size,
-                fatfs->fatc_buf[cidx], FATFS_FAT_CACHE_B);
-            if (cnt != FATFS_FAT_CACHE_B) {
-                tsk_release_lock(&fatfs->cache_lock);
-                if (cnt >= 0) {
-                    tsk_error_reset();
-                    tsk_error_set_errno(TSK_ERR_FS_READ);
-                }
-                tsk_error_set_errstr2
-                    ("fatfs_getFAT: TSK_FS_TYPE_FAT12 FAT overlap: %"
-                    PRIuDADDR, sect);
-                return 1;
-            }
-            fatfs->fatc_addr[cidx] = sect;
-
-            offs = (clust + (clust >> 1)) % fatfs->ssize;
-        }
-
-        /* get pointer to entry in current buffer */
-        a_ptr = (uint8_t *) fatfs->fatc_buf[cidx] + offs;
-
-        tmp16 = tsk_getu16(fs->endian, a_ptr);
-
-        tsk_release_lock(&fatfs->cache_lock);
-
-        /* slide it over if it is one of the odd clusters */
-        if (clust & 1)
-            tmp16 >>= 4;
-
-        *value = tmp16 & FATFS_12_MASK;
-
-        /* sanity check */
-        if ((*value > (fatfs->lastclust)) &&
-            (*value < (0x0ffffff7 & FATFS_12_MASK))) {
-            if (tsk_verbose)
-                tsk_fprintf(stderr,
-                    "fatfs_getFAT: TSK_FS_TYPE_FAT12 cluster (%" PRIuDADDR
-                    ") too large (%" PRIuDADDR ") - resetting\n", clust,
-                    *value);
-            *value = 0;
-        }
-        return 0;
-
-    case TSK_FS_TYPE_FAT16:
-        /* Get sector in FAT for cluster and load it if needed */
-        sect = fatfs->firstfatsect + ((clust << 1) >> fatfs->ssize_sh);
-
-        tsk_take_lock(&fatfs->cache_lock);
-
-        if (-1 == (cidx = getFATCacheIdx(fatfs, sect))) {
-            tsk_release_lock(&fatfs->cache_lock);
-            return 1;
-        }
-
-
-        /* get pointer to entry in the cache buffer */
-        a_ptr = (uint8_t *) fatfs->fatc_buf[cidx] +
-            ((sect - fatfs->fatc_addr[cidx]) << fatfs->ssize_sh) +
-            ((clust << 1) % fatfs->ssize);
-
-        *value = tsk_getu16(fs->endian, a_ptr) & FATFS_16_MASK;
-
-        tsk_release_lock(&fatfs->cache_lock);
-
-        /* sanity check */
-        if ((*value > (fatfs->lastclust)) &&
-            (*value < (0x0ffffff7 & FATFS_16_MASK))) {
-            if (tsk_verbose)
-                tsk_fprintf(stderr,
-                    "fatfs_getFAT: contents of TSK_FS_TYPE_FAT16 entry %"
-                    PRIuDADDR " too large - resetting\n", clust);
-            *value = 0;
-        }
-        return 0;
-
-    case TSK_FS_TYPE_FAT32:
-    case TSK_FS_TYPE_EXFAT:
-        /* Get sector in FAT for cluster and load if needed */
-        sect = fatfs->firstfatsect + ((clust << 2) >> fatfs->ssize_sh);
-
-        tsk_take_lock(&fatfs->cache_lock);
-
-        if (-1 == (cidx = getFATCacheIdx(fatfs, sect))) {
-            tsk_release_lock(&fatfs->cache_lock);
-            return 1;
-        }
-
-        /* get pointer to entry in current buffer */
-        a_ptr = (uint8_t *) fatfs->fatc_buf[cidx] +
-            ((sect - fatfs->fatc_addr[cidx]) << fatfs->ssize_sh) +
-            (clust << 2) % fatfs->ssize;
-
-        *value = tsk_getu32(fs->endian, a_ptr) & FATFS_32_MASK;
-
-        tsk_release_lock(&fatfs->cache_lock);
-
-        /* sanity check */
-        if ((*value > fatfs->lastclust) &&
-            (*value < (0x0ffffff7 & FATFS_32_MASK))) {
-            if (tsk_verbose)
-                tsk_fprintf(stderr,
-                    "fatfs_getFAT: contents of entry %" PRIuDADDR
-                    " too large - resetting\n", clust);
-
-            *value = 0;
-        }
-        return 0;
-
-    default:
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_ARG);
-        tsk_error_set_errstr("fatfs_getFAT: Unknown FAT type: %d",
-            fatfs->fs_info.ftype);
-        return 1;
-    }
-}
-
-/**************************************************************************
- *
- * BLOCK WALKING
- * 
- *************************************************************************/
-/* 
-** Walk the sectors of the partition. 
-**
-** NOTE: This is by SECTORS and not CLUSTERS
-** _flags: TSK_FS_BLOCK_FLAG_ALLOC, TSK_FS_BLOCK_FLAG_UNALLOC, TSK_FS_BLOCK_FLAG_META
-**  TSK_FS_BLOCK_FLAG_CONT
-**
-*/
-uint8_t
-fatfs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk,
-    TSK_DADDR_T a_end_blk, TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags,
-    TSK_FS_BLOCK_WALK_CB a_action, void *a_ptr)
-{
-    char *myname = "fatfs_block_walk";
-    FATFS_INFO *fatfs = (FATFS_INFO *) fs;
-    char *data_buf = NULL;
-    ssize_t cnt;
-    TSK_FS_BLOCK *fs_block;
-
-    TSK_DADDR_T addr;
-    int myflags;
-    unsigned int i;
-
-    // clean up any error messages that are lying around
-    tsk_error_reset();
-
-    /*
-     * Sanity checks.
-     */
-    if (a_start_blk < fs->first_block || a_start_blk > fs->last_block) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
-        tsk_error_set_errstr("%s: Start block: %" PRIuDADDR "", myname,
-            a_start_blk);
-        return 1;
-    }
-    if (a_end_blk < fs->first_block || a_end_blk > fs->last_block) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
-        tsk_error_set_errstr("%s: End block: %" PRIuDADDR "", myname,
-            a_end_blk);
-        return 1;
-    }
-
-    if (tsk_verbose)
-        tsk_fprintf(stderr,
-            "fatfs_block_walk: Block Walking %" PRIuDADDR " to %"
-            PRIuDADDR "\n", a_start_blk, a_end_blk);
-
-
-    /* Sanity check on a_flags -- make sure at least one ALLOC is set */
-    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC) == 0) &&
-        ((a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC) == 0)) {
-        a_flags |=
-            (TSK_FS_BLOCK_WALK_FLAG_ALLOC |
-            TSK_FS_BLOCK_WALK_FLAG_UNALLOC);
-    }
-    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_META) == 0) &&
-        ((a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT) == 0)) {
-        a_flags |=
-            (TSK_FS_BLOCK_WALK_FLAG_CONT | TSK_FS_BLOCK_WALK_FLAG_META);
-    }
-
-    if ((fs_block = tsk_fs_block_alloc(fs)) == NULL) {
-        return 1;
-    }
-
-    /* cycle through the sectors.  We do the sectors before the first
-     * cluster seperate from the data area */
-    addr = a_start_blk;
-
-    /* Before the data area beings (FAT, root directory etc.) */
-    if ((a_start_blk < fatfs->firstclustsect)
-        && (a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC)) {
-
-        if (tsk_verbose)
-            tsk_fprintf(stderr,
-                "fatfs_block_walk: Walking non-data area (pre %"
-                PRIuDADDR "\n)", fatfs->firstclustsect);
-
-        if ((data_buf = (char *) tsk_malloc(fs->block_size * 8)) == NULL) {
-            tsk_fs_block_free(fs_block);
-            return 1;
-        }
-
-        /* Read 8 sectors at a time to be faster */
-        for (; addr < fatfs->firstclustsect && addr <= a_end_blk;) {
-
-            if ((a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY) == 0) {
-                cnt =
-                    tsk_fs_read_block(fs, addr, data_buf, fs->block_size * 8);
-                if (cnt != fs->block_size * 8) {
-                    if (cnt >= 0) {
-                        tsk_error_reset();
-                        tsk_error_set_errno(TSK_ERR_FS_READ);
-                    }
-                    tsk_error_set_errstr2
-                        ("fatfs_block_walk: pre-data area block: %" PRIuDADDR,
-                        addr);
-                    free(data_buf);
-                    tsk_fs_block_free(fs_block);
-                    return 1;
-                }
-            }
-
-            /* Process the sectors until we get to the clusters, 
-             * end of target, or end of buffer */
-            for (i = 0;
-                i < 8 && (addr) <= a_end_blk
-                && (addr) < fatfs->firstclustsect; i++, addr++) {
-                int retval;
-
-                myflags = TSK_FS_BLOCK_FLAG_ALLOC;
-
-                /* stuff before the first data sector is the 
-                 * FAT and boot sector */
-                if (addr < fatfs->firstdatasect)
-                    myflags |= TSK_FS_BLOCK_FLAG_META;
-                /* This must be the root directory for FAT12/16 */
-                else
-                    myflags |= TSK_FS_BLOCK_FLAG_CONT;
-
-                // test this sector (we already tested ALLOC)
-                if ((myflags & TSK_FS_BLOCK_FLAG_META)
-                    && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_META)))
-                    continue;
-                else if ((myflags & TSK_FS_BLOCK_FLAG_CONT)
-                    && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT)))
-                    continue;
-
-                if (a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY)
-                    myflags |= TSK_FS_BLOCK_FLAG_AONLY;
-
-                tsk_fs_block_set(fs, fs_block, addr,
-                    myflags | TSK_FS_BLOCK_FLAG_RAW,
-                    &data_buf[i * fs->block_size]);
-
-                retval = a_action(fs_block, a_ptr);
-                if (retval == TSK_WALK_STOP) {
-                    free(data_buf);
-                    tsk_fs_block_free(fs_block);
-                    return 0;
-                }
-                else if (retval == TSK_WALK_ERROR) {
-                    free(data_buf);
-                    tsk_fs_block_free(fs_block);
-                    return 1;
-                }
-            }
-        }
-
-        free(data_buf);
-
-        /* Was that it? */
-        if (addr >= a_end_blk) {
-            tsk_fs_block_free(fs_block);
-            return 0;
-        }
-    }
-    /* Reset the first sector to the start of the data area if we did
-     * not examine it - the next calculation will screw up otherwise */
-    else if (addr < fatfs->firstclustsect) {
-        addr = fatfs->firstclustsect;
-    }
-
-
-    /* Now we read in the clusters in cluster-sized chunks,
-     * sectors are too small
-     */
-
-    /* Determine the base sector of the cluster where the first 
-     * sector is located */
-    addr = FATFS_CLUST_2_SECT(fatfs, (FATFS_SECT_2_CLUST(fatfs, addr)));
-
-    if ((data_buf = tsk_malloc(fs->block_size * fatfs->csize)) == NULL) {
-        tsk_fs_block_free(fs_block);
-        return 1;
-    }
-
-    if (tsk_verbose)
-        tsk_fprintf(stderr,
-            "fatfs_block_walk: Walking data area blocks (%" PRIuDADDR
-            " to %" PRIuDADDR ")\n", addr, a_end_blk);
-
-    for (; addr <= a_end_blk; addr += fatfs->csize) {
-        int retval;
-        size_t read_size;
-
-        /* Identify its allocation status */
-        retval = fatfs_is_sectalloc(fatfs, addr);
-        if (retval == -1) {
-            free(data_buf);
-            tsk_fs_block_free(fs_block);
-            return 1;
-        }
-        else if (retval == 1) {
-            myflags = TSK_FS_BLOCK_FLAG_ALLOC;
-        }
-        else {
-            myflags = TSK_FS_BLOCK_FLAG_UNALLOC;
-        }
-
-        /* At this point, there should be no more meta - just content */
-        myflags |= TSK_FS_BLOCK_FLAG_CONT;
-
-        // test if we should call the callback with this one
-        if ((myflags & TSK_FS_BLOCK_FLAG_CONT)
-            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT)))
-            continue;
-        else if ((myflags & TSK_FS_BLOCK_FLAG_ALLOC)
-            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC)))
-            continue;
-        else if ((myflags & TSK_FS_BLOCK_FLAG_UNALLOC)
-            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC)))
-            continue;
-
-        if (a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY)
-            myflags |= TSK_FS_BLOCK_FLAG_AONLY;
-
-        
-        /* The final cluster may not be full */
-        if (a_end_blk - addr + 1 < fatfs->csize)
-            read_size = (size_t) (a_end_blk - addr + 1);
-        else
-            read_size = fatfs->csize;
-
-        if ((a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY) == 0) {
-            cnt = tsk_fs_read_block
-                (fs, addr, data_buf, fs->block_size * read_size);
-            if (cnt != fs->block_size * read_size) {
-                if (cnt >= 0) {
-                    tsk_error_reset();
-                    tsk_error_set_errno(TSK_ERR_FS_READ);
-                }
-                tsk_error_set_errstr2("fatfs_block_walk: block: %" PRIuDADDR,
-                    addr);
-                free(data_buf);
-                tsk_fs_block_free(fs_block);
-                return 1;
-            }
-        }
-
-        /* go through each sector in the cluster */
-        for (i = 0; i < read_size; i++) {
-            int retval;
-
-            if (addr + i < a_start_blk)
-                continue;
-            else if (addr + i > a_end_blk)
-                break;
-
-            tsk_fs_block_set(fs, fs_block, addr + i,
-                myflags | TSK_FS_BLOCK_FLAG_RAW,
-                &data_buf[i * fs->block_size]);
-
-            retval = a_action(fs_block, a_ptr);
-            if (retval == TSK_WALK_STOP) {
-                free(data_buf);
-                tsk_fs_block_free(fs_block);
-                return 0;
-            }
-            else if (retval == TSK_WALK_ERROR) {
-                free(data_buf);
-                tsk_fs_block_free(fs_block);
-                return 1;
-            }
-        }
-    }
-
-    free(data_buf);
-    tsk_fs_block_free(fs_block);
-    return 0;
-}
-
-TSK_FS_BLOCK_FLAG_ENUM
-fatfs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
-{
-    FATFS_INFO *fatfs = (FATFS_INFO *) a_fs;
-    int flags = 0;
-
-    // FATs and boot sector
-    if (a_addr < fatfs->firstdatasect) {
-        flags = TSK_FS_BLOCK_FLAG_META | TSK_FS_BLOCK_FLAG_ALLOC;
-    }
-    // root directory for FAT12/16
-    else if (a_addr < fatfs->firstclustsect) {
-        flags = TSK_FS_BLOCK_FLAG_CONT | TSK_FS_BLOCK_FLAG_ALLOC;
-    }
-    else {
-        int retval;
-        flags = TSK_FS_BLOCK_FLAG_CONT;
-
-        /* Identify its allocation status */
-        retval = fatfs_is_sectalloc(fatfs, a_addr);
-        if (retval != -1) {
-            if (retval == 1)
-                flags |= TSK_FS_BLOCK_FLAG_ALLOC;
-            else
-                flags |= TSK_FS_BLOCK_FLAG_UNALLOC;
-        }
-    }
-    return (TSK_FS_BLOCK_FLAG_ENUM)flags;
-}
-
-/* 
- * Identifies if a sector is allocated
- *
- * If it is less than the data area, then it is allocated
- * else the FAT table is consulted
- *
- * Return 1 if allocated, 0 if unallocated, and -1 if error 
- */
-int8_t
-fatfs_is_sectalloc(FATFS_INFO * fatfs, TSK_DADDR_T sect)
-{
-    TSK_FS_INFO *fs = (TSK_FS_INFO *) fatfs;
-    /* If less than the first cluster sector, then it is allocated 
-     * otherwise check the FAT
-     */
-    if (sect < fatfs->firstclustsect)
-        return 1;
-
-    /* If we are in the unused area, then we are "unalloc" */
-    if ((sect <= fs->last_block) &&
-        (sect >= (fatfs->firstclustsect + fatfs->csize * fatfs->clustcnt)))
-        return 0;
-
-    return fatfs->is_cluster_alloc(fatfs, FATFS_SECT_2_CLUST(fatfs, sect));
-}
-
-/* return 1 on error and 0 on success */
-uint8_t
-fatfs_jopen(TSK_FS_INFO * fs, TSK_INUM_T inum)
-{
-    tsk_error_reset();
-    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
-    tsk_error_set_errstr("FAT does not have a journal\n");
-    return 1;
-}
-
-/* return 1 on error and 0 on success */
-uint8_t
-fatfs_fscheck(TSK_FS_INFO * fs, FILE * hFile)
-{
-    tsk_error_reset();
-    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
-    tsk_error_set_errstr("fscheck not implemented for FAT yet");
-    return 1;
-
-    /* Check that allocated dentries point to start of allcated cluster chain */
-
-
-    /* Size of file is consistent with cluster chain length */
-
-
-    /* Allocated cluster chains have a corresponding alloc dentry */
-
-
-    /* Non file dentries have no clusters */
-
-
-    /* Only one volume label */
-
-
-    /* Dump Bad Sector Addresses */
-
-
-    /* Dump unused sector addresses 
-     * Reserved area, end of FAT, end of Data Area */
-}
-
-/* return 1 on error and 0 on success */
-uint8_t
-fatfs_jentry_walk(TSK_FS_INFO * fs, int a_flags,
-    TSK_FS_JENTRY_WALK_CB a_action, void *a_ptr)
-{
-    tsk_error_reset();
-    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
-    tsk_error_set_errstr("FAT does not have a journal\n");
-    return 1;
-}
-
-/* return 1 on error and 0 on success */
-uint8_t
-fatfs_jblk_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T end,
-    int a_flags, TSK_FS_JBLK_WALK_CB a_action, void *a_ptr)
-{
-    tsk_error_reset();
-    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
-    tsk_error_set_errstr("FAT does not have a journal\n");
-    return 1;
-}
-
-/* fatfs_close - close an fatfs file system */
-void
-fatfs_close(TSK_FS_INFO *fs)
-{
-    FATFS_INFO *fatfs = (FATFS_INFO *) fs;
- 
-    fatfs_dir_buf_free(fatfs);
-
-    fs->tag = 0;
-	memset(fatfs->boot_sector_buffer, 0, FATFS_MASTER_BOOT_RECORD_SIZE);
-    tsk_deinit_lock(&fatfs->cache_lock);
-    tsk_deinit_lock(&fatfs->dir_lock);
-	
-    tsk_fs_free(fs);
-}
-
+/*
+** The Sleuth Kit
+**
+** Copyright (c) 2013 Basis Technology Corp.  All rights reserved
+** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
+**
+** This software is distributed under the Common Public License 1.0
+**
+*/
+
+/**
+ * \file fatfs.c
+ * Contains the internal TSK FAT file system code to handle basic file system 
+ * processing for opening file system, processing sectors, and directory entries. 
+ */
+
+#include "tsk_fs_i.h"
+#include "tsk_fatfs.h"
+#include "tsk_fatxxfs.h"
+#include "tsk_exfatfs.h"
+
+/**
+ * \internal
+ * Open part of a disk image as a FAT file system. 
+ *
+ * @param a_img_info Disk image to analyze
+ * @param a_offset Byte offset where FAT file system starts
+ * @param a_ftype Specific type of FAT file system
+ * @param a_test NOT USED
+ * @returns NULL on error or if data is not a FAT file system
+ */
+TSK_FS_INFO *
+fatfs_open(TSK_IMG_INFO *a_img_info, TSK_OFF_T a_offset, TSK_FS_TYPE_ENUM a_ftype, uint8_t a_test)
+{
+    const char *func_name = "fatfs_open";
+    FATFS_INFO *fatfs = NULL;
+    TSK_FS_INFO *fs = NULL;
+    TSK_OFF_T boot_sector_offset = 0;
+	int find_boot_sector_attempt = 0;
+    ssize_t bytes_read = 0;
+    FATFS_MASTER_BOOT_RECORD *bootSector;
+
+    tsk_error_reset();
+
+    if (TSK_FS_TYPE_ISFAT(a_ftype) == 0) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("%s: Invalid FS Type", func_name);
+        return NULL;
+    }
+
+	// RJCTODO: Add validation of other parameters?
+
+	// Allocate an FATFS_INFO and initialize its generic TSK_FS_INFO members. 
+    if ((fatfs = (FATFS_INFO*)tsk_fs_malloc(sizeof(FATFS_INFO))) == NULL) {
+        return NULL;
+	}
+    fs = &(fatfs->fs_info);
+    fs->ftype = a_ftype;
+    fs->img_info = a_img_info;
+    fs->offset = a_offset;
+    fs->dev_bsize = a_img_info->sector_size;
+    fs->journ_inum = 0;
+    fs->tag = TSK_FS_INFO_TAG;
+
+	// Look for a FAT boot sector. Try up to three times because FAT32 and exFAT file systems have backup boot sectors.
+    for (find_boot_sector_attempt = 0; find_boot_sector_attempt < 3; ++find_boot_sector_attempt) {
+        if (find_boot_sector_attempt == 1) {
+			// The FATXX backup boot sector is located in sector 6, look there.
+            boot_sector_offset = 6 * fs->img_info->sector_size; 
+		}
+        else if (find_boot_sector_attempt == 2) {
+			// The exFAT backup boot sector is located in sector 12, look there.
+            boot_sector_offset = 12 * fs->img_info->sector_size;
+		}
+
+        // Read in the prospective boot sector. 
+        bytes_read = tsk_fs_read(fs, boot_sector_offset, fatfs->boot_sector_buffer, FATFS_MASTER_BOOT_RECORD_SIZE);
+        if (bytes_read != FATFS_MASTER_BOOT_RECORD_SIZE) {
+            if (bytes_read >= 0) {
+                tsk_error_reset();
+                tsk_error_set_errno(TSK_ERR_FS_READ);
+            }
+            tsk_error_set_errstr2("%s: boot sector", func_name); // RJCTODO: Is this a helpful error message?
+			free(fatfs);
+			return NULL;
+        }
+
+        // Check it out...
+        bootSector = (FATFS_MASTER_BOOT_RECORD*)fatfs->boot_sector_buffer;
+        if (tsk_fs_guessu16(fs, bootSector->magic, FATFS_FS_MAGIC) != 0) {
+            // No magic, look for a backup boot sector. 
+            if ((tsk_getu16(TSK_LIT_ENDIAN, bootSector->magic) == 0) && (find_boot_sector_attempt < 3)) {
+                continue;
+            }
+            else {
+                tsk_error_reset();
+                tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+                tsk_error_set_errstr("Not a FATFS file system (magic)");
+                if (tsk_verbose) {
+                    fprintf(stderr, "%s: Incorrect FATFS magic\n", func_name);
+				}
+				free(fatfs);
+				return NULL;
+            }
+        }
+        else {
+            // Found the magic.
+            fatfs->using_backup_boot_sector = boot_sector_offset > 0;
+            if (fatfs->using_backup_boot_sector && tsk_verbose) {
+				fprintf(stderr, "%s: Using backup boot sector\n", func_name);
+            }
+            break;
+        }
+    }
+
+	// Attempt to open the file system as one of the FAT types.
+    if ((a_ftype == TSK_FS_TYPE_FAT_DETECT && (fatxxfs_open(fatfs) == 0 || exfatfs_open(fatfs) == 0)) ||
+		(a_ftype == TSK_FS_TYPE_EXFAT && exfatfs_open(fatfs) == 0) ||
+		(fatxxfs_open(fatfs) == 0)) {
+    	return (TSK_FS_INFO*)fatfs;
+	} else {
+        free(fatfs);
+		return NULL;
+    }
+}
+
+/* TTL is 0 if the entry has not been used.  TTL of 1 means it was the
+ * most recently used, and TTL of FATFS_FAT_CACHE_N means it was the least 
+ * recently used.  This function has a LRU replacement algo
+ *
+ * Note: This routine assumes &fatfs->cache_lock is locked by the caller.
+ */
+// return -1 on error, or cache index on success (0 to FATFS_FAT_CACHE_N)
+
+static int
+getFATCacheIdx(FATFS_INFO * fatfs, TSK_DADDR_T sect)
+{
+    int i, cidx;
+    ssize_t cnt;
+    TSK_FS_INFO *fs = (TSK_FS_INFO *) & fatfs->fs_info;
+
+    // see if we already have it in the cache
+    for (i = 0; i < FATFS_FAT_CACHE_N; i++) {
+        if ((fatfs->fatc_ttl[i] > 0) &&
+            (sect >= fatfs->fatc_addr[i]) &&
+            (sect < (fatfs->fatc_addr[i] + FATFS_FAT_CACHE_S))) {
+            int a;
+
+            // update the TTLs to push i to the front
+            for (a = 0; a < FATFS_FAT_CACHE_N; a++) {
+                if (fatfs->fatc_ttl[a] == 0)
+                    continue;
+
+                if (fatfs->fatc_ttl[a] < fatfs->fatc_ttl[i])
+                    fatfs->fatc_ttl[a]++;
+            }
+            fatfs->fatc_ttl[i] = 1;
+//          fprintf(stdout, "FAT Hit: %d\n", sect);
+//          fflush(stdout);
+            return i;
+        }
+    }
+
+//    fprintf(stdout, "FAT Miss: %d\n", (int)sect);
+//    fflush(stdout);
+
+    // Look for an unused entry or an entry with a TTL of FATFS_FAT_CACHE_N
+    cidx = 0;
+    for (i = 0; i < FATFS_FAT_CACHE_N; i++) {
+        if ((fatfs->fatc_ttl[i] == 0) ||
+            (fatfs->fatc_ttl[i] >= FATFS_FAT_CACHE_N)) {
+            cidx = i;
+        }
+    }
+//    fprintf(stdout, "FAT Removing: %d\n", (int)fatfs->fatc_addr[cidx]);
+    //   fflush(stdout);
+
+    // read the data
+    cnt =
+        tsk_fs_read(fs, sect * fs->block_size, fatfs->fatc_buf[cidx],
+        FATFS_FAT_CACHE_B);
+    if (cnt != FATFS_FAT_CACHE_B) {
+        if (cnt >= 0) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_READ);
+        }
+        tsk_error_set_errstr2("getFATCacheIdx: FAT: %" PRIuDADDR, sect);
+        return -1;
+    }
+
+    // update the TTLs
+    if (fatfs->fatc_ttl[cidx] == 0)     // special case for unused entry
+        fatfs->fatc_ttl[cidx] = FATFS_FAT_CACHE_N + 1;
+
+    for (i = 0; i < FATFS_FAT_CACHE_N; i++) {
+        if (fatfs->fatc_ttl[i] == 0)
+            continue;
+
+        if (fatfs->fatc_ttl[i] < fatfs->fatc_ttl[cidx])
+            fatfs->fatc_ttl[i]++;
+    }
+
+    fatfs->fatc_ttl[cidx] = 1;
+    fatfs->fatc_addr[cidx] = sect;
+
+    return cidx;
+}
+
+/*
+ * Set *value to the entry in the File Allocation Table (FAT) 
+ * for the given cluster
+ *
+ * *value is in clusters and may need to be coverted to
+ * sectors by the calling function
+ *
+ * Invalid values in the FAT (i.e. greater than the largest
+ * cluster have a value of 0 returned and a 0 return value.
+ *
+ * Return 1 on error and 0 on success
+ */
+uint8_t
+fatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value)
+{
+    uint8_t *a_ptr;
+    uint16_t tmp16;
+    TSK_FS_INFO *fs = (TSK_FS_INFO *) & fatfs->fs_info;
+    TSK_DADDR_T sect, offs;
+    ssize_t cnt;
+    int cidx;
+
+    /* Sanity Check */
+    if (clust > fatfs->lastclust) {
+        /* silently ignore requests for the unclustered sectors... */
+        if ((clust == fatfs->lastclust + 1) &&
+            ((fatfs->firstclustsect + fatfs->csize * fatfs->clustcnt -
+                    1) != fs->last_block)) {
+            if (tsk_verbose)
+                tsk_fprintf(stderr,
+                    "fatfs_getFAT: Ignoring request for non-clustered sector\n");
+            return 0;
+        }
+
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("fatfs_getFAT: invalid cluster address: %"
+            PRIuDADDR, clust);
+        return 1;
+    }
+
+    switch (fatfs->fs_info.ftype) {
+    case TSK_FS_TYPE_FAT12:
+        if (clust & 0xf000) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_ARG);
+            tsk_error_set_errstr
+                ("fatfs_getFAT: TSK_FS_TYPE_FAT12 Cluster %" PRIuDADDR
+                " too large", clust);
+            return 1;
+        }
+
+        /* id the sector in the FAT */
+        sect = fatfs->firstfatsect +
+            ((clust + (clust >> 1)) >> fatfs->ssize_sh);
+
+        tsk_take_lock(&fatfs->cache_lock);
+
+        /* Load the FAT if we don't have it */
+        // see if it is in the cache
+        if (-1 == (cidx = getFATCacheIdx(fatfs, sect))) {
+            tsk_release_lock(&fatfs->cache_lock);
+            return 1;
+        }
+
+        /* get the offset into the cache */
+        offs = ((sect - fatfs->fatc_addr[cidx]) << fatfs->ssize_sh) +
+            (clust + (clust >> 1)) % fatfs->ssize;
+
+        /* special case when the 12-bit value goes across the cache
+         * we load the cache to start at this sect.  The cache
+         * size must therefore be at least 2 sectors large 
+         */
+        if (offs == (FATFS_FAT_CACHE_B - 1)) {
+
+            // read the data -- TTLs will already have been updated
+            cnt =
+                tsk_fs_read(fs, sect * fs->block_size,
+                fatfs->fatc_buf[cidx], FATFS_FAT_CACHE_B);
+            if (cnt != FATFS_FAT_CACHE_B) {
+                tsk_release_lock(&fatfs->cache_lock);
+                if (cnt >= 0) {
+                    tsk_error_reset();
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
+                }
+                tsk_error_set_errstr2
+                    ("fatfs_getFAT: TSK_FS_TYPE_FAT12 FAT overlap: %"
+                    PRIuDADDR, sect);
+                return 1;
+            }
+            fatfs->fatc_addr[cidx] = sect;
+
+            offs = (clust + (clust >> 1)) % fatfs->ssize;
+        }
+
+        /* get pointer to entry in current buffer */
+        a_ptr = (uint8_t *) fatfs->fatc_buf[cidx] + offs;
+
+        tmp16 = tsk_getu16(fs->endian, a_ptr);
+
+        tsk_release_lock(&fatfs->cache_lock);
+
+        /* slide it over if it is one of the odd clusters */
+        if (clust & 1)
+            tmp16 >>= 4;
+
+        *value = tmp16 & FATFS_12_MASK;
+
+        /* sanity check */
+        if ((*value > (fatfs->lastclust)) &&
+            (*value < (0x0ffffff7 & FATFS_12_MASK))) {
+            if (tsk_verbose)
+                tsk_fprintf(stderr,
+                    "fatfs_getFAT: TSK_FS_TYPE_FAT12 cluster (%" PRIuDADDR
+                    ") too large (%" PRIuDADDR ") - resetting\n", clust,
+                    *value);
+            *value = 0;
+        }
+        return 0;
+
+    case TSK_FS_TYPE_FAT16:
+        /* Get sector in FAT for cluster and load it if needed */
+        sect = fatfs->firstfatsect + ((clust << 1) >> fatfs->ssize_sh);
+
+        tsk_take_lock(&fatfs->cache_lock);
+
+        if (-1 == (cidx = getFATCacheIdx(fatfs, sect))) {
+            tsk_release_lock(&fatfs->cache_lock);
+            return 1;
+        }
+
+
+        /* get pointer to entry in the cache buffer */
+        a_ptr = (uint8_t *) fatfs->fatc_buf[cidx] +
+            ((sect - fatfs->fatc_addr[cidx]) << fatfs->ssize_sh) +
+            ((clust << 1) % fatfs->ssize);
+
+        *value = tsk_getu16(fs->endian, a_ptr) & FATFS_16_MASK;
+
+        tsk_release_lock(&fatfs->cache_lock);
+
+        /* sanity check */
+        if ((*value > (fatfs->lastclust)) &&
+            (*value < (0x0ffffff7 & FATFS_16_MASK))) {
+            if (tsk_verbose)
+                tsk_fprintf(stderr,
+                    "fatfs_getFAT: contents of TSK_FS_TYPE_FAT16 entry %"
+                    PRIuDADDR " too large - resetting\n", clust);
+            *value = 0;
+        }
+        return 0;
+
+    case TSK_FS_TYPE_FAT32:
+    case TSK_FS_TYPE_EXFAT:
+        /* Get sector in FAT for cluster and load if needed */
+        sect = fatfs->firstfatsect + ((clust << 2) >> fatfs->ssize_sh);
+
+        tsk_take_lock(&fatfs->cache_lock);
+
+        if (-1 == (cidx = getFATCacheIdx(fatfs, sect))) {
+            tsk_release_lock(&fatfs->cache_lock);
+            return 1;
+        }
+
+        /* get pointer to entry in current buffer */
+        a_ptr = (uint8_t *) fatfs->fatc_buf[cidx] +
+            ((sect - fatfs->fatc_addr[cidx]) << fatfs->ssize_sh) +
+            (clust << 2) % fatfs->ssize;
+
+        *value = tsk_getu32(fs->endian, a_ptr) & FATFS_32_MASK;
+
+        tsk_release_lock(&fatfs->cache_lock);
+
+        /* sanity check */
+        if ((*value > fatfs->lastclust) &&
+            (*value < (0x0ffffff7 & FATFS_32_MASK))) {
+            if (tsk_verbose)
+                tsk_fprintf(stderr,
+                    "fatfs_getFAT: contents of entry %" PRIuDADDR
+                    " too large - resetting\n", clust);
+
+            *value = 0;
+        }
+        return 0;
+
+    default:
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("fatfs_getFAT: Unknown FAT type: %d",
+            fatfs->fs_info.ftype);
+        return 1;
+    }
+}
+
+/**************************************************************************
+ *
+ * BLOCK WALKING
+ * 
+ *************************************************************************/
+/* 
+** Walk the sectors of the partition. 
+**
+** NOTE: This is by SECTORS and not CLUSTERS
+** _flags: TSK_FS_BLOCK_FLAG_ALLOC, TSK_FS_BLOCK_FLAG_UNALLOC, TSK_FS_BLOCK_FLAG_META
+**  TSK_FS_BLOCK_FLAG_CONT
+**
+*/
+uint8_t
+fatfs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk,
+    TSK_DADDR_T a_end_blk, TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags,
+    TSK_FS_BLOCK_WALK_CB a_action, void *a_ptr)
+{
+    char *myname = "fatfs_block_walk";
+    FATFS_INFO *fatfs = (FATFS_INFO *) fs;
+    char *data_buf = NULL;
+    ssize_t cnt;
+    TSK_FS_BLOCK *fs_block;
+
+    TSK_DADDR_T addr;
+    int myflags;
+    unsigned int i;
+
+    // clean up any error messages that are lying around
+    tsk_error_reset();
+
+    /*
+     * Sanity checks.
+     */
+    if (a_start_blk < fs->first_block || a_start_blk > fs->last_block) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr("%s: Start block: %" PRIuDADDR "", myname,
+            a_start_blk);
+        return 1;
+    }
+    if (a_end_blk < fs->first_block || a_end_blk > fs->last_block) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr("%s: End block: %" PRIuDADDR "", myname,
+            a_end_blk);
+        return 1;
+    }
+
+    if (tsk_verbose)
+        tsk_fprintf(stderr,
+            "fatfs_block_walk: Block Walking %" PRIuDADDR " to %"
+            PRIuDADDR "\n", a_start_blk, a_end_blk);
+
+
+    /* Sanity check on a_flags -- make sure at least one ALLOC is set */
+    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC) == 0) &&
+        ((a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC) == 0)) {
+        a_flags |=
+            (TSK_FS_BLOCK_WALK_FLAG_ALLOC |
+            TSK_FS_BLOCK_WALK_FLAG_UNALLOC);
+    }
+    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_META) == 0) &&
+        ((a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT) == 0)) {
+        a_flags |=
+            (TSK_FS_BLOCK_WALK_FLAG_CONT | TSK_FS_BLOCK_WALK_FLAG_META);
+    }
+
+    if ((fs_block = tsk_fs_block_alloc(fs)) == NULL) {
+        return 1;
+    }
+
+    /* cycle through the sectors.  We do the sectors before the first
+     * cluster seperate from the data area */
+    addr = a_start_blk;
+
+    /* Before the data area beings (FAT, root directory etc.) */
+    if ((a_start_blk < fatfs->firstclustsect)
+        && (a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC)) {
+
+        if (tsk_verbose)
+            tsk_fprintf(stderr,
+                "fatfs_block_walk: Walking non-data area (pre %"
+                PRIuDADDR "\n)", fatfs->firstclustsect);
+
+        if ((data_buf = (char *) tsk_malloc(fs->block_size * 8)) == NULL) {
+            tsk_fs_block_free(fs_block);
+            return 1;
+        }
+
+        /* Read 8 sectors at a time to be faster */
+        for (; addr < fatfs->firstclustsect && addr <= a_end_blk;) {
+
+            if ((a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY) == 0) {
+                cnt =
+                    tsk_fs_read_block(fs, addr, data_buf, fs->block_size * 8);
+                if (cnt != fs->block_size * 8) {
+                    if (cnt >= 0) {
+                        tsk_error_reset();
+                        tsk_error_set_errno(TSK_ERR_FS_READ);
+                    }
+                    tsk_error_set_errstr2
+                        ("fatfs_block_walk: pre-data area block: %" PRIuDADDR,
+                        addr);
+                    free(data_buf);
+                    tsk_fs_block_free(fs_block);
+                    return 1;
+                }
+            }
+
+            /* Process the sectors until we get to the clusters, 
+             * end of target, or end of buffer */
+            for (i = 0;
+                i < 8 && (addr) <= a_end_blk
+                && (addr) < fatfs->firstclustsect; i++, addr++) {
+                int retval;
+
+                myflags = TSK_FS_BLOCK_FLAG_ALLOC;
+
+                /* stuff before the first data sector is the 
+                 * FAT and boot sector */
+                if (addr < fatfs->firstdatasect)
+                    myflags |= TSK_FS_BLOCK_FLAG_META;
+                /* This must be the root directory for FAT12/16 */
+                else
+                    myflags |= TSK_FS_BLOCK_FLAG_CONT;
+
+                // test this sector (we already tested ALLOC)
+                if ((myflags & TSK_FS_BLOCK_FLAG_META)
+                    && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_META)))
+                    continue;
+                else if ((myflags & TSK_FS_BLOCK_FLAG_CONT)
+                    && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT)))
+                    continue;
+
+                if (a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY)
+                    myflags |= TSK_FS_BLOCK_FLAG_AONLY;
+
+                tsk_fs_block_set(fs, fs_block, addr,
+                    myflags | TSK_FS_BLOCK_FLAG_RAW,
+                    &data_buf[i * fs->block_size]);
+
+                retval = a_action(fs_block, a_ptr);
+                if (retval == TSK_WALK_STOP) {
+                    free(data_buf);
+                    tsk_fs_block_free(fs_block);
+                    return 0;
+                }
+                else if (retval == TSK_WALK_ERROR) {
+                    free(data_buf);
+                    tsk_fs_block_free(fs_block);
+                    return 1;
+                }
+            }
+        }
+
+        free(data_buf);
+
+        /* Was that it? */
+        if (addr >= a_end_blk) {
+            tsk_fs_block_free(fs_block);
+            return 0;
+        }
+    }
+    /* Reset the first sector to the start of the data area if we did
+     * not examine it - the next calculation will screw up otherwise */
+    else if (addr < fatfs->firstclustsect) {
+        addr = fatfs->firstclustsect;
+    }
+
+
+    /* Now we read in the clusters in cluster-sized chunks,
+     * sectors are too small
+     */
+
+    /* Determine the base sector of the cluster where the first 
+     * sector is located */
+    addr = FATFS_CLUST_2_SECT(fatfs, (FATFS_SECT_2_CLUST(fatfs, addr)));
+
+    if ((data_buf = tsk_malloc(fs->block_size * fatfs->csize)) == NULL) {
+        tsk_fs_block_free(fs_block);
+        return 1;
+    }
+
+    if (tsk_verbose)
+        tsk_fprintf(stderr,
+            "fatfs_block_walk: Walking data area blocks (%" PRIuDADDR
+            " to %" PRIuDADDR ")\n", addr, a_end_blk);
+
+    for (; addr <= a_end_blk; addr += fatfs->csize) {
+        int retval;
+        size_t read_size;
+
+        /* Identify its allocation status */
+        retval = fatfs_is_sectalloc(fatfs, addr);
+        if (retval == -1) {
+            free(data_buf);
+            tsk_fs_block_free(fs_block);
+            return 1;
+        }
+        else if (retval == 1) {
+            myflags = TSK_FS_BLOCK_FLAG_ALLOC;
+        }
+        else {
+            myflags = TSK_FS_BLOCK_FLAG_UNALLOC;
+        }
+
+        /* At this point, there should be no more meta - just content */
+        myflags |= TSK_FS_BLOCK_FLAG_CONT;
+
+        // test if we should call the callback with this one
+        if ((myflags & TSK_FS_BLOCK_FLAG_CONT)
+            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT)))
+            continue;
+        else if ((myflags & TSK_FS_BLOCK_FLAG_ALLOC)
+            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC)))
+            continue;
+        else if ((myflags & TSK_FS_BLOCK_FLAG_UNALLOC)
+            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC)))
+            continue;
+
+        if (a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY)
+            myflags |= TSK_FS_BLOCK_FLAG_AONLY;
+
+        
+        /* The final cluster may not be full */
+        if (a_end_blk - addr + 1 < fatfs->csize)
+            read_size = (size_t) (a_end_blk - addr + 1);
+        else
+            read_size = fatfs->csize;
+
+        if ((a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY) == 0) {
+            cnt = tsk_fs_read_block
+                (fs, addr, data_buf, fs->block_size * read_size);
+            if (cnt != fs->block_size * read_size) {
+                if (cnt >= 0) {
+                    tsk_error_reset();
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
+                }
+                tsk_error_set_errstr2("fatfs_block_walk: block: %" PRIuDADDR,
+                    addr);
+                free(data_buf);
+                tsk_fs_block_free(fs_block);
+                return 1;
+            }
+        }
+
+        /* go through each sector in the cluster */
+        for (i = 0; i < read_size; i++) {
+            int retval;
+
+            if (addr + i < a_start_blk)
+                continue;
+            else if (addr + i > a_end_blk)
+                break;
+
+            tsk_fs_block_set(fs, fs_block, addr + i,
+                myflags | TSK_FS_BLOCK_FLAG_RAW,
+                &data_buf[i * fs->block_size]);
+
+            retval = a_action(fs_block, a_ptr);
+            if (retval == TSK_WALK_STOP) {
+                free(data_buf);
+                tsk_fs_block_free(fs_block);
+                return 0;
+            }
+            else if (retval == TSK_WALK_ERROR) {
+                free(data_buf);
+                tsk_fs_block_free(fs_block);
+                return 1;
+            }
+        }
+    }
+
+    free(data_buf);
+    tsk_fs_block_free(fs_block);
+    return 0;
+}
+
+TSK_FS_BLOCK_FLAG_ENUM
+fatfs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
+{
+    FATFS_INFO *fatfs = (FATFS_INFO *) a_fs;
+    int flags = 0;
+
+    // FATs and boot sector
+    if (a_addr < fatfs->firstdatasect) {
+        flags = TSK_FS_BLOCK_FLAG_META | TSK_FS_BLOCK_FLAG_ALLOC;
+    }
+    // root directory for FAT12/16
+    else if (a_addr < fatfs->firstclustsect) {
+        flags = TSK_FS_BLOCK_FLAG_CONT | TSK_FS_BLOCK_FLAG_ALLOC;
+    }
+    else {
+        int retval;
+        flags = TSK_FS_BLOCK_FLAG_CONT;
+
+        /* Identify its allocation status */
+        retval = fatfs_is_sectalloc(fatfs, a_addr);
+        if (retval != -1) {
+            if (retval == 1)
+                flags |= TSK_FS_BLOCK_FLAG_ALLOC;
+            else
+                flags |= TSK_FS_BLOCK_FLAG_UNALLOC;
+        }
+    }
+    return (TSK_FS_BLOCK_FLAG_ENUM)flags;
+}
+
+/* 
+ * Identifies if a sector is allocated
+ *
+ * If it is less than the data area, then it is allocated
+ * else the FAT table is consulted
+ *
+ * Return 1 if allocated, 0 if unallocated, and -1 if error 
+ */
+int8_t
+fatfs_is_sectalloc(FATFS_INFO * fatfs, TSK_DADDR_T sect)
+{
+    TSK_FS_INFO *fs = (TSK_FS_INFO *) fatfs;
+    /* If less than the first cluster sector, then it is allocated 
+     * otherwise check the FAT
+     */
+    if (sect < fatfs->firstclustsect)
+        return 1;
+
+    /* If we are in the unused area, then we are "unalloc" */
+    if ((sect <= fs->last_block) &&
+        (sect >= (fatfs->firstclustsect + fatfs->csize * fatfs->clustcnt)))
+        return 0;
+
+    return fatfs->is_cluster_alloc(fatfs, FATFS_SECT_2_CLUST(fatfs, sect));
+}
+
+/* return 1 on error and 0 on success */
+uint8_t
+fatfs_jopen(TSK_FS_INFO * fs, TSK_INUM_T inum)
+{
+    tsk_error_reset();
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("FAT does not have a journal\n");
+    return 1;
+}
+
+/* return 1 on error and 0 on success */
+uint8_t
+fatfs_fscheck(TSK_FS_INFO * fs, FILE * hFile)
+{
+    tsk_error_reset();
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("fscheck not implemented for FAT yet");
+    return 1;
+
+    /* Check that allocated dentries point to start of allcated cluster chain */
+
+
+    /* Size of file is consistent with cluster chain length */
+
+
+    /* Allocated cluster chains have a corresponding alloc dentry */
+
+
+    /* Non file dentries have no clusters */
+
+
+    /* Only one volume label */
+
+
+    /* Dump Bad Sector Addresses */
+
+
+    /* Dump unused sector addresses 
+     * Reserved area, end of FAT, end of Data Area */
+}
+
+/* return 1 on error and 0 on success */
+uint8_t
+fatfs_jentry_walk(TSK_FS_INFO * fs, int a_flags,
+    TSK_FS_JENTRY_WALK_CB a_action, void *a_ptr)
+{
+    tsk_error_reset();
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("FAT does not have a journal\n");
+    return 1;
+}
+
+/* return 1 on error and 0 on success */
+uint8_t
+fatfs_jblk_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T end,
+    int a_flags, TSK_FS_JBLK_WALK_CB a_action, void *a_ptr)
+{
+    tsk_error_reset();
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("FAT does not have a journal\n");
+    return 1;
+}
+
+/* fatfs_close - close an fatfs file system */
+void
+fatfs_close(TSK_FS_INFO *fs)
+{
+    FATFS_INFO *fatfs = (FATFS_INFO *) fs;
+ 
+    fatfs_dir_buf_free(fatfs);
+
+    fs->tag = 0;
+	memset(fatfs->boot_sector_buffer, 0, FATFS_MASTER_BOOT_RECORD_SIZE);
+    tsk_deinit_lock(&fatfs->cache_lock);
+    tsk_deinit_lock(&fatfs->dir_lock);
+	
+    tsk_fs_free(fs);
+}
+
diff --git a/tsk/fs/fatfs_meta.c b/tsk/fs/fatfs_meta.c
index 8f72e224e..4bc862252 100644
--- a/tsk/fs/fatfs_meta.c
+++ b/tsk/fs/fatfs_meta.c
@@ -1,1526 +1,1526 @@
-/*
-** fatfs
-** The Sleuth Kit
-**
-** Meta data layer support for the FAT file system.
-**
-** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2013 Brian Carrier, Basis Technology.  All Rights reserved
-** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
-**
-** TASK
-** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
-**
-**
-** This software is distributed under the Common Public License 1.0
-**
-** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05)
-**
-*/
-
-/**
- * \file fatfs_meta.c
- * Meta data layer support for FAT file systems.
- */
-
-#include "tsk_fatfs.h"
-#include "tsk_fatxxfs.h"
-#include "tsk_exfatfs.h"
-
-TSK_FS_ATTR_TYPE_ENUM
-fatfs_get_default_attr_type(const TSK_FS_FILE * a_file)
-{
-    return TSK_FS_ATTR_TYPE_DEFAULT;
-}
-
-/**
- * \internal
- * Create an TSK_FS_META structure for the root directory.  FAT does
- * not have a directory entry for the root directory, but this
- * function collects the data needed to make one.
- *
- * @param fatfs File system to analyze.
- * @param fs_meta Inode structure to copy root directory information into.
- * @return 1 on error and 0 on success
- */
-static uint8_t
-fatfs_make_root(FATFS_INFO *a_fatfs, TSK_FS_META *a_fs_meta)
-{
-    const char *func_name = "fatfs_make_root";
-    TSK_DADDR_T *first_clust_addr_ptr = NULL;
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
-        fatfs_ptr_arg_is_null(a_fs_meta, "a_fs_meta", func_name)) {
-        return 1;
-    }
-
-    /* Manufacture some metadata. */
-    a_fs_meta->type = TSK_FS_META_TYPE_DIR;
-    a_fs_meta->mode = TSK_FS_META_MODE_UNSPECIFIED;
-    a_fs_meta->nlink = 1;
-    a_fs_meta->addr = FATFS_ROOTINO;
-    a_fs_meta->flags = (TSK_FS_META_FLAG_ENUM)(TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC);
-    a_fs_meta->uid = a_fs_meta->gid = 0;
-    a_fs_meta->mtime = a_fs_meta->atime = a_fs_meta->ctime = a_fs_meta->crtime = 0;
-    a_fs_meta->mtime_nano = a_fs_meta->atime_nano = a_fs_meta->ctime_nano =
-        a_fs_meta->crtime_nano = 0;
-
-    /* Give the root directory an empty name. */
-    if (a_fs_meta->name2 == NULL) {
-        if ((a_fs_meta->name2 = (TSK_FS_META_NAME_LIST *)
-                tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) {
-            return 1;
-        }
-        a_fs_meta->name2->next = NULL;
-    }
-    a_fs_meta->name2->name[0] = '\0';
-
-    /* Mark the generic attribute list as not in use (in the generic file model
-     * attributes are containers for data or metadata). Population of this 
-     * list is done by lazy look up. */
-    a_fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
-    if (a_fs_meta->attr) {
-        tsk_fs_attrlist_markunused(a_fs_meta->attr);
-    }
-
-    /* Determine the size of the root directory and the address of its 
-     * first cluster. */
-    first_clust_addr_ptr = (TSK_DADDR_T*)a_fs_meta->content_ptr;
-    if (a_fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32 ||
-        a_fatfs->fs_info.ftype == TSK_FS_TYPE_EXFAT) {
-        TSK_DADDR_T cnum = 0;
-        TSK_DADDR_T clust = 0;
-        TSK_LIST *list_seen = NULL;
-
-        /* Convert the address of the first sector of the root directory into
-         * the address of its first cluster. */
-        clust = FATFS_SECT_2_CLUST(a_fatfs, a_fatfs->rootsect);
-        first_clust_addr_ptr[0] = clust;
-
-        /* Walk the FAT and count the clusters allocated to the root directory. */
-        cnum = 0;
-        while ((clust) && (0 == FATFS_ISEOF(clust, FATFS_32_MASK))) {
-            TSK_DADDR_T nxt = 0;
-
-            /* Make sure we do not get into an infinite loop */
-            if (tsk_list_find(list_seen, clust)) {
-                if (tsk_verbose) {
-                    tsk_fprintf(stderr,
-                        "Loop found while determining root directory size\n");
-                }
-                break;
-            }
-            if (tsk_list_add(&list_seen, clust)) {
-                tsk_list_free(list_seen);
-                list_seen = NULL;
-                return 1;
-            }
-
-            cnum++;
-            if (fatfs_getFAT(a_fatfs, clust, &nxt)) {
-                break;
-            }
-            else {
-                clust = nxt;
-            }
-        }
-        tsk_list_free(list_seen);
-        list_seen = NULL;
-
-        /* Calculate the size of the root directory. */
-        a_fs_meta->size = (cnum * a_fatfs->csize) << a_fatfs->ssize_sh;
-    }
-    else {
-        /* FAT12 and FAT16 don't use the FAT for the root directory, so set 
-         * the first cluster address to a distinguished value that other code
-         * will have to check as a special condition. */ 
-        first_clust_addr_ptr[0] = 1;
-
-        /* Set the size equal to the number of bytes between the end of the 
-         * FATs and the start of the clusters. */
-        a_fs_meta->size = (a_fatfs->firstclustsect - a_fatfs->firstdatasect) << a_fatfs->ssize_sh;
-    }
-
-    return 0;
-}
-
-/**
-* \internal
- * Create an TSK_FS_META structure for the master boot record.
- *
- * @param fatfs File system to analyze
- * @param fs_meta Inode structure to copy file information into.
- * @return 1 on error and 0 on success
- */
-static uint8_t
-fatfs_make_mbr(FATFS_INFO *fatfs, TSK_FS_META *fs_meta)
-{
-    TSK_DADDR_T *addr_ptr;
-    TSK_FS_INFO *fs = (TSK_FS_INFO *) fatfs;
-
-    fs_meta->type = TSK_FS_META_TYPE_VIRT;
-    fs_meta->mode = TSK_FS_META_MODE_UNSPECIFIED;
-    fs_meta->nlink = 1;
-    fs_meta->addr = fatfs->mbr_virt_inum;
-    fs_meta->flags = (TSK_FS_META_FLAG_ENUM)
-        (TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC);
-    fs_meta->uid = fs_meta->gid = 0;
-    fs_meta->mtime = fs_meta->atime = fs_meta->ctime = fs_meta->crtime = 0;
-    fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano =
-        fs_meta->crtime_nano = 0;
-
-    if (fs_meta->name2 == NULL) {
-        if ((fs_meta->name2 = (TSK_FS_META_NAME_LIST *)
-                tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) {
-            return 1;
-        }
-        fs_meta->name2->next = NULL;
-    }
-    strncpy(fs_meta->name2->name, FATFS_MBRNAME,
-        TSK_FS_META_NAME_LIST_NSIZE);
-
-    fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
-    if (fs_meta->attr) {
-        tsk_fs_attrlist_markunused(fs_meta->attr);
-    }
-
-    addr_ptr = (TSK_DADDR_T*)fs_meta->content_ptr;
-    addr_ptr[0] = 0;
-    fs_meta->size = 512;
-
-    return 0;
-}
-
-/**
-* \internal
- * Create an TSK_FS_META structure for the FAT tables.
- *
- * @param fatfs File system to analyze
- * @param a_which 1 or 2 to choose between defining FAT1 or FAT2
- * @param fs_meta Inode structure to copy file information into.
- * @return 1 on error and 0 on success
- */
-static uint8_t
-fatfs_make_fat(FATFS_INFO *fatfs, uint8_t a_which, TSK_FS_META *fs_meta)
-{
-    TSK_FS_INFO *fs = (TSK_FS_INFO*)fatfs;
-    TSK_DADDR_T *addr_ptr = (TSK_DADDR_T *)fs_meta->content_ptr;
-
-    if ((a_which != 1) && (a_which != 2)) {
-        return 1;
-    }
-
-    if (a_which > fatfs->numfat) {
-        return 1;
-    }
-
-    fs_meta->type = TSK_FS_META_TYPE_VIRT;
-    fs_meta->mode = TSK_FS_META_MODE_UNSPECIFIED;
-    fs_meta->nlink = 1;
-
-    fs_meta->flags = (TSK_FS_META_FLAG_ENUM)
-        (TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC);
-    fs_meta->uid = fs_meta->gid = 0;
-    fs_meta->mtime = fs_meta->atime = fs_meta->ctime = fs_meta->crtime = 0;
-    fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano =
-        fs_meta->crtime_nano = 0;
-
-    if (fs_meta->name2 == NULL) {
-        if ((fs_meta->name2 = (TSK_FS_META_NAME_LIST *)
-                tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL)
-            return 1;
-        fs_meta->name2->next = NULL;
-    }
-
-    if (a_which == 1) {
-        fs_meta->addr = fatfs->fat1_virt_inum;
-        strncpy(fs_meta->name2->name, FATFS_FAT1NAME,
-            TSK_FS_META_NAME_LIST_NSIZE);
-        addr_ptr[0] = fatfs->firstfatsect;
-    }
-    else {
-        fs_meta->addr = fatfs->fat2_virt_inum;
-        strncpy(fs_meta->name2->name, FATFS_FAT2NAME,
-            TSK_FS_META_NAME_LIST_NSIZE);
-        addr_ptr[0] = fatfs->firstfatsect + fatfs->sectperfat;
-    }
-
-    fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
-    if (fs_meta->attr) {
-        tsk_fs_attrlist_markunused(fs_meta->attr);
-    }
-
-    fs_meta->size = fatfs->sectperfat * fs->block_size;
-
-    return 0;
-}
-
-/**
- * \internal
- * Load a FATFS_DENTRY structure with the bytes at a given inode address.
- *
- * @param [in] a_fs The file system from which to read the bytes.
- * @param [out] a_de The FATFS_DENTRY.
- * @param [in] a_inum An inode address.
- * @return 0 on success, 1 on failure. 
- */
-uint8_t
-fatfs_dentry_load(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum)
-{
-    const char *func_name = "fatfs_dentry_load";
-    TSK_FS_INFO *fs = (TSK_FS_INFO*)a_fatfs;
-    TSK_DADDR_T sect = 0;
-    size_t off = 0;
-    ssize_t cnt = 0;
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
-        fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name) ||
-        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
-        return 1;
-    }
-    
-    /* Map the inode address to a sector. */
-    sect = FATFS_INODE_2_SECT(a_fatfs, a_inum);
-    if (sect > fs->last_block) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
-        tsk_error_set_errstr("%s: Inode %" PRIuINUM
-            " in sector too big for image: %" PRIuDADDR, func_name, a_inum, sect);
-        return 1;
-    }
-
-    /* Get the byte offset of the inode address within the sector. */
-    off = FATFS_INODE_2_OFF(a_fatfs, a_inum);
-
-    /* Read in the bytes. */
-    cnt = tsk_fs_read(fs, sect * fs->block_size + off, (char*)a_dentry, sizeof(FATFS_DENTRY));
-    if (cnt != sizeof(FATFS_DENTRY)) {
-        if (cnt >= 0) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_READ);
-        }
-        tsk_error_set_errstr2("%s: block: %" PRIuDADDR,
-            func_name, sect);
-        return 1;
-    }
-
-    return 0;
-}
-
-/**
- * \internal
- * Populate the TSK_FS_META structure of a TSK_FS_FILE structure for a 
- * given inode address.
- *
- * @param [in] a_fs File system that contains the inode.
- * @param [out] a_fs_file The file corresponding to the inode.
- * @param [in] a_inum The inode address.
- * @returns 1 if an error occurs or if the inode address is not
- * for a valid inode, 0 otherwise.
- */
-uint8_t
-fatfs_inode_lookup(TSK_FS_INFO *a_fs, TSK_FS_FILE *a_fs_file,
-    TSK_INUM_T a_inum)
-{
-    const char *func_name = "fatfs_inode_lookup";
-    FATFS_INFO *fatfs = (FATFS_INFO*)a_fs;
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fs, "a_fs", func_name) ||
-        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name) ||
-        !fatfs_inum_arg_is_in_range(fatfs, a_inum, func_name)) {
-        return 1;
-    }
-
-    /* Allocate or reset the TSK_FS_META struct. */
-    if (a_fs_file->meta == NULL) {
-        if ((a_fs_file->meta =
-                tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) {
-            return 1;
-        }
-    }
-    else {
-        tsk_fs_meta_reset(a_fs_file->meta);
-    }
-
-    /* Manufacture an inode for the root directory or a FAT virtual file,
-     * or do a look up. */
-    if (a_inum == a_fs->root_inum) {
-        if (fatfs_make_root(fatfs, a_fs_file->meta))
-            return 1;
-        else
-            return 0;
-    }
-    else if (a_inum == fatfs->mbr_virt_inum) {
-        if (fatfs_make_mbr(fatfs, a_fs_file->meta))
-            return 1;
-        else
-            return 0;
-    }
-    else if (a_inum == fatfs->fat1_virt_inum) {
-        if (fatfs_make_fat(fatfs, 1, a_fs_file->meta))
-            return 1;
-        else
-            return 0;
-    }
-    else if (a_inum == fatfs->fat2_virt_inum && fatfs->numfat == 2) {
-        if (fatfs_make_fat(fatfs, 2, a_fs_file->meta))
-            return 1;
-        else
-            return 0;
-    }
-    else if (a_inum == TSK_FS_ORPHANDIR_INUM(a_fs)) {
-        if (tsk_fs_dir_make_orphan_dir_meta(a_fs, a_fs_file->meta))
-            return 1;
-        else
-            return 0;
-    }
-    else {
-        return fatfs->inode_lookup(fatfs, a_fs_file, a_inum);
-    }
-}
-
-/** \internal
- * Make data runs out of the clusters allocated to a file represented by a 
- * TSK_FS_FILE structure. Each data run will have a starting sector and a 
- * length in sectors. The runs will be stored as a non-resident attribute in 
- * the TSK_FS_ATTRLIST of the TSK_FS_META structure of the TSK_FS_FILE. 
- *
- * @param a_fs_file A representation of a file.
- * @return 1 on error and 0 on success
- */
-uint8_t
-fatfs_make_data_runs(TSK_FS_FILE * a_fs_file)
-{
-    const char *func_name = "fatfs_make_data_runs";
-    TSK_FS_INFO *fs = NULL;
-    TSK_FS_META *fs_meta = NULL;
-    FATFS_INFO *fatfs = NULL;
-    TSK_DADDR_T clust = 0;
-    TSK_OFF_T size_remain = 0;
-    TSK_FS_ATTR *fs_attr = NULL;
-
-    if ((fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name)) ||
-        (fatfs_ptr_arg_is_null(a_fs_file->meta, "a_fs_file->meta", func_name)) ||
-        (fatfs_ptr_arg_is_null(a_fs_file->fs_info, "a_fs_file->fs_info", func_name))) {
-        return TSK_ERR;
-    }
-    
-    fs_meta = a_fs_file->meta;
-    fs = a_fs_file->fs_info;
-    fatfs = (FATFS_INFO*)fs;
-
-    /* Check for an already populated attribute list, since a lazy strategy
-     * is used to fill in attributes. If the attribute list is not yet 
-     * allocated, do so now. */  
-    if ((fs_meta->attr != NULL)
-        && (fs_meta->attr_state == TSK_FS_META_ATTR_STUDIED)) {
-        return 0;
-    }
-    else if (fs_meta->attr_state == TSK_FS_META_ATTR_ERROR) {
-        return 1;
-    }
-    else if (fs_meta->attr != NULL) {
-        tsk_fs_attrlist_markunused(fs_meta->attr);
-    }
-    else if (fs_meta->attr == NULL) {
-        fs_meta->attr = tsk_fs_attrlist_alloc();
-    }
-
-    /* Get the stashed first cluster address of the file. */
-    clust = ((TSK_DADDR_T*)fs_meta->content_ptr)[0];
-    if ((clust > (fatfs->lastclust)) &&
-        (FATFS_ISEOF(clust, fatfs->mask) == 0)) {
-        fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-        tsk_error_reset();
-        if (a_fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC) {
-            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
-        }
-        else {
-            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
-        }
-        tsk_error_set_errstr
-            ("%s: Starting cluster address too large: %"
-            PRIuDADDR, func_name, clust);
-        return 1;
-    }
-
-    /* Figure out the allocated length of the file in bytes. Because the
-     * allocation unit for FAT file systems is the cluster, round the
-     * size up to a multiple of cluster size. */
-    size_remain = roundup(fs_meta->size, fatfs->csize * fs->block_size);
-
-    // RJCTODO: Consider addressing the code duplication below.
-    // RJCTODO: Consider breaking out each of the conditional blocks into its own sub-function.
-    if ((a_fs_file->meta->addr == fs->root_inum) && 
-        (fs->ftype != TSK_FS_TYPE_FAT32) &&
-        (fs->ftype != TSK_FS_TYPE_EXFAT) &&
-        (clust == 1)) {
-        /* Make a single contiguous data run for a FAT12 or FAT16 root 
-         * directory. The root directory for these file systems is not 
-         * tracked in the FAT. */
-        TSK_FS_ATTR_RUN *data_run;
-
-        if (tsk_verbose) {
-            tsk_fprintf(stderr,
-                "%s: Loading root directory\n", func_name);
-        }
-
-        /* Allocate the run. */
-        data_run = tsk_fs_attr_run_alloc();
-        if (data_run == NULL) {
-            return 1;
-        }
-
-        /* Set the starting sector address and run length. The run begins with 
-         * the first sector of the data area. */
-        data_run->addr = fatfs->rootsect;
-        data_run->len = fatfs->firstclustsect - fatfs->firstdatasect;
-
-        /* Allocate a non-resident attribute to hold the run and add it
-         to the attribute list. */
-        if ((fs_attr =
-                tsk_fs_attrlist_getnew(fs_meta->attr,
-                    TSK_FS_ATTR_NONRES)) == NULL) {
-            return 1;
-        }
-
-        /* Tie everything together. */
-        if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run, NULL,
-                TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
-                data_run->len * fs->block_size,
-                data_run->len * fs->block_size,
-                data_run->len * fs->block_size, 0, 0)) {
-            return 1;
-        }
-
-        fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
-
-        return 0;
-    }
-    else if ((a_fs_file->meta->addr >= fatfs->mbr_virt_inum) &&
-             (a_fs_file->meta->addr <= fatfs->mbr_virt_inum + fatfs->numfat)) {
-        /* Make a single contiguous data run for a virtual file (MBR, FAT). */ 
-        TSK_FS_ATTR_RUN *data_run;
-
-        if (tsk_verbose) {
-            tsk_fprintf(stderr,
-                "%s: Loading virtual file: %" PRIuINUM
-                "\n", func_name, a_fs_file->meta->addr);
-        }
-
-        /* Allocate the run. */
-        data_run = tsk_fs_attr_run_alloc();
-        if (data_run == NULL) {
-            return 1;
-        }
-
-        /* Set the starting sector address and run length. */
-        data_run->addr = clust;
-        data_run->len = a_fs_file->meta->size / fs->block_size;
-
-        /* Allocate a non-resident attribute to hold the run and add it
-         to the attribute list. */
-        if ((fs_attr =
-                tsk_fs_attrlist_getnew(fs_meta->attr,
-                    TSK_FS_ATTR_NONRES)) == NULL) {
-            return 1;
-        }
-
-        /* Tie everything together. */
-        if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run, NULL,
-                TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
-                data_run->len * fs->block_size,
-                data_run->len * fs->block_size,
-                data_run->len * fs->block_size, 0, 0)) {
-            return 1;
-        }
-
-        fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
-        return 0;
-    }
-    else if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC) {
-        /* Make data runs for a deleted file that we want to recover.
-         * In this case, we could get a lot of errors because of inconsistent
-         * data.  To make it clear that these are from a recovery, we set most
-         * error codes to _RECOVER so that they can be more easily suppressed.
-         */
-        TSK_DADDR_T sbase;
-        TSK_DADDR_T startclust = clust;
-        TSK_OFF_T recoversize = fs_meta->size;
-        int retval;
-        TSK_FS_ATTR_RUN *data_run = NULL;
-        TSK_FS_ATTR_RUN *data_run_head = NULL;
-        TSK_OFF_T full_len_s = 0;
-        uint8_t canRecover = 1; // set to 0 if recovery is not possible
-
-        if (tsk_verbose)
-            tsk_fprintf(stderr,
-                "%s: Processing deleted file %" PRIuINUM
-                " in recovery mode\n", func_name, fs_meta->addr);
-
-        /* We know the size and the starting cluster
-         *
-         * We are going to take the clusters from the starting cluster
-         * onwards and skip the clusters that are current allocated
-         */
-
-        /* Sanity checks on the starting cluster */
-        /* Convert the cluster addr to a sector addr */
-        sbase = FATFS_CLUST_2_SECT(fatfs, startclust);
-
-        if (sbase > fs->last_block) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
-            tsk_error_set_errstr
-                ("%s: Starting cluster address too large (recovery): %"
-                PRIuDADDR, func_name, sbase);
-            fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-            return 1;
-        }
-        else {
-
-            /* If the starting cluster is already allocated then we can't
-             * recover it */
-            retval = fatfs->is_cluster_alloc(fatfs, startclust);
-            if (retval != 0) {
-                canRecover = 0;
-            }
-        }
-
-        /* Part 1 is to make sure there are enough unallocated clusters
-         * for the size of the file
-         */
-        clust = startclust;
-        size_remain = recoversize;
-
-        // we could make this negative so sign it for the comparison
-        while (((int64_t) size_remain > 0) && (canRecover)) {
-            int retval;
-            sbase = FATFS_CLUST_2_SECT(fatfs, clust);
-
-            /* Are we past the end of the FS?
-             * that means we could not find enough unallocated clusters
-             * for the file size */
-            if (sbase + fatfs->csize - 1 > fs->last_block) {
-                canRecover = 0;
-
-                if (tsk_verbose)
-                    tsk_fprintf(stderr,
-                        "%s: Could not find enough unallocated sectors to recover with - aborting\n", func_name);
-                break;
-            }
-
-            /* Skip allocated clusters */
-            retval = fatfs->is_cluster_alloc(fatfs, clust);
-            if (retval == -1) {
-                canRecover = 0;
-                break;
-            }
-            else if (retval == 1) {
-                clust++;
-                continue;
-            }
-
-            /* We can use this sector */
-            // see if we need a new run
-            if ((data_run == NULL)
-                || (data_run->addr + data_run->len != sbase)) {
-
-                TSK_FS_ATTR_RUN *data_run_tmp = tsk_fs_attr_run_alloc();
-                if (data_run_tmp == NULL) {
-                    fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-                    tsk_fs_attr_run_free(data_run_head);
-                    return 1;
-                }
-
-                if (data_run_head == NULL) {
-                    data_run_head = data_run_tmp;
-                    data_run_tmp->offset = 0;
-                }
-                else if (data_run != NULL) {
-                    data_run->next = data_run_tmp;
-                    data_run_tmp->offset =
-                        data_run->offset + data_run->len;
-                }
-                data_run = data_run_tmp;
-                data_run->len = 0;
-                data_run->addr = sbase;
-            }
-            data_run->len += fatfs->csize;
-            full_len_s += fatfs->csize;
-
-            size_remain -= (fatfs->csize << fatfs->ssize_sh);
-            clust++;
-        }
-
-        // Get a FS_DATA structure and add the runlist to it
-        if ((fs_attr =
-                tsk_fs_attrlist_getnew(fs_meta->attr,
-                    TSK_FS_ATTR_NONRES)) == NULL) {
-            fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-            return 1;
-        }
-
-        if (canRecover) {
-            /* We can recover the file */
-
-            // initialize the data run
-            if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run_head,
-                    NULL, TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
-                    fs_meta->size, fs_meta->size, roundup(fs_meta->size,
-                        fatfs->csize * fs->block_size), 0, 0)) {
-                fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-                return 1;
-            }
-
-            fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
-        }
-        // create a one cluster run
-        else {
-            TSK_FS_ATTR_RUN *data_run_tmp = tsk_fs_attr_run_alloc();
-            if (data_run_tmp == NULL) {
-                fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-                return 1;
-            }
-            data_run_tmp->addr = sbase;
-            data_run_tmp->len = fatfs->csize;
-
-            // initialize the data run
-            if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run_tmp, NULL,
-                    TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
-                    fs_meta->size, fs_meta->size, roundup(fs_meta->size,
-                        fatfs->csize * fs->block_size), 0, 0)) {
-                fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-                return 1;
-            }
-
-            fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
-        }
-
-        return 0;
-    }
-    else {
-        // RJCTODO: Find and fix the bug that causes sectors to be printed incorrectly.
-        TSK_LIST *list_seen = NULL;
-        TSK_FS_ATTR_RUN *data_run = NULL;
-        TSK_FS_ATTR_RUN *data_run_head = NULL;
-        TSK_OFF_T full_len_s = 0;
-        TSK_DADDR_T sbase;
-        /* Do normal cluster chain walking for a file or directory, including
-         * FAT32 and exFAT root directories. */
-
-        if (tsk_verbose) {
-            tsk_fprintf(stderr,
-                "%s: Processing file %" PRIuINUM
-                " in normal mode\n", func_name, fs_meta->addr);
-        }
-
-        /* Cycle through the cluster chain */
-        while ((clust & fatfs->mask) > 0 && (int64_t) size_remain > 0 &&
-            (0 == FATFS_ISEOF(clust, fatfs->mask))) {
-
-            /* Convert the cluster addr to a sector addr */
-            sbase = FATFS_CLUST_2_SECT(fatfs, clust);
-
-            if (sbase + fatfs->csize - 1 > fs->last_block) {
-                fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-                tsk_error_reset();
-
-                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
-                tsk_error_set_errstr
-                    ("%s: Invalid sector address in FAT (too large): %"
-                    PRIuDADDR " (plus %d sectors)", func_name, sbase, fatfs->csize);
-                return 1;
-            }
-
-            // see if we need a new run
-            if ((data_run == NULL)
-                || (data_run->addr + data_run->len != sbase)) {
-
-                TSK_FS_ATTR_RUN *data_run_tmp = tsk_fs_attr_run_alloc();
-                if (data_run_tmp == NULL) {
-                    tsk_fs_attr_run_free(data_run_head);
-                    fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-                    return 1;
-                }
-
-                if (data_run_head == NULL) {
-                    data_run_head = data_run_tmp;
-                    data_run_tmp->offset = 0;
-                }
-                else if (data_run != NULL) {
-                    data_run->next = data_run_tmp;
-                    data_run_tmp->offset =
-                        data_run->offset + data_run->len;
-                }
-                data_run = data_run_tmp;
-                data_run->len = 0;
-                data_run->addr = sbase;
-            }
-
-            data_run->len += fatfs->csize;
-            full_len_s += fatfs->csize;
-            size_remain -= (fatfs->csize * fs->block_size);
-
-            if ((int64_t) size_remain > 0) {
-                TSK_DADDR_T nxt;
-                if (fatfs_getFAT(fatfs, clust, &nxt)) {
-                    tsk_error_set_errstr2("%s: Inode: %" PRIuINUM
-                        "  cluster: %" PRIuDADDR, func_name, fs_meta->addr, clust);
-                    fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-                    tsk_fs_attr_run_free(data_run_head);
-                    tsk_list_free(list_seen);
-                    list_seen = NULL;
-                    return 1;
-                }
-                clust = nxt;
-
-                /* Make sure we do not get into an infinite loop */
-                if (tsk_list_find(list_seen, clust)) {
-                    if (tsk_verbose)
-                        tsk_fprintf(stderr,
-                            "Loop found while processing file\n");
-                    break;
-                }
-
-                if (tsk_list_add(&list_seen, clust)) {
-                    fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-                    tsk_list_free(list_seen);
-                    list_seen = NULL;
-                    return 1;
-                }
-            }
-        }
-
-        // add the run list to the inode structure
-        if ((fs_attr =
-                tsk_fs_attrlist_getnew(fs_meta->attr,
-                    TSK_FS_ATTR_NONRES)) == NULL) {
-            fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-            return 1;
-        }
-
-        // initialize the data run
-        if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run_head, NULL,
-                TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
-                fs_meta->size, fs_meta->size, roundup(fs_meta->size,
-                    fatfs->csize * fs->block_size), 0, 0)) {
-            fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
-            return 1;
-        }
-
-        tsk_list_free(list_seen);
-        list_seen = NULL;
-
-        fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
-
-        return 0;
-    }
-}
-
-/* Used for istat callback */
-typedef struct {
-    FILE *hFile;
-    int idx;
-    int istat_seen;
-} FATFS_PRINT_ADDR;
-
-/* Callback a_action for file_walk to print the sector addresses
- * of a file, used for istat
- */
-static TSK_WALK_RET_ENUM
-print_addr_act(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
-    char *buf, size_t size, TSK_FS_BLOCK_FLAG_ENUM a_flags, void *a_ptr)
-{
-    FATFS_PRINT_ADDR *print = (FATFS_PRINT_ADDR *) a_ptr;
-
-    tsk_fprintf(print->hFile, "%" PRIuDADDR " ", addr);
-
-    if (++(print->idx) == 8) {
-        tsk_fprintf(print->hFile, "\n");
-        print->idx = 0;
-    }
-    print->istat_seen = 1;
-
-    return TSK_WALK_CONT;
-}
-
-/**
- * Print details on a specific file to a file handle. 
- *
- * @param a_fs File system file is located in.
- * @param a_hFile File handle to print text to.
- * @param a_inum Address of file in file system.
- * @param a_numblock The number of blocks in file to force print (can go beyond file size).
- * @param a_sec_skew Clock skew in seconds to also print times in.
- * 
- * @returns 1 on error and 0 on success.
- */
-uint8_t
-fatfs_istat(TSK_FS_INFO *a_fs, FILE *a_hFile, TSK_INUM_T a_inum,
-    TSK_DADDR_T a_numblock, int32_t a_sec_skew)
-{
-    const char* func_name = "fatfs_istat";
-    FATFS_INFO *fatfs = (FATFS_INFO*)a_fs;
-    TSK_FS_META *fs_meta = NULL; 
-    TSK_FS_FILE *fs_file =  NULL;
-    TSK_FS_META_NAME_LIST *fs_name_list = NULL;
-    FATFS_PRINT_ADDR print;
-    char timeBuf[128];
- 
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fs, "a_fs", func_name) ||
-        fatfs_ptr_arg_is_null(a_hFile, "a_hFile", func_name) ||
-        !fatfs_inum_arg_is_in_range(fatfs, a_inum, func_name)) {
-        return 1;
-    }
-
-    /* Create a TSK_FS_FILE corresponding to the specified inode. */
-    if ((fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_inum)) == NULL) {
-        return 1;
-    }
-    fs_meta = fs_file->meta;
-
-    /* Print the inode address. */
-    tsk_fprintf(a_hFile, "Directory Entry: %" PRIuINUM "\n", a_inum);
-
-    /* Print the allocation status. */
-    tsk_fprintf(a_hFile, "%sAllocated\n",
-        (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC) ? "Not " : "");
-
-    /* Print the attributes. */
-    tsk_fprintf(a_hFile, "File Attributes: ");
-
-    if (a_inum == a_fs->root_inum) {
-        tsk_fprintf(a_hFile, "Root Directory\n");
-    }
-    else if (fs_meta->type == TSK_FS_META_TYPE_VIRT) {
-        tsk_fprintf(a_hFile, "Virtual File\n");
-    }
-    else if (fs_meta->addr == TSK_FS_ORPHANDIR_INUM(a_fs)) {
-        tsk_fprintf(a_hFile, "Virtual Directory\n");
-    }
-    else {
-        if (fatfs->istat_attr_flags(fatfs, a_inum, a_hFile)) {
-            return 1;
-        }
-    }
-
-    /* Print the file size. */
-    tsk_fprintf(a_hFile, "Size: %" PRIuOFF "\n", fs_meta->size);
-
-    /* Print the name. */
-    if (fs_meta->name2) {
-        fs_name_list = fs_meta->name2;
-        tsk_fprintf(a_hFile, "Name: %s\n", fs_name_list->name);
-    }
-
-    /* Print the times. */
-    if (a_sec_skew != 0) {
-        tsk_fprintf(a_hFile, "\nAdjusted Directory Entry Times:\n");
-
-        if (fs_meta->mtime)
-            fs_meta->mtime -= a_sec_skew;
-        if (fs_meta->atime)
-            fs_meta->atime -= a_sec_skew;
-        if (fs_meta->crtime)
-            fs_meta->crtime -= a_sec_skew;
-
-        tsk_fprintf(a_hFile, "Written:\t%s\n",
-            tsk_fs_time_to_str(fs_meta->mtime, timeBuf));
-        tsk_fprintf(a_hFile, "Accessed:\t%s\n",
-            tsk_fs_time_to_str(fs_meta->atime, timeBuf));
-        tsk_fprintf(a_hFile, "Created:\t%s\n",
-            tsk_fs_time_to_str(fs_meta->crtime, timeBuf));
-
-        if (fs_meta->mtime == 0)
-            fs_meta->mtime += a_sec_skew;
-        if (fs_meta->atime == 0)
-            fs_meta->atime += a_sec_skew;
-        if (fs_meta->crtime == 0)
-            fs_meta->crtime += a_sec_skew;
-
-        tsk_fprintf(a_hFile, "\nOriginal Directory Entry Times:\n");
-    }
-    else {
-        tsk_fprintf(a_hFile, "\nDirectory Entry Times:\n");
-    }
-
-    tsk_fprintf(a_hFile, "Written:\t%s\n", tsk_fs_time_to_str(fs_meta->mtime,
-            timeBuf));
-    tsk_fprintf(a_hFile, "Accessed:\t%s\n",
-        tsk_fs_time_to_str(fs_meta->atime, timeBuf));
-    tsk_fprintf(a_hFile, "Created:\t%s\n",
-        tsk_fs_time_to_str(fs_meta->crtime, timeBuf));
-
-    /* Print the specified number of sector addresses. */
-    tsk_fprintf(a_hFile, "\nSectors:\n");
-    if (a_numblock > 0) {
-        /* A bad hack to force a specified number of blocks */
-        fs_meta->size = a_numblock * a_fs->block_size;
-    }
-    print.istat_seen = 0;
-    print.idx = 0;
-    print.hFile = a_hFile;
-    if (tsk_fs_file_walk(fs_file,
-            (TSK_FS_FILE_WALK_FLAG_ENUM)(TSK_FS_FILE_WALK_FLAG_AONLY | TSK_FS_FILE_WALK_FLAG_SLACK),
-            print_addr_act, (void *) &print)) {
-        tsk_fprintf(a_hFile, "\nError reading file\n");
-        tsk_error_print(a_hFile);
-        tsk_error_reset();
-    }
-    else if (print.idx != 0) {
-        tsk_fprintf(a_hFile, "\n");
-    }
-
-    tsk_fs_file_close(fs_file);
-    return 0;
-}
-
-/* Mark the sector used in the bitmap */
-static TSK_WALK_RET_ENUM
-inode_walk_file_act(TSK_FS_FILE * fs_file, TSK_OFF_T a_off,
-    TSK_DADDR_T addr, char *buf, size_t size,
-    TSK_FS_BLOCK_FLAG_ENUM a_flags, void *a_ptr)
-{
-    setbit((uint8_t *) a_ptr, addr);
-    return TSK_WALK_CONT;
-}
-
-/* The inode_walk call back for each file.  we want only the directories */
-static TSK_WALK_RET_ENUM
-inode_walk_dent_act(TSK_FS_FILE * fs_file, const char *a_path, void *a_ptr)
-{
-    unsigned int flags = TSK_FS_FILE_WALK_FLAG_SLACK | TSK_FS_FILE_WALK_FLAG_AONLY;
-
-    if ((fs_file->meta == NULL)
-        || (fs_file->meta->type != TSK_FS_META_TYPE_DIR))
-        return TSK_WALK_CONT;
-
-    /* Get the sector addresses & ignore any errors */
-    if (tsk_fs_file_walk(fs_file,
-            (TSK_FS_FILE_WALK_FLAG_ENUM)flags,
-            inode_walk_file_act, a_ptr)) {
-        tsk_error_reset();
-    }
-
-    return TSK_WALK_CONT;
-}
-
-/**
- * Walk the inodes in a specified range and do a TSK_FS_META_WALK_CB callback
- * for each inode that satisfies criteria specified by a set of 
- * TSK_FS_META_FLAG_ENUM flags. The following flags are supported: 
- * TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC, TSK_FS_META_FLAG_ORPHAN,
- * TSK_FS_META_FLAG_USED (FATXX only), and TSK_FS_META_FLAG_UNUSED 
- * (FATXX only).
- *
- * @param [in] a_fs File system that contains the inodes.
- * @param [in] a_start_inum Inclusive lower bound of inode range.
- * @param [in] a_end_inum Inclusive upper bound of inode range.
- * @param [in] a_selection_flags Inode selection criteria.
- * @param [in] a_action Callback function for selected inodes.
- * @param [in] a_ptr Private data pointer passed through to callback function.
- * @return 0 on success, 1 on failure, per TSK convention
- */
-uint8_t
-fatfs_inode_walk(TSK_FS_INFO *a_fs, TSK_INUM_T a_start_inum,
-    TSK_INUM_T a_end_inum, TSK_FS_META_FLAG_ENUM a_selection_flags,
-    TSK_FS_META_WALK_CB a_action, void *a_ptr)
-{
-    char *func_name = "fatfs_inode_walk";
-    FATFS_INFO *fatfs = (FATFS_INFO*)a_fs;
-    unsigned int flags = a_selection_flags;
-    TSK_INUM_T end_inum_tmp = 0;
-    TSK_FS_FILE *fs_file =  NULL;
-    TSK_DADDR_T ssect = 0; 
-    TSK_DADDR_T lsect = 0; 
-    TSK_DADDR_T sect = 0; 
-    char *dino_buf = NULL;
-    FATFS_DENTRY *dep = NULL;
-    unsigned int dentry_idx = 0;
-    uint8_t *dir_sectors_bitmap = NULL;
-    ssize_t cnt = 0;
-    uint8_t done = 0;
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fs, "a_fs", func_name) ||
-        fatfs_ptr_arg_is_null(a_action, "a_action", func_name)) {
-        return 1;
-    }
-
-    if (a_start_inum < a_fs->first_inum || a_start_inum > a_fs->last_inum) {
-        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
-        tsk_error_set_errstr("%s: Begin inode out of range:  %" PRIuINUM "", 
-            func_name, a_start_inum);
-        return 1;
-    }
-    else if (a_end_inum < a_fs->first_inum || 
-             a_end_inum > a_fs->last_inum ||
-             a_end_inum < a_start_inum) {
-        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
-        tsk_error_set_errstr("%s: End inode out of range: %" PRIuINUM "", 
-            func_name, a_end_inum);
-        return 1;
-    }
-
-    // RJCTODO: Check this decision with Brian after he polls the user community.
-    /* FAT file systems do not really have the concept of unused inodes. */
-    if ((flags & TSK_FS_META_FLAG_UNUSED) && !(flags & TSK_FS_META_FLAG_USED)) {
-        return 0;
-    }
-    flags |= TSK_FS_META_FLAG_USED;
-    flags &= ~TSK_FS_META_FLAG_UNUSED;
-
-    /* Make sure the inode selection flags are set correctly. */
-    if (flags & TSK_FS_META_FLAG_ORPHAN) {
-        /* If ORPHAN file inodes are wanted, make sure that the UNALLOC
-         * selection flag is set. */
-        flags |= TSK_FS_META_FLAG_UNALLOC;
-        flags &= ~TSK_FS_META_FLAG_ALLOC;
-    }
-    else {
-        /* If neither of the ALLOC or UNALLOC inode selection flags are set,
-        *  then set them both. */
-        if (((flags & TSK_FS_META_FLAG_ALLOC) == 0) &&
-            ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) {
-            flags |= (TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC);
-        }
-    }
-
-    if (tsk_verbose) {
-        tsk_fprintf(stderr,
-            "%s: Inode walking %" PRIuINUM " to %"
-            PRIuINUM "\n", func_name, a_start_inum, a_end_inum);
-    }
-
-    /* If we are looking for orphan files and have not yet populated
-     * the list of files reachable by name for this file system, do so now.
-     */
-    if ((flags & TSK_FS_META_FLAG_ORPHAN)) {
-        if (tsk_fs_dir_load_inum_named(a_fs) != TSK_OK) {
-            tsk_error_errstr2_concat(
-                "%s: Identifying orphan inodes", func_name);
-            return 1;
-        }
-    }
-
-    /* Allocate a TSK_FS_FILE object with a TSK_FS_META object to populate and 
-     * pass to the callback function when an inode that fits the inode 
-     * selection criteria is found. */
-    if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) {
-        return 1;
-    }
-
-    if ((fs_file->meta =
-            tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) {
-        return 1;
-    }
-
-    /* Process the root directory inode, if it's included in the walk. */
-    if (a_start_inum == a_fs->root_inum) {
-        if (((TSK_FS_META_FLAG_ALLOC & flags) == TSK_FS_META_FLAG_ALLOC)
-            && ((TSK_FS_META_FLAG_ORPHAN & flags) == 0)) {
-            TSK_WALK_RET_ENUM retval = TSK_WALK_CONT;
-
-            if (fatfs_make_root(fatfs, fs_file->meta)) {
-                tsk_fs_file_close(fs_file);
-                return 1;
-            }
-
-            retval = a_action(fs_file, a_ptr);
-            if (retval == TSK_WALK_STOP) {
-                tsk_fs_file_close(fs_file);
-                return 0;
-            }
-            else if (retval == TSK_WALK_ERROR) {
-                tsk_fs_file_close(fs_file);
-                return 1;
-            }
-        }
-
-        a_start_inum++;
-        if (a_start_inum == a_end_inum) {
-            tsk_fs_file_close(fs_file);
-            return 0;
-        }
-    }
-
-    /* Allocate a bitmap to keep track of which sectors are allocated to
-     * directories. */
-    if ((dir_sectors_bitmap =
-            (uint8_t*)tsk_malloc((size_t) ((a_fs->block_count +
-                        7) / 8))) == NULL) {
-        tsk_fs_file_close(fs_file);
-        return 1;
-    }
-
-    /* If not doing an orphan files search, populate the directory sectors 
-     * bitmap. The bitmap will be used to make sure that no sector marked as
-     * allocated to a directory is skipped when searching for directory 
-     * entries to map to inodes. */
-    if ((flags & TSK_FS_META_FLAG_ORPHAN) == 0) {
-        if (tsk_verbose) {
-            tsk_fprintf(stderr,
-                "fatfs_inode_walk: Walking directories to collect sector info\n");
-        }
-
-        /* Manufacture an inode for the root directory. */
-        if (fatfs_make_root(fatfs, fs_file->meta)) {
-            tsk_fs_file_close(fs_file);
-            free(dir_sectors_bitmap);
-            return 1;
-        }
-
-        /* Do a file_walk on the root directory to set the bits in the 
-         * directory sectors bitmap for each sector allocated to the root
-         * directory. */
-        if (tsk_fs_file_walk(fs_file,
-                (TSK_FS_FILE_WALK_FLAG_ENUM)(TSK_FS_FILE_WALK_FLAG_SLACK | TSK_FS_FILE_WALK_FLAG_AONLY),
-                inode_walk_file_act, (void*)dir_sectors_bitmap)) {
-            tsk_fs_file_close(fs_file);
-            free(dir_sectors_bitmap);
-            return 1;
-        }
-
-        /* Now walk recursively through the entire directory tree to set the 
-         * bits in the directory sectors bitmap for each sector allocated to 
-         * the children of the root directory. */
-        if (tsk_fs_dir_walk(a_fs, a_fs->root_inum,
-                (TSK_FS_DIR_WALK_FLAG_ENUM)(TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE |
-                TSK_FS_DIR_WALK_FLAG_NOORPHAN), inode_walk_dent_act,
-                (void *) dir_sectors_bitmap)) {
-            tsk_error_errstr2_concat
-                ("- fatfs_inode_walk: mapping directories");
-            tsk_fs_file_close(fs_file);
-            free(dir_sectors_bitmap);
-            return 1;
-        }
-    }
-
-    /* If the end inode is the one of the virtual virtual FAT files or the 
-     * virtual orphan files directory, adjust the end inum and handle the 
-     * virtual inodes after the main inode walking loop below completes. */
-    if (a_end_inum > a_fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs)) {
-        end_inum_tmp = a_fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs);
-    }
-    else {
-        end_inum_tmp = a_end_inum;
-    }
-
-    /* Map the begin and end inodes to the sectors that contain them. 
-     * This sets the image level boundaries for the inode walking loop. */
-    ssect = FATFS_INODE_2_SECT(fatfs, a_start_inum);
-    if (ssect > a_fs->last_block) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
-        tsk_error_set_errstr
-            ("%s: Begin inode in sector too big for image: %"
-            PRIuDADDR, func_name, ssect);
-        tsk_fs_file_close(fs_file);
-        free(dir_sectors_bitmap);
-        return 1;
-    }
-
-    lsect = FATFS_INODE_2_SECT(fatfs, end_inum_tmp);
-    if (lsect > a_fs->last_block) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
-        tsk_error_set_errstr
-            ("%s: End inode in sector too big for image: %"
-            PRIuDADDR, func_name, lsect);
-        tsk_fs_file_close(fs_file);
-        free(dir_sectors_bitmap);
-        return 1;
-    }
-
-    /* Allocate a buffer big enough to read in a cluster at a time. */
-    if ((dino_buf = (char*)tsk_malloc(fatfs->csize << fatfs->ssize_sh)) ==
-        NULL) {
-        tsk_fs_file_close(fs_file);
-        free(dir_sectors_bitmap);
-        return 1;
-    }
-
-    /* Walk the inodes. */
-    sect = ssect;
-    while (sect <= lsect) {
-        int cluster_is_alloc = 0;
-        size_t num_sectors_to_process = 0;       
-        size_t sector_idx = 0;            
-        uint8_t do_basic_dentry_test = 0; 
-
-        /* Read in a chunk of the image to process on this iteration of the inode
-         * walk. The actual size of the read will depend on whether or not it is 
-         * coming from the root directory of a FAT12 or FAT16 file system. As 
-         * indicated by the size of the buffer, the data area (exFAT cluster 
-         * heap) will for the most part be read in a cluster at a time. 
-         * However, the root directory for a FAT12/FAT16 file system precedes 
-         * the data area and the read size for it should be a sector, not a 
-         * cluster. */
-        if (sect < fatfs->firstclustsect) {
-
-            if ((flags & TSK_FS_META_FLAG_ORPHAN) != 0) {
-                /* If orphan file hunting, there are no orphans in the root 
-                 * directory, so skip ahead to the data area. */
-                sect = fatfs->firstclustsect;
-                continue;
-            }
-
-            /* Read in a FAT12/FAT16 root directory sector. */
-            cnt = tsk_fs_read_block(a_fs, sect, dino_buf, fatfs->ssize);
-            if (cnt != fatfs->ssize) {
-                if (cnt >= 0) {
-                    tsk_error_reset();
-                    tsk_error_set_errno(TSK_ERR_FS_READ);
-                }
-                tsk_error_set_errstr2
-                    ("&s (root dir): sector: %" PRIuDADDR,
-                    func_name, sect);
-                tsk_fs_file_close(fs_file);
-                free(dir_sectors_bitmap);
-                free(dino_buf);
-                return 1;
-            }
-
-            cluster_is_alloc = 1;
-            num_sectors_to_process = 1;
-        }
-        else {
-            /* The walk has proceeded into the data area (exFAT cluster heap).
-             * It's time to read in a cluster at a time. Get the base sector 
-             * for the cluster that contains the current sector. */
-            sect =
-                FATFS_CLUST_2_SECT(fatfs, (FATFS_SECT_2_CLUST(fatfs,
-                        sect)));
-
-            /* Determine whether the cluster is allocated. Skip it if it is
-             * not allocated and the UNALLOCATED inode selection flag is not 
-             * set. */
-            cluster_is_alloc = fatfs_is_sectalloc(fatfs, sect);
-            if ((cluster_is_alloc == 0)
-                && ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) {
-                sect += fatfs->csize;
-                continue;
-            }
-            else if (cluster_is_alloc == -1) {
-                tsk_fs_file_close(fs_file);
-                free(dir_sectors_bitmap);
-                free(dino_buf);
-                return 1;
-            }
-
-            /* If the cluster is allocated but is not allocated to a 
-             * directory, then skip it.  NOTE: This will miss orphan file 
-             * entries in the slack space of files.
-             */
-            if ((cluster_is_alloc == 1) && (isset(dir_sectors_bitmap, sect) == 0)) {
-                sect += fatfs->csize;
-                continue;
-            }
-
-            /* The final cluster may not be full. */
-            if (lsect - sect + 1 < fatfs->csize) {
-                num_sectors_to_process = (size_t) (lsect - sect + 1);
-            }
-            else {
-                num_sectors_to_process = fatfs->csize;
-            }
-
-            /* Read in a cluster. */
-            cnt = tsk_fs_read_block
-                (a_fs, sect, dino_buf, num_sectors_to_process << fatfs->ssize_sh);
-            if (cnt != (num_sectors_to_process << fatfs->ssize_sh)) {
-                if (cnt >= 0) {
-                    tsk_error_reset();
-                    tsk_error_set_errno(TSK_ERR_FS_READ);
-                }
-                tsk_error_set_errstr2("%s: sector: %"
-                    PRIuDADDR, func_name, sect);
-                tsk_fs_file_close(fs_file);
-                free(dir_sectors_bitmap);
-                free(dino_buf);
-                return 1;
-            }
-        }
-
-        // RJCTODO: Isn't this unnecessary given the skipping of unallocated
-        // clusters above?
-        /* Now that the sectors are read in, prepare to step through them in 
-         * directory entry size chunks. Only do a basic test to confirm the 
-         * contents of each chunk is a directory entry unless the sector that
-         * contains it is not allocated to a directory or is unallocated.*/
-        do_basic_dentry_test = 1;
-        if ((isset(dir_sectors_bitmap, sect) == 0) || (cluster_is_alloc == 0)) {
-            do_basic_dentry_test = 0;
-        }
-
-        /* Walk through the sectors read in. */
-        for (sector_idx = 0; sector_idx < num_sectors_to_process; sector_idx++) {
-            TSK_INUM_T inum = 0;
-
-            /* If the last inode in this sector is before the start 
-             * inode, skip the sector. */
-            if (FATFS_SECT_2_INODE(fatfs, sect + 1) < a_start_inum) {
-                sect++;
-                continue;
-            }
-
-            /* Advance the directory entry pointer to the start of the 
-             * sector. */
-            dep = (FATFS_DENTRY*)(&dino_buf[sector_idx << fatfs->ssize_sh]);
-
-            /* If the sector is not allocated to a directory and the first 
-             * chunk is not a directory entry, skip the sector. */
-            if (!isset(dir_sectors_bitmap, sect) &&
-                !fatfs->is_dentry(fatfs, dep, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)cluster_is_alloc, do_basic_dentry_test)) {
-                sect++;
-                continue;
-            }
-
-            /* Get the base inode address of this sector. */
-            inum = FATFS_SECT_2_INODE(fatfs, sect);
-            if (tsk_verbose) {
-                tsk_fprintf(stderr,
-                    "%s: Processing sector %" PRIuDADDR
-                    " starting at inode %" PRIuINUM "\n", func_name, sect, inum);
-            }
-
-            /* Walk through the potential directory entries in the sector. */
-            for (dentry_idx = 0; dentry_idx < fatfs->dentry_cnt_se;
-                dentry_idx++, inum++, dep++) {
-                int retval;
-                EXFATFS_DIR_ENTRY_TYPE_ENUM dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
-                TSK_RETVAL_ENUM retval2 = TSK_OK;
-
-                /* If the inode address of the potential entry is less than
-                 * the beginning inode address for the inode walk, skip it. */
-                if (inum < a_start_inum) {
-                    continue;
-                }
-
-                /* If inode address of the potential entry is greater than the
-                 * ending inode address for the walk, terminate the inode walk. */ 
-                if (inum > end_inum_tmp) {
-                    done = 1;
-                    break;
-                }
-
-                /* If the potential entry is likely not an entry, or it is an  
-                 * entry that is not reported in an inode walk, or it does not   
-                 * satisfy the inode selection flags, then skip it. */
-                if (!fatfs->is_dentry(fatfs, dep, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)cluster_is_alloc, do_basic_dentry_test) ||
-                    fatfs->inode_walk_should_skip_dentry(fatfs, inum, dep, flags, cluster_is_alloc)) {
-                    continue;
-                }
-
-                retval2 = fatfs->dinode_copy(fatfs, inum, dep, cluster_is_alloc, fs_file);
-
-                if (retval2 != TSK_OK) {
-                    if (retval2 == TSK_COR) {
-                        /* Corrupted, move on to the next chunk. */
-                        if (tsk_verbose) {
-                            tsk_error_print(stderr);
-                        }
-                        tsk_error_reset();
-                        continue;
-                    }
-                    else {
-                        tsk_fs_file_close(fs_file);
-                        free(dir_sectors_bitmap);
-                        free(dino_buf);
-                        return 1;
-                    }
-                }
-
-                if (tsk_verbose) {
-                    tsk_fprintf(stderr,
-                        "%s: Directory Entry %" PRIuINUM
-                        " (%u) at sector %" PRIuDADDR "\n", func_name, inum, dentry_idx,
-                        sect);
-                }
-
-                /* Do the callback. */
-                retval = a_action(fs_file, a_ptr);
-                if (retval == TSK_WALK_STOP) {
-                    tsk_fs_file_close(fs_file);
-                    free(dir_sectors_bitmap);
-                    free(dino_buf);
-                    return 0;
-                }
-                else if (retval == TSK_WALK_ERROR) {
-                    tsk_fs_file_close(fs_file);
-                    free(dir_sectors_bitmap);
-                    free(dino_buf);
-                    return 1;
-                }
-            }                  
-            sect++;
-            if (done) {
-                break;
-            }
-        }
-        if (done) {
-            break;
-        }
-    }
-
-    free(dir_sectors_bitmap);
-    free(dino_buf);
-
-    // handle the virtual orphans folder and FAT files if they asked for them
-    if ((a_end_inum > a_fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs))
-        && (flags & TSK_FS_META_FLAG_ALLOC)
-        && ((flags & TSK_FS_META_FLAG_ORPHAN) == 0)) {
-        TSK_INUM_T inum;
-
-        // cycle through the special files
-        for (inum = a_fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs) + 1;
-            inum <= a_end_inum; inum++) {
-            int retval;
-
-            tsk_fs_meta_reset(fs_file->meta);
-
-            if (inum == fatfs->mbr_virt_inum) {
-                if (fatfs_make_mbr(fatfs, fs_file->meta)) {
-                    tsk_fs_file_close(fs_file);
-                    return 1;
-                }
-            }
-            else if (inum == fatfs->fat1_virt_inum) {
-                if (fatfs_make_fat(fatfs, 1, fs_file->meta)) {
-                    tsk_fs_file_close(fs_file);
-                    return 1;
-                }
-            }
-            else if (inum == fatfs->fat2_virt_inum && fatfs->numfat == 2) {
-                if (fatfs_make_fat(fatfs, 2, fs_file->meta)) {
-                    tsk_fs_file_close(fs_file);
-                    return 1;
-                }
-            }
-            else if (inum == TSK_FS_ORPHANDIR_INUM(a_fs)) {
-                if (tsk_fs_dir_make_orphan_dir_meta(a_fs, fs_file->meta)) {
-                    tsk_fs_file_close(fs_file);
-                    return 1;
-                }
-            }
-
-            retval = a_action(fs_file, a_ptr);
-            if (retval == TSK_WALK_STOP) {
-                tsk_fs_file_close(fs_file);
-                return 0;
-            }
-            else if (retval == TSK_WALK_ERROR) {
-                tsk_fs_file_close(fs_file);
-                return 1;
-            }
-        }
-    }
-
-    tsk_fs_file_close(fs_file);
-    return 0;
+/*
+** fatfs
+** The Sleuth Kit
+**
+** Meta data layer support for the FAT file system.
+**
+** Brian Carrier [carrier <at> sleuthkit [dot] org]
+** Copyright (c) 2006-2013 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
+**
+** TASK
+** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
+**
+**
+** This software is distributed under the Common Public License 1.0
+**
+** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05)
+**
+*/
+
+/**
+ * \file fatfs_meta.c
+ * Meta data layer support for FAT file systems.
+ */
+
+#include "tsk_fatfs.h"
+#include "tsk_fatxxfs.h"
+#include "tsk_exfatfs.h"
+
+TSK_FS_ATTR_TYPE_ENUM
+fatfs_get_default_attr_type(const TSK_FS_FILE * a_file)
+{
+    return TSK_FS_ATTR_TYPE_DEFAULT;
+}
+
+/**
+ * \internal
+ * Create an TSK_FS_META structure for the root directory.  FAT does
+ * not have a directory entry for the root directory, but this
+ * function collects the data needed to make one.
+ *
+ * @param fatfs File system to analyze.
+ * @param fs_meta Inode structure to copy root directory information into.
+ * @return 1 on error and 0 on success
+ */
+static uint8_t
+fatfs_make_root(FATFS_INFO *a_fatfs, TSK_FS_META *a_fs_meta)
+{
+    const char *func_name = "fatfs_make_root";
+    TSK_DADDR_T *first_clust_addr_ptr = NULL;
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
+        fatfs_ptr_arg_is_null(a_fs_meta, "a_fs_meta", func_name)) {
+        return 1;
+    }
+
+    /* Manufacture some metadata. */
+    a_fs_meta->type = TSK_FS_META_TYPE_DIR;
+    a_fs_meta->mode = TSK_FS_META_MODE_UNSPECIFIED;
+    a_fs_meta->nlink = 1;
+    a_fs_meta->addr = FATFS_ROOTINO;
+    a_fs_meta->flags = (TSK_FS_META_FLAG_ENUM)(TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC);
+    a_fs_meta->uid = a_fs_meta->gid = 0;
+    a_fs_meta->mtime = a_fs_meta->atime = a_fs_meta->ctime = a_fs_meta->crtime = 0;
+    a_fs_meta->mtime_nano = a_fs_meta->atime_nano = a_fs_meta->ctime_nano =
+        a_fs_meta->crtime_nano = 0;
+
+    /* Give the root directory an empty name. */
+    if (a_fs_meta->name2 == NULL) {
+        if ((a_fs_meta->name2 = (TSK_FS_META_NAME_LIST *)
+                tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) {
+            return 1;
+        }
+        a_fs_meta->name2->next = NULL;
+    }
+    a_fs_meta->name2->name[0] = '\0';
+
+    /* Mark the generic attribute list as not in use (in the generic file model
+     * attributes are containers for data or metadata). Population of this 
+     * list is done by lazy look up. */
+    a_fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
+    if (a_fs_meta->attr) {
+        tsk_fs_attrlist_markunused(a_fs_meta->attr);
+    }
+
+    /* Determine the size of the root directory and the address of its 
+     * first cluster. */
+    first_clust_addr_ptr = (TSK_DADDR_T*)a_fs_meta->content_ptr;
+    if (a_fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32 ||
+        a_fatfs->fs_info.ftype == TSK_FS_TYPE_EXFAT) {
+        TSK_DADDR_T cnum = 0;
+        TSK_DADDR_T clust = 0;
+        TSK_LIST *list_seen = NULL;
+
+        /* Convert the address of the first sector of the root directory into
+         * the address of its first cluster. */
+        clust = FATFS_SECT_2_CLUST(a_fatfs, a_fatfs->rootsect);
+        first_clust_addr_ptr[0] = clust;
+
+        /* Walk the FAT and count the clusters allocated to the root directory. */
+        cnum = 0;
+        while ((clust) && (0 == FATFS_ISEOF(clust, FATFS_32_MASK))) {
+            TSK_DADDR_T nxt = 0;
+
+            /* Make sure we do not get into an infinite loop */
+            if (tsk_list_find(list_seen, clust)) {
+                if (tsk_verbose) {
+                    tsk_fprintf(stderr,
+                        "Loop found while determining root directory size\n");
+                }
+                break;
+            }
+            if (tsk_list_add(&list_seen, clust)) {
+                tsk_list_free(list_seen);
+                list_seen = NULL;
+                return 1;
+            }
+
+            cnum++;
+            if (fatfs_getFAT(a_fatfs, clust, &nxt)) {
+                break;
+            }
+            else {
+                clust = nxt;
+            }
+        }
+        tsk_list_free(list_seen);
+        list_seen = NULL;
+
+        /* Calculate the size of the root directory. */
+        a_fs_meta->size = (cnum * a_fatfs->csize) << a_fatfs->ssize_sh;
+    }
+    else {
+        /* FAT12 and FAT16 don't use the FAT for the root directory, so set 
+         * the first cluster address to a distinguished value that other code
+         * will have to check as a special condition. */ 
+        first_clust_addr_ptr[0] = 1;
+
+        /* Set the size equal to the number of bytes between the end of the 
+         * FATs and the start of the clusters. */
+        a_fs_meta->size = (a_fatfs->firstclustsect - a_fatfs->firstdatasect) << a_fatfs->ssize_sh;
+    }
+
+    return 0;
+}
+
+/**
+* \internal
+ * Create an TSK_FS_META structure for the master boot record.
+ *
+ * @param fatfs File system to analyze
+ * @param fs_meta Inode structure to copy file information into.
+ * @return 1 on error and 0 on success
+ */
+static uint8_t
+fatfs_make_mbr(FATFS_INFO *fatfs, TSK_FS_META *fs_meta)
+{
+    TSK_DADDR_T *addr_ptr;
+    TSK_FS_INFO *fs = (TSK_FS_INFO *) fatfs;
+
+    fs_meta->type = TSK_FS_META_TYPE_VIRT;
+    fs_meta->mode = TSK_FS_META_MODE_UNSPECIFIED;
+    fs_meta->nlink = 1;
+    fs_meta->addr = fatfs->mbr_virt_inum;
+    fs_meta->flags = (TSK_FS_META_FLAG_ENUM)
+        (TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC);
+    fs_meta->uid = fs_meta->gid = 0;
+    fs_meta->mtime = fs_meta->atime = fs_meta->ctime = fs_meta->crtime = 0;
+    fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano =
+        fs_meta->crtime_nano = 0;
+
+    if (fs_meta->name2 == NULL) {
+        if ((fs_meta->name2 = (TSK_FS_META_NAME_LIST *)
+                tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) {
+            return 1;
+        }
+        fs_meta->name2->next = NULL;
+    }
+    strncpy(fs_meta->name2->name, FATFS_MBRNAME,
+        TSK_FS_META_NAME_LIST_NSIZE);
+
+    fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
+    if (fs_meta->attr) {
+        tsk_fs_attrlist_markunused(fs_meta->attr);
+    }
+
+    addr_ptr = (TSK_DADDR_T*)fs_meta->content_ptr;
+    addr_ptr[0] = 0;
+    fs_meta->size = 512;
+
+    return 0;
+}
+
+/**
+* \internal
+ * Create an TSK_FS_META structure for the FAT tables.
+ *
+ * @param fatfs File system to analyze
+ * @param a_which 1 or 2 to choose between defining FAT1 or FAT2
+ * @param fs_meta Inode structure to copy file information into.
+ * @return 1 on error and 0 on success
+ */
+static uint8_t
+fatfs_make_fat(FATFS_INFO *fatfs, uint8_t a_which, TSK_FS_META *fs_meta)
+{
+    TSK_FS_INFO *fs = (TSK_FS_INFO*)fatfs;
+    TSK_DADDR_T *addr_ptr = (TSK_DADDR_T *)fs_meta->content_ptr;
+
+    if ((a_which != 1) && (a_which != 2)) {
+        return 1;
+    }
+
+    if (a_which > fatfs->numfat) {
+        return 1;
+    }
+
+    fs_meta->type = TSK_FS_META_TYPE_VIRT;
+    fs_meta->mode = TSK_FS_META_MODE_UNSPECIFIED;
+    fs_meta->nlink = 1;
+
+    fs_meta->flags = (TSK_FS_META_FLAG_ENUM)
+        (TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC);
+    fs_meta->uid = fs_meta->gid = 0;
+    fs_meta->mtime = fs_meta->atime = fs_meta->ctime = fs_meta->crtime = 0;
+    fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano =
+        fs_meta->crtime_nano = 0;
+
+    if (fs_meta->name2 == NULL) {
+        if ((fs_meta->name2 = (TSK_FS_META_NAME_LIST *)
+                tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL)
+            return 1;
+        fs_meta->name2->next = NULL;
+    }
+
+    if (a_which == 1) {
+        fs_meta->addr = fatfs->fat1_virt_inum;
+        strncpy(fs_meta->name2->name, FATFS_FAT1NAME,
+            TSK_FS_META_NAME_LIST_NSIZE);
+        addr_ptr[0] = fatfs->firstfatsect;
+    }
+    else {
+        fs_meta->addr = fatfs->fat2_virt_inum;
+        strncpy(fs_meta->name2->name, FATFS_FAT2NAME,
+            TSK_FS_META_NAME_LIST_NSIZE);
+        addr_ptr[0] = fatfs->firstfatsect + fatfs->sectperfat;
+    }
+
+    fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
+    if (fs_meta->attr) {
+        tsk_fs_attrlist_markunused(fs_meta->attr);
+    }
+
+    fs_meta->size = fatfs->sectperfat * fs->block_size;
+
+    return 0;
+}
+
+/**
+ * \internal
+ * Load a FATFS_DENTRY structure with the bytes at a given inode address.
+ *
+ * @param [in] a_fs The file system from which to read the bytes.
+ * @param [out] a_de The FATFS_DENTRY.
+ * @param [in] a_inum An inode address.
+ * @return 0 on success, 1 on failure. 
+ */
+uint8_t
+fatfs_dentry_load(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum)
+{
+    const char *func_name = "fatfs_dentry_load";
+    TSK_FS_INFO *fs = (TSK_FS_INFO*)a_fatfs;
+    TSK_DADDR_T sect = 0;
+    size_t off = 0;
+    ssize_t cnt = 0;
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
+        fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name) ||
+        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
+        return 1;
+    }
+    
+    /* Map the inode address to a sector. */
+    sect = FATFS_INODE_2_SECT(a_fatfs, a_inum);
+    if (sect > fs->last_block) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr("%s: Inode %" PRIuINUM
+            " in sector too big for image: %" PRIuDADDR, func_name, a_inum, sect);
+        return 1;
+    }
+
+    /* Get the byte offset of the inode address within the sector. */
+    off = FATFS_INODE_2_OFF(a_fatfs, a_inum);
+
+    /* Read in the bytes. */
+    cnt = tsk_fs_read(fs, sect * fs->block_size + off, (char*)a_dentry, sizeof(FATFS_DENTRY));
+    if (cnt != sizeof(FATFS_DENTRY)) {
+        if (cnt >= 0) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_READ);
+        }
+        tsk_error_set_errstr2("%s: block: %" PRIuDADDR,
+            func_name, sect);
+        return 1;
+    }
+
+    return 0;
+}
+
+/**
+ * \internal
+ * Populate the TSK_FS_META structure of a TSK_FS_FILE structure for a 
+ * given inode address.
+ *
+ * @param [in] a_fs File system that contains the inode.
+ * @param [out] a_fs_file The file corresponding to the inode.
+ * @param [in] a_inum The inode address.
+ * @returns 1 if an error occurs or if the inode address is not
+ * for a valid inode, 0 otherwise.
+ */
+uint8_t
+fatfs_inode_lookup(TSK_FS_INFO *a_fs, TSK_FS_FILE *a_fs_file,
+    TSK_INUM_T a_inum)
+{
+    const char *func_name = "fatfs_inode_lookup";
+    FATFS_INFO *fatfs = (FATFS_INFO*)a_fs;
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fs, "a_fs", func_name) ||
+        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name) ||
+        !fatfs_inum_arg_is_in_range(fatfs, a_inum, func_name)) {
+        return 1;
+    }
+
+    /* Allocate or reset the TSK_FS_META struct. */
+    if (a_fs_file->meta == NULL) {
+        if ((a_fs_file->meta =
+                tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) {
+            return 1;
+        }
+    }
+    else {
+        tsk_fs_meta_reset(a_fs_file->meta);
+    }
+
+    /* Manufacture an inode for the root directory or a FAT virtual file,
+     * or do a look up. */
+    if (a_inum == a_fs->root_inum) {
+        if (fatfs_make_root(fatfs, a_fs_file->meta))
+            return 1;
+        else
+            return 0;
+    }
+    else if (a_inum == fatfs->mbr_virt_inum) {
+        if (fatfs_make_mbr(fatfs, a_fs_file->meta))
+            return 1;
+        else
+            return 0;
+    }
+    else if (a_inum == fatfs->fat1_virt_inum) {
+        if (fatfs_make_fat(fatfs, 1, a_fs_file->meta))
+            return 1;
+        else
+            return 0;
+    }
+    else if (a_inum == fatfs->fat2_virt_inum && fatfs->numfat == 2) {
+        if (fatfs_make_fat(fatfs, 2, a_fs_file->meta))
+            return 1;
+        else
+            return 0;
+    }
+    else if (a_inum == TSK_FS_ORPHANDIR_INUM(a_fs)) {
+        if (tsk_fs_dir_make_orphan_dir_meta(a_fs, a_fs_file->meta))
+            return 1;
+        else
+            return 0;
+    }
+    else {
+        return fatfs->inode_lookup(fatfs, a_fs_file, a_inum);
+    }
+}
+
+/** \internal
+ * Make data runs out of the clusters allocated to a file represented by a 
+ * TSK_FS_FILE structure. Each data run will have a starting sector and a 
+ * length in sectors. The runs will be stored as a non-resident attribute in 
+ * the TSK_FS_ATTRLIST of the TSK_FS_META structure of the TSK_FS_FILE. 
+ *
+ * @param a_fs_file A representation of a file.
+ * @return 1 on error and 0 on success
+ */
+uint8_t
+fatfs_make_data_runs(TSK_FS_FILE * a_fs_file)
+{
+    const char *func_name = "fatfs_make_data_runs";
+    TSK_FS_INFO *fs = NULL;
+    TSK_FS_META *fs_meta = NULL;
+    FATFS_INFO *fatfs = NULL;
+    TSK_DADDR_T clust = 0;
+    TSK_OFF_T size_remain = 0;
+    TSK_FS_ATTR *fs_attr = NULL;
+
+    if ((fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name)) ||
+        (fatfs_ptr_arg_is_null(a_fs_file->meta, "a_fs_file->meta", func_name)) ||
+        (fatfs_ptr_arg_is_null(a_fs_file->fs_info, "a_fs_file->fs_info", func_name))) {
+        return TSK_ERR;
+    }
+    
+    fs_meta = a_fs_file->meta;
+    fs = a_fs_file->fs_info;
+    fatfs = (FATFS_INFO*)fs;
+
+    /* Check for an already populated attribute list, since a lazy strategy
+     * is used to fill in attributes. If the attribute list is not yet 
+     * allocated, do so now. */  
+    if ((fs_meta->attr != NULL)
+        && (fs_meta->attr_state == TSK_FS_META_ATTR_STUDIED)) {
+        return 0;
+    }
+    else if (fs_meta->attr_state == TSK_FS_META_ATTR_ERROR) {
+        return 1;
+    }
+    else if (fs_meta->attr != NULL) {
+        tsk_fs_attrlist_markunused(fs_meta->attr);
+    }
+    else if (fs_meta->attr == NULL) {
+        fs_meta->attr = tsk_fs_attrlist_alloc();
+    }
+
+    /* Get the stashed first cluster address of the file. */
+    clust = ((TSK_DADDR_T*)fs_meta->content_ptr)[0];
+    if ((clust > (fatfs->lastclust)) &&
+        (FATFS_ISEOF(clust, fatfs->mask) == 0)) {
+        fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+        tsk_error_reset();
+        if (a_fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC) {
+            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
+        }
+        else {
+            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        }
+        tsk_error_set_errstr
+            ("%s: Starting cluster address too large: %"
+            PRIuDADDR, func_name, clust);
+        return 1;
+    }
+
+    /* Figure out the allocated length of the file in bytes. Because the
+     * allocation unit for FAT file systems is the cluster, round the
+     * size up to a multiple of cluster size. */
+    size_remain = roundup(fs_meta->size, fatfs->csize * fs->block_size);
+
+    // RJCTODO: Consider addressing the code duplication below.
+    // RJCTODO: Consider breaking out each of the conditional blocks into its own sub-function.
+    if ((a_fs_file->meta->addr == fs->root_inum) && 
+        (fs->ftype != TSK_FS_TYPE_FAT32) &&
+        (fs->ftype != TSK_FS_TYPE_EXFAT) &&
+        (clust == 1)) {
+        /* Make a single contiguous data run for a FAT12 or FAT16 root 
+         * directory. The root directory for these file systems is not 
+         * tracked in the FAT. */
+        TSK_FS_ATTR_RUN *data_run;
+
+        if (tsk_verbose) {
+            tsk_fprintf(stderr,
+                "%s: Loading root directory\n", func_name);
+        }
+
+        /* Allocate the run. */
+        data_run = tsk_fs_attr_run_alloc();
+        if (data_run == NULL) {
+            return 1;
+        }
+
+        /* Set the starting sector address and run length. The run begins with 
+         * the first sector of the data area. */
+        data_run->addr = fatfs->rootsect;
+        data_run->len = fatfs->firstclustsect - fatfs->firstdatasect;
+
+        /* Allocate a non-resident attribute to hold the run and add it
+         to the attribute list. */
+        if ((fs_attr =
+                tsk_fs_attrlist_getnew(fs_meta->attr,
+                    TSK_FS_ATTR_NONRES)) == NULL) {
+            return 1;
+        }
+
+        /* Tie everything together. */
+        if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run, NULL,
+                TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
+                data_run->len * fs->block_size,
+                data_run->len * fs->block_size,
+                data_run->len * fs->block_size, 0, 0)) {
+            return 1;
+        }
+
+        fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
+
+        return 0;
+    }
+    else if ((a_fs_file->meta->addr >= fatfs->mbr_virt_inum) &&
+             (a_fs_file->meta->addr <= fatfs->mbr_virt_inum + fatfs->numfat)) {
+        /* Make a single contiguous data run for a virtual file (MBR, FAT). */ 
+        TSK_FS_ATTR_RUN *data_run;
+
+        if (tsk_verbose) {
+            tsk_fprintf(stderr,
+                "%s: Loading virtual file: %" PRIuINUM
+                "\n", func_name, a_fs_file->meta->addr);
+        }
+
+        /* Allocate the run. */
+        data_run = tsk_fs_attr_run_alloc();
+        if (data_run == NULL) {
+            return 1;
+        }
+
+        /* Set the starting sector address and run length. */
+        data_run->addr = clust;
+        data_run->len = a_fs_file->meta->size / fs->block_size;
+
+        /* Allocate a non-resident attribute to hold the run and add it
+         to the attribute list. */
+        if ((fs_attr =
+                tsk_fs_attrlist_getnew(fs_meta->attr,
+                    TSK_FS_ATTR_NONRES)) == NULL) {
+            return 1;
+        }
+
+        /* Tie everything together. */
+        if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run, NULL,
+                TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
+                data_run->len * fs->block_size,
+                data_run->len * fs->block_size,
+                data_run->len * fs->block_size, 0, 0)) {
+            return 1;
+        }
+
+        fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
+        return 0;
+    }
+    else if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC) {
+        /* Make data runs for a deleted file that we want to recover.
+         * In this case, we could get a lot of errors because of inconsistent
+         * data.  To make it clear that these are from a recovery, we set most
+         * error codes to _RECOVER so that they can be more easily suppressed.
+         */
+        TSK_DADDR_T sbase;
+        TSK_DADDR_T startclust = clust;
+        TSK_OFF_T recoversize = fs_meta->size;
+        int retval;
+        TSK_FS_ATTR_RUN *data_run = NULL;
+        TSK_FS_ATTR_RUN *data_run_head = NULL;
+        TSK_OFF_T full_len_s = 0;
+        uint8_t canRecover = 1; // set to 0 if recovery is not possible
+
+        if (tsk_verbose)
+            tsk_fprintf(stderr,
+                "%s: Processing deleted file %" PRIuINUM
+                " in recovery mode\n", func_name, fs_meta->addr);
+
+        /* We know the size and the starting cluster
+         *
+         * We are going to take the clusters from the starting cluster
+         * onwards and skip the clusters that are current allocated
+         */
+
+        /* Sanity checks on the starting cluster */
+        /* Convert the cluster addr to a sector addr */
+        sbase = FATFS_CLUST_2_SECT(fatfs, startclust);
+
+        if (sbase > fs->last_block) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
+            tsk_error_set_errstr
+                ("%s: Starting cluster address too large (recovery): %"
+                PRIuDADDR, func_name, sbase);
+            fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+            return 1;
+        }
+        else {
+
+            /* If the starting cluster is already allocated then we can't
+             * recover it */
+            retval = fatfs->is_cluster_alloc(fatfs, startclust);
+            if (retval != 0) {
+                canRecover = 0;
+            }
+        }
+
+        /* Part 1 is to make sure there are enough unallocated clusters
+         * for the size of the file
+         */
+        clust = startclust;
+        size_remain = recoversize;
+
+        // we could make this negative so sign it for the comparison
+        while (((int64_t) size_remain > 0) && (canRecover)) {
+            int retval;
+            sbase = FATFS_CLUST_2_SECT(fatfs, clust);
+
+            /* Are we past the end of the FS?
+             * that means we could not find enough unallocated clusters
+             * for the file size */
+            if (sbase + fatfs->csize - 1 > fs->last_block) {
+                canRecover = 0;
+
+                if (tsk_verbose)
+                    tsk_fprintf(stderr,
+                        "%s: Could not find enough unallocated sectors to recover with - aborting\n", func_name);
+                break;
+            }
+
+            /* Skip allocated clusters */
+            retval = fatfs->is_cluster_alloc(fatfs, clust);
+            if (retval == -1) {
+                canRecover = 0;
+                break;
+            }
+            else if (retval == 1) {
+                clust++;
+                continue;
+            }
+
+            /* We can use this sector */
+            // see if we need a new run
+            if ((data_run == NULL)
+                || (data_run->addr + data_run->len != sbase)) {
+
+                TSK_FS_ATTR_RUN *data_run_tmp = tsk_fs_attr_run_alloc();
+                if (data_run_tmp == NULL) {
+                    fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+                    tsk_fs_attr_run_free(data_run_head);
+                    return 1;
+                }
+
+                if (data_run_head == NULL) {
+                    data_run_head = data_run_tmp;
+                    data_run_tmp->offset = 0;
+                }
+                else if (data_run != NULL) {
+                    data_run->next = data_run_tmp;
+                    data_run_tmp->offset =
+                        data_run->offset + data_run->len;
+                }
+                data_run = data_run_tmp;
+                data_run->len = 0;
+                data_run->addr = sbase;
+            }
+            data_run->len += fatfs->csize;
+            full_len_s += fatfs->csize;
+
+            size_remain -= (fatfs->csize << fatfs->ssize_sh);
+            clust++;
+        }
+
+        // Get a FS_DATA structure and add the runlist to it
+        if ((fs_attr =
+                tsk_fs_attrlist_getnew(fs_meta->attr,
+                    TSK_FS_ATTR_NONRES)) == NULL) {
+            fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+            return 1;
+        }
+
+        if (canRecover) {
+            /* We can recover the file */
+
+            // initialize the data run
+            if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run_head,
+                    NULL, TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
+                    fs_meta->size, fs_meta->size, roundup(fs_meta->size,
+                        fatfs->csize * fs->block_size), 0, 0)) {
+                fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+                return 1;
+            }
+
+            fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
+        }
+        // create a one cluster run
+        else {
+            TSK_FS_ATTR_RUN *data_run_tmp = tsk_fs_attr_run_alloc();
+            if (data_run_tmp == NULL) {
+                fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+                return 1;
+            }
+            data_run_tmp->addr = sbase;
+            data_run_tmp->len = fatfs->csize;
+
+            // initialize the data run
+            if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run_tmp, NULL,
+                    TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
+                    fs_meta->size, fs_meta->size, roundup(fs_meta->size,
+                        fatfs->csize * fs->block_size), 0, 0)) {
+                fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+                return 1;
+            }
+
+            fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
+        }
+
+        return 0;
+    }
+    else {
+        // RJCTODO: Find and fix the bug that causes sectors to be printed incorrectly.
+        TSK_LIST *list_seen = NULL;
+        TSK_FS_ATTR_RUN *data_run = NULL;
+        TSK_FS_ATTR_RUN *data_run_head = NULL;
+        TSK_OFF_T full_len_s = 0;
+        TSK_DADDR_T sbase;
+        /* Do normal cluster chain walking for a file or directory, including
+         * FAT32 and exFAT root directories. */
+
+        if (tsk_verbose) {
+            tsk_fprintf(stderr,
+                "%s: Processing file %" PRIuINUM
+                " in normal mode\n", func_name, fs_meta->addr);
+        }
+
+        /* Cycle through the cluster chain */
+        while ((clust & fatfs->mask) > 0 && (int64_t) size_remain > 0 &&
+            (0 == FATFS_ISEOF(clust, fatfs->mask))) {
+
+            /* Convert the cluster addr to a sector addr */
+            sbase = FATFS_CLUST_2_SECT(fatfs, clust);
+
+            if (sbase + fatfs->csize - 1 > fs->last_block) {
+                fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+                tsk_error_reset();
+
+                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+                tsk_error_set_errstr
+                    ("%s: Invalid sector address in FAT (too large): %"
+                    PRIuDADDR " (plus %d sectors)", func_name, sbase, fatfs->csize);
+                return 1;
+            }
+
+            // see if we need a new run
+            if ((data_run == NULL)
+                || (data_run->addr + data_run->len != sbase)) {
+
+                TSK_FS_ATTR_RUN *data_run_tmp = tsk_fs_attr_run_alloc();
+                if (data_run_tmp == NULL) {
+                    tsk_fs_attr_run_free(data_run_head);
+                    fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+                    return 1;
+                }
+
+                if (data_run_head == NULL) {
+                    data_run_head = data_run_tmp;
+                    data_run_tmp->offset = 0;
+                }
+                else if (data_run != NULL) {
+                    data_run->next = data_run_tmp;
+                    data_run_tmp->offset =
+                        data_run->offset + data_run->len;
+                }
+                data_run = data_run_tmp;
+                data_run->len = 0;
+                data_run->addr = sbase;
+            }
+
+            data_run->len += fatfs->csize;
+            full_len_s += fatfs->csize;
+            size_remain -= (fatfs->csize * fs->block_size);
+
+            if ((int64_t) size_remain > 0) {
+                TSK_DADDR_T nxt;
+                if (fatfs_getFAT(fatfs, clust, &nxt)) {
+                    tsk_error_set_errstr2("%s: Inode: %" PRIuINUM
+                        "  cluster: %" PRIuDADDR, func_name, fs_meta->addr, clust);
+                    fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+                    tsk_fs_attr_run_free(data_run_head);
+                    tsk_list_free(list_seen);
+                    list_seen = NULL;
+                    return 1;
+                }
+                clust = nxt;
+
+                /* Make sure we do not get into an infinite loop */
+                if (tsk_list_find(list_seen, clust)) {
+                    if (tsk_verbose)
+                        tsk_fprintf(stderr,
+                            "Loop found while processing file\n");
+                    break;
+                }
+
+                if (tsk_list_add(&list_seen, clust)) {
+                    fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+                    tsk_list_free(list_seen);
+                    list_seen = NULL;
+                    return 1;
+                }
+            }
+        }
+
+        // add the run list to the inode structure
+        if ((fs_attr =
+                tsk_fs_attrlist_getnew(fs_meta->attr,
+                    TSK_FS_ATTR_NONRES)) == NULL) {
+            fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+            return 1;
+        }
+
+        // initialize the data run
+        if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run_head, NULL,
+                TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
+                fs_meta->size, fs_meta->size, roundup(fs_meta->size,
+                    fatfs->csize * fs->block_size), 0, 0)) {
+            fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
+            return 1;
+        }
+
+        tsk_list_free(list_seen);
+        list_seen = NULL;
+
+        fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
+
+        return 0;
+    }
+}
+
+/* Used for istat callback */
+typedef struct {
+    FILE *hFile;
+    int idx;
+    int istat_seen;
+} FATFS_PRINT_ADDR;
+
+/* Callback a_action for file_walk to print the sector addresses
+ * of a file, used for istat
+ */
+static TSK_WALK_RET_ENUM
+print_addr_act(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
+    char *buf, size_t size, TSK_FS_BLOCK_FLAG_ENUM a_flags, void *a_ptr)
+{
+    FATFS_PRINT_ADDR *print = (FATFS_PRINT_ADDR *) a_ptr;
+
+    tsk_fprintf(print->hFile, "%" PRIuDADDR " ", addr);
+
+    if (++(print->idx) == 8) {
+        tsk_fprintf(print->hFile, "\n");
+        print->idx = 0;
+    }
+    print->istat_seen = 1;
+
+    return TSK_WALK_CONT;
+}
+
+/**
+ * Print details on a specific file to a file handle. 
+ *
+ * @param a_fs File system file is located in.
+ * @param a_hFile File handle to print text to.
+ * @param a_inum Address of file in file system.
+ * @param a_numblock The number of blocks in file to force print (can go beyond file size).
+ * @param a_sec_skew Clock skew in seconds to also print times in.
+ * 
+ * @returns 1 on error and 0 on success.
+ */
+uint8_t
+fatfs_istat(TSK_FS_INFO *a_fs, FILE *a_hFile, TSK_INUM_T a_inum,
+    TSK_DADDR_T a_numblock, int32_t a_sec_skew)
+{
+    const char* func_name = "fatfs_istat";
+    FATFS_INFO *fatfs = (FATFS_INFO*)a_fs;
+    TSK_FS_META *fs_meta = NULL; 
+    TSK_FS_FILE *fs_file =  NULL;
+    TSK_FS_META_NAME_LIST *fs_name_list = NULL;
+    FATFS_PRINT_ADDR print;
+    char timeBuf[128];
+ 
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fs, "a_fs", func_name) ||
+        fatfs_ptr_arg_is_null(a_hFile, "a_hFile", func_name) ||
+        !fatfs_inum_arg_is_in_range(fatfs, a_inum, func_name)) {
+        return 1;
+    }
+
+    /* Create a TSK_FS_FILE corresponding to the specified inode. */
+    if ((fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_inum)) == NULL) {
+        return 1;
+    }
+    fs_meta = fs_file->meta;
+
+    /* Print the inode address. */
+    tsk_fprintf(a_hFile, "Directory Entry: %" PRIuINUM "\n", a_inum);
+
+    /* Print the allocation status. */
+    tsk_fprintf(a_hFile, "%sAllocated\n",
+        (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC) ? "Not " : "");
+
+    /* Print the attributes. */
+    tsk_fprintf(a_hFile, "File Attributes: ");
+
+    if (a_inum == a_fs->root_inum) {
+        tsk_fprintf(a_hFile, "Root Directory\n");
+    }
+    else if (fs_meta->type == TSK_FS_META_TYPE_VIRT) {
+        tsk_fprintf(a_hFile, "Virtual File\n");
+    }
+    else if (fs_meta->addr == TSK_FS_ORPHANDIR_INUM(a_fs)) {
+        tsk_fprintf(a_hFile, "Virtual Directory\n");
+    }
+    else {
+        if (fatfs->istat_attr_flags(fatfs, a_inum, a_hFile)) {
+            return 1;
+        }
+    }
+
+    /* Print the file size. */
+    tsk_fprintf(a_hFile, "Size: %" PRIuOFF "\n", fs_meta->size);
+
+    /* Print the name. */
+    if (fs_meta->name2) {
+        fs_name_list = fs_meta->name2;
+        tsk_fprintf(a_hFile, "Name: %s\n", fs_name_list->name);
+    }
+
+    /* Print the times. */
+    if (a_sec_skew != 0) {
+        tsk_fprintf(a_hFile, "\nAdjusted Directory Entry Times:\n");
+
+        if (fs_meta->mtime)
+            fs_meta->mtime -= a_sec_skew;
+        if (fs_meta->atime)
+            fs_meta->atime -= a_sec_skew;
+        if (fs_meta->crtime)
+            fs_meta->crtime -= a_sec_skew;
+
+        tsk_fprintf(a_hFile, "Written:\t%s\n",
+            tsk_fs_time_to_str(fs_meta->mtime, timeBuf));
+        tsk_fprintf(a_hFile, "Accessed:\t%s\n",
+            tsk_fs_time_to_str(fs_meta->atime, timeBuf));
+        tsk_fprintf(a_hFile, "Created:\t%s\n",
+            tsk_fs_time_to_str(fs_meta->crtime, timeBuf));
+
+        if (fs_meta->mtime == 0)
+            fs_meta->mtime += a_sec_skew;
+        if (fs_meta->atime == 0)
+            fs_meta->atime += a_sec_skew;
+        if (fs_meta->crtime == 0)
+            fs_meta->crtime += a_sec_skew;
+
+        tsk_fprintf(a_hFile, "\nOriginal Directory Entry Times:\n");
+    }
+    else {
+        tsk_fprintf(a_hFile, "\nDirectory Entry Times:\n");
+    }
+
+    tsk_fprintf(a_hFile, "Written:\t%s\n", tsk_fs_time_to_str(fs_meta->mtime,
+            timeBuf));
+    tsk_fprintf(a_hFile, "Accessed:\t%s\n",
+        tsk_fs_time_to_str(fs_meta->atime, timeBuf));
+    tsk_fprintf(a_hFile, "Created:\t%s\n",
+        tsk_fs_time_to_str(fs_meta->crtime, timeBuf));
+
+    /* Print the specified number of sector addresses. */
+    tsk_fprintf(a_hFile, "\nSectors:\n");
+    if (a_numblock > 0) {
+        /* A bad hack to force a specified number of blocks */
+        fs_meta->size = a_numblock * a_fs->block_size;
+    }
+    print.istat_seen = 0;
+    print.idx = 0;
+    print.hFile = a_hFile;
+    if (tsk_fs_file_walk(fs_file,
+            (TSK_FS_FILE_WALK_FLAG_ENUM)(TSK_FS_FILE_WALK_FLAG_AONLY | TSK_FS_FILE_WALK_FLAG_SLACK),
+            print_addr_act, (void *) &print)) {
+        tsk_fprintf(a_hFile, "\nError reading file\n");
+        tsk_error_print(a_hFile);
+        tsk_error_reset();
+    }
+    else if (print.idx != 0) {
+        tsk_fprintf(a_hFile, "\n");
+    }
+
+    tsk_fs_file_close(fs_file);
+    return 0;
+}
+
+/* Mark the sector used in the bitmap */
+static TSK_WALK_RET_ENUM
+inode_walk_file_act(TSK_FS_FILE * fs_file, TSK_OFF_T a_off,
+    TSK_DADDR_T addr, char *buf, size_t size,
+    TSK_FS_BLOCK_FLAG_ENUM a_flags, void *a_ptr)
+{
+    setbit((uint8_t *) a_ptr, addr);
+    return TSK_WALK_CONT;
+}
+
+/* The inode_walk call back for each file.  we want only the directories */
+static TSK_WALK_RET_ENUM
+inode_walk_dent_act(TSK_FS_FILE * fs_file, const char *a_path, void *a_ptr)
+{
+    unsigned int flags = TSK_FS_FILE_WALK_FLAG_SLACK | TSK_FS_FILE_WALK_FLAG_AONLY;
+
+    if ((fs_file->meta == NULL)
+        || (fs_file->meta->type != TSK_FS_META_TYPE_DIR))
+        return TSK_WALK_CONT;
+
+    /* Get the sector addresses & ignore any errors */
+    if (tsk_fs_file_walk(fs_file,
+            (TSK_FS_FILE_WALK_FLAG_ENUM)flags,
+            inode_walk_file_act, a_ptr)) {
+        tsk_error_reset();
+    }
+
+    return TSK_WALK_CONT;
+}
+
+/**
+ * Walk the inodes in a specified range and do a TSK_FS_META_WALK_CB callback
+ * for each inode that satisfies criteria specified by a set of 
+ * TSK_FS_META_FLAG_ENUM flags. The following flags are supported: 
+ * TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC, TSK_FS_META_FLAG_ORPHAN,
+ * TSK_FS_META_FLAG_USED (FATXX only), and TSK_FS_META_FLAG_UNUSED 
+ * (FATXX only).
+ *
+ * @param [in] a_fs File system that contains the inodes.
+ * @param [in] a_start_inum Inclusive lower bound of inode range.
+ * @param [in] a_end_inum Inclusive upper bound of inode range.
+ * @param [in] a_selection_flags Inode selection criteria.
+ * @param [in] a_action Callback function for selected inodes.
+ * @param [in] a_ptr Private data pointer passed through to callback function.
+ * @return 0 on success, 1 on failure, per TSK convention
+ */
+uint8_t
+fatfs_inode_walk(TSK_FS_INFO *a_fs, TSK_INUM_T a_start_inum,
+    TSK_INUM_T a_end_inum, TSK_FS_META_FLAG_ENUM a_selection_flags,
+    TSK_FS_META_WALK_CB a_action, void *a_ptr)
+{
+    char *func_name = "fatfs_inode_walk";
+    FATFS_INFO *fatfs = (FATFS_INFO*)a_fs;
+    unsigned int flags = a_selection_flags;
+    TSK_INUM_T end_inum_tmp = 0;
+    TSK_FS_FILE *fs_file =  NULL;
+    TSK_DADDR_T ssect = 0; 
+    TSK_DADDR_T lsect = 0; 
+    TSK_DADDR_T sect = 0; 
+    char *dino_buf = NULL;
+    FATFS_DENTRY *dep = NULL;
+    unsigned int dentry_idx = 0;
+    uint8_t *dir_sectors_bitmap = NULL;
+    ssize_t cnt = 0;
+    uint8_t done = 0;
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fs, "a_fs", func_name) ||
+        fatfs_ptr_arg_is_null(a_action, "a_action", func_name)) {
+        return 1;
+    }
+
+    if (a_start_inum < a_fs->first_inum || a_start_inum > a_fs->last_inum) {
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr("%s: Begin inode out of range:  %" PRIuINUM "", 
+            func_name, a_start_inum);
+        return 1;
+    }
+    else if (a_end_inum < a_fs->first_inum || 
+             a_end_inum > a_fs->last_inum ||
+             a_end_inum < a_start_inum) {
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr("%s: End inode out of range: %" PRIuINUM "", 
+            func_name, a_end_inum);
+        return 1;
+    }
+
+    // RJCTODO: Check this decision with Brian after he polls the user community.
+    /* FAT file systems do not really have the concept of unused inodes. */
+    if ((flags & TSK_FS_META_FLAG_UNUSED) && !(flags & TSK_FS_META_FLAG_USED)) {
+        return 0;
+    }
+    flags |= TSK_FS_META_FLAG_USED;
+    flags &= ~TSK_FS_META_FLAG_UNUSED;
+
+    /* Make sure the inode selection flags are set correctly. */
+    if (flags & TSK_FS_META_FLAG_ORPHAN) {
+        /* If ORPHAN file inodes are wanted, make sure that the UNALLOC
+         * selection flag is set. */
+        flags |= TSK_FS_META_FLAG_UNALLOC;
+        flags &= ~TSK_FS_META_FLAG_ALLOC;
+    }
+    else {
+        /* If neither of the ALLOC or UNALLOC inode selection flags are set,
+        *  then set them both. */
+        if (((flags & TSK_FS_META_FLAG_ALLOC) == 0) &&
+            ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) {
+            flags |= (TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC);
+        }
+    }
+
+    if (tsk_verbose) {
+        tsk_fprintf(stderr,
+            "%s: Inode walking %" PRIuINUM " to %"
+            PRIuINUM "\n", func_name, a_start_inum, a_end_inum);
+    }
+
+    /* If we are looking for orphan files and have not yet populated
+     * the list of files reachable by name for this file system, do so now.
+     */
+    if ((flags & TSK_FS_META_FLAG_ORPHAN)) {
+        if (tsk_fs_dir_load_inum_named(a_fs) != TSK_OK) {
+            tsk_error_errstr2_concat(
+                "%s: Identifying orphan inodes", func_name);
+            return 1;
+        }
+    }
+
+    /* Allocate a TSK_FS_FILE object with a TSK_FS_META object to populate and 
+     * pass to the callback function when an inode that fits the inode 
+     * selection criteria is found. */
+    if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) {
+        return 1;
+    }
+
+    if ((fs_file->meta =
+            tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) {
+        return 1;
+    }
+
+    /* Process the root directory inode, if it's included in the walk. */
+    if (a_start_inum == a_fs->root_inum) {
+        if (((TSK_FS_META_FLAG_ALLOC & flags) == TSK_FS_META_FLAG_ALLOC)
+            && ((TSK_FS_META_FLAG_ORPHAN & flags) == 0)) {
+            TSK_WALK_RET_ENUM retval = TSK_WALK_CONT;
+
+            if (fatfs_make_root(fatfs, fs_file->meta)) {
+                tsk_fs_file_close(fs_file);
+                return 1;
+            }
+
+            retval = a_action(fs_file, a_ptr);
+            if (retval == TSK_WALK_STOP) {
+                tsk_fs_file_close(fs_file);
+                return 0;
+            }
+            else if (retval == TSK_WALK_ERROR) {
+                tsk_fs_file_close(fs_file);
+                return 1;
+            }
+        }
+
+        a_start_inum++;
+        if (a_start_inum == a_end_inum) {
+            tsk_fs_file_close(fs_file);
+            return 0;
+        }
+    }
+
+    /* Allocate a bitmap to keep track of which sectors are allocated to
+     * directories. */
+    if ((dir_sectors_bitmap =
+            (uint8_t*)tsk_malloc((size_t) ((a_fs->block_count +
+                        7) / 8))) == NULL) {
+        tsk_fs_file_close(fs_file);
+        return 1;
+    }
+
+    /* If not doing an orphan files search, populate the directory sectors 
+     * bitmap. The bitmap will be used to make sure that no sector marked as
+     * allocated to a directory is skipped when searching for directory 
+     * entries to map to inodes. */
+    if ((flags & TSK_FS_META_FLAG_ORPHAN) == 0) {
+        if (tsk_verbose) {
+            tsk_fprintf(stderr,
+                "fatfs_inode_walk: Walking directories to collect sector info\n");
+        }
+
+        /* Manufacture an inode for the root directory. */
+        if (fatfs_make_root(fatfs, fs_file->meta)) {
+            tsk_fs_file_close(fs_file);
+            free(dir_sectors_bitmap);
+            return 1;
+        }
+
+        /* Do a file_walk on the root directory to set the bits in the 
+         * directory sectors bitmap for each sector allocated to the root
+         * directory. */
+        if (tsk_fs_file_walk(fs_file,
+                (TSK_FS_FILE_WALK_FLAG_ENUM)(TSK_FS_FILE_WALK_FLAG_SLACK | TSK_FS_FILE_WALK_FLAG_AONLY),
+                inode_walk_file_act, (void*)dir_sectors_bitmap)) {
+            tsk_fs_file_close(fs_file);
+            free(dir_sectors_bitmap);
+            return 1;
+        }
+
+        /* Now walk recursively through the entire directory tree to set the 
+         * bits in the directory sectors bitmap for each sector allocated to 
+         * the children of the root directory. */
+        if (tsk_fs_dir_walk(a_fs, a_fs->root_inum,
+                (TSK_FS_DIR_WALK_FLAG_ENUM)(TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE |
+                TSK_FS_DIR_WALK_FLAG_NOORPHAN), inode_walk_dent_act,
+                (void *) dir_sectors_bitmap)) {
+            tsk_error_errstr2_concat
+                ("- fatfs_inode_walk: mapping directories");
+            tsk_fs_file_close(fs_file);
+            free(dir_sectors_bitmap);
+            return 1;
+        }
+    }
+
+    /* If the end inode is the one of the virtual virtual FAT files or the 
+     * virtual orphan files directory, adjust the end inum and handle the 
+     * virtual inodes after the main inode walking loop below completes. */
+    if (a_end_inum > a_fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs)) {
+        end_inum_tmp = a_fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs);
+    }
+    else {
+        end_inum_tmp = a_end_inum;
+    }
+
+    /* Map the begin and end inodes to the sectors that contain them. 
+     * This sets the image level boundaries for the inode walking loop. */
+    ssect = FATFS_INODE_2_SECT(fatfs, a_start_inum);
+    if (ssect > a_fs->last_block) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr
+            ("%s: Begin inode in sector too big for image: %"
+            PRIuDADDR, func_name, ssect);
+        tsk_fs_file_close(fs_file);
+        free(dir_sectors_bitmap);
+        return 1;
+    }
+
+    lsect = FATFS_INODE_2_SECT(fatfs, end_inum_tmp);
+    if (lsect > a_fs->last_block) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr
+            ("%s: End inode in sector too big for image: %"
+            PRIuDADDR, func_name, lsect);
+        tsk_fs_file_close(fs_file);
+        free(dir_sectors_bitmap);
+        return 1;
+    }
+
+    /* Allocate a buffer big enough to read in a cluster at a time. */
+    if ((dino_buf = (char*)tsk_malloc(fatfs->csize << fatfs->ssize_sh)) ==
+        NULL) {
+        tsk_fs_file_close(fs_file);
+        free(dir_sectors_bitmap);
+        return 1;
+    }
+
+    /* Walk the inodes. */
+    sect = ssect;
+    while (sect <= lsect) {
+        int cluster_is_alloc = 0;
+        size_t num_sectors_to_process = 0;       
+        size_t sector_idx = 0;            
+        uint8_t do_basic_dentry_test = 0; 
+
+        /* Read in a chunk of the image to process on this iteration of the inode
+         * walk. The actual size of the read will depend on whether or not it is 
+         * coming from the root directory of a FAT12 or FAT16 file system. As 
+         * indicated by the size of the buffer, the data area (exFAT cluster 
+         * heap) will for the most part be read in a cluster at a time. 
+         * However, the root directory for a FAT12/FAT16 file system precedes 
+         * the data area and the read size for it should be a sector, not a 
+         * cluster. */
+        if (sect < fatfs->firstclustsect) {
+
+            if ((flags & TSK_FS_META_FLAG_ORPHAN) != 0) {
+                /* If orphan file hunting, there are no orphans in the root 
+                 * directory, so skip ahead to the data area. */
+                sect = fatfs->firstclustsect;
+                continue;
+            }
+
+            /* Read in a FAT12/FAT16 root directory sector. */
+            cnt = tsk_fs_read_block(a_fs, sect, dino_buf, fatfs->ssize);
+            if (cnt != fatfs->ssize) {
+                if (cnt >= 0) {
+                    tsk_error_reset();
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
+                }
+                tsk_error_set_errstr2
+                    ("&s (root dir): sector: %" PRIuDADDR,
+                    func_name, sect);
+                tsk_fs_file_close(fs_file);
+                free(dir_sectors_bitmap);
+                free(dino_buf);
+                return 1;
+            }
+
+            cluster_is_alloc = 1;
+            num_sectors_to_process = 1;
+        }
+        else {
+            /* The walk has proceeded into the data area (exFAT cluster heap).
+             * It's time to read in a cluster at a time. Get the base sector 
+             * for the cluster that contains the current sector. */
+            sect =
+                FATFS_CLUST_2_SECT(fatfs, (FATFS_SECT_2_CLUST(fatfs,
+                        sect)));
+
+            /* Determine whether the cluster is allocated. Skip it if it is
+             * not allocated and the UNALLOCATED inode selection flag is not 
+             * set. */
+            cluster_is_alloc = fatfs_is_sectalloc(fatfs, sect);
+            if ((cluster_is_alloc == 0)
+                && ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) {
+                sect += fatfs->csize;
+                continue;
+            }
+            else if (cluster_is_alloc == -1) {
+                tsk_fs_file_close(fs_file);
+                free(dir_sectors_bitmap);
+                free(dino_buf);
+                return 1;
+            }
+
+            /* If the cluster is allocated but is not allocated to a 
+             * directory, then skip it.  NOTE: This will miss orphan file 
+             * entries in the slack space of files.
+             */
+            if ((cluster_is_alloc == 1) && (isset(dir_sectors_bitmap, sect) == 0)) {
+                sect += fatfs->csize;
+                continue;
+            }
+
+            /* The final cluster may not be full. */
+            if (lsect - sect + 1 < fatfs->csize) {
+                num_sectors_to_process = (size_t) (lsect - sect + 1);
+            }
+            else {
+                num_sectors_to_process = fatfs->csize;
+            }
+
+            /* Read in a cluster. */
+            cnt = tsk_fs_read_block
+                (a_fs, sect, dino_buf, num_sectors_to_process << fatfs->ssize_sh);
+            if (cnt != (num_sectors_to_process << fatfs->ssize_sh)) {
+                if (cnt >= 0) {
+                    tsk_error_reset();
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
+                }
+                tsk_error_set_errstr2("%s: sector: %"
+                    PRIuDADDR, func_name, sect);
+                tsk_fs_file_close(fs_file);
+                free(dir_sectors_bitmap);
+                free(dino_buf);
+                return 1;
+            }
+        }
+
+        // RJCTODO: Isn't this unnecessary given the skipping of unallocated
+        // clusters above?
+        /* Now that the sectors are read in, prepare to step through them in 
+         * directory entry size chunks. Only do a basic test to confirm the 
+         * contents of each chunk is a directory entry unless the sector that
+         * contains it is not allocated to a directory or is unallocated.*/
+        do_basic_dentry_test = 1;
+        if ((isset(dir_sectors_bitmap, sect) == 0) || (cluster_is_alloc == 0)) {
+            do_basic_dentry_test = 0;
+        }
+
+        /* Walk through the sectors read in. */
+        for (sector_idx = 0; sector_idx < num_sectors_to_process; sector_idx++) {
+            TSK_INUM_T inum = 0;
+
+            /* If the last inode in this sector is before the start 
+             * inode, skip the sector. */
+            if (FATFS_SECT_2_INODE(fatfs, sect + 1) < a_start_inum) {
+                sect++;
+                continue;
+            }
+
+            /* Advance the directory entry pointer to the start of the 
+             * sector. */
+            dep = (FATFS_DENTRY*)(&dino_buf[sector_idx << fatfs->ssize_sh]);
+
+            /* If the sector is not allocated to a directory and the first 
+             * chunk is not a directory entry, skip the sector. */
+            if (!isset(dir_sectors_bitmap, sect) &&
+                !fatfs->is_dentry(fatfs, dep, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)cluster_is_alloc, do_basic_dentry_test)) {
+                sect++;
+                continue;
+            }
+
+            /* Get the base inode address of this sector. */
+            inum = FATFS_SECT_2_INODE(fatfs, sect);
+            if (tsk_verbose) {
+                tsk_fprintf(stderr,
+                    "%s: Processing sector %" PRIuDADDR
+                    " starting at inode %" PRIuINUM "\n", func_name, sect, inum);
+            }
+
+            /* Walk through the potential directory entries in the sector. */
+            for (dentry_idx = 0; dentry_idx < fatfs->dentry_cnt_se;
+                dentry_idx++, inum++, dep++) {
+                int retval;
+                EXFATFS_DIR_ENTRY_TYPE_ENUM dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
+                TSK_RETVAL_ENUM retval2 = TSK_OK;
+
+                /* If the inode address of the potential entry is less than
+                 * the beginning inode address for the inode walk, skip it. */
+                if (inum < a_start_inum) {
+                    continue;
+                }
+
+                /* If inode address of the potential entry is greater than the
+                 * ending inode address for the walk, terminate the inode walk. */ 
+                if (inum > end_inum_tmp) {
+                    done = 1;
+                    break;
+                }
+
+                /* If the potential entry is likely not an entry, or it is an  
+                 * entry that is not reported in an inode walk, or it does not   
+                 * satisfy the inode selection flags, then skip it. */
+                if (!fatfs->is_dentry(fatfs, dep, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)cluster_is_alloc, do_basic_dentry_test) ||
+                    fatfs->inode_walk_should_skip_dentry(fatfs, inum, dep, flags, cluster_is_alloc)) {
+                    continue;
+                }
+
+                retval2 = fatfs->dinode_copy(fatfs, inum, dep, cluster_is_alloc, fs_file);
+
+                if (retval2 != TSK_OK) {
+                    if (retval2 == TSK_COR) {
+                        /* Corrupted, move on to the next chunk. */
+                        if (tsk_verbose) {
+                            tsk_error_print(stderr);
+                        }
+                        tsk_error_reset();
+                        continue;
+                    }
+                    else {
+                        tsk_fs_file_close(fs_file);
+                        free(dir_sectors_bitmap);
+                        free(dino_buf);
+                        return 1;
+                    }
+                }
+
+                if (tsk_verbose) {
+                    tsk_fprintf(stderr,
+                        "%s: Directory Entry %" PRIuINUM
+                        " (%u) at sector %" PRIuDADDR "\n", func_name, inum, dentry_idx,
+                        sect);
+                }
+
+                /* Do the callback. */
+                retval = a_action(fs_file, a_ptr);
+                if (retval == TSK_WALK_STOP) {
+                    tsk_fs_file_close(fs_file);
+                    free(dir_sectors_bitmap);
+                    free(dino_buf);
+                    return 0;
+                }
+                else if (retval == TSK_WALK_ERROR) {
+                    tsk_fs_file_close(fs_file);
+                    free(dir_sectors_bitmap);
+                    free(dino_buf);
+                    return 1;
+                }
+            }                  
+            sect++;
+            if (done) {
+                break;
+            }
+        }
+        if (done) {
+            break;
+        }
+    }
+
+    free(dir_sectors_bitmap);
+    free(dino_buf);
+
+    // handle the virtual orphans folder and FAT files if they asked for them
+    if ((a_end_inum > a_fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs))
+        && (flags & TSK_FS_META_FLAG_ALLOC)
+        && ((flags & TSK_FS_META_FLAG_ORPHAN) == 0)) {
+        TSK_INUM_T inum;
+
+        // cycle through the special files
+        for (inum = a_fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs) + 1;
+            inum <= a_end_inum; inum++) {
+            int retval;
+
+            tsk_fs_meta_reset(fs_file->meta);
+
+            if (inum == fatfs->mbr_virt_inum) {
+                if (fatfs_make_mbr(fatfs, fs_file->meta)) {
+                    tsk_fs_file_close(fs_file);
+                    return 1;
+                }
+            }
+            else if (inum == fatfs->fat1_virt_inum) {
+                if (fatfs_make_fat(fatfs, 1, fs_file->meta)) {
+                    tsk_fs_file_close(fs_file);
+                    return 1;
+                }
+            }
+            else if (inum == fatfs->fat2_virt_inum && fatfs->numfat == 2) {
+                if (fatfs_make_fat(fatfs, 2, fs_file->meta)) {
+                    tsk_fs_file_close(fs_file);
+                    return 1;
+                }
+            }
+            else if (inum == TSK_FS_ORPHANDIR_INUM(a_fs)) {
+                if (tsk_fs_dir_make_orphan_dir_meta(a_fs, fs_file->meta)) {
+                    tsk_fs_file_close(fs_file);
+                    return 1;
+                }
+            }
+
+            retval = a_action(fs_file, a_ptr);
+            if (retval == TSK_WALK_STOP) {
+                tsk_fs_file_close(fs_file);
+                return 0;
+            }
+            else if (retval == TSK_WALK_ERROR) {
+                tsk_fs_file_close(fs_file);
+                return 1;
+            }
+        }
+    }
+
+    tsk_fs_file_close(fs_file);
+    return 0;
 }
\ No newline at end of file
diff --git a/tsk/fs/fatfs_utils.c b/tsk/fs/fatfs_utils.c
index 01c0bccc7..2b628bdef 100755
--- a/tsk/fs/fatfs_utils.c
+++ b/tsk/fs/fatfs_utils.c
@@ -1,329 +1,329 @@
-/*
-** The Sleuth Kit
-**
-** Copyright (c) 2013 Basis Technology Corp.  All rights reserved
-** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
-**
-** This software is distributed under the Common Public License 1.0
-**
-*/
-
-/**
- * \file fatfs_utils.c
- * Contains utility functions for processing FAT file systems. 
- */
-
-#include "tsk_fs_i.h"
-#include "tsk_fatfs.h"
-#include <assert.h>
-
-/**
- * \internal
- * Tests whether a pointer argument is set to NULL. If the pointer is NULL,
- * sets a TSK_ERR_FS_ARG error with an error message that includes a parameter
- * name and a function name supplied by the caller.
- *
- * @param a_ptr The pointer to test for NULL.
- * @param a_param_name The name of the parameter for which the pointer was
- * passed as an argument.
- * @param a_func_name The name of the function for which a_param is a 
- * parameter.
- * @return Returns 1 if the pointer is NULL, 0 otherwise.
- */
-uint8_t
-fatfs_ptr_arg_is_null(void *a_ptr, const char *a_param_name, const char *a_func_name)
-{
-    const char *func_name = "fatfs_ptr_arg_is_null";
-
-    assert(a_param_name != NULL);
-    assert(a_func_name != NULL);
-
-    if (a_ptr == NULL) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_ARG);
-        if ((a_param_name != NULL) && (a_func_name != NULL)) { 
-            tsk_error_set_errstr("%s: %s is NULL", a_param_name, a_func_name);
-        }
-        else {
-            tsk_error_set_errstr("%s: NULL pointer", func_name);
-        }
-
-        return 1;
-    }
-
-    return 0;
-}
-
-/**
- * \internal
- * Tests whether an inode address is in the range of valid inode addresses for
- * a given file system.
- *
- * @param a_fatfs Generic FAT file system info structure.
- * @param a_inum An inode address. 
- * @return Returns 1 if the address is in range, 0 otherwise.
- */
-uint8_t
-fatfs_inum_is_in_range(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum)
-{
-    const char *func_name = "fatfs_inum_is_in_range";
-    TSK_FS_INFO *fs = (TSK_FS_INFO*)a_fatfs; 
-
-    assert(a_fatfs != NULL);
-
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) {
-        return 0;
-    }
-
-    if ((a_inum < fs->first_inum) || (a_inum > fs->last_inum)) {
-        return 0;
-    }
-
-    return 1;
-}
-
-/**
- * \internal
- * Tests whether an inode address argument is in the range of valid inode
- * addresses for a given file system. If the address is out of range,
- * sets a TSK_ERR_FS_ARG error with an error message that includes the inode
- * address and a function name supplied by the caller.
- *
- * @param a_fatfs Generic FAT file system info structure.
- * @param a_inum An inode address. 
- * @param a_func_name The name of the function that received the inode address 
- * as an argument.
- * @return Returns 1 if the address is in range, 0 otherwise.
- */
-uint8_t
-fatfs_inum_arg_is_in_range(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, const char *a_func_name)
-{
-    const char *func_name = "fatfs_inum_arg_is_in_range";
-    TSK_FS_INFO *fs = (TSK_FS_INFO*)a_fatfs; 
-
-    assert(a_fatfs != NULL);
-    assert(a_func_name != NULL);
-
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) {
-        return 0;
-    }
-
-    if (!fatfs_inum_is_in_range(a_fatfs, a_inum)) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_ARG);
-        if (a_func_name != NULL) {
-            tsk_error_set_errstr("%s: inode address: %" PRIuINUM " out of range", a_func_name, a_inum);
-        }
-        else {
-            tsk_error_set_errstr("%s: inode address: %" PRIuINUM " out of range", func_name, a_inum);
-        }
-
-        return 0;
-    }              
-    return 1;
-}
-
-/**
- * \internal
- * Convert a DOS time stamp into a UNIX time stamp. A DOS time stamp consists
- * of a date with the year specified as an offset from 1980. A UNIX time stamp
- * is seconds since January 1, 1970 in UTC.
- *
- * @param date Date part of a DOS time stamp.
- * @param time Time part of a DOS time stamp. 
- * @param timetens Tenths of seconds part of a DOS time stamp, range is 0-199.
- * @return A UNIX time stamp.
- */
-time_t
-fatfs_dos_2_unix_time(uint16_t date, uint16_t time, uint8_t timetens)
-{
-    struct tm tm1;
-    time_t ret;
-
-    if (date == 0)
-        return 0;
-
-    memset(&tm1, 0, sizeof(struct tm));
-
-    tm1.tm_sec = ((time & FATFS_SEC_MASK) >> FATFS_SEC_SHIFT) * 2;
-    if ((tm1.tm_sec < 0) || (tm1.tm_sec > 60))
-        tm1.tm_sec = 0;
-
-    /* The ctimetens value has a range of 0 to 199 */
-    if (timetens > 100)
-        tm1.tm_sec++;
-
-    tm1.tm_min = ((time & FATFS_MIN_MASK) >> FATFS_MIN_SHIFT);
-    if ((tm1.tm_min < 0) || (tm1.tm_min > 59))
-        tm1.tm_min = 0;
-
-    tm1.tm_hour = ((time & FATFS_HOUR_MASK) >> FATFS_HOUR_SHIFT);
-    if ((tm1.tm_hour < 0) || (tm1.tm_hour > 23))
-        tm1.tm_hour = 0;
-
-    tm1.tm_mday = ((date & FATFS_DAY_MASK) >> FATFS_DAY_SHIFT);
-    if ((tm1.tm_mday < 1) || (tm1.tm_mday > 31))
-        tm1.tm_mday = 0;
-
-    tm1.tm_mon = ((date & FATFS_MON_MASK) >> FATFS_MON_SHIFT) - 1;
-    if ((tm1.tm_mon < 0) || (tm1.tm_mon > 11))
-        tm1.tm_mon = 0;
-
-    /* There is a limit to the year because the UNIX time value is
-     * a 32-bit value
-     * the maximum UNIX time is Tue Jan 19 03:14:07 2038 */
-    tm1.tm_year = ((date & FATFS_YEAR_MASK) >> FATFS_YEAR_SHIFT) + 80;
-    if ((tm1.tm_year < 0) || (tm1.tm_year > 137))
-        tm1.tm_year = 0;
-
-    /* set the daylight savings variable to -1 so that mktime() figures
-     * it out */
-    tm1.tm_isdst = -1;
-
-    ret = mktime(&tm1);
-
-    if (ret < 0) {
-        if (tsk_verbose)
-            tsk_fprintf(stderr,
-                "fatfs_dos_2_unix_time: Error running mktime() on: %d:%d:%d %d/%d/%d\n",
-                ((time & FATFS_HOUR_MASK) >> FATFS_HOUR_SHIFT),
-                ((time & FATFS_MIN_MASK) >> FATFS_MIN_SHIFT),
-                ((time & FATFS_SEC_MASK) >> FATFS_SEC_SHIFT) * 2,
-                ((date & FATFS_MON_MASK) >> FATFS_MON_SHIFT) - 1,
-                ((date & FATFS_DAY_MASK) >> FATFS_DAY_SHIFT),
-                ((date & FATFS_YEAR_MASK) >> FATFS_YEAR_SHIFT) + 80);
-        return 0;
-    }
-
-    return ret;
-}
-
-/**
- * \internal
- * Converts the tenths of seconds part a DOS time stamp into nanoseconds.
- * of a date with the year specified as an offset from 1980. A UNIX time stamp
- * is seconds since January 1, 1970 in UTC.
- *
- * @param timetens Tenths of seconds part of a DOS time stamp, range is 0-199.
- * @return A duration in nanoseconds.
- */
-uint32_t
-fatfs_dos_2_nanosec(uint8_t timetens)
-{
-    timetens %= 100;
-    return timetens * 10000000;
-}
-
-/** 
- * \internal
- * Cleans up a string so that it contains only ASCII characters. Useful when
- *
- * @param name The string
- */
-void
-fatfs_cleanup_ascii(char *str)
-{
-    const char *func_name = "fatfs_cleanup_ascii";
-
-    assert(str != NULL);
-
-    if (!fatfs_ptr_arg_is_null(str, "str", func_name)) {
-        int i;
-        for (i = 0; str[i] != '\0'; i++) {
-            if ((unsigned char) (str[i]) > 0x7e) {
-                str[i] = '^';
-            }
-        }
-    }
-}
-
-/**
- * \internal
- * Converts a UTF-16 string from an inode into a UTF-8 string. If the 
- * conversion fails, sets a TSK_ERR_FS_UNICODE error with an error message 
- * that includes the inode address and a description of the UTF-16 string
- * supplied by the caller.
- *
- * @param a_fatfs Generic FAT file system info structure.
- * @param a_src The UTF-16 string.
- * @param a_src_len The length of the UTF-16 string.
- * @param a_dest The buffer for the UTF-8 string.
- * @param a_dest_len The length of the UTF-8 string buffer.
- * @param a_inum The address of the source inode, used if an error message is
- * generated.
- * @param a_desc A description of the source string, used if an error message 
- * is generated.
- * @return TSKConversionResult.
- */
-TSKConversionResult
-fatfs_utf16_inode_str_2_utf8(FATFS_INFO *a_fatfs, UTF16 *a_src, size_t a_src_len, UTF8 *a_dest, size_t a_dest_len, TSK_INUM_T a_inum, const char *a_desc)
-{
-    const char *func_name = "fatfs_copy_utf16_str";
-    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
-    TSKConversionResult conv_result = TSKconversionOK;
-    uint32_t i = 0;
-    
-    assert(a_fatfs != NULL);
-    assert(a_src != NULL);
-    assert(a_src_len > 0);
-    assert(a_dest != NULL);
-    assert(a_dest_len > 0);
-    assert(a_desc != NULL);
-
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) {
-        return TSKsourceIllegal; 
-    }
-
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_src", func_name)) {
-        return TSKsourceExhausted; 
-    }
-
-    if (a_src_len <= 0) {
-        return TSKsourceExhausted; 
-    }
-
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_dest", func_name)) {
-        return TSKtargetExhausted; 
-    }
-
-    if (a_dest_len <= 0) {
-        return TSKtargetExhausted; 
-    }
-
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_desc", func_name)) {
-        return TSKsourceIllegal; 
-    }
-
-    conv_result = tsk_UTF16toUTF8(fs->endian, (const UTF16**)&a_src, (UTF16*)&a_src[a_src_len], &a_dest, (UTF8*)&a_dest[a_dest_len], TSKlenientConversion);
-    if (conv_result == TSKconversionOK) {
-        /* Make sure the result is NULL-terminated. */
-        if ((uintptr_t) a_dest > (uintptr_t)a_dest + sizeof(a_dest)) {
-            a_dest[sizeof(a_dest) - 1] = '\0';
-        }
-        else {
-            *a_dest = '\0';
-        }
-    }
-    else {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_UNICODE);
-        tsk_error_set_errstr("%s: Error converting %s for inum %d from UTF16 to UTF8: %d", func_name, a_desc, a_inum, conv_result);
-        *a_dest = '\0';
-    }
-
-    /* Clean up non-ASCII characters since the destination buffer is supposed 
-     * to be UTF-8 and the encoding of the source is essentially unknown and 
-     * may be junk. */
-    fatfs_cleanup_ascii((char*)a_dest);
-
-    /* Remove control characters. */
-    i = 0;
-    while (a_dest[i] != '\0') {
-        if (TSK_IS_CNTRL(a_dest[i])) {
-            a_dest[i] = '^';
-        }
-        ++i;
-    }
-
-    return conv_result;
+/*
+** The Sleuth Kit
+**
+** Copyright (c) 2013 Basis Technology Corp.  All rights reserved
+** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
+**
+** This software is distributed under the Common Public License 1.0
+**
+*/
+
+/**
+ * \file fatfs_utils.c
+ * Contains utility functions for processing FAT file systems. 
+ */
+
+#include "tsk_fs_i.h"
+#include "tsk_fatfs.h"
+#include <assert.h>
+
+/**
+ * \internal
+ * Tests whether a pointer argument is set to NULL. If the pointer is NULL,
+ * sets a TSK_ERR_FS_ARG error with an error message that includes a parameter
+ * name and a function name supplied by the caller.
+ *
+ * @param a_ptr The pointer to test for NULL.
+ * @param a_param_name The name of the parameter for which the pointer was
+ * passed as an argument.
+ * @param a_func_name The name of the function for which a_param is a 
+ * parameter.
+ * @return Returns 1 if the pointer is NULL, 0 otherwise.
+ */
+uint8_t
+fatfs_ptr_arg_is_null(void *a_ptr, const char *a_param_name, const char *a_func_name)
+{
+    const char *func_name = "fatfs_ptr_arg_is_null";
+
+    assert(a_param_name != NULL);
+    assert(a_func_name != NULL);
+
+    if (a_ptr == NULL) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        if ((a_param_name != NULL) && (a_func_name != NULL)) { 
+            tsk_error_set_errstr("%s: %s is NULL", a_param_name, a_func_name);
+        }
+        else {
+            tsk_error_set_errstr("%s: NULL pointer", func_name);
+        }
+
+        return 1;
+    }
+
+    return 0;
+}
+
+/**
+ * \internal
+ * Tests whether an inode address is in the range of valid inode addresses for
+ * a given file system.
+ *
+ * @param a_fatfs Generic FAT file system info structure.
+ * @param a_inum An inode address. 
+ * @return Returns 1 if the address is in range, 0 otherwise.
+ */
+uint8_t
+fatfs_inum_is_in_range(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum)
+{
+    const char *func_name = "fatfs_inum_is_in_range";
+    TSK_FS_INFO *fs = (TSK_FS_INFO*)a_fatfs; 
+
+    assert(a_fatfs != NULL);
+
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) {
+        return 0;
+    }
+
+    if ((a_inum < fs->first_inum) || (a_inum > fs->last_inum)) {
+        return 0;
+    }
+
+    return 1;
+}
+
+/**
+ * \internal
+ * Tests whether an inode address argument is in the range of valid inode
+ * addresses for a given file system. If the address is out of range,
+ * sets a TSK_ERR_FS_ARG error with an error message that includes the inode
+ * address and a function name supplied by the caller.
+ *
+ * @param a_fatfs Generic FAT file system info structure.
+ * @param a_inum An inode address. 
+ * @param a_func_name The name of the function that received the inode address 
+ * as an argument.
+ * @return Returns 1 if the address is in range, 0 otherwise.
+ */
+uint8_t
+fatfs_inum_arg_is_in_range(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, const char *a_func_name)
+{
+    const char *func_name = "fatfs_inum_arg_is_in_range";
+    TSK_FS_INFO *fs = (TSK_FS_INFO*)a_fatfs; 
+
+    assert(a_fatfs != NULL);
+    assert(a_func_name != NULL);
+
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) {
+        return 0;
+    }
+
+    if (!fatfs_inum_is_in_range(a_fatfs, a_inum)) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        if (a_func_name != NULL) {
+            tsk_error_set_errstr("%s: inode address: %" PRIuINUM " out of range", a_func_name, a_inum);
+        }
+        else {
+            tsk_error_set_errstr("%s: inode address: %" PRIuINUM " out of range", func_name, a_inum);
+        }
+
+        return 0;
+    }              
+    return 1;
+}
+
+/**
+ * \internal
+ * Convert a DOS time stamp into a UNIX time stamp. A DOS time stamp consists
+ * of a date with the year specified as an offset from 1980. A UNIX time stamp
+ * is seconds since January 1, 1970 in UTC.
+ *
+ * @param date Date part of a DOS time stamp.
+ * @param time Time part of a DOS time stamp. 
+ * @param timetens Tenths of seconds part of a DOS time stamp, range is 0-199.
+ * @return A UNIX time stamp.
+ */
+time_t
+fatfs_dos_2_unix_time(uint16_t date, uint16_t time, uint8_t timetens)
+{
+    struct tm tm1;
+    time_t ret;
+
+    if (date == 0)
+        return 0;
+
+    memset(&tm1, 0, sizeof(struct tm));
+
+    tm1.tm_sec = ((time & FATFS_SEC_MASK) >> FATFS_SEC_SHIFT) * 2;
+    if ((tm1.tm_sec < 0) || (tm1.tm_sec > 60))
+        tm1.tm_sec = 0;
+
+    /* The ctimetens value has a range of 0 to 199 */
+    if (timetens > 100)
+        tm1.tm_sec++;
+
+    tm1.tm_min = ((time & FATFS_MIN_MASK) >> FATFS_MIN_SHIFT);
+    if ((tm1.tm_min < 0) || (tm1.tm_min > 59))
+        tm1.tm_min = 0;
+
+    tm1.tm_hour = ((time & FATFS_HOUR_MASK) >> FATFS_HOUR_SHIFT);
+    if ((tm1.tm_hour < 0) || (tm1.tm_hour > 23))
+        tm1.tm_hour = 0;
+
+    tm1.tm_mday = ((date & FATFS_DAY_MASK) >> FATFS_DAY_SHIFT);
+    if ((tm1.tm_mday < 1) || (tm1.tm_mday > 31))
+        tm1.tm_mday = 0;
+
+    tm1.tm_mon = ((date & FATFS_MON_MASK) >> FATFS_MON_SHIFT) - 1;
+    if ((tm1.tm_mon < 0) || (tm1.tm_mon > 11))
+        tm1.tm_mon = 0;
+
+    /* There is a limit to the year because the UNIX time value is
+     * a 32-bit value
+     * the maximum UNIX time is Tue Jan 19 03:14:07 2038 */
+    tm1.tm_year = ((date & FATFS_YEAR_MASK) >> FATFS_YEAR_SHIFT) + 80;
+    if ((tm1.tm_year < 0) || (tm1.tm_year > 137))
+        tm1.tm_year = 0;
+
+    /* set the daylight savings variable to -1 so that mktime() figures
+     * it out */
+    tm1.tm_isdst = -1;
+
+    ret = mktime(&tm1);
+
+    if (ret < 0) {
+        if (tsk_verbose)
+            tsk_fprintf(stderr,
+                "fatfs_dos_2_unix_time: Error running mktime() on: %d:%d:%d %d/%d/%d\n",
+                ((time & FATFS_HOUR_MASK) >> FATFS_HOUR_SHIFT),
+                ((time & FATFS_MIN_MASK) >> FATFS_MIN_SHIFT),
+                ((time & FATFS_SEC_MASK) >> FATFS_SEC_SHIFT) * 2,
+                ((date & FATFS_MON_MASK) >> FATFS_MON_SHIFT) - 1,
+                ((date & FATFS_DAY_MASK) >> FATFS_DAY_SHIFT),
+                ((date & FATFS_YEAR_MASK) >> FATFS_YEAR_SHIFT) + 80);
+        return 0;
+    }
+
+    return ret;
+}
+
+/**
+ * \internal
+ * Converts the tenths of seconds part a DOS time stamp into nanoseconds.
+ * of a date with the year specified as an offset from 1980. A UNIX time stamp
+ * is seconds since January 1, 1970 in UTC.
+ *
+ * @param timetens Tenths of seconds part of a DOS time stamp, range is 0-199.
+ * @return A duration in nanoseconds.
+ */
+uint32_t
+fatfs_dos_2_nanosec(uint8_t timetens)
+{
+    timetens %= 100;
+    return timetens * 10000000;
+}
+
+/** 
+ * \internal
+ * Cleans up a string so that it contains only ASCII characters. Useful when
+ *
+ * @param name The string
+ */
+void
+fatfs_cleanup_ascii(char *str)
+{
+    const char *func_name = "fatfs_cleanup_ascii";
+
+    assert(str != NULL);
+
+    if (!fatfs_ptr_arg_is_null(str, "str", func_name)) {
+        int i;
+        for (i = 0; str[i] != '\0'; i++) {
+            if ((unsigned char) (str[i]) > 0x7e) {
+                str[i] = '^';
+            }
+        }
+    }
+}
+
+/**
+ * \internal
+ * Converts a UTF-16 string from an inode into a UTF-8 string. If the 
+ * conversion fails, sets a TSK_ERR_FS_UNICODE error with an error message 
+ * that includes the inode address and a description of the UTF-16 string
+ * supplied by the caller.
+ *
+ * @param a_fatfs Generic FAT file system info structure.
+ * @param a_src The UTF-16 string.
+ * @param a_src_len The length of the UTF-16 string.
+ * @param a_dest The buffer for the UTF-8 string.
+ * @param a_dest_len The length of the UTF-8 string buffer.
+ * @param a_inum The address of the source inode, used if an error message is
+ * generated.
+ * @param a_desc A description of the source string, used if an error message 
+ * is generated.
+ * @return TSKConversionResult.
+ */
+TSKConversionResult
+fatfs_utf16_inode_str_2_utf8(FATFS_INFO *a_fatfs, UTF16 *a_src, size_t a_src_len, UTF8 *a_dest, size_t a_dest_len, TSK_INUM_T a_inum, const char *a_desc)
+{
+    const char *func_name = "fatfs_copy_utf16_str";
+    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
+    TSKConversionResult conv_result = TSKconversionOK;
+    uint32_t i = 0;
+    
+    assert(a_fatfs != NULL);
+    assert(a_src != NULL);
+    assert(a_src_len > 0);
+    assert(a_dest != NULL);
+    assert(a_dest_len > 0);
+    assert(a_desc != NULL);
+
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) {
+        return TSKsourceIllegal; 
+    }
+
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_src", func_name)) {
+        return TSKsourceExhausted; 
+    }
+
+    if (a_src_len <= 0) {
+        return TSKsourceExhausted; 
+    }
+
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_dest", func_name)) {
+        return TSKtargetExhausted; 
+    }
+
+    if (a_dest_len <= 0) {
+        return TSKtargetExhausted; 
+    }
+
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_desc", func_name)) {
+        return TSKsourceIllegal; 
+    }
+
+    conv_result = tsk_UTF16toUTF8(fs->endian, (const UTF16**)&a_src, (UTF16*)&a_src[a_src_len], &a_dest, (UTF8*)&a_dest[a_dest_len], TSKlenientConversion);
+    if (conv_result == TSKconversionOK) {
+        /* Make sure the result is NULL-terminated. */
+        if ((uintptr_t) a_dest > (uintptr_t)a_dest + sizeof(a_dest)) {
+            a_dest[sizeof(a_dest) - 1] = '\0';
+        }
+        else {
+            *a_dest = '\0';
+        }
+    }
+    else {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_UNICODE);
+        tsk_error_set_errstr("%s: Error converting %s for inum %d from UTF16 to UTF8: %d", func_name, a_desc, a_inum, conv_result);
+        *a_dest = '\0';
+    }
+
+    /* Clean up non-ASCII characters since the destination buffer is supposed 
+     * to be UTF-8 and the encoding of the source is essentially unknown and 
+     * may be junk. */
+    fatfs_cleanup_ascii((char*)a_dest);
+
+    /* Remove control characters. */
+    i = 0;
+    while (a_dest[i] != '\0') {
+        if (TSK_IS_CNTRL(a_dest[i])) {
+            a_dest[i] = '^';
+        }
+        ++i;
+    }
+
+    return conv_result;
 }
\ No newline at end of file
diff --git a/tsk/fs/fatxxfs.c b/tsk/fs/fatxxfs.c
index 8e6538e98..a88674cef 100755
--- a/tsk/fs/fatxxfs.c
+++ b/tsk/fs/fatxxfs.c
@@ -1,860 +1,860 @@
-/*
-** fatxxfs
-** The Sleuth Kit 
-**
-** Content and meta data layer support for the FATXX file system 
-**
-** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2013 Brian Carrier, Basis Technology. All Rights reserved
-** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
-**
-** TASK
-** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
-**
-**
-** This software is distributed under the Common Public License 1.0
-**
-** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05)
-**
-*/
-
-/**
- * \file fatxxfs.c
- * Contains the internal TSK FATXX (FAT12, FAT16, FAT32) file system code to 
- * handle basic file system processing for opening file system, processing 
- * sectors, and directory entries. 
- */
-
-#include "tsk_fatxxfs.h"
-
-/*
- * Implementation NOTES 
- *
- * TSK_FS_META contains the first cluster.  file_walk will return sector
- * values though because the cluster numbers do not start until after
- * the FAT.  That makes it very hard to address the first few blocks!
- *
- * Inodes numbers do not exist in FAT.  To make up for this we will count
- * directory entries as the inodes.   As the root directory does not have
- * any records in FAT, we will give it times of 0 and call it inode 2 to
- * keep consistent with UNIX.  After that, each 32-byte slot is numbered
- * as though it were a directory entry (even if it is not).  Therefore,
- * when an inode walk is performed, not all inode values will be displayed
- * even when '-e' is given for ils. 
- *
- * Progs like 'ils -e' are very slow because we have to look at each
- * block to see if it is a file system structure.
- */
-
-/**
- * Print details about the file system to a file handle. 
- *
- * @param fs File system to print details on
- * @param hFile File handle to print text to
- * 
- * @returns 1 on error and 0 on success
- */
-static uint8_t
-fatxxfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
-{
-    unsigned int i;
-    int a;
-    TSK_DADDR_T next, snext, sstart, send;
-    FATFS_INFO *fatfs = (FATFS_INFO *) fs;
-    FATXXFS_SB *sb = (FATXXFS_SB*)fatfs->boot_sector_buffer;
-    char *data_buf;
-    FATXXFS_DENTRY *de;
-    ssize_t cnt;
-
-    // clean up any error messages that are lying around
-    tsk_error_reset();
-
-    if ((data_buf = (char *) tsk_malloc(fs->block_size)) == NULL) {
-        return 1;
-    }
-
-
-    /* Read the root directory sector so that we can get the volume
-     * label from it */
-    cnt = tsk_fs_read_block(fs, fatfs->rootsect, data_buf, fs->block_size);
-    if (cnt != fs->block_size) {
-        if (cnt >= 0) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_READ);
-        }
-        tsk_error_set_errstr2("fatxxfs_fsstat: root directory: %" PRIuDADDR,
-            fatfs->rootsect);
-        free(data_buf);
-        return 1;
-    }
-
-
-    /* Find the dentry that is set as the volume label */
-    de = (FATXXFS_DENTRY *) data_buf;
-    for (i = 0; i < fatfs->ssize; i += sizeof(*de)) {
-        if (de->attrib == FATFS_ATTR_VOLUME)
-            break;
-        de++;
-    }
-    /* If we didn't find it, then reset de */
-    if (de->attrib != FATFS_ATTR_VOLUME)
-        de = NULL;
-
-
-    /* Print the general file system information */
-
-    tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n");
-    tsk_fprintf(hFile, "--------------------------------------------\n");
-
-    tsk_fprintf(hFile, "File System Type: FAT");
-    if (fs->ftype == TSK_FS_TYPE_FAT12)
-        tsk_fprintf(hFile, "12\n");
-    else if (fs->ftype == TSK_FS_TYPE_FAT16)
-        tsk_fprintf(hFile, "16\n");
-    else if (fs->ftype == TSK_FS_TYPE_FAT32)
-        tsk_fprintf(hFile, "32\n");
-    else
-        tsk_fprintf(hFile, "\n");
-
-    tsk_fprintf(hFile, "\nOEM Name: %c%c%c%c%c%c%c%c\n", sb->oemname[0],
-        sb->oemname[1], sb->oemname[2], sb->oemname[3], sb->oemname[4],
-        sb->oemname[5], sb->oemname[6], sb->oemname[7]);
-
-
-    if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) {
-        tsk_fprintf(hFile, "Volume ID: 0x%" PRIx32 "\n",
-            tsk_getu32(fs->endian, sb->a.f16.vol_id));
-
-        tsk_fprintf(hFile,
-            "Volume Label (Boot Sector): %c%c%c%c%c%c%c%c%c%c%c\n",
-            sb->a.f16.vol_lab[0], sb->a.f16.vol_lab[1],
-            sb->a.f16.vol_lab[2], sb->a.f16.vol_lab[3],
-            sb->a.f16.vol_lab[4], sb->a.f16.vol_lab[5],
-            sb->a.f16.vol_lab[6], sb->a.f16.vol_lab[7],
-            sb->a.f16.vol_lab[8], sb->a.f16.vol_lab[9],
-            sb->a.f16.vol_lab[10]);
-
-        if ((de) && (de->name)) {
-            tsk_fprintf(hFile,
-                "Volume Label (Root Directory): %c%c%c%c%c%c%c%c%c%c%c\n",
-                de->name[0], de->name[1], de->name[2], de->name[3],
-                de->name[4], de->name[5], de->name[6], de->name[7],
-                de->ext[0], de->ext[1], de->ext[2]);
-        }
-        else {
-            tsk_fprintf(hFile, "Volume Label (Root Directory):\n");
-        }
-
-        tsk_fprintf(hFile, "File System Type Label: %c%c%c%c%c%c%c%c\n",
-            sb->a.f16.fs_type[0], sb->a.f16.fs_type[1],
-            sb->a.f16.fs_type[2], sb->a.f16.fs_type[3],
-            sb->a.f16.fs_type[4], sb->a.f16.fs_type[5],
-            sb->a.f16.fs_type[6], sb->a.f16.fs_type[7]);
-    }
-    else {
-
-        char *fat_fsinfo_buf;
-        FATXXFS_FSINFO *fat_info;
-
-        if ((fat_fsinfo_buf = (char *)
-                tsk_malloc(sizeof(FATXXFS_FSINFO))) == NULL) {
-            free(data_buf);
-            return 1;
-        }
-
-        tsk_fprintf(hFile, "Volume ID: 0x%" PRIx32 "\n",
-            tsk_getu32(fs->endian, sb->a.f32.vol_id));
-
-        tsk_fprintf(hFile,
-            "Volume Label (Boot Sector): %c%c%c%c%c%c%c%c%c%c%c\n",
-            sb->a.f32.vol_lab[0], sb->a.f32.vol_lab[1],
-            sb->a.f32.vol_lab[2], sb->a.f32.vol_lab[3],
-            sb->a.f32.vol_lab[4], sb->a.f32.vol_lab[5],
-            sb->a.f32.vol_lab[6], sb->a.f32.vol_lab[7],
-            sb->a.f32.vol_lab[8], sb->a.f32.vol_lab[9],
-            sb->a.f32.vol_lab[10]);
-
-        if ((de) && (de->name)) {
-            tsk_fprintf(hFile,
-                "Volume Label (Root Directory): %c%c%c%c%c%c%c%c%c%c%c\n",
-                de->name[0], de->name[1], de->name[2], de->name[3],
-                de->name[4], de->name[5], de->name[6], de->name[7],
-                de->ext[0], de->ext[1], de->ext[2]);
-        }
-        else {
-            tsk_fprintf(hFile, "Volume Label (Root Directory):\n");
-        }
-
-        tsk_fprintf(hFile, "File System Type Label: %c%c%c%c%c%c%c%c\n",
-            sb->a.f32.fs_type[0], sb->a.f32.fs_type[1],
-            sb->a.f32.fs_type[2], sb->a.f32.fs_type[3],
-            sb->a.f32.fs_type[4], sb->a.f32.fs_type[5],
-            sb->a.f32.fs_type[6], sb->a.f32.fs_type[7]);
-
-
-        /* Process the FS info */
-        if (tsk_getu16(fs->endian, sb->a.f32.fsinfo)) {
-            cnt =
-                tsk_fs_read_block(fs, (TSK_DADDR_T) tsk_getu16(fs->endian,
-                    sb->a.f32.fsinfo), fat_fsinfo_buf,
-                sizeof(FATXXFS_FSINFO));
-
-            if (cnt != sizeof(FATXXFS_FSINFO)) {
-                if (cnt >= 0) {
-                    tsk_error_reset();
-                    tsk_error_set_errno(TSK_ERR_FS_READ);
-                }
-                tsk_error_set_errstr2
-                    ("fatxxfs_fsstat: TSK_FS_TYPE_FAT32 FSINFO block: %"
-                    PRIuDADDR, (TSK_DADDR_T) tsk_getu16(fs->endian,
-                        sb->a.f32.fsinfo));
-                free(data_buf);
-                free(fat_fsinfo_buf);
-                return 1;
-            }
-
-
-            fat_info = (FATXXFS_FSINFO *) fat_fsinfo_buf;
-            tsk_fprintf(hFile,
-                "Next Free Sector (FS Info): %" PRIuDADDR "\n",
-                FATFS_CLUST_2_SECT(fatfs, tsk_getu32(fs->endian,
-                        fat_info->nextfree)));
-
-            tsk_fprintf(hFile,
-                "Free Sector Count (FS Info): %" PRIu32 "\n",
-                (tsk_getu32(fs->endian,
-                        fat_info->freecnt) * fatfs->csize));
-
-            free(fat_fsinfo_buf);
-        }
-    }
-
-    free(data_buf);
-
-    tsk_fprintf(hFile, "\nSectors before file system: %" PRIu32 "\n",
-        tsk_getu32(fs->endian, sb->prevsect));
-
-    tsk_fprintf(hFile, "\nFile System Layout (in sectors)\n");
-
-    tsk_fprintf(hFile, "Total Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
-        fs->first_block, fs->last_block);
-
-    if (fs->last_block != fs->last_block_act)
-        tsk_fprintf(hFile,
-            "Total Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n",
-            fs->first_block, fs->last_block_act);
-
-    tsk_fprintf(hFile, "* Reserved: 0 - %" PRIuDADDR "\n",
-        fatfs->firstfatsect - 1);
-
-    tsk_fprintf(hFile, "** Boot Sector: 0\n");
-
-    if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) {
-        tsk_fprintf(hFile, "** FS Info Sector: %" PRIu16 "\n",
-            tsk_getu16(fs->endian, sb->a.f32.fsinfo));
-
-        tsk_fprintf(hFile, "** Backup Boot Sector: %" PRIu16 "\n",
-            tsk_getu16(fs->endian, sb->a.f32.bs_backup));
-    }
-
-    for (i = 0; i < fatfs->numfat; i++) {
-        TSK_DADDR_T base = fatfs->firstfatsect + i * (fatfs->sectperfat);
-
-        tsk_fprintf(hFile, "* FAT %d: %" PRIuDADDR " - %" PRIuDADDR "\n",
-            i, base, (base + fatfs->sectperfat - 1));
-    }
-
-    tsk_fprintf(hFile, "* Data Area: %" PRIuDADDR " - %" PRIuDADDR "\n",
-        fatfs->firstdatasect, fs->last_block);
-
-    if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) {
-        TSK_DADDR_T x = fatfs->csize * fatfs->clustcnt;
-
-        tsk_fprintf(hFile,
-            "** Root Directory: %" PRIuDADDR " - %" PRIuDADDR "\n",
-            fatfs->firstdatasect, fatfs->firstclustsect - 1);
-
-        tsk_fprintf(hFile,
-            "** Cluster Area: %" PRIuDADDR " - %" PRIuDADDR "\n",
-            fatfs->firstclustsect, (fatfs->firstclustsect + x - 1));
-
-        if ((fatfs->firstclustsect + x - 1) != fs->last_block) {
-            tsk_fprintf(hFile,
-                "** Non-clustered: %" PRIuDADDR " - %" PRIuDADDR "\n",
-                (fatfs->firstclustsect + x), fs->last_block);
-        }
-    }
-    else {
-        TSK_LIST *list_seen = NULL;
-        TSK_DADDR_T x = fatfs->csize * (fatfs->lastclust - 1);
-        TSK_DADDR_T clust, clust_p;
-
-        tsk_fprintf(hFile,
-            "** Cluster Area: %" PRIuDADDR " - %" PRIuDADDR "\n",
-            fatfs->firstclustsect, (fatfs->firstclustsect + x - 1));
-
-
-        clust_p = fatfs->rootsect;
-        clust = FATFS_SECT_2_CLUST(fatfs, fatfs->rootsect);
-        while ((clust) && (0 == FATFS_ISEOF(clust, FATFS_32_MASK))) {
-            TSK_DADDR_T nxt;
-            clust_p = clust;
-
-            /* Make sure we do not get into an infinite loop */
-            if (tsk_list_find(list_seen, clust)) {
-                if (tsk_verbose)
-                    tsk_fprintf(stderr,
-                        "Loop found while determining root directory size\n");
-                break;
-            }
-            if (tsk_list_add(&list_seen, clust)) {
-                tsk_list_free(list_seen);
-                list_seen = NULL;
-                return 1;
-            }
-
-            if (fatfs_getFAT(fatfs, clust, &nxt))
-                break;
-            clust = nxt;
-        }
-        tsk_list_free(list_seen);
-        list_seen = NULL;
-
-        tsk_fprintf(hFile,
-            "*** Root Directory: %" PRIuDADDR " - %" PRIuDADDR "\n",
-            fatfs->rootsect, (FATFS_CLUST_2_SECT(fatfs, clust_p + 1) - 1));
-
-        if ((fatfs->firstclustsect + x - 1) != fs->last_block) {
-            tsk_fprintf(hFile,
-                "** Non-clustered: %" PRIuDADDR " - %" PRIuDADDR "\n",
-                (fatfs->firstclustsect + x), fs->last_block);
-        }
-    }
-
-
-    tsk_fprintf(hFile, "\nMETADATA INFORMATION\n");
-    tsk_fprintf(hFile, "--------------------------------------------\n");
-
-    tsk_fprintf(hFile, "Range: %" PRIuINUM " - %" PRIuINUM "\n",
-        fs->first_inum, fs->last_inum);
-    tsk_fprintf(hFile, "Root Directory: %" PRIuINUM "\n", fs->root_inum);
-
-
-    tsk_fprintf(hFile, "\nCONTENT INFORMATION\n");
-    tsk_fprintf(hFile, "--------------------------------------------\n");
-    tsk_fprintf(hFile, "Sector Size: %" PRIu16 "\n", fatfs->ssize);
-    tsk_fprintf(hFile, "Cluster Size: %" PRIu32 "\n",
-        (uint32_t) fatfs->csize << fatfs->ssize_sh);
-
-    tsk_fprintf(hFile, "Total Cluster Range: 2 - %" PRIuDADDR "\n",
-        fatfs->lastclust);
-
-
-    /* cycle via cluster and look at each cluster in the FAT 
-     * for clusters marked as bad */
-    cnt = 0;
-    for (i = 2; i <= fatfs->lastclust; i++) {
-        TSK_DADDR_T entry;
-        TSK_DADDR_T sect;
-
-        /* Get the FAT table entry */
-        if (fatfs_getFAT(fatfs, i, &entry))
-            break;
-
-        if (FATFS_ISBAD(entry, fatfs->mask) == 0) {
-            continue;
-        }
-
-        if (cnt == 0)
-            tsk_fprintf(hFile, "Bad Sectors: ");
-
-        sect = FATFS_CLUST_2_SECT(fatfs, i);
-        for (a = 0; a < fatfs->csize; a++) {
-            tsk_fprintf(hFile, "%" PRIuDADDR " ", sect + a);
-            if ((++cnt % 8) == 0)
-                tsk_fprintf(hFile, "\n");
-        }
-    }
-    if ((cnt > 0) && ((cnt % 8) != 0))
-        tsk_fprintf(hFile, "\n");
-
-    /* Display the FAT Table */
-    tsk_fprintf(hFile, "\nFAT CONTENTS (in sectors)\n");
-    tsk_fprintf(hFile, "--------------------------------------------\n");
-
-    /* 'sstart' marks the first sector of the current run to print */
-    sstart = fatfs->firstclustsect;
-
-    /* cycle via cluster and look at each cluster in the FAT  to make runs */
-    for (i = 2; i <= fatfs->lastclust; i++) {
-
-        /* 'send' marks the end sector of the current run, which will extend
-         * when the current cluster continues to the next 
-         */
-        send = FATFS_CLUST_2_SECT(fatfs, i + 1) - 1;
-
-        /* get the next cluster */
-        if (fatfs_getFAT(fatfs, i, &next))
-            break;
-
-        snext = FATFS_CLUST_2_SECT(fatfs, next);
-
-        /* we are also using the next sector (clust) */
-        if ((next & fatfs->mask) == (i + 1)) {
-            continue;
-        }
-
-        /* The next clust is either further away or the clust is available,
-         * print it if is further away 
-         */
-        else if ((next & fatfs->mask)) {
-            if (FATFS_ISEOF(next, fatfs->mask))
-                tsk_fprintf(hFile,
-                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
-                    ") -> EOF\n", sstart, send, send - sstart + 1);
-            else if (FATFS_ISBAD(next, fatfs->mask))
-                tsk_fprintf(hFile,
-                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
-                    ") -> BAD\n", sstart, send, send - sstart + 1);
-            else
-                tsk_fprintf(hFile,
-                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
-                    ") -> %" PRIuDADDR "\n", sstart, send,
-                    send - sstart + 1, snext);
-        }
-
-        /* reset the starting counter */
-        sstart = send + 1;
-    }
-
-    return 0;
-}
-
-uint8_t
-fatxxfs_open(FATFS_INFO *fatfs)
-{
-    const char *func_name = "fatxxfs_open";
-	TSK_FS_INFO *fs = &(fatfs->fs_info);
-	FATXXFS_SB *fatsb = (FATXXFS_SB*)(&fatfs->boot_sector_buffer);
-	int i = 0;
-	ssize_t cnt = 0;
-    TSK_DADDR_T sectors = 0;
-	TSK_FS_DIR * test_dir1; // Directories used to try opening the root directory
-	TSK_FS_DIR * test_dir2; //  to see if it's the Android FAT version
-
-    // clean up any error messages that are lying around
-    tsk_error_reset();
-
-    /* Calculate block sizes and layout info */
-    // sector size
-    fatfs->ssize = tsk_getu16(fs->endian, fatsb->ssize);
-    if (fatfs->ssize == 512) {
-        fatfs->ssize_sh = 9;
-    }
-    else if (fatfs->ssize == 1024) {
-        fatfs->ssize_sh = 10;
-    }
-    else if (fatfs->ssize == 2048) {
-        fatfs->ssize_sh = 11;
-    }
-    else if (fatfs->ssize == 4096) {
-        fatfs->ssize_sh = 12;
-    }
-    else {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-        tsk_error_set_errstr
-            ("Error: sector size (%d) is not a multiple of device size (%d)\nDo you have a disk image instead of a partition image?",
-            fatfs->ssize, fs->dev_bsize);
-        if (tsk_verbose)
-            fprintf(stderr, "%s: Invalid sector size (%d)\n",
-                func_name, fatfs->ssize);
-        return 1;
-    }
-
-    // cluster size 
-    fatfs->csize = fatsb->csize;
-    if ((fatfs->csize != 0x01) &&
-        (fatfs->csize != 0x02) &&
-        (fatfs->csize != 0x04) &&
-        (fatfs->csize != 0x08) &&
-        (fatfs->csize != 0x10) &&
-        (fatfs->csize != 0x20) &&
-        (fatfs->csize != 0x40) && (fatfs->csize != 0x80)) {
-        if (tsk_verbose)
-            fprintf(stderr, "%s: Invalid cluster size (%d)\n",
-                func_name, fatfs->csize);
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-        tsk_error_set_errstr("Not a FATXX file system (cluster size)");
-        return 1;
-    }
-
-    // number of FAT tables
-    fatfs->numfat = fatsb->numfat;
-    if ((fatfs->numfat == 0) || (fatfs->numfat > 8)) {
-        if (tsk_verbose)
-            fprintf(stderr, "%s: Invalid number of FATS (%d)\n",
-                func_name, fatfs->numfat);
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-        tsk_error_set_errstr("Not a FATXX file system (number of FATs)");
-        return 1;
-    }
-
-    /* We can't do a sanity check on this b.c. TSK_FS_TYPE_FAT32 has a value of 0 */
-    /* num of root entries */
-    fatfs->numroot = tsk_getu16(fs->endian, fatsb->numroot);
-
-    /* if sectors16 is 0, then the number of sectors is stored in sectors32 */
-    if (0 == (sectors = tsk_getu16(fs->endian, fatsb->sectors16)))
-        sectors = tsk_getu32(fs->endian, fatsb->sectors32);
-
-    /* if secperfat16 is 0, then read sectperfat32 */
-    if (0 == (fatfs->sectperfat =
-            tsk_getu16(fs->endian, fatsb->sectperfat16)))
-        fatfs->sectperfat =
-            tsk_getu32(fs->endian, fatsb->a.f32.sectperfat32);
-
-    if (fatfs->sectperfat == 0) {
-        if (tsk_verbose)
-            fprintf(stderr,
-                "%s: Invalid number of sectors per FAT (%d)\n",
-                func_name, fatfs->sectperfat);
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-        tsk_error_set_errstr
-            ("Not a FATXX file system (invalid sectors per FAT)");
-        return 1;
-    }
-
-    fatfs->firstfatsect = tsk_getu16(fs->endian, fatsb->reserved);
-    if ((fatfs->firstfatsect == 0) || (fatfs->firstfatsect > sectors)) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
-        tsk_error_set_errstr
-            ("Not a FATXX file system (invalid first FAT sector %"
-            PRIuDADDR ")", fatfs->firstfatsect);
-        if (tsk_verbose)
-            fprintf(stderr,
-                "%s: Invalid first FAT (%" PRIuDADDR ")\n",
-                func_name, fatfs->firstfatsect);
-        return 1;
-    }
-
-    /* Calculate the block info
-     * 
-     * The sector of the begining of the data area  - which is 
-     * after all of the FATs
-     *
-     * For TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16, the data area starts with the root
-     * directory entries and then the first cluster.  For TSK_FS_TYPE_FAT32,
-     * the data area starts with clusters and the root directory
-     * is somewhere in the data area
-     */
-    fatfs->firstdatasect = fatfs->firstfatsect +
-        fatfs->sectperfat * fatfs->numfat;
-
-    /* The sector where the first cluster is located.  It will be used
-     * to translate cluster addresses to sector addresses 
-     *
-     * For TSK_FS_TYPE_FAT32, the first cluster is the start of the data area and
-     * it is after the root directory for TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16.  At this
-     * point in the program, numroot is set to 0 for TSK_FS_TYPE_FAT32
-     */
-    fatfs->firstclustsect = fatfs->firstdatasect +
-        ((fatfs->numroot * 32 + fatfs->ssize - 1) / fatfs->ssize);
-
-    /* total number of clusters */
-    fatfs->clustcnt = (sectors - fatfs->firstclustsect) / fatfs->csize;
-
-    /* the first cluster is #2, so the final cluster is: */
-    fatfs->lastclust = 1 + fatfs->clustcnt;
-
-
-    /* identify the FAT type by the total number of data clusters
-     * this calculation is from the MS FAT Overview Doc
-     *
-     * A FAT file system made by another OS could use different values
-     */
-    if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT_DETECT) {
-
-        if (fatfs->clustcnt < 4085) {
-            fatfs->fs_info.ftype = TSK_FS_TYPE_FAT12;
-        }
-        else if (fatfs->clustcnt < 65525) {
-            fatfs->fs_info.ftype = TSK_FS_TYPE_FAT16;
-        }
-        else {
-            fatfs->fs_info.ftype = TSK_FS_TYPE_FAT32;
-        }
-
-        fatfs->fs_info.ftype = fatfs->fs_info.ftype;
-    }
-
-    /* Some sanity checks */
-    else {
-        if ((fatfs->fs_info.ftype == TSK_FS_TYPE_FAT12)
-            && (fatfs->clustcnt >= 4085)) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-            tsk_error_set_errstr
-                ("Too many sectors for TSK_FS_TYPE_FAT12: try auto-detect mode");
-            if (tsk_verbose)
-                fprintf(stderr,
-                    "%s: Too many sectors for FAT12\n", func_name);
-            return 1;
-        }
-    }
-
-    if ((fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) && (fatfs->numroot != 0)) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-        tsk_error_set_errstr
-            ("Invalid TSK_FS_TYPE_FAT32 image (numroot != 0)");
-        if (tsk_verbose)
-            fprintf(stderr, "%s: numroom != 0 for FAT32\n", func_name);
-        return 1;
-    }
-
-    if ((fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) && (fatfs->numroot == 0)) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-        tsk_error_set_errstr
-            ("Invalid FAT image (numroot == 0, and not TSK_FS_TYPE_FAT32)");
-        if (tsk_verbose)
-            fprintf(stderr, "%s: numroom == 0 and not FAT32\n", func_name);
-        return 1;
-    }
-
-    /* additional sanity checks if we think we are using the backup boot sector.
-     * The scenario to prevent here is if fat_open is called 6 sectors before the real start
-     * of the file system, then we want to detect that it was not a backup that we saw.  
-     */
-    if (fatfs->using_backup_boot_sector) {
-        // only FAT32 has backup boot sectors..
-        if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-            tsk_error_set_errstr
-                ("Invalid FAT image (Used what we thought was a backup boot sector, but it is not TSK_FS_TYPE_FAT32)");
-            if (tsk_verbose)
-                fprintf(stderr,
-                    "%s: Had to use backup boot sector, but this isn't FAT32\n", func_name);
-            return 1;
-        }
-        if (fatfs->numroot > 1) {
-            uint8_t buf1[512];
-            uint8_t buf2[512];
-            int i2;
-            int numDiffs;
-
-            cnt =
-                tsk_fs_read(fs, fatfs->firstfatsect * fatfs->ssize,
-                (char *) buf1, 512);
-            if (cnt != 512) {
-                if (cnt >= 0) {
-                    tsk_error_reset();
-                    tsk_error_set_errno(TSK_ERR_FS_READ);
-                }
-                tsk_error_set_errstr2("%s: FAT1", func_name);
-                fs->tag = 0;
-                return 1;
-            }
-
-            cnt =
-                tsk_fs_read(fs,
-                (fatfs->firstfatsect + fatfs->sectperfat) * fatfs->ssize,
-                (char *) buf2, 512);
-            if (cnt != 512) {
-                if (cnt >= 0) {
-                    tsk_error_reset();
-                    tsk_error_set_errno(TSK_ERR_FS_READ);
-                }
-                tsk_error_set_errstr2("%s: FAT2", func_name);
-                fs->tag = 0;
-                return 1;
-            }
-
-            numDiffs = 0;
-            for (i2 = 0; i2 < 512; i2++) {
-                if (buf1[i2] != buf2[i2]) {
-                    numDiffs++;
-                }
-            }
-            if (numDiffs > 25) {
-                tsk_error_reset();
-                tsk_error_set_errno(TSK_ERR_FS_MAGIC);
-                tsk_error_set_errstr
-                    ("Invalid FAT image (Too many differences between FATS from guessing (%d diffs))",
-                    numDiffs);
-                if (tsk_verbose)
-                    fprintf(stderr,
-                        "%s: Too many differences in FAT from guessing (%d diffs)\n",
-                        func_name, numDiffs);
-                return 1;
-            }
-        }
-    }
-
-    /* Set the mask to use on the cluster values */
-    if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT12) {
-        fatfs->mask = FATFS_12_MASK;
-    }
-    else if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT16) {
-        fatfs->mask = FATFS_16_MASK;
-    }
-    else if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) {
-        fatfs->mask = FATFS_32_MASK;
-    }
-    else {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_ARG);
-        tsk_error_set_errstr("Unknown FAT type in %s: %d\n",
-            func_name, fatfs->fs_info.ftype);
-        return 1;
-    }
-    fs->duname = "Sector";
-
-    /* the root directories are always after the FAT for TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16,
-     * but are dynamically located for TSK_FS_TYPE_FAT32
-     */
-    if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32)
-        fatfs->rootsect = FATFS_CLUST_2_SECT(fatfs,
-            tsk_getu32(fs->endian, fatsb->a.f32.rootclust));
-    else
-        fatfs->rootsect = fatfs->firstdatasect;
-
-    for (i = 0; i < FATFS_FAT_CACHE_N; i++) {
-        fatfs->fatc_addr[i] = 0;
-        fatfs->fatc_ttl[i] = 0;
-    }
-
-    /*
-     * block calculations : although there are no blocks in fat, we will
-     * use these fields for sector calculations
-     */
-    fs->first_block = 0;
-    fs->block_count = sectors;
-    fs->last_block = fs->last_block_act = fs->block_count - 1;
-    fs->block_size = fatfs->ssize;
-
-    // determine the last block we have in this image
-    if ((TSK_DADDR_T) ((fatfs->fs_info.img_info->size - fatfs->fs_info.offset) / fs->block_size) <
-        fs->block_count)
-        fs->last_block_act =
-            (fatfs->fs_info.img_info->size - fatfs->fs_info.offset) / fs->block_size - 1;
-
-    /*
-     * inode calculations
-     */
-
-    /* maximum number of dentries in a sector & cluster */
-    fatfs->dentry_cnt_se = fatfs->ssize / sizeof(FATXXFS_DENTRY);
-    fatfs->dentry_cnt_cl = fatfs->dentry_cnt_se * fatfs->csize;
-
-    fs->root_inum = FATFS_ROOTINO;
-    fs->first_inum = FATFS_FIRSTINO;
-
-    /* Calculate inode addresses for the virtual files (MBR, one or two FATS) 
-     * and the virtual orphan files directory. */
-    fs->last_inum = (FATFS_SECT_2_INODE(fatfs, fs->last_block_act + 1) - 1) + FATFS_NUM_VIRT_FILES(fatfs);
-    fatfs->mbr_virt_inum = fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs) + 1;
-    fatfs->fat1_virt_inum = fatfs->mbr_virt_inum + 1;
-    if (fatfs->numfat == 2) {
-        fatfs->fat2_virt_inum = fatfs->fat1_virt_inum + 1;
-    }
-    else {
-        fatfs->fat2_virt_inum = fatfs->fat1_virt_inum;
-    }
-
-    /* Calculate the total number of inodes. */
-    fs->inum_count = fs->last_inum - fs->first_inum + 1;
-
-    /* Volume ID */
-    for (fs->fs_id_used = 0; fs->fs_id_used < 4; fs->fs_id_used++) {
-        if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32)
-            fs->fs_id[fs->fs_id_used] =
-                fatsb->a.f32.vol_id[fs->fs_id_used];
-        else
-            fs->fs_id[fs->fs_id_used] =
-                fatsb->a.f16.vol_id[fs->fs_id_used];
-    }
-
-    /*
-     * Set the function pointers  
-     */
-
-    fs->block_walk = fatfs_block_walk;
-    fs->block_getflags = fatfs_block_getflags;
-
-    fs->inode_walk = fatfs_inode_walk;
-    fs->istat = fatfs_istat;
-    fs->file_add_meta = fatfs_inode_lookup;
-
-    fs->get_default_attr_type = fatfs_get_default_attr_type;
-    fs->load_attrs = fatfs_make_data_runs;
-
-    fs->dir_open_meta = fatfs_dir_open_meta;
-    fs->name_cmp = fatfs_name_cmp;
-
-    fs->fsstat = fatxxfs_fsstat;
-    fs->fscheck = fatfs_fscheck;
-
-    fs->close = fatfs_close;
-
-    fs->jblk_walk = fatfs_jblk_walk;
-    fs->jentry_walk = fatfs_jentry_walk;
-    fs->jopen = fatfs_jopen;
-
-    fatfs->is_cluster_alloc = fatxxfs_is_cluster_alloc;
-    fatfs->is_dentry = fatxxfs_is_dentry;
-    fatfs->dinode_copy =  fatxxfs_dinode_copy;
-    fatfs->inode_lookup = fatxxfs_inode_lookup;
-    fatfs->inode_walk_should_skip_dentry = fatxxfs_inode_walk_should_skip_dentry;
-    fatfs->istat_attr_flags = fatxxfs_istat_attr_flags;
-    fatfs->dent_parse_buf = fatxxfs_dent_parse_buf;
-
-    // initialize the caches
-    tsk_init_lock(&fatfs->cache_lock);
-    tsk_init_lock(&fatfs->dir_lock);
-    fatfs->inum2par = NULL;
-
-	// Test to see if this is the odd Android case where the FAT entries have no short name
-	//
-	// If there are no entries found with the normal short name
-	// and we find more entries by removing the short name test for allocated directories, then assume
-	// this is the case where we have no short names
-	fatfs->subtype = TSK_FATFS_SUBTYPE_SPEC;
-	test_dir1 = tsk_fs_dir_open_meta(fs, fs->root_inum);
-
-	if(test_dir1->names_used <= 4){ // At most four automatic directores ($MBR, $FAT1, $FAT1, $OrphanFiles)
-		fatfs->subtype = TSK_FATFS_SUBTYPE_ANDROID_1;
-		test_dir2 = tsk_fs_dir_open_meta(fs, fs->root_inum);
-
-		if(test_dir2->names_used > test_dir1->names_used){
-			fatfs->subtype = TSK_FATFS_SUBTYPE_ANDROID_1;
-		}
-		else{
-			fatfs->subtype = TSK_FATFS_SUBTYPE_SPEC;
-		}
-		tsk_fs_dir_close(test_dir2);
-	}
-	tsk_fs_dir_close(test_dir1);
-
-    return 0;
-}
-
-/* Return 1 if allocated, 0 if unallocated, and -1 if error */
-int8_t
-fatxxfs_is_cluster_alloc(FATFS_INFO *fatfs, TSK_DADDR_T clust)
-{
-    TSK_DADDR_T content = 0;
-
-    if (fatfs_getFAT(fatfs, clust, &content))
-        return -1;
-    else if (content == FATFS_UNALLOC)
-        return 0;
-    else
-        return 1;
+/*
+** fatxxfs
+** The Sleuth Kit 
+**
+** Content and meta data layer support for the FATXX file system 
+**
+** Brian Carrier [carrier <at> sleuthkit [dot] org]
+** Copyright (c) 2006-2013 Brian Carrier, Basis Technology. All Rights reserved
+** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
+**
+** TASK
+** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
+**
+**
+** This software is distributed under the Common Public License 1.0
+**
+** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05)
+**
+*/
+
+/**
+ * \file fatxxfs.c
+ * Contains the internal TSK FATXX (FAT12, FAT16, FAT32) file system code to 
+ * handle basic file system processing for opening file system, processing 
+ * sectors, and directory entries. 
+ */
+
+#include "tsk_fatxxfs.h"
+
+/*
+ * Implementation NOTES 
+ *
+ * TSK_FS_META contains the first cluster.  file_walk will return sector
+ * values though because the cluster numbers do not start until after
+ * the FAT.  That makes it very hard to address the first few blocks!
+ *
+ * Inodes numbers do not exist in FAT.  To make up for this we will count
+ * directory entries as the inodes.   As the root directory does not have
+ * any records in FAT, we will give it times of 0 and call it inode 2 to
+ * keep consistent with UNIX.  After that, each 32-byte slot is numbered
+ * as though it were a directory entry (even if it is not).  Therefore,
+ * when an inode walk is performed, not all inode values will be displayed
+ * even when '-e' is given for ils. 
+ *
+ * Progs like 'ils -e' are very slow because we have to look at each
+ * block to see if it is a file system structure.
+ */
+
+/**
+ * Print details about the file system to a file handle. 
+ *
+ * @param fs File system to print details on
+ * @param hFile File handle to print text to
+ * 
+ * @returns 1 on error and 0 on success
+ */
+static uint8_t
+fatxxfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
+{
+    unsigned int i;
+    int a;
+    TSK_DADDR_T next, snext, sstart, send;
+    FATFS_INFO *fatfs = (FATFS_INFO *) fs;
+    FATXXFS_SB *sb = (FATXXFS_SB*)fatfs->boot_sector_buffer;
+    char *data_buf;
+    FATXXFS_DENTRY *de;
+    ssize_t cnt;
+
+    // clean up any error messages that are lying around
+    tsk_error_reset();
+
+    if ((data_buf = (char *) tsk_malloc(fs->block_size)) == NULL) {
+        return 1;
+    }
+
+
+    /* Read the root directory sector so that we can get the volume
+     * label from it */
+    cnt = tsk_fs_read_block(fs, fatfs->rootsect, data_buf, fs->block_size);
+    if (cnt != fs->block_size) {
+        if (cnt >= 0) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_READ);
+        }
+        tsk_error_set_errstr2("fatxxfs_fsstat: root directory: %" PRIuDADDR,
+            fatfs->rootsect);
+        free(data_buf);
+        return 1;
+    }
+
+
+    /* Find the dentry that is set as the volume label */
+    de = (FATXXFS_DENTRY *) data_buf;
+    for (i = 0; i < fatfs->ssize; i += sizeof(*de)) {
+        if (de->attrib == FATFS_ATTR_VOLUME)
+            break;
+        de++;
+    }
+    /* If we didn't find it, then reset de */
+    if (de->attrib != FATFS_ATTR_VOLUME)
+        de = NULL;
+
+
+    /* Print the general file system information */
+
+    tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n");
+    tsk_fprintf(hFile, "--------------------------------------------\n");
+
+    tsk_fprintf(hFile, "File System Type: FAT");
+    if (fs->ftype == TSK_FS_TYPE_FAT12)
+        tsk_fprintf(hFile, "12\n");
+    else if (fs->ftype == TSK_FS_TYPE_FAT16)
+        tsk_fprintf(hFile, "16\n");
+    else if (fs->ftype == TSK_FS_TYPE_FAT32)
+        tsk_fprintf(hFile, "32\n");
+    else
+        tsk_fprintf(hFile, "\n");
+
+    tsk_fprintf(hFile, "\nOEM Name: %c%c%c%c%c%c%c%c\n", sb->oemname[0],
+        sb->oemname[1], sb->oemname[2], sb->oemname[3], sb->oemname[4],
+        sb->oemname[5], sb->oemname[6], sb->oemname[7]);
+
+
+    if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) {
+        tsk_fprintf(hFile, "Volume ID: 0x%" PRIx32 "\n",
+            tsk_getu32(fs->endian, sb->a.f16.vol_id));
+
+        tsk_fprintf(hFile,
+            "Volume Label (Boot Sector): %c%c%c%c%c%c%c%c%c%c%c\n",
+            sb->a.f16.vol_lab[0], sb->a.f16.vol_lab[1],
+            sb->a.f16.vol_lab[2], sb->a.f16.vol_lab[3],
+            sb->a.f16.vol_lab[4], sb->a.f16.vol_lab[5],
+            sb->a.f16.vol_lab[6], sb->a.f16.vol_lab[7],
+            sb->a.f16.vol_lab[8], sb->a.f16.vol_lab[9],
+            sb->a.f16.vol_lab[10]);
+
+        if ((de) && (de->name)) {
+            tsk_fprintf(hFile,
+                "Volume Label (Root Directory): %c%c%c%c%c%c%c%c%c%c%c\n",
+                de->name[0], de->name[1], de->name[2], de->name[3],
+                de->name[4], de->name[5], de->name[6], de->name[7],
+                de->ext[0], de->ext[1], de->ext[2]);
+        }
+        else {
+            tsk_fprintf(hFile, "Volume Label (Root Directory):\n");
+        }
+
+        tsk_fprintf(hFile, "File System Type Label: %c%c%c%c%c%c%c%c\n",
+            sb->a.f16.fs_type[0], sb->a.f16.fs_type[1],
+            sb->a.f16.fs_type[2], sb->a.f16.fs_type[3],
+            sb->a.f16.fs_type[4], sb->a.f16.fs_type[5],
+            sb->a.f16.fs_type[6], sb->a.f16.fs_type[7]);
+    }
+    else {
+
+        char *fat_fsinfo_buf;
+        FATXXFS_FSINFO *fat_info;
+
+        if ((fat_fsinfo_buf = (char *)
+                tsk_malloc(sizeof(FATXXFS_FSINFO))) == NULL) {
+            free(data_buf);
+            return 1;
+        }
+
+        tsk_fprintf(hFile, "Volume ID: 0x%" PRIx32 "\n",
+            tsk_getu32(fs->endian, sb->a.f32.vol_id));
+
+        tsk_fprintf(hFile,
+            "Volume Label (Boot Sector): %c%c%c%c%c%c%c%c%c%c%c\n",
+            sb->a.f32.vol_lab[0], sb->a.f32.vol_lab[1],
+            sb->a.f32.vol_lab[2], sb->a.f32.vol_lab[3],
+            sb->a.f32.vol_lab[4], sb->a.f32.vol_lab[5],
+            sb->a.f32.vol_lab[6], sb->a.f32.vol_lab[7],
+            sb->a.f32.vol_lab[8], sb->a.f32.vol_lab[9],
+            sb->a.f32.vol_lab[10]);
+
+        if ((de) && (de->name)) {
+            tsk_fprintf(hFile,
+                "Volume Label (Root Directory): %c%c%c%c%c%c%c%c%c%c%c\n",
+                de->name[0], de->name[1], de->name[2], de->name[3],
+                de->name[4], de->name[5], de->name[6], de->name[7],
+                de->ext[0], de->ext[1], de->ext[2]);
+        }
+        else {
+            tsk_fprintf(hFile, "Volume Label (Root Directory):\n");
+        }
+
+        tsk_fprintf(hFile, "File System Type Label: %c%c%c%c%c%c%c%c\n",
+            sb->a.f32.fs_type[0], sb->a.f32.fs_type[1],
+            sb->a.f32.fs_type[2], sb->a.f32.fs_type[3],
+            sb->a.f32.fs_type[4], sb->a.f32.fs_type[5],
+            sb->a.f32.fs_type[6], sb->a.f32.fs_type[7]);
+
+
+        /* Process the FS info */
+        if (tsk_getu16(fs->endian, sb->a.f32.fsinfo)) {
+            cnt =
+                tsk_fs_read_block(fs, (TSK_DADDR_T) tsk_getu16(fs->endian,
+                    sb->a.f32.fsinfo), fat_fsinfo_buf,
+                sizeof(FATXXFS_FSINFO));
+
+            if (cnt != sizeof(FATXXFS_FSINFO)) {
+                if (cnt >= 0) {
+                    tsk_error_reset();
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
+                }
+                tsk_error_set_errstr2
+                    ("fatxxfs_fsstat: TSK_FS_TYPE_FAT32 FSINFO block: %"
+                    PRIuDADDR, (TSK_DADDR_T) tsk_getu16(fs->endian,
+                        sb->a.f32.fsinfo));
+                free(data_buf);
+                free(fat_fsinfo_buf);
+                return 1;
+            }
+
+
+            fat_info = (FATXXFS_FSINFO *) fat_fsinfo_buf;
+            tsk_fprintf(hFile,
+                "Next Free Sector (FS Info): %" PRIuDADDR "\n",
+                FATFS_CLUST_2_SECT(fatfs, tsk_getu32(fs->endian,
+                        fat_info->nextfree)));
+
+            tsk_fprintf(hFile,
+                "Free Sector Count (FS Info): %" PRIu32 "\n",
+                (tsk_getu32(fs->endian,
+                        fat_info->freecnt) * fatfs->csize));
+
+            free(fat_fsinfo_buf);
+        }
+    }
+
+    free(data_buf);
+
+    tsk_fprintf(hFile, "\nSectors before file system: %" PRIu32 "\n",
+        tsk_getu32(fs->endian, sb->prevsect));
+
+    tsk_fprintf(hFile, "\nFile System Layout (in sectors)\n");
+
+    tsk_fprintf(hFile, "Total Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
+        fs->first_block, fs->last_block);
+
+    if (fs->last_block != fs->last_block_act)
+        tsk_fprintf(hFile,
+            "Total Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n",
+            fs->first_block, fs->last_block_act);
+
+    tsk_fprintf(hFile, "* Reserved: 0 - %" PRIuDADDR "\n",
+        fatfs->firstfatsect - 1);
+
+    tsk_fprintf(hFile, "** Boot Sector: 0\n");
+
+    if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) {
+        tsk_fprintf(hFile, "** FS Info Sector: %" PRIu16 "\n",
+            tsk_getu16(fs->endian, sb->a.f32.fsinfo));
+
+        tsk_fprintf(hFile, "** Backup Boot Sector: %" PRIu16 "\n",
+            tsk_getu16(fs->endian, sb->a.f32.bs_backup));
+    }
+
+    for (i = 0; i < fatfs->numfat; i++) {
+        TSK_DADDR_T base = fatfs->firstfatsect + i * (fatfs->sectperfat);
+
+        tsk_fprintf(hFile, "* FAT %d: %" PRIuDADDR " - %" PRIuDADDR "\n",
+            i, base, (base + fatfs->sectperfat - 1));
+    }
+
+    tsk_fprintf(hFile, "* Data Area: %" PRIuDADDR " - %" PRIuDADDR "\n",
+        fatfs->firstdatasect, fs->last_block);
+
+    if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) {
+        TSK_DADDR_T x = fatfs->csize * fatfs->clustcnt;
+
+        tsk_fprintf(hFile,
+            "** Root Directory: %" PRIuDADDR " - %" PRIuDADDR "\n",
+            fatfs->firstdatasect, fatfs->firstclustsect - 1);
+
+        tsk_fprintf(hFile,
+            "** Cluster Area: %" PRIuDADDR " - %" PRIuDADDR "\n",
+            fatfs->firstclustsect, (fatfs->firstclustsect + x - 1));
+
+        if ((fatfs->firstclustsect + x - 1) != fs->last_block) {
+            tsk_fprintf(hFile,
+                "** Non-clustered: %" PRIuDADDR " - %" PRIuDADDR "\n",
+                (fatfs->firstclustsect + x), fs->last_block);
+        }
+    }
+    else {
+        TSK_LIST *list_seen = NULL;
+        TSK_DADDR_T x = fatfs->csize * (fatfs->lastclust - 1);
+        TSK_DADDR_T clust, clust_p;
+
+        tsk_fprintf(hFile,
+            "** Cluster Area: %" PRIuDADDR " - %" PRIuDADDR "\n",
+            fatfs->firstclustsect, (fatfs->firstclustsect + x - 1));
+
+
+        clust_p = fatfs->rootsect;
+        clust = FATFS_SECT_2_CLUST(fatfs, fatfs->rootsect);
+        while ((clust) && (0 == FATFS_ISEOF(clust, FATFS_32_MASK))) {
+            TSK_DADDR_T nxt;
+            clust_p = clust;
+
+            /* Make sure we do not get into an infinite loop */
+            if (tsk_list_find(list_seen, clust)) {
+                if (tsk_verbose)
+                    tsk_fprintf(stderr,
+                        "Loop found while determining root directory size\n");
+                break;
+            }
+            if (tsk_list_add(&list_seen, clust)) {
+                tsk_list_free(list_seen);
+                list_seen = NULL;
+                return 1;
+            }
+
+            if (fatfs_getFAT(fatfs, clust, &nxt))
+                break;
+            clust = nxt;
+        }
+        tsk_list_free(list_seen);
+        list_seen = NULL;
+
+        tsk_fprintf(hFile,
+            "*** Root Directory: %" PRIuDADDR " - %" PRIuDADDR "\n",
+            fatfs->rootsect, (FATFS_CLUST_2_SECT(fatfs, clust_p + 1) - 1));
+
+        if ((fatfs->firstclustsect + x - 1) != fs->last_block) {
+            tsk_fprintf(hFile,
+                "** Non-clustered: %" PRIuDADDR " - %" PRIuDADDR "\n",
+                (fatfs->firstclustsect + x), fs->last_block);
+        }
+    }
+
+
+    tsk_fprintf(hFile, "\nMETADATA INFORMATION\n");
+    tsk_fprintf(hFile, "--------------------------------------------\n");
+
+    tsk_fprintf(hFile, "Range: %" PRIuINUM " - %" PRIuINUM "\n",
+        fs->first_inum, fs->last_inum);
+    tsk_fprintf(hFile, "Root Directory: %" PRIuINUM "\n", fs->root_inum);
+
+
+    tsk_fprintf(hFile, "\nCONTENT INFORMATION\n");
+    tsk_fprintf(hFile, "--------------------------------------------\n");
+    tsk_fprintf(hFile, "Sector Size: %" PRIu16 "\n", fatfs->ssize);
+    tsk_fprintf(hFile, "Cluster Size: %" PRIu32 "\n",
+        (uint32_t) fatfs->csize << fatfs->ssize_sh);
+
+    tsk_fprintf(hFile, "Total Cluster Range: 2 - %" PRIuDADDR "\n",
+        fatfs->lastclust);
+
+
+    /* cycle via cluster and look at each cluster in the FAT 
+     * for clusters marked as bad */
+    cnt = 0;
+    for (i = 2; i <= fatfs->lastclust; i++) {
+        TSK_DADDR_T entry;
+        TSK_DADDR_T sect;
+
+        /* Get the FAT table entry */
+        if (fatfs_getFAT(fatfs, i, &entry))
+            break;
+
+        if (FATFS_ISBAD(entry, fatfs->mask) == 0) {
+            continue;
+        }
+
+        if (cnt == 0)
+            tsk_fprintf(hFile, "Bad Sectors: ");
+
+        sect = FATFS_CLUST_2_SECT(fatfs, i);
+        for (a = 0; a < fatfs->csize; a++) {
+            tsk_fprintf(hFile, "%" PRIuDADDR " ", sect + a);
+            if ((++cnt % 8) == 0)
+                tsk_fprintf(hFile, "\n");
+        }
+    }
+    if ((cnt > 0) && ((cnt % 8) != 0))
+        tsk_fprintf(hFile, "\n");
+
+    /* Display the FAT Table */
+    tsk_fprintf(hFile, "\nFAT CONTENTS (in sectors)\n");
+    tsk_fprintf(hFile, "--------------------------------------------\n");
+
+    /* 'sstart' marks the first sector of the current run to print */
+    sstart = fatfs->firstclustsect;
+
+    /* cycle via cluster and look at each cluster in the FAT  to make runs */
+    for (i = 2; i <= fatfs->lastclust; i++) {
+
+        /* 'send' marks the end sector of the current run, which will extend
+         * when the current cluster continues to the next 
+         */
+        send = FATFS_CLUST_2_SECT(fatfs, i + 1) - 1;
+
+        /* get the next cluster */
+        if (fatfs_getFAT(fatfs, i, &next))
+            break;
+
+        snext = FATFS_CLUST_2_SECT(fatfs, next);
+
+        /* we are also using the next sector (clust) */
+        if ((next & fatfs->mask) == (i + 1)) {
+            continue;
+        }
+
+        /* The next clust is either further away or the clust is available,
+         * print it if is further away 
+         */
+        else if ((next & fatfs->mask)) {
+            if (FATFS_ISEOF(next, fatfs->mask))
+                tsk_fprintf(hFile,
+                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
+                    ") -> EOF\n", sstart, send, send - sstart + 1);
+            else if (FATFS_ISBAD(next, fatfs->mask))
+                tsk_fprintf(hFile,
+                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
+                    ") -> BAD\n", sstart, send, send - sstart + 1);
+            else
+                tsk_fprintf(hFile,
+                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
+                    ") -> %" PRIuDADDR "\n", sstart, send,
+                    send - sstart + 1, snext);
+        }
+
+        /* reset the starting counter */
+        sstart = send + 1;
+    }
+
+    return 0;
+}
+
+uint8_t
+fatxxfs_open(FATFS_INFO *fatfs)
+{
+    const char *func_name = "fatxxfs_open";
+	TSK_FS_INFO *fs = &(fatfs->fs_info);
+	FATXXFS_SB *fatsb = (FATXXFS_SB*)(&fatfs->boot_sector_buffer);
+	int i = 0;
+	ssize_t cnt = 0;
+    TSK_DADDR_T sectors = 0;
+	TSK_FS_DIR * test_dir1; // Directories used to try opening the root directory
+	TSK_FS_DIR * test_dir2; //  to see if it's the Android FAT version
+
+    // clean up any error messages that are lying around
+    tsk_error_reset();
+
+    /* Calculate block sizes and layout info */
+    // sector size
+    fatfs->ssize = tsk_getu16(fs->endian, fatsb->ssize);
+    if (fatfs->ssize == 512) {
+        fatfs->ssize_sh = 9;
+    }
+    else if (fatfs->ssize == 1024) {
+        fatfs->ssize_sh = 10;
+    }
+    else if (fatfs->ssize == 2048) {
+        fatfs->ssize_sh = 11;
+    }
+    else if (fatfs->ssize == 4096) {
+        fatfs->ssize_sh = 12;
+    }
+    else {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr
+            ("Error: sector size (%d) is not a multiple of device size (%d)\nDo you have a disk image instead of a partition image?",
+            fatfs->ssize, fs->dev_bsize);
+        if (tsk_verbose)
+            fprintf(stderr, "%s: Invalid sector size (%d)\n",
+                func_name, fatfs->ssize);
+        return 1;
+    }
+
+    // cluster size 
+    fatfs->csize = fatsb->csize;
+    if ((fatfs->csize != 0x01) &&
+        (fatfs->csize != 0x02) &&
+        (fatfs->csize != 0x04) &&
+        (fatfs->csize != 0x08) &&
+        (fatfs->csize != 0x10) &&
+        (fatfs->csize != 0x20) &&
+        (fatfs->csize != 0x40) && (fatfs->csize != 0x80)) {
+        if (tsk_verbose)
+            fprintf(stderr, "%s: Invalid cluster size (%d)\n",
+                func_name, fatfs->csize);
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr("Not a FATXX file system (cluster size)");
+        return 1;
+    }
+
+    // number of FAT tables
+    fatfs->numfat = fatsb->numfat;
+    if ((fatfs->numfat == 0) || (fatfs->numfat > 8)) {
+        if (tsk_verbose)
+            fprintf(stderr, "%s: Invalid number of FATS (%d)\n",
+                func_name, fatfs->numfat);
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr("Not a FATXX file system (number of FATs)");
+        return 1;
+    }
+
+    /* We can't do a sanity check on this b.c. TSK_FS_TYPE_FAT32 has a value of 0 */
+    /* num of root entries */
+    fatfs->numroot = tsk_getu16(fs->endian, fatsb->numroot);
+
+    /* if sectors16 is 0, then the number of sectors is stored in sectors32 */
+    if (0 == (sectors = tsk_getu16(fs->endian, fatsb->sectors16)))
+        sectors = tsk_getu32(fs->endian, fatsb->sectors32);
+
+    /* if secperfat16 is 0, then read sectperfat32 */
+    if (0 == (fatfs->sectperfat =
+            tsk_getu16(fs->endian, fatsb->sectperfat16)))
+        fatfs->sectperfat =
+            tsk_getu32(fs->endian, fatsb->a.f32.sectperfat32);
+
+    if (fatfs->sectperfat == 0) {
+        if (tsk_verbose)
+            fprintf(stderr,
+                "%s: Invalid number of sectors per FAT (%d)\n",
+                func_name, fatfs->sectperfat);
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr
+            ("Not a FATXX file system (invalid sectors per FAT)");
+        return 1;
+    }
+
+    fatfs->firstfatsect = tsk_getu16(fs->endian, fatsb->reserved);
+    if ((fatfs->firstfatsect == 0) || (fatfs->firstfatsect > sectors)) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr
+            ("Not a FATXX file system (invalid first FAT sector %"
+            PRIuDADDR ")", fatfs->firstfatsect);
+        if (tsk_verbose)
+            fprintf(stderr,
+                "%s: Invalid first FAT (%" PRIuDADDR ")\n",
+                func_name, fatfs->firstfatsect);
+        return 1;
+    }
+
+    /* Calculate the block info
+     * 
+     * The sector of the begining of the data area  - which is 
+     * after all of the FATs
+     *
+     * For TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16, the data area starts with the root
+     * directory entries and then the first cluster.  For TSK_FS_TYPE_FAT32,
+     * the data area starts with clusters and the root directory
+     * is somewhere in the data area
+     */
+    fatfs->firstdatasect = fatfs->firstfatsect +
+        fatfs->sectperfat * fatfs->numfat;
+
+    /* The sector where the first cluster is located.  It will be used
+     * to translate cluster addresses to sector addresses 
+     *
+     * For TSK_FS_TYPE_FAT32, the first cluster is the start of the data area and
+     * it is after the root directory for TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16.  At this
+     * point in the program, numroot is set to 0 for TSK_FS_TYPE_FAT32
+     */
+    fatfs->firstclustsect = fatfs->firstdatasect +
+        ((fatfs->numroot * 32 + fatfs->ssize - 1) / fatfs->ssize);
+
+    /* total number of clusters */
+    fatfs->clustcnt = (sectors - fatfs->firstclustsect) / fatfs->csize;
+
+    /* the first cluster is #2, so the final cluster is: */
+    fatfs->lastclust = 1 + fatfs->clustcnt;
+
+
+    /* identify the FAT type by the total number of data clusters
+     * this calculation is from the MS FAT Overview Doc
+     *
+     * A FAT file system made by another OS could use different values
+     */
+    if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT_DETECT) {
+
+        if (fatfs->clustcnt < 4085) {
+            fatfs->fs_info.ftype = TSK_FS_TYPE_FAT12;
+        }
+        else if (fatfs->clustcnt < 65525) {
+            fatfs->fs_info.ftype = TSK_FS_TYPE_FAT16;
+        }
+        else {
+            fatfs->fs_info.ftype = TSK_FS_TYPE_FAT32;
+        }
+
+        fatfs->fs_info.ftype = fatfs->fs_info.ftype;
+    }
+
+    /* Some sanity checks */
+    else {
+        if ((fatfs->fs_info.ftype == TSK_FS_TYPE_FAT12)
+            && (fatfs->clustcnt >= 4085)) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+            tsk_error_set_errstr
+                ("Too many sectors for TSK_FS_TYPE_FAT12: try auto-detect mode");
+            if (tsk_verbose)
+                fprintf(stderr,
+                    "%s: Too many sectors for FAT12\n", func_name);
+            return 1;
+        }
+    }
+
+    if ((fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) && (fatfs->numroot != 0)) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr
+            ("Invalid TSK_FS_TYPE_FAT32 image (numroot != 0)");
+        if (tsk_verbose)
+            fprintf(stderr, "%s: numroom != 0 for FAT32\n", func_name);
+        return 1;
+    }
+
+    if ((fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) && (fatfs->numroot == 0)) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr
+            ("Invalid FAT image (numroot == 0, and not TSK_FS_TYPE_FAT32)");
+        if (tsk_verbose)
+            fprintf(stderr, "%s: numroom == 0 and not FAT32\n", func_name);
+        return 1;
+    }
+
+    /* additional sanity checks if we think we are using the backup boot sector.
+     * The scenario to prevent here is if fat_open is called 6 sectors before the real start
+     * of the file system, then we want to detect that it was not a backup that we saw.  
+     */
+    if (fatfs->using_backup_boot_sector) {
+        // only FAT32 has backup boot sectors..
+        if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+            tsk_error_set_errstr
+                ("Invalid FAT image (Used what we thought was a backup boot sector, but it is not TSK_FS_TYPE_FAT32)");
+            if (tsk_verbose)
+                fprintf(stderr,
+                    "%s: Had to use backup boot sector, but this isn't FAT32\n", func_name);
+            return 1;
+        }
+        if (fatfs->numroot > 1) {
+            uint8_t buf1[512];
+            uint8_t buf2[512];
+            int i2;
+            int numDiffs;
+
+            cnt =
+                tsk_fs_read(fs, fatfs->firstfatsect * fatfs->ssize,
+                (char *) buf1, 512);
+            if (cnt != 512) {
+                if (cnt >= 0) {
+                    tsk_error_reset();
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
+                }
+                tsk_error_set_errstr2("%s: FAT1", func_name);
+                fs->tag = 0;
+                return 1;
+            }
+
+            cnt =
+                tsk_fs_read(fs,
+                (fatfs->firstfatsect + fatfs->sectperfat) * fatfs->ssize,
+                (char *) buf2, 512);
+            if (cnt != 512) {
+                if (cnt >= 0) {
+                    tsk_error_reset();
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
+                }
+                tsk_error_set_errstr2("%s: FAT2", func_name);
+                fs->tag = 0;
+                return 1;
+            }
+
+            numDiffs = 0;
+            for (i2 = 0; i2 < 512; i2++) {
+                if (buf1[i2] != buf2[i2]) {
+                    numDiffs++;
+                }
+            }
+            if (numDiffs > 25) {
+                tsk_error_reset();
+                tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+                tsk_error_set_errstr
+                    ("Invalid FAT image (Too many differences between FATS from guessing (%d diffs))",
+                    numDiffs);
+                if (tsk_verbose)
+                    fprintf(stderr,
+                        "%s: Too many differences in FAT from guessing (%d diffs)\n",
+                        func_name, numDiffs);
+                return 1;
+            }
+        }
+    }
+
+    /* Set the mask to use on the cluster values */
+    if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT12) {
+        fatfs->mask = FATFS_12_MASK;
+    }
+    else if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT16) {
+        fatfs->mask = FATFS_16_MASK;
+    }
+    else if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) {
+        fatfs->mask = FATFS_32_MASK;
+    }
+    else {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("Unknown FAT type in %s: %d\n",
+            func_name, fatfs->fs_info.ftype);
+        return 1;
+    }
+    fs->duname = "Sector";
+
+    /* the root directories are always after the FAT for TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16,
+     * but are dynamically located for TSK_FS_TYPE_FAT32
+     */
+    if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32)
+        fatfs->rootsect = FATFS_CLUST_2_SECT(fatfs,
+            tsk_getu32(fs->endian, fatsb->a.f32.rootclust));
+    else
+        fatfs->rootsect = fatfs->firstdatasect;
+
+    for (i = 0; i < FATFS_FAT_CACHE_N; i++) {
+        fatfs->fatc_addr[i] = 0;
+        fatfs->fatc_ttl[i] = 0;
+    }
+
+    /*
+     * block calculations : although there are no blocks in fat, we will
+     * use these fields for sector calculations
+     */
+    fs->first_block = 0;
+    fs->block_count = sectors;
+    fs->last_block = fs->last_block_act = fs->block_count - 1;
+    fs->block_size = fatfs->ssize;
+
+    // determine the last block we have in this image
+    if ((TSK_DADDR_T) ((fatfs->fs_info.img_info->size - fatfs->fs_info.offset) / fs->block_size) <
+        fs->block_count)
+        fs->last_block_act =
+            (fatfs->fs_info.img_info->size - fatfs->fs_info.offset) / fs->block_size - 1;
+
+    /*
+     * inode calculations
+     */
+
+    /* maximum number of dentries in a sector & cluster */
+    fatfs->dentry_cnt_se = fatfs->ssize / sizeof(FATXXFS_DENTRY);
+    fatfs->dentry_cnt_cl = fatfs->dentry_cnt_se * fatfs->csize;
+
+    fs->root_inum = FATFS_ROOTINO;
+    fs->first_inum = FATFS_FIRSTINO;
+
+    /* Calculate inode addresses for the virtual files (MBR, one or two FATS) 
+     * and the virtual orphan files directory. */
+    fs->last_inum = (FATFS_SECT_2_INODE(fatfs, fs->last_block_act + 1) - 1) + FATFS_NUM_VIRT_FILES(fatfs);
+    fatfs->mbr_virt_inum = fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs) + 1;
+    fatfs->fat1_virt_inum = fatfs->mbr_virt_inum + 1;
+    if (fatfs->numfat == 2) {
+        fatfs->fat2_virt_inum = fatfs->fat1_virt_inum + 1;
+    }
+    else {
+        fatfs->fat2_virt_inum = fatfs->fat1_virt_inum;
+    }
+
+    /* Calculate the total number of inodes. */
+    fs->inum_count = fs->last_inum - fs->first_inum + 1;
+
+    /* Volume ID */
+    for (fs->fs_id_used = 0; fs->fs_id_used < 4; fs->fs_id_used++) {
+        if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32)
+            fs->fs_id[fs->fs_id_used] =
+                fatsb->a.f32.vol_id[fs->fs_id_used];
+        else
+            fs->fs_id[fs->fs_id_used] =
+                fatsb->a.f16.vol_id[fs->fs_id_used];
+    }
+
+    /*
+     * Set the function pointers  
+     */
+
+    fs->block_walk = fatfs_block_walk;
+    fs->block_getflags = fatfs_block_getflags;
+
+    fs->inode_walk = fatfs_inode_walk;
+    fs->istat = fatfs_istat;
+    fs->file_add_meta = fatfs_inode_lookup;
+
+    fs->get_default_attr_type = fatfs_get_default_attr_type;
+    fs->load_attrs = fatfs_make_data_runs;
+
+    fs->dir_open_meta = fatfs_dir_open_meta;
+    fs->name_cmp = fatfs_name_cmp;
+
+    fs->fsstat = fatxxfs_fsstat;
+    fs->fscheck = fatfs_fscheck;
+
+    fs->close = fatfs_close;
+
+    fs->jblk_walk = fatfs_jblk_walk;
+    fs->jentry_walk = fatfs_jentry_walk;
+    fs->jopen = fatfs_jopen;
+
+    fatfs->is_cluster_alloc = fatxxfs_is_cluster_alloc;
+    fatfs->is_dentry = fatxxfs_is_dentry;
+    fatfs->dinode_copy =  fatxxfs_dinode_copy;
+    fatfs->inode_lookup = fatxxfs_inode_lookup;
+    fatfs->inode_walk_should_skip_dentry = fatxxfs_inode_walk_should_skip_dentry;
+    fatfs->istat_attr_flags = fatxxfs_istat_attr_flags;
+    fatfs->dent_parse_buf = fatxxfs_dent_parse_buf;
+
+    // initialize the caches
+    tsk_init_lock(&fatfs->cache_lock);
+    tsk_init_lock(&fatfs->dir_lock);
+    fatfs->inum2par = NULL;
+
+	// Test to see if this is the odd Android case where the FAT entries have no short name
+	//
+	// If there are no entries found with the normal short name
+	// and we find more entries by removing the short name test for allocated directories, then assume
+	// this is the case where we have no short names
+	fatfs->subtype = TSK_FATFS_SUBTYPE_SPEC;
+	test_dir1 = tsk_fs_dir_open_meta(fs, fs->root_inum);
+
+	if(test_dir1->names_used <= 4){ // At most four automatic directores ($MBR, $FAT1, $FAT1, $OrphanFiles)
+		fatfs->subtype = TSK_FATFS_SUBTYPE_ANDROID_1;
+		test_dir2 = tsk_fs_dir_open_meta(fs, fs->root_inum);
+
+		if(test_dir2->names_used > test_dir1->names_used){
+			fatfs->subtype = TSK_FATFS_SUBTYPE_ANDROID_1;
+		}
+		else{
+			fatfs->subtype = TSK_FATFS_SUBTYPE_SPEC;
+		}
+		tsk_fs_dir_close(test_dir2);
+	}
+	tsk_fs_dir_close(test_dir1);
+
+    return 0;
+}
+
+/* Return 1 if allocated, 0 if unallocated, and -1 if error */
+int8_t
+fatxxfs_is_cluster_alloc(FATFS_INFO *fatfs, TSK_DADDR_T clust)
+{
+    TSK_DADDR_T content = 0;
+
+    if (fatfs_getFAT(fatfs, clust, &content))
+        return -1;
+    else if (content == FATFS_UNALLOC)
+        return 0;
+    else
+        return 1;
 }
\ No newline at end of file
diff --git a/tsk/fs/fatxxfs_dent.c b/tsk/fs/fatxxfs_dent.c
index 7894dbba9..7f7cd6413 100755
--- a/tsk/fs/fatxxfs_dent.c
+++ b/tsk/fs/fatxxfs_dent.c
@@ -1,431 +1,431 @@
-/*
-** fatfs_dent
-** The Sleuth Kit
-**
-** file name layer support for the FAT file system
-**
-** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2013 Brian Carrier, Basis Technology.  All Rights reserved
-** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
-**
-** TASK
-** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
-**
-**
-** This software is distributed under the Common Public License 1.0
-**
-** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05)
-**
-*/
-
-/**
-* \file fatfs_dent.cpp
-* Contains the internal TSK FAT file name processing code.
-*/
-
-#include "tsk_fs_i.h"
-#include "tsk_fatxxfs.h"
-#include <assert.h>
-
-/* Special data structure allocated for each directory to hold the long
-* file name entries until all entries have been found */
-typedef struct {
-    uint8_t name[FATFS_MAXNAMLEN_UTF8]; /* buffer for lfn - in reverse order */
-    uint16_t start;             /* current start of name */
-    uint8_t chk;                /* current checksum */
-    uint8_t seq;                /* seq of first entry in lfn */
-} FATXXFS_LFN;
-
-/**
- * /internal
- * Parse a buffer containing the contents of a directory and add TSK_FS_NAME 
- * objects for each named file found to the TSK_FS_DIR representation of the 
- * directory.
- *
- * @param a_fatfs File system information structure for file system that
- * contains the directory.
- * @param a_fs_dir Directory structure into to which parsed file metadata will
- * be added.
- * @param a_buf Buffer that contains the directory contents.
- * @param a_buf_len Length of buffer in bytes (must be a multiple of sector
-*  size).
- * @param a_sector_addrs Array where each element is the original address of
- * the corresponding sector in a_buf (size of array is number of sectors in
- * the directory).
- * @return TSK_RETVAL_ENUM
-*/
-TSK_RETVAL_ENUM
-fatxxfs_dent_parse_buf(FATFS_INFO *fatfs, TSK_FS_DIR *a_fs_dir, char *buf,
-    TSK_OFF_T len, TSK_DADDR_T *addrs)
-{
-    char *func_name = "fatxxfs_dent_parse_buf";
-    unsigned int idx = 0; 
-    unsigned int sidx = 0;
-    int a = 0;
-    int b = 0;
-    TSK_INUM_T ibase = 0;
-    FATXXFS_DENTRY *dep = NULL;
-    TSK_FS_INFO *fs = (TSK_FS_INFO*)&fatfs->fs_info;
-    int sectalloc = 0;
-    TSK_FS_NAME *fs_name = NULL;
-    FATXXFS_LFN lfninfo;
-    int entrySeenCount = 0;
-    int entryInvalidCount = 0;
-    uint8_t isCorruptDir = 0;
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(fatfs, "fatfs", func_name) ||
-        fatfs_ptr_arg_is_null(a_fs_dir, "a_fs_dir", func_name) ||
-        fatfs_ptr_arg_is_null(buf, "buf", func_name) ||
-        fatfs_ptr_arg_is_null(addrs, "addrs", func_name)) {
-        return TSK_ERR; 
-    }
-
-    assert(len > 0);
-    if (len < 0) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_ARG);
-        tsk_error_set_errstr("%s: invalid buffer length", func_name);
-        return TSK_ERR; 
-    }
-
-    dep = (FATXXFS_DENTRY*)buf;
-
-    if ((fs_name = tsk_fs_name_alloc(FATFS_MAXNAMLEN_UTF8, 32)) == NULL) {
-        return TSK_ERR;
-    }
-
-    memset(&lfninfo, 0, sizeof(FATXXFS_LFN));
-    lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1;
-
-    /* Loop through the sectors in the buffer. */ 
-    for (sidx = 0; sidx < (unsigned int) (len / fatfs->ssize); sidx++) {
-
-        /* Get the base inode for the current sector */
-        ibase = FATFS_SECT_2_INODE(fatfs, addrs[sidx]);
-        if (ibase > fs->last_inum) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_ARG);
-            tsk_error_set_errstr
-                ("fatfs_parse: inode address is too large");
-            tsk_fs_name_free(fs_name);
-            return TSK_COR;
-        }
-
-        if (tsk_verbose)
-            tsk_fprintf(stderr,
-            "fatfs_dent_parse_buf: Parsing sector %" PRIuDADDR
-            " for dir %" PRIuINUM "\n", addrs[sidx], a_fs_dir->addr);
-
-        /* Get the allocation status of the current sector. */
-        if ((sectalloc = fatfs_is_sectalloc(fatfs, addrs[sidx])) == -1) {
-            if (tsk_verbose) {
-                tsk_fprintf(stderr,
-                    "fatfs_dent_parse_buf: Error looking up sector allocation: %"
-                    PRIuDADDR "\n", addrs[sidx]);
-                tsk_error_print(stderr);
-            }
-            tsk_error_reset();
-            continue;
-        }
-
-        /* Loop through the putative directory entries in the current sector. */
-        for (idx = 0; idx < fatfs->dentry_cnt_se; idx++, dep++) {
-            FATXXFS_DENTRY *dir;
-            TSK_INUM_T inode;
-
-            entrySeenCount++;
-
-            /* Is the current entry a valid entry? */
-            if (0 == fatxxfs_is_dentry(fatfs, (FATFS_DENTRY*)dep, 
-                (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)sectalloc,
-                ((isCorruptDir == 0) && (sectalloc)) ? 1 : 0)) {
-                    if (tsk_verbose)
-                        tsk_fprintf(stderr,
-                        "fatfs_dent_parse_buf: Entry %u is invalid\n",
-                        idx);
-                    entryInvalidCount++;
-                    /* If we have seen four entries and all of them are corrupt,
-                    * then test every remaining entry in this folder -- 
-                    * even if the sector is allocated. The scenario is one
-                    * where we are processing a cluster that is allocated
-                    * to a file and we happen to get some data that matches
-                    * every now and then. */
-                    if ((entrySeenCount == 4) && (entryInvalidCount == 4)) {
-                        isCorruptDir = 1;
-                    }
-                    continue;
-            }
-
-            dir = dep;
-
-            /* Compute the inode address corresponding to this directory entry. */
-            inode = ibase + idx;
-
-            if ((dir->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
-                /* The current entry is a long file name entry. */
-                FATXXFS_DENTRY_LFN *dirl = (FATXXFS_DENTRY_LFN *) dir;
-
-                /* Store the name in dinfo until we get the 8.3 name
-                 * Use the checksum to identify a new sequence. */
-                if (((dirl->seq & FATXXFS_LFN_SEQ_FIRST) && (dirl->seq != FATXXFS_SLOT_DELETED)) || 
-                    (dirl->chksum != lfninfo.chk)) {
-                    // @@@ Do a partial output here
-                    
-                    /* This is the last long file name entry in a sequence. 
-                     * Reset the sequence number, check sum, and next char
-                     * address. */
-                    lfninfo.seq = dirl->seq & FATXXFS_LFN_SEQ_MASK;
-                    lfninfo.chk = dirl->chksum;
-                    lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1;
-                }
-                else if (dirl->seq != lfninfo.seq - 1) {
-                    // @@@ Check the sequence number - the checksum is correct though...
-                }
-
-                /* Copy the UTF16 values starting at end of buffer */
-                for (a = 3; a >= 0; a--) {
-                    if ((lfninfo.start > 0))
-                        lfninfo.name[lfninfo.start--] = dirl->part3[a];
-                }
-                for (a = 11; a >= 0; a--) {
-                    if ((lfninfo.start > 0))
-                        lfninfo.name[lfninfo.start--] = dirl->part2[a];
-                }
-                for (a = 9; a >= 0; a--) {
-                    if ((lfninfo.start > 0))
-                        lfninfo.name[lfninfo.start--] = dirl->part1[a];
-                }
-
-                // Skip ahead until we get a new sequence num or the 8.3 name
-                continue;
-            }
-            else if ((dir->attrib & FATFS_ATTR_VOLUME) == FATFS_ATTR_VOLUME) {
-                /* Special case for volume label: name does not have an
-                * extension and we add a note at the end that it is a label */
-                a = 0;
-
-                for (b = 0; b < 8; b++) {
-                    if ((dir->name[b] >= 0x20) && (dir->name[b] != 0xff)) {
-                        fs_name->name[a++] = dir->name[b];
-                    }
-                    else {
-                        fs_name->name[a++] = '^';
-                    }
-                }
-                for (b = 0; b < 3; b++) {
-                    if ((dir->ext[b] >= 0x20) && (dir->ext[b] != 0xff)) {
-                        fs_name->name[a++] = dir->ext[b];
-                    }
-                    else {
-                        fs_name->name[a++] = '^';
-                    }
-                }
-
-                fs_name->name[a] = '\0';
-                /* Append a string to show it is a label */
-                if (a + 22 < FATFS_MAXNAMLEN_UTF8) {
-                    const char *volstr = " (Volume Label Entry)";
-                    strncat(fs_name->name, volstr,
-                        FATFS_MAXNAMLEN_UTF8 - a);
-                }
-            }
-            else {
-                /* A short (8.3) entry */
-                char *name_ptr; // The dest location for the short name
-
-                /* if we have a lfn, copy it into fs_name->name
-                * and put the short name in fs_name->shrt_name */
-                if (lfninfo.start != FATFS_MAXNAMLEN_UTF8 - 1) {
-                    int retVal;
-
-                    /* @@@ Check the checksum */
-
-                    /* Convert the UTF16 to UTF8 */
-                    UTF16 *name16 =
-                        (UTF16 *) ((uintptr_t) & lfninfo.
-                        name[lfninfo.start + 1]);
-                    UTF8 *name8 = (UTF8 *) fs_name->name;
-
-                    retVal =
-                        tsk_UTF16toUTF8(fs->endian,
-                        (const UTF16 **) &name16,
-                        (UTF16 *) & lfninfo.name[FATFS_MAXNAMLEN_UTF8],
-                        &name8,
-                        (UTF8 *) ((uintptr_t) name8 +
-                        FATFS_MAXNAMLEN_UTF8), TSKlenientConversion);
-
-                    if (retVal != TSKconversionOK) {
-                        tsk_error_reset();
-                        tsk_error_set_errno(TSK_ERR_FS_UNICODE);
-                        tsk_error_set_errstr
-                            ("fatfs_parse: Error converting FAT LFN to UTF8: %d",
-                            retVal);
-                        continue;
-                    }
-
-                    /* Make sure it is NULL Terminated */
-                    if ((uintptr_t) name8 >
-                        (uintptr_t) fs_name->name + FATFS_MAXNAMLEN_UTF8)
-                        fs_name->name[FATFS_MAXNAMLEN_UTF8 - 1] = '\0';
-                    else
-                        *name8 = '\0';
-
-                    lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1;
-                    name_ptr = fs_name->shrt_name;      // put 8.3 into shrt_name
-                }
-                /* We don't have a LFN, so put the short name in
-                * fs_name->name */
-                else {
-                    fs_name->shrt_name[0] = '\0';
-                    name_ptr = fs_name->name;   // put 8.3 into normal location
-                }
-
-                /* copy in the short name into the place specified above.
-                * Skip spaces and put in the . */
-                a = 0;
-                for (b = 0; b < 8; b++) {
-                    if ((dir->name[b] != 0) && (dir->name[b] != 0xff) &&
-                        (dir->name[b] != 0x20)) {
-
-                            if ((b == 0)
-                                && (dir->name[0] == FATXXFS_SLOT_DELETED)) {
-                                    name_ptr[a++] = '_';
-                            }
-                            else if ((dir->lowercase & FATXXFS_CASE_LOWER_BASE)
-                                && (dir->name[b] >= 'A')
-                                && (dir->name[b] <= 'Z')) {
-                                    name_ptr[a++] = dir->name[b] + 32;
-                            }
-                            else {
-                                name_ptr[a++] = dir->name[b];
-                            }
-                    }
-                }
-
-                for (b = 0; b < 3; b++) {
-                    if ((dir->ext[b] != 0) && (dir->ext[b] != 0xff) &&
-                        (dir->ext[b] != 0x20)) {
-                            if (b == 0)
-                                name_ptr[a++] = '.';
-                            if ((dir->lowercase & FATXXFS_CASE_LOWER_EXT) &&
-                                (dir->ext[b] >= 'A') && (dir->ext[b] <= 'Z'))
-                                name_ptr[a++] = dir->ext[b] + 32;
-                            else
-                                name_ptr[a++] = dir->ext[b];
-                    }
-                }
-                name_ptr[a] = '\0';
-
-                // make sure that only ASCII is in the short name
-                fatfs_cleanup_ascii(name_ptr);
-            }
-
-            /* file type: FAT only knows DIR and FILE */
-            if ((dir->attrib & FATFS_ATTR_DIRECTORY) ==
-                FATFS_ATTR_DIRECTORY)
-                fs_name->type = TSK_FS_NAME_TYPE_DIR;
-            else
-                fs_name->type = TSK_FS_NAME_TYPE_REG;
-
-            /* set the inode */
-            fs_name->meta_addr = inode;
-            inode = 0;  // so that we don't use it anymore -- use only fs_name->meta_addr
-
-            /* Handle the . and .. entries specially
-            * The current inode 'address' they have is for the current
-            * slot in the cluster, but it needs to refer to the original
-            * slot
-            */
-            if (TSK_FS_ISDOT(fs_name->name)
-                    && (fs_name->type == TSK_FS_NAME_TYPE_DIR)
-                    && idx < 2) {
-                if (fs_name->name[1] == '\0') {
-                    /* Current directory - "." */
-                    fs_name->meta_addr =
-                        a_fs_dir->fs_file->meta->addr;
-                }
-                /* for the parent directory, look up in the list that
-                * is maintained in fafs_info */
-                else if (fs_name->name[1] == '.') {
-                    /* Parent directory - ".." */
-                    uint8_t dir_found = 0;
-
-                    if (fatfs_dir_buf_get(fatfs, a_fs_dir->fs_file->meta->addr, &(fs_name->meta_addr)) == 0)  {
-                        dir_found = 1;
-                    }
-
-                    if ((dir_found == 0)
-                        && (addrs[0] == fatfs->firstdatasect)) {
-                            /* if we are currently in the root directory, we aren't going to find
-                            * a parent.  This shouldn't happen, but could result in an infinite loop. */
-                            fs_name->meta_addr = 0;
-                            dir_found = 1;
-                    }
-                    if (dir_found == 0) {
-                        if (tsk_verbose)
-                            fprintf(stderr,
-                            "fatfs_dent_parse_buf: Walking directory to find parent\n");
-
-                        /* The parent directory is not in the list.  We are going to walk
-                        * the directory until we hit this directory. This process will
-                        * populate the buffer table and we will then rescan it */
-                        if (tsk_fs_dir_walk(fs, fs->root_inum,
-                            (TSK_FS_DIR_WALK_FLAG_ENUM)(TSK_FS_DIR_WALK_FLAG_ALLOC |
-                            TSK_FS_DIR_WALK_FLAG_UNALLOC |
-                            TSK_FS_DIR_WALK_FLAG_RECURSE),
-                            fatfs_find_parent_act,
-                            (void *) &a_fs_dir->fs_file->meta->addr)) {
-                                return TSK_OK;
-                        }
-
-                        if (tsk_verbose)
-                            fprintf(stderr,
-                            "fatfs_dent_parse_buf: Finished walking directory to find parent\n");
-
-                        if (fatfs_dir_buf_get(fatfs, a_fs_dir->fs_file->meta->addr, &(fs_name->meta_addr)) == 0) {
-                            dir_found = 1;
-                        }
-
-                        // if we did not find it, then it was probably
-                        // from the orphan directory...
-                        if (dir_found == 0)
-                            fs_name->meta_addr = TSK_FS_ORPHANDIR_INUM(fs);
-                    }
-                }
-            }
-            else {
-                /* Save the (non-. or ..) directory to parent directory info to local
-                * structures so that we can later fill into the inode
-                * info for '..' entries */
-                if (fs_name->type == TSK_FS_NAME_TYPE_DIR) {
-                    if (fatfs_dir_buf_add(fatfs,
-                        a_fs_dir->fs_file->meta->addr, fs_name->meta_addr))
-                        return TSK_ERR;
-                }
-            }
-
-
-            /* The allocation status of an entry is based on the allocation
-            * status of the sector it is in and the flag.  Deleted directories
-            * do not always clear the flags of each entry
-            */
-            if (sectalloc == 1) {
-				if(FATXXFS_IS_DELETED(dep->name, fatfs)){
-						fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;
-				}
-				else{
-					fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
-				}
-            }
-            else {
-                fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;
-            }
-
-            tsk_fs_dir_add(a_fs_dir, fs_name);
-        }
-    }
-    tsk_fs_name_free(fs_name);
-
-    return TSK_OK;
+/*
+** fatfs_dent
+** The Sleuth Kit
+**
+** file name layer support for the FAT file system
+**
+** Brian Carrier [carrier <at> sleuthkit [dot] org]
+** Copyright (c) 2006-2013 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
+**
+** TASK
+** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
+**
+**
+** This software is distributed under the Common Public License 1.0
+**
+** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05)
+**
+*/
+
+/**
+* \file fatfs_dent.cpp
+* Contains the internal TSK FAT file name processing code.
+*/
+
+#include "tsk_fs_i.h"
+#include "tsk_fatxxfs.h"
+#include <assert.h>
+
+/* Special data structure allocated for each directory to hold the long
+* file name entries until all entries have been found */
+typedef struct {
+    uint8_t name[FATFS_MAXNAMLEN_UTF8]; /* buffer for lfn - in reverse order */
+    uint16_t start;             /* current start of name */
+    uint8_t chk;                /* current checksum */
+    uint8_t seq;                /* seq of first entry in lfn */
+} FATXXFS_LFN;
+
+/**
+ * /internal
+ * Parse a buffer containing the contents of a directory and add TSK_FS_NAME 
+ * objects for each named file found to the TSK_FS_DIR representation of the 
+ * directory.
+ *
+ * @param a_fatfs File system information structure for file system that
+ * contains the directory.
+ * @param a_fs_dir Directory structure into to which parsed file metadata will
+ * be added.
+ * @param a_buf Buffer that contains the directory contents.
+ * @param a_buf_len Length of buffer in bytes (must be a multiple of sector
+*  size).
+ * @param a_sector_addrs Array where each element is the original address of
+ * the corresponding sector in a_buf (size of array is number of sectors in
+ * the directory).
+ * @return TSK_RETVAL_ENUM
+*/
+TSK_RETVAL_ENUM
+fatxxfs_dent_parse_buf(FATFS_INFO *fatfs, TSK_FS_DIR *a_fs_dir, char *buf,
+    TSK_OFF_T len, TSK_DADDR_T *addrs)
+{
+    char *func_name = "fatxxfs_dent_parse_buf";
+    unsigned int idx = 0; 
+    unsigned int sidx = 0;
+    int a = 0;
+    int b = 0;
+    TSK_INUM_T ibase = 0;
+    FATXXFS_DENTRY *dep = NULL;
+    TSK_FS_INFO *fs = (TSK_FS_INFO*)&fatfs->fs_info;
+    int sectalloc = 0;
+    TSK_FS_NAME *fs_name = NULL;
+    FATXXFS_LFN lfninfo;
+    int entrySeenCount = 0;
+    int entryInvalidCount = 0;
+    uint8_t isCorruptDir = 0;
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(fatfs, "fatfs", func_name) ||
+        fatfs_ptr_arg_is_null(a_fs_dir, "a_fs_dir", func_name) ||
+        fatfs_ptr_arg_is_null(buf, "buf", func_name) ||
+        fatfs_ptr_arg_is_null(addrs, "addrs", func_name)) {
+        return TSK_ERR; 
+    }
+
+    assert(len > 0);
+    if (len < 0) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("%s: invalid buffer length", func_name);
+        return TSK_ERR; 
+    }
+
+    dep = (FATXXFS_DENTRY*)buf;
+
+    if ((fs_name = tsk_fs_name_alloc(FATFS_MAXNAMLEN_UTF8, 32)) == NULL) {
+        return TSK_ERR;
+    }
+
+    memset(&lfninfo, 0, sizeof(FATXXFS_LFN));
+    lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1;
+
+    /* Loop through the sectors in the buffer. */ 
+    for (sidx = 0; sidx < (unsigned int) (len / fatfs->ssize); sidx++) {
+
+        /* Get the base inode for the current sector */
+        ibase = FATFS_SECT_2_INODE(fatfs, addrs[sidx]);
+        if (ibase > fs->last_inum) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_ARG);
+            tsk_error_set_errstr
+                ("fatfs_parse: inode address is too large");
+            tsk_fs_name_free(fs_name);
+            return TSK_COR;
+        }
+
+        if (tsk_verbose)
+            tsk_fprintf(stderr,
+            "fatfs_dent_parse_buf: Parsing sector %" PRIuDADDR
+            " for dir %" PRIuINUM "\n", addrs[sidx], a_fs_dir->addr);
+
+        /* Get the allocation status of the current sector. */
+        if ((sectalloc = fatfs_is_sectalloc(fatfs, addrs[sidx])) == -1) {
+            if (tsk_verbose) {
+                tsk_fprintf(stderr,
+                    "fatfs_dent_parse_buf: Error looking up sector allocation: %"
+                    PRIuDADDR "\n", addrs[sidx]);
+                tsk_error_print(stderr);
+            }
+            tsk_error_reset();
+            continue;
+        }
+
+        /* Loop through the putative directory entries in the current sector. */
+        for (idx = 0; idx < fatfs->dentry_cnt_se; idx++, dep++) {
+            FATXXFS_DENTRY *dir;
+            TSK_INUM_T inode;
+
+            entrySeenCount++;
+
+            /* Is the current entry a valid entry? */
+            if (0 == fatxxfs_is_dentry(fatfs, (FATFS_DENTRY*)dep, 
+                (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)sectalloc,
+                ((isCorruptDir == 0) && (sectalloc)) ? 1 : 0)) {
+                    if (tsk_verbose)
+                        tsk_fprintf(stderr,
+                        "fatfs_dent_parse_buf: Entry %u is invalid\n",
+                        idx);
+                    entryInvalidCount++;
+                    /* If we have seen four entries and all of them are corrupt,
+                    * then test every remaining entry in this folder -- 
+                    * even if the sector is allocated. The scenario is one
+                    * where we are processing a cluster that is allocated
+                    * to a file and we happen to get some data that matches
+                    * every now and then. */
+                    if ((entrySeenCount == 4) && (entryInvalidCount == 4)) {
+                        isCorruptDir = 1;
+                    }
+                    continue;
+            }
+
+            dir = dep;
+
+            /* Compute the inode address corresponding to this directory entry. */
+            inode = ibase + idx;
+
+            if ((dir->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
+                /* The current entry is a long file name entry. */
+                FATXXFS_DENTRY_LFN *dirl = (FATXXFS_DENTRY_LFN *) dir;
+
+                /* Store the name in dinfo until we get the 8.3 name
+                 * Use the checksum to identify a new sequence. */
+                if (((dirl->seq & FATXXFS_LFN_SEQ_FIRST) && (dirl->seq != FATXXFS_SLOT_DELETED)) || 
+                    (dirl->chksum != lfninfo.chk)) {
+                    // @@@ Do a partial output here
+                    
+                    /* This is the last long file name entry in a sequence. 
+                     * Reset the sequence number, check sum, and next char
+                     * address. */
+                    lfninfo.seq = dirl->seq & FATXXFS_LFN_SEQ_MASK;
+                    lfninfo.chk = dirl->chksum;
+                    lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1;
+                }
+                else if (dirl->seq != lfninfo.seq - 1) {
+                    // @@@ Check the sequence number - the checksum is correct though...
+                }
+
+                /* Copy the UTF16 values starting at end of buffer */
+                for (a = 3; a >= 0; a--) {
+                    if ((lfninfo.start > 0))
+                        lfninfo.name[lfninfo.start--] = dirl->part3[a];
+                }
+                for (a = 11; a >= 0; a--) {
+                    if ((lfninfo.start > 0))
+                        lfninfo.name[lfninfo.start--] = dirl->part2[a];
+                }
+                for (a = 9; a >= 0; a--) {
+                    if ((lfninfo.start > 0))
+                        lfninfo.name[lfninfo.start--] = dirl->part1[a];
+                }
+
+                // Skip ahead until we get a new sequence num or the 8.3 name
+                continue;
+            }
+            else if ((dir->attrib & FATFS_ATTR_VOLUME) == FATFS_ATTR_VOLUME) {
+                /* Special case for volume label: name does not have an
+                * extension and we add a note at the end that it is a label */
+                a = 0;
+
+                for (b = 0; b < 8; b++) {
+                    if ((dir->name[b] >= 0x20) && (dir->name[b] != 0xff)) {
+                        fs_name->name[a++] = dir->name[b];
+                    }
+                    else {
+                        fs_name->name[a++] = '^';
+                    }
+                }
+                for (b = 0; b < 3; b++) {
+                    if ((dir->ext[b] >= 0x20) && (dir->ext[b] != 0xff)) {
+                        fs_name->name[a++] = dir->ext[b];
+                    }
+                    else {
+                        fs_name->name[a++] = '^';
+                    }
+                }
+
+                fs_name->name[a] = '\0';
+                /* Append a string to show it is a label */
+                if (a + 22 < FATFS_MAXNAMLEN_UTF8) {
+                    const char *volstr = " (Volume Label Entry)";
+                    strncat(fs_name->name, volstr,
+                        FATFS_MAXNAMLEN_UTF8 - a);
+                }
+            }
+            else {
+                /* A short (8.3) entry */
+                char *name_ptr; // The dest location for the short name
+
+                /* if we have a lfn, copy it into fs_name->name
+                * and put the short name in fs_name->shrt_name */
+                if (lfninfo.start != FATFS_MAXNAMLEN_UTF8 - 1) {
+                    int retVal;
+
+                    /* @@@ Check the checksum */
+
+                    /* Convert the UTF16 to UTF8 */
+                    UTF16 *name16 =
+                        (UTF16 *) ((uintptr_t) & lfninfo.
+                        name[lfninfo.start + 1]);
+                    UTF8 *name8 = (UTF8 *) fs_name->name;
+
+                    retVal =
+                        tsk_UTF16toUTF8(fs->endian,
+                        (const UTF16 **) &name16,
+                        (UTF16 *) & lfninfo.name[FATFS_MAXNAMLEN_UTF8],
+                        &name8,
+                        (UTF8 *) ((uintptr_t) name8 +
+                        FATFS_MAXNAMLEN_UTF8), TSKlenientConversion);
+
+                    if (retVal != TSKconversionOK) {
+                        tsk_error_reset();
+                        tsk_error_set_errno(TSK_ERR_FS_UNICODE);
+                        tsk_error_set_errstr
+                            ("fatfs_parse: Error converting FAT LFN to UTF8: %d",
+                            retVal);
+                        continue;
+                    }
+
+                    /* Make sure it is NULL Terminated */
+                    if ((uintptr_t) name8 >
+                        (uintptr_t) fs_name->name + FATFS_MAXNAMLEN_UTF8)
+                        fs_name->name[FATFS_MAXNAMLEN_UTF8 - 1] = '\0';
+                    else
+                        *name8 = '\0';
+
+                    lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1;
+                    name_ptr = fs_name->shrt_name;      // put 8.3 into shrt_name
+                }
+                /* We don't have a LFN, so put the short name in
+                * fs_name->name */
+                else {
+                    fs_name->shrt_name[0] = '\0';
+                    name_ptr = fs_name->name;   // put 8.3 into normal location
+                }
+
+                /* copy in the short name into the place specified above.
+                * Skip spaces and put in the . */
+                a = 0;
+                for (b = 0; b < 8; b++) {
+                    if ((dir->name[b] != 0) && (dir->name[b] != 0xff) &&
+                        (dir->name[b] != 0x20)) {
+
+                            if ((b == 0)
+                                && (dir->name[0] == FATXXFS_SLOT_DELETED)) {
+                                    name_ptr[a++] = '_';
+                            }
+                            else if ((dir->lowercase & FATXXFS_CASE_LOWER_BASE)
+                                && (dir->name[b] >= 'A')
+                                && (dir->name[b] <= 'Z')) {
+                                    name_ptr[a++] = dir->name[b] + 32;
+                            }
+                            else {
+                                name_ptr[a++] = dir->name[b];
+                            }
+                    }
+                }
+
+                for (b = 0; b < 3; b++) {
+                    if ((dir->ext[b] != 0) && (dir->ext[b] != 0xff) &&
+                        (dir->ext[b] != 0x20)) {
+                            if (b == 0)
+                                name_ptr[a++] = '.';
+                            if ((dir->lowercase & FATXXFS_CASE_LOWER_EXT) &&
+                                (dir->ext[b] >= 'A') && (dir->ext[b] <= 'Z'))
+                                name_ptr[a++] = dir->ext[b] + 32;
+                            else
+                                name_ptr[a++] = dir->ext[b];
+                    }
+                }
+                name_ptr[a] = '\0';
+
+                // make sure that only ASCII is in the short name
+                fatfs_cleanup_ascii(name_ptr);
+            }
+
+            /* file type: FAT only knows DIR and FILE */
+            if ((dir->attrib & FATFS_ATTR_DIRECTORY) ==
+                FATFS_ATTR_DIRECTORY)
+                fs_name->type = TSK_FS_NAME_TYPE_DIR;
+            else
+                fs_name->type = TSK_FS_NAME_TYPE_REG;
+
+            /* set the inode */
+            fs_name->meta_addr = inode;
+            inode = 0;  // so that we don't use it anymore -- use only fs_name->meta_addr
+
+            /* Handle the . and .. entries specially
+            * The current inode 'address' they have is for the current
+            * slot in the cluster, but it needs to refer to the original
+            * slot
+            */
+            if (TSK_FS_ISDOT(fs_name->name)
+                    && (fs_name->type == TSK_FS_NAME_TYPE_DIR)
+                    && idx < 2) {
+                if (fs_name->name[1] == '\0') {
+                    /* Current directory - "." */
+                    fs_name->meta_addr =
+                        a_fs_dir->fs_file->meta->addr;
+                }
+                /* for the parent directory, look up in the list that
+                * is maintained in fafs_info */
+                else if (fs_name->name[1] == '.') {
+                    /* Parent directory - ".." */
+                    uint8_t dir_found = 0;
+
+                    if (fatfs_dir_buf_get(fatfs, a_fs_dir->fs_file->meta->addr, &(fs_name->meta_addr)) == 0)  {
+                        dir_found = 1;
+                    }
+
+                    if ((dir_found == 0)
+                        && (addrs[0] == fatfs->firstdatasect)) {
+                            /* if we are currently in the root directory, we aren't going to find
+                            * a parent.  This shouldn't happen, but could result in an infinite loop. */
+                            fs_name->meta_addr = 0;
+                            dir_found = 1;
+                    }
+                    if (dir_found == 0) {
+                        if (tsk_verbose)
+                            fprintf(stderr,
+                            "fatfs_dent_parse_buf: Walking directory to find parent\n");
+
+                        /* The parent directory is not in the list.  We are going to walk
+                        * the directory until we hit this directory. This process will
+                        * populate the buffer table and we will then rescan it */
+                        if (tsk_fs_dir_walk(fs, fs->root_inum,
+                            (TSK_FS_DIR_WALK_FLAG_ENUM)(TSK_FS_DIR_WALK_FLAG_ALLOC |
+                            TSK_FS_DIR_WALK_FLAG_UNALLOC |
+                            TSK_FS_DIR_WALK_FLAG_RECURSE),
+                            fatfs_find_parent_act,
+                            (void *) &a_fs_dir->fs_file->meta->addr)) {
+                                return TSK_OK;
+                        }
+
+                        if (tsk_verbose)
+                            fprintf(stderr,
+                            "fatfs_dent_parse_buf: Finished walking directory to find parent\n");
+
+                        if (fatfs_dir_buf_get(fatfs, a_fs_dir->fs_file->meta->addr, &(fs_name->meta_addr)) == 0) {
+                            dir_found = 1;
+                        }
+
+                        // if we did not find it, then it was probably
+                        // from the orphan directory...
+                        if (dir_found == 0)
+                            fs_name->meta_addr = TSK_FS_ORPHANDIR_INUM(fs);
+                    }
+                }
+            }
+            else {
+                /* Save the (non-. or ..) directory to parent directory info to local
+                * structures so that we can later fill into the inode
+                * info for '..' entries */
+                if (fs_name->type == TSK_FS_NAME_TYPE_DIR) {
+                    if (fatfs_dir_buf_add(fatfs,
+                        a_fs_dir->fs_file->meta->addr, fs_name->meta_addr))
+                        return TSK_ERR;
+                }
+            }
+
+
+            /* The allocation status of an entry is based on the allocation
+            * status of the sector it is in and the flag.  Deleted directories
+            * do not always clear the flags of each entry
+            */
+            if (sectalloc == 1) {
+				if(FATXXFS_IS_DELETED(dep->name, fatfs)){
+						fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;
+				}
+				else{
+					fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
+				}
+            }
+            else {
+                fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;
+            }
+
+            tsk_fs_dir_add(a_fs_dir, fs_name);
+        }
+    }
+    tsk_fs_name_free(fs_name);
+
+    return TSK_OK;
 }
\ No newline at end of file
diff --git a/tsk/fs/fatxxfs_meta.c b/tsk/fs/fatxxfs_meta.c
index 92b0cec3d..cefd5fcf0 100755
--- a/tsk/fs/fatxxfs_meta.c
+++ b/tsk/fs/fatxxfs_meta.c
@@ -1,858 +1,858 @@
-/*
-** fatxxfs
-** The Sleuth Kit 
-**
-** Content and meta data layer support for the FATXX file system 
-**
-** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2013 Brian Carrier, Basis Technology. All Rights reserved
-** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
-**
-** TASK
-** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
-**
-**
-** This software is distributed under the Common Public License 1.0
-**
-** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05)
-**
-*/
-
-/**
- * \file fatxxfs.c
- * Contains the internal TSK FATXX (FAT12, FAT16, FAT32) file system code to 
- * handle basic file system processing for opening file system, processing 
- * sectors, and directory entries. 
- */
-
-#include "tsk_fatxxfs.h"
-#include <assert.h>
-
-/*
- * Identify if the dentry is a valid 8.3 name
- *
- * returns 1 if it is, 0 if it does not
- */
-static uint8_t
-is_83_name(FATXXFS_DENTRY * de)
-{
-    if (!de)
-        return 0;
-
-    /* The IS_NAME macro will fail if the value is 0x05, which is only
-     * valid in name[0], similarly with '.' */
-    if ((de->name[0] != FATXXFS_SLOT_E5) && (de->name[0] != '.') &&
-        (FATXXFS_IS_83_NAME(de->name[0]) == 0)) {
-        if (tsk_verbose)
-            fprintf(stderr, "fatfs_is_83_name: name[0] is invalid\n");
-        return 0;
-    }
-
-    // the name cannot start with 0x20
-    else if (de->name[0] == 0x20) {
-        if (tsk_verbose)
-            fprintf(stderr, "fatfs_is_83_name: name[0] has 0x20\n");
-        return 0;
-    }
-
-    /* the second name field can only be . if the first one is a . */
-    if (de->name[1] == '.') {
-        if (de->name[0] != '.') {
-            if (tsk_verbose)
-                fprintf(stderr, "fatfs_is_83_name: name[1] is .\n");
-            return 0;
-        }
-    }
-    else if (FATXXFS_IS_83_NAME(de->name[1]) == 0) {
-        if (tsk_verbose)
-            fprintf(stderr, "fatfs_is_83_name: name[1] is invalid\n");
-        return 0;
-    }
-
-    if (FATXXFS_IS_83_NAME(de->name[2]) == 0) {
-        if (tsk_verbose)
-            fprintf(stderr, "fatfs_is_83_name: name[2] is invalid\n");
-        return 0;
-    }
-    else if (FATXXFS_IS_83_NAME(de->name[3]) == 0) {
-        if (tsk_verbose)
-            fprintf(stderr, "fatfs_is_83_name: name[3] is invalid\n");
-        return 0;
-    }
-    else if (FATXXFS_IS_83_NAME(de->name[4]) == 0) {
-        if (tsk_verbose)
-            fprintf(stderr, "fatfs_is_83_name: name[4] is invalid\n");
-        return 0;
-    }
-    else if (FATXXFS_IS_83_NAME(de->name[5]) == 0) {
-        if (tsk_verbose)
-            fprintf(stderr, "fatfs_is_83_name: name[5] is invalid\n");
-        return 0;
-    }
-    else if (FATXXFS_IS_83_NAME(de->name[6]) == 0) {
-        if (tsk_verbose)
-            fprintf(stderr, "fatfs_is_83_name: name[6] is invalid\n");
-        return 0;
-    }
-    else if (FATXXFS_IS_83_NAME(de->name[7]) == 0) {
-        if (tsk_verbose)
-            fprintf(stderr, "fatfs_is_83_name: name[7] is invalid\n");
-        return 0;
-    }
-    else if (FATXXFS_IS_83_NAME(de->ext[0]) == 0) {
-        if (tsk_verbose)
-            fprintf(stderr, "fatfs_is_83_name: ext[0] is invalid\n");
-        return 0;
-    }
-    else if (FATXXFS_IS_83_NAME(de->ext[1]) == 0) {
-        if (tsk_verbose)
-            fprintf(stderr, "fatfs_is_83_name: ext[1] is invalid\n");
-        return 0;
-    }
-    else if (FATXXFS_IS_83_NAME(de->ext[2]) == 0) {
-        if (tsk_verbose)
-            fprintf(stderr, "fatfs_is_83_name: ext[2] is invalid\n");
-        return 0;
-    }
-
-    /* Ensure that if we get a "space", that the rest of the
-     * name is spaces.  This is not in the spec, but is how
-     * windows operates and serves as a good check to remove
-     * false positives.  We do not do this check for the
-     * volume label though. */
-    if ((de->attrib & FATFS_ATTR_VOLUME) != FATFS_ATTR_VOLUME) {
-        if (((de->name[1] == 0x20) && (de->name[2] != 0x20)) ||
-            ((de->name[2] == 0x20) && (de->name[3] != 0x20)) ||
-            ((de->name[3] == 0x20) && (de->name[4] != 0x20)) ||
-            ((de->name[4] == 0x20) && (de->name[5] != 0x20)) ||
-            ((de->name[5] == 0x20) && (de->name[6] != 0x20)) ||
-            ((de->name[6] == 0x20) && (de->name[7] != 0x20)) ||
-            ((de->ext[1] == 0x20) && (de->ext[2] != 0x20))) {
-            if (tsk_verbose)
-                fprintf(stderr,
-                    "fatfs_is_83_name: space before non-space\n");
-            return 0;
-        }
-    }
-
-    return 1;
-}
-
-/**
- * \internal
- * Determine whether a buffer likely contains a directory entry.
- * For the most reliable results, request the in-depth test.
- *
- * @param [in] a_fatfs Source file system for the directory entry.
- * @param [in] a_dentry Buffer that may contain a directory entry.
- * @param [in] a_cluster_is_alloc The allocation status, possibly unknown, of the 
- * cluster from which the buffer was filled. 
- * @param [in] a_do_basic_tests_only Whether to do basic or in-depth testing. 
- * @return 1 if the buffer likely contains a direcotry entry, 0 otherwise
- */
-uint8_t
-fatxxfs_is_dentry(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_cluster_is_alloc, uint8_t a_do_basic_tests_only)
-{
-    const char *func_name = "fatxxfs_is_dentry";
-    TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_fatfs->fs_info;
-    FATXXFS_DENTRY *dentry = (FATXXFS_DENTRY*)a_dentry;
-
-    if (!a_dentry)
-        return 0;
-
-    /* LFN have their own checks, which are pretty weak since most
-     * fields are UTF16 */
-    if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
-        FATXXFS_DENTRY_LFN *de_lfn = (FATXXFS_DENTRY_LFN*) dentry;
-
-        if ((de_lfn->seq > (FATXXFS_LFN_SEQ_FIRST | 0x0f))
-            && (de_lfn->seq != FATXXFS_SLOT_DELETED)) {
-            if (tsk_verbose)
-                fprintf(stderr, "%s: LFN seq\n", func_name);
-            return 0;
-        }
-
-        return 1;
-    }
-    else {
-        // the basic test is only for the 'essential data'.
-        if (a_do_basic_tests_only == 0) {
-            if (dentry->lowercase & ~(FATXXFS_CASE_LOWER_ALL)) {
-                if (tsk_verbose)
-                    fprintf(stderr, "%s: lower case all\n", func_name);
-                return 0;
-            }
-            else if (dentry->attrib & ~(FATFS_ATTR_ALL)) {
-                if (tsk_verbose)
-                    fprintf(stderr, "%s: attribute all\n", func_name);
-                return 0;
-            }
-
-            // verify we do not have too many flags set
-            if (dentry->attrib & FATFS_ATTR_VOLUME) {
-                if ((dentry->attrib & FATFS_ATTR_DIRECTORY) ||
-                    (dentry->attrib & FATFS_ATTR_READONLY) ||
-                    (dentry->attrib & FATFS_ATTR_ARCHIVE)) {
-                    if (tsk_verbose)
-                        fprintf(stderr,
-                            "%s: Vol and Dir/RO/Arch\n", func_name);
-                    return 0;
-                }
-            }
-
-            /* The ctime, cdate, and adate fields are optional and 
-             * therefore 0 is a valid value
-             * We have had scenarios where ISDATE and ISTIME return true,
-             * but the unix2dos fail during the conversion.  This has been
-             * useful to detect corrupt entries, so we do both. 
-             */
-            if ((tsk_getu16(fs->endian, dentry->ctime) != 0) &&
-                (FATFS_ISTIME(tsk_getu16(fs->endian, dentry->ctime)) == 0)) {
-                if (tsk_verbose)
-                    fprintf(stderr, "%s: ctime\n", func_name);
-                return 0;
-            }
-            else if ((tsk_getu16(fs->endian, dentry->wtime) != 0) &&
-                (FATFS_ISTIME(tsk_getu16(fs->endian, dentry->wtime)) == 0)) {
-                if (tsk_verbose)
-                    fprintf(stderr, "%s: wtime\n", func_name);
-                return 0;
-            }
-            else if ((tsk_getu16(fs->endian, dentry->cdate) != 0) &&
-                ((FATFS_ISDATE(tsk_getu16(fs->endian, dentry->cdate)) == 0) ||
-                    (fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->cdate),
-                            tsk_getu16(fs->endian, dentry->ctime),
-                            dentry->ctimeten) == 0))) {
-                if (tsk_verbose)
-                    fprintf(stderr, "%s: cdate\n", func_name);
-                return 0;
-            }
-            else if (dentry->ctimeten > 200) {
-                if (tsk_verbose)
-                    fprintf(stderr, "%s: ctimeten\n", func_name);
-                return 0;
-            }
-            else if ((tsk_getu16(fs->endian, dentry->adate) != 0) &&
-                ((FATFS_ISDATE(tsk_getu16(fs->endian, dentry->adate)) == 0) ||
-                    (fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->adate),
-                            0, 0) == 0))) {
-                if (tsk_verbose)
-                    fprintf(stderr, "%s: adate\n", func_name);
-                return 0;
-            }
-            else if ((tsk_getu16(fs->endian, dentry->wdate) != 0) &&
-                ((FATFS_ISDATE(tsk_getu16(fs->endian, dentry->wdate)) == 0) ||
-                    (fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->wdate),
-                            tsk_getu16(fs->endian, dentry->wtime), 0) == 0))) {
-                if (tsk_verbose)
-                    fprintf(stderr, "%s: wdate\n", func_name);
-                return 0;
-            }
-        }
-
-        /* verify the starting cluster is small enough */
-        if ((FATXXFS_DENTRY_CLUST(fs, dentry) > (a_fatfs->lastclust)) &&
-            (FATFS_ISEOF(FATXXFS_DENTRY_CLUST(fs, dentry), a_fatfs->mask) == 0)) {
-            if (tsk_verbose)
-                fprintf(stderr, "%s: start cluster\n", func_name);
-            return 0;
-        }
-
-        /* Verify the file size is smaller than the data area */
-        else if (tsk_getu32(fs->endian, dentry->size) >
-            ((a_fatfs->clustcnt * a_fatfs->csize) << a_fatfs->ssize_sh)) {
-            if (tsk_verbose)
-                fprintf(stderr, "%s: size\n", func_name);
-            return 0;
-        }
-
-        else if ((tsk_getu32(fs->endian, dentry->size) > 0)
-            && (FATXXFS_DENTRY_CLUST(fs, dentry) == 0)) {
-            if (tsk_verbose)
-                fprintf(stderr,
-                    "%s: non-zero size and NULL starting cluster\n", func_name);
-            return 0;
-        }
-		
-		else if((a_fatfs->subtype == TSK_FATFS_SUBTYPE_SPEC) && (is_83_name(dentry) == 0))
-			return 0;
-
-        // basic sanity check on values
-        else if ((tsk_getu16(fs->endian, dentry->ctime) == 0)
-            && (tsk_getu16(fs->endian, dentry->wtime) == 0)
-            && (tsk_getu16(fs->endian, dentry->cdate) == 0)
-            && (tsk_getu16(fs->endian, dentry->adate) == 0)
-            && (tsk_getu16(fs->endian, dentry->wdate) == 0)
-            && (FATXXFS_DENTRY_CLUST(fs, dentry) == 0)
-            && (tsk_getu32(fs->endian, dentry->size) == 0)) {
-            if (tsk_verbose)
-                fprintf(stderr,
-                    "s: nearly all values zero\n", func_name);
-            return 0;
-        }
-
-        return 1;
-    }
-}
-
-/*
- * convert the attribute list in FAT to a UNIX mode
- */
-static TSK_FS_META_TYPE_ENUM
-attr2type(uint16_t attr)
-{
-    if (attr & FATFS_ATTR_DIRECTORY)
-        return TSK_FS_META_TYPE_DIR;
-    else
-        return TSK_FS_META_TYPE_REG;
-}
-
-static int
-attr2mode(uint16_t attr)
-{
-    int mode;
-
-    /* every file is executable */
-    mode =
-        (TSK_FS_META_MODE_IXUSR | TSK_FS_META_MODE_IXGRP |
-        TSK_FS_META_MODE_IXOTH);
-
-    if ((attr & FATFS_ATTR_READONLY) == 0)
-        mode |=
-            (TSK_FS_META_MODE_IRUSR | TSK_FS_META_MODE_IRGRP |
-            TSK_FS_META_MODE_IROTH);
-
-    if ((attr & FATFS_ATTR_HIDDEN) == 0)
-        mode |=
-            (TSK_FS_META_MODE_IWUSR | TSK_FS_META_MODE_IWGRP |
-            TSK_FS_META_MODE_IWOTH);
-
-    return mode;
-}
-
-TSK_RETVAL_ENUM
-fatxxfs_dinode_copy(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
-    FATFS_DENTRY *a_dentry, uint8_t a_cluster_is_alloc, TSK_FS_FILE *a_fs_file)
-{
-    const char *func_name = "fatxxfs_dinode_copy";
-    int i;
-    TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_fatfs->fs_info;
-    TSK_FS_META *fs_meta = a_fs_file->meta;
-    FATXXFS_DENTRY *dentry = (FATXXFS_DENTRY*)a_dentry;
-    TSK_DADDR_T *addr_ptr;
-    uint32_t flags = 0;
-
-    if (fs_meta->content_len < FATFS_FILE_CONTENT_LEN) {
-        if ((fs_meta =
-                tsk_fs_meta_realloc(fs_meta,
-                    FATFS_FILE_CONTENT_LEN)) == NULL) {
-            return TSK_ERR;
-        }
-    }
-
-    fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
-    if (fs_meta->attr) {
-        tsk_fs_attrlist_markunused(fs_meta->attr);
-    }
-
-    fs_meta->mode = (TSK_FS_META_MODE_ENUM)attr2mode(dentry->attrib);
-    fs_meta->type = attr2type(dentry->attrib);
-
-    fs_meta->addr = a_inum;
-
-    if (a_cluster_is_alloc) {
-
-		if(FATXXFS_IS_DELETED(dentry->name, a_fatfs)){
-				flags = TSK_FS_META_FLAG_UNALLOC;
-		}
-		else{
-			flags = TSK_FS_META_FLAG_ALLOC;
-		}
-    }
-    else {
-        flags = TSK_FS_META_FLAG_UNALLOC;
-    }
-    fs_meta->flags = (TSK_FS_META_FLAG_ENUM)flags;
-
-    if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
-        /* LFN entries don't have these values */
-        fs_meta->nlink = 0;
-        fs_meta->size = 0;
-        fs_meta->mtime = 0;
-        fs_meta->atime = 0;
-        fs_meta->ctime = 0;
-        fs_meta->crtime = 0;
-        fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano =
-            fs_meta->crtime_nano = 0;
-    }
-    else {
-        /* There is no notion of link in FAT, just deleted or not */
-		if(FATXXFS_IS_DELETED(dentry->name, a_fatfs)){
-			fs_meta->nlink = 0;
-		}
-		else{
-			fs_meta->nlink = 1;
-		}
-        fs_meta->size = (TSK_OFF_T) tsk_getu32(fs->endian, dentry->size);
-
-        /* If these are valid dates, then convert to a unix date format */
-        if (FATFS_ISDATE(tsk_getu16(fs->endian, dentry->wdate)))
-            fs_meta->mtime =
-                fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->wdate),
-                tsk_getu16(fs->endian, dentry->wtime), 0);
-        else
-            fs_meta->mtime = 0;
-        fs_meta->mtime_nano = 0;
-
-        if (FATFS_ISDATE(tsk_getu16(fs->endian, dentry->adate)))
-            fs_meta->atime =
-                fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->adate), 0, 0);
-        else
-            fs_meta->atime = 0;
-        fs_meta->atime_nano = 0;
-
-
-        /* cdate is the creation date in FAT and there is no change,
-         * so we just put in into change and set create to 0.  The other
-         * front-end code knows how to handle it and display it
-         */
-        if (FATFS_ISDATE(tsk_getu16(fs->endian, dentry->cdate))) {
-            fs_meta->crtime =
-                fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->cdate),
-                tsk_getu16(fs->endian, dentry->ctime), dentry->ctimeten);
-            fs_meta->crtime_nano = fatfs_dos_2_nanosec(dentry->ctimeten);
-        }
-        else {
-            fs_meta->crtime = 0;
-            fs_meta->crtime_nano = 0;
-        }
-
-        // FAT does not have a changed time
-        fs_meta->ctime = 0;
-        fs_meta->ctime_nano = 0;
-    }
-
-    /* Values that do not exist in FAT */
-    fs_meta->uid = 0;
-    fs_meta->gid = 0;
-    fs_meta->seq = 0;
-
-
-    /* We will be copying a name, so allocate a structure */
-    if (fs_meta->name2 == NULL) {
-        if ((fs_meta->name2 = (TSK_FS_META_NAME_LIST *)
-                tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL)
-            return TSK_ERR;
-        fs_meta->name2->next = NULL;
-    }
-
-    /* If we have a LFN entry, then we need to convert the three
-     * parts of the name to UTF-8 and copy it into the name structure .
-     */
-    if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
-        FATXXFS_DENTRY_LFN *lfn = (FATXXFS_DENTRY_LFN *) dentry;
-
-        /* Convert the first part of the name */
-        UTF8 *name8 = (UTF8 *) fs_meta->name2->name;
-        UTF16 *name16 = (UTF16 *) lfn->part1;
-
-        int retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
-            (UTF16 *) & lfn->part1[10],
-            &name8,
-            (UTF8 *) ((uintptr_t) fs_meta->name2->name +
-                sizeof(fs_meta->name2->name)),
-            TSKlenientConversion);
-
-        if (retVal != TSKconversionOK) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_UNICODE);
-            tsk_error_set_errstr
-                ("%s: Error converting FAT LFN (1) to UTF8: %d",
-                func_name, retVal);
-            *name8 = '\0';
-
-            return TSK_COR;
-        }
-
-        /* Convert the second part of the name */
-        name16 = (UTF16 *) lfn->part2;
-        retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
-            (UTF16 *) & lfn->part2[12],
-            &name8,
-            (UTF8 *) ((uintptr_t) fs_meta->name2->name +
-                sizeof(fs_meta->name2->name)), TSKlenientConversion);
-
-        if (retVal != TSKconversionOK) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_UNICODE);
-            tsk_error_set_errstr
-                ("%s: Error converting FAT LFN (2) to UTF8: %d",
-                func_name, retVal);
-            *name8 = '\0';
-
-            return TSK_COR;
-        }
-
-        /* Convert the third part of the name */
-        name16 = (UTF16 *) lfn->part3;
-        retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
-            (UTF16 *) & lfn->part3[4],
-            &name8,
-            (UTF8 *) ((uintptr_t) fs_meta->name2->name +
-                sizeof(fs_meta->name2->name)), TSKlenientConversion);
-
-        if (retVal != TSKconversionOK) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_FS_UNICODE);
-            tsk_error_set_errstr
-                ("%s: Error converting FAT LFN (3) to UTF8: %d",
-                func_name, retVal);
-            *name8 = '\0';
-
-            return TSK_COR;
-        }
-
-        /* Make sure it is NULL Terminated */
-        if ((uintptr_t) name8 >
-            (uintptr_t) fs_meta->name2->name +
-            sizeof(fs_meta->name2->name))
-            fs_meta->name2->name[sizeof(fs_meta->name2->name) - 1] = '\0';
-        else
-            *name8 = '\0';
-    }
-    /* If the entry is for a volume label, then copy the label.
-     */
-    else if ((dentry->attrib & FATFS_ATTR_VOLUME) == FATFS_ATTR_VOLUME) {
-        int a;
-
-        i = 0;
-        for (a = 0; a < 8; a++) {
-            if ((dentry->name[a] != 0x00) && (dentry->name[a] != 0xff))
-                fs_meta->name2->name[i++] = dentry->name[a];
-        }
-        for (a = 0; a < 3; a++) {
-            if ((dentry->ext[a] != 0x00) && (dentry->ext[a] != 0xff))
-                fs_meta->name2->name[i++] = dentry->ext[a];
-        }
-        fs_meta->name2->name[i] = '\0';
-
-        /* clean up non-ASCII because we are
-         * copying it into a buffer that is supposed to be UTF-8 and
-         * we don't know what encoding it is actually in or if it is 
-         * simply junk. */
-        fatfs_cleanup_ascii(fs_meta->name2->name);
-    }
-    /* If the entry is a normal short entry, then copy the name
-     * and add the '.' for the extension
-     */
-    else {
-        for (i = 0; (i < 8) && (dentry->name[i] != 0) && (dentry->name[i] != ' ');
-            i++) {
-            if ((i == 0) && (dentry->name[0] == FATXXFS_SLOT_DELETED))
-                fs_meta->name2->name[0] = '_';
-            else if ((dentry->lowercase & FATXXFS_CASE_LOWER_BASE) &&
-                (dentry->name[i] >= 'A') && (dentry->name[i] <= 'Z'))
-                fs_meta->name2->name[i] = dentry->name[i] + 32;
-            else
-                fs_meta->name2->name[i] = dentry->name[i];
-        }
-
-        if ((dentry->ext[0]) && (dentry->ext[0] != ' ')) {
-            int a;
-            fs_meta->name2->name[i++] = '.';
-            for (a = 0;
-                (a < 3) && (dentry->ext[a] != 0) && (dentry->ext[a] != ' ');
-                a++, i++) {
-                if ((dentry->lowercase & FATXXFS_CASE_LOWER_EXT)
-                    && (dentry->ext[a] >= 'A') && (dentry->ext[a] <= 'Z'))
-                    fs_meta->name2->name[i] = dentry->ext[a] + 32;
-                else
-                    fs_meta->name2->name[i] = dentry->ext[a];
-            }
-        }
-        fs_meta->name2->name[i] = '\0';
-
-        /* clean up non-ASCII because we are
-         * copying it into a buffer that is supposed to be UTF-8 and
-         * we don't know what encoding it is actually in or if it is 
-         * simply junk. */
-        fatfs_cleanup_ascii(fs_meta->name2->name);
-    }
-
-    /* Clean up name to remove control characters */
-    i = 0;
-    while (fs_meta->name2->name[i] != '\0') {
-        if (TSK_IS_CNTRL(fs_meta->name2->name[i]))
-            fs_meta->name2->name[i] = '^';
-        i++;
-    }
-
-    /* get the starting cluster */
-    addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr;
-    if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
-        addr_ptr[0] = 0;
-    }
-    else {
-        addr_ptr[0] = FATXXFS_DENTRY_CLUST(fs, dentry) & a_fatfs->mask;
-    }
-
-    /* FAT does not store a size for its directories so make one based
-     * on the number of allocated sectors
-     */
-    if ((dentry->attrib & FATFS_ATTR_DIRECTORY) &&
-        ((dentry->attrib & FATFS_ATTR_LFN) != FATFS_ATTR_LFN)) {
-        if (fs_meta->flags & TSK_FS_META_FLAG_ALLOC) {
-            TSK_LIST *list_seen = NULL;
-
-            /* count the total number of clusters in this file */
-            TSK_DADDR_T clust = FATXXFS_DENTRY_CLUST(fs, dentry);
-            int cnum = 0;
-
-            while ((clust) && (0 == FATFS_ISEOF(clust, a_fatfs->mask))) {
-                TSK_DADDR_T nxt;
-
-                /* Make sure we do not get into an infinite loop */
-                if (tsk_list_find(list_seen, clust)) {
-                    if (tsk_verbose)
-                        tsk_fprintf(stderr,
-                            "Loop found while determining directory size\n");
-                    break;
-                }
-                if (tsk_list_add(&list_seen, clust)) {
-                    tsk_list_free(list_seen);
-                    list_seen = NULL;
-                    return TSK_ERR;
-                }
-
-                cnum++;
-
-                if (fatfs_getFAT(a_fatfs, clust, &nxt))
-                    break;
-                else
-                    clust = nxt;
-            }
-
-            tsk_list_free(list_seen);
-            list_seen = NULL;
-
-            fs_meta->size =
-                (TSK_OFF_T) ((cnum * a_fatfs->csize) << a_fatfs->ssize_sh);
-        }
-        /* if the dir is unallocated, then assume 0 or cluster size
-         * Ideally, we would have a smart algo here to do recovery
-         * and look for dentries.  However, we do not have that right
-         * now and if we do not add this special check then it can
-         * assume that an allocated file cluster chain belongs to the
-         * directory */
-        else {
-            // if the first cluster is allocated, then set size to be 0
-            if (fatxxfs_is_cluster_alloc(a_fatfs, FATXXFS_DENTRY_CLUST(fs,
-                        dentry)) == 1)
-                fs_meta->size = 0;
-            else
-                fs_meta->size = a_fatfs->csize << a_fatfs->ssize_sh;
-        }
-    }
-
-    return TSK_OK;
-}
-
-/**
- * \internal
- * Populate the TSK_FS_META object of a TSK_FS_FILE object for a 
- * given inode address.
- *
- * @param [in] a_fs File system that contains the inode.
- * @param [out] a_fs_file The file corresponding to the inode.
- * @param [in] a_inum The inode address.
- * @returns 1 if an error occurs or if the inode address is not
- * for a valid inode, 0 otherwise.
- */
-uint8_t
-fatxxfs_inode_lookup(FATFS_INFO *a_fatfs, TSK_FS_FILE *a_fs_file,
-    TSK_INUM_T a_inum)
-{
-    const char *func_name = "fatxxfs_inode_lookup";
-    TSK_DADDR_T sector = 0;
-    int8_t ret_val = 0;
-    FATFS_DATA_UNIT_ALLOC_STATUS_ENUM sector_alloc_status = FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN;
-    FATFS_DENTRY dentry;
-    TSK_RETVAL_ENUM copy_result = TSK_OK;
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
-        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name) ||
-        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
-        return 1;
-    }
-
-    sector = FATFS_INODE_2_SECT(a_fatfs, a_inum);
-    if (sector > a_fatfs->fs_info.last_block) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
-        tsk_error_set_errstr("%s: Inode %" PRIuINUM
-            " in sector too big for image: %" PRIuDADDR, func_name, a_inum, sector);
-        return 1;
-    }
-
-    if (fatfs_dentry_load(a_fatfs, &dentry, a_inum) != 0) {
-        return 1;
-    }
-
-    ret_val = fatfs_is_sectalloc(a_fatfs, sector);
-    if (ret_val == -1) {
-        return 1;
-    }
-    else {
-        sector_alloc_status = (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)ret_val;
-    }
-
-    /* Note that only the sector allocation status is used to choose
-     * between the basic or in-depth version of the inode validity 
-     * test. In other places in the code information about whether or not 
-     * the sector that contains the inode is part of a folder is used to 
-     * make this decision. Here, that information is not available. Thus, 
-     * the test here is less reliable and may result in some false 
-     * positives. */
-    if (!fatxxfs_is_dentry(a_fatfs, &dentry, sector_alloc_status, sector_alloc_status)) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
-        tsk_error_set_errstr("%s: %" PRIuINUM
-            " is not an inode", func_name, a_inum);
-        return 1;
-    }
-
-    copy_result = fatxxfs_dinode_copy(a_fatfs, a_inum, &dentry, (uint8_t)sector_alloc_status, a_fs_file);
-    if (copy_result == TSK_OK) {
-        return 0;
-    }
-    else if (copy_result == TSK_COR) {
-        /* If there was a Unicode conversion error,
-         * then still return the inode. */
-        if (tsk_verbose) {
-            tsk_error_print(stderr);
-        }
-        tsk_error_reset();
-        return 0;
-    }
-    else {
-        return 1;
-    }
-}
-
-/**
- * Output the file attributes of an exFAT file directory entry in 
- * human-readable form.
- *
- * @param a_fatfs Source file system for the directory entry.
- * @param a_inum Inode address associated with the directory entry.
- * @param a_hFile Handle of the file to which to write.
- * @return 0 on success, 1 on failure, per TSK convention
- */
-uint8_t
-fatxxfs_istat_attr_flags(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, FILE *a_hFile)
-{
-    const char *func_name = "fatxxfs_istat_attr_flags";
-    FATXXFS_DENTRY dentry;
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
-        fatfs_ptr_arg_is_null(a_hFile, "a_hFile", func_name) ||
-        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
-        return 1; 
-    }
-
-    if (fatfs_dentry_load(a_fatfs, (FATFS_DENTRY*)(&dentry), a_inum)) {
-        return 1; 
-    }
-
-    if ((dentry.attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
-        tsk_fprintf(a_hFile, "Long File Name\n");
-    }
-    else {
-        if (dentry.attrib & FATFS_ATTR_DIRECTORY)
-            tsk_fprintf(a_hFile, "Directory");
-        else if (dentry.attrib & FATFS_ATTR_VOLUME)
-            tsk_fprintf(a_hFile, "Volume Label");
-        else
-            tsk_fprintf(a_hFile, "File");
-
-        if (dentry.attrib & FATFS_ATTR_READONLY)
-            tsk_fprintf(a_hFile, ", Read Only");
-        if (dentry.attrib & FATFS_ATTR_HIDDEN)
-            tsk_fprintf(a_hFile, ", Hidden");
-        if (dentry.attrib & FATFS_ATTR_SYSTEM)
-            tsk_fprintf(a_hFile, ", System");
-        if (dentry.attrib & FATFS_ATTR_ARCHIVE)
-            tsk_fprintf(a_hFile, ", Archive");
-
-        tsk_fprintf(a_hFile, "\n");
-    }
-
-    return 0;
-}
-
-uint8_t
-fatxxfs_inode_walk_should_skip_dentry(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
-    FATFS_DENTRY *a_dentry, unsigned int a_selection_flags, 
-    int a_cluster_is_alloc)
-{
-    const char *func_name = "fatxxfs_inode_walk_should_skip_dentry";
-    FATXXFS_DENTRY *dentry = (FATXXFS_DENTRY*)a_dentry;
-    unsigned int dentry_flags = 0;
-
-    assert(a_fatfs != NULL);
-    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
-    assert(a_dentry != NULL);
-
-    tsk_error_reset();
-    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
-        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name) ||
-        fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
-        return 1; 
-    }
-
-    /* If this is a long file name entry, then skip it and
-     * wait for the short name. */
-    if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
-        return 1;
-    }
-
-    /* Skip the "." and ".." entries because they are redundant. */
-    if (((dentry->attrib & FATFS_ATTR_DIRECTORY) == FATFS_ATTR_DIRECTORY) &&
-         (dentry->name[0] == '.')) {
-        return 1;
-    }
-
-    /* Compare directory entry allocation status with the inode selection
-     * flags. Allocation status is determined first by the allocation status 
-     * of the sector that contains the entry, then by the deleted status of 
-     * the file. This is necessary because when a directory is deleted, its 
-     * contents are not always marked as unallocated. */
-    if (a_cluster_is_alloc == 1) {
-		if(FATXXFS_IS_DELETED(dentry->name, a_fatfs)){
-			dentry_flags = TSK_FS_META_FLAG_UNALLOC;
-		}
-		else{
-			dentry_flags = TSK_FS_META_FLAG_ALLOC;
-		}
-    }
-    else {
-        dentry_flags = TSK_FS_META_FLAG_UNALLOC;
-    }
-
-    if ((a_selection_flags & dentry_flags) != dentry_flags) {
-        return 1;
-    }
-
-    /* If the processing flags call for only processing orphan files, check 
-     * whether or not this inode is in list of non-orphan files found via name
-     * walk. */
-    if ((dentry_flags & TSK_FS_META_FLAG_UNALLOC) &&
-        (a_selection_flags & TSK_FS_META_FLAG_ORPHAN) &&
-        (tsk_fs_dir_find_inum_named(&(a_fatfs->fs_info), a_inum))) {
-        return 1;
-    }
-
-    return 0;
+/*
+** fatxxfs
+** The Sleuth Kit 
+**
+** Content and meta data layer support for the FATXX file system 
+**
+** Brian Carrier [carrier <at> sleuthkit [dot] org]
+** Copyright (c) 2006-2013 Brian Carrier, Basis Technology. All Rights reserved
+** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
+**
+** TASK
+** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
+**
+**
+** This software is distributed under the Common Public License 1.0
+**
+** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05)
+**
+*/
+
+/**
+ * \file fatxxfs.c
+ * Contains the internal TSK FATXX (FAT12, FAT16, FAT32) file system code to 
+ * handle basic file system processing for opening file system, processing 
+ * sectors, and directory entries. 
+ */
+
+#include "tsk_fatxxfs.h"
+#include <assert.h>
+
+/*
+ * Identify if the dentry is a valid 8.3 name
+ *
+ * returns 1 if it is, 0 if it does not
+ */
+static uint8_t
+is_83_name(FATXXFS_DENTRY * de)
+{
+    if (!de)
+        return 0;
+
+    /* The IS_NAME macro will fail if the value is 0x05, which is only
+     * valid in name[0], similarly with '.' */
+    if ((de->name[0] != FATXXFS_SLOT_E5) && (de->name[0] != '.') &&
+        (FATXXFS_IS_83_NAME(de->name[0]) == 0)) {
+        if (tsk_verbose)
+            fprintf(stderr, "fatfs_is_83_name: name[0] is invalid\n");
+        return 0;
+    }
+
+    // the name cannot start with 0x20
+    else if (de->name[0] == 0x20) {
+        if (tsk_verbose)
+            fprintf(stderr, "fatfs_is_83_name: name[0] has 0x20\n");
+        return 0;
+    }
+
+    /* the second name field can only be . if the first one is a . */
+    if (de->name[1] == '.') {
+        if (de->name[0] != '.') {
+            if (tsk_verbose)
+                fprintf(stderr, "fatfs_is_83_name: name[1] is .\n");
+            return 0;
+        }
+    }
+    else if (FATXXFS_IS_83_NAME(de->name[1]) == 0) {
+        if (tsk_verbose)
+            fprintf(stderr, "fatfs_is_83_name: name[1] is invalid\n");
+        return 0;
+    }
+
+    if (FATXXFS_IS_83_NAME(de->name[2]) == 0) {
+        if (tsk_verbose)
+            fprintf(stderr, "fatfs_is_83_name: name[2] is invalid\n");
+        return 0;
+    }
+    else if (FATXXFS_IS_83_NAME(de->name[3]) == 0) {
+        if (tsk_verbose)
+            fprintf(stderr, "fatfs_is_83_name: name[3] is invalid\n");
+        return 0;
+    }
+    else if (FATXXFS_IS_83_NAME(de->name[4]) == 0) {
+        if (tsk_verbose)
+            fprintf(stderr, "fatfs_is_83_name: name[4] is invalid\n");
+        return 0;
+    }
+    else if (FATXXFS_IS_83_NAME(de->name[5]) == 0) {
+        if (tsk_verbose)
+            fprintf(stderr, "fatfs_is_83_name: name[5] is invalid\n");
+        return 0;
+    }
+    else if (FATXXFS_IS_83_NAME(de->name[6]) == 0) {
+        if (tsk_verbose)
+            fprintf(stderr, "fatfs_is_83_name: name[6] is invalid\n");
+        return 0;
+    }
+    else if (FATXXFS_IS_83_NAME(de->name[7]) == 0) {
+        if (tsk_verbose)
+            fprintf(stderr, "fatfs_is_83_name: name[7] is invalid\n");
+        return 0;
+    }
+    else if (FATXXFS_IS_83_NAME(de->ext[0]) == 0) {
+        if (tsk_verbose)
+            fprintf(stderr, "fatfs_is_83_name: ext[0] is invalid\n");
+        return 0;
+    }
+    else if (FATXXFS_IS_83_NAME(de->ext[1]) == 0) {
+        if (tsk_verbose)
+            fprintf(stderr, "fatfs_is_83_name: ext[1] is invalid\n");
+        return 0;
+    }
+    else if (FATXXFS_IS_83_NAME(de->ext[2]) == 0) {
+        if (tsk_verbose)
+            fprintf(stderr, "fatfs_is_83_name: ext[2] is invalid\n");
+        return 0;
+    }
+
+    /* Ensure that if we get a "space", that the rest of the
+     * name is spaces.  This is not in the spec, but is how
+     * windows operates and serves as a good check to remove
+     * false positives.  We do not do this check for the
+     * volume label though. */
+    if ((de->attrib & FATFS_ATTR_VOLUME) != FATFS_ATTR_VOLUME) {
+        if (((de->name[1] == 0x20) && (de->name[2] != 0x20)) ||
+            ((de->name[2] == 0x20) && (de->name[3] != 0x20)) ||
+            ((de->name[3] == 0x20) && (de->name[4] != 0x20)) ||
+            ((de->name[4] == 0x20) && (de->name[5] != 0x20)) ||
+            ((de->name[5] == 0x20) && (de->name[6] != 0x20)) ||
+            ((de->name[6] == 0x20) && (de->name[7] != 0x20)) ||
+            ((de->ext[1] == 0x20) && (de->ext[2] != 0x20))) {
+            if (tsk_verbose)
+                fprintf(stderr,
+                    "fatfs_is_83_name: space before non-space\n");
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * \internal
+ * Determine whether a buffer likely contains a directory entry.
+ * For the most reliable results, request the in-depth test.
+ *
+ * @param [in] a_fatfs Source file system for the directory entry.
+ * @param [in] a_dentry Buffer that may contain a directory entry.
+ * @param [in] a_cluster_is_alloc The allocation status, possibly unknown, of the 
+ * cluster from which the buffer was filled. 
+ * @param [in] a_do_basic_tests_only Whether to do basic or in-depth testing. 
+ * @return 1 if the buffer likely contains a direcotry entry, 0 otherwise
+ */
+uint8_t
+fatxxfs_is_dentry(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_cluster_is_alloc, uint8_t a_do_basic_tests_only)
+{
+    const char *func_name = "fatxxfs_is_dentry";
+    TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_fatfs->fs_info;
+    FATXXFS_DENTRY *dentry = (FATXXFS_DENTRY*)a_dentry;
+
+    if (!a_dentry)
+        return 0;
+
+    /* LFN have their own checks, which are pretty weak since most
+     * fields are UTF16 */
+    if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
+        FATXXFS_DENTRY_LFN *de_lfn = (FATXXFS_DENTRY_LFN*) dentry;
+
+        if ((de_lfn->seq > (FATXXFS_LFN_SEQ_FIRST | 0x0f))
+            && (de_lfn->seq != FATXXFS_SLOT_DELETED)) {
+            if (tsk_verbose)
+                fprintf(stderr, "%s: LFN seq\n", func_name);
+            return 0;
+        }
+
+        return 1;
+    }
+    else {
+        // the basic test is only for the 'essential data'.
+        if (a_do_basic_tests_only == 0) {
+            if (dentry->lowercase & ~(FATXXFS_CASE_LOWER_ALL)) {
+                if (tsk_verbose)
+                    fprintf(stderr, "%s: lower case all\n", func_name);
+                return 0;
+            }
+            else if (dentry->attrib & ~(FATFS_ATTR_ALL)) {
+                if (tsk_verbose)
+                    fprintf(stderr, "%s: attribute all\n", func_name);
+                return 0;
+            }
+
+            // verify we do not have too many flags set
+            if (dentry->attrib & FATFS_ATTR_VOLUME) {
+                if ((dentry->attrib & FATFS_ATTR_DIRECTORY) ||
+                    (dentry->attrib & FATFS_ATTR_READONLY) ||
+                    (dentry->attrib & FATFS_ATTR_ARCHIVE)) {
+                    if (tsk_verbose)
+                        fprintf(stderr,
+                            "%s: Vol and Dir/RO/Arch\n", func_name);
+                    return 0;
+                }
+            }
+
+            /* The ctime, cdate, and adate fields are optional and 
+             * therefore 0 is a valid value
+             * We have had scenarios where ISDATE and ISTIME return true,
+             * but the unix2dos fail during the conversion.  This has been
+             * useful to detect corrupt entries, so we do both. 
+             */
+            if ((tsk_getu16(fs->endian, dentry->ctime) != 0) &&
+                (FATFS_ISTIME(tsk_getu16(fs->endian, dentry->ctime)) == 0)) {
+                if (tsk_verbose)
+                    fprintf(stderr, "%s: ctime\n", func_name);
+                return 0;
+            }
+            else if ((tsk_getu16(fs->endian, dentry->wtime) != 0) &&
+                (FATFS_ISTIME(tsk_getu16(fs->endian, dentry->wtime)) == 0)) {
+                if (tsk_verbose)
+                    fprintf(stderr, "%s: wtime\n", func_name);
+                return 0;
+            }
+            else if ((tsk_getu16(fs->endian, dentry->cdate) != 0) &&
+                ((FATFS_ISDATE(tsk_getu16(fs->endian, dentry->cdate)) == 0) ||
+                    (fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->cdate),
+                            tsk_getu16(fs->endian, dentry->ctime),
+                            dentry->ctimeten) == 0))) {
+                if (tsk_verbose)
+                    fprintf(stderr, "%s: cdate\n", func_name);
+                return 0;
+            }
+            else if (dentry->ctimeten > 200) {
+                if (tsk_verbose)
+                    fprintf(stderr, "%s: ctimeten\n", func_name);
+                return 0;
+            }
+            else if ((tsk_getu16(fs->endian, dentry->adate) != 0) &&
+                ((FATFS_ISDATE(tsk_getu16(fs->endian, dentry->adate)) == 0) ||
+                    (fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->adate),
+                            0, 0) == 0))) {
+                if (tsk_verbose)
+                    fprintf(stderr, "%s: adate\n", func_name);
+                return 0;
+            }
+            else if ((tsk_getu16(fs->endian, dentry->wdate) != 0) &&
+                ((FATFS_ISDATE(tsk_getu16(fs->endian, dentry->wdate)) == 0) ||
+                    (fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->wdate),
+                            tsk_getu16(fs->endian, dentry->wtime), 0) == 0))) {
+                if (tsk_verbose)
+                    fprintf(stderr, "%s: wdate\n", func_name);
+                return 0;
+            }
+        }
+
+        /* verify the starting cluster is small enough */
+        if ((FATXXFS_DENTRY_CLUST(fs, dentry) > (a_fatfs->lastclust)) &&
+            (FATFS_ISEOF(FATXXFS_DENTRY_CLUST(fs, dentry), a_fatfs->mask) == 0)) {
+            if (tsk_verbose)
+                fprintf(stderr, "%s: start cluster\n", func_name);
+            return 0;
+        }
+
+        /* Verify the file size is smaller than the data area */
+        else if (tsk_getu32(fs->endian, dentry->size) >
+            ((a_fatfs->clustcnt * a_fatfs->csize) << a_fatfs->ssize_sh)) {
+            if (tsk_verbose)
+                fprintf(stderr, "%s: size\n", func_name);
+            return 0;
+        }
+
+        else if ((tsk_getu32(fs->endian, dentry->size) > 0)
+            && (FATXXFS_DENTRY_CLUST(fs, dentry) == 0)) {
+            if (tsk_verbose)
+                fprintf(stderr,
+                    "%s: non-zero size and NULL starting cluster\n", func_name);
+            return 0;
+        }
+		
+		else if((a_fatfs->subtype == TSK_FATFS_SUBTYPE_SPEC) && (is_83_name(dentry) == 0))
+			return 0;
+
+        // basic sanity check on values
+        else if ((tsk_getu16(fs->endian, dentry->ctime) == 0)
+            && (tsk_getu16(fs->endian, dentry->wtime) == 0)
+            && (tsk_getu16(fs->endian, dentry->cdate) == 0)
+            && (tsk_getu16(fs->endian, dentry->adate) == 0)
+            && (tsk_getu16(fs->endian, dentry->wdate) == 0)
+            && (FATXXFS_DENTRY_CLUST(fs, dentry) == 0)
+            && (tsk_getu32(fs->endian, dentry->size) == 0)) {
+            if (tsk_verbose)
+                fprintf(stderr,
+                    "s: nearly all values zero\n", func_name);
+            return 0;
+        }
+
+        return 1;
+    }
+}
+
+/*
+ * convert the attribute list in FAT to a UNIX mode
+ */
+static TSK_FS_META_TYPE_ENUM
+attr2type(uint16_t attr)
+{
+    if (attr & FATFS_ATTR_DIRECTORY)
+        return TSK_FS_META_TYPE_DIR;
+    else
+        return TSK_FS_META_TYPE_REG;
+}
+
+static int
+attr2mode(uint16_t attr)
+{
+    int mode;
+
+    /* every file is executable */
+    mode =
+        (TSK_FS_META_MODE_IXUSR | TSK_FS_META_MODE_IXGRP |
+        TSK_FS_META_MODE_IXOTH);
+
+    if ((attr & FATFS_ATTR_READONLY) == 0)
+        mode |=
+            (TSK_FS_META_MODE_IRUSR | TSK_FS_META_MODE_IRGRP |
+            TSK_FS_META_MODE_IROTH);
+
+    if ((attr & FATFS_ATTR_HIDDEN) == 0)
+        mode |=
+            (TSK_FS_META_MODE_IWUSR | TSK_FS_META_MODE_IWGRP |
+            TSK_FS_META_MODE_IWOTH);
+
+    return mode;
+}
+
+TSK_RETVAL_ENUM
+fatxxfs_dinode_copy(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
+    FATFS_DENTRY *a_dentry, uint8_t a_cluster_is_alloc, TSK_FS_FILE *a_fs_file)
+{
+    const char *func_name = "fatxxfs_dinode_copy";
+    int i;
+    TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_fatfs->fs_info;
+    TSK_FS_META *fs_meta = a_fs_file->meta;
+    FATXXFS_DENTRY *dentry = (FATXXFS_DENTRY*)a_dentry;
+    TSK_DADDR_T *addr_ptr;
+    uint32_t flags = 0;
+
+    if (fs_meta->content_len < FATFS_FILE_CONTENT_LEN) {
+        if ((fs_meta =
+                tsk_fs_meta_realloc(fs_meta,
+                    FATFS_FILE_CONTENT_LEN)) == NULL) {
+            return TSK_ERR;
+        }
+    }
+
+    fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
+    if (fs_meta->attr) {
+        tsk_fs_attrlist_markunused(fs_meta->attr);
+    }
+
+    fs_meta->mode = (TSK_FS_META_MODE_ENUM)attr2mode(dentry->attrib);
+    fs_meta->type = attr2type(dentry->attrib);
+
+    fs_meta->addr = a_inum;
+
+    if (a_cluster_is_alloc) {
+
+		if(FATXXFS_IS_DELETED(dentry->name, a_fatfs)){
+				flags = TSK_FS_META_FLAG_UNALLOC;
+		}
+		else{
+			flags = TSK_FS_META_FLAG_ALLOC;
+		}
+    }
+    else {
+        flags = TSK_FS_META_FLAG_UNALLOC;
+    }
+    fs_meta->flags = (TSK_FS_META_FLAG_ENUM)flags;
+
+    if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
+        /* LFN entries don't have these values */
+        fs_meta->nlink = 0;
+        fs_meta->size = 0;
+        fs_meta->mtime = 0;
+        fs_meta->atime = 0;
+        fs_meta->ctime = 0;
+        fs_meta->crtime = 0;
+        fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano =
+            fs_meta->crtime_nano = 0;
+    }
+    else {
+        /* There is no notion of link in FAT, just deleted or not */
+		if(FATXXFS_IS_DELETED(dentry->name, a_fatfs)){
+			fs_meta->nlink = 0;
+		}
+		else{
+			fs_meta->nlink = 1;
+		}
+        fs_meta->size = (TSK_OFF_T) tsk_getu32(fs->endian, dentry->size);
+
+        /* If these are valid dates, then convert to a unix date format */
+        if (FATFS_ISDATE(tsk_getu16(fs->endian, dentry->wdate)))
+            fs_meta->mtime =
+                fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->wdate),
+                tsk_getu16(fs->endian, dentry->wtime), 0);
+        else
+            fs_meta->mtime = 0;
+        fs_meta->mtime_nano = 0;
+
+        if (FATFS_ISDATE(tsk_getu16(fs->endian, dentry->adate)))
+            fs_meta->atime =
+                fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->adate), 0, 0);
+        else
+            fs_meta->atime = 0;
+        fs_meta->atime_nano = 0;
+
+
+        /* cdate is the creation date in FAT and there is no change,
+         * so we just put in into change and set create to 0.  The other
+         * front-end code knows how to handle it and display it
+         */
+        if (FATFS_ISDATE(tsk_getu16(fs->endian, dentry->cdate))) {
+            fs_meta->crtime =
+                fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->cdate),
+                tsk_getu16(fs->endian, dentry->ctime), dentry->ctimeten);
+            fs_meta->crtime_nano = fatfs_dos_2_nanosec(dentry->ctimeten);
+        }
+        else {
+            fs_meta->crtime = 0;
+            fs_meta->crtime_nano = 0;
+        }
+
+        // FAT does not have a changed time
+        fs_meta->ctime = 0;
+        fs_meta->ctime_nano = 0;
+    }
+
+    /* Values that do not exist in FAT */
+    fs_meta->uid = 0;
+    fs_meta->gid = 0;
+    fs_meta->seq = 0;
+
+
+    /* We will be copying a name, so allocate a structure */
+    if (fs_meta->name2 == NULL) {
+        if ((fs_meta->name2 = (TSK_FS_META_NAME_LIST *)
+                tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL)
+            return TSK_ERR;
+        fs_meta->name2->next = NULL;
+    }
+
+    /* If we have a LFN entry, then we need to convert the three
+     * parts of the name to UTF-8 and copy it into the name structure .
+     */
+    if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
+        FATXXFS_DENTRY_LFN *lfn = (FATXXFS_DENTRY_LFN *) dentry;
+
+        /* Convert the first part of the name */
+        UTF8 *name8 = (UTF8 *) fs_meta->name2->name;
+        UTF16 *name16 = (UTF16 *) lfn->part1;
+
+        int retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
+            (UTF16 *) & lfn->part1[10],
+            &name8,
+            (UTF8 *) ((uintptr_t) fs_meta->name2->name +
+                sizeof(fs_meta->name2->name)),
+            TSKlenientConversion);
+
+        if (retVal != TSKconversionOK) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_UNICODE);
+            tsk_error_set_errstr
+                ("%s: Error converting FAT LFN (1) to UTF8: %d",
+                func_name, retVal);
+            *name8 = '\0';
+
+            return TSK_COR;
+        }
+
+        /* Convert the second part of the name */
+        name16 = (UTF16 *) lfn->part2;
+        retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
+            (UTF16 *) & lfn->part2[12],
+            &name8,
+            (UTF8 *) ((uintptr_t) fs_meta->name2->name +
+                sizeof(fs_meta->name2->name)), TSKlenientConversion);
+
+        if (retVal != TSKconversionOK) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_UNICODE);
+            tsk_error_set_errstr
+                ("%s: Error converting FAT LFN (2) to UTF8: %d",
+                func_name, retVal);
+            *name8 = '\0';
+
+            return TSK_COR;
+        }
+
+        /* Convert the third part of the name */
+        name16 = (UTF16 *) lfn->part3;
+        retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
+            (UTF16 *) & lfn->part3[4],
+            &name8,
+            (UTF8 *) ((uintptr_t) fs_meta->name2->name +
+                sizeof(fs_meta->name2->name)), TSKlenientConversion);
+
+        if (retVal != TSKconversionOK) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_UNICODE);
+            tsk_error_set_errstr
+                ("%s: Error converting FAT LFN (3) to UTF8: %d",
+                func_name, retVal);
+            *name8 = '\0';
+
+            return TSK_COR;
+        }
+
+        /* Make sure it is NULL Terminated */
+        if ((uintptr_t) name8 >
+            (uintptr_t) fs_meta->name2->name +
+            sizeof(fs_meta->name2->name))
+            fs_meta->name2->name[sizeof(fs_meta->name2->name) - 1] = '\0';
+        else
+            *name8 = '\0';
+    }
+    /* If the entry is for a volume label, then copy the label.
+     */
+    else if ((dentry->attrib & FATFS_ATTR_VOLUME) == FATFS_ATTR_VOLUME) {
+        int a;
+
+        i = 0;
+        for (a = 0; a < 8; a++) {
+            if ((dentry->name[a] != 0x00) && (dentry->name[a] != 0xff))
+                fs_meta->name2->name[i++] = dentry->name[a];
+        }
+        for (a = 0; a < 3; a++) {
+            if ((dentry->ext[a] != 0x00) && (dentry->ext[a] != 0xff))
+                fs_meta->name2->name[i++] = dentry->ext[a];
+        }
+        fs_meta->name2->name[i] = '\0';
+
+        /* clean up non-ASCII because we are
+         * copying it into a buffer that is supposed to be UTF-8 and
+         * we don't know what encoding it is actually in or if it is 
+         * simply junk. */
+        fatfs_cleanup_ascii(fs_meta->name2->name);
+    }
+    /* If the entry is a normal short entry, then copy the name
+     * and add the '.' for the extension
+     */
+    else {
+        for (i = 0; (i < 8) && (dentry->name[i] != 0) && (dentry->name[i] != ' ');
+            i++) {
+            if ((i == 0) && (dentry->name[0] == FATXXFS_SLOT_DELETED))
+                fs_meta->name2->name[0] = '_';
+            else if ((dentry->lowercase & FATXXFS_CASE_LOWER_BASE) &&
+                (dentry->name[i] >= 'A') && (dentry->name[i] <= 'Z'))
+                fs_meta->name2->name[i] = dentry->name[i] + 32;
+            else
+                fs_meta->name2->name[i] = dentry->name[i];
+        }
+
+        if ((dentry->ext[0]) && (dentry->ext[0] != ' ')) {
+            int a;
+            fs_meta->name2->name[i++] = '.';
+            for (a = 0;
+                (a < 3) && (dentry->ext[a] != 0) && (dentry->ext[a] != ' ');
+                a++, i++) {
+                if ((dentry->lowercase & FATXXFS_CASE_LOWER_EXT)
+                    && (dentry->ext[a] >= 'A') && (dentry->ext[a] <= 'Z'))
+                    fs_meta->name2->name[i] = dentry->ext[a] + 32;
+                else
+                    fs_meta->name2->name[i] = dentry->ext[a];
+            }
+        }
+        fs_meta->name2->name[i] = '\0';
+
+        /* clean up non-ASCII because we are
+         * copying it into a buffer that is supposed to be UTF-8 and
+         * we don't know what encoding it is actually in or if it is 
+         * simply junk. */
+        fatfs_cleanup_ascii(fs_meta->name2->name);
+    }
+
+    /* Clean up name to remove control characters */
+    i = 0;
+    while (fs_meta->name2->name[i] != '\0') {
+        if (TSK_IS_CNTRL(fs_meta->name2->name[i]))
+            fs_meta->name2->name[i] = '^';
+        i++;
+    }
+
+    /* get the starting cluster */
+    addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr;
+    if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
+        addr_ptr[0] = 0;
+    }
+    else {
+        addr_ptr[0] = FATXXFS_DENTRY_CLUST(fs, dentry) & a_fatfs->mask;
+    }
+
+    /* FAT does not store a size for its directories so make one based
+     * on the number of allocated sectors
+     */
+    if ((dentry->attrib & FATFS_ATTR_DIRECTORY) &&
+        ((dentry->attrib & FATFS_ATTR_LFN) != FATFS_ATTR_LFN)) {
+        if (fs_meta->flags & TSK_FS_META_FLAG_ALLOC) {
+            TSK_LIST *list_seen = NULL;
+
+            /* count the total number of clusters in this file */
+            TSK_DADDR_T clust = FATXXFS_DENTRY_CLUST(fs, dentry);
+            int cnum = 0;
+
+            while ((clust) && (0 == FATFS_ISEOF(clust, a_fatfs->mask))) {
+                TSK_DADDR_T nxt;
+
+                /* Make sure we do not get into an infinite loop */
+                if (tsk_list_find(list_seen, clust)) {
+                    if (tsk_verbose)
+                        tsk_fprintf(stderr,
+                            "Loop found while determining directory size\n");
+                    break;
+                }
+                if (tsk_list_add(&list_seen, clust)) {
+                    tsk_list_free(list_seen);
+                    list_seen = NULL;
+                    return TSK_ERR;
+                }
+
+                cnum++;
+
+                if (fatfs_getFAT(a_fatfs, clust, &nxt))
+                    break;
+                else
+                    clust = nxt;
+            }
+
+            tsk_list_free(list_seen);
+            list_seen = NULL;
+
+            fs_meta->size =
+                (TSK_OFF_T) ((cnum * a_fatfs->csize) << a_fatfs->ssize_sh);
+        }
+        /* if the dir is unallocated, then assume 0 or cluster size
+         * Ideally, we would have a smart algo here to do recovery
+         * and look for dentries.  However, we do not have that right
+         * now and if we do not add this special check then it can
+         * assume that an allocated file cluster chain belongs to the
+         * directory */
+        else {
+            // if the first cluster is allocated, then set size to be 0
+            if (fatxxfs_is_cluster_alloc(a_fatfs, FATXXFS_DENTRY_CLUST(fs,
+                        dentry)) == 1)
+                fs_meta->size = 0;
+            else
+                fs_meta->size = a_fatfs->csize << a_fatfs->ssize_sh;
+        }
+    }
+
+    return TSK_OK;
+}
+
+/**
+ * \internal
+ * Populate the TSK_FS_META object of a TSK_FS_FILE object for a 
+ * given inode address.
+ *
+ * @param [in] a_fs File system that contains the inode.
+ * @param [out] a_fs_file The file corresponding to the inode.
+ * @param [in] a_inum The inode address.
+ * @returns 1 if an error occurs or if the inode address is not
+ * for a valid inode, 0 otherwise.
+ */
+uint8_t
+fatxxfs_inode_lookup(FATFS_INFO *a_fatfs, TSK_FS_FILE *a_fs_file,
+    TSK_INUM_T a_inum)
+{
+    const char *func_name = "fatxxfs_inode_lookup";
+    TSK_DADDR_T sector = 0;
+    int8_t ret_val = 0;
+    FATFS_DATA_UNIT_ALLOC_STATUS_ENUM sector_alloc_status = FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN;
+    FATFS_DENTRY dentry;
+    TSK_RETVAL_ENUM copy_result = TSK_OK;
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
+        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name) ||
+        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
+        return 1;
+    }
+
+    sector = FATFS_INODE_2_SECT(a_fatfs, a_inum);
+    if (sector > a_fatfs->fs_info.last_block) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr("%s: Inode %" PRIuINUM
+            " in sector too big for image: %" PRIuDADDR, func_name, a_inum, sector);
+        return 1;
+    }
+
+    if (fatfs_dentry_load(a_fatfs, &dentry, a_inum) != 0) {
+        return 1;
+    }
+
+    ret_val = fatfs_is_sectalloc(a_fatfs, sector);
+    if (ret_val == -1) {
+        return 1;
+    }
+    else {
+        sector_alloc_status = (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)ret_val;
+    }
+
+    /* Note that only the sector allocation status is used to choose
+     * between the basic or in-depth version of the inode validity 
+     * test. In other places in the code information about whether or not 
+     * the sector that contains the inode is part of a folder is used to 
+     * make this decision. Here, that information is not available. Thus, 
+     * the test here is less reliable and may result in some false 
+     * positives. */
+    if (!fatxxfs_is_dentry(a_fatfs, &dentry, sector_alloc_status, sector_alloc_status)) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr("%s: %" PRIuINUM
+            " is not an inode", func_name, a_inum);
+        return 1;
+    }
+
+    copy_result = fatxxfs_dinode_copy(a_fatfs, a_inum, &dentry, (uint8_t)sector_alloc_status, a_fs_file);
+    if (copy_result == TSK_OK) {
+        return 0;
+    }
+    else if (copy_result == TSK_COR) {
+        /* If there was a Unicode conversion error,
+         * then still return the inode. */
+        if (tsk_verbose) {
+            tsk_error_print(stderr);
+        }
+        tsk_error_reset();
+        return 0;
+    }
+    else {
+        return 1;
+    }
+}
+
+/**
+ * Output the file attributes of an exFAT file directory entry in 
+ * human-readable form.
+ *
+ * @param a_fatfs Source file system for the directory entry.
+ * @param a_inum Inode address associated with the directory entry.
+ * @param a_hFile Handle of the file to which to write.
+ * @return 0 on success, 1 on failure, per TSK convention
+ */
+uint8_t
+fatxxfs_istat_attr_flags(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, FILE *a_hFile)
+{
+    const char *func_name = "fatxxfs_istat_attr_flags";
+    FATXXFS_DENTRY dentry;
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
+        fatfs_ptr_arg_is_null(a_hFile, "a_hFile", func_name) ||
+        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
+        return 1; 
+    }
+
+    if (fatfs_dentry_load(a_fatfs, (FATFS_DENTRY*)(&dentry), a_inum)) {
+        return 1; 
+    }
+
+    if ((dentry.attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
+        tsk_fprintf(a_hFile, "Long File Name\n");
+    }
+    else {
+        if (dentry.attrib & FATFS_ATTR_DIRECTORY)
+            tsk_fprintf(a_hFile, "Directory");
+        else if (dentry.attrib & FATFS_ATTR_VOLUME)
+            tsk_fprintf(a_hFile, "Volume Label");
+        else
+            tsk_fprintf(a_hFile, "File");
+
+        if (dentry.attrib & FATFS_ATTR_READONLY)
+            tsk_fprintf(a_hFile, ", Read Only");
+        if (dentry.attrib & FATFS_ATTR_HIDDEN)
+            tsk_fprintf(a_hFile, ", Hidden");
+        if (dentry.attrib & FATFS_ATTR_SYSTEM)
+            tsk_fprintf(a_hFile, ", System");
+        if (dentry.attrib & FATFS_ATTR_ARCHIVE)
+            tsk_fprintf(a_hFile, ", Archive");
+
+        tsk_fprintf(a_hFile, "\n");
+    }
+
+    return 0;
+}
+
+uint8_t
+fatxxfs_inode_walk_should_skip_dentry(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, 
+    FATFS_DENTRY *a_dentry, unsigned int a_selection_flags, 
+    int a_cluster_is_alloc)
+{
+    const char *func_name = "fatxxfs_inode_walk_should_skip_dentry";
+    FATXXFS_DENTRY *dentry = (FATXXFS_DENTRY*)a_dentry;
+    unsigned int dentry_flags = 0;
+
+    assert(a_fatfs != NULL);
+    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
+    assert(a_dentry != NULL);
+
+    tsk_error_reset();
+    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
+        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name) ||
+        fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
+        return 1; 
+    }
+
+    /* If this is a long file name entry, then skip it and
+     * wait for the short name. */
+    if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
+        return 1;
+    }
+
+    /* Skip the "." and ".." entries because they are redundant. */
+    if (((dentry->attrib & FATFS_ATTR_DIRECTORY) == FATFS_ATTR_DIRECTORY) &&
+         (dentry->name[0] == '.')) {
+        return 1;
+    }
+
+    /* Compare directory entry allocation status with the inode selection
+     * flags. Allocation status is determined first by the allocation status 
+     * of the sector that contains the entry, then by the deleted status of 
+     * the file. This is necessary because when a directory is deleted, its 
+     * contents are not always marked as unallocated. */
+    if (a_cluster_is_alloc == 1) {
+		if(FATXXFS_IS_DELETED(dentry->name, a_fatfs)){
+			dentry_flags = TSK_FS_META_FLAG_UNALLOC;
+		}
+		else{
+			dentry_flags = TSK_FS_META_FLAG_ALLOC;
+		}
+    }
+    else {
+        dentry_flags = TSK_FS_META_FLAG_UNALLOC;
+    }
+
+    if ((a_selection_flags & dentry_flags) != dentry_flags) {
+        return 1;
+    }
+
+    /* If the processing flags call for only processing orphan files, check 
+     * whether or not this inode is in list of non-orphan files found via name
+     * walk. */
+    if ((dentry_flags & TSK_FS_META_FLAG_UNALLOC) &&
+        (a_selection_flags & TSK_FS_META_FLAG_ORPHAN) &&
+        (tsk_fs_dir_find_inum_named(&(a_fatfs->fs_info), a_inum))) {
+        return 1;
+    }
+
+    return 0;
 }
\ No newline at end of file
diff --git a/win32/libtsk/libtsk.vcxproj b/win32/libtsk/libtsk.vcxproj
index 86b584e83..5f221eb47 100755
--- a/win32/libtsk/libtsk.vcxproj
+++ b/win32/libtsk/libtsk.vcxproj
@@ -1,242 +1,242 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup Label="ProjectConfigurations">
-    <ProjectConfiguration Include="Debug_NoLibs|Win32">
-      <Configuration>Debug_NoLibs</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Debug|Win32">
-      <Configuration>Debug</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|Win32">
-      <Configuration>Release</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-  </ItemGroup>
-  <PropertyGroup Label="Globals">
-    <ProjectGuid>{76EFC06C-1F64-4478-ABE8-79832716B393}</ProjectGuid>
-    <RootNamespace>libtsk</RootNamespace>
-    <Keyword>Win32Proj</Keyword>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_NoLibs|Win32'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <CharacterSet>Unicode</CharacterSet>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
-  <ImportGroup Label="ExtensionSettings">
-  </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug_NoLibs|Win32'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <PropertyGroup Label="UserMacros" />
-  <PropertyGroup>
-    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
-    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
-    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
-    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
-    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
-    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug_NoLibs|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
-    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug_NoLibs|Win32'">$(Configuration)\</IntDir>
-  </PropertyGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <ClCompile>
-      <Optimization>Disabled</Optimization>
-      <AdditionalIncludeDirectories>$(ProjectDir)\..\..\;$(LIBEWF_HOME)\common;$(LIBEWF_HOME)\include;$(LIBEWF_HOME)\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;HAVE_LIBEWF;HAVE_LIBZ;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <MinimalRebuild>true</MinimalRebuild>
-      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
-      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
-      <CompileAs>Default</CompileAs>
-    </ClCompile>
-    <PostBuildEvent>
-      <Command>copy "$(LIBEWF_HOME)\msvscpp\release\libewf.dll" "$(OutDir)"
-copy "$(LIBEWF_HOME)\msvscpp\release\zlib.dll" "$(OutDir)"
-</Command>
-    </PostBuildEvent>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <ClCompile>
-      <Optimization>MaxSpeed</Optimization>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <AdditionalIncludeDirectories>$(ProjectDir)\..\..\;$(LIBEWF_HOME)\common;$(LIBEWF_HOME)\include;$(LIBEWF_HOME)\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;HAVE_LIBEWF;HAVE_LIBZ;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
-    </ClCompile>
-    <PostBuildEvent>
-      <Command>copy "$(LIBEWF_HOME)\msvscpp\release\libewf.dll" "$(OutDir)"
-copy "$(LIBEWF_HOME)\msvscpp\release\zlib.dll" "$(OutDir)"
-</Command>
-    </PostBuildEvent>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_NoLibs|Win32'">
-    <ClCompile>
-      <Optimization>Disabled</Optimization>
-      <AdditionalIncludeDirectories>$(ProjectDir)\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <MinimalRebuild>true</MinimalRebuild>
-      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
-      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
-    </ClCompile>
-  </ItemDefinitionGroup>
-  <ItemGroup>
-    <ClCompile Include="..\..\tsk\fs\exfatfs_dent.c" />
-    <ClCompile Include="..\..\tsk\fs\exfatfs.c" />
-    <ClCompile Include="..\..\tsk\fs\exfatfs_meta.c" />
-    <ClCompile Include="..\..\tsk\fs\fatfs_utils.c" />
-    <ClCompile Include="..\..\tsk\fs\fatxxfs.c" />
-    <ClCompile Include="..\..\tsk\fs\fatxxfs_dent.c" />
-    <ClCompile Include="..\..\tsk\fs\fatxxfs_meta.c" />
-    <ClCompile Include="..\..\tsk\vs\bsd.c" />
-    <ClCompile Include="..\..\tsk\vs\dos.c" />
-    <ClCompile Include="..\..\tsk\vs\gpt.c" />
-    <ClCompile Include="..\..\tsk\vs\mac.c" />
-    <ClCompile Include="..\..\tsk\vs\mm_io.c" />
-    <ClCompile Include="..\..\tsk\vs\mm_open.c" />
-    <ClCompile Include="..\..\tsk\vs\mm_part.c" />
-    <ClCompile Include="..\..\tsk\vs\mm_types.c" />
-    <ClCompile Include="..\..\tsk\vs\sun.c" />
-    <ClCompile Include="..\..\tsk\fs\dcalc_lib.c" />
-    <ClCompile Include="..\..\tsk\fs\dcat_lib.c" />
-    <ClCompile Include="..\..\tsk\fs\dls_lib.c" />
-    <ClCompile Include="..\..\tsk\fs\dstat_lib.c" />
-    <ClCompile Include="..\..\tsk\fs\ext2fs.c" />
-    <ClCompile Include="..\..\tsk\fs\ext2fs_dent.c" />
-    <ClCompile Include="..\..\tsk\fs\ext2fs_journal.c" />
-    <ClCompile Include="..\..\tsk\fs\fatfs.c" />
-    <ClCompile Include="..\..\tsk\fs\fatfs_dent.cpp" />
-    <ClCompile Include="..\..\tsk\fs\fatfs_meta.c" />
-    <ClCompile Include="..\..\tsk\fs\ffind_lib.c" />
-    <ClCompile Include="..\..\tsk\fs\ffs.c" />
-    <ClCompile Include="..\..\tsk\fs\ffs_dent.c" />
-    <ClCompile Include="..\..\tsk\fs\fls_lib.c" />
-    <ClCompile Include="..\..\tsk\fs\fs_attr.c" />
-    <ClCompile Include="..\..\tsk\fs\fs_attrlist.c" />
-    <ClCompile Include="..\..\tsk\fs\fs_block.c" />
-    <ClCompile Include="..\..\tsk\fs\fs_dir.c" />
-    <ClCompile Include="..\..\tsk\fs\fs_file.c" />
-    <ClCompile Include="..\..\tsk\fs\fs_inode.c" />
-    <ClCompile Include="..\..\tsk\fs\fs_io.c" />
-    <ClCompile Include="..\..\tsk\fs\fs_load.c" />
-    <ClCompile Include="..\..\tsk\fs\fs_name.c" />
-    <ClCompile Include="..\..\tsk\fs\fs_open.c" />
-    <ClCompile Include="..\..\tsk\fs\fs_parse.c" />
-    <ClCompile Include="..\..\tsk\fs\fs_types.c" />
-    <ClCompile Include="..\..\tsk\fs\hfs.c" />
-    <ClCompile Include="..\..\tsk\fs\hfs_dent.c" />
-    <ClCompile Include="..\..\tsk\fs\hfs_journal.c" />
-    <ClCompile Include="..\..\tsk\fs\hfs_unicompare.c" />
-    <ClCompile Include="..\..\tsk\fs\icat_lib.c" />
-    <ClCompile Include="..\..\tsk\fs\ifind_lib.c" />
-    <ClCompile Include="..\..\tsk\fs\ils_lib.c" />
-    <ClCompile Include="..\..\tsk\fs\iso9660.c" />
-    <ClCompile Include="..\..\tsk\fs\iso9660_dent.c" />
-    <ClCompile Include="..\..\tsk\fs\nofs_misc.c" />
-    <ClCompile Include="..\..\tsk\fs\ntfs.c" />
-    <ClCompile Include="..\..\tsk\fs\ntfs_dent.cpp" />
-    <ClCompile Include="..\..\tsk\fs\rawfs.c" />
-    <ClCompile Include="..\..\tsk\fs\swapfs.c" />
-    <ClCompile Include="..\..\tsk\fs\unix_misc.c" />
-    <ClCompile Include="..\..\tsk\fs\walk_cpp.cpp" />
-    <ClCompile Include="..\..\tsk\fs\yaffs.cpp" />
-    <ClCompile Include="..\..\tsk\auto\auto.cpp" />
-    <ClCompile Include="..\..\tsk\auto\auto_db.cpp" />
-    <ClCompile Include="..\..\tsk\auto\case_db.cpp" />
-    <ClCompile Include="..\..\tsk\auto\db_sqlite.cpp" />
-    <ClCompile Include="..\..\tsk\auto\sqlite3.c" />
-    <ClCompile Include="..\..\tsk\base\md5c.c" />
-    <ClCompile Include="..\..\tsk\base\mymalloc.c" />
-    <ClCompile Include="..\..\tsk\base\sha1c.c" />
-    <ClCompile Include="..\..\tsk\base\tsk_endian.c" />
-    <ClCompile Include="..\..\tsk\base\tsk_error.c" />
-    <ClCompile Include="..\..\tsk\base\tsk_error_win32.cpp" />
-    <ClCompile Include="..\..\tsk\base\tsk_list.c" />
-    <ClCompile Include="..\..\tsk\base\tsk_lock.c" />
-    <ClCompile Include="..\..\tsk\base\tsk_parse.c" />
-    <ClCompile Include="..\..\tsk\base\tsk_printf.c" />
-    <ClCompile Include="..\..\tsk\base\tsk_stack.c" />
-    <ClCompile Include="..\..\tsk\base\tsk_unicode.c" />
-    <ClCompile Include="..\..\tsk\base\tsk_version.c" />
-    <ClCompile Include="..\..\tsk\base\XGetopt.c" />
-    <ClCompile Include="..\..\tsk\hashdb\encase_index.c" />
-    <ClCompile Include="..\..\tsk\hashdb\hk_index.c" />
-    <ClCompile Include="..\..\tsk\hashdb\idxonly_index.c" />
-    <ClCompile Include="..\..\tsk\hashdb\md5sum_index.c" />
-    <ClCompile Include="..\..\tsk\hashdb\nsrl_index.c" />
-    <ClCompile Include="..\..\tsk\hashdb\tm_lookup.c" />
-    <ClCompile Include="..\..\tsk\img\aff.c" />
-    <ClCompile Include="..\..\tsk\img\ewf.c" />
-    <ClCompile Include="..\..\tsk\img\img_io.c" />
-    <ClCompile Include="..\..\tsk\img\img_open.c" />
-    <ClCompile Include="..\..\tsk\img\img_types.c" />
-    <ClCompile Include="..\..\tsk\img\mult_files.c" />
-    <ClCompile Include="..\..\tsk\img\raw.c" />
-  </ItemGroup>
-  <ItemGroup>
-    <ClInclude Include="..\..\tsk\fs\tsk_exfatfs.h" />
-    <ClInclude Include="..\..\tsk\fs\tsk_fatxxfs.h" />
-    <ClInclude Include="..\..\tsk\vs\tsk_bsd.h" />
-    <ClInclude Include="..\..\tsk\vs\tsk_dos.h" />
-    <ClInclude Include="..\..\tsk\vs\tsk_gpt.h" />
-    <ClInclude Include="..\..\tsk\vs\tsk_mac.h" />
-    <ClInclude Include="..\..\tsk\vs\tsk_sun.h" />
-    <ClInclude Include="..\..\tsk\vs\tsk_vs.h" />
-    <ClInclude Include="..\..\tsk\vs\tsk_vs_i.h" />
-    <ClInclude Include="..\..\tsk\fs\tsk_ext2fs.h" />
-    <ClInclude Include="..\..\tsk\fs\tsk_fatfs.h" />
-    <ClInclude Include="..\..\tsk\fs\tsk_ffs.h" />
-    <ClInclude Include="..\..\tsk\fs\tsk_fs.h" />
-    <ClInclude Include="..\..\tsk\fs\tsk_fs_i.h" />
-    <ClInclude Include="..\..\tsk\fs\tsk_hfs.h" />
-    <ClInclude Include="..\..\tsk\fs\tsk_iso9660.h" />
-    <ClInclude Include="..\..\tsk\fs\tsk_ntfs.h" />
-    <ClInclude Include="..\..\tsk\fs\tsk_yaffs.h" />
-    <ClInclude Include="..\..\tsk\auto\sqlite3.h" />
-    <ClInclude Include="..\..\tsk\auto\tsk_auto.h" />
-    <ClInclude Include="..\..\tsk\auto\tsk_auto_i.h" />
-    <ClInclude Include="..\..\tsk\auto\tsk_case_db.h" />
-    <ClInclude Include="..\..\tsk\auto\tsk_db_sqlite.h" />
-    <ClInclude Include="..\..\tsk\base\tsk_base.h" />
-    <ClInclude Include="..\..\tsk\base\tsk_base_i.h" />
-    <ClInclude Include="..\..\tsk\base\tsk_os.h" />
-    <ClInclude Include="..\..\tsk\hashdb\tsk_hashdb.h" />
-    <ClInclude Include="..\..\tsk\hashdb\tsk_hashdb_i.h" />
-    <ClInclude Include="..\..\tsk\img\aff.h" />
-    <ClInclude Include="..\..\tsk\img\ewf.h" />
-    <ClInclude Include="..\..\tsk\img\raw.h" />
-    <ClInclude Include="..\..\tsk\img\tsk_img.h" />
-    <ClInclude Include="..\..\tsk\img\tsk_img_i.h" />
-  </ItemGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-  <ImportGroup Label="ExtensionTargets">
-  </ImportGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug_NoLibs|Win32">
+      <Configuration>Debug_NoLibs</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{76EFC06C-1F64-4478-ABE8-79832716B393}</ProjectGuid>
+    <RootNamespace>libtsk</RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_NoLibs|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug_NoLibs|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug_NoLibs|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug_NoLibs|Win32'">$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>$(ProjectDir)\..\..\;$(LIBEWF_HOME)\common;$(LIBEWF_HOME)\include;$(LIBEWF_HOME)\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;HAVE_LIBEWF;HAVE_LIBZ;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <CompileAs>Default</CompileAs>
+    </ClCompile>
+    <PostBuildEvent>
+      <Command>copy "$(LIBEWF_HOME)\msvscpp\release\libewf.dll" "$(OutDir)"
+copy "$(LIBEWF_HOME)\msvscpp\release\zlib.dll" "$(OutDir)"
+</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>$(ProjectDir)\..\..\;$(LIBEWF_HOME)\common;$(LIBEWF_HOME)\include;$(LIBEWF_HOME)\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;HAVE_LIBEWF;HAVE_LIBZ;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <PostBuildEvent>
+      <Command>copy "$(LIBEWF_HOME)\msvscpp\release\libewf.dll" "$(OutDir)"
+copy "$(LIBEWF_HOME)\msvscpp\release\zlib.dll" "$(OutDir)"
+</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_NoLibs|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>$(ProjectDir)\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\tsk\fs\exfatfs_dent.c" />
+    <ClCompile Include="..\..\tsk\fs\exfatfs.c" />
+    <ClCompile Include="..\..\tsk\fs\exfatfs_meta.c" />
+    <ClCompile Include="..\..\tsk\fs\fatfs_utils.c" />
+    <ClCompile Include="..\..\tsk\fs\fatxxfs.c" />
+    <ClCompile Include="..\..\tsk\fs\fatxxfs_dent.c" />
+    <ClCompile Include="..\..\tsk\fs\fatxxfs_meta.c" />
+    <ClCompile Include="..\..\tsk\vs\bsd.c" />
+    <ClCompile Include="..\..\tsk\vs\dos.c" />
+    <ClCompile Include="..\..\tsk\vs\gpt.c" />
+    <ClCompile Include="..\..\tsk\vs\mac.c" />
+    <ClCompile Include="..\..\tsk\vs\mm_io.c" />
+    <ClCompile Include="..\..\tsk\vs\mm_open.c" />
+    <ClCompile Include="..\..\tsk\vs\mm_part.c" />
+    <ClCompile Include="..\..\tsk\vs\mm_types.c" />
+    <ClCompile Include="..\..\tsk\vs\sun.c" />
+    <ClCompile Include="..\..\tsk\fs\dcalc_lib.c" />
+    <ClCompile Include="..\..\tsk\fs\dcat_lib.c" />
+    <ClCompile Include="..\..\tsk\fs\dls_lib.c" />
+    <ClCompile Include="..\..\tsk\fs\dstat_lib.c" />
+    <ClCompile Include="..\..\tsk\fs\ext2fs.c" />
+    <ClCompile Include="..\..\tsk\fs\ext2fs_dent.c" />
+    <ClCompile Include="..\..\tsk\fs\ext2fs_journal.c" />
+    <ClCompile Include="..\..\tsk\fs\fatfs.c" />
+    <ClCompile Include="..\..\tsk\fs\fatfs_dent.cpp" />
+    <ClCompile Include="..\..\tsk\fs\fatfs_meta.c" />
+    <ClCompile Include="..\..\tsk\fs\ffind_lib.c" />
+    <ClCompile Include="..\..\tsk\fs\ffs.c" />
+    <ClCompile Include="..\..\tsk\fs\ffs_dent.c" />
+    <ClCompile Include="..\..\tsk\fs\fls_lib.c" />
+    <ClCompile Include="..\..\tsk\fs\fs_attr.c" />
+    <ClCompile Include="..\..\tsk\fs\fs_attrlist.c" />
+    <ClCompile Include="..\..\tsk\fs\fs_block.c" />
+    <ClCompile Include="..\..\tsk\fs\fs_dir.c" />
+    <ClCompile Include="..\..\tsk\fs\fs_file.c" />
+    <ClCompile Include="..\..\tsk\fs\fs_inode.c" />
+    <ClCompile Include="..\..\tsk\fs\fs_io.c" />
+    <ClCompile Include="..\..\tsk\fs\fs_load.c" />
+    <ClCompile Include="..\..\tsk\fs\fs_name.c" />
+    <ClCompile Include="..\..\tsk\fs\fs_open.c" />
+    <ClCompile Include="..\..\tsk\fs\fs_parse.c" />
+    <ClCompile Include="..\..\tsk\fs\fs_types.c" />
+    <ClCompile Include="..\..\tsk\fs\hfs.c" />
+    <ClCompile Include="..\..\tsk\fs\hfs_dent.c" />
+    <ClCompile Include="..\..\tsk\fs\hfs_journal.c" />
+    <ClCompile Include="..\..\tsk\fs\hfs_unicompare.c" />
+    <ClCompile Include="..\..\tsk\fs\icat_lib.c" />
+    <ClCompile Include="..\..\tsk\fs\ifind_lib.c" />
+    <ClCompile Include="..\..\tsk\fs\ils_lib.c" />
+    <ClCompile Include="..\..\tsk\fs\iso9660.c" />
+    <ClCompile Include="..\..\tsk\fs\iso9660_dent.c" />
+    <ClCompile Include="..\..\tsk\fs\nofs_misc.c" />
+    <ClCompile Include="..\..\tsk\fs\ntfs.c" />
+    <ClCompile Include="..\..\tsk\fs\ntfs_dent.cpp" />
+    <ClCompile Include="..\..\tsk\fs\rawfs.c" />
+    <ClCompile Include="..\..\tsk\fs\swapfs.c" />
+    <ClCompile Include="..\..\tsk\fs\unix_misc.c" />
+    <ClCompile Include="..\..\tsk\fs\walk_cpp.cpp" />
+    <ClCompile Include="..\..\tsk\fs\yaffs.cpp" />
+    <ClCompile Include="..\..\tsk\auto\auto.cpp" />
+    <ClCompile Include="..\..\tsk\auto\auto_db.cpp" />
+    <ClCompile Include="..\..\tsk\auto\case_db.cpp" />
+    <ClCompile Include="..\..\tsk\auto\db_sqlite.cpp" />
+    <ClCompile Include="..\..\tsk\auto\sqlite3.c" />
+    <ClCompile Include="..\..\tsk\base\md5c.c" />
+    <ClCompile Include="..\..\tsk\base\mymalloc.c" />
+    <ClCompile Include="..\..\tsk\base\sha1c.c" />
+    <ClCompile Include="..\..\tsk\base\tsk_endian.c" />
+    <ClCompile Include="..\..\tsk\base\tsk_error.c" />
+    <ClCompile Include="..\..\tsk\base\tsk_error_win32.cpp" />
+    <ClCompile Include="..\..\tsk\base\tsk_list.c" />
+    <ClCompile Include="..\..\tsk\base\tsk_lock.c" />
+    <ClCompile Include="..\..\tsk\base\tsk_parse.c" />
+    <ClCompile Include="..\..\tsk\base\tsk_printf.c" />
+    <ClCompile Include="..\..\tsk\base\tsk_stack.c" />
+    <ClCompile Include="..\..\tsk\base\tsk_unicode.c" />
+    <ClCompile Include="..\..\tsk\base\tsk_version.c" />
+    <ClCompile Include="..\..\tsk\base\XGetopt.c" />
+    <ClCompile Include="..\..\tsk\hashdb\encase_index.c" />
+    <ClCompile Include="..\..\tsk\hashdb\hk_index.c" />
+    <ClCompile Include="..\..\tsk\hashdb\idxonly_index.c" />
+    <ClCompile Include="..\..\tsk\hashdb\md5sum_index.c" />
+    <ClCompile Include="..\..\tsk\hashdb\nsrl_index.c" />
+    <ClCompile Include="..\..\tsk\hashdb\tm_lookup.c" />
+    <ClCompile Include="..\..\tsk\img\aff.c" />
+    <ClCompile Include="..\..\tsk\img\ewf.c" />
+    <ClCompile Include="..\..\tsk\img\img_io.c" />
+    <ClCompile Include="..\..\tsk\img\img_open.c" />
+    <ClCompile Include="..\..\tsk\img\img_types.c" />
+    <ClCompile Include="..\..\tsk\img\mult_files.c" />
+    <ClCompile Include="..\..\tsk\img\raw.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\tsk\fs\tsk_exfatfs.h" />
+    <ClInclude Include="..\..\tsk\fs\tsk_fatxxfs.h" />
+    <ClInclude Include="..\..\tsk\vs\tsk_bsd.h" />
+    <ClInclude Include="..\..\tsk\vs\tsk_dos.h" />
+    <ClInclude Include="..\..\tsk\vs\tsk_gpt.h" />
+    <ClInclude Include="..\..\tsk\vs\tsk_mac.h" />
+    <ClInclude Include="..\..\tsk\vs\tsk_sun.h" />
+    <ClInclude Include="..\..\tsk\vs\tsk_vs.h" />
+    <ClInclude Include="..\..\tsk\vs\tsk_vs_i.h" />
+    <ClInclude Include="..\..\tsk\fs\tsk_ext2fs.h" />
+    <ClInclude Include="..\..\tsk\fs\tsk_fatfs.h" />
+    <ClInclude Include="..\..\tsk\fs\tsk_ffs.h" />
+    <ClInclude Include="..\..\tsk\fs\tsk_fs.h" />
+    <ClInclude Include="..\..\tsk\fs\tsk_fs_i.h" />
+    <ClInclude Include="..\..\tsk\fs\tsk_hfs.h" />
+    <ClInclude Include="..\..\tsk\fs\tsk_iso9660.h" />
+    <ClInclude Include="..\..\tsk\fs\tsk_ntfs.h" />
+    <ClInclude Include="..\..\tsk\fs\tsk_yaffs.h" />
+    <ClInclude Include="..\..\tsk\auto\sqlite3.h" />
+    <ClInclude Include="..\..\tsk\auto\tsk_auto.h" />
+    <ClInclude Include="..\..\tsk\auto\tsk_auto_i.h" />
+    <ClInclude Include="..\..\tsk\auto\tsk_case_db.h" />
+    <ClInclude Include="..\..\tsk\auto\tsk_db_sqlite.h" />
+    <ClInclude Include="..\..\tsk\base\tsk_base.h" />
+    <ClInclude Include="..\..\tsk\base\tsk_base_i.h" />
+    <ClInclude Include="..\..\tsk\base\tsk_os.h" />
+    <ClInclude Include="..\..\tsk\hashdb\tsk_hashdb.h" />
+    <ClInclude Include="..\..\tsk\hashdb\tsk_hashdb_i.h" />
+    <ClInclude Include="..\..\tsk\img\aff.h" />
+    <ClInclude Include="..\..\tsk\img\ewf.h" />
+    <ClInclude Include="..\..\tsk\img\raw.h" />
+    <ClInclude Include="..\..\tsk\img\tsk_img.h" />
+    <ClInclude Include="..\..\tsk\img\tsk_img_i.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
 </Project>
\ No newline at end of file
diff --git a/win32/libtsk/libtsk.vcxproj.filters b/win32/libtsk/libtsk.vcxproj.filters
index 45cf06e8a..49f07d1e2 100755
--- a/win32/libtsk/libtsk.vcxproj.filters
+++ b/win32/libtsk/libtsk.vcxproj.filters
@@ -1,399 +1,399 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <Filter Include="vs">
-      <UniqueIdentifier>{ea463a38-dc16-4410-8333-48b75a1916e0}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="fs">
-      <UniqueIdentifier>{ded26c4b-698a-47d7-a44e-fe2cdfb0b3c1}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="auto">
-      <UniqueIdentifier>{29b01bdc-51f0-402a-afcd-5598446ee469}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="base">
-      <UniqueIdentifier>{3f962b73-5e48-4022-b993-f7da3364b7e5}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="hash">
-      <UniqueIdentifier>{9837d797-a638-4895-91a9-dc09de38e8cf}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="img">
-      <UniqueIdentifier>{36ca1943-034a-4915-a0ac-ede5f6e26bfd}</UniqueIdentifier>
-    </Filter>
-  </ItemGroup>
-  <ItemGroup>
-    <ClCompile Include="..\..\tsk\vs\bsd.c">
-      <Filter>vs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\vs\dos.c">
-      <Filter>vs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\vs\gpt.c">
-      <Filter>vs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\vs\mac.c">
-      <Filter>vs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\vs\mm_io.c">
-      <Filter>vs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\vs\mm_open.c">
-      <Filter>vs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\vs\mm_part.c">
-      <Filter>vs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\vs\mm_types.c">
-      <Filter>vs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\vs\sun.c">
-      <Filter>vs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\dcalc_lib.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\dcat_lib.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\dls_lib.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\dstat_lib.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\ext2fs.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\ext2fs_dent.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\ext2fs_journal.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fatfs.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fatfs_meta.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\ffind_lib.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\ffs.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\ffs_dent.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fls_lib.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fs_attr.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fs_attrlist.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fs_block.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fs_dir.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fs_file.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fs_inode.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fs_io.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fs_load.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fs_name.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fs_open.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fs_parse.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fs_types.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\hfs.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\hfs_dent.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\hfs_journal.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\hfs_unicompare.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\icat_lib.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\ifind_lib.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\ils_lib.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\iso9660.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\iso9660_dent.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\nofs_misc.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\rawfs.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\swapfs.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\unix_misc.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\walk_cpp.cpp">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\auto\auto.cpp">
-      <Filter>auto</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\auto\auto_db.cpp">
-      <Filter>auto</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\auto\case_db.cpp">
-      <Filter>auto</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\auto\db_sqlite.cpp">
-      <Filter>auto</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\auto\sqlite3.c">
-      <Filter>auto</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\md5c.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\mymalloc.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\sha1c.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\tsk_endian.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\tsk_error.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\tsk_error_win32.cpp">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\tsk_list.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\tsk_lock.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\tsk_parse.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\tsk_printf.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\tsk_stack.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\tsk_unicode.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\tsk_version.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\base\XGetopt.c">
-      <Filter>base</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\hashdb\encase_index.c">
-      <Filter>hash</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\hashdb\hk_index.c">
-      <Filter>hash</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\hashdb\idxonly_index.c">
-      <Filter>hash</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\hashdb\md5sum_index.c">
-      <Filter>hash</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\hashdb\nsrl_index.c">
-      <Filter>hash</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\hashdb\tm_lookup.c">
-      <Filter>hash</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\img\aff.c">
-      <Filter>img</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\img\ewf.c">
-      <Filter>img</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\img\img_io.c">
-      <Filter>img</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\img\img_open.c">
-      <Filter>img</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\img\img_types.c">
-      <Filter>img</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\img\mult_files.c">
-      <Filter>img</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\img\raw.c">
-      <Filter>img</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fatfs_dent.cpp">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\ntfs.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\ntfs_dent.cpp">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\exfatfs.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fatxxfs.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\exfatfs_meta.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fatxxfs_meta.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fatfs_utils.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\fatxxfs_dent.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\yaffs.cpp">
-      <Filter>fs</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\tsk\fs\exfatfs_dent.c">
-      <Filter>fs</Filter>
-    </ClCompile>
-  </ItemGroup>
-  <ItemGroup>
-    <ClInclude Include="..\..\tsk\vs\tsk_bsd.h">
-      <Filter>vs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\vs\tsk_dos.h">
-      <Filter>vs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\vs\tsk_gpt.h">
-      <Filter>vs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\vs\tsk_mac.h">
-      <Filter>vs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\vs\tsk_sun.h">
-      <Filter>vs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\vs\tsk_vs.h">
-      <Filter>vs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\vs\tsk_vs_i.h">
-      <Filter>vs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\fs\tsk_ext2fs.h">
-      <Filter>fs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\fs\tsk_fatfs.h">
-      <Filter>fs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\fs\tsk_ffs.h">
-      <Filter>fs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\fs\tsk_fs.h">
-      <Filter>fs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\fs\tsk_fs_i.h">
-      <Filter>fs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\fs\tsk_hfs.h">
-      <Filter>fs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\fs\tsk_iso9660.h">
-      <Filter>fs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\fs\tsk_ntfs.h">
-      <Filter>fs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\auto\sqlite3.h">
-      <Filter>auto</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\auto\tsk_auto.h">
-      <Filter>auto</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\auto\tsk_auto_i.h">
-      <Filter>auto</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\auto\tsk_case_db.h">
-      <Filter>auto</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\auto\tsk_db_sqlite.h">
-      <Filter>auto</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\base\tsk_base.h">
-      <Filter>base</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\base\tsk_base_i.h">
-      <Filter>base</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\base\tsk_os.h">
-      <Filter>base</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\hashdb\tsk_hashdb.h">
-      <Filter>hash</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\hashdb\tsk_hashdb_i.h">
-      <Filter>hash</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\img\aff.h">
-      <Filter>img</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\img\ewf.h">
-      <Filter>img</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\img\raw.h">
-      <Filter>img</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\img\tsk_img.h">
-      <Filter>img</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\img\tsk_img_i.h">
-      <Filter>img</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\fs\tsk_exfatfs.h">
-      <Filter>fs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\fs\tsk_fatxxfs.h">
-      <Filter>fs</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\tsk\fs\tsk_yaffs.h">
-      <Filter>fs</Filter>
-    </ClInclude>
-  </ItemGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="vs">
+      <UniqueIdentifier>{ea463a38-dc16-4410-8333-48b75a1916e0}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="fs">
+      <UniqueIdentifier>{ded26c4b-698a-47d7-a44e-fe2cdfb0b3c1}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="auto">
+      <UniqueIdentifier>{29b01bdc-51f0-402a-afcd-5598446ee469}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="base">
+      <UniqueIdentifier>{3f962b73-5e48-4022-b993-f7da3364b7e5}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="hash">
+      <UniqueIdentifier>{9837d797-a638-4895-91a9-dc09de38e8cf}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="img">
+      <UniqueIdentifier>{36ca1943-034a-4915-a0ac-ede5f6e26bfd}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\tsk\vs\bsd.c">
+      <Filter>vs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\vs\dos.c">
+      <Filter>vs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\vs\gpt.c">
+      <Filter>vs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\vs\mac.c">
+      <Filter>vs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\vs\mm_io.c">
+      <Filter>vs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\vs\mm_open.c">
+      <Filter>vs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\vs\mm_part.c">
+      <Filter>vs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\vs\mm_types.c">
+      <Filter>vs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\vs\sun.c">
+      <Filter>vs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\dcalc_lib.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\dcat_lib.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\dls_lib.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\dstat_lib.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\ext2fs.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\ext2fs_dent.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\ext2fs_journal.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fatfs.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fatfs_meta.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\ffind_lib.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\ffs.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\ffs_dent.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fls_lib.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fs_attr.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fs_attrlist.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fs_block.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fs_dir.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fs_file.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fs_inode.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fs_io.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fs_load.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fs_name.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fs_open.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fs_parse.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fs_types.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\hfs.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\hfs_dent.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\hfs_journal.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\hfs_unicompare.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\icat_lib.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\ifind_lib.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\ils_lib.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\iso9660.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\iso9660_dent.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\nofs_misc.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\rawfs.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\swapfs.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\unix_misc.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\walk_cpp.cpp">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\auto\auto.cpp">
+      <Filter>auto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\auto\auto_db.cpp">
+      <Filter>auto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\auto\case_db.cpp">
+      <Filter>auto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\auto\db_sqlite.cpp">
+      <Filter>auto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\auto\sqlite3.c">
+      <Filter>auto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\md5c.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\mymalloc.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\sha1c.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\tsk_endian.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\tsk_error.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\tsk_error_win32.cpp">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\tsk_list.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\tsk_lock.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\tsk_parse.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\tsk_printf.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\tsk_stack.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\tsk_unicode.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\tsk_version.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\base\XGetopt.c">
+      <Filter>base</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\hashdb\encase_index.c">
+      <Filter>hash</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\hashdb\hk_index.c">
+      <Filter>hash</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\hashdb\idxonly_index.c">
+      <Filter>hash</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\hashdb\md5sum_index.c">
+      <Filter>hash</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\hashdb\nsrl_index.c">
+      <Filter>hash</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\hashdb\tm_lookup.c">
+      <Filter>hash</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\img\aff.c">
+      <Filter>img</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\img\ewf.c">
+      <Filter>img</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\img\img_io.c">
+      <Filter>img</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\img\img_open.c">
+      <Filter>img</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\img\img_types.c">
+      <Filter>img</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\img\mult_files.c">
+      <Filter>img</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\img\raw.c">
+      <Filter>img</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fatfs_dent.cpp">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\ntfs.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\ntfs_dent.cpp">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\exfatfs.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fatxxfs.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\exfatfs_meta.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fatxxfs_meta.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fatfs_utils.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\fatxxfs_dent.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\yaffs.cpp">
+      <Filter>fs</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tsk\fs\exfatfs_dent.c">
+      <Filter>fs</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\tsk\vs\tsk_bsd.h">
+      <Filter>vs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\vs\tsk_dos.h">
+      <Filter>vs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\vs\tsk_gpt.h">
+      <Filter>vs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\vs\tsk_mac.h">
+      <Filter>vs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\vs\tsk_sun.h">
+      <Filter>vs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\vs\tsk_vs.h">
+      <Filter>vs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\vs\tsk_vs_i.h">
+      <Filter>vs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\fs\tsk_ext2fs.h">
+      <Filter>fs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\fs\tsk_fatfs.h">
+      <Filter>fs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\fs\tsk_ffs.h">
+      <Filter>fs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\fs\tsk_fs.h">
+      <Filter>fs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\fs\tsk_fs_i.h">
+      <Filter>fs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\fs\tsk_hfs.h">
+      <Filter>fs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\fs\tsk_iso9660.h">
+      <Filter>fs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\fs\tsk_ntfs.h">
+      <Filter>fs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\auto\sqlite3.h">
+      <Filter>auto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\auto\tsk_auto.h">
+      <Filter>auto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\auto\tsk_auto_i.h">
+      <Filter>auto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\auto\tsk_case_db.h">
+      <Filter>auto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\auto\tsk_db_sqlite.h">
+      <Filter>auto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\base\tsk_base.h">
+      <Filter>base</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\base\tsk_base_i.h">
+      <Filter>base</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\base\tsk_os.h">
+      <Filter>base</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\hashdb\tsk_hashdb.h">
+      <Filter>hash</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\hashdb\tsk_hashdb_i.h">
+      <Filter>hash</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\img\aff.h">
+      <Filter>img</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\img\ewf.h">
+      <Filter>img</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\img\raw.h">
+      <Filter>img</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\img\tsk_img.h">
+      <Filter>img</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\img\tsk_img_i.h">
+      <Filter>img</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\fs\tsk_exfatfs.h">
+      <Filter>fs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\fs\tsk_fatxxfs.h">
+      <Filter>fs</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tsk\fs\tsk_yaffs.h">
+      <Filter>fs</Filter>
+    </ClInclude>
+  </ItemGroup>
 </Project>
\ No newline at end of file
-- 
GitLab