📄 ntfsresize.c
字号:
/** * write_mft_record * * Write an MFT Record back to the disk. If the read-only command line option * was given, this function will do nothing. */static int write_mft_record(ntfs_resize_t *resize, ntfs_volume *v, const MFT_REF mref, MFT_RECORD *buf){ if (ntfs_mft_record_write(v, mref, buf)) { err_printf(resize, "ntfs_mft_record_write failed"); return (-1); }// if (v->dev->d_ops->sync(v->dev) == -1)// perr_exit("Failed to sync device"); return 0;}static int lseek_to_cluster(ntfs_resize_t *resize, ntfs_volume *vol, s64 lcn){ off_t pos; pos = (off_t)(lcn * vol->cluster_size); if (vol->dev->d_ops->seek(vol->dev, pos, SEEK_SET) == (off_t)-1) { err_printf(resize, "Seek failed to position %lld", lcn); return (-1); } return (0);}static int copy_clusters(ntfs_resize_t *resize, s64 dest, s64 src, s64 len){ s64 i; char *buff = NULL; /* overflow checked at mount time */ ntfs_volume *vol = resize->vol; if (!(buff = malloc(NTFS_MAX_CLUSTER_SIZE))) return (-1); for (i = 0; i < len; i++) { if (lseek_to_cluster(resize, vol, src + i) < 0) goto err_out; if (read_all(resize, vol->dev, buff, vol->cluster_size) == -1) { err_printf(resize, "Failed to read from the disk"); if (errno == EIO) err_printf(resize, "%s", bad_sectors_warning_msg); goto err_out; } if (lseek_to_cluster(resize, vol, dest + i) < 0) goto err_out; if (write_all(vol->dev, buff, vol->cluster_size) == -1) { err_printf(resize, "Failed to write to the disk"); if (errno == EIO) err_printf(resize, "%s", bad_sectors_warning_msg); goto err_out; } resize->relocations++; } free(buff); return (0); err_out: free(buff); return (-1);}static int relocate_clusters(ntfs_resize_t *r, runlist *dest_rl, s64 src_lcn){ /* collect_shrink_constraints() ensured $MFTMir DATA is one run */ if (r->mref == FILE_MFTMirr && r->ctx->attr->type == AT_DATA) { if (!r->mftmir_old) { r->mftmir_rl.lcn = dest_rl->lcn; r->mftmir_rl.length = dest_rl->length; r->mftmir_old = src_lcn; } else { err_printf(r, "Multi-run $MFTMirr. Please report!"); return (-1); } } for (; dest_rl->length; src_lcn += dest_rl->length, dest_rl++) { if (copy_clusters(r, dest_rl->lcn, src_lcn, dest_rl->length) < 0) return (-1); } return (0);}static int rl_split_run(ntfs_resize_t *resize, runlist **rl, int run, s64 pos){ runlist *rl_new, *rle_new, *rle; int items, new_size, size_head, size_tail; s64 len_head, len_tail; items = rl_items(*rl); new_size = (items + 1) * sizeof(runlist_element); size_head = run * sizeof(runlist_element); size_tail = (items - run - 1) * sizeof(runlist_element); if (!(rl_new = (runlist *)malloc(new_size))) { err_printf(resize, "malloc failed"); return (-1); } rle_new = rl_new + run; rle = *rl + run; memmove(rl_new, *rl, size_head); memmove(rle_new + 2, rle + 1, size_tail); len_tail = rle->length - (pos - rle->lcn); len_head = rle->length - len_tail; rl_set(rle_new, rle->vcn, rle->lcn, len_head); rl_set(rle_new + 1, rle->vcn + len_head, rle->lcn + len_head, len_tail); progress_message(resize, "Splitting run at cluster %lld:", (long long)pos); free(*rl); *rl = rl_new; return (0);}static int rl_insert_at_run(ntfs_resize_t *resize, runlist **rl, int run, runlist *ins){ int items, ins_items; int new_size, size_tail; runlist *rle; s64 vcn; items = rl_items(*rl); ins_items = rl_items(ins) - 1; new_size = ((items - 1) + ins_items) * sizeof(runlist_element); size_tail = (items - run - 1) * sizeof(runlist_element); if (!(*rl = (runlist *)realloc(*rl, new_size))) { err_printf(resize, "realloc failed"); return (-1); } rle = *rl + run; memmove(rle + ins_items, rle + 1, size_tail); for (vcn = rle->vcn; ins->length; rle++, vcn += ins->length, ins++) { rl_set(rle, vcn, ins->lcn, ins->length);// dump_run(rle); } return (0); /* FIXME: fast path if ins_items = 1 */// (*rl + run)->lcn = ins->lcn;}static int relocate_run(ntfs_resize_t *resize, runlist **rl, int run){ s64 lcn, lcn_length; s64 new_vol_size; /* (last LCN on the volume) + 1 */ runlist *relocate_rl; /* relocate runlist to relocate_rl */ int hint; lcn = (*rl + run)->lcn; lcn_length = (*rl + run)->length; new_vol_size = resize->new_volume_size; if (lcn + lcn_length <= new_vol_size) return (0); if (lcn < new_vol_size) { if (rl_split_run(resize, rl, run, new_vol_size) < 0) return (-1); return (0); } hint = (resize->mref == FILE_MFTMirr) ? 1 : 0; if (!(relocate_rl = alloc_cluster(resize, &resize->lcn_bitmap, lcn_length, new_vol_size, hint))) { err_printf(resize, "Cluster allocation failed for %llu:%lld", resize->mref, lcn_length); return (-1); } /* FIXME: check $MFTMirr DATA isn't multi-run (or support it) */ progress_message(resize, "Relocate record %llu 0x%llx->0x%llx", (unsigned long long)resize->mref, (unsigned long long)lcn, (unsigned long long)relocate_rl->lcn); if (relocate_clusters(resize, relocate_rl, lcn) < 0) return (-1); if (rl_insert_at_run(resize, rl, run, relocate_rl) < 0) return (-1); /* We don't release old clusters in the bitmap, that area isn't used by the allocator and will be truncated later on */ free(relocate_rl); resize->dirty_inode = DIRTY_ATTRIB; return (0);}static int relocate_attribute(ntfs_resize_t *resize){ ATTR_RECORD *a; runlist *rl; int i; a = resize->ctx->attr; if (!a->non_resident) return (0); if (!(rl = ntfs_mapping_pairs_decompress(resize->vol, a, NULL))) { err_printf(resize, "ntfs_decompress_mapping_pairs failed"); return (-1); } for (i = 0; rl[i].length; i++) { s64 lcn = rl[i].lcn; s64 lcn_length = rl[i].length; if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) continue; /* FIXME: ntfs_mapping_pairs_decompress should return error */ if (lcn < 0 || lcn_length <= 0) { err_printf(resize, "Corrupt runlist in MTF %llu attr %x LCN " "%llx length %llx", resize->mref, (unsigned int)le32_to_cpu(a->type), lcn, lcn_length); return (-1); } if (relocate_run(resize, &rl, i) < 0) return (-1); } if (resize->dirty_inode == DIRTY_ATTRIB) { if (replace_attribute_runlist(resize, resize->vol, resize->ctx, rl) < 0) return (-1); resize->dirty_inode = DIRTY_INODE; } free(rl); return (0);}static int is_mftdata(ntfs_resize_t *resize){ if (resize->ctx->attr->type != AT_DATA) return 0; if (resize->mref == 0) return 1; if ( MREF(resize->mrec->base_mft_record) == 0 && MSEQNO(resize->mrec->base_mft_record) != 0) return 1; return 0;}static int handle_mftdata(ntfs_resize_t *resize, int do_mftdata){ ATTR_RECORD *attr = resize->ctx->attr; VCN highest_vcn, lowest_vcn; if (do_mftdata) { if (!is_mftdata(resize)) return 0; highest_vcn = sle64_to_cpu(attr->highest_vcn); lowest_vcn = sle64_to_cpu(attr->lowest_vcn); if (resize->mft_highest_vcn != highest_vcn) return 0; if (lowest_vcn == 0) resize->mft_highest_vcn = lowest_vcn; else resize->mft_highest_vcn = lowest_vcn - 1; } else if (is_mftdata(resize)) { highest_vcn = sle64_to_cpu(attr->highest_vcn); if (resize->mft_highest_vcn < highest_vcn) resize->mft_highest_vcn = highest_vcn; return 0; } return 1;}static int relocate_attributes(ntfs_resize_t *resize, int do_mftdata){ int ret; if (!(resize->ctx = attr_get_search_ctx(resize, NULL, resize->mrec))) return (-1); while (!ntfs_attrs_walk(resize->ctx)) { if (resize->ctx->attr->type == AT_END) break; if (handle_mftdata(resize, do_mftdata) == 0) continue; ret = ntfs_inode_badclus_bad(resize->mref, resize->ctx->attr); if (ret == -1) { err_printf(resize, "Bad sector list check failed"); return (-1); } else if (ret == 1) continue; if (resize->mref == FILE_Bitmap && resize->ctx->attr->type == AT_DATA) continue; if (relocate_attribute(resize) < 0) return (-1); } ntfs_attr_put_search_ctx(resize->ctx); return (0);}static int relocate_inode(ntfs_resize_t *resize, MFT_REF mref, int do_mftdata){ if (ntfs_file_record_read(resize->vol, mref, &resize->mrec, NULL)) { /* FIXME: continue only if it make sense, e.g. MFT record not in use based on $MFT bitmap */ if (errno == EIO || errno == ENOENT) return (0); err_printf(resize, "ntfs_file_record_read failed"); return (-1); } if (!(resize->mrec->flags & MFT_RECORD_IN_USE)) return (0); resize->mref = mref; resize->dirty_inode = DIRTY_NONE; if (relocate_attributes(resize, do_mftdata) != 0) return (-1); if (resize->dirty_inode == DIRTY_INODE) {// if (vol->dev->d_ops->sync(vol->dev) == -1)// perr_exit("Failed to sync device"); if (write_mft_record(resize, resize->vol, mref, resize->mrec)) { err_printf(resize, "Couldn't update record %llu", mref); return (-1); } } return (0);}static int relocate_inodes(ntfs_resize_t *resize){ s64 nr_mft_records; MFT_REF mref; VCN highest_vcn; progress_message(resize, "Relocating needed data"); resize->relocations = 0; resize->mrec = (MFT_RECORD *)malloc(resize->vol->mft_record_size); if (!resize->mrec) { err_printf(resize, "malloc failed"); return (-1); } nr_mft_records = resize->vol->mft_na->initialized_size >> resize->vol->mft_record_size_bits; for (mref = 0; mref < (MFT_REF)nr_mft_records; mref++) { if (relocate_inode(resize, mref, 0) != 0) return (-1); } while (1) { highest_vcn = resize->mft_highest_vcn; mref = nr_mft_records; do { if (relocate_inode(resize, --mref, 1) != 0) return (-1); if (resize->mft_highest_vcn == 0) goto done; } while (mref); if (highest_vcn == resize->mft_highest_vcn) { err_printf(resize, "Sanity check failed! Highest_vcn = %lld. " "Please report!", highest_vcn); return (-1); } }done: if (resize->mrec) free(resize->mrec); return (0);}#ifndef __VISOPSYS__static void print_hint(ntfs_volume *vol, const char *s, struct llcn_t llcn){ s64 runs_b, runs_mb; if (llcn.lcn == 0) return; runs_b = llcn.lcn * vol->cluster_size; runs_mb = rounded_up_division(runs_b, NTFS_MBYTE); printf("%-19s: %9lld MB %8lld\n", s, (long long)runs_mb, (long long)llcn.inode);}/** * advise_on_resize * * The metadata file $Bitmap has one bit for each cluster on disk. This has * already been read into lcn_bitmap. By looking for the last used cluster on * the disk, we can work out by how much we can shrink the volume. */static void advise_on_resize(ntfs_resize_t *resize){ ntfs_volume *vol = resize->vol; if (opt.verbose) { printf("Estimating smallest shrunken size supported ...\n"); printf("File feature Last used at By inode\n"); print_hint(vol, "$MFT", resize->last_mft); print_hint(vol, "Multi-Record", resize->last_multi_mft); print_hint(vol, "$MFTMirr", resize->last_mftmir); print_hint(vol, "Compressed", resize->last_compressed); print_hint(vol, "Sparse", resize->last_sparse); print_hint(vol, "Ordinary", resize->last_lcn); } print_advise(vol, resize->last_unsupp);}#endif /* __VISOPSYS__ */static int rl_expand(ntfs_resize_t *resize, runlist **rl, const VCN last_vcn){ int len; runlist *p = *rl; len = rl_items(p) - 1; if (len <= 0) { err_printf(resize, "rl_expand: bad runlist length: %d", len); return (-1); } if (p[len].vcn > last_vcn) { err_printf(resize, "rl_expand: length is already more than requested " "(%lld > %lld)", p[len].vcn, last_vcn); return (-1); } if (p[len - 1].lcn == LCN_HOLE) { p[len - 1].length += last_vcn - p[len].vcn; p[len].vcn = last_vcn; } else if (p[len - 1].lcn >= 0) { p = realloc(*rl, (++len + 1) * sizeof(runlist_element)); if (!p) { err_printf(resize, "rl_expand: realloc failed"); return (-1); } p[len - 1].lcn = LCN_HOLE; p[len - 1].length = last_vcn - p[len - 1].vcn; rl_set(p + len, last_vcn, LCN_ENOENT, 0LL); *rl = p; } else { err_printf(resize, "rl_expand: bad LCN: %lld", p[len - 1].lcn); return (-1); } return (0);}static int rl_truncate(ntfs_resize_t *resize, runlist **rl, const VCN last_vcn){ int len; VCN vcn; len = rl_items(*rl) - 1; if (len <= 0) { err_printf(resize, "rl_truncate: bad runlist length: %d", len); return (-1); } vcn = (*rl)[len].vcn; if (vcn < last_vcn) { if (rl_expand(resize, rl, last_vcn) < 0) return (-1); } else if (vcn > last_vcn) if (ntfs_rl_truncate(rl, last_vcn) == -1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -