📄 ntfsresize.c
字号:
opt.debug++; break; case 'f': opt.force++; break; case 'h': case '?': help++; break; case 'i': opt.info++; break; case 'n': opt.ro_flag = MS_RDONLY; break; case 'P': opt.show_progress = 0; break; case 's': if (!err && (opt.bytes == 0)) opt.bytes = get_new_volume_size(optarg); else err++; break; case 'v': opt.verbose++; ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); break; case 'V': ver++; break; default: if (optopt == 's') { printf("Option '%s' requires an argument.\n", argv[optind-1]); } else { printf("Unknown option '%s'.\n", argv[optind-1]); } err++; break; } } if (!help && !ver) { if (opt.volume == NULL) { if (argc > 1) printf("You must specify exactly one device.\n"); err++; } if (opt.info) { opt.ro_flag = MS_RDONLY; if (opt.bytes) { printf(NERR_PREFIX "Options --info and --size " "can't be used together.\n"); usage(); } } } /* Redirect stderr to stdout, note fflush()es are essential! */ fflush(stdout); fflush(stderr); if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) perr_exit("Failed to redirect stderr to stdout"); fflush(stdout); fflush(stderr);#ifdef DEBUG if (!opt.debug) if (!freopen("/dev/null", "w", stderr)) perr_exit("Failed to redirect stderr to /dev/null");#endif if (ver) version(); if (help || err) usage(); return (!err && !help && !ver);}static void print_advise(ntfs_volume *vol, s64 supp_lcn){ s64 old_b, new_b, freed_b, old_mb, new_mb, freed_mb; old_b = vol->nr_clusters * vol->cluster_size; old_mb = rounded_up_division(old_b, NTFS_MBYTE); /* Take the next supported cluster (free or relocatable) plus reserve a cluster for the backup boot sector */ supp_lcn += 2; if (supp_lcn > vol->nr_clusters) { err_printf("Very rare fragmentation type detected. " "Sorry, it's not supported yet.\n" "Try to defragment your NTFS, perhaps it helps.\n"); exit(1); } new_b = supp_lcn * vol->cluster_size; new_mb = rounded_up_division(new_b, NTFS_MBYTE); freed_b = (vol->nr_clusters - supp_lcn + 1) * vol->cluster_size; freed_mb = freed_b / NTFS_MBYTE; /* WARNING: don't modify the text, external tools grep for it */ printf("You might resize at %lld bytes ", (long long)new_b); if ((new_mb * NTFS_MBYTE) < old_b) printf("or %lld MB ", (long long)new_mb); printf("(freeing "); if (freed_mb && (old_mb - new_mb)) printf("%lld MB", (long long)(old_mb - new_mb)); else printf("%lld bytes", (long long)freed_b); printf(").\n"); printf("Please make a test run using both the -n and -s options " "before real resizing!\n");}#endif /* __VISOPSYS__ */__attribute__((format(printf, 2, 3)))static void progress_message(ntfs_resize_t *resize, const char *fmt, ...){ va_list ap; char tmp[PROGRESS_MAX_MESSAGELEN]; va_start(ap, fmt); vsprintf(tmp, fmt, ap); va_end(ap); if (resize->prog && (lockGet(&(resize->prog->lock)) >= 0)) { strncpy((char *) resize->prog->statusMessage, tmp, PROGRESS_MAX_MESSAGELEN); lockRelease(&(resize->prog->lock)); } ntfs_log_debug("%s\n", tmp);}__attribute__((format(printf, 3, 4)))static void _err_printf(ntfs_resize_t *resize, const char *function, const char *fmt, ...){ va_list ap; char tmp[PROGRESS_MAX_MESSAGELEN]; strcpy(tmp, ERR_PREFIX); if (function) sprintf((tmp + strlen(tmp)), "%s: ", function); va_start(ap, fmt); vsprintf((tmp + strlen(tmp)), fmt, ap); va_end(ap); if (resize->prog && (lockGet(&(resize->prog->lock)) >= 0)) { strncpy((char *) resize->prog->statusMessage, tmp, PROGRESS_MAX_MESSAGELEN); resize->prog->error = 1; lockRelease(&(resize->prog->lock)); while (resize->prog->error) multitaskerYield(); } ntfs_log_trace("%s\n", tmp);}#ifdef DEBUG#define err_printf(r, f, a...) _err_printf(r, __FUNCTION__, f, ##a)#else#define err_printf(r, f, a...) _err_printf(r, NULL, f, ##a)#endifstatic void rl_set(runlist *rl, VCN vcn, LCN lcn, s64 len){ rl->vcn = vcn; rl->lcn = lcn; rl->length = len;}static int rl_items(runlist *rl){ int i = 0; while (rl[i++].length) ; return i;}#ifndef __VISOPSYS__static void dump_run(runlist_element *r){ ntfs_log_verbose(" %8lld %8lld (0x%08llx) %lld\n", (long long)r->vcn, (long long)r->lcn, (long long)r->lcn, (long long)r->length);}static void dump_runlist(runlist *rl){ while (rl->length) dump_run(rl++);}#endif /* __VISOPSYS__ *//** * nr_clusters_to_bitmap_byte_size * * Take the number of clusters in the volume and calculate the size of $Bitmap. * The size must be always a multiple of 8 bytes. */static s64 nr_clusters_to_bitmap_byte_size(s64 nr_clusters){ s64 bm_bsize; bm_bsize = rounded_up_division(nr_clusters, 8); bm_bsize = (bm_bsize + 7) & ~7; return bm_bsize;}static int collect_resize_constraints(ntfs_resize_t *resize, runlist *rl){ s64 inode, last_lcn; ATTR_FLAGS flags; ATTR_TYPES atype; struct llcn_t *llcn = NULL; int ret, supported = 0; last_lcn = rl->lcn + (rl->length - 1); inode = resize->ni->mft_no; flags = resize->ctx->attr->flags; atype = resize->ctx->attr->type; if ((ret = ntfs_inode_badclus_bad(inode, resize->ctx->attr)) != 0) { if (ret == -1) err_printf(resize, "Bad sector list check failed"); return (-1); } if (inode == FILE_Bitmap) { llcn = &resize->last_lcn; if (atype == AT_DATA && NInoAttrList(resize->ni)) { err_printf(resize, "Highly fragmented $Bitmap isn't supported yet."); return (-1); } supported = 1; } else if (inode == FILE_MFT) { llcn = &resize->last_mft; /* * First run of $MFT AT_DATA isn't supported yet. */ if (atype != AT_DATA || rl->vcn) supported = 1; } else if (NInoAttrList(resize->ni)) { llcn = &resize->last_multi_mft; if (inode != FILE_MFTMirr) supported = 1; } else if (flags & ATTR_IS_SPARSE) { llcn = &resize->last_sparse; supported = 1; } else if (flags & ATTR_IS_COMPRESSED) { llcn = &resize->last_compressed; supported = 1; } else if (inode == FILE_MFTMirr) { llcn = &resize->last_mftmir; supported = 1; /* Fragmented $MFTMirr DATA attribute isn't supported yet */ if (atype == AT_DATA) if (rl[1].length != 0 || rl->vcn) supported = 0; } else { llcn = &resize->last_lcn; supported = 1; } if (llcn->lcn < last_lcn) { llcn->lcn = last_lcn; llcn->inode = inode; } if (supported) return (0); if (resize->last_unsupp < last_lcn) resize->last_unsupp = last_lcn; return (0);}static int collect_relocation_info(ntfs_resize_t *resize, runlist *rl){ s64 lcn, lcn_length, start, len, inode; s64 new_vol_size; /* (last LCN on the volume) + 1 */ lcn = rl->lcn; lcn_length = rl->length; inode = resize->ni->mft_no; new_vol_size = resize->new_volume_size; if (lcn + lcn_length <= new_vol_size) return (0); if (inode == FILE_Bitmap && resize->ctx->attr->type == AT_DATA) return (0); start = lcn; len = lcn_length; if (lcn < new_vol_size) { start = new_vol_size; len = lcn_length - (new_vol_size - lcn); if (!opt.info && (inode == FILE_MFTMirr)) { err_printf(resize, "$MFTMirr can't be split up yet. " "Please try a different size."); return (-1); } } resize->relocations += len; if (!opt.info || !resize->new_volume_size) return (0); progress_message(resize, "Relocation needed for inode %8lld", (long long)inode); return (0);}/** * build_lcn_usage_bitmap * * lcn_bitmap has one bit for each cluster on the disk. Initially, lcn_bitmap * has no bits set. As each attribute record is read the bits in lcn_bitmap are * checked to ensure that no other file already references that cluster. * * This serves as a rudimentary "chkdsk" operation. */static int build_lcn_usage_bitmap(ntfs_resize_t *resize, ntfs_volume *vol, ntfsck_t *fsck){ s64 inode; ATTR_RECORD *a; runlist *rl; int i, j; struct bitmap *lcn_bitmap = &fsck->lcn_bitmap; a = fsck->ctx->attr; inode = fsck->ni->mft_no; if (!a->non_resident) return (0); if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) { if (errno == EIO) err_printf(resize, "ntfs_decompress_mapping_pairs: %s", corrupt_volume_msg); return (-1); } for (i = 0; rl[i].length; i++) { s64 lcn = rl[i].lcn; s64 lcn_length = rl[i].length; /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ 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 inode %lld attr %x LCN " "%llx length %llx", inode, (unsigned int)le32_to_cpu(a->type), lcn, lcn_length); return (-1); } for (j = 0; j < lcn_length; j++) { u64 k = (u64)lcn + j; if (k >= (u64)vol->nr_clusters) { long long outsiders = lcn_length - j; fsck->outsider += outsiders; if (++fsck->show_outsider <= 10) progress_message(resize, "Outside of the volume reference" " for inode %lld at %lld:%lld", inode, (long long)k, outsiders); break; } if (ntfs_bit_get_and_set(lcn_bitmap->bm, k, 1)) { if (++fsck->multi_ref <= 10) progress_message(resize, "Cluster %lld is referenced " "multiple times!", (long long)k); continue; } } fsck->inuse += lcn_length; } free(rl); return (0);}static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_resize_t *resize, ntfs_inode *ni, MFT_RECORD *mrec){ ntfs_attr_search_ctx *ret; if ((ret = ntfs_attr_get_search_ctx(ni, mrec)) == NULL) err_printf(resize, "ntfs_attr_get_search_ctx failed"); return ret;}/** * walk_attributes * * For a given MFT Record, iterate through all its attributes. Any non-resident * data runs will be marked in lcn_bitmap. */static int walk_attributes(ntfs_resize_t *resize, ntfs_volume *vol, ntfsck_t *fsck){ if (!(fsck->ctx = attr_get_search_ctx(resize, fsck->ni, NULL))) return -1; while (!ntfs_attrs_walk(fsck->ctx)) { if (fsck->ctx->attr->type == AT_END) break; if (build_lcn_usage_bitmap(resize, vol, fsck) != 0) return -1; } ntfs_attr_put_search_ctx(fsck->ctx); return 0;}/** * progress_update * * Update the progress bar and tell the user. */void progress_update(progress *prog, int myPercentIndex, u64 current, u64 total){ unsigned finished = 0; int count; if (prog && (lockGet(&(prog->lock)) >= 0)) { for (count = 0; count < myPercentIndex; count ++) finished += progressPercentages[count]; finished += (unsigned) ((current * progressPercentages[myPercentIndex]) / total); if (finished >= 100) finished = 99; prog->finished = finished; prog->percentFinished = finished; lockRelease(&(prog->lock)); }}/** * compare_bitmaps * * Compare two bitmaps. In this case, $Bitmap as read from the disk and * lcn_bitmap which we built from the MFT Records. */static int compare_bitmaps(ntfs_resize_t *resize, ntfs_volume *vol, struct bitmap *a){ s64 i, pos, count; int mismatch = 0; int backup_boot = 0; u8 *bm = NULL; progress_message(resize, "Accounting clusters"); bm = calloc(NTFS_BUF_SIZE, 1); if (bm == NULL) { err_printf(resize, "Not enough memory"); return (-1); } pos = 0; while (1) { count = ntfs_attr_pread(vol->lcnbmp_na, pos, NTFS_BUF_SIZE, bm); if (count == -1) { err_printf(resize, "Couldn't get $Bitmap $DATA"); free(bm); return (-1); } if (count == 0) { if (a->size > pos) { err_printf(resize, "$Bitmap size is smaller than expected" " (%lld != %lld)", a->size, pos); free(bm); return (-1); } break; } for (i = 0; i < count; i++, pos++) { s64 cl; /* current cluster */ progress_update(resize->prog, RSZPCNT_ACCOUNTING, pos, a->size);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -