📄 fatfs.c
字号:
/*** fatfs** The Sleuth Kit **** Content and meta data layer support for the FAT file system **** Brian Carrier [carrier <at> sleuthkit [dot] org]** Copyright (c) 2006-2008 Brian Carrier, Basis Technology. All Rights reserved** Copyright (c) 2003-2005 Brian Carrier. All rights reserved **** TASK** Copyright (c) 2002 Brian Carrier, @stake Inc. All rights reserved****** This software is distributed under the Common Public License 1.0**** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05)***//** * \file fatfs.c * Contains the internal TSK FAT file system code to handle basic file system * processing for opening file system, processing sectors, and directory entries. */#include "tsk_fs_i.h"#include "tsk_fatfs.h"/* * Implementation NOTES * * TSK_FS_META contains the first cluster. file_walk will return sector * values though because the cluster numbers do not start until after * the FAT. That makes it very hard to address the first few blocks! * * Inodes numbers do not exist in FAT. To make up for this we will count * directory entries as the inodes. As the root directory does not have * any records in FAT, we will give it times of 0 and call it inode 2 to * keep consistent with UNIX. After that, each 32-byte slot is numbered * as though it were a directory entry (even if it is not). Therefore, * when an inode walk is performed, not all inode values will be displayed * even when '-e' is given for ils. * * Progs like 'ils -e' are very slow because we have to look at each * block to see if it is a file system structure. *//* TTL is 0 if the entry has not been used. TTL of 1 means it was the * most recently used, and TTL of FAT_CACHE_N means it was the least * recently used. This function has a LRU replacement algo */// return -1 on error, or cache index on success (0 to FAT_CACHE_N)static intgetFATCacheIdx(FATFS_INFO * fatfs, TSK_DADDR_T sect){ int i, cidx; ssize_t cnt; TSK_FS_INFO *fs = (TSK_FS_INFO *) & fatfs->fs_info; // see if we already have it in the cache for (i = 0; i < FAT_CACHE_N; i++) { if ((fatfs->fatc_ttl[i] > 0) && (sect >= fatfs->fatc_addr[i]) && (sect < (fatfs->fatc_addr[i] + FAT_CACHE_S))) { int a; // update the TTLs to push i to the front for (a = 0; a < FAT_CACHE_N; a++) { if (fatfs->fatc_ttl[a] == 0) continue; if (fatfs->fatc_ttl[a] < fatfs->fatc_ttl[i]) fatfs->fatc_ttl[a]++; } fatfs->fatc_ttl[i] = 1;// fprintf(stdout, "FAT Hit: %d\n", sect);// fflush(stdout); return i; } }// fprintf(stdout, "FAT Miss: %d\n", (int)sect);// fflush(stdout); // Look for an unused entry or an entry with a TTL of FAT_CACHE_N cidx = 0; for (i = 0; i < FAT_CACHE_N; i++) { if ((fatfs->fatc_ttl[i] == 0) || (fatfs->fatc_ttl[i] >= FAT_CACHE_N)) { cidx = i; } }// fprintf(stdout, "FAT Removing: %d\n", (int)fatfs->fatc_addr[cidx]); // fflush(stdout); // read the data cnt = tsk_fs_read(fs, sect * fs->block_size, fatfs->fatc_buf[cidx], FAT_CACHE_B); if (cnt != FAT_CACHE_B) { if (cnt >= 0) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_READ; } snprintf(tsk_errstr2, TSK_ERRSTR_L, "getFATCacheIdx: FAT: %" PRIuDADDR, sect); return -1; } // update the TTLs if (fatfs->fatc_ttl[cidx] == 0) // special case for unused entry fatfs->fatc_ttl[cidx] = FAT_CACHE_N + 1; for (i = 0; i < FAT_CACHE_N; i++) { if (fatfs->fatc_ttl[i] == 0) continue; if (fatfs->fatc_ttl[i] < fatfs->fatc_ttl[cidx]) fatfs->fatc_ttl[i]++; } fatfs->fatc_ttl[cidx] = 1; fatfs->fatc_addr[cidx] = sect; return cidx;}/* * Set *value to the entry in the File Allocation Table (FAT) * for the given cluster * * *value is in clusters and may need to be coverted to * sectors by the calling function * * Invalid values in the FAT (i.e. greater than the largest * cluster have a value of 0 returned and a 0 return value. * * Return 1 on error and 0 on success */uint8_tfatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value){ uint8_t *a_ptr; uint16_t tmp16; TSK_FS_INFO *fs = (TSK_FS_INFO *) & fatfs->fs_info; TSK_DADDR_T sect, offs; ssize_t cnt; int cidx; /* Sanity Check */ if (clust > fatfs->lastclust) { /* silently ignore requests for the unclustered sectors... */ if ((clust == fatfs->lastclust + 1) && ((fatfs->firstclustsect + fatfs->csize * fatfs->clustcnt - 1) != fs->last_block)) { if (tsk_verbose) tsk_fprintf(stderr, "fatfs_getFAT: Ignoring request for non-clustered sector\n"); return 0; } tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "fatfs_getFAT: invalid cluster address: %" PRIuDADDR, clust); return 1; } switch (fatfs->fs_info.ftype) { case TSK_FS_TYPE_FAT12: if (clust & 0xf000) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "fatfs_getFAT: TSK_FS_TYPE_FAT12 Cluster %" PRIuDADDR " too large", clust); return 1; } /* id the sector in the FAT */ sect = fatfs->firstfatsect + ((clust + (clust >> 1)) >> fatfs->ssize_sh); /* Load the FAT if we don't have it */ // see if it is in the cache if (-1 == (cidx = getFATCacheIdx(fatfs, sect))) return 1; /* get the offset into the cache */ offs = ((sect - fatfs->fatc_addr[cidx]) << fatfs->ssize_sh) + (clust + (clust >> 1)) % fatfs->ssize; /* special case when the 12-bit value goes across the cache * we load the cache to start at this sect. The cache * size must therefore be at least 2 sectors large */ if (offs == (FAT_CACHE_B - 1)) { // read the data -- TTLs will already have been updated cnt = tsk_fs_read(fs, sect * fs->block_size, fatfs->fatc_buf[cidx], FAT_CACHE_B); if (cnt != FAT_CACHE_B) { if (cnt >= 0) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_READ; } snprintf(tsk_errstr2, TSK_ERRSTR_L, "fatfs_getFAT: TSK_FS_TYPE_FAT12 FAT overlap: %" PRIuDADDR, sect); return 1; } fatfs->fatc_addr[cidx] = sect; offs = (clust + (clust >> 1)) % fatfs->ssize; } /* get pointer to entry in current buffer */ a_ptr = (uint8_t *) fatfs->fatc_buf[cidx] + offs; tmp16 = tsk_getu16(fs->endian, a_ptr); /* slide it over if it is one of the odd clusters */ if (clust & 1) tmp16 >>= 4; *value = tmp16 & FATFS_12_MASK; /* sanity check */ if ((*value > (fatfs->lastclust)) && (*value < (0x0ffffff7 & FATFS_12_MASK))) { if (tsk_verbose) tsk_fprintf(stderr, "fatfs_getFAT: TSK_FS_TYPE_FAT12 cluster (%" PRIuDADDR ") too large (%" PRIuDADDR ") - resetting\n", clust, *value); *value = 0; } return 0; case TSK_FS_TYPE_FAT16: /* Get sector in FAT for cluster and load it if needed */ sect = fatfs->firstfatsect + ((clust << 1) >> fatfs->ssize_sh); if (-1 == (cidx = getFATCacheIdx(fatfs, sect))) return 1; /* get pointer to entry in the cache buffer */ a_ptr = (uint8_t *) fatfs->fatc_buf[cidx] + ((sect - fatfs->fatc_addr[cidx]) << fatfs->ssize_sh) + ((clust << 1) % fatfs->ssize); *value = tsk_getu16(fs->endian, a_ptr) & FATFS_16_MASK; /* sanity check */ if ((*value > (fatfs->lastclust)) && (*value < (0x0ffffff7 & FATFS_16_MASK))) { if (tsk_verbose) tsk_fprintf(stderr, "fatfs_getFAT: contents of TSK_FS_TYPE_FAT16 entry %" PRIuDADDR " too large - resetting\n", clust); *value = 0; } return 0; case TSK_FS_TYPE_FAT32: /* Get sector in FAT for cluster and load if needed */ sect = fatfs->firstfatsect + ((clust << 2) >> fatfs->ssize_sh); if (-1 == (cidx = getFATCacheIdx(fatfs, sect))) return 1; /* get pointer to entry in current buffer */ a_ptr = (uint8_t *) fatfs->fatc_buf[cidx] + ((sect - fatfs->fatc_addr[cidx]) << fatfs->ssize_sh) + (clust << 2) % fatfs->ssize; *value = tsk_getu32(fs->endian, a_ptr) & FATFS_32_MASK; /* sanity check */ if ((*value > fatfs->lastclust) && (*value < (0x0ffffff7 & FATFS_32_MASK))) { if (tsk_verbose) tsk_fprintf(stderr, "fatfs_getFAT: contents of entry %" PRIuDADDR " too large - resetting\n", clust); *value = 0; } return 0; default: tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "fatfs_getFAT: Unknown FAT type: %d", fatfs->fs_info.ftype); return 1; }}/* Return 1 if allocated, 0 if unallocated, and -1 if error */int8_tfatfs_is_clustalloc(FATFS_INFO * fatfs, TSK_DADDR_T clust){ TSK_DADDR_T content; if (fatfs_getFAT(fatfs, clust, &content)) return -1; else if (content == FATFS_UNALLOC) return 0; else return 1;}/* * Identifies if a sector is allocated * * If it is less than the data area, then it is allocated * else the FAT table is consulted * * Return 1 if allocated, 0 if unallocated, and -1 if error */int8_tfatfs_is_sectalloc(FATFS_INFO * fatfs, TSK_DADDR_T sect){ TSK_FS_INFO *fs = (TSK_FS_INFO *) fatfs; /* If less than the first cluster sector, then it is allocated * otherwise check the FAT */ if (sect < fatfs->firstclustsect) return 1; /* If we are in the unused area, then we are "unalloc" */ if ((sect <= fs->last_block) && (sect >= (fatfs->firstclustsect + fatfs->csize * fatfs->clustcnt))) return 0; return fatfs_is_clustalloc(fatfs, FATFS_SECT_2_CLUST(fatfs, sect));}TSK_FS_BLOCK_FLAG_ENUMfatfs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr){ FATFS_INFO *fatfs = (FATFS_INFO *) a_fs; int flags = 0; // FATs and boot sector if (a_addr < fatfs->firstdatasect) { flags = TSK_FS_BLOCK_FLAG_META | TSK_FS_BLOCK_FLAG_ALLOC; } // root directory for FAT12/16 else if (a_addr < fatfs->firstclustsect) { flags = TSK_FS_BLOCK_FLAG_CONT | TSK_FS_BLOCK_FLAG_ALLOC; } else { int retval; flags = TSK_FS_BLOCK_FLAG_CONT; /* Identify its allocation status */ retval = fatfs_is_sectalloc(fatfs, a_addr); if (retval != -1) { if (retval == 1) flags |= TSK_FS_BLOCK_FLAG_ALLOC; else flags |= TSK_FS_BLOCK_FLAG_UNALLOC; } } return flags;}/************************************************************************** * * BLOCK WALKING * *************************************************************************//* ** Walk the sectors of the partition. **** NOTE: This is by SECTORS and not CLUSTERS** _flags: TSK_FS_BLOCK_FLAG_ALLOC, TSK_FS_BLOCK_FLAG_UNALLOC, TSK_FS_BLOCK_FLAG_META** TSK_FS_BLOCK_FLAG_CONT***/uint8_tfatfs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk, TSK_DADDR_T a_end_blk, TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags, TSK_FS_BLOCK_WALK_CB a_action, void *a_ptr){ char *myname = "fatfs_block_walk"; FATFS_INFO *fatfs = (FATFS_INFO *) fs; char *data_buf = NULL; ssize_t cnt; TSK_FS_BLOCK *fs_block; TSK_DADDR_T addr; int myflags; unsigned int i; // clean up any error messages that are lying around tsk_error_reset(); /* * Sanity checks. */ if (a_start_blk < fs->first_block || a_start_blk > fs->last_block) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_WALK_RNG; snprintf(tsk_errstr, TSK_ERRSTR_L, "%s: Start block: %" PRIuDADDR "", myname, a_start_blk); return 1; } if (a_end_blk < fs->first_block || a_end_blk > fs->last_block) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_WALK_RNG; snprintf(tsk_errstr, TSK_ERRSTR_L, "%s: End block: %" PRIuDADDR "", myname, a_end_blk);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -