ntfsresize.c revision 7e7bd3dccbfe8f79e25e5c1554b5bc3a9aaca321
/**
* ntfsresize - Part of the Linux-NTFS project.
*
* Copyright (c) 2002-2006 Szabolcs Szakacsits
* Copyright (c) 2002-2005 Anton Altaparmakov
* Copyright (c) 2002-2003 Richard Russon
* Copyright (c) 2007 Yura Pakhuchiy
*
* This utility will resize an NTFS volume without data loss.
*
* WARNING FOR DEVELOPERS!!! Several external tools grep for text messages
* to control execution thus if you would like to change any message
* then PLEASE think twice before doing so then don't modify it. Thanks!
*
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include "debug.h"
#include "types.h"
#include "support.h"
#include "endians.h"
#include "bootsect.h"
#include "device.h"
#include "attrib.h"
#include "volume.h"
#include "mft.h"
#include "bitmap.h"
#include "inode.h"
#include "runlist.h"
#include "utils.h"
#include "version.h"
static const char *EXEC_NAME = "ntfsresize";
static const char *resize_warning_msg =
"WARNING: Every sanity check passed and only the dangerous operations left.\n"
"Make sure that important data has been backed up! Power outage or computer\n"
"crash may result major data loss!\n";
static const char *resize_important_msg =
#ifdef __sun
"When booted, Windows will check the file system and may reboot.\n"
"If you are running this from inside parted, STOP reading now.\n"
"Otherwise, you can go on to shrink the device with fdisk or parted.\n"
#else
"You can go on to shrink the device for example with Linux fdisk.\n"
#endif
"IMPORTANT: When recreating the partition, make sure that you\n"
" 1) create it at the same disk sector (use sector as the unit!)\n"
" 3) do not make it smaller than the new NTFS filesystem size\n"
" 4) set the bootable flag for the partition if it existed before\n"
"Otherwise you won't be able to access NTFS or can't boot from the disk!\n"
"If you make a mistake and don't have a partition table backup then you\n"
"can recover the partition table by TestDisk or Parted's rescue mode.\n";
static const char *invalid_ntfs_msg =
"The device '%s' doesn't have a valid NTFS.\n"
"Maybe you selected the wrong partition? Or the whole disk instead of a\n"
"if the disk was incorrectly repartitioned (see the ntfsresize FAQ).\n";
static const char *corrupt_volume_msg =
"NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n"
"The usage of the /f parameter is very IMPORTANT! No modification was\n"
"and will be made to NTFS by this software until it gets repaired.\n";
static const char *hibernated_volume_msg =
"The NTFS partition is hibernated. Windows must be resumed and turned off\n"
"properly, so resizing could be done safely.\n";
static const char *unclean_journal_msg =
"The NTFS journal file is unclean. Please shutdown Windows properly before\n"
"using this software! Note, if you have run chkdsk previously then boot\n"
"Windows again which will automatically initialize the journal correctly.\n";
static const char *opened_volume_msg =
"This software has detected that the NTFS volume is already opened by another\n"
"software thus it refuses to progress to preserve data consistency.\n";
static const char *bad_sectors_warning_msg =
"****************************************************************************\n"
"* WARNING: The disk has bad sector. This means physical damage on the disk *\n"
"* surface caused by deterioration, manufacturing faults or other reason. *\n"
"* The reliability of the disk may stay stable or degrade fast. We suggest *\n"
"* making a full backup urgently by running 'ntfsclone --rescue ...' then *\n"
"* run 'chkdsk /f /r' on Windows and rebooot it TWICE! Then you can resize *\n"
"* NTFS safely by additionally using the --bad-sectors option of ntfsresize.*\n"
"****************************************************************************\n";
static const char *many_bad_sectors_msg =
"***************************************************************************\n"
"* WARNING: The disk has many bad sectors. This means physical damage *\n"
"* on the disk surface caused by deterioration, manufacturing faults or *\n"
"* other reason. We suggest to get a replacement disk as soon as possible. *\n"
"***************************************************************************\n";
static struct {
int verbose;
int debug;
int ro_flag;
int force;
int info;
int show_progress;
int badsectors;
char *volume;
} opt;
struct bitmap {
};
#define NTFS_PROGBAR 0x0001
#define NTFS_PROGBAR_SUPPRESS 0x0002
struct progress_bar {
int resolution;
int flags;
float unit;
};
struct llcn_t {
};
#define NTFSCK_PROGBAR 0x0001
typedef struct {
int multi_ref; /* num of clusters referenced many times */
int outsider; /* num of clusters outside the volume */
int show_outsider; /* controls showing the above information */
int flags;
struct bitmap lcn_bitmap;
} ntfsck_t;
typedef struct {
int dirty_inode; /* some inode data got relocated */
int shrink; /* shrink = 1, enlarge = 0 */
struct progress_bar progress;
struct bitmap lcn_bitmap;
/* Temporary statistics until all case is supported */
struct llcn_t last_mftmir;
struct llcn_t last_multi_mft;
struct llcn_t last_sparse;
struct llcn_t last_compressed;
/* FIXME: This, lcn_bitmap and pos from find_free_cluster() will make a cluster
allocation related structure, attached to ntfs_resize_t */
static s64 max_free_cluster_range = 0;
/* WARNING: don't modify the text, external tools grep for it */
#define ERR_PREFIX "ERROR"
#define DIRTY_NONE (0)
#define DIRTY_INODE (1)
#define DIRTY_ATTRIB (2)
#define NTFS_MAX_CLUSTER_SIZE (65536)
{
}
/**
* perr_printf
*
* Print an error message.
*/
static void perr_printf(const char *fmt, ...)
{
}
static void err_printf(const char *fmt, ...)
{
}
/**
* err_exit
*
* Print and error message and exit the program.
*/
{
exit(1);
}
/**
* perr_exit
*
* Print and error message and exit the program
*/
{
exit(1);
}
/**
* usage - Print a list of the parameters to the program
*
* Print a list of the parameters and options for the program.
*
* Return: none
*/
static void usage(void)
{
printf("\nUsage: %s [OPTIONS] DEVICE\n"
" Resize an NTFS volume non-destructively, safely move any data if needed.\n"
"\n"
" -i, --info Estimate the smallest shrunken size possible\n"
" -s, --size SIZE Resize volume to SIZE[k|M|G] bytes\n"
"\n"
" -n, --no-action Do not write to disk\n"
" -b, --bad-sectors Support disks having bad sectors\n"
" -f, --force Force to progress\n"
" -P, --no-progress-bar Don't show progress bar\n"
" -v, --verbose More output\n"
" -V, --version Display version information\n"
" -h, --help Display this help\n"
#ifdef DEBUG
" -d, --debug Show debug information\n"
#endif
"\n"
" The options -i and -s are mutually exclusive. If both options are\n"
" omitted then the NTFS volume will be enlarged to the DEVICE size.\n"
"\n", EXEC_NAME);
printf("Ntfsresize FAQ: http://linux-ntfs.sourceforge.net/info/ntfsresize.html\n");
exit(1);
}
/**
* proceed_question
*
* Force the user to confirm an action before performing it.
* Copy-paste from e2fsprogs
*/
static void proceed_question(void)
{
char buf[256];
const char *short_yes = "yY";
printf("Are you sure you want to proceed (y/[n])? ");
buf[0] = 0;
printf("OK quitting. NO CHANGES have been made to your "
"NTFS volume.\n");
exit(1);
}
}
/**
* version - Print version information about the program
*
* Print a copyright statement and a brief description of the program.
*
* Return: none
*/
static void version(void)
{
printf("\nResize an NTFS Volume, without data loss.\n\n");
printf("Copyright (c) 2002-2006 Szabolcs Szakacsits\n");
printf("Copyright (c) 2002-2005 Anton Altaparmakov\n");
printf("Copyright (c) 2002-2003 Richard Russon\n");
printf("Copyright (c) 2007 Yura Pakhuchiy\n");
}
/**
* get_new_volume_size
*
* Convert a user-supplied string into a size. Without any suffix the number
* will be assumed to be in bytes. If the number has a suffix of k, M or G it
* will be scaled up by 1000, 1000000, or 1000000000.
*/
static s64 get_new_volume_size(char *s)
{
char *suffix;
int prefix_kind = 1000;
err_exit("Illegal new volume size\n");
if (!*suffix)
return size;
prefix_kind = 1024;
usage();
/* We follow the SI prefixes:
Disk partitioning tools use prefixes as,
k M G
fdisk 2.11x- 2^10 2^20 10^3*2^20
fdisk 2.11y+ 10^3 10^6 10^9
cfdisk 10^3 10^6 10^9
sfdisk 2^10 2^20
parted 2^10 2^20 (may change)
fdisk (DOS) 2^10 2^20
*/
/* FIXME: check for overflow */
switch (*suffix) {
case 'G':
size *= prefix_kind;
case 'M':
size *= prefix_kind;
case 'k':
size *= prefix_kind;
break;
default:
usage();
}
return size;
}
/**
* parse_options - Read and validate the programs command line
*
* Read the command line, verify the syntax and parse the options.
* This function is very long, but quite simple.
*
* Return: 1 Success
* 0 Error, one or more problems
*/
{
static const char *sopt = "-bdfhinPs:vV";
#ifdef DEBUG
#endif
};
int c;
int err = 0;
int ver = 0;
int help = 0;
switch (c) {
case 1: /* A non-option argument */
else
err++;
break;
case 'b':
opt.badsectors++;
break;
case 'd':
break;
case 'f':
break;
case 'h':
case '?':
help++;
break;
case 'i':
break;
case 'n':
break;
case 'P':
opt.show_progress = 0;
break;
case 's':
else
err++;
break;
case 'v':
break;
case 'V':
ver++;
break;
default:
if (optopt == 's') {
} else {
}
err++;
break;
}
}
if (argc > 1)
printf("You must specify exactly one device.\n");
err++;
}
"can't be used together.\n");
usage();
}
}
}
/* Redirect stderr to stdout, note fflush()es are essential! */
perr_exit("Failed to redirect stderr to stdout");
#ifdef DEBUG
#endif
if (ver)
version();
usage();
}
{
/* Take the next supported cluster (free or relocatable)
plus reserve a cluster for the backup boot sector */
supp_lcn += 2;
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);
}
/* WARNING: don't modify the text, external tools grep for it */
printf("(freeing ");
else
printf(").\n");
printf("Please make a test run using both the -n and -s options "
"before real resizing!\n");
}
{
}
{
int i = 0;
;
return i;
}
static void dump_run(runlist_element *r)
{
(long long)r->length);
}
{
}
/**
* 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.
*/
{
return bm_bsize;
}
{
if (ret == -1)
perr_exit("Bad sector list check failed");
return;
}
if (inode == FILE_Bitmap) {
err_exit("Highly fragmented $Bitmap isn't supported yet.");
supported = 1;
/*
* First run of $MFT AT_DATA isn't supported yet.
*/
supported = 1;
if (inode != FILE_MFTMirr)
supported = 1;
} else if (flags & ATTR_IS_SPARSE) {
supported = 1;
} else if (flags & ATTR_IS_COMPRESSED) {
supported = 1;
} else if (inode == FILE_MFTMirr) {
supported = 1;
/* Fragmented $MFTMirr DATA attribute isn't supported yet */
supported = 0;
} else {
supported = 1;
}
}
if (supported)
return;
}
{
return;
return;
len = lcn_length;
if (lcn < new_vol_size) {
err_printf("$MFTMirr can't be split up yet. Please try "
"a different size.\n");
exit(1);
}
}
return;
printf("Relocation needed for inode %8lld attr 0x%x LCN 0x%08llx "
"length %6lld\n", (long long)inode,
}
/**
* 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.
*/
{
ATTR_RECORD *a;
int i, j;
if (!a->non_resident)
return;
perr_printf("ntfs_decompress_mapping_pairs");
exit(1);
}
/* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */
continue;
/* FIXME: ntfs_mapping_pairs_decompress should return error */
if (lcn < 0 || lcn_length <= 0)
err_exit("Corrupt runlist in inode %lld attr %x LCN "
"%llx length %llx\n", inode,
for (j = 0; j < lcn_length; j++) {
long long outsiders = lcn_length - j;
printf("Outside of the volume reference"
" for inode %lld at %lld:%lld\n",
break;
}
printf("Cluster %lld is referenced "
"multiple times!\n",
(long long)k);
continue;
}
}
}
}
{
perr_printf("ntfs_attr_get_search_ctx");
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.
*/
{
return -1;
break;
}
return 0;
}
/**
* compare_bitmaps
*
* Compare two bitmaps. In this case, $Bitmap as read from the disk and
* lcn_bitmap which we built from the MFT Records.
*/
{
int mismatch = 0;
int backup_boot = 0;
printf("Accounting clusters ...\n");
pos = 0;
while (1) {
if (count == -1)
perr_exit("Couldn't get $Bitmap $DATA");
if (count == 0) {
err_exit("$Bitmap size is smaller than expected"
break;
}
goto done;
continue;
char bit;
continue;
/* FIXME: call also boot sector check */
backup_boot = 1;
printf("Found backup boot sector in "
"the middle of the volume.\n");
continue;
}
continue;
printf("Cluster accounting failed at %lld "
"(0x%llx): %s cluster in "
"$Bitmap\n", (long long)cl,
(unsigned long long)cl,
}
}
}
done:
if (mismatch) {
printf("Filesystem check failed! Totally %d cluster "
"accounting mismatches.\n", mismatch);
exit(1);
}
}
/**
* progress_init
*
* Create and scale our progress bar.
*/
{
p->resolution = 100;
}
/**
* progress_update
*
* Update the progress bar and tell the user.
*/
{
float percent;
if (!(p->flags & NTFS_PROGBAR))
return;
if (p->flags & NTFS_PROGBAR_SUPPRESS)
return;
/* WARNING: don't modify the texts, external tools grep for them */
return;
} else
printf("100.00 percent completed\n");
}
{
if (ntfs_inode_close(ni)) {
perr_printf("ntfs_inode_close for inode %llu",
return -1;
}
return 0;
}
/**
* walk_inodes
*
* Read each record in the MFT, skipping the unused ones, and build up a bitmap
* from all the non-resident attributes.
*/
{
ntfs_inode *ni;
struct progress_bar progress;
int pb_flags = 0; /* progress bar flags */
/* WARNING: don't modify the text, external tools grep for it */
printf("Checking filesystem consistency ...\n");
pb_flags |= NTFS_PROGBAR;
/* FIXME: continue only if it make sense, e.g.
MFT record not in use based on $MFT bitmap */
continue;
return -1;
}
goto close_inode;
return -1;
}
if (inode_close(ni) != 0)
return -1;
}
return 0;
}
{
s64 i;
return;
perr_exit("ntfs_decompress_mapping_pairs");
/* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */
continue;
}
}
{
exit(1);
break;
}
}
{
ntfs_inode *ni;
printf("Collecting resizing constraints ...\n");
continue;
}
goto close_inode;
if (inode_close(ni) != 0)
exit(1);
}
}
{
ntfs_log_verbose("Skip unmapped run at the beginning ...\n");
err_exit("Empty unmapped runlist! Please report!\n");
(*rl)++;
}
ntfs_log_verbose("Skip unmapped run at the end ...\n");
err_exit("Unmapped runlist in the middle! "
"Please report!\n");
}
}
}
{
int mp_size, l;
void *mp;
perr_exit("ntfs_get_size_for_mapping_pairs");
if (a->name_length) {
err_exit("Attribute name is after mapping pairs! "
"Please report!\n");
}
/* CHECKME: don't trust mapping_pairs is always the last item in the
if (mp_size > l) {
char *next_attr;
ntfs_log_verbose("Enlarging attribute header ...\n");
ntfs_log_verbose("Old mp size : %d\n", l);
ntfs_log_verbose("Bytes in use : %u\n", (unsigned int)
l = mp_size - l;
ntfs_log_verbose("Bytes in use new : %u\n", l + (unsigned int)
ntfs_log_verbose("Bytes allocated : %u\n", (unsigned int)
ntfs_log_verbose("increase : %d\n", l);
ntfs_log_verbose("shift : %lld\n",
(long long)remains_size);
err_exit("Extended record needed (%u > %u), not yet "
"supported!\nPlease try to free less space.\n",
bytes_in_use) + l,
}
if (!mp)
perr_exit("ntfsc_calloc couldn't get memory");
perr_exit("ntfs_mapping_pairs_build");
}
{
while (length--)
}
{
}
{
}
{
}
}
int hint)
{
/* FIXME: get rid of this 'static' variable */
if (pos >= nr_vol_clusters)
pos = 0;
if (!max_free_cluster_range)
if (hint)
i = pos;
do {
break;
}
} else {
free_zone = 0;
}
if (++i == nr_vol_clusters) {
i = free_zone = 0;
}
break;
} while (i != pos);
if (i)
return -1;
}
ntfs_log_verbose("Max free range: %7lld \n",
(long long)max_free_cluster_range);
}
if (pos == nr_vol_clusters)
pos = 0;
return 0;
}
int hint)
{
if (items <= 0) {
return NULL;
}
while (items > 0) {
if (runs)
hint = 0;
return NULL;
return NULL;
runs++;
}
if (runs > 1) {
ntfs_log_verbose("Multi-run allocation: \n");
}
return rl;
}
{
int i;
while (count > 0) {
i = count;
if (!NDevReadOnly(dev))
if (i < 0) {
return -1;
} else if (i > 0) {
count -= i;
} else
err_exit("Unexpected end of file!\n");
}
return 0;
}
{
int i;
while (count > 0) {
i = count;
if (!NDevReadOnly(dev))
if (i < 0) {
return -1;
} else {
count -= i;
}
}
return 0;
}
/**
* 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.
*/
{
perr_exit("ntfs_mft_record_write");
// if (v->u.dev->d_ops->sync(v->u.dev) == -1)
// perr_exit("Failed to sync device");
return 0;
}
{
}
{
s64 i;
for (i = 0; i < len; i++) {
perr_printf("Failed to read from the disk");
exit(1);
}
perr_printf("Failed to write to the disk");
exit(1);
}
resize->relocations++;
}
}
{
/* collect_shrink_constraints() ensured $MFTMir DATA is one run */
if (!r->mftmir_old) {
r->mftmir_old = src_lcn;
} else
err_exit("Multi-run $MFTMirr. Please report!\n");
}
}
{
if (!rl_new)
perr_exit("ntfs_malloc");
}
{
perr_exit("realloc");
// dump_run(rle);
}
return;
/* FIXME: fast path if ins_items = 1 */
// (*rl + run)->lcn = ins->lcn;
}
{
int hint;
return;
if (lcn < new_vol_size) {
return;
}
new_vol_size, hint)))
perr_exit("Cluster allocation failed for %llu:%lld",
/* FIXME: check $MFTMirr DATA isn't multi-run (or support it) */
ntfs_log_verbose("Relocate record %7llu:0x%x:%08lld:0x%08llx:0x%08llx "
(long long)lcn_length,
(unsigned long long)lcn,
(unsigned long long)relocate_rl->lcn);
/* We don't release old clusters in the bitmap, that area isn't
used by the allocator and will be truncated later on */
}
{
ATTR_RECORD *a;
int i;
if (!a->non_resident)
return;
perr_exit("ntfs_decompress_mapping_pairs");
continue;
/* FIXME: ntfs_mapping_pairs_decompress should return error */
if (lcn < 0 || lcn_length <= 0)
err_exit("Corrupt runlist in MTF %llu attr %x LCN "
(unsigned int)le32_to_cpu(a->type),
lcn, lcn_length);
}
}
}
{
return 0;
return 1;
return 1;
return 0;
}
{
if (do_mftdata) {
if (!is_mftdata(resize))
return 0;
return 0;
if (lowest_vcn == 0)
else
} else if (is_mftdata(resize)) {
return 0;
}
return 1;
}
{
int ret;
exit(1);
break;
continue;
if (ret == -1)
perr_exit("Bad sector list check failed");
else if (ret == 1)
continue;
continue;
}
}
{
/* FIXME: continue only if it make sense, e.g.
MFT record not in use based on $MFT bitmap */
return;
perr_exit("ntfs_file_record_record");
}
return;
// if (vol->u.dev->d_ops->sync(vol->u.dev) == -1)
// perr_exit("Failed to sync device");
}
}
{
printf("Relocating needed data ...\n");
resize->relocations = 0;
perr_exit("ntfs_malloc failed");
while (1) {
do {
if (resize->mft_highest_vcn == 0)
goto done;
} while (mref);
err_exit("Sanity check failed! Highest_vcn = %lld. "
"Please report!\n", highest_vcn);
}
done:
}
{
return;
}
/**
* 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.
*/
{
printf("Estimating smallest shrunken size supported ...\n");
printf("File feature Last used at By inode\n");
}
}
{
int len;
if (len <= 0)
err_exit("rl_expand: length is already more than requested "
if (!p)
perr_exit("rl_expand: realloc");
*rl = p;
} else
}
{
int len;
if (len <= 0)
perr_exit("ntfs_rl_truncate");
}
/**
* bitmap_file_data_fixup
*
* $Bitmap can overlap the end of the volume. Any bits in this region
* must be set. This region also encompasses the backup boot sector.
*/
{
}
/**
* truncate_badclust_bad_attr
*
* The metadata file $BadClus needs to be shrunk.
*
* FIXME: this function should go away and instead using a generalized
* "truncate_bitmap_data_attr()"
*/
{
ATTR_RECORD *a;
if (!a->non_resident)
/* FIXME: handle resident attribute value */
err_exit("Resident attribute in $BadClust isn't supported!\n");
perr_exit("ntfs_mapping_pairs_decompress");
}
/**
* realloc_bitmap_data_attr
*
* Reallocate the metadata file $Bitmap. It must be large enough for one bit
* per cluster of the shrunken volume. Also it must be a of 8 bytes in size.
*/
{
s64 i;
perr_exit("ntfs_mapping_pairs_decompress");
perr_exit("Couldn't allocate $Bitmap clusters");
}
{
perr_exit("realloc");
}
/**
* truncate_bitmap_data_attr
*/
{
ATTR_RECORD *a;
if (!a->non_resident)
/* FIXME: handle resident attribute value */
err_exit("Resident attribute in $Bitmap isn't supported!\n");
} else {
}
/*
* attribute too, for now chkdsk will do this for us.
*/
if (size == -1)
perr_exit("Couldn't write $Bitmap");
err_exit("Couldn't write full $Bitmap file (%lld from %lld)\n",
}
}
/**
* lookup_data_attr
*
* Find the $DATA attribute (with or without a name) for the given MFT reference
* (inode number).
*/
const char *aname,
{
ntfs_inode *ni;
int len = 0;
perr_exit("ntfs_open_inode");
exit(1);
exit(1);
}
perr_exit("ntfs_lookup_attr");
}
{
s64 i, badclusters = 0;
ntfs_log_verbose("Checking for bad sectors ...\n");
if (!base_ni)
if (NInoAttrList(base_ni)) {
err_printf("Hopelessly many bad sectors has been detected!\n");
exit(1);
}
err_exit("Resident attribute in $BadClust! Please report to "
"%s\n", NTFS_DEV_LIST);
/*
* FIXME: The below would be partial for non-base records in the
* not yet supported multi-record case. Alternatively use audited
* ntfs_attr_truncate after an umount & mount.
*/
perr_exit("Decompressing $BadClust:$Bad mapping pairs failed");
/* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */
continue;
ntfs_log_verbose("Bad cluster: %#8llx - %#llx (%lld)\n",
}
if (badclusters) {
printf("%sThis software has detected that the disk has at least"
" %lld bad sector%s.\n",
if (!opt.badsectors) {
exit(1);
} else
printf("WARNING: Bad sectors can cause reliability "
"problems and massive data loss!!!\n");
}
return badclusters;
}
/**
* truncate_badclust_file
*
* Shrink the $BadClus file to match the new volume size.
*/
{
printf("Updating $BadClust file ...\n");
/* FIXME: sanity_check_attr(ctx->attr); */
perr_exit("Couldn't update $BadClust");
}
/**
* truncate_bitmap_file
*
* Shrink the $Bitmap file to match the new volume size.
*/
{
printf("Updating $Bitmap file ...\n");
perr_exit("Couldn't update $Bitmap");
}
/**
* setup_lcn_bitmap
*
* Allocate a block of memory with one bit for each cluster of the disk.
* All the bits are set to 0, except those representing the region beyond the
* end of the disk.
*/
{
/* Determine lcn bitmap byte size and allocate it. */
return -1;
return 0;
}
/**
* update_bootsector
*
* FIXME: should be done using ntfs_* functions
*/
static void update_bootsector(ntfs_resize_t *r)
{
printf("Updating Boot record ...\n");
perr_exit("lseek");
perr_exit("read() error");
if (r->mftmir_old) {
}
perr_exit("lseek");
perr_exit("write() error");
}
/**
* vol_size
*/
{
/* add one sector_size for the backup boot sector */
}
/**
* print_vol_size
*
* Print the volume size in bytes and decimal megabytes.
*/
{
}
/**
* print_disk_usage
*
* Display the amount of disk space in use.
*/
{
/* WARNING: don't modify the text, external tools grep for it */
printf("Space in use : %lld MB (%.1f%%)\n",
}
{
printf("Needed relocations : %lld (%lld MB)\n",
(long long)resize->relocations, (long long)
}
/**
* mount_volume
*
* First perform some checks to determine if the volume is already mounted, or
* is dirty (Windows wasn't shutdown properly). If everything is OK, then mount
* the volume (load the metadata into memory).
*/
static ntfs_volume *mount_volume(void)
{
unsigned long mntflag;
"continue. You might try\nan another Linux distro.\n");
exit(1);
}
if (mntflag & NTFS_MF_MOUNTED) {
if (!(mntflag & NTFS_MF_READONLY))
err_exit("Device '%s' is mounted read-write. "
err_exit("Device '%s' is mounted. "
}
/*
* Pass NTFS_MNT_FORENSIC so that the mount process does not modify the
* volume at all. We will do the logfile emptying and dirty setting
* later if needed.
*/
else if (err == EOPNOTSUPP)
exit(1);
}
if (NVolWasDirty(vol))
err_exit("Volume is scheduled for check.\nRun chkdsk /f"
" and please try again, or see option -f.\n");
err_exit("Cluster size %u is too large!\n",
(unsigned int)vol->cluster_size);
if (ntfs_version_is_supported(vol))
perr_exit("Unknown NTFS version");
printf("Cluster size : %u bytes\n",
(unsigned int)vol->cluster_size);
return vol;
}
/**
* prepare_volume_fixup
*
* Set the volume's dirty flag and wipe the filesystem journal. When Windows
* boots it will automatically run chkdsk to check for any problems. If the
* read-only command line option was given, this function will do nothing.
*/
{
printf("Schedule chkdsk for NTFS consistency check at Windows boot "
"time ...\n");
perr_exit("Failed to set the volume dirty");
perr_exit("Failed to sync device");
printf("Resetting $LogFile ... (this might take a while)\n");
if (ntfs_logfile_reset(vol))
perr_exit("Failed to reset $LogFile");
perr_exit("Failed to sync device");
}
{
/* last lcn for a filled up volume (no empty space) */
}
{
/* FIXME: resize.shrink true also if only -i is used */
return;
err_exit("Volume is full. To shrink it, "
"delete unused files.\n");
return;
/* FIXME: reserve some extra space so Windows can boot ... */
err_exit("New size can't be less than the space already"
" occupied by data.\nYou either need to delete unused"
" files or see the -i option.\n");
err_exit("The fragmentation type, you have, isn't "
"supported yet. Rerun ntfsresize\nwith "
"the -i option to estimate the smallest "
"shrunken volume size supported.\n");
}
{
if (opt.show_progress)
perr_exit("Failed to setup allocation bitmap");
exit(1);
err_printf("Filesystem check failed!\n");
err_printf("%d clusters are referenced outside "
err_printf("%d clusters are referenced multiply"
exit(1);
}
}
{
return 1;
if (!(vol = mount_volume()))
if (device_size <= 0)
err_exit("Current NTFS volume size is bigger than the device "
"size!\nCorrupt partition table or incorrect device "
"partitioning?\n");
/* Take the integer part: don't make the volume bigger than requested */
/* Backup boot sector at the end of device isn't counted in NTFS
volume size thus we have to reserve space for it. */
if (new_size)
--new_size;
err_exit("New size can't be bigger than the device size"
".\nIf you want to enlarge NTFS then first "
"enlarge the device size by e.g. fdisk.\n");
}
printf("Nothing to do: NTFS volume size is already OK.\n");
exit(0);
}
/* This is also true if --info was used w/o --size (new_size = 0) */
if (opt.show_progress)
/*
* Checking and __reporting__ of bad sectors must be done before cluster
* allocation check because chkdsk doesn't fix $Bitmap's w/ bad sectors
* thus users would (were) quite confused why chkdsk doesn't work.
*/
exit(0);
}
}
/* FIXME: performance - relocate logfile here if it's needed */
if (resize.relocations)
/* We don't create backup boot sector because we don't know where the
partition will be split. The scheduled chkdsk will fix it */
printf("The read-only test run ended successfully.\n");
exit(0);
}
/* WARNING: don't modify the texts, external tools grep for them */
printf("Syncing device ...\n");
perr_exit("fsync");
return 0;
}