/** * dump_file */ static int dump_file(ntfs_volume *vol, ntfs_inode *ino) { char buffer[1024]; ntfs_attr_search_ctx *ctx; ATTR_RECORD *rec; int i; runlist *runs; utils_inode_get_name(ino, buffer, sizeof(buffer)); ntfs_log_info("Dump: %s\n", buffer); ctx = ntfs_attr_get_search_ctx(ino, NULL); while ((rec = find_attribute(AT_UNUSED, ctx))) { ntfs_log_info(" 0x%02x - ", rec->type); if (rec->non_resident) { ntfs_log_info("non-resident\n"); runs = ntfs_mapping_pairs_decompress(vol, rec, NULL); if (runs) { ntfs_log_info(" VCN LCN Length\n"); for (i = 0; runs[i].length > 0; i++) { ntfs_log_info(" %8lld %8lld %8lld\n", (long long)runs[i].vcn, (long long)runs[i].lcn, (long long) runs[i].length); } free(runs); } } else { ntfs_log_info("resident\n"); } } ntfs_attr_put_search_ctx(ctx); return 0; }
/** * info */ static int info(ntfs_volume *vol) { u64 a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u; int cb, sb, cps; u64 uc = 0, mc = 0, fc = 0; struct mft_search_ctx *m_ctx; ntfs_attr_search_ctx *a_ctx; runlist_element *rl; ATTR_RECORD *rec; int z; int inuse = 0; m_ctx = mft_get_search_ctx(vol); m_ctx->flags_search = FEMR_IN_USE | FEMR_METADATA | FEMR_BASE_RECORD | FEMR_NOT_BASE_RECORD; while (mft_next_record(m_ctx) == 0) { if (!(m_ctx->flags_match & FEMR_IN_USE)) continue; inuse++; a_ctx = ntfs_attr_get_search_ctx(m_ctx->inode, NULL); while ((rec = find_attribute(AT_UNUSED, a_ctx))) { if (!rec->non_resident) continue; rl = ntfs_mapping_pairs_decompress(vol, rec, NULL); for (z = 0; rl[z].length > 0; z++) { if (rl[z].lcn >= 0) { if (m_ctx->flags_match & FEMR_METADATA) mc += rl[z].length; else uc += rl[z].length; } } free(rl); } ntfs_attr_put_search_ctx(a_ctx); } mft_put_search_ctx(m_ctx); cb = vol->cluster_size_bits; sb = vol->sector_size_bits; cps = cb - sb; fc = vol->nr_clusters-mc-uc; fc <<= cb; mc <<= cb; uc <<= cb; a = vol->sector_size; b = vol->cluster_size; c = 1 << cps; d = vol->nr_clusters << cb; e = vol->nr_clusters; f = vol->nr_clusters >> cps; g = vol->mft_na->initialized_size >> vol->mft_record_size_bits; h = inuse; i = h * 100 / g; j = fc; k = fc >> sb; l = fc >> cb; m = fc * 100 / b / e; n = uc; o = uc >> sb; p = uc >> cb; q = uc * 100 / b / e; r = mc; s = mc >> sb; t = mc >> cb; u = mc * 100 / b / e; ntfs_log_info("bytes per sector : %llu\n", (unsigned long long)a); ntfs_log_info("bytes per cluster : %llu\n", (unsigned long long)b); ntfs_log_info("sectors per cluster : %llu\n", (unsigned long long)c); ntfs_log_info("bytes per volume : %llu\n", (unsigned long long)d); ntfs_log_info("sectors per volume : %llu\n", (unsigned long long)e); ntfs_log_info("clusters per volume : %llu\n", (unsigned long long)f); ntfs_log_info("initialized mft records : %llu\n", (unsigned long long)g); ntfs_log_info("mft records in use : %llu\n", (unsigned long long)h); ntfs_log_info("mft records percentage : %llu\n", (unsigned long long)i); ntfs_log_info("bytes of free space : %llu\n", (unsigned long long)j); ntfs_log_info("sectors of free space : %llu\n", (unsigned long long)k); ntfs_log_info("clusters of free space : %llu\n", (unsigned long long)l); ntfs_log_info("percentage free space : %llu\n", (unsigned long long)m); ntfs_log_info("bytes of user data : %llu\n", (unsigned long long)n); ntfs_log_info("sectors of user data : %llu\n", (unsigned long long)o); ntfs_log_info("clusters of user data : %llu\n", (unsigned long long)p); ntfs_log_info("percentage user data : %llu\n", (unsigned long long)q); ntfs_log_info("bytes of metadata : %llu\n", (unsigned long long)r); ntfs_log_info("sectors of metadata : %llu\n", (unsigned long long)s); ntfs_log_info("clusters of metadata : %llu\n", (unsigned long long)t); ntfs_log_info("percentage metadata : %llu\n", (unsigned long long)u); return 0; }
/** * Load the runlist of the <attr_type> attribute. * * Return NULL if an error. * The caller is responsible on freeing the allocated memory if the result is not NULL. * * Set size_of_file_record to some reasonable size when in doubt (the Windows default is 1024.) * * attr_type must be little endian. * * This function has code duplication with check_file_record() and * check_attr_record() but its goal is to be less strict. Thus the * duplicated checks are the minimal required for not crashing. * * Assumes dev is open. */ static runlist *load_runlist(ntfs_volume *rawvol, s64 offset_to_file_record, u32 attr_type, u32 size_of_file_record) { u8 *buf; u16 attrs_offset; u32 length; ATTR_RECORD *attr_rec; if (size_of_file_record<22) // offset to attrs_offset return NULL; buf = (u8*)ntfs_malloc(size_of_file_record); if (!buf) return NULL; if (ntfs_pread(rawvol->dev, offset_to_file_record, size_of_file_record, buf) != size_of_file_record) { check_failed("Failed to read file record at offset %lld (0x%llx).\n", (long long)offset_to_file_record, (long long)offset_to_file_record); return NULL; } attrs_offset = le16_to_cpu(((MFT_RECORD*)buf)->attrs_offset); // first attribute must be after the header. if (attrs_offset<42) { check_failed("First attribute must be after the header (%u).\n", (int)attrs_offset); } attr_rec = (ATTR_RECORD *)(buf + attrs_offset); //printf("uv1.\n"); while ((u8*)attr_rec<=buf+size_of_file_record-4) { //printf("Attr type: 0x%x.\n", attr_rec->type); // Check attribute record. (Only what is in the buffer) if (attr_rec->type==AT_END) { check_failed("Attribute 0x%x not found in file record at offset %lld (0x%llx).\n", (int)le32_to_cpu(attr_rec->type), (long long)offset_to_file_record, (long long)offset_to_file_record); return NULL; } if ((u8*)attr_rec>buf+size_of_file_record-8) { // not AT_END yet no room for the length field. check_failed("Attribute 0x%x is not AT_END, yet no " "room for the length field.\n", (int)le32_to_cpu(attr_rec->type)); return NULL; } length = le32_to_cpu(attr_rec->length); // Check that this attribute does not overflow the mft_record if ((u8*)attr_rec+length >= buf+size_of_file_record) { check_failed("Attribute (0x%x) is larger than FILE record at offset %lld (0x%llx).\n", (int)le32_to_cpu(attr_rec->type), (long long)offset_to_file_record, (long long)offset_to_file_record); return NULL; } // todo: what ATTRIBUTE_LIST (0x20)? if (attr_rec->type==attr_type) { // Eurika! // ntfs_mapping_pairs_decompress only use two values from vol. Just fake it. // todo: it will also use vol->major_ver if defined(DEBUG). But only for printing purposes. // Assume ntfs_boot_sector_parse() was called. return ntfs_mapping_pairs_decompress(rawvol, attr_rec, NULL); } attr_rec = (ATTR_RECORD*)((u8*)attr_rec+length); } // If we got here, there was an overflow. check_failed("file record corrupted at offset %lld (0x%llx).\n", (long long)offset_to_file_record, (long long)offset_to_file_record); return NULL; }