/** \internal * Process a directory and load up FS_DIR with the entries. If a pointer to * an already allocated FS_DIR struture is given, it will be cleared. If no existing * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return * value is error or corruption, then the FS_DIR structure could * have entries (depending on when the error occured). * * @param a_fs File system to analyze * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated * structure or a new structure. * @param a_addr Address of directory to process. * @returns error, corruption, ok etc. */ TSK_RETVAL_ENUM ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { NTFS_INFO *ntfs = (NTFS_INFO *) a_fs; TSK_FS_DIR *fs_dir; const TSK_FS_ATTR *fs_attr_root = NULL; const TSK_FS_ATTR *fs_attr_idx; char *idxalloc; ntfs_idxentry *idxe; ntfs_idxroot *idxroot; ntfs_idxelist *idxelist; ntfs_idxrec *idxrec_p, *idxrec; int off; TSK_OFF_T idxalloc_len; TSK_FS_LOAD_FILE load_file; NTFS_PAR_MAP *map; /* In this function, we will return immediately if we get an error. * If we get corruption though, we will record that in 'retval_final' * and continue processing. */ TSK_RETVAL_ENUM retval_final = TSK_OK; TSK_RETVAL_ENUM retval_tmp; /* sanity check */ if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_WALK_RNG; snprintf(tsk_errstr, TSK_ERRSTR_L, "ntfs_dir_open_meta: inode value: %" PRIuINUM "\n", a_addr); return TSK_ERR; } else if (a_fs_dir == NULL) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "ntfs_dir_open_meta: NULL fs_attr argument given"); return TSK_ERR; } if (tsk_verbose) tsk_fprintf(stderr, "ntfs_open_dir: Processing directory %" PRIuINUM "\n", a_addr); fs_dir = *a_fs_dir; if (fs_dir) { tsk_fs_dir_reset(fs_dir); } else { if ((*a_fs_dir = fs_dir = tsk_fs_dir_alloc(a_fs, 128)) == NULL) { return TSK_ERR; } } // handle the orphan directory if its contents were requested if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { return tsk_fs_dir_find_orphans(a_fs, fs_dir); } /* Get the inode and verify it has attributes */ if ((fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) { strncat(tsk_errstr2, " - ntfs_dir_open_meta", TSK_ERRSTR_L - strlen(tsk_errstr2)); return TSK_COR; } if (!(fs_dir->fs_file->meta->attr)) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_COR; snprintf(tsk_errstr, TSK_ERRSTR_L, "dent_walk: Error: Directory address %" PRIuINUM " has no attributes", a_addr); return TSK_COR; } /* * Read the Index Root Attribute -- we do some sanity checking here * to report errors before we start to make up data for the "." and ".." * entries */ fs_attr_root = tsk_fs_attrlist_get(fs_dir->fs_file->meta->attr, NTFS_ATYPE_IDXROOT); if (!fs_attr_root) { strncat(tsk_errstr2, " - dent_walk: $IDX_ROOT not found", TSK_ERRSTR_L - strlen(tsk_errstr2)); return TSK_COR; } if (fs_attr_root->flags & TSK_FS_ATTR_NONRES) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_COR; snprintf(tsk_errstr, TSK_ERRSTR_L, "dent_walk: $IDX_ROOT is not resident - it should be"); return TSK_COR; } idxroot = (ntfs_idxroot *) fs_attr_root->rd.buf; /* Verify that the attribute type is $FILE_NAME */ if (tsk_getu32(a_fs->endian, idxroot->type) == 0) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_COR; snprintf(tsk_errstr, TSK_ERRSTR_L, "dent_walk: Attribute type in index root is 0"); return TSK_COR; } else if (tsk_getu32(a_fs->endian, idxroot->type) != NTFS_ATYPE_FNAME) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_COR; snprintf(tsk_errstr, TSK_ERRSTR_L, "ERROR: Directory index is sorted by type: %" PRIu32 ".\nOnly $FNAME is currently supported", tsk_getu32(a_fs->endian, idxroot->type)); return TSK_COR; } /* Get the header of the index entry list */ idxelist = &idxroot->list; /* Get the offset to the start of the index entry list */ idxe = (ntfs_idxentry *) ((uintptr_t) idxelist + tsk_getu32(a_fs->endian, idxelist->begin_off)); /* * NTFS does not have "." and ".." entries in the index trees * (except for a "." entry in the root directory) * * So, we'll make 'em up by making a TSK_FS_NAME structure for * a '.' and '..' entry and call the action */ if (a_addr != a_fs->root_inum) { // && (flags & TSK_FS_NAME_FLAG_ALLOC)) { TSK_FS_NAME *fs_name; TSK_FS_META_NAME_LIST *fs_name_list; if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dir_open_meta: Creating . and .. entries\n"); if ((fs_name = tsk_fs_name_alloc(16, 0)) == NULL) { return TSK_ERR; } /* * "." */ fs_name->meta_addr = a_addr; fs_name->meta_seq = fs_dir->fs_file->meta->seq; fs_name->type = TSK_FS_NAME_TYPE_DIR; strcpy(fs_name->name, "."); fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } /* * ".." */ strcpy(fs_name->name, ".."); fs_name->type = TSK_FS_NAME_TYPE_DIR; /* The fs_name structure holds the parent inode value, so we * just cycle using those */ for (fs_name_list = fs_dir->fs_file->meta->name2; fs_name_list != NULL; fs_name_list = fs_name_list->next) { fs_name->meta_addr = fs_name_list->par_inode; fs_name->meta_seq = fs_name_list->par_seq; if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } } tsk_fs_name_free(fs_name); fs_name = NULL; } /* Now we return to processing the Index Root Attribute */ if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dir_open_meta: Processing $IDX_ROOT of inum %" PRIuINUM "\n", a_addr); /* Verify the offset pointers */ if ((tsk_getu32(a_fs->endian, idxelist->seqend_off) < tsk_getu32(a_fs->endian, idxelist->begin_off)) || (tsk_getu32(a_fs->endian, idxelist->bufend_off) < tsk_getu32(a_fs->endian, idxelist->seqend_off)) || (((uintptr_t) idxe + tsk_getu32(a_fs->endian, idxelist->bufend_off)) > ((uintptr_t) fs_attr_root->rd.buf + fs_attr_root->rd.buf_size))) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_COR; snprintf(tsk_errstr, TSK_ERRSTR_L, "Error: Index list offsets are invalid on entry: %" PRIuINUM, fs_dir->fs_file->meta->addr); return TSK_COR; } retval_tmp = ntfs_proc_idxentry(ntfs, fs_dir, (fs_dir->fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, idxe, tsk_getu32(a_fs->endian, idxelist->bufend_off) - tsk_getu32(a_fs->endian, idxelist->begin_off), tsk_getu32(a_fs->endian, idxelist->seqend_off) - tsk_getu32(a_fs->endian, idxelist->begin_off)); // stop if we get an error, continue if we got corruption if (retval_tmp == TSK_ERR) { return TSK_ERR; } else if (retval_tmp == TSK_COR) { retval_final = TSK_COR; } /* * get the index allocation attribute if it exists (it doesn't for * small directories */ fs_attr_idx = tsk_fs_attrlist_get(fs_dir->fs_file->meta->attr, NTFS_ATYPE_IDXALLOC); /* if we don't have an index alloc then return, we have processed * all of the entries */ if (!fs_attr_idx) { if (tsk_getu32(a_fs->endian, idxelist->flags) & NTFS_IDXELIST_CHILD) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_COR; snprintf(tsk_errstr, TSK_ERRSTR_L, "Error: $IDX_ROOT says there should be children, but there isn't"); return TSK_COR; } } else { if (fs_attr_idx->flags & TSK_FS_ATTR_RES) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_COR; snprintf(tsk_errstr, TSK_ERRSTR_L, "$IDX_ALLOC is Resident - it shouldn't be"); return TSK_COR; } /* * Copy the index allocation run into a big buffer */ idxalloc_len = fs_attr_idx->nrd.allocsize; if ((idxalloc = tsk_malloc((size_t) idxalloc_len)) == NULL) { return TSK_ERR; } /* Fill in the loading data structure */ load_file.total = load_file.left = (size_t) idxalloc_len; load_file.cur = load_file.base = idxalloc; if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dir_open_meta: Copying $IDX_ALLOC into buffer\n"); if (tsk_fs_attr_walk(fs_attr_idx, TSK_FS_FILE_WALK_FLAG_SLACK, tsk_fs_load_file_action, (void *) &load_file)) { free(idxalloc); strncat(tsk_errstr2, " - ntfs_dir_open_meta", TSK_ERRSTR_L - strlen(tsk_errstr2)); return TSK_COR; // this could be an error though } /* Not all of the directory was copied, so we exit */ if (load_file.left > 0) { free(idxalloc); tsk_error_reset(); tsk_errno = TSK_ERR_FS_FWALK; snprintf(tsk_errstr, TSK_ERRSTR_L, "Error reading directory contents: %" PRIuINUM "\n", a_addr); return TSK_COR; } /* * The idxalloc is a big buffer that contains one or more * idx buffer structures. Each idxrec is a node in the B-Tree. * We do not process the tree as a tree because then we could * not find the deleted file names. * * Therefore, we scan the big buffer looking for the index record * structures. We save a pointer to the known beginning (idxrec_p). * Then we scan for the beginning of the next one (idxrec) and process * everything in the middle as an ntfs_idxrec. We can't use the * size given because then we wouldn't see the deleted names */ /* Set the previous pointer to NULL */ idxrec_p = idxrec = NULL; /* Loop by cluster size */ for (off = 0; off < idxalloc_len; off += ntfs->csize_b) { uint32_t list_len, rec_len; idxrec = (ntfs_idxrec *) & idxalloc[off]; if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dir_open_meta: Index Buffer Offset: %d Magic: %" PRIx32 "\n", off, tsk_getu32(a_fs->endian, idxrec->magic)); /* Is this the begining of an index record? */ if (tsk_getu32(a_fs->endian, idxrec->magic) != NTFS_IDXREC_MAGIC) continue; /* idxrec_p is only NULL for the first time * Set it and start again to find the next one */ if (idxrec_p == NULL) { idxrec_p = idxrec; continue; } /* Process the previous structure */ /* idxrec points to the next idxrec structure, idxrec_p * points to the one we are going to process */ rec_len = (uint32_t) ((uintptr_t) idxrec - (uintptr_t) idxrec_p); if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dir_open_meta: Processing previous index record (len: %" PRIu32 ")\n", rec_len); /* remove the update sequence in the index record */ if (ntfs_fix_idxrec(ntfs, idxrec_p, rec_len)) { free(idxalloc); return TSK_COR; } /* Locate the start of the index entry list */ idxelist = &idxrec_p->list; idxe = (ntfs_idxentry *) ((uintptr_t) idxelist + tsk_getu32(a_fs->endian, idxelist->begin_off)); /* the length from the start of the next record to where our * list starts. * This should be the same as bufend_off in idxelist, but we don't * trust it. */ list_len = (uint32_t) ((uintptr_t) idxrec - (uintptr_t) idxe); /* Verify the offset pointers */ if (((uintptr_t) idxe > (uintptr_t) idxrec) || ((uintptr_t) idxelist + tsk_getu32(a_fs->endian, idxelist->seqend_off) > (uintptr_t) idxrec)) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_COR; snprintf(tsk_errstr, TSK_ERRSTR_L, "Error: Index list offsets are invalid on entry: %" PRIuINUM, fs_dir->fs_file->meta->addr); free(idxalloc); return TSK_COR; } /* process the list of index entries */ retval_tmp = ntfs_proc_idxentry(ntfs, fs_dir, (fs_dir->fs_file-> meta->flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, idxe, list_len, tsk_getu32(a_fs->endian, idxelist->seqend_off) - tsk_getu32(a_fs->endian, idxelist->begin_off)); // stop if we get an error, record if we get corruption if (retval_tmp == TSK_ERR) { free(idxalloc); return TSK_ERR; } else if (retval_tmp == TSK_COR) { retval_final = TSK_COR; } /* reset the pointer to the next record */ idxrec_p = idxrec; } /* end of cluster loop */ /* Process the final record */ if (idxrec_p) { uint32_t list_len, rec_len; /* Length from end of attribute to start of this */ rec_len = (uint32_t) (idxalloc_len - (uintptr_t) idxrec_p - (uintptr_t) idxalloc); if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dir_open_meta: Processing final index record (len: %" PRIu32 ")\n", rec_len); /* remove the update sequence */ if (ntfs_fix_idxrec(ntfs, idxrec_p, rec_len)) { free(idxalloc); return TSK_COR; } idxelist = &idxrec_p->list; idxe = (ntfs_idxentry *) ((uintptr_t) idxelist + tsk_getu32(a_fs->endian, idxelist->begin_off)); /* This is the length of the idx entries */ list_len = (uint32_t) ((uintptr_t) idxalloc + idxalloc_len) - (uintptr_t) idxe; /* Verify the offset pointers */ if ((list_len > rec_len) || ((uintptr_t) idxelist + tsk_getu32(a_fs->endian, idxelist->seqend_off) > (uintptr_t) idxalloc + idxalloc_len)) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_COR; snprintf(tsk_errstr, TSK_ERRSTR_L, "Error: Index list offsets are invalid on entry: %" PRIuINUM, fs_dir->fs_file->meta->addr); free(idxalloc); return TSK_COR; } /* process the list of index entries */ retval_tmp = ntfs_proc_idxentry(ntfs, fs_dir, (fs_dir->fs_file-> meta->flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, idxe, list_len, tsk_getu32(a_fs->endian, idxelist->seqend_off) - tsk_getu32(a_fs->endian, idxelist->begin_off)); // stop if we get an error, record if we get corruption if (retval_tmp == TSK_ERR) { free(idxalloc); return TSK_ERR; } else if (retval_tmp == TSK_COR) { retval_final = TSK_COR; } } free(idxalloc); } // get the orphan files // load and cache the map if it has not already been done if (ntfs->orphan_map == NULL) { if (a_fs->inode_walk(a_fs, a_fs->first_inum, a_fs->last_inum, TSK_FS_META_FLAG_UNALLOC, ntfs_orphan_act, NULL)) { return TSK_ERR; } } // see if there are any entries for this dir map = ntfs_orphan_map_get(ntfs, a_addr); if (map != NULL) { int a; TSK_FS_NAME *fs_name; TSK_FS_FILE *fs_file_orp = NULL; if ((fs_name = tsk_fs_name_alloc(256, 0)) == NULL) return TSK_ERR; fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; fs_name->type = TSK_FS_NAME_TYPE_UNDEF; for (a = 0; a < map->used_cnt; a++) { /* Fill in the basics of the fs_name entry * so we can print in the fls formats */ fs_name->meta_addr = map->addrs[a]; // lookup the file to get its name (we did not cache that) fs_file_orp = tsk_fs_file_open_meta(a_fs, fs_file_orp, map->addrs[a]); if ((fs_file_orp) && (fs_file_orp->meta) && (fs_file_orp->meta->name2)) { TSK_FS_META_NAME_LIST *n2 = fs_file_orp->meta->name2; while (n2) { if (n2->par_inode == a_addr) { strncpy(fs_name->name, n2->name, fs_name->name_size); tsk_fs_dir_add(fs_dir, fs_name); } n2 = n2->next; } } } tsk_fs_name_free(fs_name); } // if we are listing the root directory, add the Orphan directory entry if (a_addr == a_fs->root_inum) { TSK_FS_NAME *fs_name; if ((fs_name = tsk_fs_name_alloc(256, 0)) == NULL) return TSK_ERR; if (tsk_fs_dir_make_orphan_dir_name(a_fs, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } tsk_fs_name_free(fs_name); } return retval_final; }
static uint8_t ntfs_dent_walk_lcl(TSK_FS_INFO * fs, NTFS_DINFO * dinfo, TSK_LIST ** list_seen, INUM_T inum, TSK_FS_DENT_FLAG_ENUM flags, TSK_FS_DENT_TYPE_WALK_CB action, void *ptr) { NTFS_INFO *ntfs = (NTFS_INFO *) fs; TSK_FS_INODE *fs_inode; TSK_FS_DATA *fs_data_root, *tsk_fs_data_alloc; char *idxalloc; ntfs_idxentry *idxe; ntfs_idxroot *idxroot; ntfs_idxelist *idxelist; ntfs_idxrec *idxrec_p, *idxrec; int off; OFF_T idxalloc_len; TSK_FS_LOAD_FILE load_file; int retval; /* sanity check */ if (inum < fs->first_inum || inum > fs->last_inum) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_WALK_RNG; snprintf(tsk_errstr, TSK_ERRSTR_L, "ntfs_dent_walk: inode value: %" PRIuINUM "\n", inum); return 1; } if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_walk: Processing directory %" PRIuINUM " with flags %x\n", inum, flags); /* Get the inode and verify it has attributes */ fs_inode = fs->inode_lookup(fs, inum); if (!fs_inode) { strncat(tsk_errstr2, " - ntfs_dent_walk", TSK_ERRSTR_L - strlen(tsk_errstr2)); return 1; } if (!fs_inode->attr) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_INT; snprintf(tsk_errstr, TSK_ERRSTR_L, "dent_walk: Error: Directory address %" PRIuINUM " has no attributes", inum); return 1; } /* * Read the Index Root Attribute -- we do some sanity checking here * to report errors before we start to make up data for the "." and ".." * entries */ fs_data_root = tsk_fs_data_lookup_noid(fs_inode->attr, NTFS_ATYPE_IDXROOT); if (!fs_data_root) { strncat(tsk_errstr2, " - dent_walk: $IDX_ROOT not found", TSK_ERRSTR_L - strlen(tsk_errstr2)); tsk_fs_inode_free(fs_inode); return 1; } if (fs_data_root->flags & TSK_FS_DATA_NONRES) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_INT; snprintf(tsk_errstr, TSK_ERRSTR_L, "dent_walk: $IDX_ROOT is not resident - it should be"); tsk_fs_inode_free(fs_inode); return 1; } idxroot = (ntfs_idxroot *) fs_data_root->buf; /* Verify that the attribute type is $FILE_NAME */ if (tsk_getu32(fs->endian, idxroot->type) == 0) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_INT; snprintf(tsk_errstr, TSK_ERRSTR_L, "dent_walk: Attribute type in index root is 0"); tsk_fs_inode_free(fs_inode); return 1; } else if (tsk_getu32(fs->endian, idxroot->type) != NTFS_ATYPE_FNAME) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_INT; snprintf(tsk_errstr, TSK_ERRSTR_L, "ERROR: Directory index is sorted by type: %" PRIu32 ".\nOnly $FNAME is currently supported", tsk_getu32(fs->endian, idxroot->type)); return 1; } /* Get the header of the index entry list */ idxelist = &idxroot->list; /* Get the offset to the start of the index entry list */ idxe = (ntfs_idxentry *) ((uintptr_t) idxelist + tsk_getu32(fs->endian, idxelist->begin_off)); /* * NTFS does not have "." and ".." entries in the index trees * (except for a "." entry in the root directory) * * So, we'll make 'em up by making a TSK_FS_DENT structure for * a '.' and '..' entry and call the action */ if ((inum != fs->root_inum) && (flags & TSK_FS_DENT_FLAG_ALLOC)) { TSK_FS_DENT *fs_dent; TSK_FS_INODE_NAME_LIST *fs_name; if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_walk: Creating . and .. entries\n"); if ((fs_dent = tsk_fs_dent_alloc(16, 0)) == NULL) { tsk_fs_inode_free(fs_inode); return 1; } /* * "." */ fs_dent->inode = inum; strcpy(fs_dent->name, "."); /* copy the path data */ fs_dent->path = dinfo->dirs; fs_dent->pathdepth = dinfo->depth; fs_dent->flags = TSK_FS_DENT_FLAG_ALLOC; /* this is probably a waste, but just in case the action mucks * with it ... */ fs_dent->fsi = fs->inode_lookup(fs, fs_dent->inode); if (fs_dent->fsi != NULL) { fs_dent->ent_type = TSK_FS_DENT_TYPE_DIR; retval = action(fs, fs_dent, ptr); if (retval == TSK_WALK_STOP) { tsk_fs_dent_free(fs_dent); tsk_fs_inode_free(fs_inode); return 0; } else if (retval == TSK_WALK_ERROR) { tsk_fs_dent_free(fs_dent); tsk_fs_inode_free(fs_inode); return 1; } } else { if (tsk_verbose) tsk_fprintf(stderr, "Error reading . entry: %" PRIuINUM, fs_dent->inode); } /* * ".." */ strcpy(fs_dent->name, ".."); fs_dent->ent_type = TSK_FS_DENT_TYPE_DIR; /* The fs_name structure holds the parent inode value, so we * just cycle using those */ for (fs_name = fs_inode->name; fs_name != NULL; fs_name = fs_name->next) { if (fs_dent->fsi) { tsk_fs_inode_free(fs_dent->fsi); fs_dent->fsi = NULL; } fs_dent->inode = fs_name->par_inode; fs_dent->fsi = fs->inode_lookup(fs, fs_dent->inode); if (fs_dent->fsi) { retval = action(fs, fs_dent, ptr); if (retval == TSK_WALK_STOP) { tsk_fs_dent_free(fs_dent); tsk_fs_inode_free(fs_inode); return 0; } else if (retval == TSK_WALK_ERROR) { tsk_fs_dent_free(fs_dent); tsk_fs_inode_free(fs_inode); return 1; } } else { if (tsk_verbose) tsk_fprintf(stderr, "dent_walk: Error reading .. inode: %" PRIuINUM, fs_dent->inode); } } tsk_fs_dent_free(fs_dent); fs_dent = NULL; } /* Now we return to processing the Index Root Attribute */ if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_walk: Processing $IDX_ROOT of inum %" PRIuINUM "\n", inum); /* Verify the offset pointers */ if ((tsk_getu32(fs->endian, idxelist->seqend_off) < tsk_getu32(fs->endian, idxelist->begin_off)) || (tsk_getu32(fs->endian, idxelist->bufend_off) < tsk_getu32(fs->endian, idxelist->seqend_off)) || (((uintptr_t) idxe + tsk_getu32(fs->endian, idxelist->bufend_off)) > ((uintptr_t) fs_data_root->buf + fs_data_root->buflen))) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_INT; snprintf(tsk_errstr, TSK_ERRSTR_L, "Error: Index list offsets are invalid on entry: %" PRIuINUM, fs_inode->addr); tsk_fs_inode_free(fs_inode); return 1; } retval = ntfs_dent_idxentry(ntfs, dinfo, list_seen, idxe, tsk_getu32(fs->endian, idxelist->bufend_off) - tsk_getu32(fs->endian, idxelist->begin_off), tsk_getu32(fs->endian, idxelist->seqend_off) - tsk_getu32(fs->endian, idxelist->begin_off), flags, action, ptr); if (retval != 0) { tsk_fs_inode_free(fs_inode); return (retval == -1) ? 1 : 0; } /* * get the index allocation attribute if it exists (it doesn't for * small directories */ tsk_fs_data_alloc = tsk_fs_data_lookup_noid(fs_inode->attr, NTFS_ATYPE_IDXALLOC); /* if we don't have an index alloc then return, we have processed * all of the entries */ if (!tsk_fs_data_alloc) { int retval; if (tsk_getu32(fs->endian, idxelist->flags) & NTFS_IDXELIST_CHILD) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_INT; snprintf(tsk_errstr, TSK_ERRSTR_L, "Error: $IDX_ROOT says there should be children, but there isn't"); retval = 1; } else { retval = 0; } tsk_fs_inode_free(fs_inode); return retval; } if (tsk_fs_data_alloc->flags & TSK_FS_DATA_RES) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_INT; snprintf(tsk_errstr, TSK_ERRSTR_L, "$IDX_ALLOC is Resident - it shouldn't be"); tsk_fs_inode_free(fs_inode); return 1; } /* * Copy the index allocation run into a big buffer */ idxalloc_len = tsk_fs_data_alloc->allocsize; if ((idxalloc = tsk_malloc((size_t) idxalloc_len)) == NULL) { tsk_fs_inode_free(fs_inode); return 1; } /* Fill in the loading data structure */ load_file.total = load_file.left = (size_t) idxalloc_len; load_file.cur = load_file.base = idxalloc; if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_walk: Copying $IDX_ALLOC into buffer\n"); if (ntfs_data_walk(ntfs, fs_inode->addr, tsk_fs_data_alloc, TSK_FS_FILE_FLAG_SLACK, tsk_fs_load_file_action, (void *) &load_file)) { free(idxalloc); tsk_fs_inode_free(fs_inode); strncat(tsk_errstr2, " - ntfs_dent_walk", TSK_ERRSTR_L - strlen(tsk_errstr2)); return 1; } /* Not all of the directory was copied, so we exit */ if (load_file.left > 0) { free(idxalloc); tsk_fs_inode_free(fs_inode); tsk_error_reset(); tsk_errno = TSK_ERR_FS_FWALK; snprintf(tsk_errstr, TSK_ERRSTR_L, "Error reading directory contents: %" PRIuINUM "\n", inum); return 1; } /* * The idxalloc is a big buffer that contains one or more * idx buffer structures. Each idxrec is a node in the B-Tree. * We do not process the tree as a tree because then we could * not find the deleted file names. * * Therefore, we scan the big buffer looking for the index record * structures. We save a pointer to the known beginning (idxrec_p). * Then we scan for the beginning of the next one (idxrec) and process * everything in the middle as an ntfs_idxrec. We can't use the * size given because then we wouldn't see the deleted names */ /* Set the previous pointer to NULL */ idxrec_p = idxrec = NULL; /* Loop by cluster size */ for (off = 0; off < idxalloc_len; off += ntfs->csize_b) { uint32_t list_len, rec_len; idxrec = (ntfs_idxrec *) & idxalloc[off]; if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_walk: Index Buffer Offset: %d Magic: %" PRIx32 "\n", off, tsk_getu32(fs->endian, idxrec->magic)); /* Is this the begining of an index record? */ if (tsk_getu32(fs->endian, idxrec->magic) != NTFS_IDXREC_MAGIC) continue; /* idxrec_p is only NULL for the first time * Set it and start again to find the next one */ if (idxrec_p == NULL) { idxrec_p = idxrec; continue; } /* Process the previous structure */ /* idxrec points to the next idxrec structure, idxrec_p * points to the one we are going to process */ rec_len = (uint32_t) ((uintptr_t) idxrec - (uintptr_t) idxrec_p); if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_walk: Processing previous index record (len: %" PRIu32 ")\n", rec_len); /* remove the update sequence in the index record */ if (ntfs_fix_idxrec(ntfs, idxrec_p, rec_len)) { free(idxalloc); tsk_fs_inode_free(fs_inode); return 1; } /* Locate the start of the index entry list */ idxelist = &idxrec_p->list; idxe = (ntfs_idxentry *) ((uintptr_t) idxelist + tsk_getu32(fs->endian, idxelist->begin_off)); /* the length from the start of the next record to where our * list starts. * This should be the same as bufend_off in idxelist, but we don't * trust it. */ list_len = (uint32_t) ((uintptr_t) idxrec - (uintptr_t) idxe); /* Verify the offset pointers */ if (((uintptr_t) idxe > (uintptr_t) idxrec) || ((uintptr_t) idxelist + tsk_getu32(fs->endian, idxelist->seqend_off) > (uintptr_t) idxrec)) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_INT; snprintf(tsk_errstr, TSK_ERRSTR_L, "Error: Index list offsets are invalid on entry: %" PRIuINUM, fs_inode->addr); tsk_fs_inode_free(fs_inode); free(idxalloc); return 1; } /* process the list of index entries */ retval = ntfs_dent_idxentry(ntfs, dinfo, list_seen, idxe, list_len, tsk_getu32(fs->endian, idxelist->seqend_off) - tsk_getu32(fs->endian, idxelist->begin_off), flags, action, ptr); if (retval != 0) { tsk_fs_inode_free(fs_inode); free(idxalloc); return (retval == -1) ? 1 : 0; } /* reset the pointer to the next record */ idxrec_p = idxrec; } /* end of cluster loop */ /* Process the final record */ if (idxrec_p) { uint32_t list_len, rec_len; /* Length from end of attribute to start of this */ rec_len = (uint32_t) (idxalloc_len - (uintptr_t) idxrec_p - (uintptr_t) idxalloc); if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_walk: Processing final index record (len: %" PRIu32 ")\n", rec_len); /* remove the update sequence */ if (ntfs_fix_idxrec(ntfs, idxrec_p, rec_len)) { tsk_fs_inode_free(fs_inode); free(idxalloc); return 1; } idxelist = &idxrec_p->list; idxe = (ntfs_idxentry *) ((uintptr_t) idxelist + tsk_getu32(fs->endian, idxelist->begin_off)); /* This is the length of the idx entries */ list_len = (uint32_t) ((uintptr_t) idxalloc + idxalloc_len) - (uintptr_t) idxe; /* Verify the offset pointers */ if ((list_len > rec_len) || ((uintptr_t) idxelist + tsk_getu32(fs->endian, idxelist->seqend_off) > (uintptr_t) idxalloc + idxalloc_len)) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_INT; snprintf(tsk_errstr, TSK_ERRSTR_L, "Error: Index list offsets are invalid on entry: %" PRIuINUM, fs_inode->addr); tsk_fs_inode_free(fs_inode); free(idxalloc); return 1; } /* process the list of index entries */ retval = ntfs_dent_idxentry(ntfs, dinfo, list_seen, idxe, list_len, tsk_getu32(fs->endian, idxelist->seqend_off) - tsk_getu32(fs->endian, idxelist->begin_off), flags, action, ptr); if (retval != 0) { tsk_fs_inode_free(fs_inode); free(idxalloc); return (retval == -1) ? 1 : 0; } } tsk_fs_inode_free(fs_inode); free(idxalloc); return 0; } /* end of dent_walk */