/** * \internal * Add a set of consecutive runs to an attribute. This will add and remove FILLER entries * as needed and update internal variables. * * @param a_fs File system run is from * @param fs_attr Attribute to add run to * @param a_data_run_new The set of runs to add. * * @returns 1 on error and 0 on succes */ uint8_t tsk_fs_attr_add_run(TSK_FS_INFO * a_fs, TSK_FS_ATTR * a_fs_attr, TSK_FS_ATTR_RUN * a_data_run_new) { TSK_FS_ATTR_RUN *data_run_cur, *data_run_prev; TSK_DADDR_T run_len; tsk_error_reset(); if (a_fs_attr == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_attr_add_run: Error, a_fs_attr is NULL"); return 1; } // we only support the case of a null run if it is the only run... if (a_data_run_new == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_attr_add_run: Error, a_data_run_new is NULL (%" PRIuINUM ")", a_fs_attr->fs_file->meta->addr); return 1; } run_len = 0; data_run_cur = a_data_run_new; while (data_run_cur) { run_len += data_run_cur->len; data_run_cur = data_run_cur->next; } /* First thing, is to check if we can just add it to the end */ if ((a_fs_attr->nrd.run_end) && (a_fs_attr->nrd.run_end->offset + a_fs_attr->nrd.run_end->len == a_data_run_new->offset)) { a_fs_attr->nrd.run_end->next = a_data_run_new; // update the pointer to the end of the list while (a_fs_attr->nrd.run_end->next) a_fs_attr->nrd.run_end = a_fs_attr->nrd.run_end->next; /* return head of a_fs_attr list */ return 0; } // cycle through existing runs and see if we can add this into a filler spot data_run_cur = a_fs_attr->nrd.run; data_run_prev = NULL; while (data_run_cur) { if (tsk_verbose) tsk_fprintf(stderr, "tsk_fs_attr_add: %" PRIuOFF "@%" PRIuOFF " (Filler: %s)\n", data_run_cur->offset, data_run_cur->len, (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) ? "Yes" : "No"); /* Do we replace this filler spot? */ if (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) { /* This should never happen because we always add * the filler to start from VCN 0 */ if (data_run_cur->offset > a_data_run_new->offset) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_GENFS); tsk_error_set_errstr ("tsk_fs_attr_add_run: could not add data_run b.c. offset (%" PRIuOFF ") is larger than FILLER (%" PRIuOFF ") (%" PRIuINUM ")", a_data_run_new->offset, data_run_cur->offset, a_fs_attr->fs_file->meta->addr); dump_attr(a_fs_attr); return 1; } /* Check if the new run starts inside of this filler. */ if (data_run_cur->offset + data_run_cur->len > a_data_run_new->offset) { TSK_FS_ATTR_RUN *endrun; /* if the new starts at the same as the filler, * replace the pointer */ if (data_run_cur->offset == a_data_run_new->offset) { if (data_run_prev) data_run_prev->next = a_data_run_new; else a_fs_attr->nrd.run = a_data_run_new; } /* The new run does not start at the begining of * the filler, so make a new start filler */ else { TSK_FS_ATTR_RUN *newfill = tsk_fs_attr_run_alloc(); if (newfill == NULL) return 1; if (data_run_prev) data_run_prev->next = newfill; else a_fs_attr->nrd.run = newfill; newfill->next = a_data_run_new; newfill->len = a_data_run_new->offset - data_run_cur->offset; newfill->offset = data_run_cur->offset; newfill->flags = TSK_FS_ATTR_RUN_FLAG_FILLER; data_run_cur->len -= newfill->len; } /* get to the end of the run that we are trying to add */ endrun = a_data_run_new; while (endrun->next) endrun = endrun->next; /* if the filler is the same size as the * new one, replace it */ if (run_len == data_run_cur->len) { endrun->next = data_run_cur->next; // update the pointer to the end of the list (if we are the end) if (endrun->next == NULL) a_fs_attr->nrd.run_end = endrun; free(data_run_cur); } /* else adjust the last filler entry */ else { endrun->next = data_run_cur; data_run_cur->len -= run_len; data_run_cur->offset = a_data_run_new->offset + a_data_run_new->len; } return 0; } } data_run_prev = data_run_cur; data_run_cur = data_run_cur->next; } /* * There is no filler holding the location of this run, so * we will add it to the end of the list * * we got here because it did not fit in the current list or * because the current list is NULL * * At this point data_run_prev is the end of the existing list or * 0 if there is no list */ /* This is an error condition. * It means that we cycled through the existing runs, * ended at a VCN that is larger than what we are adding, * and never found a filler entry to insert it into... */ if ((data_run_prev) && (data_run_prev->offset + data_run_prev->len > a_data_run_new->offset)) { /* MAYBE this is because of a duplicate entry .. */ if ((data_run_prev->addr == a_data_run_new->addr) && (data_run_prev->len == a_data_run_new->len)) { // @@@ Sould be we freeing this....? What if the caller tries to write to it? tsk_fs_attr_run_free(a_data_run_new); return 0; } tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_GENFS); tsk_error_set_errstr ("fs_attr_add_run: error adding additional run (%" PRIuINUM "): No filler entry for %" PRIuDADDR ". Final: %" PRIuDADDR, a_fs_attr->fs_file->meta->addr, a_data_run_new->offset, data_run_prev->offset + data_run_prev->len); dump_attr(a_fs_attr); return 1; } /* we should add it right here */ else if (((data_run_prev) && (data_run_prev->offset + data_run_prev->len == a_data_run_new->offset)) || (a_data_run_new->offset == 0)) { if (data_run_prev) data_run_prev->next = a_data_run_new; else a_fs_attr->nrd.run = a_data_run_new; } /* we need to make a filler before it */ else { TSK_FS_ATTR_RUN *tmprun = tsk_fs_attr_run_alloc(); if (tmprun == NULL) return 1; if (data_run_prev) { data_run_prev->next = tmprun; tmprun->offset = data_run_prev->offset + data_run_prev->len; } else { a_fs_attr->nrd.run = tmprun; } tmprun->len = a_data_run_new->offset - tmprun->offset; tmprun->flags = TSK_FS_ATTR_RUN_FLAG_FILLER; tmprun->next = a_data_run_new; } // update the pointer to the end of the list a_fs_attr->nrd.run_end = a_data_run_new; while (a_fs_attr->nrd.run_end->next) a_fs_attr->nrd.run_end = a_fs_attr->nrd.run_end->next; return 0; }
/** \internal * Process an array of addresses and turn them into runs * * @param fs File system to analyze * @param fs_attr Data attribute to add runs to * @param addrs Buffer of addresses to process and turn into runs * @param addr_len Number of addresses in buffer * @param length Length of file remaining * * @returns the number of bytes processed and -1 if an error occurred */ static TSK_OFF_T unix_make_data_run_direct(TSK_FS_INFO * fs, TSK_FS_ATTR * fs_attr, TSK_DADDR_T * addrs, size_t addr_len, TSK_OFF_T length) { TSK_DADDR_T run_start = 0; TSK_DADDR_T run_len = 0; TSK_DADDR_T blks_processed = 0; size_t i; size_t fs_blen; // how big is each "block" (in fragments) if (addr_len == 0) { return 0; } // block_size is a fragment size in UFS, so we need to maintain length in fragments if (TSK_FS_TYPE_ISFFS(fs->ftype)) { FFS_INFO *ffs = (FFS_INFO *) fs; fs_blen = ffs->ffsbsize_f; } else { fs_blen = 1; } run_start = addrs[0]; run_len = fs_blen; /* Note that we are lazy about length. We stop only when a run is past length, * we do not end exactly at length -- although that should happen anyway. */ for (i = 0; i < addr_len; i++) { /* Make a new run if: * - This is the last addresss in the buffer * - The next address is not part of the current run * -- special case for sparse since they use 0 as an address */ if ((i + 1 == addr_len) || ((run_start + run_len != addrs[i + 1]) && (run_start != 0)) || ((run_start == 0) && (addrs[i + 1] != 0))) { TSK_FS_ATTR_RUN *data_run; // make a non-resident run data_run = tsk_fs_attr_run_alloc(); if (data_run == NULL) return -1; data_run->addr = run_start; data_run->len = run_len; if (run_start == 0) data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE; // save the run tsk_fs_attr_append_run(fs, fs_attr, data_run); // get ready for the next run if (i + 1 != addr_len) run_start = addrs[i + 1]; run_len = 0; // stop if we are past the length requested if (blks_processed * fs->block_size > (TSK_DADDR_T) length) break; } run_len += fs_blen; blks_processed += fs_blen; } return blks_processed * fs->block_size; }
/** * \internal * Set the needed fields along with an initial run list for a data attribute. To add more * runs, use ...._add_run(). * * @param a_fs File system the run comes from. * @param a_fs_attr The data attribute to initialize and add the run to * @param a_data_run_new The set of runs to add (can be NULL). * @param name Name of the attribute (can be NULL) * @param type Type of attribute to add run to * @param id Id of attribute to add run to * @param size Total size of the attribute (can be larger than length of initial run being added) * @param init_size Number of bytes in attribute that have been initialized (less then or equal to size) * (note that this sets the initialized size for the attribute and it will not be updated as more runs are added). * @param alloc_size Allocated size of the attribute (>= size). Identifies the slack space. * (note that this sets the allocated size for the attribute and it will not be updated as more runs are added). * @param flags Flags about compression, sparse etc. of data * @param compsize Compression unit size (in case it needs to be created) * * @returns 1 on error and 0 on success */ uint8_t tsk_fs_attr_set_run(TSK_FS_FILE * a_fs_file, TSK_FS_ATTR * a_fs_attr, TSK_FS_ATTR_RUN * a_data_run_new, const char *name, TSK_FS_ATTR_TYPE_ENUM type, uint16_t id, TSK_OFF_T size, TSK_OFF_T init_size, TSK_OFF_T alloc_size, TSK_FS_ATTR_FLAG_ENUM flags, uint32_t compsize) { if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("Null fs_file in tsk_fs_attr_set_run"); return 1; } if (a_fs_attr == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("Null fs_attr in tsk_fs_attr_set_run"); return 1; } if (alloc_size < size) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_attr_set_run: alloc_size (%" PRIuOFF ") is less than size (%" PRIuOFF ")", alloc_size, size); return 1; } a_fs_attr->fs_file = a_fs_file; a_fs_attr->flags = (TSK_FS_ATTR_INUSE | TSK_FS_ATTR_NONRES | flags); a_fs_attr->type = type; a_fs_attr->id = id; a_fs_attr->size = size; a_fs_attr->nrd.allocsize = alloc_size; a_fs_attr->nrd.initsize = init_size; a_fs_attr->nrd.compsize = compsize; if (fs_attr_put_name(a_fs_attr, name)) { return 1; } /* Add the a_data_run_new to the attribute. */ /* We support the ODD case where the run is NULL. In this case, * we set the attribute size info, but set everything else to NULL. */ if (a_data_run_new == NULL) { a_fs_attr->nrd.run = NULL; a_fs_attr->nrd.run_end = NULL; return 0; } /* * If this is not in the begining, then we need to make a filler * to account for the cluster numbers we haven't seen yet * * This commonly happens when we process an MFT entry that * is not a base entry and it is referenced in an $ATTR_LIST * * The $DATA attribute in the non-base have a non-zero * a_data_run_new->offset. */ if (a_data_run_new->offset != 0) { TSK_FS_ATTR_RUN *fill_run = tsk_fs_attr_run_alloc(); fill_run->flags = TSK_FS_ATTR_RUN_FLAG_FILLER; fill_run->offset = 0; fill_run->addr = 0; fill_run->len = a_data_run_new->offset; fill_run->next = a_data_run_new; a_data_run_new = fill_run; } a_fs_attr->nrd.run = a_data_run_new; // update the pointer to the end of the list a_fs_attr->nrd.run_end = a_data_run_new; while (a_fs_attr->nrd.run_end->next) { a_fs_attr->nrd.run_end = a_fs_attr->nrd.run_end->next; } return 0; }
/** \internal * Read an indirect block and process the contents to make a runlist from the pointers. * * @param fs File system to analyze * @param fs_attr Structure to save run data into * @param fs_attr_indir Structure to save addresses of indirect block pointers in * @param buf Buffers to read block data into (0 is block sized, 1+ are DADDR_T arrays based on FS type) * @param level Indirection level that this will process at (1+) * @param addr Address of block to read * @param length Length of file remaining * * @returns the number of bytes processed during call and -1 if an error occurred */ static TSK_OFF_T unix_make_data_run_indirect(TSK_FS_INFO * fs, TSK_FS_ATTR * fs_attr, TSK_FS_ATTR * fs_attr_indir, char *buf[], int level, TSK_DADDR_T addr, TSK_OFF_T length) { char *myname = "unix_make_data_run_indirect"; size_t addr_cnt = 0; TSK_DADDR_T *myaddrs = (TSK_DADDR_T *) buf[level]; TSK_OFF_T length_remain = length; TSK_OFF_T retval; size_t fs_bufsize; size_t fs_blen; TSK_FS_ATTR_RUN *data_run; if (tsk_verbose) tsk_fprintf(stderr, "%s: level %d block %" PRIuDADDR "\n", myname, level, addr); // block_size is a fragment size in UFS, so we need to maintain length in fragments if (TSK_FS_TYPE_ISFFS(fs->ftype)) { FFS_INFO *ffs = (FFS_INFO *) fs; fs_blen = ffs->ffsbsize_f; fs_bufsize = ffs->ffsbsize_b; } else { fs_blen = 1; fs_bufsize = fs->block_size; } if (addr > fs->last_block) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr("unix: Indirect block address too large: %" PRIuDADDR "", addr); return -1; } // make a non-resident run data_run = tsk_fs_attr_run_alloc(); if (data_run == NULL) return -1; data_run->addr = addr; data_run->len = fs_blen; /* * Read a block of disk addresses. */ // sparse if (addr == 0) { memset(buf[0], 0, fs_bufsize); data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE; } else { ssize_t cnt; // read the data into the scratch buffer cnt = tsk_fs_read_block(fs, addr, buf[0], fs_bufsize); if (cnt != fs_bufsize) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2("unix_make_data_run_indir: Block %" PRIuDADDR, addr); return -1; } } // save the run tsk_fs_attr_append_run(fs, fs_attr_indir, data_run); // convert the raw addresses to the correct endian ordering if ((fs->ftype == TSK_FS_TYPE_FFS1) || (fs->ftype == TSK_FS_TYPE_FFS1B) || (TSK_FS_TYPE_ISEXT(fs->ftype))) { size_t n; uint32_t *iaddr = (uint32_t *) buf[0]; addr_cnt = fs_bufsize / sizeof(*iaddr); for (n = 0; n < addr_cnt; n++) { myaddrs[n] = tsk_getu32(fs->endian, (uint8_t *) & iaddr[n]); } } else if (fs->ftype == TSK_FS_TYPE_FFS2) { size_t n; uint64_t *iaddr = (uint64_t *) buf[0]; addr_cnt = fs_bufsize / sizeof(*iaddr); for (n = 0; n < addr_cnt; n++) { myaddrs[n] = tsk_getu64(fs->endian, (uint8_t *) & iaddr[n]); } } // pass the addresses to the next level if (level == 1) { retval = unix_make_data_run_direct(fs, fs_attr, myaddrs, addr_cnt, length_remain); if (retval != -1) { length_remain -= retval; } } else { size_t i; retval = 0; for (i = 0; i < addr_cnt && retval != -1; i++) { retval = unix_make_data_run_indirect(fs, fs_attr, fs_attr_indir, buf, level - 1, myaddrs[i], length_remain); if (retval == -1) { break; } else { length_remain -= retval; } } } if (retval == -1) return -1; else return length - length_remain; }