Exemple #1
0
/*
 * Class:     edu_uw_apl_commons_tsk4j_filesys_FileSystem
 * Method:    dirOpenMeta
 * Signature: (JJ)Ledu/uw/apl/commons/tsk4j/filesys/Directory;
 */
JNIEXPORT jobject JNICALL 
Java_edu_uw_apl_commons_tsk4j_filesys_FileSystem_dirOpenMeta
(JNIEnv *env, jobject thiz, jlong nativePtr, jlong metadataAddr ) {

  TSK_FS_INFO* info = (TSK_FS_INFO*)nativePtr;
  TSK_FS_DIR* fsDir = tsk_fs_dir_open_meta( info, (TSK_INUM_T)metadataAddr );
  if( fsDir == NULL )
	return (jobject)NULL;
  TSK_FS_FILE* fsFile = fsDir->fs_file;

  jobject fileMeta = NULL;
  if( fsFile->meta ) {
	fileMeta = createFileMeta( env, fsFile->meta );
	if( !fileMeta ) {
	  tsk_fs_dir_close( fsDir );
	  return NULL;
	}
  }

  jobject fileName = NULL;
  if( fsFile->name ) {
	fileName = createFileName( env, fsFile->name );
	if( !fileName ) {
	  tsk_fs_dir_close( fsDir );
	  // LOOK: release fileMeta ????
	  return NULL;
	}
  }

  jobject file = createFile( env, fsFile, thiz, fileMeta, fileName ); 
  if( !file ) {
	  tsk_fs_dir_close( fsDir );
	  // LOOK: release fileMeta, fileName ????
	  return NULL;
  }
  
  jobject result = createDirectory( env, fsDir, thiz, file );
  if( !result ) {
	  tsk_fs_dir_close( fsDir );
	  // LOOK: release fileMeta, fileName, file ????
	  return NULL;
  }
  return result;
}
Exemple #2
0
/** \ingroup fslib
* Open a directory (using its path) so that each of the files in it can be accessed.
* @param a_fs File system to analyze
* @param a_dir Path of the directory to open
* @returns NULL on error
*/
TSK_FS_DIR *
tsk_fs_dir_open(TSK_FS_INFO * a_fs, const char *a_dir)
{
    TSK_INUM_T inum;
    int8_t retval;
    TSK_FS_DIR *fs_dir;
    TSK_FS_NAME *fs_name;

    if ((a_fs == NULL) || (a_fs->tag != TSK_FS_INFO_TAG)) {
        tsk_error_set_errno(TSK_ERR_FS_ARG);
        tsk_error_set_errstr
            ("tsk_fs_dir_open: called with NULL or unallocated structures");
        return NULL;
    }

    // allocate a structure to store the name in
    if ((fs_name = tsk_fs_name_alloc(128, 32)) == NULL) {
        return NULL;
    }

    retval = tsk_fs_path2inum(a_fs, a_dir, &inum, fs_name);
    if (retval == -1) {
        tsk_fs_name_free(fs_name);
        return NULL;
    }
    else if (retval == 1) {
        tsk_error_set_errno(TSK_ERR_FS_ARG);
        tsk_error_set_errstr("tsk_fs_dir_open: path not found: %s", a_dir);
        tsk_fs_name_free(fs_name);
        return NULL;
    }

    fs_dir = tsk_fs_dir_open_meta(a_fs, inum);

    // add the name structure on to it
    if ((fs_dir) && (fs_dir->fs_file))
        fs_dir->fs_file->name = fs_name;

    return fs_dir;
}
Exemple #3
0
/* dir_walk local function that is used for recursive calls.  Callers
 * should initially call the non-local version. */
static TSK_WALK_RET_ENUM
tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo,
    TSK_INUM_T a_addr, TSK_FS_DIR_WALK_FLAG_ENUM a_flags,
    TSK_FS_DIR_WALK_CB a_action, void *a_ptr)
{
    TSK_FS_DIR *fs_dir;
    TSK_FS_FILE *fs_file;
    size_t i;

    // get the list of entries in the directory
    if ((fs_dir = tsk_fs_dir_open_meta(a_fs, a_addr)) == NULL) {
        return TSK_WALK_ERROR;
    }

    /* Allocate a file structure for the callbacks.  We
     * will allocate fs_meta structures as needed and
     * point into the fs_dir structure for the names. */
    if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) {
        tsk_fs_dir_close(fs_dir);
        return TSK_WALK_ERROR;
    }

    for (i = 0; i < fs_dir->names_used; i++) {
        TSK_WALK_RET_ENUM retval;

        /* Point name to the buffer of names.  We need to be
         * careful about resetting this before we free fs_file */
        fs_file->name = (TSK_FS_NAME *) & fs_dir->names[i];

        /* load the fs_meta structure if possible.
         * Must have non-zero inode addr or have allocated name (if inode is 0) */
        if (((fs_file->name->meta_addr)
                || (fs_file->name->flags & TSK_FS_NAME_FLAG_ALLOC))) {
            if (a_fs->file_add_meta(a_fs, fs_file,
                    fs_file->name->meta_addr)) {
                if (tsk_verbose)
                    tsk_error_print(stderr);
                tsk_error_reset();
            }
        }

        // call the action if we have the right flags.
        if ((fs_file->name->flags & a_flags) == fs_file->name->flags) {
            retval = a_action(fs_file, a_dinfo->dirs, a_ptr);
            if (retval == TSK_WALK_STOP) {
                tsk_fs_dir_close(fs_dir);
                fs_file->name = NULL;
                tsk_fs_file_close(fs_file);

                /* free the list -- fs_dir_walk has no way
                 * of knowing that we stopped early w/out error.
                 */
                if (a_dinfo->save_inum_named) {
                    tsk_list_free(a_dinfo->list_inum_named);
                    a_dinfo->list_inum_named = NULL;
                    a_dinfo->save_inum_named = 0;
                }
                return TSK_WALK_STOP;
            }
            else if (retval == TSK_WALK_ERROR) {
                tsk_fs_dir_close(fs_dir);
                fs_file->name = NULL;
                tsk_fs_file_close(fs_file);
                return TSK_WALK_ERROR;
            }
        }

        // save the inode info for orphan finding - if requested
        if ((a_dinfo->save_inum_named) && (fs_file->meta)
            && (fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)) {

            if (tsk_list_add(&a_dinfo->list_inum_named,
                    fs_file->meta->addr)) {

                // if there is an error, then clear the list
                tsk_list_free(a_dinfo->list_inum_named);
                a_dinfo->list_inum_named = NULL;
                a_dinfo->save_inum_named = 0;
            }
        }

        /* Recurse into a directory if:
         * - Both dir entry and inode have DIR type (or name is undefined)
         * - Recurse flag is set
         * - dir entry is allocated OR both are unallocated
         * - not one of the '.' or '..' entries
         * - A Non-Orphan Dir or the Orphan Dir with the NOORPHAN flag not set.
         */
        if (((fs_file->name->type == TSK_FS_NAME_TYPE_DIR)
                || (fs_file->name->type == TSK_FS_NAME_TYPE_UNDEF))
            && (fs_file->meta)
            && (fs_file->meta->type == TSK_FS_META_TYPE_DIR)
            && (a_flags & TSK_FS_DIR_WALK_FLAG_RECURSE)
            && ((fs_file->name->flags & TSK_FS_NAME_FLAG_ALLOC)
                || ((fs_file->name->flags & TSK_FS_NAME_FLAG_UNALLOC)
                    && (fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC))
            )
            && (!TSK_FS_ISDOT(fs_file->name->name))
            && ((fs_file->name->meta_addr != TSK_FS_ORPHANDIR_INUM(a_fs))
                || ((a_flags & TSK_FS_DIR_WALK_FLAG_NOORPHAN) == 0))
            ) {

            /* Make sure we do not get into an infinite loop */
            if (0 == tsk_stack_find(a_dinfo->stack_seen,
                    fs_file->name->meta_addr)) {
                int depth_added = 0;
                uint8_t save_bak = 0;

                if (tsk_stack_push(a_dinfo->stack_seen,
                        fs_file->name->meta_addr)) {
                    tsk_fs_dir_close(fs_dir);
                    fs_file->name = NULL;
                    tsk_fs_file_close(fs_file);
                    return TSK_WALK_ERROR;
                }

                if ((a_dinfo->depth < MAX_DEPTH) &&
                    (DIR_STRSZ >
                        strlen(a_dinfo->dirs) +
                        strlen(fs_file->name->name))) {
                    a_dinfo->didx[a_dinfo->depth] =
                        &a_dinfo->dirs[strlen(a_dinfo->dirs)];
                    strncpy(a_dinfo->didx[a_dinfo->depth],
                        fs_file->name->name,
                        DIR_STRSZ - strlen(a_dinfo->dirs));
                    strncat(a_dinfo->dirs, "/", DIR_STRSZ);
                    depth_added = 1;
                }
                a_dinfo->depth++;

                /* We do not want to save info about named unalloc files
                 * when we go into the Orphan directory (because then we have
                 * no orphans).  So, disable it for this recursion.
                 */
                if (fs_file->name->meta_addr ==
                    TSK_FS_ORPHANDIR_INUM(a_fs)) {
                    save_bak = a_dinfo->save_inum_named;
                    a_dinfo->save_inum_named = 0;
                }
                retval = tsk_fs_dir_walk_lcl(a_fs,
                    a_dinfo, fs_file->name->meta_addr, a_flags,
                    a_action, a_ptr);
                if (retval == TSK_WALK_ERROR) {
                    /* If this fails because the directory could not be
                     * loaded, then we still continue */
                    if (tsk_verbose) {
                        tsk_fprintf(stderr,
                            "tsk_fs_dir_walk_lcl: error reading directory: %"
                            PRIuINUM "\n", fs_file->name->meta_addr);
                        tsk_error_print(stderr);
                    }

                    tsk_error_reset();
                }
                else if (retval == TSK_WALK_STOP) {
                    tsk_fs_dir_close(fs_dir);
                    fs_file->name = NULL;
                    tsk_fs_file_close(fs_file);
                    return TSK_WALK_STOP;
                }

                // reset the save status
                if (fs_file->name->meta_addr ==
                    TSK_FS_ORPHANDIR_INUM(a_fs)) {
                    a_dinfo->save_inum_named = save_bak;
                }

                tsk_stack_pop(a_dinfo->stack_seen);
                a_dinfo->depth--;
                if (depth_added)
                    *a_dinfo->didx[a_dinfo->depth] = '\0';
            }
            else {
                if (tsk_verbose)
                    fprintf(stderr,
                        "tsk_fs_dir_walk_lcl: Loop detected with address %"
                        PRIuINUM, fs_file->name->meta_addr);
            }
        }

        // remove the pointer to name buffer
        fs_file->name = NULL;

        // free the metadata if we allocated it
        if (fs_file->meta) {
            tsk_fs_meta_close(fs_file->meta);
            fs_file->meta = NULL;
        }
    }

    tsk_fs_dir_close(fs_dir);
    fs_file->name = NULL;
    tsk_fs_file_close(fs_file);
    return TSK_WALK_CONT;
}
/**
 * Open a directory and cycle through its contents.  Read each file and recurse
 * into each directory.
 *
 * @param fs_info File system to process
 * @param stack Stack to prevent infinite recursion loops
 * @param dir_inum Metadata address of directory to open
 * @param path Path of directory being opened
 * @returns 1 on error
 */
static uint8_t
proc_dir(TSK_FS_INFO * fs_info, TSK_STACK * stack,
    TSK_INUM_T dir_inum, const char *path)
{
    TSK_FS_DIR *fs_dir;
    size_t i;
    char *path2 = NULL;
    char *buf = NULL;

    // open the directory
    if ((fs_dir = tsk_fs_dir_open_meta(fs_info, dir_inum)) == NULL) {
        fprintf(stderr, "Error opening directory: %" PRIuINUM "\n",
            dir_inum);
        tsk_error_print(stderr);
        return 1;
    }

    /* These should be dynamic lengths, but this is just a sample program.
     * Allocate heap space instead of stack to prevent overflow for deep
     * directories. */
    if ((path2 = (char *) malloc(4096)) == NULL) {
        return 1;
    }

    if ((buf = (char *) malloc(2048)) == NULL) {
        free(path2);
        return 1;
    }

    // cycle through each entry
    for (i = 0; i < tsk_fs_dir_getsize(fs_dir); i++) {
        TSK_FS_FILE *fs_file;
        TSK_OFF_T off = 0;
        size_t len = 0;

        // get the entry
        if ((fs_file = tsk_fs_dir_get(fs_dir, i)) == NULL) {
            fprintf(stderr,
                "Error getting directory entry %" PRIuSIZE
                " in directory %" PRIuINUM "\n", i, dir_inum);
            tsk_error_print(stderr);

            free(path2);
            free(buf);
            return 1;
        }

        /* Ignore NTFS System files */
        if ((TSK_FS_TYPE_ISNTFS(fs_file->fs_info->ftype)) &&
            (fs_file->name->name[0] == '$')) {
            tsk_fs_file_close(fs_file);
            continue;
        }

        //printf("Processing %s/%s\n", path, fs_file->name->name);

        // make sure it's got metadata and not only a name
        if (fs_file->meta) {
            ssize_t cnt;

            /* Note that we could also cycle through all of the attributes in the
             * file by using one of the tsk_fs_attr_get() functions and reading it
             * with tsk_fs_attr_read().  See the File Systems section of the Library
             * User's Guide for more details: 
             * http://www.sleuthkit.org/sleuthkit/docs/api-docs/ */
            
            // read file contents
            if (fs_file->meta->type == TSK_FS_META_TYPE_REG) {
                int myflags = 0;

                for (off = 0; off < fs_file->meta->size; off += len) {
                    if (fs_file->meta->size - off < 2048)
                        len = (size_t) (fs_file->meta->size - off);
                    else
                        len = 2048;

                    cnt = tsk_fs_file_read(fs_file, off, buf, len,
                        (TSK_FS_FILE_READ_FLAG_ENUM) myflags);
                    if (cnt == -1) {
                        // could check tsk_errno here for a recovery error (TSK_ERR_FS_RECOVER)
                        fprintf(stderr, "Error reading %s file: %s\n",
                            ((fs_file->name->
                                    flags & TSK_FS_NAME_FLAG_UNALLOC)
                                || (fs_file->meta->
                                    flags & TSK_FS_META_FLAG_UNALLOC)) ?
                            "unallocated" : "allocated",
                            fs_file->name->name);
                        tsk_error_print(stderr);
                        break;
                    }
                    else if (cnt != (ssize_t) len) {
                        fprintf(stderr,
                            "Warning: %" PRIuSIZE " of %" PRIuSIZE
                            " bytes read from %s file %s\n", cnt, len,
                            ((fs_file->name->
                                    flags & TSK_FS_NAME_FLAG_UNALLOC)
                                || (fs_file->meta->
                                    flags & TSK_FS_META_FLAG_UNALLOC)) ?
                            "unallocated" : "allocated",
                            fs_file->name->name);
                    }

                    // do something with the data...
                }
            }

            // recurse into another directory (unless it is a '.' or '..')
            else if (fs_file->meta->type == TSK_FS_META_TYPE_DIR) {
                if (TSK_FS_ISDOT(fs_file->name->name) == 0) {

                    // only go in if it is not on our stack
                    if (tsk_stack_find(stack, fs_file->meta->addr) == 0) {
                        // add the address to the top of the stack
                        tsk_stack_push(stack, fs_file->meta->addr);

                        snprintf(path2, 4096, "%s/%s", path,
                            fs_file->name->name);
                        if (proc_dir(fs_info, stack, fs_file->meta->addr,
                                path2)) {
                            tsk_fs_file_close(fs_file);
                            tsk_fs_dir_close(fs_dir);
                            free(path2);
                            free(buf);
                            return 1;
                        }

                        // pop the address
                        tsk_stack_pop(stack);
                    }
                }
            }
        }
        tsk_fs_file_close(fs_file);
    }
    tsk_fs_dir_close(fs_dir);

    free(path2);
    free(buf);
    return 0;
}
Exemple #5
0
/**
 * \ingroup fslib
 *
 * Find the meta data address for a given file name (UTF-8).
 * The basic idea of the function is to break the given name into its
 * subdirectories and start looking for each (starting in the root
 * directory).
 *
 * @param a_fs FS to analyze
 * @param a_path UTF-8 path of file to search for
 * @param [out] a_result Meta data address of file
 * @param [out] a_fs_name Copy of name details (or NULL if details not wanted)
 * @returns -1 on (system) error, 0 if found, and 1 if not found
 */
int8_t
tsk_fs_path2inum(TSK_FS_INFO * a_fs, const char *a_path,
    TSK_INUM_T * a_result, TSK_FS_NAME * a_fs_name)
{
    char *cpath;
    size_t clen;
    char *cur_dir;              // The "current" directory or file we are looking for
    char *cur_attr;             // The "current" attribute of the dir we are looking for
    TSK_INUM_T next_meta;
    uint8_t is_done;
    char *strtok_last;
    *a_result = 0;

    // copy path to a buffer that we can modify
    clen = strlen(a_path) + 1;
    if ((cpath = (char *) tsk_malloc(clen)) == NULL) {
        return -1;
    }
    strncpy(cpath, a_path, clen);

    // Get the first part of the directory path.
    cur_dir = (char *) strtok_r(cpath, "/", &strtok_last);
    cur_attr = NULL;

    /* If there is no token, then only a '/' was given */
    if (cur_dir == NULL) {
        free(cpath);
        *a_result = a_fs->root_inum;

        // create the dummy entry if needed
        if (a_fs_name) {
            a_fs_name->meta_addr = a_fs->root_inum;
            // Note that we are not filling in meta_seq -- we could, we just aren't

            a_fs_name->type = TSK_FS_NAME_TYPE_DIR;
            a_fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
            if (a_fs_name->name)
                a_fs_name->name[0] = '\0';
            if (a_fs_name->shrt_name)
                a_fs_name->shrt_name[0] = '\0';
        }
        return 0;
    }

    /* If this is NTFS, separate out the attribute of the current directory */
    if (TSK_FS_TYPE_ISNTFS(a_fs->ftype)
        && ((cur_attr = strchr(cur_dir, ':')) != NULL)) {
        *(cur_attr) = '\0';
        cur_attr++;
    }

    if (tsk_verbose)
        tsk_fprintf(stderr, "Looking for %s\n", cur_dir);

    // initialize the first place to look, the root dir
    next_meta = a_fs->root_inum;

    // we loop until we know the outcome and then exit.
    // everything should return from inside the loop.
    is_done = 0;
    while (is_done == 0) {
        size_t i;
        TSK_FS_FILE *fs_file_alloc = NULL;      // set to the allocated file that is our target
        TSK_FS_FILE *fs_file_del = NULL;        // set to an unallocated file that matches our criteria

        TSK_FS_DIR *fs_dir = NULL;

        // open the next directory in the recursion
        if ((fs_dir = tsk_fs_dir_open_meta(a_fs, next_meta)) == NULL) {
            free(cpath);
            return -1;
        }

        /* Verify this is indeed a directory.  We had one reported
         * problem where a file was a disk image and opening it as
         * a directory found the directory entries inside of the file
         * and this caused problems... */
        if ( !TSK_FS_IS_DIR_META(fs_dir->fs_file->meta->type)) {
            tsk_error_reset();
            tsk_error_set_errno(TSK_ERR_FS_GENFS);
            tsk_error_set_errstr("Address %" PRIuINUM
                " is not for a directory\n", next_meta);
            free(cpath);
            return -1;
        }

        // cycle through each entry
        for (i = 0; i < tsk_fs_dir_getsize(fs_dir); i++) {

            TSK_FS_FILE *fs_file;
            uint8_t found_name = 0;

            if ((fs_file = tsk_fs_dir_get(fs_dir, i)) == NULL) {
                tsk_fs_dir_close(fs_dir);
                free(cpath);
                return -1;
            }

            /*
             * Check if this is the name that we are currently looking for,
             * as identified in 'cur_dir'
             */
            if ((fs_file->name->name)
                && (a_fs->name_cmp(a_fs, fs_file->name->name,
                        cur_dir) == 0)) {
                found_name = 1;
            }
            else if ((fs_file->name->shrt_name)
                && (a_fs->name_cmp(a_fs, fs_file->name->shrt_name,
                        cur_dir) == 0)) {
                found_name = 1;
            }

            /* For NTFS, we have to check the attribute name. */
            if ((found_name == 1) && (TSK_FS_TYPE_ISNTFS(a_fs->ftype))) {
                /*  ensure we have the right attribute name */
                if (cur_attr != NULL) {
                    found_name = 0;
                    if (fs_file->meta) {
                        int cnt, i;

                        // cycle through the attributes
                        cnt = tsk_fs_file_attr_getsize(fs_file);
                        for (i = 0; i < cnt; i++) {
                            const TSK_FS_ATTR *fs_attr =
                                tsk_fs_file_attr_get_idx(fs_file, i);
                            if (!fs_attr)
                                continue;

                            if ((fs_attr->name)
                                && (a_fs->name_cmp(a_fs, fs_attr->name,
                                        cur_attr) == 0)) {
                                found_name = 1;
                                break;
                            }
                        }
                    }
                }
            }

            if (found_name) {
                /* If we found our file and it is allocated, then stop. If
                 * it is unallocated, keep on going to see if we can get
                 * an allocated hit */
                if (fs_file->name->flags & TSK_FS_NAME_FLAG_ALLOC) {
                    fs_file_alloc = fs_file;
                    break;
                }
                else {
                    // if we already have an unalloc and its addr is 0, then use the new one
                    if ((fs_file_del)
                        && (fs_file_del->name->meta_addr == 0)) {
                        tsk_fs_file_close(fs_file_del);
                    }
                    fs_file_del = fs_file;
                }
            }
            // close the file if we did not save it for future analysis.
            else {
                tsk_fs_file_close(fs_file);
                fs_file = NULL;
            }
        }

        // we found a directory, go into it
        if ((fs_file_alloc) || (fs_file_del)) {

            const char *pname;
            TSK_FS_FILE *fs_file_tmp;

            // choose the alloc one first (if they both exist)
            if (fs_file_alloc)
                fs_file_tmp = fs_file_alloc;
            else
                fs_file_tmp = fs_file_del;

            pname = cur_dir;    // save a copy of the current name pointer

            // advance to the next name
            cur_dir = (char *) strtok_r(NULL, "/", &(strtok_last));
            cur_attr = NULL;

            if (tsk_verbose)
                tsk_fprintf(stderr,
                    "Found it (%s), now looking for %s\n", pname, cur_dir);

            /* That was the last name in the path -- we found the file! */
            if (cur_dir == NULL) {
                *a_result = fs_file_tmp->name->meta_addr;

                // make a copy if one was requested
                if (a_fs_name) {
                    tsk_fs_name_copy(a_fs_name, fs_file_tmp->name);
                }

                if (fs_file_alloc)
                    tsk_fs_file_close(fs_file_alloc);
                if (fs_file_del)
                    tsk_fs_file_close(fs_file_del);

                tsk_fs_dir_close(fs_dir);
                free(cpath);
                return 0;
            }

            // update the attribute field, if needed
            if (TSK_FS_TYPE_ISNTFS(a_fs->ftype)
                && ((cur_attr = strchr(cur_dir, ':')) != NULL)) {
                *(cur_attr) = '\0';
                cur_attr++;
            }

            // update the value for the next directory to open
            next_meta = fs_file_tmp->name->meta_addr;

            if (fs_file_alloc) {
                tsk_fs_file_close(fs_file_alloc);
                fs_file_alloc = NULL;
            }
            if (fs_file_del) {
                tsk_fs_file_close(fs_file_del);
                fs_file_del = NULL;
            }
        }

        // no hit in directory
        else {
            is_done = 1;
        }

        tsk_fs_dir_close(fs_dir);
        fs_dir = NULL;
    }

    free(cpath);
    return 1;
}
Exemple #6
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;
    TSK_DADDR_T sectors = 0;
	TSK_FS_DIR * test_dir1; // Directories used to try opening the root directory

    // 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 beginning 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;
	        ssize_t cnt = 0;

            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 != NULL && test_dir1->names_used <= 4){ // At most four automatic directories ($MBR, $FAT1, $FAT1, $OrphanFiles)
	    TSK_FS_DIR * test_dir2; //  to see if it's the Android FAT version

		fatfs->subtype = TSK_FATFS_SUBTYPE_ANDROID_1;
		test_dir2 = tsk_fs_dir_open_meta(fs, fs->root_inum);

		if (test_dir2 != NULL && 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;
}
/* Compare the differences between dir_open_meta and dir_open 
 * @param a_path Path of directory to open
 * @param a_addr The metadata address of the same directory as the path
 * @returns 1 if a test failed
 */
static int
test_dir_open_apis(TSK_FS_INFO * a_fs, const char *a_path,
    TSK_INUM_T a_addr)
{
    TSK_FS_DIR *fs_dir_m;
    TSK_FS_DIR *fs_dir_p;
    TSK_FS_DIR *fs_dir_tmp;
    TSK_FS_FILE *fs_file_m;
    TSK_FS_FILE *fs_file_p;
    int retval = 0;
    size_t entry = 0;

    // open via inode addr
    fs_dir_m = tsk_fs_dir_open_meta(a_fs, a_addr);
    if (!fs_dir_m) {
        fprintf(stderr, "Error opening dir %" PRIuINUM " via meta\n",
            a_addr);
        tsk_error_print(stderr);
        return 1;
    }

    /* open the root directory to throw some more state into the system
     * in case data is cached from first call */
    fs_dir_tmp = tsk_fs_dir_open_meta(a_fs, a_fs->root_inum);
    if (!fs_dir_tmp) {
        fprintf(stderr, "Error opening root directory via meta\n");
        tsk_error_print(stderr);
        return 1;
    }

    // open via path
    fs_dir_p = tsk_fs_dir_open(a_fs, a_path);
    if (!fs_dir_p) {
        fprintf(stderr, "Error opening directory %s\n", a_path);
        tsk_error_print(stderr);
        return 1;
    }

    // test that path has the name structure set (correctly)
    if ((fs_dir_p->fs_file == NULL) || (fs_dir_p->fs_file->name == NULL)) {
        fprintf(stderr, "dir opened via path has null name (%s)\n",
            a_path);
        retval = 1;
        goto open_cleanup;
    }

    if (fs_dir_p->fs_file->name->meta_addr !=
        fs_dir_p->fs_file->meta->addr) {
        fprintf(stderr,
            "dir opened via path has different meta addresses in name and meta (%s) (%"
            PRIuINUM " vs %" PRIuINUM "\n", a_path,
            fs_dir_p->fs_file->name->meta_addr,
            fs_dir_p->fs_file->meta->addr);
        retval = 1;
        goto open_cleanup;
    }

    // verify both methods have same dir addr
    if (fs_dir_p->fs_file->meta->addr != fs_dir_m->fs_file->meta->addr) {
        fprintf(stderr,
            "parent dir addrs from fs_dir_open_meta and via path are different: %"
            PRIuINUM " vs %" PRIuINUM " (%s - %" PRIuINUM "\n",
            fs_dir_p->fs_file->meta->addr, fs_dir_m->fs_file->meta->addr,
            a_path, a_addr);
        retval = 1;
        goto open_cleanup;
    }

    // verify path method has same dir addr as open via meta
    if (fs_dir_p->fs_file->meta->addr != a_addr) {
        fprintf(stderr,
            "parent dir addrs from fs_dir_open is diff from meta address %"
            PRIuINUM " (%s - %" PRIuINUM "\n",
            fs_dir_p->fs_file->meta->addr, a_path, a_addr);
        retval = 1;
        goto open_cleanup;
    }

    // verify both have same size
    if (tsk_fs_dir_getsize(fs_dir_p) != tsk_fs_dir_getsize(fs_dir_m)) {
        fprintf(stderr,
            "sizes from fs_dir_open_meta and via path are different: %"
            PRIuSIZE " vs %" PRIuSIZE " (%s - %" PRIuINUM "\n",
            tsk_fs_dir_getsize(fs_dir_p), tsk_fs_dir_getsize(fs_dir_m),
            a_path, a_addr);
        retval = 1;
        goto open_cleanup;
    }


    // compare the first entry in both. 
    if (tsk_fs_dir_getsize(fs_dir_p) == 0) {
        fprintf(stderr, "directory sizes are 0\n");
        retval = 1;
        goto open_cleanup;
    }

    fs_file_m = tsk_fs_dir_get(fs_dir_m, 0);
    if (fs_file_m == NULL) {
        fprintf(stderr,
            "Error opening entry 0 from meta open: %" PRIuINUM "\n",
            a_addr);
        tsk_error_print(stderr);
        retval = 1;
        goto open_cleanup;
    }

    fs_file_p = tsk_fs_dir_get(fs_dir_p, 0);
    if (fs_file_p == NULL) {
        fprintf(stderr,
            "Error opening entry 0 from path open: %" PRIuINUM "\n",
            a_addr);
        tsk_error_print(stderr);
        retval = 1;
        goto open_cleanup;
    }

    if (compare_names(fs_file_p->name, fs_file_m->name, 1)) {
        fprintf(stderr, "results from entry 0 are different\n");
        retval = 1;
        goto open_cleanup;
    }
    tsk_fs_file_close(fs_file_m);
    tsk_fs_file_close(fs_file_p);


    // compare the last entry in both
    entry = tsk_fs_dir_getsize(fs_dir_m) - 1;
    fs_file_m = tsk_fs_dir_get(fs_dir_m, entry);
    if (fs_file_m == NULL) {
        fprintf(stderr,
            "Error opening entry %" PRIuSIZE " from meta open: %" PRIuINUM
            "\n", entry, a_addr);
        tsk_error_print(stderr);
        retval = 1;
        goto open_cleanup;
    }

    fs_file_p = tsk_fs_dir_get(fs_dir_p, entry);
    if (fs_file_p == NULL) {
        fprintf(stderr,
            "Error opening entry %" PRIuSIZE " from path open: %" PRIuINUM
            "\n", entry, a_addr);
        tsk_error_print(stderr);
        retval = 1;
        goto open_cleanup;
    }

    if (compare_names(fs_file_p->name, fs_file_m->name, 1)) {
        fprintf(stderr, "results from entry %" PRIuSIZE " are different\n",
            entry);
        retval = 1;
        goto open_cleanup;
    }
    tsk_fs_file_close(fs_file_m);
    tsk_fs_file_close(fs_file_p);


  open_cleanup:
    tsk_fs_dir_close(fs_dir_p);
    tsk_fs_dir_close(fs_dir_tmp);
    tsk_fs_dir_close(fs_dir_m);

    return retval;
}
/* Test function that compares the dir_open/dir_get() APIs
 * with the dir_walk results
 * @param a_addr Address of directory to analyze
 * @returns 1 if a test failed.
 */
static int
test_walk_apis(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr)
{
    TSK_FS_DIR *fs_dir;
    int retval = 0;

    fs_dir = tsk_fs_dir_open_meta(a_fs, a_addr);
    if (!fs_dir) {
        fprintf(stderr, "Error opening dir %" PRIuINUM " via meta\n",
            a_addr);
        tsk_error_print(stderr);
        return 1;
    }

    // verify they have the same number of entries
    // walk the directory to get its count
    size_t walk_size = 0;
    if (tsk_fs_dir_walk(a_fs, a_addr, (TSK_FS_DIR_WALK_FLAG_ENUM) 0,
            dir_walk_count_cb, &walk_size)) {
        fprintf(stderr, "Error doing dent walk on dir %" PRIuINUM "\n",
            a_addr);
        retval = 1;
        goto walk_cleanup;
    }

    if (walk_size != tsk_fs_dir_getsize(fs_dir)) {
        fprintf(stderr,
            "Size returned by dir_walk different from dir_getsize: %"
            PRIuINUM ": %" PRIuSIZE " %" PRIuSIZE "\n", a_addr, walk_size,
            tsk_fs_dir_getsize(fs_dir));
        retval = 1;
        goto walk_cleanup;
    }

    // verify each entry is the same
    for (size_t i = 0; i < tsk_fs_dir_getsize(fs_dir); i++) {
        TSK_FS_FILE *fs_file;

        fs_file = tsk_fs_dir_get(fs_dir, i);
        if (fs_file == NULL) {
            fprintf(stderr,
                "Error getting entry %" PRIuSIZE " from directory %"
                PRIuINUM "\n", i, a_addr);
            tsk_error_print(stderr);
            retval = 1;
            goto walk_cleanup;
        }
        if (fs_file->meta == NULL) {
            fprintf(stderr,
                "Error: %s (%" PRIuINUM
                ") does not have meta set in dir: \n", fs_file->name->name,
                fs_file->name->meta_addr);
            retval = 1;
            goto walk_cleanup;
        }

        s_found = 0;
        if (tsk_fs_dir_walk(a_fs, a_addr, (TSK_FS_DIR_WALK_FLAG_ENUM) 0,
                dir_walk_comp_cb, (void *) fs_file)) {
            fprintf(stderr, "Error doing dent walk on dir %" PRIuINUM "\n",
                a_addr);
            retval = 1;
            goto walk_cleanup;
        }
        if (s_found == 0) {
            fprintf(stderr,
                "entry %" PRIuSIZE " in dir not found via walk: %s\n", i,
                fs_file->name->name);
            retval = 1;
            goto walk_cleanup;
        }
        tsk_fs_file_close(fs_file);
    }

  walk_cleanup:
    tsk_fs_dir_close(fs_dir);
    return retval;
}