/* * Class: edu_uw_apl_commons_tsk4j_filesys_FileSystem * Method: dirOpenMeta * Signature: (JJ)Ledu/uw/apl/commons/tsk4j/filesys/Directory; */ JNIEXPORT jobject JNICALL Java_edu_uw_apl_commons_tsk4j_filesys_FileSystem_dirOpenMeta (JNIEnv *env, jobject thiz, jlong nativePtr, jlong metadataAddr ) { TSK_FS_INFO* info = (TSK_FS_INFO*)nativePtr; TSK_FS_DIR* fsDir = tsk_fs_dir_open_meta( info, (TSK_INUM_T)metadataAddr ); if( fsDir == NULL ) return (jobject)NULL; TSK_FS_FILE* fsFile = fsDir->fs_file; jobject fileMeta = NULL; if( fsFile->meta ) { fileMeta = createFileMeta( env, fsFile->meta ); if( !fileMeta ) { tsk_fs_dir_close( fsDir ); return NULL; } } jobject fileName = NULL; if( fsFile->name ) { fileName = createFileName( env, fsFile->name ); if( !fileName ) { tsk_fs_dir_close( fsDir ); // LOOK: release fileMeta ???? return NULL; } } jobject file = createFile( env, fsFile, thiz, fileMeta, fileName ); if( !file ) { tsk_fs_dir_close( fsDir ); // LOOK: release fileMeta, fileName ???? return NULL; } jobject result = createDirectory( env, fsDir, thiz, file ); if( !result ) { tsk_fs_dir_close( fsDir ); // LOOK: release fileMeta, fileName, file ???? return NULL; } return result; }
/** \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; }
/* dir_walk local function that is used for recursive calls. Callers * should initially call the non-local version. */ static TSK_WALK_RET_ENUM tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo, TSK_INUM_T a_addr, TSK_FS_DIR_WALK_FLAG_ENUM a_flags, TSK_FS_DIR_WALK_CB a_action, void *a_ptr) { TSK_FS_DIR *fs_dir; TSK_FS_FILE *fs_file; size_t i; // get the list of entries in the directory if ((fs_dir = tsk_fs_dir_open_meta(a_fs, a_addr)) == NULL) { return TSK_WALK_ERROR; } /* Allocate a file structure for the callbacks. We * will allocate fs_meta structures as needed and * point into the fs_dir structure for the names. */ if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) { tsk_fs_dir_close(fs_dir); return TSK_WALK_ERROR; } for (i = 0; i < fs_dir->names_used; i++) { TSK_WALK_RET_ENUM retval; /* Point name to the buffer of names. We need to be * careful about resetting this before we free fs_file */ fs_file->name = (TSK_FS_NAME *) & fs_dir->names[i]; /* load the fs_meta structure if possible. * Must have non-zero inode addr or have allocated name (if inode is 0) */ if (((fs_file->name->meta_addr) || (fs_file->name->flags & TSK_FS_NAME_FLAG_ALLOC))) { if (a_fs->file_add_meta(a_fs, fs_file, fs_file->name->meta_addr)) { if (tsk_verbose) tsk_error_print(stderr); tsk_error_reset(); } } // call the action if we have the right flags. if ((fs_file->name->flags & a_flags) == fs_file->name->flags) { retval = a_action(fs_file, a_dinfo->dirs, a_ptr); if (retval == TSK_WALK_STOP) { tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); /* free the list -- fs_dir_walk has no way * of knowing that we stopped early w/out error. */ if (a_dinfo->save_inum_named) { tsk_list_free(a_dinfo->list_inum_named); a_dinfo->list_inum_named = NULL; a_dinfo->save_inum_named = 0; } return TSK_WALK_STOP; } else if (retval == TSK_WALK_ERROR) { tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); return TSK_WALK_ERROR; } } // save the inode info for orphan finding - if requested if ((a_dinfo->save_inum_named) && (fs_file->meta) && (fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)) { if (tsk_list_add(&a_dinfo->list_inum_named, fs_file->meta->addr)) { // if there is an error, then clear the list tsk_list_free(a_dinfo->list_inum_named); a_dinfo->list_inum_named = NULL; a_dinfo->save_inum_named = 0; } } /* Recurse into a directory if: * - Both dir entry and inode have DIR type (or name is undefined) * - Recurse flag is set * - dir entry is allocated OR both are unallocated * - not one of the '.' or '..' entries * - A Non-Orphan Dir or the Orphan Dir with the NOORPHAN flag not set. */ if (((fs_file->name->type == TSK_FS_NAME_TYPE_DIR) || (fs_file->name->type == TSK_FS_NAME_TYPE_UNDEF)) && (fs_file->meta) && (fs_file->meta->type == TSK_FS_META_TYPE_DIR) && (a_flags & TSK_FS_DIR_WALK_FLAG_RECURSE) && ((fs_file->name->flags & TSK_FS_NAME_FLAG_ALLOC) || ((fs_file->name->flags & TSK_FS_NAME_FLAG_UNALLOC) && (fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)) ) && (!TSK_FS_ISDOT(fs_file->name->name)) && ((fs_file->name->meta_addr != TSK_FS_ORPHANDIR_INUM(a_fs)) || ((a_flags & TSK_FS_DIR_WALK_FLAG_NOORPHAN) == 0)) ) { /* Make sure we do not get into an infinite loop */ if (0 == tsk_stack_find(a_dinfo->stack_seen, fs_file->name->meta_addr)) { int depth_added = 0; uint8_t save_bak = 0; if (tsk_stack_push(a_dinfo->stack_seen, fs_file->name->meta_addr)) { tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); return TSK_WALK_ERROR; } if ((a_dinfo->depth < MAX_DEPTH) && (DIR_STRSZ > strlen(a_dinfo->dirs) + strlen(fs_file->name->name))) { a_dinfo->didx[a_dinfo->depth] = &a_dinfo->dirs[strlen(a_dinfo->dirs)]; strncpy(a_dinfo->didx[a_dinfo->depth], fs_file->name->name, DIR_STRSZ - strlen(a_dinfo->dirs)); strncat(a_dinfo->dirs, "/", DIR_STRSZ); depth_added = 1; } a_dinfo->depth++; /* We do not want to save info about named unalloc files * when we go into the Orphan directory (because then we have * no orphans). So, disable it for this recursion. */ if (fs_file->name->meta_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { save_bak = a_dinfo->save_inum_named; a_dinfo->save_inum_named = 0; } retval = tsk_fs_dir_walk_lcl(a_fs, a_dinfo, fs_file->name->meta_addr, a_flags, a_action, a_ptr); if (retval == TSK_WALK_ERROR) { /* If this fails because the directory could not be * loaded, then we still continue */ if (tsk_verbose) { tsk_fprintf(stderr, "tsk_fs_dir_walk_lcl: error reading directory: %" PRIuINUM "\n", fs_file->name->meta_addr); tsk_error_print(stderr); } tsk_error_reset(); } else if (retval == TSK_WALK_STOP) { tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); return TSK_WALK_STOP; } // reset the save status if (fs_file->name->meta_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { a_dinfo->save_inum_named = save_bak; } tsk_stack_pop(a_dinfo->stack_seen); a_dinfo->depth--; if (depth_added) *a_dinfo->didx[a_dinfo->depth] = '\0'; } else { if (tsk_verbose) fprintf(stderr, "tsk_fs_dir_walk_lcl: Loop detected with address %" PRIuINUM, fs_file->name->meta_addr); } } // remove the pointer to name buffer fs_file->name = NULL; // free the metadata if we allocated it if (fs_file->meta) { tsk_fs_meta_close(fs_file->meta); fs_file->meta = NULL; } } tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); return TSK_WALK_CONT; }
/** * Open a directory and cycle through its contents. Read each file and recurse * into each directory. * * @param fs_info File system to process * @param stack Stack to prevent infinite recursion loops * @param dir_inum Metadata address of directory to open * @param path Path of directory being opened * @returns 1 on error */ static uint8_t proc_dir(TSK_FS_INFO * fs_info, TSK_STACK * stack, TSK_INUM_T dir_inum, const char *path) { TSK_FS_DIR *fs_dir; size_t i; char *path2 = NULL; char *buf = NULL; // open the directory if ((fs_dir = tsk_fs_dir_open_meta(fs_info, dir_inum)) == NULL) { fprintf(stderr, "Error opening directory: %" PRIuINUM "\n", dir_inum); tsk_error_print(stderr); return 1; } /* These should be dynamic lengths, but this is just a sample program. * Allocate heap space instead of stack to prevent overflow for deep * directories. */ if ((path2 = (char *) malloc(4096)) == NULL) { return 1; } if ((buf = (char *) malloc(2048)) == NULL) { free(path2); return 1; } // cycle through each entry for (i = 0; i < tsk_fs_dir_getsize(fs_dir); i++) { TSK_FS_FILE *fs_file; TSK_OFF_T off = 0; size_t len = 0; // get the entry if ((fs_file = tsk_fs_dir_get(fs_dir, i)) == NULL) { fprintf(stderr, "Error getting directory entry %" PRIuSIZE " in directory %" PRIuINUM "\n", i, dir_inum); tsk_error_print(stderr); free(path2); free(buf); return 1; } /* Ignore NTFS System files */ if ((TSK_FS_TYPE_ISNTFS(fs_file->fs_info->ftype)) && (fs_file->name->name[0] == '$')) { tsk_fs_file_close(fs_file); continue; } //printf("Processing %s/%s\n", path, fs_file->name->name); // make sure it's got metadata and not only a name if (fs_file->meta) { ssize_t cnt; /* Note that we could also cycle through all of the attributes in the * file by using one of the tsk_fs_attr_get() functions and reading it * with tsk_fs_attr_read(). See the File Systems section of the Library * User's Guide for more details: * http://www.sleuthkit.org/sleuthkit/docs/api-docs/ */ // read file contents if (fs_file->meta->type == TSK_FS_META_TYPE_REG) { int myflags = 0; for (off = 0; off < fs_file->meta->size; off += len) { if (fs_file->meta->size - off < 2048) len = (size_t) (fs_file->meta->size - off); else len = 2048; cnt = tsk_fs_file_read(fs_file, off, buf, len, (TSK_FS_FILE_READ_FLAG_ENUM) myflags); if (cnt == -1) { // could check tsk_errno here for a recovery error (TSK_ERR_FS_RECOVER) fprintf(stderr, "Error reading %s file: %s\n", ((fs_file->name-> flags & TSK_FS_NAME_FLAG_UNALLOC) || (fs_file->meta-> flags & TSK_FS_META_FLAG_UNALLOC)) ? "unallocated" : "allocated", fs_file->name->name); tsk_error_print(stderr); break; } else if (cnt != (ssize_t) len) { fprintf(stderr, "Warning: %" PRIuSIZE " of %" PRIuSIZE " bytes read from %s file %s\n", cnt, len, ((fs_file->name-> flags & TSK_FS_NAME_FLAG_UNALLOC) || (fs_file->meta-> flags & TSK_FS_META_FLAG_UNALLOC)) ? "unallocated" : "allocated", fs_file->name->name); } // do something with the data... } } // recurse into another directory (unless it is a '.' or '..') else if (fs_file->meta->type == TSK_FS_META_TYPE_DIR) { if (TSK_FS_ISDOT(fs_file->name->name) == 0) { // only go in if it is not on our stack if (tsk_stack_find(stack, fs_file->meta->addr) == 0) { // add the address to the top of the stack tsk_stack_push(stack, fs_file->meta->addr); snprintf(path2, 4096, "%s/%s", path, fs_file->name->name); if (proc_dir(fs_info, stack, fs_file->meta->addr, path2)) { tsk_fs_file_close(fs_file); tsk_fs_dir_close(fs_dir); free(path2); free(buf); return 1; } // pop the address tsk_stack_pop(stack); } } } } tsk_fs_file_close(fs_file); } tsk_fs_dir_close(fs_dir); free(path2); free(buf); return 0; }
/** * \ingroup fslib * * Find the meta data address for a given file name (UTF-8). * The basic idea of the function is to break the given name into its * subdirectories and start looking for each (starting in the root * directory). * * @param a_fs FS to analyze * @param a_path UTF-8 path of file to search for * @param [out] a_result Meta data address of file * @param [out] a_fs_name Copy of name details (or NULL if details not wanted) * @returns -1 on (system) error, 0 if found, and 1 if not found */ int8_t tsk_fs_path2inum(TSK_FS_INFO * a_fs, const char *a_path, TSK_INUM_T * a_result, TSK_FS_NAME * a_fs_name) { char *cpath; size_t clen; char *cur_dir; // The "current" directory or file we are looking for char *cur_attr; // The "current" attribute of the dir we are looking for TSK_INUM_T next_meta; uint8_t is_done; char *strtok_last; *a_result = 0; // copy path to a buffer that we can modify clen = strlen(a_path) + 1; if ((cpath = (char *) tsk_malloc(clen)) == NULL) { return -1; } strncpy(cpath, a_path, clen); // Get the first part of the directory path. cur_dir = (char *) strtok_r(cpath, "/", &strtok_last); cur_attr = NULL; /* If there is no token, then only a '/' was given */ if (cur_dir == NULL) { free(cpath); *a_result = a_fs->root_inum; // create the dummy entry if needed if (a_fs_name) { a_fs_name->meta_addr = a_fs->root_inum; // Note that we are not filling in meta_seq -- we could, we just aren't a_fs_name->type = TSK_FS_NAME_TYPE_DIR; a_fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (a_fs_name->name) a_fs_name->name[0] = '\0'; if (a_fs_name->shrt_name) a_fs_name->shrt_name[0] = '\0'; } return 0; } /* If this is NTFS, separate out the attribute of the current directory */ if (TSK_FS_TYPE_ISNTFS(a_fs->ftype) && ((cur_attr = strchr(cur_dir, ':')) != NULL)) { *(cur_attr) = '\0'; cur_attr++; } if (tsk_verbose) tsk_fprintf(stderr, "Looking for %s\n", cur_dir); // initialize the first place to look, the root dir next_meta = a_fs->root_inum; // we loop until we know the outcome and then exit. // everything should return from inside the loop. is_done = 0; while (is_done == 0) { size_t i; TSK_FS_FILE *fs_file_alloc = NULL; // set to the allocated file that is our target TSK_FS_FILE *fs_file_del = NULL; // set to an unallocated file that matches our criteria TSK_FS_DIR *fs_dir = NULL; // open the next directory in the recursion if ((fs_dir = tsk_fs_dir_open_meta(a_fs, next_meta)) == NULL) { free(cpath); return -1; } /* Verify this is indeed a directory. We had one reported * problem where a file was a disk image and opening it as * a directory found the directory entries inside of the file * and this caused problems... */ if ( !TSK_FS_IS_DIR_META(fs_dir->fs_file->meta->type)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_GENFS); tsk_error_set_errstr("Address %" PRIuINUM " is not for a directory\n", next_meta); free(cpath); return -1; } // cycle through each entry for (i = 0; i < tsk_fs_dir_getsize(fs_dir); i++) { TSK_FS_FILE *fs_file; uint8_t found_name = 0; if ((fs_file = tsk_fs_dir_get(fs_dir, i)) == NULL) { tsk_fs_dir_close(fs_dir); free(cpath); return -1; } /* * Check if this is the name that we are currently looking for, * as identified in 'cur_dir' */ if ((fs_file->name->name) && (a_fs->name_cmp(a_fs, fs_file->name->name, cur_dir) == 0)) { found_name = 1; } else if ((fs_file->name->shrt_name) && (a_fs->name_cmp(a_fs, fs_file->name->shrt_name, cur_dir) == 0)) { found_name = 1; } /* For NTFS, we have to check the attribute name. */ if ((found_name == 1) && (TSK_FS_TYPE_ISNTFS(a_fs->ftype))) { /* ensure we have the right attribute name */ if (cur_attr != NULL) { found_name = 0; if (fs_file->meta) { int cnt, i; // cycle through the attributes cnt = tsk_fs_file_attr_getsize(fs_file); for (i = 0; i < cnt; i++) { const TSK_FS_ATTR *fs_attr = tsk_fs_file_attr_get_idx(fs_file, i); if (!fs_attr) continue; if ((fs_attr->name) && (a_fs->name_cmp(a_fs, fs_attr->name, cur_attr) == 0)) { found_name = 1; break; } } } } } if (found_name) { /* If we found our file and it is allocated, then stop. If * it is unallocated, keep on going to see if we can get * an allocated hit */ if (fs_file->name->flags & TSK_FS_NAME_FLAG_ALLOC) { fs_file_alloc = fs_file; break; } else { // if we already have an unalloc and its addr is 0, then use the new one if ((fs_file_del) && (fs_file_del->name->meta_addr == 0)) { tsk_fs_file_close(fs_file_del); } fs_file_del = fs_file; } } // close the file if we did not save it for future analysis. else { tsk_fs_file_close(fs_file); fs_file = NULL; } } // we found a directory, go into it if ((fs_file_alloc) || (fs_file_del)) { const char *pname; TSK_FS_FILE *fs_file_tmp; // choose the alloc one first (if they both exist) if (fs_file_alloc) fs_file_tmp = fs_file_alloc; else fs_file_tmp = fs_file_del; pname = cur_dir; // save a copy of the current name pointer // advance to the next name cur_dir = (char *) strtok_r(NULL, "/", &(strtok_last)); cur_attr = NULL; if (tsk_verbose) tsk_fprintf(stderr, "Found it (%s), now looking for %s\n", pname, cur_dir); /* That was the last name in the path -- we found the file! */ if (cur_dir == NULL) { *a_result = fs_file_tmp->name->meta_addr; // make a copy if one was requested if (a_fs_name) { tsk_fs_name_copy(a_fs_name, fs_file_tmp->name); } if (fs_file_alloc) tsk_fs_file_close(fs_file_alloc); if (fs_file_del) tsk_fs_file_close(fs_file_del); tsk_fs_dir_close(fs_dir); free(cpath); return 0; } // update the attribute field, if needed if (TSK_FS_TYPE_ISNTFS(a_fs->ftype) && ((cur_attr = strchr(cur_dir, ':')) != NULL)) { *(cur_attr) = '\0'; cur_attr++; } // update the value for the next directory to open next_meta = fs_file_tmp->name->meta_addr; if (fs_file_alloc) { tsk_fs_file_close(fs_file_alloc); fs_file_alloc = NULL; } if (fs_file_del) { tsk_fs_file_close(fs_file_del); fs_file_del = NULL; } } // no hit in directory else { is_done = 1; } tsk_fs_dir_close(fs_dir); fs_dir = NULL; } free(cpath); return 1; }
uint8_t fatxxfs_open(FATFS_INFO *fatfs) { const char *func_name = "fatxxfs_open"; TSK_FS_INFO *fs = &(fatfs->fs_info); FATXXFS_SB *fatsb = (FATXXFS_SB*)(&fatfs->boot_sector_buffer); int i = 0; TSK_DADDR_T sectors = 0; TSK_FS_DIR * test_dir1; // Directories used to try opening the root directory // clean up any error messages that are lying around tsk_error_reset(); /* Calculate block sizes and layout info */ // sector size fatfs->ssize = tsk_getu16(fs->endian, fatsb->ssize); if (fatfs->ssize == 512) { fatfs->ssize_sh = 9; } else if (fatfs->ssize == 1024) { fatfs->ssize_sh = 10; } else if (fatfs->ssize == 2048) { fatfs->ssize_sh = 11; } else if (fatfs->ssize == 4096) { fatfs->ssize_sh = 12; } else { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Error: sector size (%d) is not a multiple of device size (%d)\nDo you have a disk image instead of a partition image?", fatfs->ssize, fs->dev_bsize); if (tsk_verbose) fprintf(stderr, "%s: Invalid sector size (%d)\n", func_name, fatfs->ssize); return 1; } // cluster size fatfs->csize = fatsb->csize; if ((fatfs->csize != 0x01) && (fatfs->csize != 0x02) && (fatfs->csize != 0x04) && (fatfs->csize != 0x08) && (fatfs->csize != 0x10) && (fatfs->csize != 0x20) && (fatfs->csize != 0x40) && (fatfs->csize != 0x80)) { if (tsk_verbose) fprintf(stderr, "%s: Invalid cluster size (%d)\n", func_name, fatfs->csize); tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr("Not a FATXX file system (cluster size)"); return 1; } // number of FAT tables fatfs->numfat = fatsb->numfat; if ((fatfs->numfat == 0) || (fatfs->numfat > 8)) { if (tsk_verbose) fprintf(stderr, "%s: Invalid number of FATS (%d)\n", func_name, fatfs->numfat); tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr("Not a FATXX file system (number of FATs)"); return 1; } /* We can't do a sanity check on this b.c. TSK_FS_TYPE_FAT32 has a value of 0 */ /* num of root entries */ fatfs->numroot = tsk_getu16(fs->endian, fatsb->numroot); /* if sectors16 is 0, then the number of sectors is stored in sectors32 */ if (0 == (sectors = tsk_getu16(fs->endian, fatsb->sectors16))) sectors = tsk_getu32(fs->endian, fatsb->sectors32); /* if secperfat16 is 0, then read sectperfat32 */ if (0 == (fatfs->sectperfat = tsk_getu16(fs->endian, fatsb->sectperfat16))) fatfs->sectperfat = tsk_getu32(fs->endian, fatsb->a.f32.sectperfat32); if (fatfs->sectperfat == 0) { if (tsk_verbose) fprintf(stderr, "%s: Invalid number of sectors per FAT (%d)\n", func_name, fatfs->sectperfat); tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Not a FATXX file system (invalid sectors per FAT)"); return 1; } fatfs->firstfatsect = tsk_getu16(fs->endian, fatsb->reserved); if ((fatfs->firstfatsect == 0) || (fatfs->firstfatsect > sectors)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr ("Not a FATXX file system (invalid first FAT sector %" PRIuDADDR ")", fatfs->firstfatsect); if (tsk_verbose) fprintf(stderr, "%s: Invalid first FAT (%" PRIuDADDR ")\n", func_name, fatfs->firstfatsect); return 1; } /* Calculate the block info * * The sector of the beginning of the data area - which is * after all of the FATs * * For TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16, the data area starts with the root * directory entries and then the first cluster. For TSK_FS_TYPE_FAT32, * the data area starts with clusters and the root directory * is somewhere in the data area */ fatfs->firstdatasect = fatfs->firstfatsect + fatfs->sectperfat * fatfs->numfat; /* The sector where the first cluster is located. It will be used * to translate cluster addresses to sector addresses * * For TSK_FS_TYPE_FAT32, the first cluster is the start of the data area and * it is after the root directory for TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16. At this * point in the program, numroot is set to 0 for TSK_FS_TYPE_FAT32 */ fatfs->firstclustsect = fatfs->firstdatasect + ((fatfs->numroot * 32 + fatfs->ssize - 1) / fatfs->ssize); /* total number of clusters */ fatfs->clustcnt = (sectors - fatfs->firstclustsect) / fatfs->csize; /* the first cluster is #2, so the final cluster is: */ fatfs->lastclust = 1 + fatfs->clustcnt; /* identify the FAT type by the total number of data clusters * this calculation is from the MS FAT Overview Doc * * A FAT file system made by another OS could use different values */ if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT_DETECT) { if (fatfs->clustcnt < 4085) { fatfs->fs_info.ftype = TSK_FS_TYPE_FAT12; } else if (fatfs->clustcnt < 65525) { fatfs->fs_info.ftype = TSK_FS_TYPE_FAT16; } else { fatfs->fs_info.ftype = TSK_FS_TYPE_FAT32; } fatfs->fs_info.ftype = fatfs->fs_info.ftype; } /* Some sanity checks */ else { if ((fatfs->fs_info.ftype == TSK_FS_TYPE_FAT12) && (fatfs->clustcnt >= 4085)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Too many sectors for TSK_FS_TYPE_FAT12: try auto-detect mode"); if (tsk_verbose) fprintf(stderr, "%s: Too many sectors for FAT12\n", func_name); return 1; } } if ((fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) && (fatfs->numroot != 0)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Invalid TSK_FS_TYPE_FAT32 image (numroot != 0)"); if (tsk_verbose) fprintf(stderr, "%s: numroom != 0 for FAT32\n", func_name); return 1; } if ((fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) && (fatfs->numroot == 0)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Invalid FAT image (numroot == 0, and not TSK_FS_TYPE_FAT32)"); if (tsk_verbose) fprintf(stderr, "%s: numroom == 0 and not FAT32\n", func_name); return 1; } /* additional sanity checks if we think we are using the backup boot sector. * The scenario to prevent here is if fat_open is called 6 sectors before the real start * of the file system, then we want to detect that it was not a backup that we saw. */ if (fatfs->using_backup_boot_sector) { // only FAT32 has backup boot sectors.. if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Invalid FAT image (Used what we thought was a backup boot sector, but it is not TSK_FS_TYPE_FAT32)"); if (tsk_verbose) fprintf(stderr, "%s: Had to use backup boot sector, but this isn't FAT32\n", func_name); return 1; } if (fatfs->numroot > 1) { uint8_t buf1[512]; uint8_t buf2[512]; int i2; int numDiffs; ssize_t cnt = 0; cnt = tsk_fs_read(fs, fatfs->firstfatsect * fatfs->ssize, (char *) buf1, 512); if (cnt != 512) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2("%s: FAT1", func_name); fs->tag = 0; return 1; } cnt = tsk_fs_read(fs, (fatfs->firstfatsect + fatfs->sectperfat) * fatfs->ssize, (char *) buf2, 512); if (cnt != 512) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2("%s: FAT2", func_name); fs->tag = 0; return 1; } numDiffs = 0; for (i2 = 0; i2 < 512; i2++) { if (buf1[i2] != buf2[i2]) { numDiffs++; } } if (numDiffs > 25) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Invalid FAT image (Too many differences between FATS from guessing (%d diffs))", numDiffs); if (tsk_verbose) fprintf(stderr, "%s: Too many differences in FAT from guessing (%d diffs)\n", func_name, numDiffs); return 1; } } } /* Set the mask to use on the cluster values */ if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT12) { fatfs->mask = FATFS_12_MASK; } else if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT16) { fatfs->mask = FATFS_16_MASK; } else if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) { fatfs->mask = FATFS_32_MASK; } else { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("Unknown FAT type in %s: %d\n", func_name, fatfs->fs_info.ftype); return 1; } fs->duname = "Sector"; /* the root directories are always after the FAT for TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16, * but are dynamically located for TSK_FS_TYPE_FAT32 */ if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) fatfs->rootsect = FATFS_CLUST_2_SECT(fatfs, tsk_getu32(fs->endian, fatsb->a.f32.rootclust)); else fatfs->rootsect = fatfs->firstdatasect; for (i = 0; i < FATFS_FAT_CACHE_N; i++) { fatfs->fatc_addr[i] = 0; fatfs->fatc_ttl[i] = 0; } /* * block calculations : although there are no blocks in fat, we will * use these fields for sector calculations */ fs->first_block = 0; fs->block_count = sectors; fs->last_block = fs->last_block_act = fs->block_count - 1; fs->block_size = fatfs->ssize; // determine the last block we have in this image if ((TSK_DADDR_T) ((fatfs->fs_info.img_info->size - fatfs->fs_info.offset) / fs->block_size) < fs->block_count) fs->last_block_act = (fatfs->fs_info.img_info->size - fatfs->fs_info.offset) / fs->block_size - 1; /* * inode calculations */ /* maximum number of dentries in a sector & cluster */ fatfs->dentry_cnt_se = fatfs->ssize / sizeof(FATXXFS_DENTRY); fatfs->dentry_cnt_cl = fatfs->dentry_cnt_se * fatfs->csize; fs->root_inum = FATFS_ROOTINO; fs->first_inum = FATFS_FIRSTINO; /* Calculate inode addresses for the virtual files (MBR, one or two FATS) * and the virtual orphan files directory. */ fs->last_inum = (FATFS_SECT_2_INODE(fatfs, fs->last_block_act + 1) - 1) + FATFS_NUM_VIRT_FILES(fatfs); fatfs->mbr_virt_inum = fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs) + 1; fatfs->fat1_virt_inum = fatfs->mbr_virt_inum + 1; if (fatfs->numfat == 2) { fatfs->fat2_virt_inum = fatfs->fat1_virt_inum + 1; } else { fatfs->fat2_virt_inum = fatfs->fat1_virt_inum; } /* Calculate the total number of inodes. */ fs->inum_count = fs->last_inum - fs->first_inum + 1; /* Volume ID */ for (fs->fs_id_used = 0; fs->fs_id_used < 4; fs->fs_id_used++) { if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) fs->fs_id[fs->fs_id_used] = fatsb->a.f32.vol_id[fs->fs_id_used]; else fs->fs_id[fs->fs_id_used] = fatsb->a.f16.vol_id[fs->fs_id_used]; } /* * Set the function pointers */ fs->block_walk = fatfs_block_walk; fs->block_getflags = fatfs_block_getflags; fs->inode_walk = fatfs_inode_walk; fs->istat = fatfs_istat; fs->file_add_meta = fatfs_inode_lookup; fs->get_default_attr_type = fatfs_get_default_attr_type; fs->load_attrs = fatfs_make_data_runs; fs->dir_open_meta = fatfs_dir_open_meta; fs->name_cmp = fatfs_name_cmp; fs->fsstat = fatxxfs_fsstat; fs->fscheck = fatfs_fscheck; fs->close = fatfs_close; fs->jblk_walk = fatfs_jblk_walk; fs->jentry_walk = fatfs_jentry_walk; fs->jopen = fatfs_jopen; fatfs->is_cluster_alloc = fatxxfs_is_cluster_alloc; fatfs->is_dentry = fatxxfs_is_dentry; fatfs->dinode_copy = fatxxfs_dinode_copy; fatfs->inode_lookup = fatxxfs_inode_lookup; fatfs->inode_walk_should_skip_dentry = fatxxfs_inode_walk_should_skip_dentry; fatfs->istat_attr_flags = fatxxfs_istat_attr_flags; fatfs->dent_parse_buf = fatxxfs_dent_parse_buf; // initialize the caches tsk_init_lock(&fatfs->cache_lock); tsk_init_lock(&fatfs->dir_lock); fatfs->inum2par = NULL; // Test to see if this is the odd Android case where the FAT entries have no short name // // If there are no entries found with the normal short name // and we find more entries by removing the short name test for allocated directories, then assume // this is the case where we have no short names fatfs->subtype = TSK_FATFS_SUBTYPE_SPEC; test_dir1 = tsk_fs_dir_open_meta(fs, fs->root_inum); if (test_dir1 != NULL && test_dir1->names_used <= 4){ // At most four automatic directories ($MBR, $FAT1, $FAT1, $OrphanFiles) TSK_FS_DIR * test_dir2; // to see if it's the Android FAT version fatfs->subtype = TSK_FATFS_SUBTYPE_ANDROID_1; test_dir2 = tsk_fs_dir_open_meta(fs, fs->root_inum); if (test_dir2 != NULL && test_dir2->names_used > test_dir1->names_used){ fatfs->subtype = TSK_FATFS_SUBTYPE_ANDROID_1; } else{ fatfs->subtype = TSK_FATFS_SUBTYPE_SPEC; } tsk_fs_dir_close(test_dir2); } tsk_fs_dir_close(test_dir1); return 0; }
/* Compare the differences between dir_open_meta and dir_open * @param a_path Path of directory to open * @param a_addr The metadata address of the same directory as the path * @returns 1 if a test failed */ static int test_dir_open_apis(TSK_FS_INFO * a_fs, const char *a_path, TSK_INUM_T a_addr) { TSK_FS_DIR *fs_dir_m; TSK_FS_DIR *fs_dir_p; TSK_FS_DIR *fs_dir_tmp; TSK_FS_FILE *fs_file_m; TSK_FS_FILE *fs_file_p; int retval = 0; size_t entry = 0; // open via inode addr fs_dir_m = tsk_fs_dir_open_meta(a_fs, a_addr); if (!fs_dir_m) { fprintf(stderr, "Error opening dir %" PRIuINUM " via meta\n", a_addr); tsk_error_print(stderr); return 1; } /* open the root directory to throw some more state into the system * in case data is cached from first call */ fs_dir_tmp = tsk_fs_dir_open_meta(a_fs, a_fs->root_inum); if (!fs_dir_tmp) { fprintf(stderr, "Error opening root directory via meta\n"); tsk_error_print(stderr); return 1; } // open via path fs_dir_p = tsk_fs_dir_open(a_fs, a_path); if (!fs_dir_p) { fprintf(stderr, "Error opening directory %s\n", a_path); tsk_error_print(stderr); return 1; } // test that path has the name structure set (correctly) if ((fs_dir_p->fs_file == NULL) || (fs_dir_p->fs_file->name == NULL)) { fprintf(stderr, "dir opened via path has null name (%s)\n", a_path); retval = 1; goto open_cleanup; } if (fs_dir_p->fs_file->name->meta_addr != fs_dir_p->fs_file->meta->addr) { fprintf(stderr, "dir opened via path has different meta addresses in name and meta (%s) (%" PRIuINUM " vs %" PRIuINUM "\n", a_path, fs_dir_p->fs_file->name->meta_addr, fs_dir_p->fs_file->meta->addr); retval = 1; goto open_cleanup; } // verify both methods have same dir addr if (fs_dir_p->fs_file->meta->addr != fs_dir_m->fs_file->meta->addr) { fprintf(stderr, "parent dir addrs from fs_dir_open_meta and via path are different: %" PRIuINUM " vs %" PRIuINUM " (%s - %" PRIuINUM "\n", fs_dir_p->fs_file->meta->addr, fs_dir_m->fs_file->meta->addr, a_path, a_addr); retval = 1; goto open_cleanup; } // verify path method has same dir addr as open via meta if (fs_dir_p->fs_file->meta->addr != a_addr) { fprintf(stderr, "parent dir addrs from fs_dir_open is diff from meta address %" PRIuINUM " (%s - %" PRIuINUM "\n", fs_dir_p->fs_file->meta->addr, a_path, a_addr); retval = 1; goto open_cleanup; } // verify both have same size if (tsk_fs_dir_getsize(fs_dir_p) != tsk_fs_dir_getsize(fs_dir_m)) { fprintf(stderr, "sizes from fs_dir_open_meta and via path are different: %" PRIuSIZE " vs %" PRIuSIZE " (%s - %" PRIuINUM "\n", tsk_fs_dir_getsize(fs_dir_p), tsk_fs_dir_getsize(fs_dir_m), a_path, a_addr); retval = 1; goto open_cleanup; } // compare the first entry in both. if (tsk_fs_dir_getsize(fs_dir_p) == 0) { fprintf(stderr, "directory sizes are 0\n"); retval = 1; goto open_cleanup; } fs_file_m = tsk_fs_dir_get(fs_dir_m, 0); if (fs_file_m == NULL) { fprintf(stderr, "Error opening entry 0 from meta open: %" PRIuINUM "\n", a_addr); tsk_error_print(stderr); retval = 1; goto open_cleanup; } fs_file_p = tsk_fs_dir_get(fs_dir_p, 0); if (fs_file_p == NULL) { fprintf(stderr, "Error opening entry 0 from path open: %" PRIuINUM "\n", a_addr); tsk_error_print(stderr); retval = 1; goto open_cleanup; } if (compare_names(fs_file_p->name, fs_file_m->name, 1)) { fprintf(stderr, "results from entry 0 are different\n"); retval = 1; goto open_cleanup; } tsk_fs_file_close(fs_file_m); tsk_fs_file_close(fs_file_p); // compare the last entry in both entry = tsk_fs_dir_getsize(fs_dir_m) - 1; fs_file_m = tsk_fs_dir_get(fs_dir_m, entry); if (fs_file_m == NULL) { fprintf(stderr, "Error opening entry %" PRIuSIZE " from meta open: %" PRIuINUM "\n", entry, a_addr); tsk_error_print(stderr); retval = 1; goto open_cleanup; } fs_file_p = tsk_fs_dir_get(fs_dir_p, entry); if (fs_file_p == NULL) { fprintf(stderr, "Error opening entry %" PRIuSIZE " from path open: %" PRIuINUM "\n", entry, a_addr); tsk_error_print(stderr); retval = 1; goto open_cleanup; } if (compare_names(fs_file_p->name, fs_file_m->name, 1)) { fprintf(stderr, "results from entry %" PRIuSIZE " are different\n", entry); retval = 1; goto open_cleanup; } tsk_fs_file_close(fs_file_m); tsk_fs_file_close(fs_file_p); open_cleanup: tsk_fs_dir_close(fs_dir_p); tsk_fs_dir_close(fs_dir_tmp); tsk_fs_dir_close(fs_dir_m); return retval; }
/* Test function that compares the dir_open/dir_get() APIs * with the dir_walk results * @param a_addr Address of directory to analyze * @returns 1 if a test failed. */ static int test_walk_apis(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr) { TSK_FS_DIR *fs_dir; int retval = 0; fs_dir = tsk_fs_dir_open_meta(a_fs, a_addr); if (!fs_dir) { fprintf(stderr, "Error opening dir %" PRIuINUM " via meta\n", a_addr); tsk_error_print(stderr); return 1; } // verify they have the same number of entries // walk the directory to get its count size_t walk_size = 0; if (tsk_fs_dir_walk(a_fs, a_addr, (TSK_FS_DIR_WALK_FLAG_ENUM) 0, dir_walk_count_cb, &walk_size)) { fprintf(stderr, "Error doing dent walk on dir %" PRIuINUM "\n", a_addr); retval = 1; goto walk_cleanup; } if (walk_size != tsk_fs_dir_getsize(fs_dir)) { fprintf(stderr, "Size returned by dir_walk different from dir_getsize: %" PRIuINUM ": %" PRIuSIZE " %" PRIuSIZE "\n", a_addr, walk_size, tsk_fs_dir_getsize(fs_dir)); retval = 1; goto walk_cleanup; } // verify each entry is the same for (size_t i = 0; i < tsk_fs_dir_getsize(fs_dir); i++) { TSK_FS_FILE *fs_file; fs_file = tsk_fs_dir_get(fs_dir, i); if (fs_file == NULL) { fprintf(stderr, "Error getting entry %" PRIuSIZE " from directory %" PRIuINUM "\n", i, a_addr); tsk_error_print(stderr); retval = 1; goto walk_cleanup; } if (fs_file->meta == NULL) { fprintf(stderr, "Error: %s (%" PRIuINUM ") does not have meta set in dir: \n", fs_file->name->name, fs_file->name->meta_addr); retval = 1; goto walk_cleanup; } s_found = 0; if (tsk_fs_dir_walk(a_fs, a_addr, (TSK_FS_DIR_WALK_FLAG_ENUM) 0, dir_walk_comp_cb, (void *) fs_file)) { fprintf(stderr, "Error doing dent walk on dir %" PRIuINUM "\n", a_addr); retval = 1; goto walk_cleanup; } if (s_found == 0) { fprintf(stderr, "entry %" PRIuSIZE " in dir not found via walk: %s\n", i, fs_file->name->name); retval = 1; goto walk_cleanup; } tsk_fs_file_close(fs_file); } walk_cleanup: tsk_fs_dir_close(fs_dir); return retval; }