Esempio n. 1
0
/*
 * Parse a V 2.0 USN record.
 * Returns 0 on success, 1 otherwise
 */
static uint8_t
parse_v2_record(const unsigned char *buf, TSK_USN_RECORD_HEADER *header,
                TSK_USN_RECORD_V2 *record, TSK_ENDIAN_ENUM endian)
{
    uint64_t timestamp = 0;
    uint16_t name_offset = 0, name_length = 0;

    record->refnum = tsk_getu48(endian, &buf[8]);
    record->refnum_seq = tsk_getu16(endian, &buf[14]);
    record->parent_refnum = tsk_getu48(endian, &buf[16]);
    record->parent_refnum_seq = tsk_getu16(endian, &buf[22]);
    record->usn = tsk_getu64(endian, &buf[24]);

    /* Convert NT timestamp into Unix */
    timestamp = tsk_getu64(endian, &buf[32]);
    record->time_sec = nt2unixtime(timestamp);
    record->time_nsec = nt2nano(timestamp);

    record->reason = tsk_getu32(endian, &buf[40]);
    record->source_info = tsk_getu32(endian, &buf[44]);
    record->security = tsk_getu32(endian, &buf[48]);
    record->attributes = tsk_getu32(endian, &buf[52]);

    /* Extract file name */
    name_length = tsk_getu16(endian, &buf[56]);
    name_offset = tsk_getu16(endian, &buf[58]);

    return parse_fname(&buf[name_offset], name_length, record, endian);
}
Esempio n. 2
0
static uint8_t
ntfs_dent_copy(NTFS_INFO * ntfs, ntfs_idxentry * idxe,
    TSK_FS_NAME * fs_name)
{
    ntfs_attr_fname *fname = (ntfs_attr_fname *) & idxe->stream;
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info;
    UTF16 *name16;
    UTF8 *name8;
    int retVal;
    int i;

    fs_name->meta_addr = tsk_getu48(fs->endian, idxe->file_ref);
    fs_name->meta_seq = tsk_getu16(fs->endian, idxe->seq_num);

    name16 = (UTF16 *) & fname->name;
    name8 = (UTF8 *) fs_name->name;

    retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
        (UTF16 *) ((uintptr_t) name16 +
            fname->nlen * 2), &name8,
        (UTF8 *) ((uintptr_t) name8 +
            fs_name->name_size), TSKlenientConversion);

    if (retVal != TSKconversionOK) {
        *name8 = '\0';
        if (tsk_verbose)
            tsk_fprintf(stderr,
                "Error converting NTFS name to UTF8: %d %" PRIuINUM,
                retVal, fs_name->meta_addr);
    }

    /* Make sure it is NULL Terminated */
    if ((uintptr_t) name8 > (uintptr_t) fs_name->name + fs_name->name_size)
        fs_name->name[fs_name->name_size] = '\0';
    else
        *name8 = '\0';

    /* Clean up name */
    i = 0;
    while (fs_name->name[i] != '\0') {
        if (TSK_IS_CNTRL(fs_name->name[i]))
            fs_name->name[i] = '^';
        i++;
    }

    if (tsk_getu64(fs->endian, fname->flags) & NTFS_FNAME_FLAGS_DIR)
        fs_name->type = TSK_FS_NAME_TYPE_DIR;
    else
        fs_name->type = TSK_FS_NAME_TYPE_REG;

    fs_name->flags = 0;

    return 0;
}
Esempio n. 3
0
// @@@ Should make a_idxe const and use internal pointer in function loop
static TSK_RETVAL_ENUM
ntfs_proc_idxentry(NTFS_INFO * a_ntfs, TSK_FS_DIR * a_fs_dir,
    uint8_t a_is_del, ntfs_idxentry * a_idxe, uint32_t a_idxe_len,
    uint32_t a_used_len)
{
    uintptr_t endaddr, endaddr_alloc;
    TSK_FS_NAME *fs_name;
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_ntfs->fs_info;

    if ((fs_name = tsk_fs_name_alloc(NTFS_MAXNAMLEN_UTF8, 0)) == NULL) {
        return TSK_ERR;
    }

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "ntfs_proc_idxentry: Processing index entry: %" PRIu64
            "  Size: %" PRIu32 "  Len: %" PRIu32 "\n",
            (uint64_t) ((uintptr_t) a_idxe), a_idxe_len, a_used_len);

    /* Sanity check */
    if (a_idxe_len < a_used_len) {
        tsk_error_reset();
        tsk_errno = TSK_ERR_FS_ARG;
        snprintf(tsk_errstr, TSK_ERRSTR_L,
            "ntfs_proc_idxentry: Allocated length of index entries is larger than buffer length");
        return TSK_ERR;
    }

    /* where is the end of the buffer */
    endaddr = ((uintptr_t) a_idxe + a_idxe_len);

    /* where is the end of the allocated data */
    endaddr_alloc = ((uintptr_t) a_idxe + a_used_len);

    /* cycle through the index entries, based on provided size */
    while (((uintptr_t) & (a_idxe->stream) + sizeof(ntfs_attr_fname)) <
        endaddr) {

        ntfs_attr_fname *fname = (ntfs_attr_fname *) & a_idxe->stream;


        if (tsk_verbose)
            tsk_fprintf(stderr,
                "ntfs_proc_idxentry: New IdxEnt: %" PRIu64
                " $FILE_NAME Entry: %" PRIu64 "  File Ref: %" PRIu64
                "  IdxEnt Len: %" PRIu16 "  StrLen: %" PRIu16 "\n",
                (uint64_t) ((uintptr_t) a_idxe),
                (uint64_t) ((uintptr_t) fname),
                (uint64_t) tsk_getu48(fs->endian, a_idxe->file_ref),
                tsk_getu16(fs->endian, a_idxe->idxlen),
                tsk_getu16(fs->endian, a_idxe->strlen));

        /* perform some sanity checks on index buffer head
         * and advance by 4-bytes if invalid
         */
        if ((tsk_getu48(fs->endian, a_idxe->file_ref) > fs->last_inum) ||
            (tsk_getu48(fs->endian, a_idxe->file_ref) < fs->first_inum) ||
            (tsk_getu16(fs->endian,
                    a_idxe->idxlen) <= tsk_getu16(fs->endian,
                    a_idxe->strlen))
            || (tsk_getu16(fs->endian, a_idxe->idxlen) % 4)
            || (tsk_getu16(fs->endian, a_idxe->idxlen) > a_idxe_len)) {
            a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4);
            continue;
        }

        /* do some sanity checks on the deleted entries
         */
        if ((tsk_getu16(fs->endian, a_idxe->strlen) == 0) ||
            (((uintptr_t) a_idxe + tsk_getu16(fs->endian,
                        a_idxe->idxlen)) > endaddr_alloc)) {

            /* name space checks */
            if ((fname->nspace != NTFS_FNAME_POSIX) &&
                (fname->nspace != NTFS_FNAME_WIN32) &&
                (fname->nspace != NTFS_FNAME_DOS) &&
                (fname->nspace != NTFS_FNAME_WINDOS)) {
                a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4);
                if (tsk_verbose)
                    tsk_fprintf(stderr,
                        "ntfs_proc_idxentry: Skipping because of invalid name space\n");
                continue;
            }

            if ((tsk_getu64(fs->endian, fname->alloc_fsize) <
                    tsk_getu64(fs->endian, fname->real_fsize))
                || (fname->nlen == 0)
                || (*(uint8_t *) & fname->name == 0)) {

                a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4);
                if (tsk_verbose)
                    tsk_fprintf(stderr,
                        "ntfs_proc_idxentry: Skipping because of reported file sizes, name length, or NULL name\n");
                continue;
            }

            if ((is_time(tsk_getu64(fs->endian, fname->crtime)) == 0) ||
                (is_time(tsk_getu64(fs->endian, fname->atime)) == 0) ||
                (is_time(tsk_getu64(fs->endian, fname->mtime)) == 0)) {

                a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4);
                if (tsk_verbose)
                    tsk_fprintf(stderr,
                        "ntfs_proc_idxentry: Skipping because of invalid times\n");
                continue;
            }
        }

        /* For all fname entries, there will exist a DOS style 8.3 
         * entry.  We don't process those because we already processed
         * them before in their full version.  If the type is 
         * full POSIX or WIN32 that does not satisfy DOS, then a 
         * type NTFS_FNAME_DOS will exist.  If the name is WIN32,
         * but already satisfies DOS, then a type NTFS_FNAME_WINDOS
         * will exist 
         *
         * Note that we could be missing some info from deleted files
         * if the windows version was deleted and the DOS wasn't...
         *
         * @@@ This should be added to the shrt_name entry of TSK_FS_NAME.  The short
         * name entry typically comes after the long name
         */

        if (fname->nspace == NTFS_FNAME_DOS) {
            if (tsk_verbose)
                tsk_fprintf(stderr,
                    "ntfs_proc_idxentry: Skipping because of name space: %d\n",
                    fname->nspace);

            goto incr_entry;
        }

        /* Copy it into the generic form */
        if (ntfs_dent_copy(a_ntfs, a_idxe, fs_name)) {
            if (tsk_verbose)
                tsk_fprintf(stderr,
                    "ntfs_proc_idxentry: Skipping because error copying dent_entry\n");
            goto incr_entry;
        }

        /* 
         * Check if this entry is deleted
         *
         * The final check is to see if the end of this entry is 
         * within the space that the idxallocbuf claimed was valid OR
         * if the parent directory is deleted
         */
        if ((a_is_del == 1) ||
            (tsk_getu16(fs->endian, a_idxe->strlen) == 0) ||
            (((uintptr_t) a_idxe + tsk_getu16(fs->endian,
                        a_idxe->idxlen)) > endaddr_alloc)) {
            fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;
        }
        else {
            fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
        }

        if (tsk_verbose)
            tsk_fprintf(stderr,
                "ntfs_proc_idxentry: Entry Details of %s: Str Len: %"
                PRIu16 "  Len to end after current: %" PRIu64
                "  flags: %x\n", fs_name->name, tsk_getu16(fs->endian,
                    a_idxe->strlen),
                (uint64_t) (endaddr_alloc - (uintptr_t) a_idxe -
                    tsk_getu16(fs->endian, a_idxe->idxlen)),
                fs_name->flags);


        if (tsk_fs_dir_add(a_fs_dir, fs_name)) {
            tsk_fs_name_free(fs_name);
            return TSK_ERR;
        }

      incr_entry:

        /* the theory here is that deleted entries have strlen == 0 and
         * have been found to have idxlen == 16
         *
         * if the strlen is 0, then guess how much the indexlen was
         * before it was deleted
         */

        /* 16: size of idxentry before stream
         * 66: size of fname before name
         * 2*nlen: size of name (in unicode)
         */
        if (tsk_getu16(fs->endian, a_idxe->strlen) == 0) {
            a_idxe =
                (ntfs_idxentry
                *) ((((uintptr_t) a_idxe + 16 + 66 + 2 * fname->nlen +
                        3) / 4) * 4);
        }
        else {
            a_idxe =
                (ntfs_idxentry *) ((uintptr_t) a_idxe +
                tsk_getu16(fs->endian, a_idxe->idxlen));
        }

    }                           /* end of loop of index entries */

    tsk_fs_name_free(fs_name);
    return TSK_OK;
}
Esempio n. 4
0
/*
 * copy the index (directory) entry into the generic structure
 *
 * uses the global variables 'dirs' and 'depth'
 *
 * Returns 1 on eror and 0 on success
 */
static uint8_t
ntfs_dent_copy(NTFS_INFO * ntfs, NTFS_DINFO * dinfo, ntfs_idxentry * idxe,
               TSK_FS_DENT * fs_dent)
{
    ntfs_attr_fname *fname = (ntfs_attr_fname *) & idxe->stream;
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info;
    UTF16 *name16;
    UTF8 *name8;
    int retVal;
    int i;

    fs_dent->inode = tsk_getu48(fs->endian, idxe->file_ref);

    name16 = (UTF16 *) & fname->name;
    name8 = (UTF8 *) fs_dent->name;

    retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
                             (UTF16 *) ((uintptr_t) name16 +
                                        fname->nlen * 2), &name8,
                             (UTF8 *) ((uintptr_t) name8 +
                                       fs_dent->name_max), TSKlenientConversion);

    if (retVal != TSKconversionOK) {
        *name8 = '\0';
        if (tsk_verbose)
            tsk_fprintf(stderr,
                        "Error converting NTFS name to UTF8: %d %" PRIuINUM,
                        retVal, fs_dent->inode);
    }

    /* Make sure it is NULL Terminated */
    if ((uintptr_t) name8 > (uintptr_t) fs_dent->name + fs_dent->name_max)
        fs_dent->name[fs_dent->name_max] = '\0';
    else
        *name8 = '\0';

    /* Clean up name */
    i = 0;
    while (fs_dent->name[i] != '\0') {
        if (TSK_IS_CNTRL(fs_dent->name[i]))
            fs_dent->name[i] = '^';
        i++;
    }

    /* copy the path data */
    fs_dent->path = dinfo->dirs;
    fs_dent->pathdepth = dinfo->depth;

    /* Get the actual inode */
    if (fs_dent->fsi != NULL)
        tsk_fs_inode_free(fs_dent->fsi);

    if (NULL == (fs_dent->fsi = fs->inode_lookup(fs, fs_dent->inode))) {
        if (tsk_verbose) {
            tsk_fprintf(stderr,
                        "ntfs_dent_copy: error looking up inode: %" PRIuINUM "\n",
                        fs_dent->inode);
            tsk_error_print(stderr);
            tsk_error_reset();
        }
    }

    if (tsk_getu64(fs->endian, fname->flags) & NTFS_FNAME_FLAGS_DIR)
        fs_dent->ent_type = TSK_FS_DENT_TYPE_DIR;
    else
        fs_dent->ent_type = TSK_FS_DENT_TYPE_REG;

    fs_dent->flags = 0;

    return 0;
}
Esempio n. 5
0
/**
 * Process a lsit of index entries and call the callback for
 * each.
 *
 * @param list_seen List of directories that have already been analyzed
 * @param idxe Buffer with index entries to process
 * @param idxe_len Length of idxe buffer (in bytes)
 * @param used_len Length of data as reported by idexlist header (everything
 * after which and less then idxe_len is considered deleted)
 * @param flags (All we care about is ALLOC and UNALLOC)
 * @param action Callback
 * @param ptr Pointer to data to pass to callback
 *
 * @returns 1 to stop, 0 on success, and -1 on error
 */
static int
ntfs_dent_idxentry(NTFS_INFO * ntfs, NTFS_DINFO * dinfo,
                   TSK_LIST ** list_seen, ntfs_idxentry * idxe, uint32_t idxe_len,
                   uint32_t used_len, int flags, TSK_FS_DENT_TYPE_WALK_CB action,
                   void *ptr)
{
    uintptr_t endaddr, endaddr_alloc;
    TSK_FS_DENT *fs_dent;
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info;

    if ((fs_dent = tsk_fs_dent_alloc(NTFS_MAXNAMLEN_UTF8, 0)) == NULL) {
        return -1;
    }

    if (tsk_verbose)
        tsk_fprintf(stderr,
                    "ntfs_dent_idxentry: Processing index entry: %" PRIu64
                    "  Size: %" PRIu32 "  Len: %" PRIu32 "  Flags: %x\n",
                    (uint64_t) ((uintptr_t) idxe), idxe_len, used_len, flags);

    /* Sanity check */
    if (idxe_len < used_len) {
        tsk_error_reset();
        tsk_errno = TSK_ERR_FS_INODE_INT;
        snprintf(tsk_errstr, TSK_ERRSTR_L,
                 "ntfs_dent_idxentry: Allocated length of index entries is larger than buffer length");
        return 1;
    }

    /* where is the end of the buffer */
    endaddr = ((uintptr_t) idxe + idxe_len);

    /* where is the end of the allocated data */
    endaddr_alloc = ((uintptr_t) idxe + used_len);

    /* cycle through the index entries, based on provided size */
    while (((uintptr_t) & (idxe->stream) + sizeof(ntfs_attr_fname)) <
            endaddr) {

        ntfs_attr_fname *fname = (ntfs_attr_fname *) & idxe->stream;


        if (tsk_verbose)
            tsk_fprintf(stderr,
                        "ntfs_dent_idxentry: New IdxEnt: %" PRIu64
                        " $FILE_NAME Entry: %" PRIu64 "  File Ref: %" PRIu64
                        "  IdxEnt Len: %" PRIu16 "  StrLen: %" PRIu16 "\n",
                        (uint64_t) ((uintptr_t) idxe),
                        (uint64_t) ((uintptr_t) fname),
                        (uint64_t) tsk_getu48(fs->endian, idxe->file_ref),
                        tsk_getu16(fs->endian, idxe->idxlen),
                        tsk_getu16(fs->endian, idxe->strlen));

        /* perform some sanity checks on index buffer head
         * and advance by 4-bytes if invalid
         */
        if ((tsk_getu48(fs->endian, idxe->file_ref) > fs->last_inum) ||
                (tsk_getu48(fs->endian, idxe->file_ref) < fs->first_inum) ||
                (tsk_getu16(fs->endian, idxe->idxlen) <= tsk_getu16(fs->endian,
                        idxe->strlen))
                || (tsk_getu16(fs->endian, idxe->idxlen) % 4)
                || (tsk_getu16(fs->endian, idxe->idxlen) > idxe_len)) {
            idxe = (ntfs_idxentry *) ((uintptr_t) idxe + 4);
            continue;
        }

        /* do some sanity checks on the deleted entries
         */
        if ((tsk_getu16(fs->endian, idxe->strlen) == 0) ||
                (((uintptr_t) idxe + tsk_getu16(fs->endian, idxe->idxlen)) >
                 endaddr_alloc)) {

            /* name space checks */
            if ((fname->nspace != NTFS_FNAME_POSIX) &&
                    (fname->nspace != NTFS_FNAME_WIN32) &&
                    (fname->nspace != NTFS_FNAME_DOS) &&
                    (fname->nspace != NTFS_FNAME_WINDOS)) {
                idxe = (ntfs_idxentry *) ((uintptr_t) idxe + 4);
                if (tsk_verbose)
                    tsk_fprintf(stderr,
                                "ntfs_dent_idxentry: Skipping because of invalid name space\n");
                continue;
            }

            if ((tsk_getu64(fs->endian, fname->alloc_fsize) <
                    tsk_getu64(fs->endian, fname->real_fsize))
                    || (fname->nlen == 0)
                    || (*(uint8_t *) & fname->name == 0)) {

                idxe = (ntfs_idxentry *) ((uintptr_t) idxe + 4);
                if (tsk_verbose)
                    tsk_fprintf(stderr,
                                "ntfs_dent_idxentry: Skipping because of reported file sizes, name length, or NULL name\n");
                continue;
            }

            if ((is_time(tsk_getu64(fs->endian, fname->crtime)) == 0) ||
                    (is_time(tsk_getu64(fs->endian, fname->atime)) == 0) ||
                    (is_time(tsk_getu64(fs->endian, fname->mtime)) == 0)) {

                idxe = (ntfs_idxentry *) ((uintptr_t) idxe + 4);
                if (tsk_verbose)
                    tsk_fprintf(stderr,
                                "ntfs_dent_idxentry: Skipping because of invalid times\n");
                continue;
            }
        }

        /* For all fname entries, there will exist a DOS style 8.3
         * entry.  We don't process those because we already processed
         * them before in their full version.  If the type is
         * full POSIX or WIN32 that does not satisfy DOS, then a
         * type NTFS_FNAME_DOS will exist.  If the name is WIN32,
         * but already satisfies DOS, then a type NTFS_FNAME_WINDOS
         * will exist
         *
         * Note that we could be missing some info from deleted files
         * if the windows version was deleted and the DOS wasn't...
         *
         * @@@ This should be added to the shrt_name entry of TSK_FS_DENT.  The short
         * name entry typically comes after the long name
         */

        if (fname->nspace == NTFS_FNAME_DOS) {
            if (tsk_verbose)
                tsk_fprintf(stderr,
                            "ntfs_dent_idxentry: Skipping because of name space: %d\n",
                            fname->nspace);

            goto incr_entry;
        }

        /* Copy it into the generic form */
        if (ntfs_dent_copy(ntfs, dinfo, idxe, fs_dent)) {
            if (tsk_verbose)
                tsk_fprintf(stderr,
                            "ntfs_dent_idxentry: Skipping because error copying dent_entry\n");
            goto incr_entry;
        }

        /*
         * Check if this entry is deleted
         *
         * The final check is to see if the end of this entry is
         * within the space that the idxallocbuf claimed was valid
         */
        if ((tsk_getu16(fs->endian, idxe->strlen) == 0) ||
                (((uintptr_t) idxe + tsk_getu16(fs->endian, idxe->idxlen)) >
                 endaddr_alloc)) {

            /* we know deleted entries with an inode of 0 are not legit because
             * that is the MFT value.  Free it so it does not confuse
             * people with invalid data
             */
            if ((fs_dent->inode == 0) && (fs_dent->fsi)) {
                tsk_fs_inode_free(fs_dent->fsi);
                fs_dent->fsi = NULL;
            }
            fs_dent->flags = TSK_FS_DENT_FLAG_UNALLOC;
        }
        else {
            fs_dent->flags = TSK_FS_DENT_FLAG_ALLOC;
        }

        if (tsk_verbose)
            tsk_fprintf(stderr,
                        "ntfs_dent_idxentry: Entry Details of %s: Str Len: %"
                        PRIu16 "  Len to end after current: %" PRIu64
                        "  flags: %x\n", fs_dent->name, tsk_getu16(fs->endian,
                                idxe->strlen),
                        (uint64_t) (endaddr_alloc - (uintptr_t) idxe -
                                    tsk_getu16(fs->endian, idxe->idxlen)), fs_dent->flags);


        if ((flags & fs_dent->flags) == fs_dent->flags) {
            int retval = action(fs, fs_dent, ptr);
            if (retval == TSK_WALK_STOP) {
                tsk_fs_dent_free(fs_dent);
                return 1;
            }
            else if (retval == TSK_WALK_ERROR) {
                tsk_fs_dent_free(fs_dent);
                return -1;
            }
        }

        /* Recurse if we need to */
        if ((fs_dent->flags & TSK_FS_DENT_FLAG_ALLOC) &&
                (flags & TSK_FS_DENT_FLAG_RECURSE) &&
                (!TSK_FS_ISDOT(fs_dent->name)) &&
                (fs_dent->fsi) &&
                ((fs_dent->fsi->mode & TSK_FS_INODE_MODE_FMT) ==
                 TSK_FS_INODE_MODE_DIR) && (fs_dent->inode)) {
            int depth_added = 0;

            /* Make sure we do not get into an infinite loop */
            if (0 == tsk_list_find(*list_seen, fs_dent->inode)) {
                if (tsk_list_add(list_seen, fs_dent->inode)) {
                    tsk_fs_dent_free(fs_dent);
                    return -1;
                }


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

                if (ntfs_dent_walk_lcl(&(ntfs->fs_info), dinfo, list_seen,
                                       fs_dent->inode, flags, action, ptr)) {
                    if (tsk_verbose)
                        tsk_fprintf(stderr,
                                    "Error recursing into directory\n");
                    tsk_error_reset();
                }

                dinfo->depth--;
                if (depth_added)
                    *dinfo->didx[dinfo->depth] = '\0';
            }

        }                       /* end of recurse */

incr_entry:

        /* the theory here is that deleted entries have strlen == 0 and
         * have been found to have idxlen == 16
         *
         * if the strlen is 0, then guess how much the indexlen was
         * before it was deleted
         */

        /* 16: size of idxentry before stream
         * 66: size of fname before name
         * 2*nlen: size of name (in unicode)
         */
        if (tsk_getu16(fs->endian, idxe->strlen) == 0) {
            idxe =
                (ntfs_idxentry
                 *) ((((uintptr_t) idxe + 16 + 66 + 2 * fname->nlen +
                       3) / 4) * 4);
        }
        else {
            idxe =
                (ntfs_idxentry *) ((uintptr_t) idxe +
                                   tsk_getu16(fs->endian, idxe->idxlen));
        }

    }                           /* end of loop of index entries */

    tsk_fs_dent_free(fs_dent);
    return 0;
}