/** \internal * Processes a non-resident TSK_FS_ATTR structure and calls the callback with the associated * data. * * @param fs_attr Resident data structure to be walked * @param a_flags Flags for walking * @param a_action Callback action * @param a_ptr Pointer to data that is passed to callback * @returns 1 on error or 0 on success */ static uint8_t tsk_fs_attr_walk_nonres(const TSK_FS_ATTR * fs_attr, TSK_FS_FILE_WALK_FLAG_ENUM a_flags, TSK_FS_FILE_WALK_CB a_action, void *a_ptr) { char *buf = NULL; TSK_OFF_T tot_size; TSK_OFF_T off = 0; TSK_FS_ATTR_RUN *fs_attr_run; int retval; uint32_t skip_remain; TSK_FS_INFO *fs = fs_attr->fs_file->fs_info; uint8_t stop_loop = 0; if ((fs_attr->flags & TSK_FS_ATTR_NONRES) == 0) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_file_walk_nonres: called with non-non-resident data"); return 1; } /* if we want the slack space too, then use the allocsize */ if (a_flags & TSK_FS_FILE_WALK_FLAG_SLACK) tot_size = fs_attr->nrd.allocsize; else tot_size = fs_attr->size; skip_remain = fs_attr->nrd.skiplen; if ((a_flags & TSK_FS_FILE_WALK_FLAG_AONLY) == 0) { if ((buf = (char *) tsk_malloc(fs->block_size)) == NULL) { return 1; } } /* cycle through the number of runs we have */ retval = TSK_WALK_CONT; for (fs_attr_run = fs_attr->nrd.run; fs_attr_run; fs_attr_run = fs_attr_run->next) { TSK_DADDR_T addr, len_idx; addr = fs_attr_run->addr; /* cycle through each block in the run */ for (len_idx = 0; len_idx < fs_attr_run->len; len_idx++) { TSK_FS_BLOCK_FLAG_ENUM myflags; /* If the address is too large then give an error */ if (addr + len_idx > fs->last_block) { if (fs_attr->fs_file->meta-> flags & TSK_FS_META_FLAG_UNALLOC) tsk_error_set_errno(TSK_ERR_FS_RECOVER); else tsk_error_set_errno(TSK_ERR_FS_BLK_NUM); tsk_error_set_errstr ("Invalid address in run (too large): %" PRIuDADDR "", addr + len_idx); return 1; } // load the buffer if they want more than just the address if ((a_flags & TSK_FS_FILE_WALK_FLAG_AONLY) == 0) { /* sparse files just get 0s */ if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) { memset(buf, 0, fs->block_size); } /* FILLER entries exist when the source file system can store run * info out of order and we did not get all of the run info. We * return 0s if data is read from this type of run. */ else if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) { memset(buf, 0, fs->block_size); if (tsk_verbose) fprintf(stderr, "tsk_fs_attr_walk_nonres: File %" PRIuINUM " has FILLER entry, using 0s\n", fs_attr->fs_file->meta->addr); } // we return 0s for reads past the initsize else if ((off >= fs_attr->nrd.initsize) && ((a_flags & TSK_FS_FILE_READ_FLAG_SLACK) == 0)) { memset(buf, 0, fs->block_size); } else { ssize_t cnt; cnt = tsk_fs_read_block (fs, addr + len_idx, buf, fs->block_size); if (cnt != fs->block_size) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2 ("tsk_fs_file_walk: Error reading block at %" PRIuDADDR, addr + len_idx); return 1; } if ((off + fs->block_size > fs_attr->nrd.initsize) && ((a_flags & TSK_FS_FILE_READ_FLAG_SLACK) == 0)) { memset(&buf[fs_attr->nrd.initsize - off], 0, fs->block_size - (size_t) (fs_attr->nrd.initsize - off)); } } } /* Need to account for the skip length, which is the number of bytes * in the start of the attribute that are skipped and that are not * included in the overall length. We will seek past those and not * return those in the action. We just read a block size so check * if there is data to be returned in this buffer. */ if (skip_remain >= fs->block_size) { skip_remain -= fs->block_size; } else { size_t ret_len; /* Do we want to return a full block, or just the end? */ if ((TSK_OFF_T) fs->block_size - skip_remain < tot_size - off) ret_len = fs->block_size - skip_remain; else ret_len = (size_t) (tot_size - off); /* Only do sparse or FILLER clusters if NOSPARSE is not set */ if ((fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) || (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) || (off > fs_attr->nrd.initsize)) { myflags = fs->block_getflags(fs, 0); myflags |= TSK_FS_BLOCK_FLAG_SPARSE; if ((a_flags & TSK_FS_FILE_WALK_FLAG_NOSPARSE) == 0) { retval = a_action(fs_attr->fs_file, off, 0, &buf[skip_remain], ret_len, myflags, a_ptr); } } else { myflags = fs->block_getflags(fs, addr + len_idx); myflags |= TSK_FS_BLOCK_FLAG_RAW; retval = a_action(fs_attr->fs_file, off, addr + len_idx, &buf[skip_remain], ret_len, myflags, a_ptr); } off += ret_len; skip_remain = 0; if (retval != TSK_WALK_CONT) { stop_loop = 1; break; } if (off >= tot_size) { stop_loop = 1; break; } } } if (stop_loop) break; } if (buf) free(buf); if (retval == TSK_WALK_ERROR) return 1; else return 0; }