/** * \ingroup fslib * Return the handle structure for a specific file, given its full path. Note that * if you have the metadata address fo the file, then tsk_fs_file_open_meta() is a * more efficient approach. * * @param a_fs File system to analyze * @param a_fs_file Structure to store file data in or NULL to have one allocated. * @param a_path Path of file to open * @returns NULL on error */ TSK_FS_FILE * tsk_fs_file_open(TSK_FS_INFO * a_fs, TSK_FS_FILE * a_fs_file, const char *a_path) { TSK_INUM_T inum; int8_t retval; TSK_FS_FILE *fs_file = NULL; TSK_FS_NAME *fs_name = NULL; 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_file_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_path, &inum, fs_name); if (retval == -1) { tsk_fs_name_free(fs_name); return NULL; } else if (retval == 1) { tsk_fs_name_free(fs_name); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_file_open: path not found: %s", a_path); return NULL; } fs_file = tsk_fs_file_open_meta(a_fs, a_fs_file, inum); if (fs_file) { // Add the name to the structure fs_file->name = fs_name; // path2inum did not put this in there... fs_name->meta_seq = fs_file->meta->seq; } else { tsk_fs_name_free(fs_name); } return fs_file; }
/** \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; }
/** * \ingroup fslib * Close an open file. * @param a_fs_file Pointer to open file */ void tsk_fs_file_close(TSK_FS_FILE * a_fs_file) { if ((a_fs_file == NULL) || (a_fs_file->tag != TSK_FS_FILE_TAG)) return; a_fs_file->tag = 0; if (a_fs_file->meta) { tsk_fs_meta_close(a_fs_file->meta); a_fs_file->meta = NULL; } if (a_fs_file->name) { tsk_fs_name_free(a_fs_file->name); a_fs_file->name = NULL; } free(a_fs_file); }
/** * \ingroup fslib * * Open a file given its metadata address. This function loads the metadata * and returns a handle that can be used to read and process the file. Note * that the returned TSK_FS_FILE structure will not have the file name set because * it was not used to load the file and this function does not search the * directory structure to find the name that points to the address. In general, * if you know the metadata address of a file, this function is more efficient * then tsk_fs_file_open, which first maps a file name to the metadata address * and then opens the file using this function. * * @param a_fs File system to analyze * @param a_fs_file Structure to store file data in or NULL to have one allocated. * @param a_addr Metadata address of file to lookup * @returns NULL on error */ TSK_FS_FILE * tsk_fs_file_open_meta(TSK_FS_INFO * a_fs, TSK_FS_FILE * a_fs_file, TSK_INUM_T a_addr) { TSK_FS_FILE *fs_file; 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_file_open_meta: called with NULL or unallocated structures"); return NULL; } fs_file = a_fs_file; if (fs_file == NULL) { if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) return NULL; } else { /* if the structure passed has a name structure, free it * because we won't use it. */ if (fs_file->name) { tsk_fs_name_free(fs_file->name); fs_file->name = NULL; } // reset the rest of it tsk_fs_file_reset(fs_file); } if (a_fs->file_add_meta(a_fs, fs_file, a_addr)) { if (a_fs_file == NULL) tsk_fs_file_close(fs_file); return NULL; } return fs_file; }
/** \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; }
// @@@ 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; }
TSK_RETVAL_ENUM fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { TSK_OFF_T size, len; FATFS_INFO *fatfs = (FATFS_INFO *) a_fs; char *dirbuf; TSK_DADDR_T *addrbuf; FATFS_LOAD_DIR load; int retval; TSK_FS_DIR *fs_dir; if ((a_addr < a_fs->first_inum) || (a_addr > a_fs->last_inum)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("fatfs_dir_open_meta: invalid a_addr value: %" PRIuINUM "\n", a_addr); return TSK_ERR; } else if (a_fs_dir == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("fatfs_dir_open_meta: NULL fs_attr argument given"); return TSK_ERR; } 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, a_addr, 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); } fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr); if (fs_dir->fs_file == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_NUM); tsk_error_set_errstr("fatfs_dir_open_meta: %" PRIuINUM " is not a valid inode", a_addr); return TSK_COR; } size = fs_dir->fs_file->meta->size; len = roundup(size, fatfs->ssize); if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dir_open_meta: Processing directory %" PRIuINUM "\n", a_addr); if (size == 0) { if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dir_open_meta: directory has 0 size\n"); return TSK_OK; } /* Make a copy of the directory contents using file_walk */ if ((dirbuf = tsk_malloc((size_t) len)) == NULL) { return TSK_ERR; } load.curdirptr = dirbuf; load.dirleft = (size_t) size; /* We are going to save the address of each sector in the directory * in a stack - they are needed to determine the inode address. */ load.addrsize = (size_t) (len / fatfs->ssize); addrbuf = (TSK_DADDR_T *) tsk_malloc(load.addrsize * sizeof(TSK_DADDR_T)); if (addrbuf == NULL) { free(dirbuf); return TSK_ERR; } /* Set the variables that are used during the copy */ load.addridx = 0; load.addrbuf = addrbuf; /* save the directory contents into dirbuf */ if (tsk_fs_file_walk(fs_dir->fs_file, TSK_FS_FILE_WALK_FLAG_SLACK, fatfs_dent_action, (void *) &load)) { tsk_error_errstr2_concat("- fatfs_dir_open_meta"); free(dirbuf); free(addrbuf); return TSK_COR; } /* We did not copy the entire directory, which occurs if an error occured */ if (load.dirleft > 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_FWALK); tsk_error_set_errstr ("fatfs_dir_open_meta: Error reading directory %" PRIuINUM, a_addr); /* Free the local buffers */ free(dirbuf); free(addrbuf); return TSK_COR; } if (tsk_verbose) fprintf(stderr, "fatfs_dir_open_meta: Parsing directory %" PRIuINUM "\n", a_addr); retval = fatfs_dent_parse_buf(fatfs, fs_dir, dirbuf, len, addrbuf); free(dirbuf); free(addrbuf); // if we are listing the root directory, add the Orphan directory and special FAT file entries if (a_addr == a_fs->root_inum) { TSK_FS_NAME *fs_name = tsk_fs_name_alloc(256, 0); if (fs_name == NULL) return TSK_ERR; // MBR Entry strncpy(fs_name->name, FATFS_MBRNAME, fs_name->name_size); fs_name->meta_addr = FATFS_MBRINO(a_fs); fs_name->type = TSK_FS_NAME_TYPE_VIRT; 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; } // FAT1 Entry strncpy(fs_name->name, FATFS_FAT1NAME, fs_name->name_size); fs_name->meta_addr = FATFS_FAT1INO(a_fs); fs_name->type = TSK_FS_NAME_TYPE_VIRT; 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; } // FAT2 Entry strncpy(fs_name->name, FATFS_FAT2NAME, fs_name->name_size); fs_name->meta_addr = FATFS_FAT2INO(a_fs); fs_name->type = TSK_FS_NAME_TYPE_VIRT; 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; } // orphan directory 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; }
/** \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 hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { HFS_INFO *hfs = (HFS_INFO *) fs; uint32_t cnid; /* catalog node ID of the entry (= inum) */ TSK_FS_DIR *fs_dir; TSK_FS_NAME *fs_name; HFS_DIR_OPEN_META_INFO info; tsk_error_reset(); cnid = (uint32_t) a_addr; if (tsk_verbose) fprintf(stderr, "hfs_dir_open_meta: called for directory %" PRIu32 "\n", cnid); if (a_addr < fs->first_inum || a_addr > fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("hfs_dir_open_meta: Invalid inode value: %" PRIuINUM, a_addr); return TSK_ERR; } else if (a_fs_dir == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("hfs_dir_open_meta: NULL fs_dir argument given"); return TSK_ERR; } if (tsk_verbose) tsk_fprintf(stderr, "hfs_dir_open_meta: Processing directory %" PRIuINUM "\n", a_addr); fs_dir = *a_fs_dir; if (fs_dir) { tsk_fs_dir_reset(fs_dir); fs_dir->addr = a_addr; } else if ((*a_fs_dir = fs_dir = tsk_fs_dir_alloc(fs, a_addr, 128)) == NULL) { return TSK_ERR; } if ((fs_name = tsk_fs_name_alloc(HFS_MAXNAMLEN, 0)) == NULL) { return TSK_ERR; } info.fs_dir = fs_dir; info.fs_name = fs_name; if ((fs_dir->fs_file = tsk_fs_file_open_meta(fs, NULL, a_addr)) == NULL) { tsk_error_errstr2_concat(" - hfs_dir_open_meta"); tsk_fs_name_free(fs_name); return TSK_ERR; } // if we are listing the root directory, add the Orphan directory and special HFS file entries if (a_addr == fs->root_inum) { int i; for (i = 0; i < 6; i++) { switch (i) { case 0: if (!hfs->has_extents_file) continue; strncpy(fs_name->name, HFS_EXTENTS_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_EXTENTS_FILE_ID; break; case 1: strncpy(fs_name->name, HFS_CATALOG_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_CATALOG_FILE_ID; break; case 2: // Note: the Extents file and the BadBlocks file are really the same. if (!hfs->has_extents_file) continue; strncpy(fs_name->name, HFS_BAD_BLOCK_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_BAD_BLOCK_FILE_ID; break; case 3: strncpy(fs_name->name, HFS_ALLOCATION_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_ALLOCATION_FILE_ID; break; case 4: if (!hfs->has_startup_file) continue; strncpy(fs_name->name, HFS_STARTUP_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_STARTUP_FILE_ID; break; case 5: if (!hfs->has_attributes_file) continue; strncpy(fs_name->name, HFS_ATTRIBUTES_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_ATTRIBUTES_FILE_ID; break; /* case 6: strncpy(fs_name->name, HFS_REPAIR_CATALOG_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_REPAIR_CATALOG_FILE_ID; break; case 7: strncpy(fs_name->name, HFS_BOGUS_EXTENT_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_BOGUS_EXTENT_FILE_ID; break; */ } fs_name->type = TSK_FS_NAME_TYPE_REG; 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; } } } if (hfs_cat_traverse(hfs, &cnid, hfs_dir_open_meta_cb, &info)) { tsk_fs_name_free(fs_name); return TSK_ERR; } tsk_fs_name_free(fs_name); return TSK_OK; }
/** \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 iso9660_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { TSK_RETVAL_ENUM retval; TSK_FS_DIR *fs_dir; ssize_t cnt; char *buf; size_t length; if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr ("iso9660_dir_open_meta: Invalid inode value: %" PRIuINUM, a_addr); return TSK_ERR; } else if (a_fs_dir == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("iso9660_dir_open_meta: NULL fs_attr argument given"); return TSK_ERR; } if (tsk_verbose) tsk_fprintf(stderr, "iso9660_dir_open_meta: 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, a_addr, 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); } fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr); if (fs_dir->fs_file == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_NUM); tsk_error_set_errstr("iso9660_dir_open_meta: %" PRIuINUM " is not a valid inode", a_addr); return TSK_COR; } /* read directory extent into memory */ length = (size_t) fs_dir->fs_file->meta->size; if ((buf = tsk_malloc(length)) == NULL) return TSK_ERR; cnt = tsk_fs_file_read(fs_dir->fs_file, 0, buf, length, 0); if (cnt != length) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2("iso9660_dir_open_meta"); return TSK_ERR; } // process the contents retval = iso9660_proc_dir(a_fs, fs_dir, buf, length, a_addr, fs_dir->fs_file->meta->attr->head->nrd.run->addr); // if we are listing the root directory, add the Orphan directory entry if (a_addr == a_fs->root_inum) { TSK_FS_NAME *fs_name = tsk_fs_name_alloc(256, 0); if (fs_name == 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; }
/* * @param a_is_del Set to 1 if block is from a deleted directory. */ static TSK_RETVAL_ENUM ffs_dent_parse_block(FFS_INFO * ffs, TSK_FS_DIR * fs_dir, uint8_t a_is_del, char *buf, unsigned int len) { unsigned int idx; unsigned int inode = 0, dellen = 0, reclen = 0; unsigned int minreclen = 4; TSK_FS_INFO *fs = &(ffs->fs_info); char *dirPtr; TSK_FS_NAME *fs_name; if ((fs_name = tsk_fs_name_alloc(FFS_MAXNAMLEN + 1, 0)) == NULL) return TSK_ERR; /* update each time by the actual length instead of the ** recorded length so we can view the deleted entries */ for (idx = 0; idx <= len - FFS_DIRSIZ_lcl(1); idx += minreclen) { unsigned int namelen = 0; dirPtr = (char *) &buf[idx]; /* copy to local variables */ if ((fs->ftype == TSK_FS_TYPE_FFS1) || (fs->ftype == TSK_FS_TYPE_FFS2)) { ffs_dentry1 *dir = (ffs_dentry1 *) dirPtr; inode = tsk_getu32(fs->endian, dir->d_ino); namelen = dir->d_namlen; reclen = tsk_getu16(fs->endian, dir->d_reclen); } /* TSK_FS_TYPE_FFS1B */ else if (fs->ftype == TSK_FS_TYPE_FFS1B) { ffs_dentry2 *dir = (ffs_dentry2 *) dirPtr; inode = tsk_getu32(fs->endian, dir->d_ino); namelen = tsk_getu16(fs->endian, dir->d_namlen); reclen = tsk_getu16(fs->endian, dir->d_reclen); } /* what is the minimum size needed for this entry */ minreclen = FFS_DIRSIZ_lcl(namelen); /* Perform a couple sanity checks ** OpenBSD never zeros the inode number, but solaris ** does. These checks will hopefully catch all non ** entries */ if ((inode > fs->last_inum) || // inode is unsigned (namelen > FFS_MAXNAMLEN) || // namelen is unsigned (namelen == 0) || (reclen < minreclen) || (reclen % 4) || (idx + reclen > len)) { /* we don't have a valid entry, so skip ahead 4 */ minreclen = 4; if (dellen > 0) dellen -= 4; continue; } /* Before we process an entry in unallocated space, make * sure that it also ends in the unalloc space */ if ((dellen) && (dellen < minreclen)) { minreclen = 4; dellen -= 4; continue; } /* the entry is valid */ if (ffs_dent_copy(ffs, dirPtr, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } /* Do we have a deleted entry? (are we in a deleted space) */ if ((dellen > 0) || (inode == 0) || (a_is_del)) { fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; if (dellen) dellen -= minreclen; } else { 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; } /* If we have some slack and an entry could exist in it, the set dellen */ if (dellen <= 0) { if (reclen - minreclen >= FFS_DIRSIZ_lcl(1)) dellen = reclen - minreclen; else minreclen = reclen; } } tsk_fs_name_free(fs_name); return TSK_OK; } /* end ffs_dent_parse_block */
/* * Process the contents of a directory and add them to FS_DIR. * * @param fatfs File system information structure * @param a_fs_dir Structure to store the files in. * @param list_seen List of directory inodes that have been seen thus far in * directory walking (can be a pointer to a NULL pointer on first call). * @param buf Buffer that contains the directory contents. * @param len Length of buffer in bytes (must be a multiple of sector size) * @param addrs Array where each element is the original address of the * corresponding block in buf (size of array is number of blocks in directory). * * @return -1 on error, 0 on success, and 1 to stop */ static TSK_RETVAL_ENUM fatfs_dent_parse_buf(FATFS_INFO * fatfs, TSK_FS_DIR * a_fs_dir, char *buf, TSK_OFF_T len, TSK_DADDR_T * addrs) { unsigned int idx, sidx; int a, b; TSK_INUM_T inode, ibase; fatfs_dentry *dep; TSK_FS_INFO *fs = (TSK_FS_INFO *) & fatfs->fs_info; int sectalloc; TSK_FS_NAME *fs_name; FATFS_LFN lfninfo; if (buf == NULL) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "fatfs_dent_parse_buf: buffer is NULL"); return TSK_ERR; } dep = (fatfs_dentry *) buf; if ((fs_name = tsk_fs_name_alloc(FATFS_MAXNAMLEN_UTF8, 32)) == NULL) { return TSK_ERR; } memset(&lfninfo, 0, sizeof(FATFS_LFN)); lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1; for (sidx = 0; sidx < (unsigned int) (len / fatfs->ssize); sidx++) { /* Get the base inode for this sector */ ibase = FATFS_SECT_2_INODE(fatfs, addrs[sidx]); if (ibase > fs->last_inum) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "fatfs_parse: inode address is too large"); tsk_fs_name_free(fs_name); return TSK_COR; } if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dent_parse_buf: Parsing sector %" PRIuDADDR "\n", addrs[sidx]); if ((sectalloc = fatfs_is_sectalloc(fatfs, addrs[sidx])) == -1) { if (tsk_verbose) { tsk_fprintf(stderr, "fatfs_dent_parse_buf: Error looking up sector allocation: %" PRIuDADDR "\n", addrs[sidx]); tsk_error_print(stderr); } tsk_error_reset(); continue; } /* cycle through the directory entries */ for (idx = 0; idx < fatfs->dentry_cnt_se; idx++, dep++) { fatfs_dentry *dir; int i; /* is it a valid dentry? */ if (0 == fatfs_isdentry(fatfs, dep)) { if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dent_parse_buf: Entry %u is invalid\n", idx); continue; } /* Copy the directory entry into the TSK_FS_NAME structure */ dir = (fatfs_dentry *) dep; inode = ibase + idx; /* Take care of the name * Copy a long name to a buffer and take action if it * is a small name */ if ((dir->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) { fatfs_dentry_lfn *dirl = (fatfs_dentry_lfn *) dir; /* Store the name in dinfo until we get the 8.3 name * Use the checksum to identify a new sequence * */ if (((dirl->seq & FATFS_LFN_SEQ_FIRST) && (dirl->seq != FATFS_SLOT_DELETED)) || (dirl->chksum != lfninfo.chk)) { // @@@ Do a partial output here /* Reset the values */ lfninfo.seq = dirl->seq & FATFS_LFN_SEQ_MASK; lfninfo.chk = dirl->chksum; lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1; } else if (dirl->seq != lfninfo.seq - 1) { // @@@ Check the sequence number - the checksum is correct though... } /* Copy the UTF16 values starting at end of buffer */ for (a = 3; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part3[a]; } for (a = 11; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part2[a]; } for (a = 9; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part1[a]; } // Skip ahead until we get a new sequence num or the 8.3 name continue; } /* Special case for volume label: name does not have an * extension and we add a note at the end that it is a label */ else if ((dir->attrib & FATFS_ATTR_VOLUME) == FATFS_ATTR_VOLUME) { a = 0; for (b = 0; b < 8; b++) { if ((dir->name[b] >= 0x20) && (dir->name[b] != 0xff)) { fs_name->name[a++] = dir->name[b]; } else { fs_name->name[a++] = '^'; } } for (b = 0; b < 3; b++) { if ((dir->ext[b] >= 0x20) && (dir->ext[b] != 0xff)) { fs_name->name[a++] = dir->ext[b]; } else { fs_name->name[a++] = '^'; } } fs_name->name[a] = '\0'; /* Append a string to show it is a label */ if (a + 22 < FATFS_MAXNAMLEN_UTF8) { char *volstr = " (Volume Label Entry)"; strncat(fs_name->name, volstr, FATFS_MAXNAMLEN_UTF8 - a); } } /* A short (8.3) entry */ else { char *name_ptr; // The dest location for the short name /* if we have a lfn, copy it into fs_name->name * and put the short name in fs_name->shrt_name */ if (lfninfo.start != FATFS_MAXNAMLEN_UTF8 - 1) { int retVal; /* @@@ Check the checksum */ /* Convert the UTF16 to UTF8 */ UTF16 *name16 = (UTF16 *) ((uintptr_t) & lfninfo.name[lfninfo. start + 1]); UTF8 *name8 = (UTF8 *) fs_name->name; retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, (UTF16 *) & lfninfo.name[FATFS_MAXNAMLEN_UTF8], &name8, (UTF8 *) ((uintptr_t) name8 + FATFS_MAXNAMLEN_UTF8), TSKlenientConversion); if (retVal != TSKconversionOK) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_UNICODE; snprintf(tsk_errstr, TSK_ERRSTR_L, "fatfs_parse: Error converting FAT LFN to UTF8: %d", retVal); continue; } /* Make sure it is NULL Terminated */ if ((uintptr_t) name8 > (uintptr_t) fs_name->name + FATFS_MAXNAMLEN_UTF8) fs_name->name[FATFS_MAXNAMLEN_UTF8 - 1] = '\0'; else *name8 = '\0'; /* Clean up name */ i = 0; while (fs_name->name[i] != '\0') { if (TSK_IS_CNTRL(fs_name->name[i])) fs_name->name[i] = '^'; i++; } lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1; name_ptr = fs_name->shrt_name; // put 8.3 into shrt_name } /* We don't have a LFN, so put the short name in * fs_name->name */ else { fs_name->shrt_name[0] = '\0'; name_ptr = fs_name->name; // put 8.3 into normal location } /* copy in the short name into the place specified above. * Skip spaces and put in the . */ a = 0; for (b = 0; b < 8; b++) { if ((dir->name[b] != 0) && (dir->name[b] != 0xff) && (dir->name[b] != 0x20)) { if ((b == 0) && (dir->name[0] == FATFS_SLOT_DELETED)) { name_ptr[a++] = '_'; } else if ((dir->lowercase & FATFS_CASE_LOWER_BASE) && (dir->name[b] >= 'A') && (dir->name[b] <= 'Z')) { name_ptr[a++] = dir->name[b] + 32; } else { name_ptr[a++] = dir->name[b]; } } } for (b = 0; b < 3; b++) { if ((dir->ext[b] != 0) && (dir->ext[b] != 0xff) && (dir->ext[b] != 0x20)) { if (b == 0) name_ptr[a++] = '.'; if ((dir->lowercase & FATFS_CASE_LOWER_EXT) && (dir->ext[b] >= 'A') && (dir->ext[b] <= 'Z')) name_ptr[a++] = dir->ext[b] + 32; else name_ptr[a++] = dir->ext[b]; } } name_ptr[a] = '\0'; } /* Clean up name to remove control chars */ i = 0; while (fs_name->name[i] != '\0') { if (TSK_IS_CNTRL(fs_name->name[i])) fs_name->name[i] = '^'; i++; } /* file type: FAT only knows DIR and FILE */ if ((dir->attrib & FATFS_ATTR_DIRECTORY) == FATFS_ATTR_DIRECTORY) fs_name->type = TSK_FS_NAME_TYPE_DIR; else fs_name->type = TSK_FS_NAME_TYPE_REG; /* Get inode */ fs_name->meta_addr = inode; /* Handle the . and .. entries specially * The current inode 'address' they have is for the current * slot in the cluster, but it needs to refer to the original * slot */ if (TSK_FS_ISDOT(fs_name->name)) { if (fs_name->name[1] == '\0') { inode = fs_name->meta_addr = a_fs_dir->fs_file->meta->addr; } /* for the parent directory, look up in the list that * is maintained in fafs_info */ else if (fs_name->name[1] == '.') { size_t q; uint8_t dir_found = 0; for (q = 0; q < fatfs->dir_buf_next; q++) { if (fatfs->dir_buf[q] == a_fs_dir->fs_file->meta->addr) { inode = fs_name->meta_addr = fatfs->par_buf[q]; dir_found = 1; break; } } if ((dir_found == 0) && (fs->isOrphanHunting)) { /* if we are currently scanning the fs to determine the orphan files, * then we do not care about the value of '..' and this can only cause * infinite loop problems */ inode = fs_name->meta_addr = 0; dir_found = 1; } if ((dir_found == 0) && (addrs[0] == fatfs->firstdatasect)) { /* if we are currently in the root directory, we aren't going to find * a parent. This shouldn't happen, but could result in an infinite loop. */ inode = fs_name->meta_addr = 0; dir_found = 1; } if (dir_found == 0) { /* The parent directory is not in the list. We are going to walk * the directory until we hit this directory. This process will * populate the buffer table and we will then rescan it */ if (tsk_fs_dir_walk(fs, fs->root_inum, TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_UNALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE, find_parent_act, (void *) &a_fs_dir->fs_file->meta->addr)) { return 0; } for (q = 0; q < fatfs->dir_buf_next; q++) { if (fatfs->dir_buf[q] == a_fs_dir->fs_file->meta->addr) { inode = fs_name->meta_addr = fatfs->par_buf[q]; dir_found = 1; break; } } // if we did not find it, then it was probably // from the orphan directory... if (dir_found == 0) inode = fs_name->meta_addr = TSK_FS_ORPHANDIR_INUM(fs); } } } else { /* Save the (non-. or ..) directory to parent directory info to local * structures so that we can later fill into the inode * info for '..' entries */ if (fs_name->type == TSK_FS_NAME_TYPE_DIR) { if (fatfs_dir_buf_add(fatfs, a_fs_dir->fs_file->meta->addr, inode)) return TSK_ERR; } } /* The allocation status of an entry is based on the allocation * status of the sector it is in and the flag. Deleted directories * do not always clear the flags of each entry */ if (sectalloc == 1) { fs_name->flags = (dep->name[0] == FATFS_SLOT_DELETED) ? TSK_FS_NAME_FLAG_UNALLOC : TSK_FS_NAME_FLAG_ALLOC; } else { fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; } tsk_fs_dir_add(a_fs_dir, fs_name); } } tsk_fs_name_free(fs_name); return TSK_OK; }
TSK_RETVAL_ENUM ext2fs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) a_fs; char *dirbuf, *dirptr; TSK_OFF_T size; TSK_FS_LOAD_FILE load_file; TSK_FS_DIR *fs_dir; TSK_LIST *list_seen = NULL; /* If we get corruption in one of the blocks, then continue processing. * retval_final will change when corruption is detected. Errors are * returned immediately. */ TSK_RETVAL_ENUM retval_tmp; TSK_RETVAL_ENUM retval_final = TSK_OK; if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("ext2fs_dir_open_meta: inode value: %" PRIuINUM "\n", a_addr); return TSK_ERR; } else if (a_fs_dir == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("ext2fs_dir_open_meta: NULL fs_attr argument given"); return TSK_ERR; } if (tsk_verbose) { tsk_fprintf(stderr, "ext2fs_dir_open_meta: Processing directory %" PRIuINUM "\n", a_addr); #ifdef Ext4_DBG tsk_fprintf(stderr, "ext2fs_dir_open_meta: $OrphanFiles Inum %" PRIuINUM " == %" PRIuINUM ": %d\n", TSK_FS_ORPHANDIR_INUM(a_fs), a_addr, a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)); #endif } fs_dir = *a_fs_dir; if (fs_dir) { tsk_fs_dir_reset(fs_dir); fs_dir->addr = a_addr; } else { if ((*a_fs_dir = fs_dir = tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) { return TSK_ERR; } } // handle the orphan directory if its contents were requested if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { #ifdef Ext4_DBG tsk_fprintf(stderr, "DEBUG: Getting ready to process ORPHANS\n"); #endif return tsk_fs_dir_find_orphans(a_fs, fs_dir); } else { #ifdef Ext4_DBG tsk_fprintf(stderr, "DEBUG: not orphan %" PRIuINUM "!=%" PRIuINUM "\n", a_addr, TSK_FS_ORPHANDIR_INUM(a_fs)); #endif } if ((fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) { tsk_error_reset(); tsk_error_errstr2_concat("- ext2fs_dir_open_meta"); return TSK_COR; } size = roundup(fs_dir->fs_file->meta->size, a_fs->block_size); if ((dirbuf = tsk_malloc((size_t) size)) == NULL) { return TSK_ERR; } /* make a copy of the directory contents that we can process */ load_file.left = load_file.total = (size_t) size; load_file.base = load_file.cur = dirbuf; if (tsk_fs_file_walk(fs_dir->fs_file, TSK_FS_FILE_WALK_FLAG_SLACK, tsk_fs_load_file_action, (void *) &load_file)) { tsk_error_reset(); tsk_error_errstr2_concat("- ext2fs_dir_open_meta"); free(dirbuf); return TSK_COR; } /* Not all of the directory was copied, so we exit */ if (load_file.left > 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_FWALK); tsk_error_set_errstr ("ext2fs_dir_open_meta: Error reading directory contents: %" PRIuINUM "\n", a_addr); free(dirbuf); return TSK_COR; } dirptr = dirbuf; while ((int64_t) size > 0) { int len = (a_fs->block_size < size) ? a_fs->block_size : (int) size; retval_tmp = ext2fs_dent_parse_block(ext2fs, fs_dir, (fs_dir->fs_file->meta-> flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, &list_seen, dirptr, len); if (retval_tmp == TSK_ERR) { retval_final = TSK_ERR; break; } else if (retval_tmp == TSK_COR) { retval_final = TSK_COR; } size -= len; dirptr = (char *) ((uintptr_t) dirptr + len); } free(dirbuf); // if we are listing the root directory, add the Orphan directory entry if (a_addr == a_fs->root_inum) { TSK_FS_NAME *fs_name = tsk_fs_name_alloc(256, 0); if (fs_name == 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; }
/* * @param a_is_del Set to 1 if block is from a deleted directory. */ static TSK_RETVAL_ENUM ext2fs_dent_parse_block(EXT2FS_INFO * ext2fs, TSK_FS_DIR * a_fs_dir, uint8_t a_is_del, TSK_LIST ** list_seen, char *buf, int len) { TSK_FS_INFO *fs = &(ext2fs->fs_info); int dellen = 0; int idx; uint16_t reclen; uint32_t inode; char *dirPtr; TSK_FS_NAME *fs_name; int minreclen = 4; if ((fs_name = tsk_fs_name_alloc(EXT2FS_MAXNAMLEN + 1, 0)) == NULL) return TSK_ERR; /* update each time by the actual length instead of the ** recorded length so we can view the deleted entries */ for (idx = 0; idx <= len - EXT2FS_DIRSIZ_lcl(1); idx += minreclen) { unsigned int namelen; dirPtr = &buf[idx]; if (ext2fs->deentry_type == EXT2_DE_V1) { ext2fs_dentry1 *dir = (ext2fs_dentry1 *) dirPtr; inode = tsk_getu32(fs->endian, dir->inode); namelen = tsk_getu16(fs->endian, dir->name_len); reclen = tsk_getu16(fs->endian, dir->rec_len); } else { ext2fs_dentry2 *dir = (ext2fs_dentry2 *) dirPtr; inode = tsk_getu32(fs->endian, dir->inode); namelen = dir->name_len; reclen = tsk_getu16(fs->endian, dir->rec_len); } minreclen = EXT2FS_DIRSIZ_lcl(namelen); /* ** Check if we may have a valid directory entry. If we don't, ** then increment to the next word and try again. */ if ((inode > fs->last_inum) || // inode is unsigned (namelen > EXT2FS_MAXNAMLEN) || (namelen == 0) || // namelen is unsigned (reclen < minreclen) || (reclen % 4) || (idx + reclen > len)) { minreclen = 4; if (dellen > 0) dellen -= 4; continue; } /* Before we process an entry in unallocated space, make * sure that it also ends in the unalloc space */ if ((dellen) && (dellen < minreclen)) { minreclen = 4; dellen -= 4; continue; } if (ext2fs_dent_copy(ext2fs, dirPtr, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } /* Do we have a deleted entry? */ if ((dellen > 0) || (inode == 0) || (a_is_del)) { fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; if (dellen > 0) dellen -= minreclen; } /* We have a non-deleted entry */ else { fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; } if (tsk_fs_dir_add(a_fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } /* If the actual length is shorter then the ** recorded length, then the next entry(ies) have been ** deleted. Set dellen to the length of data that ** has been deleted ** ** Because we aren't guaranteed with Ext2FS that the next ** entry begins right after this one, we will check to ** see if the difference is less than a possible entry ** before we waste time searching it */ if (dellen <= 0) { if (reclen - minreclen >= EXT2FS_DIRSIZ_lcl(1)) dellen = reclen - minreclen; else minreclen = reclen; } } tsk_fs_name_free(fs_name); return TSK_OK; }
/** \internal * process the data from inside of a directory and load the corresponding * file data into a TSK_FS_DIR structure. * * @param a_fs File system * @param a_fs_dir Structore to store file names into * @param buf Buffer that contains the directory content * @param a_length Number of bytes in buffer * @param a_addr The metadata address for the directory being processed * @param a_dir_addr The block offset where this directory starts * @returns TSK_ERR on error and TSK_OK otherwise */ static uint8_t iso9660_proc_dir(TSK_FS_INFO * a_fs, TSK_FS_DIR * a_fs_dir, char *buf, size_t a_length, TSK_INUM_T a_addr, TSK_OFF_T a_dir_addr) { ISO_INFO *iso = (ISO_INFO *) a_fs; TSK_FS_NAME *fs_name; size_t buf_idx; iso9660_dentry *dd; /* directory descriptor */ iso9660_inode_node *in; TSK_OFF_T dir_offs = a_dir_addr * a_fs->block_size; if ((fs_name = tsk_fs_name_alloc(ISO9660_MAXNAMLEN + 1, 0)) == NULL) return TSK_ERR; buf_idx = 0; dd = (iso9660_dentry *) & buf[buf_idx]; /* handle "." entry */ fs_name->meta_addr = a_addr; strcpy(fs_name->name, "."); fs_name->type = TSK_FS_NAME_TYPE_DIR; fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; tsk_fs_dir_add(a_fs_dir, fs_name); buf_idx += dd->entry_len; if (buf_idx > a_length - sizeof(iso9660_dentry)) { free(buf); tsk_fs_name_free(fs_name); return TSK_OK; } dd = (iso9660_dentry *) & buf[buf_idx]; /* handle ".." entry */ in = iso->in_list; while (in && (tsk_getu32(a_fs->endian, in->inode.dr.ext_loc_m) != tsk_getu32(a_fs->endian, dd->ext_loc_m))) in = in->next; if (in) { fs_name->meta_addr = in->inum; strcpy(fs_name->name, ".."); fs_name->type = TSK_FS_NAME_TYPE_DIR; fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; tsk_fs_dir_add(a_fs_dir, fs_name); } buf_idx += dd->entry_len; // process the rest of the entries in the directory while (buf_idx < a_length - sizeof(iso9660_dentry)) { dd = (iso9660_dentry *) & buf[buf_idx]; // process the entry (if it has a defined and valid length) if ((dd->entry_len) && (buf_idx + dd->entry_len < a_length)) { /* We need to find the data in the pre-processed list because that * contains the meta data address that TSK assigned to this file. * We find the entry by looking for one that was stored at the same * byte offset that we now are. We used to use the extent location, but * we found an image * that had a file with 0 bytes with the same starting block as another * file. */ for (in = iso->in_list; in; in = in->next) { if (in->dentry_offset == dir_offs + buf_idx) break; } // we may have not found it because we are reading corrupt data... if (!in) { buf_idx++; continue; } // copy the data in fs_name for loading fs_name->meta_addr = in->inum; strncpy(fs_name->name, in->inode.fn, ISO9660_MAXNAMLEN); if (dd->flags & ISO9660_FLAG_DIR) fs_name->type = TSK_FS_NAME_TYPE_DIR; else fs_name->type = TSK_FS_NAME_TYPE_REG; fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; tsk_fs_dir_add(a_fs_dir, fs_name); buf_idx += dd->entry_len; } /* If the length was not defined, we are probably in a hole in the * directory. The contents are block aligned. So, we * scan ahead until we get either a non-zero entry or the block boundary */ else { buf_idx++; for (; buf_idx < a_length - sizeof(iso9660_dentry); buf_idx++) { if (buf[buf_idx] != 0) { dd = (iso9660_dentry *) & buf[buf_idx]; if ((dd->entry_len) && (buf_idx + dd->entry_len < a_length)) break; } if (buf_idx % a_fs->block_size == 0) break; } } } free(buf); tsk_fs_name_free(fs_name); return TSK_OK; }
/** * /internal * Parse a buffer containing the contents of a directory and add TSK_FS_NAME * objects for each named file found to the TSK_FS_DIR representation of the * directory. * * @param fatfs File system information structure for file system that * contains the directory. * @param a_fs_dir Directory structure into to which parsed file metadata will * be added. * @param buf Buffer that contains the directory contents. * @param len Length of buffer in bytes (must be a multiple of sector * size). * @param addrs Array where each element is the original address of * the corresponding sector in a_buf (size of array is number of sectors in * the directory). * @return TSK_RETVAL_ENUM */ TSK_RETVAL_ENUM fatxxfs_dent_parse_buf(FATFS_INFO *fatfs, TSK_FS_DIR *a_fs_dir, char *buf, TSK_OFF_T len, TSK_DADDR_T *addrs) { char *func_name = "fatxxfs_dent_parse_buf"; unsigned int idx = 0; unsigned int sidx = 0; int a = 0; int b = 0; TSK_INUM_T ibase = 0; FATXXFS_DENTRY *dep = NULL; TSK_FS_INFO *fs = (TSK_FS_INFO*)&fatfs->fs_info; int sectalloc = 0; TSK_FS_NAME *fs_name = NULL; FATXXFS_LFN lfninfo; int entrySeenCount = 0; int entryInvalidCount = 0; uint8_t isCorruptDir = 0; tsk_error_reset(); if (fatfs_ptr_arg_is_null(fatfs, "fatfs", func_name) || fatfs_ptr_arg_is_null(a_fs_dir, "a_fs_dir", func_name) || fatfs_ptr_arg_is_null(buf, "buf", func_name) || fatfs_ptr_arg_is_null(addrs, "addrs", func_name)) { return TSK_ERR; } assert(len > 0); if (len < 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("%s: invalid buffer length", func_name); return TSK_ERR; } dep = (FATXXFS_DENTRY*)buf; if ((fs_name = tsk_fs_name_alloc(FATFS_MAXNAMLEN_UTF8, 32)) == NULL) { return TSK_ERR; } memset(&lfninfo, 0, sizeof(FATXXFS_LFN)); lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1; /* Loop through the sectors in the buffer. */ for (sidx = 0; sidx < (unsigned int) (len / fatfs->ssize); sidx++) { /* Get the base inode for the current sector */ ibase = FATFS_SECT_2_INODE(fatfs, addrs[sidx]); if (ibase > fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("fatfs_parse: inode address is too large"); tsk_fs_name_free(fs_name); return TSK_COR; } if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dent_parse_buf: Parsing sector %" PRIuDADDR " for dir %" PRIuINUM "\n", addrs[sidx], a_fs_dir->addr); /* Get the allocation status of the current sector. */ if ((sectalloc = fatfs_is_sectalloc(fatfs, addrs[sidx])) == -1) { if (tsk_verbose) { tsk_fprintf(stderr, "fatfs_dent_parse_buf: Error looking up sector allocation: %" PRIuDADDR "\n", addrs[sidx]); tsk_error_print(stderr); } tsk_error_reset(); continue; } /* Loop through the putative directory entries in the current sector. */ for (idx = 0; idx < fatfs->dentry_cnt_se; idx++, dep++) { FATXXFS_DENTRY *dir; TSK_INUM_T inode; entrySeenCount++; /* Is the current entry a valid entry? */ if (0 == fatxxfs_is_dentry(fatfs, (FATFS_DENTRY*)dep, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)sectalloc, ((isCorruptDir == 0) && (sectalloc)) ? 1 : 0)) { if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dent_parse_buf: Entry %u is invalid\n", idx); entryInvalidCount++; /* If we have seen four entries and all of them are corrupt, * then test every remaining entry in this folder -- * even if the sector is allocated. The scenario is one * where we are processing a cluster that is allocated * to a file and we happen to get some data that matches * every now and then. */ if ((entrySeenCount == 4) && (entryInvalidCount == 4)) { isCorruptDir = 1; } continue; } dir = dep; /* Compute the inode address corresponding to this directory entry. */ inode = ibase + idx; if ((dir->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) { /* The current entry is a long file name entry. */ FATXXFS_DENTRY_LFN *dirl = (FATXXFS_DENTRY_LFN *) dir; /* Store the name in dinfo until we get the 8.3 name * Use the checksum to identify a new sequence. */ if (((dirl->seq & FATXXFS_LFN_SEQ_FIRST) && (dirl->seq != FATXXFS_SLOT_DELETED)) || (dirl->chksum != lfninfo.chk)) { // @@@ Do a partial output here /* This is the last long file name entry in a sequence. * Reset the sequence number, check sum, and next char * address. */ lfninfo.seq = dirl->seq & FATXXFS_LFN_SEQ_MASK; lfninfo.chk = dirl->chksum; lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1; } else if (dirl->seq != lfninfo.seq - 1) { // @@@ Check the sequence number - the checksum is correct though... } /* Copy the UTF16 values starting at end of buffer */ for (a = 3; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part3[a]; } for (a = 11; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part2[a]; } for (a = 9; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part1[a]; } // Skip ahead until we get a new sequence num or the 8.3 name continue; } else if ((dir->attrib & FATFS_ATTR_VOLUME) == FATFS_ATTR_VOLUME) { /* Special case for volume label: name does not have an * extension and we add a note at the end that it is a label */ a = 0; for (b = 0; b < 8; b++) { if ((dir->name[b] >= 0x20) && (dir->name[b] != 0xff)) { fs_name->name[a++] = dir->name[b]; } else { fs_name->name[a++] = '^'; } } for (b = 0; b < 3; b++) { if ((dir->ext[b] >= 0x20) && (dir->ext[b] != 0xff)) { fs_name->name[a++] = dir->ext[b]; } else { fs_name->name[a++] = '^'; } } fs_name->name[a] = '\0'; /* Append a string to show it is a label */ if (a + 22 < FATFS_MAXNAMLEN_UTF8) { const char *volstr = " (Volume Label Entry)"; strncat(fs_name->name, volstr, FATFS_MAXNAMLEN_UTF8 - a); } } else { /* A short (8.3) entry */ char *name_ptr; // The dest location for the short name /* if we have a lfn, copy it into fs_name->name * and put the short name in fs_name->shrt_name */ if (lfninfo.start != FATFS_MAXNAMLEN_UTF8 - 1) { int retVal; /* @@@ Check the checksum */ /* Convert the UTF16 to UTF8 */ UTF16 *name16 = (UTF16 *) ((uintptr_t) & lfninfo. name[lfninfo.start + 1]); UTF8 *name8 = (UTF8 *) fs_name->name; retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, (UTF16 *) & lfninfo.name[FATFS_MAXNAMLEN_UTF8], &name8, (UTF8 *) ((uintptr_t) name8 + FATFS_MAXNAMLEN_UTF8), TSKlenientConversion); if (retVal != TSKconversionOK) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_UNICODE); tsk_error_set_errstr ("fatfs_parse: Error converting FAT LFN to UTF8: %d", retVal); continue; } /* Make sure it is NULL Terminated */ if ((uintptr_t) name8 > (uintptr_t) fs_name->name + FATFS_MAXNAMLEN_UTF8) fs_name->name[FATFS_MAXNAMLEN_UTF8 - 1] = '\0'; else *name8 = '\0'; lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1; name_ptr = fs_name->shrt_name; // put 8.3 into shrt_name } /* We don't have a LFN, so put the short name in * fs_name->name */ else { fs_name->shrt_name[0] = '\0'; name_ptr = fs_name->name; // put 8.3 into normal location } /* copy in the short name into the place specified above. * Skip spaces and put in the . */ a = 0; for (b = 0; b < 8; b++) { if ((dir->name[b] != 0) && (dir->name[b] != 0xff) && (dir->name[b] != 0x20)) { if ((b == 0) && (dir->name[0] == FATXXFS_SLOT_DELETED)) { name_ptr[a++] = '_'; } else if ((dir->lowercase & FATXXFS_CASE_LOWER_BASE) && (dir->name[b] >= 'A') && (dir->name[b] <= 'Z')) { name_ptr[a++] = dir->name[b] + 32; } else { name_ptr[a++] = dir->name[b]; } } } for (b = 0; b < 3; b++) { if ((dir->ext[b] != 0) && (dir->ext[b] != 0xff) && (dir->ext[b] != 0x20)) { if (b == 0) name_ptr[a++] = '.'; if ((dir->lowercase & FATXXFS_CASE_LOWER_EXT) && (dir->ext[b] >= 'A') && (dir->ext[b] <= 'Z')) name_ptr[a++] = dir->ext[b] + 32; else name_ptr[a++] = dir->ext[b]; } } name_ptr[a] = '\0'; // make sure that only ASCII is in the short name fatfs_cleanup_ascii(name_ptr); } /* file type: FAT only knows DIR and FILE */ if ((dir->attrib & FATFS_ATTR_DIRECTORY) == FATFS_ATTR_DIRECTORY) fs_name->type = TSK_FS_NAME_TYPE_DIR; else fs_name->type = TSK_FS_NAME_TYPE_REG; /* set the inode */ fs_name->meta_addr = inode; inode = 0; // so that we don't use it anymore -- use only fs_name->meta_addr /* Handle the . and .. entries specially * The current inode 'address' they have is for the current * slot in the cluster, but it needs to refer to the original * slot */ if (TSK_FS_ISDOT(fs_name->name) && (fs_name->type == TSK_FS_NAME_TYPE_DIR) && idx < 2) { if (fs_name->name[1] == '\0') { /* Current directory - "." */ fs_name->meta_addr = a_fs_dir->fs_file->meta->addr; } /* for the parent directory, look up in the list that * is maintained in fafs_info */ else if (fs_name->name[1] == '.') { /* Parent directory - ".." */ uint8_t dir_found = 0; if (fatfs_dir_buf_get(fatfs, a_fs_dir->fs_file->meta->addr, &(fs_name->meta_addr)) == 0) { dir_found = 1; } if ((dir_found == 0) && (addrs[0] == fatfs->firstdatasect)) { /* if we are currently in the root directory, we aren't going to find * a parent. This shouldn't happen, but could result in an infinite loop. */ fs_name->meta_addr = 0; dir_found = 1; } if (dir_found == 0) { if (tsk_verbose) fprintf(stderr, "fatfs_dent_parse_buf: Walking directory to find parent\n"); /* The parent directory is not in the list. We are going to walk * the directory until we hit this directory. This process will * populate the buffer table and we will then rescan it */ if (tsk_fs_dir_walk(fs, fs->root_inum, (TSK_FS_DIR_WALK_FLAG_ENUM)(TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_UNALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE), fatfs_find_parent_act, (void *) &a_fs_dir->fs_file->meta->addr)) { return TSK_OK; } if (tsk_verbose) fprintf(stderr, "fatfs_dent_parse_buf: Finished walking directory to find parent\n"); if (fatfs_dir_buf_get(fatfs, a_fs_dir->fs_file->meta->addr, &(fs_name->meta_addr)) == 0) { dir_found = 1; } // if we did not find it, then it was probably // from the orphan directory... if (dir_found == 0) fs_name->meta_addr = TSK_FS_ORPHANDIR_INUM(fs); } } } else { /* Save the (non-. or ..) directory to parent directory info to local * structures so that we can later fill into the inode * info for '..' entries */ if (fs_name->type == TSK_FS_NAME_TYPE_DIR) { if (fatfs_dir_buf_add(fatfs, a_fs_dir->fs_file->meta->addr, fs_name->meta_addr)) return TSK_ERR; } } /* The allocation status of an entry is based on the allocation * status of the sector it is in and the flag. Deleted directories * do not always clear the flags of each entry */ if (sectalloc == 1) { if(FATXXFS_IS_DELETED(dep->name, fatfs)){ fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; } else{ fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; } } else { fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; } tsk_fs_dir_add(a_fs_dir, fs_name); } } tsk_fs_name_free(fs_name); return TSK_OK; }
/** \internal * Search the file system for orphan files and create the orphan file directory. * @param a_fs File system to search * @param a_fs_dir Structure to store the orphan file directory info in. */ TSK_RETVAL_ENUM tsk_fs_dir_find_orphans(TSK_FS_INFO * a_fs, TSK_FS_DIR * a_fs_dir) { FIND_ORPHAN_DATA data; size_t i; tsk_take_lock(&a_fs->orphan_dir_lock); if (a_fs->orphan_dir != NULL) { if (tsk_fs_dir_copy(a_fs->orphan_dir, a_fs_dir)) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } if (tsk_fs_dir_add_orphan_dir_meta(a_fs, a_fs_dir)) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_OK; } if (tsk_verbose) fprintf(stderr, "tsk_fs_dir_find_orphans: Searching for orphan files\n"); memset(&data, 0, sizeof(FIND_ORPHAN_DATA)); /* We first need to determine which of the unallocated meta structures * have a name pointing to them. We cache this data, so see if it is * already known. */ if (tsk_fs_dir_load_inum_named(a_fs) != TSK_OK) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } // note that list_inum_named could still be NULL if there are no deleted names. /* Now we walk the unallocated metadata structures and find ones that are * not named. The callback will add the names to the FS_DIR structure. */ data.fs_dir = a_fs_dir; // allocate a name once so that we will reuse for each name we add to FS_DIR if ((data.fs_name = tsk_fs_name_alloc(256, 0)) == NULL) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } if (tsk_verbose) fprintf(stderr, "tsk_fs_dir_find_orphans: Performing inode_walk to find unnamed metadata structures\n"); if (tsk_fs_meta_walk(a_fs, a_fs->first_inum, a_fs->last_inum, TSK_FS_META_FLAG_UNALLOC | TSK_FS_META_FLAG_USED, find_orphan_meta_walk_cb, &data)) { tsk_fs_name_free(data.fs_name); tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } tsk_fs_name_free(data.fs_name); data.fs_name = NULL; if (tsk_verbose) fprintf(stderr, "tsk_fs_dir_find_orphans: De-duping orphan files and directories\n"); /* do some cleanup on the final list. This cleanup will compare the * entries in the root orphan directory with files that can be accessed * from subdirectories of the orphan directory. These entries will exist if * they were added before their parent directory was added to the orphan directory. */ for (i = 0; i < a_fs_dir->names_used; i++) { if (tsk_list_find(data.orphan_subdir_list, a_fs_dir->names[i].meta_addr)) { if (a_fs_dir->names_used > 1) { tsk_fs_name_copy(&a_fs_dir->names[i], &a_fs_dir->names[a_fs_dir->names_used - 1]); } a_fs_dir->names_used--; } } if (data.orphan_subdir_list) { tsk_list_free(data.orphan_subdir_list); data.orphan_subdir_list = NULL; } // make copy of this so that we don't need to do it again. if ((a_fs->orphan_dir = tsk_fs_dir_alloc(a_fs, a_fs_dir->addr, a_fs_dir->names_used)) == NULL) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } if (tsk_fs_dir_copy(a_fs_dir, a_fs->orphan_dir)) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } // populate the fake FS_FILE structure in the struct to be returned for the "Orphan Directory" if (tsk_fs_dir_add_orphan_dir_meta(a_fs, a_fs_dir)) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_OK; }
/* inode walk call back for tsk_fs_ifind_par to find unallocated files * based on parent directory */ static TSK_WALK_RET_ENUM ifind_par_act(TSK_FS_FILE * fs_file, void *ptr) { IFIND_PAR_DATA *data = (IFIND_PAR_DATA *) ptr; TSK_FS_META_NAME_LIST *fs_name_list; /* go through each file name attribute for this file */ fs_name_list = fs_file->meta->name2; while (fs_name_list) { /* we found a file that has the target parent directory. * Make a FS_NAME structure and print it. */ if (fs_name_list->par_inode == data->parinode) { int i, cnt; uint8_t printed; TSK_FS_NAME *fs_name; if ((fs_name = tsk_fs_name_alloc(256, 0)) == NULL) return TSK_WALK_ERROR; /* Fill in the basics of the fs_name entry * so we can print in the fls formats */ fs_name->meta_addr = fs_file->meta->addr; fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; strncpy(fs_name->name, fs_name_list->name, fs_name->name_size); // now look for the $Data and $IDXROOT attributes fs_file->name = fs_name; printed = 0; // 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->type == TSK_FS_ATTR_TYPE_NTFS_DATA) || (fs_attr->type == TSK_FS_ATTR_TYPE_NTFS_IDXROOT)) { if (data->flags & TSK_FS_IFIND_PAR_LONG) { tsk_fs_name_print_long(stdout, fs_file, NULL, fs_file->fs_info, fs_attr, 0, 0); tsk_printf("\n"); } else { tsk_fs_name_print(stdout, fs_file, NULL, fs_file->fs_info, fs_attr, 0); tsk_printf("\n"); } printed = 1; } } // if there were no attributes, print what we got if (printed == 0) { if (data->flags & TSK_FS_IFIND_PAR_LONG) { tsk_fs_name_print_long(stdout, fs_file, NULL, fs_file->fs_info, NULL, 0, 0); tsk_printf("\n"); } else { tsk_fs_name_print(stdout, fs_file, NULL, fs_file->fs_info, NULL, 0); tsk_printf("\n"); } } tsk_fs_name_free(fs_name); data->found = 1; } fs_name_list = fs_name_list->next; } return TSK_WALK_CONT; }
/** \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 ffs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { TSK_OFF_T size; FFS_INFO *ffs = (FFS_INFO *) a_fs; char *dirbuf; int nchnk, cidx; TSK_FS_LOAD_FILE load_file; TSK_FS_DIR *fs_dir; /* If we get corruption in one of the blocks, then continue processing. * retval_final will change when corruption is detected. Errors are * returned immediately. */ TSK_RETVAL_ENUM retval_tmp; TSK_RETVAL_ENUM retval_final = TSK_OK; if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("ffs_dir_open_meta: Invalid inode value: %" PRIuINUM, a_addr); return TSK_ERR; } else if (a_fs_dir == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("ffs_dir_open_meta: NULL fs_attr argument given"); return TSK_ERR; } if (tsk_verbose) tsk_fprintf(stderr, "ffs_dir_open_meta: 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, a_addr, 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); } if ((fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) { tsk_error_reset(); tsk_error_errstr2_concat("- ffs_dir_open_meta"); return TSK_COR; } /* make a copy of the directory contents that we can process */ /* round up cause we want the slack space too */ size = roundup(fs_dir->fs_file->meta->size, FFS_DIRBLKSIZ); if ((dirbuf = tsk_malloc((size_t) size)) == NULL) { return TSK_ERR; } load_file.total = load_file.left = (size_t) size; load_file.base = load_file.cur = dirbuf; if (tsk_fs_file_walk(fs_dir->fs_file, TSK_FS_FILE_WALK_FLAG_SLACK, tsk_fs_load_file_action, (void *) &load_file)) { tsk_error_reset(); tsk_error_errstr2_concat("- ffs_dir_open_meta"); free(dirbuf); return TSK_COR; } /* Not all of the directory was copied, so we return */ if (load_file.left > 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_FWALK); tsk_error_set_errstr("ffs_dir_open_meta: Error reading directory %" PRIuINUM, a_addr); free(dirbuf); return TSK_COR; } /* Directory entries are written in chunks of DIRBLKSIZ ** determine how many chunks of this size we have to read to ** get a full block ** ** Entries do not cross over the DIRBLKSIZ boundary */ nchnk = (int) (size) / (FFS_DIRBLKSIZ) + 1; for (cidx = 0; cidx < nchnk && (int64_t) size > 0; cidx++) { int len = (FFS_DIRBLKSIZ < size) ? FFS_DIRBLKSIZ : (int) size; retval_tmp = ffs_dent_parse_block(ffs, fs_dir, (fs_dir->fs_file-> meta->flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, dirbuf + cidx * FFS_DIRBLKSIZ, len); if (retval_tmp == TSK_ERR) { retval_final = TSK_ERR; break; } else if (retval_tmp == TSK_COR) { retval_final = TSK_COR; } size -= len; } free(dirbuf); // if we are listing the root directory, add the Orphan directory entry if (a_addr == a_fs->root_inum) { TSK_FS_NAME *fs_name = tsk_fs_name_alloc(256, 0); if (fs_name == 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; }
/* * Process the contents of a directory and add them to FS_DIR. * * @param xtaffs File system information structure * @param a_fs_dir Structure to store the files in. * @param list_seen List of directory inodes that have been seen thus far in * directory walking (can be a pointer to a NULL pointer on first call). * @param buf Buffer that contains the directory contents. * @param len Length of buffer in bytes (must be a multiple of sector size) * @param addrs Array where each element is the original address of the * corresponding block in buf (size of array is number of blocks in directory). * * @return -1 on error, 0 on success, and 1 to stop */ static TSK_RETVAL_ENUM xtaffs_dent_parse_buf(XTAFFS_INFO * xtaffs, TSK_FS_DIR * a_fs_dir, char *buf, TSK_OFF_T len, TSK_DADDR_T * addrs) { unsigned int idx, sidx; int a, b; TSK_INUM_T ibase; xtaffs_dentry *dep; TSK_FS_INFO *fs = (TSK_FS_INFO *) & xtaffs->fs_info; int sectalloc; TSK_FS_NAME *fs_name; XTAFFS_LFN lfninfo; int entrySeenCount = 0; int entryInvalidCount = 0; uint8_t isCorruptDir = 0; if (buf == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("xtaffs_dent_parse_buf: buffer is NULL"); return TSK_ERR; } dep = (xtaffs_dentry *) buf; if ((fs_name = tsk_fs_name_alloc(XTAFFS_MAXNAMLEN_UTF8, 32)) == NULL) { return TSK_ERR; } memset(&lfninfo, 0, sizeof(XTAFFS_LFN)); lfninfo.start = XTAFFS_MAXNAMLEN_UTF8 - 1; for (sidx = 0; sidx < (unsigned int) (len / xtaffs->ssize); sidx++) { /* Get the base inode for this sector */ ibase = XTAFFS_SECT_2_INODE(xtaffs, addrs[sidx]); if (ibase > fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("xtaffs_parse: inode address is too large"); tsk_fs_name_free(fs_name); return TSK_COR; } if (tsk_verbose) tsk_fprintf(stderr, "xtaffs_dent_parse_buf: Parsing sector %" PRIuDADDR " for dir %" PRIuINUM "\n", addrs[sidx], a_fs_dir->addr); if ((sectalloc = xtaffs_is_sectalloc(xtaffs, addrs[sidx])) == -1) { if (tsk_verbose) { tsk_fprintf(stderr, "xtaffs_dent_parse_buf: Error looking up sector allocation: %" PRIuDADDR "\n", addrs[sidx]); tsk_error_print(stderr); } tsk_error_reset(); continue; } /* cycle through the directory entries */ for (idx = 0; idx < xtaffs->dentry_cnt_se; idx++, dep++) { xtaffs_dentry *dir; TSK_INUM_T inode; entrySeenCount++; /* is it a valid dentry? */ if (0 == xtaffs_isdentry(xtaffs, dep, ((isCorruptDir == 0) && (sectalloc)) ? 1 : 0)) { if (tsk_verbose) tsk_fprintf(stderr, "xtaffs_dent_parse_buf: Entry %u is invalid\n", idx); entryInvalidCount++; /* If we have seen four entries and all of them are corrupt, * then test every remaining entry in this folder -- * even if the sector is allocated. The scenario is one * where we are processing a cluster that is allocated * to a file and we happen to get some data that matches * every now and then. */ if ((entrySeenCount == 4) && (entryInvalidCount == 4)) { isCorruptDir = 1; } continue; } /* Copy the directory entry into the TSK_FS_NAME structure */ dir = (xtaffs_dentry *) dep; inode = ibase + idx; /* Take care of the name * Copy a long name to a buffer and take action if it * is a small name if ((dir->attrib & XTAFFS_ATTR_LFN) == XTAFFS_ATTR_LFN) { xtaffs_dentry_lfn *dirl = (xtaffs_dentry_lfn *) dir; * Store the name in dinfo until we get the 8.3 name * Use the checksum to identify a new sequence * * if (((dirl->seq & XTAFFS_LFN_SEQ_FIRST) && (dirl->seq != XTAFFS_SLOT_DELETED)) || (dirl->chksum != lfninfo.chk)) { // @@@ Do a partial output here * Reset the values * lfninfo.seq = dirl->seq & XTAFFS_LFN_SEQ_MASK; lfninfo.chk = dirl->chksum; lfninfo.start = XTAFFS_MAXNAMLEN_UTF8 - 1; } else if (dirl->seq != lfninfo.seq - 1) { // @@@ Check the sequence number - the checksum is correct though... } * Copy the UTF16 values starting at end of buffer * for (a = 3; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part3[a]; } for (a = 11; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part2[a]; } for (a = 9; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part1[a]; } // Skip ahead until we get a new sequence num or the 8.3 name continue; } */ /* Special case for volume label: name does not have an * extension and we add a note at the end that it is a label */ if ((dir->attrib & XTAFFS_ATTR_VOLUME) == XTAFFS_ATTR_VOLUME) { a = 0; for (b = 0; b < 42; b++) { if(dir->name[b] < 32 || dir->name[b] > 126) break; if ((dir->name[b] >= 0x20) && (dir->name[b] != 0xff)) { fs_name->name[a++] = dir->name[b]; } else { fs_name->name[a++] = '^'; } } fs_name->name[a] = '\0'; /* Append a string to show it is a label */ if (a + 22 < XTAFFS_MAXNAMLEN_UTF8) { const char *volstr = " (Volume Label Entry)"; strncat(fs_name->name, volstr, XTAFFS_MAXNAMLEN_UTF8 - a); } } /* AJN TODO Scrap this 8.3 stuff, it isn't in XTAF.*/ /* A short (8.3) entry */ else { char *name_ptr; // The dest location for the short name fs_name->shrt_name[0] = '\0'; name_ptr = fs_name->name; // put 8.3 into normal location /* copy in the short name into the place specified above. */ a = 0; for (b = 0; b < 42; b++) { if(dir->name[b] < 32 || dir->name[b] > 126) break; if ((dir->name[b] != 0) && (dir->name[b] != 0xff)) { if ((b == 0) && (dir->name[0] == XTAFFS_SLOT_DELETED)) { name_ptr[a++] = '_'; } else { name_ptr[a++] = dir->name[b]; } } } name_ptr[a] = '\0'; // make sure that only ASCII is in the short name xtaffs_cleanup_ascii(name_ptr); } /* file type: FAT only knows DIR and FILE */ if ((dir->attrib & XTAFFS_ATTR_DIRECTORY) == XTAFFS_ATTR_DIRECTORY) fs_name->type = TSK_FS_NAME_TYPE_DIR; else fs_name->type = TSK_FS_NAME_TYPE_REG; /* set the inode */ fs_name->meta_addr = inode; inode = 0; // so that we don't use it anymore -- use only fs_name->meta_addr /* Handle the . and .. entries specially * The current inode 'address' they have is for the current * slot in the cluster, but it needs to refer to the original * slot */ if (TSK_FS_ISDOT(fs_name->name) && (fs_name->type == TSK_FS_NAME_TYPE_DIR) && idx < 2) { if (fs_name->name[1] == '\0') { fs_name->meta_addr = a_fs_dir->fs_file->meta->addr; } /* for the parent directory, look up in the list that * is maintained in fafs_info */ else if (fs_name->name[1] == '.') { uint8_t dir_found = 0; if (xtaffs_dir_buf_get(xtaffs, a_fs_dir->fs_file->meta->addr, &(fs_name->meta_addr)) == 0) { dir_found = 1; } if ((dir_found == 0) && (addrs[0] == xtaffs->firstdatasect)) { /* if we are currently in the root directory, we aren't going to find * a parent. This shouldn't happen, but could result in an infinite loop. */ fs_name->meta_addr = 0; dir_found = 1; } if (dir_found == 0) { if (tsk_verbose) fprintf(stderr, "xtaffs_dent_parse_buf: Walking directory to find parent\n"); /* The parent directory is not in the list. We are going to walk * the directory until we hit this directory. This process will * populate the buffer table and we will then rescan it */ if (tsk_fs_dir_walk(fs, fs->root_inum, (TSK_FS_DIR_WALK_FLAG_ENUM)(TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_UNALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE), find_parent_act, (void *) &a_fs_dir->fs_file->meta->addr)) { return TSK_OK; } if (tsk_verbose) fprintf(stderr, "xtaffs_dent_parse_buf: Finished walking directory to find parent\n"); if (xtaffs_dir_buf_get(xtaffs, a_fs_dir->fs_file->meta->addr, &(fs_name->meta_addr)) == 0) { dir_found = 1; } // if we did not find it, then it was probably // from the orphan directory... if (dir_found == 0) fs_name->meta_addr = TSK_FS_ORPHANDIR_INUM(fs); } } } else { /* Save the (non-. or ..) directory to parent directory info to local * structures so that we can later fill into the inode * info for '..' entries */ if (fs_name->type == TSK_FS_NAME_TYPE_DIR) { if (xtaffs_dir_buf_add(xtaffs, a_fs_dir->fs_file->meta->addr, fs_name->meta_addr)) return TSK_ERR; } } /* The allocation status of an entry is based on the allocation * status of the sector it is in and the flag. Deleted directories * do not always clear the flags of each entry */ if (sectalloc == 1) { fs_name->flags = (dep->name[0] == XTAFFS_SLOT_DELETED) ? TSK_FS_NAME_FLAG_UNALLOC : TSK_FS_NAME_FLAG_ALLOC; } else { fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; } tsk_fs_dir_add(a_fs_dir, fs_name); } } tsk_fs_name_free(fs_name); return TSK_OK; }