xref: /onnv-gate/usr/src/cmd/ntfsprogs/ntfsresize.c (revision 10465:f9789e1a1626)
19663SMark.Logan@Sun.COM /**
29663SMark.Logan@Sun.COM  * ntfsresize - Part of the Linux-NTFS project.
39663SMark.Logan@Sun.COM  *
49663SMark.Logan@Sun.COM  * Copyright (c) 2002-2006 Szabolcs Szakacsits
59663SMark.Logan@Sun.COM  * Copyright (c) 2002-2005 Anton Altaparmakov
69663SMark.Logan@Sun.COM  * Copyright (c) 2002-2003 Richard Russon
79663SMark.Logan@Sun.COM  * Copyright (c) 2007      Yura Pakhuchiy
89663SMark.Logan@Sun.COM  *
99663SMark.Logan@Sun.COM  * This utility will resize an NTFS volume without data loss.
109663SMark.Logan@Sun.COM  *
119663SMark.Logan@Sun.COM  * WARNING FOR DEVELOPERS!!! Several external tools grep for text messages
129663SMark.Logan@Sun.COM  * to control execution thus if you would like to change any message
139663SMark.Logan@Sun.COM  * then PLEASE think twice before doing so then don't modify it. Thanks!
149663SMark.Logan@Sun.COM  *
159663SMark.Logan@Sun.COM  * This program is free software; you can redistribute it and/or modify
169663SMark.Logan@Sun.COM  * it under the terms of the GNU General Public License as published by
179663SMark.Logan@Sun.COM  * the Free Software Foundation; either version 2 of the License, or
189663SMark.Logan@Sun.COM  * (at your option) any later version.
199663SMark.Logan@Sun.COM  *
209663SMark.Logan@Sun.COM  * This program is distributed in the hope that it will be useful,
219663SMark.Logan@Sun.COM  * but WITHOUT ANY WARRANTY; without even the implied warranty of
229663SMark.Logan@Sun.COM  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
239663SMark.Logan@Sun.COM  * GNU General Public License for more details.
249663SMark.Logan@Sun.COM  *
259663SMark.Logan@Sun.COM  * You should have received a copy of the GNU General Public License
269663SMark.Logan@Sun.COM  * along with this program (in the main directory of the Linux-NTFS
279663SMark.Logan@Sun.COM  * distribution in the file COPYING); if not, write to the Free Software
289663SMark.Logan@Sun.COM  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
299663SMark.Logan@Sun.COM  */
309663SMark.Logan@Sun.COM 
319663SMark.Logan@Sun.COM #include "config.h"
329663SMark.Logan@Sun.COM 
339663SMark.Logan@Sun.COM #ifdef HAVE_UNISTD_H
349663SMark.Logan@Sun.COM #include <unistd.h>
359663SMark.Logan@Sun.COM #endif
369663SMark.Logan@Sun.COM #ifdef HAVE_STDLIB_H
379663SMark.Logan@Sun.COM #include <stdlib.h>
389663SMark.Logan@Sun.COM #endif
399663SMark.Logan@Sun.COM #ifdef HAVE_STDIO_H
409663SMark.Logan@Sun.COM #include <stdio.h>
419663SMark.Logan@Sun.COM #endif
429663SMark.Logan@Sun.COM #ifdef HAVE_STDARG_H
439663SMark.Logan@Sun.COM #include <stdarg.h>
449663SMark.Logan@Sun.COM #endif
459663SMark.Logan@Sun.COM #ifdef HAVE_STRING_H
469663SMark.Logan@Sun.COM #include <string.h>
479663SMark.Logan@Sun.COM #endif
489663SMark.Logan@Sun.COM #ifdef HAVE_ERRNO_H
499663SMark.Logan@Sun.COM #include <errno.h>
509663SMark.Logan@Sun.COM #endif
519663SMark.Logan@Sun.COM #ifdef HAVE_GETOPT_H
529663SMark.Logan@Sun.COM #include <getopt.h>
539663SMark.Logan@Sun.COM #endif
549663SMark.Logan@Sun.COM 
55*10465SMark.Logan@Sun.COM #include "compat.h"
569663SMark.Logan@Sun.COM #include "debug.h"
579663SMark.Logan@Sun.COM #include "types.h"
589663SMark.Logan@Sun.COM #include "support.h"
599663SMark.Logan@Sun.COM #include "endians.h"
609663SMark.Logan@Sun.COM #include "bootsect.h"
619663SMark.Logan@Sun.COM #include "device.h"
629663SMark.Logan@Sun.COM #include "attrib.h"
639663SMark.Logan@Sun.COM #include "volume.h"
649663SMark.Logan@Sun.COM #include "mft.h"
659663SMark.Logan@Sun.COM #include "bitmap.h"
669663SMark.Logan@Sun.COM #include "inode.h"
679663SMark.Logan@Sun.COM #include "runlist.h"
689663SMark.Logan@Sun.COM #include "utils.h"
699663SMark.Logan@Sun.COM #include "version.h"
709663SMark.Logan@Sun.COM 
719663SMark.Logan@Sun.COM static const char *EXEC_NAME = "ntfsresize";
729663SMark.Logan@Sun.COM 
739663SMark.Logan@Sun.COM static const char *resize_warning_msg =
749663SMark.Logan@Sun.COM "WARNING: Every sanity check passed and only the dangerous operations left.\n"
759663SMark.Logan@Sun.COM "Make sure that important data has been backed up! Power outage or computer\n"
769663SMark.Logan@Sun.COM "crash may result major data loss!\n";
779663SMark.Logan@Sun.COM 
789663SMark.Logan@Sun.COM static const char *resize_important_msg =
799663SMark.Logan@Sun.COM #ifdef __sun
809663SMark.Logan@Sun.COM "When booted, Windows will check the file system and may reboot.\n"
819663SMark.Logan@Sun.COM "If you are running this from inside parted, STOP reading now.\n"
829663SMark.Logan@Sun.COM "Otherwise, you can go on to shrink the device with fdisk or parted.\n"
839663SMark.Logan@Sun.COM #else
849663SMark.Logan@Sun.COM "You can go on to shrink the device for example with Linux fdisk.\n"
859663SMark.Logan@Sun.COM #endif
869663SMark.Logan@Sun.COM "IMPORTANT: When recreating the partition, make sure that you\n"
879663SMark.Logan@Sun.COM "  1)  create it at the same disk sector (use sector as the unit!)\n"
889663SMark.Logan@Sun.COM "  2)  create it with the same partition type (usually 7, HPFS/NTFS)\n"
899663SMark.Logan@Sun.COM "  3)  do not make it smaller than the new NTFS filesystem size\n"
909663SMark.Logan@Sun.COM "  4)  set the bootable flag for the partition if it existed before\n"
919663SMark.Logan@Sun.COM "Otherwise you won't be able to access NTFS or can't boot from the disk!\n"
929663SMark.Logan@Sun.COM "If you make a mistake and don't have a partition table backup then you\n"
939663SMark.Logan@Sun.COM "can recover the partition table by TestDisk or Parted's rescue mode.\n";
949663SMark.Logan@Sun.COM 
959663SMark.Logan@Sun.COM static const char *invalid_ntfs_msg =
969663SMark.Logan@Sun.COM "The device '%s' doesn't have a valid NTFS.\n"
979663SMark.Logan@Sun.COM "Maybe you selected the wrong partition? Or the whole disk instead of a\n"
989663SMark.Logan@Sun.COM "partition (e.g. /dev/hda, not /dev/hda1)? This error might also occur\n"
999663SMark.Logan@Sun.COM "if the disk was incorrectly repartitioned (see the ntfsresize FAQ).\n";
1009663SMark.Logan@Sun.COM 
1019663SMark.Logan@Sun.COM static const char *corrupt_volume_msg =
1029663SMark.Logan@Sun.COM "NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n"
1039663SMark.Logan@Sun.COM "The usage of the /f parameter is very IMPORTANT! No modification was\n"
1049663SMark.Logan@Sun.COM "and will be made to NTFS by this software until it gets repaired.\n";
1059663SMark.Logan@Sun.COM 
1069663SMark.Logan@Sun.COM static const char *hibernated_volume_msg =
1079663SMark.Logan@Sun.COM "The NTFS partition is hibernated. Windows must be resumed and turned off\n"
1089663SMark.Logan@Sun.COM "properly, so resizing could be done safely.\n";
1099663SMark.Logan@Sun.COM 
1109663SMark.Logan@Sun.COM static const char *unclean_journal_msg =
1119663SMark.Logan@Sun.COM "The NTFS journal file is unclean. Please shutdown Windows properly before\n"
1129663SMark.Logan@Sun.COM "using this software! Note, if you have run chkdsk previously then boot\n"
1139663SMark.Logan@Sun.COM "Windows again which will automatically initialize the journal correctly.\n";
1149663SMark.Logan@Sun.COM 
1159663SMark.Logan@Sun.COM static const char *opened_volume_msg =
1169663SMark.Logan@Sun.COM "This software has detected that the NTFS volume is already opened by another\n"
1179663SMark.Logan@Sun.COM "software thus it refuses to progress to preserve data consistency.\n";
1189663SMark.Logan@Sun.COM 
1199663SMark.Logan@Sun.COM static const char *bad_sectors_warning_msg =
1209663SMark.Logan@Sun.COM "****************************************************************************\n"
1219663SMark.Logan@Sun.COM "* WARNING: The disk has bad sector. This means physical damage on the disk *\n"
1229663SMark.Logan@Sun.COM "* surface caused by deterioration, manufacturing faults or other reason.   *\n"
1239663SMark.Logan@Sun.COM "* The reliability of the disk may stay stable or degrade fast. We suggest  *\n"
1249663SMark.Logan@Sun.COM "* making a full backup urgently by running 'ntfsclone --rescue ...' then   *\n"
1259663SMark.Logan@Sun.COM "* run 'chkdsk /f /r' on Windows and rebooot it TWICE! Then you can resize  *\n"
1269663SMark.Logan@Sun.COM "* NTFS safely by additionally using the --bad-sectors option of ntfsresize.*\n"
1279663SMark.Logan@Sun.COM "****************************************************************************\n";
1289663SMark.Logan@Sun.COM 
1299663SMark.Logan@Sun.COM static const char *many_bad_sectors_msg =
1309663SMark.Logan@Sun.COM "***************************************************************************\n"
1319663SMark.Logan@Sun.COM "* WARNING: The disk has many bad sectors. This means physical damage      *\n"
1329663SMark.Logan@Sun.COM "* on the disk surface caused by deterioration, manufacturing faults or    *\n"
1339663SMark.Logan@Sun.COM "* other reason. We suggest to get a replacement disk as soon as possible. *\n"
1349663SMark.Logan@Sun.COM "***************************************************************************\n";
1359663SMark.Logan@Sun.COM 
1369663SMark.Logan@Sun.COM static struct {
1379663SMark.Logan@Sun.COM 	int verbose;
1389663SMark.Logan@Sun.COM 	int debug;
1399663SMark.Logan@Sun.COM 	int ro_flag;
1409663SMark.Logan@Sun.COM 	int force;
1419663SMark.Logan@Sun.COM 	int info;
1429663SMark.Logan@Sun.COM 	int show_progress;
1439663SMark.Logan@Sun.COM 	int badsectors;
1449663SMark.Logan@Sun.COM 	s64 bytes;
1459663SMark.Logan@Sun.COM 	char *volume;
1469663SMark.Logan@Sun.COM } opt;
1479663SMark.Logan@Sun.COM 
1489663SMark.Logan@Sun.COM struct bitmap {
1499663SMark.Logan@Sun.COM 	s64 size;
1509663SMark.Logan@Sun.COM 	u8 *bm;
1519663SMark.Logan@Sun.COM };
1529663SMark.Logan@Sun.COM 
1539663SMark.Logan@Sun.COM #define NTFS_PROGBAR		0x0001
1549663SMark.Logan@Sun.COM #define NTFS_PROGBAR_SUPPRESS	0x0002
1559663SMark.Logan@Sun.COM 
1569663SMark.Logan@Sun.COM struct progress_bar {
1579663SMark.Logan@Sun.COM 	u64 start;
1589663SMark.Logan@Sun.COM 	u64 stop;
1599663SMark.Logan@Sun.COM 	int resolution;
1609663SMark.Logan@Sun.COM 	int flags;
1619663SMark.Logan@Sun.COM 	float unit;
1629663SMark.Logan@Sun.COM };
1639663SMark.Logan@Sun.COM 
1649663SMark.Logan@Sun.COM struct llcn_t {
1659663SMark.Logan@Sun.COM 	s64 lcn;	/* last used LCN for a "special" file/attr type */
1669663SMark.Logan@Sun.COM 	s64 inode;	/* inode using it */
1679663SMark.Logan@Sun.COM };
1689663SMark.Logan@Sun.COM 
1699663SMark.Logan@Sun.COM #define NTFSCK_PROGBAR		0x0001
1709663SMark.Logan@Sun.COM 
1719663SMark.Logan@Sun.COM typedef struct {
1729663SMark.Logan@Sun.COM 	ntfs_inode *ni;		     /* inode being processed */
1739663SMark.Logan@Sun.COM 	ntfs_attr_search_ctx *ctx;   /* inode attribute being processed */
1749663SMark.Logan@Sun.COM 	s64 inuse;		     /* num of clusters in use */
1759663SMark.Logan@Sun.COM 	int multi_ref;		     /* num of clusters referenced many times */
1769663SMark.Logan@Sun.COM 	int outsider;		     /* num of clusters outside the volume */
1779663SMark.Logan@Sun.COM 	int show_outsider;	     /* controls showing the above information */
1789663SMark.Logan@Sun.COM 	int flags;
1799663SMark.Logan@Sun.COM 	struct bitmap lcn_bitmap;
1809663SMark.Logan@Sun.COM } ntfsck_t;
1819663SMark.Logan@Sun.COM 
1829663SMark.Logan@Sun.COM typedef struct {
1839663SMark.Logan@Sun.COM 	ntfs_volume *vol;
1849663SMark.Logan@Sun.COM 	ntfs_inode *ni;		     /* inode being processed */
1859663SMark.Logan@Sun.COM 	s64 new_volume_size;	     /* in clusters; 0 = --info w/o --size */
1869663SMark.Logan@Sun.COM 	MFT_REF mref;                /* mft reference */
1879663SMark.Logan@Sun.COM 	MFT_RECORD *mrec;            /* mft record */
1889663SMark.Logan@Sun.COM 	ntfs_attr_search_ctx *ctx;   /* inode attribute being processed */
1899663SMark.Logan@Sun.COM 	u64 relocations;	     /* num of clusters to relocate */
1909663SMark.Logan@Sun.COM 	s64 inuse;		     /* num of clusters in use */
1919663SMark.Logan@Sun.COM 	runlist mftmir_rl;	     /* $MFTMirr AT_DATA's new position */
1929663SMark.Logan@Sun.COM 	s64 mftmir_old;		     /* $MFTMirr AT_DATA's old LCN */
1939663SMark.Logan@Sun.COM 	int dirty_inode;	     /* some inode data got relocated */
1949663SMark.Logan@Sun.COM 	int shrink;		     /* shrink = 1, enlarge = 0 */
1959663SMark.Logan@Sun.COM 	s64 badclusters;	     /* num of physically dead clusters */
1969663SMark.Logan@Sun.COM 	VCN mft_highest_vcn;	     /* used for relocating the $MFT */
1979663SMark.Logan@Sun.COM 	struct progress_bar progress;
1989663SMark.Logan@Sun.COM 	struct bitmap lcn_bitmap;
1999663SMark.Logan@Sun.COM 	/* Temporary statistics until all case is supported */
2009663SMark.Logan@Sun.COM 	struct llcn_t last_mft;
2019663SMark.Logan@Sun.COM 	struct llcn_t last_mftmir;
2029663SMark.Logan@Sun.COM 	struct llcn_t last_multi_mft;
2039663SMark.Logan@Sun.COM 	struct llcn_t last_sparse;
2049663SMark.Logan@Sun.COM 	struct llcn_t last_compressed;
2059663SMark.Logan@Sun.COM 	struct llcn_t last_lcn;
2069663SMark.Logan@Sun.COM 	s64 last_unsupp;	     /* last unsupported cluster */
2079663SMark.Logan@Sun.COM } ntfs_resize_t;
2089663SMark.Logan@Sun.COM 
2099663SMark.Logan@Sun.COM /* FIXME: This, lcn_bitmap and pos from find_free_cluster() will make a cluster
2109663SMark.Logan@Sun.COM    allocation related structure, attached to ntfs_resize_t */
2119663SMark.Logan@Sun.COM static s64 max_free_cluster_range = 0;
2129663SMark.Logan@Sun.COM 
2139663SMark.Logan@Sun.COM #define NTFS_MBYTE (1000 * 1000)
2149663SMark.Logan@Sun.COM 
2159663SMark.Logan@Sun.COM /* WARNING: don't modify the text, external tools grep for it */
2169663SMark.Logan@Sun.COM #define ERR_PREFIX   "ERROR"
2179663SMark.Logan@Sun.COM #define PERR_PREFIX  ERR_PREFIX "(%d): "
2189663SMark.Logan@Sun.COM #define NERR_PREFIX  ERR_PREFIX ": "
2199663SMark.Logan@Sun.COM 
2209663SMark.Logan@Sun.COM #define DIRTY_NONE		(0)
2219663SMark.Logan@Sun.COM #define DIRTY_INODE		(1)
2229663SMark.Logan@Sun.COM #define DIRTY_ATTRIB		(2)
2239663SMark.Logan@Sun.COM 
2249663SMark.Logan@Sun.COM #define NTFS_MAX_CLUSTER_SIZE	(65536)
2259663SMark.Logan@Sun.COM 
rounded_up_division(s64 numer,s64 denom)2269663SMark.Logan@Sun.COM static s64 rounded_up_division(s64 numer, s64 denom)
2279663SMark.Logan@Sun.COM {
2289663SMark.Logan@Sun.COM 	return (numer + (denom - 1)) / denom;
2299663SMark.Logan@Sun.COM }
2309663SMark.Logan@Sun.COM 
2319663SMark.Logan@Sun.COM /**
2329663SMark.Logan@Sun.COM  * perr_printf
2339663SMark.Logan@Sun.COM  *
2349663SMark.Logan@Sun.COM  * Print an error message.
2359663SMark.Logan@Sun.COM  */
2369663SMark.Logan@Sun.COM __attribute__((format(printf, 1, 2)))
perr_printf(const char * fmt,...)2379663SMark.Logan@Sun.COM static void perr_printf(const char *fmt, ...)
2389663SMark.Logan@Sun.COM {
2399663SMark.Logan@Sun.COM 	va_list ap;
2409663SMark.Logan@Sun.COM 	int eo = errno;
2419663SMark.Logan@Sun.COM 
2429663SMark.Logan@Sun.COM 	fprintf(stdout, PERR_PREFIX, eo);
2439663SMark.Logan@Sun.COM 	va_start(ap, fmt);
2449663SMark.Logan@Sun.COM 	vfprintf(stdout, fmt, ap);
2459663SMark.Logan@Sun.COM 	va_end(ap);
2469663SMark.Logan@Sun.COM 	fprintf(stdout, ": %s\n", strerror(eo));
2479663SMark.Logan@Sun.COM 	fflush(stdout);
2489663SMark.Logan@Sun.COM 	fflush(stderr);
2499663SMark.Logan@Sun.COM }
2509663SMark.Logan@Sun.COM 
2519663SMark.Logan@Sun.COM __attribute__((format(printf, 1, 2)))
err_printf(const char * fmt,...)2529663SMark.Logan@Sun.COM static void err_printf(const char *fmt, ...)
2539663SMark.Logan@Sun.COM {
2549663SMark.Logan@Sun.COM 	va_list ap;
2559663SMark.Logan@Sun.COM 
2569663SMark.Logan@Sun.COM 	fprintf(stdout, NERR_PREFIX);
2579663SMark.Logan@Sun.COM 	va_start(ap, fmt);
2589663SMark.Logan@Sun.COM 	vfprintf(stdout, fmt, ap);
2599663SMark.Logan@Sun.COM 	va_end(ap);
2609663SMark.Logan@Sun.COM 	fflush(stdout);
2619663SMark.Logan@Sun.COM 	fflush(stderr);
2629663SMark.Logan@Sun.COM }
2639663SMark.Logan@Sun.COM 
2649663SMark.Logan@Sun.COM /**
2659663SMark.Logan@Sun.COM  * err_exit
2669663SMark.Logan@Sun.COM  *
2679663SMark.Logan@Sun.COM  * Print and error message and exit the program.
2689663SMark.Logan@Sun.COM  */
2699663SMark.Logan@Sun.COM __attribute__((noreturn))
2709663SMark.Logan@Sun.COM __attribute__((format(printf, 1, 2)))
err_exit(const char * fmt,...)2719663SMark.Logan@Sun.COM static int err_exit(const char *fmt, ...)
2729663SMark.Logan@Sun.COM {
2739663SMark.Logan@Sun.COM 	va_list ap;
2749663SMark.Logan@Sun.COM 
2759663SMark.Logan@Sun.COM 	fprintf(stdout, NERR_PREFIX);
2769663SMark.Logan@Sun.COM 	va_start(ap, fmt);
2779663SMark.Logan@Sun.COM 	vfprintf(stdout, fmt, ap);
2789663SMark.Logan@Sun.COM 	va_end(ap);
2799663SMark.Logan@Sun.COM 	fflush(stdout);
2809663SMark.Logan@Sun.COM 	fflush(stderr);
2819663SMark.Logan@Sun.COM 	exit(1);
2829663SMark.Logan@Sun.COM }
2839663SMark.Logan@Sun.COM 
2849663SMark.Logan@Sun.COM /**
2859663SMark.Logan@Sun.COM  * perr_exit
2869663SMark.Logan@Sun.COM  *
2879663SMark.Logan@Sun.COM  * Print and error message and exit the program
2889663SMark.Logan@Sun.COM  */
2899663SMark.Logan@Sun.COM __attribute__((noreturn))
2909663SMark.Logan@Sun.COM __attribute__((format(printf, 1, 2)))
perr_exit(const char * fmt,...)2919663SMark.Logan@Sun.COM static int perr_exit(const char *fmt, ...)
2929663SMark.Logan@Sun.COM {
2939663SMark.Logan@Sun.COM 	va_list ap;
2949663SMark.Logan@Sun.COM 	int eo = errno;
2959663SMark.Logan@Sun.COM 
2969663SMark.Logan@Sun.COM 	fprintf(stdout, PERR_PREFIX, eo);
2979663SMark.Logan@Sun.COM 	va_start(ap, fmt);
2989663SMark.Logan@Sun.COM 	vfprintf(stdout, fmt, ap);
2999663SMark.Logan@Sun.COM 	va_end(ap);
3009663SMark.Logan@Sun.COM 	printf(": %s\n", strerror(eo));
3019663SMark.Logan@Sun.COM 	fflush(stdout);
3029663SMark.Logan@Sun.COM 	fflush(stderr);
3039663SMark.Logan@Sun.COM 	exit(1);
3049663SMark.Logan@Sun.COM }
3059663SMark.Logan@Sun.COM 
3069663SMark.Logan@Sun.COM /**
3079663SMark.Logan@Sun.COM  * usage - Print a list of the parameters to the program
3089663SMark.Logan@Sun.COM  *
3099663SMark.Logan@Sun.COM  * Print a list of the parameters and options for the program.
3109663SMark.Logan@Sun.COM  *
3119663SMark.Logan@Sun.COM  * Return:  none
3129663SMark.Logan@Sun.COM  */
3139663SMark.Logan@Sun.COM __attribute__((noreturn))
usage(void)3149663SMark.Logan@Sun.COM static void usage(void)
3159663SMark.Logan@Sun.COM {
3169663SMark.Logan@Sun.COM 
3179663SMark.Logan@Sun.COM 	printf("\nUsage: %s [OPTIONS] DEVICE\n"
3189663SMark.Logan@Sun.COM 		"    Resize an NTFS volume non-destructively, safely move any data if needed.\n"
3199663SMark.Logan@Sun.COM 		"\n"
3209663SMark.Logan@Sun.COM 		"    -i, --info             Estimate the smallest shrunken size possible\n"
3219663SMark.Logan@Sun.COM 		"    -s, --size SIZE        Resize volume to SIZE[k|M|G] bytes\n"
3229663SMark.Logan@Sun.COM 		"\n"
3239663SMark.Logan@Sun.COM 		"    -n, --no-action        Do not write to disk\n"
3249663SMark.Logan@Sun.COM 		"    -b, --bad-sectors      Support disks having bad sectors\n"
3259663SMark.Logan@Sun.COM 		"    -f, --force            Force to progress\n"
3269663SMark.Logan@Sun.COM 		"    -P, --no-progress-bar  Don't show progress bar\n"
3279663SMark.Logan@Sun.COM 		"    -v, --verbose          More output\n"
3289663SMark.Logan@Sun.COM 		"    -V, --version          Display version information\n"
3299663SMark.Logan@Sun.COM 		"    -h, --help             Display this help\n"
3309663SMark.Logan@Sun.COM #ifdef DEBUG
3319663SMark.Logan@Sun.COM 		"    -d, --debug            Show debug information\n"
3329663SMark.Logan@Sun.COM #endif
3339663SMark.Logan@Sun.COM 		"\n"
3349663SMark.Logan@Sun.COM 		"    The options -i and -s are mutually exclusive. If both options are\n"
3359663SMark.Logan@Sun.COM 		"    omitted then the NTFS volume will be enlarged to the DEVICE size.\n"
3369663SMark.Logan@Sun.COM 		"\n", EXEC_NAME);
3379663SMark.Logan@Sun.COM 	printf("%s%s", ntfs_bugs, ntfs_home);
3389663SMark.Logan@Sun.COM 	printf("Ntfsresize FAQ: http://linux-ntfs.sourceforge.net/info/ntfsresize.html\n");
3399663SMark.Logan@Sun.COM 	exit(1);
3409663SMark.Logan@Sun.COM }
3419663SMark.Logan@Sun.COM 
3429663SMark.Logan@Sun.COM /**
3439663SMark.Logan@Sun.COM  * proceed_question
3449663SMark.Logan@Sun.COM  *
3459663SMark.Logan@Sun.COM  * Force the user to confirm an action before performing it.
3469663SMark.Logan@Sun.COM  * Copy-paste from e2fsprogs
3479663SMark.Logan@Sun.COM  */
proceed_question(void)3489663SMark.Logan@Sun.COM static void proceed_question(void)
3499663SMark.Logan@Sun.COM {
3509663SMark.Logan@Sun.COM 	char buf[256];
3519663SMark.Logan@Sun.COM 	const char *short_yes = "yY";
3529663SMark.Logan@Sun.COM 
3539663SMark.Logan@Sun.COM 	fflush(stdout);
3549663SMark.Logan@Sun.COM 	fflush(stderr);
3559663SMark.Logan@Sun.COM 	printf("Are you sure you want to proceed (y/[n])? ");
3569663SMark.Logan@Sun.COM 	buf[0] = 0;
3579663SMark.Logan@Sun.COM 	fgets(buf, sizeof(buf), stdin);
3589663SMark.Logan@Sun.COM 	if (!strchr(short_yes, buf[0])) {
3599663SMark.Logan@Sun.COM 		printf("OK quitting. NO CHANGES have been made to your "
3609663SMark.Logan@Sun.COM 				"NTFS volume.\n");
3619663SMark.Logan@Sun.COM 		exit(1);
3629663SMark.Logan@Sun.COM 	}
3639663SMark.Logan@Sun.COM }
3649663SMark.Logan@Sun.COM 
3659663SMark.Logan@Sun.COM /**
3669663SMark.Logan@Sun.COM  * version - Print version information about the program
3679663SMark.Logan@Sun.COM  *
3689663SMark.Logan@Sun.COM  * Print a copyright statement and a brief description of the program.
3699663SMark.Logan@Sun.COM  *
3709663SMark.Logan@Sun.COM  * Return:  none
3719663SMark.Logan@Sun.COM  */
version(void)3729663SMark.Logan@Sun.COM static void version(void)
3739663SMark.Logan@Sun.COM {
3749663SMark.Logan@Sun.COM 	printf("\nResize an NTFS Volume, without data loss.\n\n");
3759663SMark.Logan@Sun.COM 	printf("Copyright (c) 2002-2006  Szabolcs Szakacsits\n");
3769663SMark.Logan@Sun.COM 	printf("Copyright (c) 2002-2005  Anton Altaparmakov\n");
3779663SMark.Logan@Sun.COM 	printf("Copyright (c) 2002-2003  Richard Russon\n");
3789663SMark.Logan@Sun.COM 	printf("Copyright (c) 2007       Yura Pakhuchiy\n");
3799663SMark.Logan@Sun.COM 	printf("\n%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home);
3809663SMark.Logan@Sun.COM }
3819663SMark.Logan@Sun.COM 
3829663SMark.Logan@Sun.COM /**
3839663SMark.Logan@Sun.COM  * get_new_volume_size
3849663SMark.Logan@Sun.COM  *
3859663SMark.Logan@Sun.COM  * Convert a user-supplied string into a size.  Without any suffix the number
3869663SMark.Logan@Sun.COM  * will be assumed to be in bytes.  If the number has a suffix of k, M or G it
3879663SMark.Logan@Sun.COM  * will be scaled up by 1000, 1000000, or 1000000000.
3889663SMark.Logan@Sun.COM  */
get_new_volume_size(char * s)3899663SMark.Logan@Sun.COM static s64 get_new_volume_size(char *s)
3909663SMark.Logan@Sun.COM {
3919663SMark.Logan@Sun.COM 	s64 size;
3929663SMark.Logan@Sun.COM 	char *suffix;
3939663SMark.Logan@Sun.COM 	int prefix_kind = 1000;
3949663SMark.Logan@Sun.COM 
3959663SMark.Logan@Sun.COM 	size = strtoll(s, &suffix, 10);
3969663SMark.Logan@Sun.COM 	if (size <= 0 || errno == ERANGE)
3979663SMark.Logan@Sun.COM 		err_exit("Illegal new volume size\n");
3989663SMark.Logan@Sun.COM 
3999663SMark.Logan@Sun.COM 	if (!*suffix)
4009663SMark.Logan@Sun.COM 		return size;
4019663SMark.Logan@Sun.COM 
4029663SMark.Logan@Sun.COM 	if (strlen(suffix) == 2 && suffix[1] == 'i')
4039663SMark.Logan@Sun.COM 		prefix_kind = 1024;
4049663SMark.Logan@Sun.COM 	else if (strlen(suffix) > 1)
4059663SMark.Logan@Sun.COM 		usage();
4069663SMark.Logan@Sun.COM 
4079663SMark.Logan@Sun.COM 	/* We follow the SI prefixes:
4089663SMark.Logan@Sun.COM 	   http://physics.nist.gov/cuu/Units/prefixes.html
4099663SMark.Logan@Sun.COM 	   http://physics.nist.gov/cuu/Units/binary.html
4109663SMark.Logan@Sun.COM 	   Disk partitioning tools use prefixes as,
4119663SMark.Logan@Sun.COM 	                       k        M          G
4129663SMark.Logan@Sun.COM 	   fdisk 2.11x-      2^10     2^20      10^3*2^20
4139663SMark.Logan@Sun.COM 	   fdisk 2.11y+     10^3     10^6       10^9
4149663SMark.Logan@Sun.COM 	   cfdisk           10^3     10^6       10^9
4159663SMark.Logan@Sun.COM 	   sfdisk            2^10     2^20
4169663SMark.Logan@Sun.COM 	   parted            2^10     2^20  (may change)
4179663SMark.Logan@Sun.COM 	   fdisk (DOS)       2^10     2^20
4189663SMark.Logan@Sun.COM 	*/
4199663SMark.Logan@Sun.COM 	/* FIXME: check for overflow */
4209663SMark.Logan@Sun.COM 	switch (*suffix) {
4219663SMark.Logan@Sun.COM 	case 'G':
4229663SMark.Logan@Sun.COM 		size *= prefix_kind;
4239663SMark.Logan@Sun.COM 	case 'M':
4249663SMark.Logan@Sun.COM 		size *= prefix_kind;
4259663SMark.Logan@Sun.COM 	case 'k':
4269663SMark.Logan@Sun.COM 		size *= prefix_kind;
4279663SMark.Logan@Sun.COM 		break;
4289663SMark.Logan@Sun.COM 	default:
4299663SMark.Logan@Sun.COM 		usage();
4309663SMark.Logan@Sun.COM 	}
4319663SMark.Logan@Sun.COM 
4329663SMark.Logan@Sun.COM 	return size;
4339663SMark.Logan@Sun.COM }
4349663SMark.Logan@Sun.COM 
4359663SMark.Logan@Sun.COM /**
4369663SMark.Logan@Sun.COM  * parse_options - Read and validate the programs command line
4379663SMark.Logan@Sun.COM  *
4389663SMark.Logan@Sun.COM  * Read the command line, verify the syntax and parse the options.
4399663SMark.Logan@Sun.COM  * This function is very long, but quite simple.
4409663SMark.Logan@Sun.COM  *
4419663SMark.Logan@Sun.COM  * Return:  1 Success
4429663SMark.Logan@Sun.COM  *	    0 Error, one or more problems
4439663SMark.Logan@Sun.COM  */
parse_options(int argc,char ** argv)4449663SMark.Logan@Sun.COM static int parse_options(int argc, char **argv)
4459663SMark.Logan@Sun.COM {
4469663SMark.Logan@Sun.COM 	static const char *sopt = "-bdfhinPs:vV";
4479663SMark.Logan@Sun.COM 	static const struct option lopt[] = {
4489663SMark.Logan@Sun.COM 		{ "bad-sectors",no_argument,		NULL, 'b' },
4499663SMark.Logan@Sun.COM #ifdef DEBUG
4509663SMark.Logan@Sun.COM 		{ "debug",	no_argument,		NULL, 'd' },
4519663SMark.Logan@Sun.COM #endif
4529663SMark.Logan@Sun.COM 		{ "force",	no_argument,		NULL, 'f' },
4539663SMark.Logan@Sun.COM 		{ "help",	no_argument,		NULL, 'h' },
4549663SMark.Logan@Sun.COM 		{ "info",	no_argument,		NULL, 'i' },
4559663SMark.Logan@Sun.COM 		{ "no-action",	no_argument,		NULL, 'n' },
4569663SMark.Logan@Sun.COM 		{ "no-progress-bar", no_argument,	NULL, 'P' },
4579663SMark.Logan@Sun.COM 		{ "size",	required_argument,	NULL, 's' },
4589663SMark.Logan@Sun.COM 		{ "verbose",	no_argument,		NULL, 'v' },
4599663SMark.Logan@Sun.COM 		{ "version",	no_argument,		NULL, 'V' },
4609663SMark.Logan@Sun.COM 		{ NULL, 0, NULL, 0 }
4619663SMark.Logan@Sun.COM 	};
4629663SMark.Logan@Sun.COM 
4639663SMark.Logan@Sun.COM 	int c;
4649663SMark.Logan@Sun.COM 	int err  = 0;
4659663SMark.Logan@Sun.COM 	int ver  = 0;
4669663SMark.Logan@Sun.COM 	int help = 0;
4679663SMark.Logan@Sun.COM 
4689663SMark.Logan@Sun.COM 	memset(&opt, 0, sizeof(opt));
4699663SMark.Logan@Sun.COM 	opt.show_progress = 1;
4709663SMark.Logan@Sun.COM 
4719663SMark.Logan@Sun.COM 	while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
4729663SMark.Logan@Sun.COM 		switch (c) {
4739663SMark.Logan@Sun.COM 		case 1:	/* A non-option argument */
4749663SMark.Logan@Sun.COM 			if (!err && !opt.volume)
4759663SMark.Logan@Sun.COM 				opt.volume = argv[optind-1];
4769663SMark.Logan@Sun.COM 			else
4779663SMark.Logan@Sun.COM 				err++;
4789663SMark.Logan@Sun.COM 			break;
4799663SMark.Logan@Sun.COM 		case 'b':
4809663SMark.Logan@Sun.COM 			opt.badsectors++;
4819663SMark.Logan@Sun.COM 			break;
4829663SMark.Logan@Sun.COM 		case 'd':
4839663SMark.Logan@Sun.COM 			opt.debug++;
4849663SMark.Logan@Sun.COM 			break;
4859663SMark.Logan@Sun.COM 		case 'f':
4869663SMark.Logan@Sun.COM 			opt.force++;
4879663SMark.Logan@Sun.COM 			break;
4889663SMark.Logan@Sun.COM 		case 'h':
4899663SMark.Logan@Sun.COM 		case '?':
4909663SMark.Logan@Sun.COM 			help++;
4919663SMark.Logan@Sun.COM 			break;
4929663SMark.Logan@Sun.COM 		case 'i':
4939663SMark.Logan@Sun.COM 			opt.info++;
4949663SMark.Logan@Sun.COM 			break;
4959663SMark.Logan@Sun.COM 		case 'n':
4969663SMark.Logan@Sun.COM 			opt.ro_flag = NTFS_MNT_RDONLY;
4979663SMark.Logan@Sun.COM 			break;
4989663SMark.Logan@Sun.COM 		case 'P':
4999663SMark.Logan@Sun.COM 			opt.show_progress = 0;
5009663SMark.Logan@Sun.COM 			break;
5019663SMark.Logan@Sun.COM 		case 's':
5029663SMark.Logan@Sun.COM 			if (!err && (opt.bytes == 0))
5039663SMark.Logan@Sun.COM 				opt.bytes = get_new_volume_size(optarg);
5049663SMark.Logan@Sun.COM 			else
5059663SMark.Logan@Sun.COM 				err++;
5069663SMark.Logan@Sun.COM 			break;
5079663SMark.Logan@Sun.COM 		case 'v':
5089663SMark.Logan@Sun.COM 			opt.verbose++;
5099663SMark.Logan@Sun.COM 			ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
5109663SMark.Logan@Sun.COM 			break;
5119663SMark.Logan@Sun.COM 		case 'V':
5129663SMark.Logan@Sun.COM 			ver++;
5139663SMark.Logan@Sun.COM 			break;
5149663SMark.Logan@Sun.COM 		default:
5159663SMark.Logan@Sun.COM 			if (optopt == 's') {
5169663SMark.Logan@Sun.COM 				printf("Option '%s' requires an argument.\n", argv[optind-1]);
5179663SMark.Logan@Sun.COM 			} else {
5189663SMark.Logan@Sun.COM 				printf("Unknown option '%s'.\n", argv[optind-1]);
5199663SMark.Logan@Sun.COM 			}
5209663SMark.Logan@Sun.COM 			err++;
5219663SMark.Logan@Sun.COM 			break;
5229663SMark.Logan@Sun.COM 		}
5239663SMark.Logan@Sun.COM 	}
5249663SMark.Logan@Sun.COM 
5259663SMark.Logan@Sun.COM 	if (!help && !ver) {
5269663SMark.Logan@Sun.COM 		if (opt.volume == NULL) {
5279663SMark.Logan@Sun.COM 			if (argc > 1)
5289663SMark.Logan@Sun.COM 				printf("You must specify exactly one device.\n");
5299663SMark.Logan@Sun.COM 			err++;
5309663SMark.Logan@Sun.COM 		}
5319663SMark.Logan@Sun.COM 		if (opt.info) {
5329663SMark.Logan@Sun.COM 			opt.ro_flag = NTFS_MNT_RDONLY;
5339663SMark.Logan@Sun.COM 			if (opt.bytes) {
5349663SMark.Logan@Sun.COM 				printf(NERR_PREFIX "Options --info and --size "
5359663SMark.Logan@Sun.COM 					"can't be used together.\n");
5369663SMark.Logan@Sun.COM 				usage();
5379663SMark.Logan@Sun.COM 			}
5389663SMark.Logan@Sun.COM 		}
5399663SMark.Logan@Sun.COM 	}
5409663SMark.Logan@Sun.COM 
5419663SMark.Logan@Sun.COM 	/* Redirect stderr to stdout, note fflush()es are essential! */
5429663SMark.Logan@Sun.COM 	fflush(stdout);
5439663SMark.Logan@Sun.COM 	fflush(stderr);
5449663SMark.Logan@Sun.COM 	if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1)
5459663SMark.Logan@Sun.COM 		perr_exit("Failed to redirect stderr to stdout");
5469663SMark.Logan@Sun.COM 	fflush(stdout);
5479663SMark.Logan@Sun.COM 	fflush(stderr);
5489663SMark.Logan@Sun.COM 
5499663SMark.Logan@Sun.COM #ifdef DEBUG
5509663SMark.Logan@Sun.COM 	if (!opt.debug)
5519663SMark.Logan@Sun.COM 		if (!freopen("/dev/null", "w", stderr))
5529663SMark.Logan@Sun.COM 			perr_exit("Failed to redirect stderr to /dev/null");
5539663SMark.Logan@Sun.COM #endif
5549663SMark.Logan@Sun.COM 
5559663SMark.Logan@Sun.COM 	if (ver)
5569663SMark.Logan@Sun.COM 		version();
5579663SMark.Logan@Sun.COM 	if (help || err)
5589663SMark.Logan@Sun.COM 		usage();
5599663SMark.Logan@Sun.COM 
5609663SMark.Logan@Sun.COM 	return (!err && !help && !ver);
5619663SMark.Logan@Sun.COM }
5629663SMark.Logan@Sun.COM 
print_advise(ntfs_volume * vol,s64 supp_lcn)5639663SMark.Logan@Sun.COM static void print_advise(ntfs_volume *vol, s64 supp_lcn)
5649663SMark.Logan@Sun.COM {
5659663SMark.Logan@Sun.COM 	s64 old_b, new_b, freed_b, old_mb, new_mb, freed_mb;
5669663SMark.Logan@Sun.COM 
5679663SMark.Logan@Sun.COM 	old_b = vol->nr_clusters * vol->cluster_size;
5689663SMark.Logan@Sun.COM 	old_mb = rounded_up_division(old_b, NTFS_MBYTE);
5699663SMark.Logan@Sun.COM 
5709663SMark.Logan@Sun.COM 	/* Take the next supported cluster (free or relocatable)
5719663SMark.Logan@Sun.COM 	   plus reserve a cluster for the backup boot sector */
5729663SMark.Logan@Sun.COM 	supp_lcn += 2;
5739663SMark.Logan@Sun.COM 
5749663SMark.Logan@Sun.COM 	if (supp_lcn > vol->nr_clusters) {
5759663SMark.Logan@Sun.COM 		err_printf("Very rare fragmentation type detected. "
5769663SMark.Logan@Sun.COM 			   "Sorry, it's not supported yet.\n"
5779663SMark.Logan@Sun.COM 			   "Try to defragment your NTFS, perhaps it helps.\n");
5789663SMark.Logan@Sun.COM 		exit(1);
5799663SMark.Logan@Sun.COM 	}
5809663SMark.Logan@Sun.COM 
5819663SMark.Logan@Sun.COM 	new_b = supp_lcn * vol->cluster_size;
5829663SMark.Logan@Sun.COM 	new_mb = rounded_up_division(new_b, NTFS_MBYTE);
5839663SMark.Logan@Sun.COM 	freed_b = (vol->nr_clusters - supp_lcn + 1) * vol->cluster_size;
5849663SMark.Logan@Sun.COM 	freed_mb = freed_b / NTFS_MBYTE;
5859663SMark.Logan@Sun.COM 
5869663SMark.Logan@Sun.COM 	/* WARNING: don't modify the text, external tools grep for it */
5879663SMark.Logan@Sun.COM 	printf("You might resize at %lld bytes ", (long long)new_b);
5889663SMark.Logan@Sun.COM 	if ((new_mb * NTFS_MBYTE) < old_b)
5899663SMark.Logan@Sun.COM 		printf("or %lld MB ", (long long)new_mb);
5909663SMark.Logan@Sun.COM 
5919663SMark.Logan@Sun.COM 	printf("(freeing ");
5929663SMark.Logan@Sun.COM 	if (freed_mb && (old_mb - new_mb))
5939663SMark.Logan@Sun.COM 	    printf("%lld MB", (long long)(old_mb - new_mb));
5949663SMark.Logan@Sun.COM 	else
5959663SMark.Logan@Sun.COM 	    printf("%lld bytes", (long long)freed_b);
5969663SMark.Logan@Sun.COM 	printf(").\n");
5979663SMark.Logan@Sun.COM 
5989663SMark.Logan@Sun.COM 	printf("Please make a test run using both the -n and -s options "
5999663SMark.Logan@Sun.COM 	       "before real resizing!\n");
6009663SMark.Logan@Sun.COM }
6019663SMark.Logan@Sun.COM 
rl_set(runlist * rl,VCN vcn,LCN lcn,s64 len)6029663SMark.Logan@Sun.COM static void rl_set(runlist *rl, VCN vcn, LCN lcn, s64 len)
6039663SMark.Logan@Sun.COM {
6049663SMark.Logan@Sun.COM 	rl->vcn = vcn;
6059663SMark.Logan@Sun.COM 	rl->lcn = lcn;
6069663SMark.Logan@Sun.COM 	rl->length = len;
6079663SMark.Logan@Sun.COM }
6089663SMark.Logan@Sun.COM 
rl_items(runlist * rl)6099663SMark.Logan@Sun.COM static int rl_items(runlist *rl)
6109663SMark.Logan@Sun.COM {
6119663SMark.Logan@Sun.COM 	int i = 0;
6129663SMark.Logan@Sun.COM 
6139663SMark.Logan@Sun.COM 	while (rl[i++].length)
6149663SMark.Logan@Sun.COM 		;
6159663SMark.Logan@Sun.COM 
6169663SMark.Logan@Sun.COM 	return i;
6179663SMark.Logan@Sun.COM }
6189663SMark.Logan@Sun.COM 
dump_run(runlist_element * r)6199663SMark.Logan@Sun.COM static void dump_run(runlist_element *r)
6209663SMark.Logan@Sun.COM {
6219663SMark.Logan@Sun.COM 	ntfs_log_verbose(" %8lld  %8lld (0x%08llx)  %lld\n", (long long)r->vcn,
6229663SMark.Logan@Sun.COM 			 (long long)r->lcn, (long long)r->lcn,
6239663SMark.Logan@Sun.COM 			 (long long)r->length);
6249663SMark.Logan@Sun.COM }
6259663SMark.Logan@Sun.COM 
dump_runlist(runlist * rl)6269663SMark.Logan@Sun.COM static void dump_runlist(runlist *rl)
6279663SMark.Logan@Sun.COM {
6289663SMark.Logan@Sun.COM 	while (rl->length)
6299663SMark.Logan@Sun.COM 		dump_run(rl++);
6309663SMark.Logan@Sun.COM }
6319663SMark.Logan@Sun.COM 
6329663SMark.Logan@Sun.COM /**
6339663SMark.Logan@Sun.COM  * nr_clusters_to_bitmap_byte_size
6349663SMark.Logan@Sun.COM  *
6359663SMark.Logan@Sun.COM  * Take the number of clusters in the volume and calculate the size of $Bitmap.
6369663SMark.Logan@Sun.COM  * The size must be always a multiple of 8 bytes.
6379663SMark.Logan@Sun.COM  */
nr_clusters_to_bitmap_byte_size(s64 nr_clusters)6389663SMark.Logan@Sun.COM static s64 nr_clusters_to_bitmap_byte_size(s64 nr_clusters)
6399663SMark.Logan@Sun.COM {
6409663SMark.Logan@Sun.COM 	s64 bm_bsize;
6419663SMark.Logan@Sun.COM 
6429663SMark.Logan@Sun.COM 	bm_bsize = rounded_up_division(nr_clusters, 8);
6439663SMark.Logan@Sun.COM 	bm_bsize = (bm_bsize + 7) & ~7;
6449663SMark.Logan@Sun.COM 
6459663SMark.Logan@Sun.COM 	return bm_bsize;
6469663SMark.Logan@Sun.COM }
6479663SMark.Logan@Sun.COM 
collect_resize_constraints(ntfs_resize_t * resize,runlist * rl)6489663SMark.Logan@Sun.COM static void collect_resize_constraints(ntfs_resize_t *resize, runlist *rl)
6499663SMark.Logan@Sun.COM {
6509663SMark.Logan@Sun.COM 	s64 inode, last_lcn;
6519663SMark.Logan@Sun.COM 	ATTR_FLAGS flags;
6529663SMark.Logan@Sun.COM 	ATTR_TYPES atype;
6539663SMark.Logan@Sun.COM 	struct llcn_t *llcn = NULL;
6549663SMark.Logan@Sun.COM 	int ret, supported = 0;
6559663SMark.Logan@Sun.COM 
6569663SMark.Logan@Sun.COM 	last_lcn = rl->lcn + (rl->length - 1);
6579663SMark.Logan@Sun.COM 
6589663SMark.Logan@Sun.COM 	inode = resize->ni->mft_no;
6599663SMark.Logan@Sun.COM 	flags = resize->ctx->attr->flags;
6609663SMark.Logan@Sun.COM 	atype = resize->ctx->attr->type;
6619663SMark.Logan@Sun.COM 
6629663SMark.Logan@Sun.COM 	if ((ret = ntfs_inode_badclus_bad(inode, resize->ctx->attr)) != 0) {
6639663SMark.Logan@Sun.COM 		if (ret == -1)
6649663SMark.Logan@Sun.COM 			perr_exit("Bad sector list check failed");
6659663SMark.Logan@Sun.COM 		return;
6669663SMark.Logan@Sun.COM 	}
6679663SMark.Logan@Sun.COM 
6689663SMark.Logan@Sun.COM 	if (inode == FILE_Bitmap) {
6699663SMark.Logan@Sun.COM 		llcn = &resize->last_lcn;
6709663SMark.Logan@Sun.COM 		if (atype == AT_DATA && NInoAttrList(resize->ni))
6719663SMark.Logan@Sun.COM 		    err_exit("Highly fragmented $Bitmap isn't supported yet.");
6729663SMark.Logan@Sun.COM 
6739663SMark.Logan@Sun.COM 		supported = 1;
6749663SMark.Logan@Sun.COM 
6759663SMark.Logan@Sun.COM 	} else if (inode == FILE_MFT) {
6769663SMark.Logan@Sun.COM 		llcn = &resize->last_mft;
6779663SMark.Logan@Sun.COM 		/*
6789663SMark.Logan@Sun.COM 		 *  First run of $MFT AT_DATA isn't supported yet.
6799663SMark.Logan@Sun.COM 		 */
6809663SMark.Logan@Sun.COM 		if (atype != AT_DATA || rl->vcn)
6819663SMark.Logan@Sun.COM 			supported = 1;
6829663SMark.Logan@Sun.COM 
6839663SMark.Logan@Sun.COM 	} else if (NInoAttrList(resize->ni)) {
6849663SMark.Logan@Sun.COM 		llcn = &resize->last_multi_mft;
6859663SMark.Logan@Sun.COM 
6869663SMark.Logan@Sun.COM 		if (inode != FILE_MFTMirr)
6879663SMark.Logan@Sun.COM 			supported = 1;
6889663SMark.Logan@Sun.COM 
6899663SMark.Logan@Sun.COM 	} else if (flags & ATTR_IS_SPARSE) {
6909663SMark.Logan@Sun.COM 		llcn = &resize->last_sparse;
6919663SMark.Logan@Sun.COM 		supported = 1;
6929663SMark.Logan@Sun.COM 
6939663SMark.Logan@Sun.COM 	} else if (flags & ATTR_IS_COMPRESSED) {
6949663SMark.Logan@Sun.COM 		llcn = &resize->last_compressed;
6959663SMark.Logan@Sun.COM 		supported = 1;
6969663SMark.Logan@Sun.COM 
6979663SMark.Logan@Sun.COM 	} else if (inode == FILE_MFTMirr) {
6989663SMark.Logan@Sun.COM 		llcn = &resize->last_mftmir;
6999663SMark.Logan@Sun.COM 		supported = 1;
7009663SMark.Logan@Sun.COM 
7019663SMark.Logan@Sun.COM 		/* Fragmented $MFTMirr DATA attribute isn't supported yet */
7029663SMark.Logan@Sun.COM 		if (atype == AT_DATA)
7039663SMark.Logan@Sun.COM 			if (rl[1].length != 0 || rl->vcn)
7049663SMark.Logan@Sun.COM 				supported = 0;
7059663SMark.Logan@Sun.COM 	} else {
7069663SMark.Logan@Sun.COM 		llcn = &resize->last_lcn;
7079663SMark.Logan@Sun.COM 		supported = 1;
7089663SMark.Logan@Sun.COM 	}
7099663SMark.Logan@Sun.COM 
7109663SMark.Logan@Sun.COM 	if (llcn->lcn < last_lcn) {
7119663SMark.Logan@Sun.COM 		llcn->lcn = last_lcn;
7129663SMark.Logan@Sun.COM 		llcn->inode = inode;
7139663SMark.Logan@Sun.COM 	}
7149663SMark.Logan@Sun.COM 
7159663SMark.Logan@Sun.COM 	if (supported)
7169663SMark.Logan@Sun.COM 		return;
7179663SMark.Logan@Sun.COM 
7189663SMark.Logan@Sun.COM 	if (resize->last_unsupp < last_lcn)
7199663SMark.Logan@Sun.COM 		resize->last_unsupp = last_lcn;
7209663SMark.Logan@Sun.COM }
7219663SMark.Logan@Sun.COM 
7229663SMark.Logan@Sun.COM 
collect_relocation_info(ntfs_resize_t * resize,runlist * rl)7239663SMark.Logan@Sun.COM static void collect_relocation_info(ntfs_resize_t *resize, runlist *rl)
7249663SMark.Logan@Sun.COM {
7259663SMark.Logan@Sun.COM 	s64 lcn, lcn_length, start, len, inode;
7269663SMark.Logan@Sun.COM 	s64 new_vol_size;	/* (last LCN on the volume) + 1 */
7279663SMark.Logan@Sun.COM 
7289663SMark.Logan@Sun.COM 	lcn = rl->lcn;
7299663SMark.Logan@Sun.COM 	lcn_length = rl->length;
7309663SMark.Logan@Sun.COM 	inode = resize->ni->mft_no;
7319663SMark.Logan@Sun.COM 	new_vol_size = resize->new_volume_size;
7329663SMark.Logan@Sun.COM 
7339663SMark.Logan@Sun.COM 	if (lcn + lcn_length <= new_vol_size)
7349663SMark.Logan@Sun.COM 		return;
7359663SMark.Logan@Sun.COM 
7369663SMark.Logan@Sun.COM 	if (inode == FILE_Bitmap && resize->ctx->attr->type == AT_DATA)
7379663SMark.Logan@Sun.COM 		return;
7389663SMark.Logan@Sun.COM 
7399663SMark.Logan@Sun.COM 	start = lcn;
7409663SMark.Logan@Sun.COM 	len = lcn_length;
7419663SMark.Logan@Sun.COM 
7429663SMark.Logan@Sun.COM 	if (lcn < new_vol_size) {
7439663SMark.Logan@Sun.COM 		start = new_vol_size;
7449663SMark.Logan@Sun.COM 		len = lcn_length - (new_vol_size - lcn);
7459663SMark.Logan@Sun.COM 
7469663SMark.Logan@Sun.COM 		if (!opt.info && (inode == FILE_MFTMirr)) {
7479663SMark.Logan@Sun.COM 			err_printf("$MFTMirr can't be split up yet. Please try "
7489663SMark.Logan@Sun.COM 				   "a different size.\n");
7499663SMark.Logan@Sun.COM 			print_advise(resize->vol, lcn + lcn_length - 1);
7509663SMark.Logan@Sun.COM 			exit(1);
7519663SMark.Logan@Sun.COM 		}
7529663SMark.Logan@Sun.COM 	}
7539663SMark.Logan@Sun.COM 
7549663SMark.Logan@Sun.COM 	resize->relocations += len;
7559663SMark.Logan@Sun.COM 
7569663SMark.Logan@Sun.COM 	if (!opt.info || !resize->new_volume_size)
7579663SMark.Logan@Sun.COM 		return;
7589663SMark.Logan@Sun.COM 
7599663SMark.Logan@Sun.COM 	printf("Relocation needed for inode %8lld attr 0x%x LCN 0x%08llx "
7609663SMark.Logan@Sun.COM 			"length %6lld\n", (long long)inode,
7619663SMark.Logan@Sun.COM 			(unsigned int)le32_to_cpu(resize->ctx->attr->type),
7629663SMark.Logan@Sun.COM 			(unsigned long long)start, (long long)len);
7639663SMark.Logan@Sun.COM }
7649663SMark.Logan@Sun.COM 
7659663SMark.Logan@Sun.COM /**
7669663SMark.Logan@Sun.COM  * build_lcn_usage_bitmap
7679663SMark.Logan@Sun.COM  *
7689663SMark.Logan@Sun.COM  * lcn_bitmap has one bit for each cluster on the disk.  Initially, lcn_bitmap
7699663SMark.Logan@Sun.COM  * has no bits set.  As each attribute record is read the bits in lcn_bitmap are
7709663SMark.Logan@Sun.COM  * checked to ensure that no other file already references that cluster.
7719663SMark.Logan@Sun.COM  *
7729663SMark.Logan@Sun.COM  * This serves as a rudimentary "chkdsk" operation.
7739663SMark.Logan@Sun.COM  */
build_lcn_usage_bitmap(ntfs_volume * vol,ntfsck_t * fsck)7749663SMark.Logan@Sun.COM static void build_lcn_usage_bitmap(ntfs_volume *vol, ntfsck_t *fsck)
7759663SMark.Logan@Sun.COM {
7769663SMark.Logan@Sun.COM 	s64 inode;
7779663SMark.Logan@Sun.COM 	ATTR_RECORD *a;
7789663SMark.Logan@Sun.COM 	runlist *rl;
7799663SMark.Logan@Sun.COM 	int i, j;
7809663SMark.Logan@Sun.COM 	struct bitmap *lcn_bitmap = &fsck->lcn_bitmap;
7819663SMark.Logan@Sun.COM 
7829663SMark.Logan@Sun.COM 	a = fsck->ctx->attr;
7839663SMark.Logan@Sun.COM 	inode = fsck->ni->mft_no;
7849663SMark.Logan@Sun.COM 
7859663SMark.Logan@Sun.COM 	if (!a->non_resident)
7869663SMark.Logan@Sun.COM 		return;
7879663SMark.Logan@Sun.COM 
7889663SMark.Logan@Sun.COM 	if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) {
7899663SMark.Logan@Sun.COM 		int err = errno;
7909663SMark.Logan@Sun.COM 		perr_printf("ntfs_decompress_mapping_pairs");
7919663SMark.Logan@Sun.COM 		if (err == EIO)
7929663SMark.Logan@Sun.COM 			printf("%s", corrupt_volume_msg);
7939663SMark.Logan@Sun.COM 		exit(1);
7949663SMark.Logan@Sun.COM 	}
7959663SMark.Logan@Sun.COM 
7969663SMark.Logan@Sun.COM 
7979663SMark.Logan@Sun.COM 	for (i = 0; rl[i].length; i++) {
7989663SMark.Logan@Sun.COM 		s64 lcn = rl[i].lcn;
7999663SMark.Logan@Sun.COM 		s64 lcn_length = rl[i].length;
8009663SMark.Logan@Sun.COM 
8019663SMark.Logan@Sun.COM 		/* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */
8029663SMark.Logan@Sun.COM 		if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED)
8039663SMark.Logan@Sun.COM 			continue;
8049663SMark.Logan@Sun.COM 
8059663SMark.Logan@Sun.COM 		/* FIXME: ntfs_mapping_pairs_decompress should return error */
8069663SMark.Logan@Sun.COM 		if (lcn < 0 || lcn_length <= 0)
8079663SMark.Logan@Sun.COM 			err_exit("Corrupt runlist in inode %lld attr %x LCN "
8089663SMark.Logan@Sun.COM 				 "%llx length %llx\n", inode,
8099663SMark.Logan@Sun.COM 				 (unsigned int)le32_to_cpu(a->type), lcn,
8109663SMark.Logan@Sun.COM 				 lcn_length);
8119663SMark.Logan@Sun.COM 
8129663SMark.Logan@Sun.COM 		for (j = 0; j < lcn_length; j++) {
8139663SMark.Logan@Sun.COM 			u64 k = (u64)lcn + j;
8149663SMark.Logan@Sun.COM 
8159663SMark.Logan@Sun.COM 			if (k >= (u64)vol->nr_clusters) {
8169663SMark.Logan@Sun.COM 				long long outsiders = lcn_length - j;
8179663SMark.Logan@Sun.COM 
8189663SMark.Logan@Sun.COM 				fsck->outsider += outsiders;
8199663SMark.Logan@Sun.COM 
8209663SMark.Logan@Sun.COM 				if (++fsck->show_outsider <= 10 || opt.verbose)
8219663SMark.Logan@Sun.COM 					printf("Outside of the volume reference"
8229663SMark.Logan@Sun.COM 					       " for inode %lld at %lld:%lld\n",
8239663SMark.Logan@Sun.COM 					       inode, (long long)k, outsiders);
8249663SMark.Logan@Sun.COM 
8259663SMark.Logan@Sun.COM 				break;
8269663SMark.Logan@Sun.COM 			}
8279663SMark.Logan@Sun.COM 
8289663SMark.Logan@Sun.COM 			if (ntfs_bit_get_and_set(lcn_bitmap->bm, k, 1)) {
8299663SMark.Logan@Sun.COM 				if (++fsck->multi_ref <= 10 || opt.verbose)
8309663SMark.Logan@Sun.COM 					printf("Cluster %lld is referenced "
8319663SMark.Logan@Sun.COM 					       "multiple times!\n",
8329663SMark.Logan@Sun.COM 					       (long long)k);
8339663SMark.Logan@Sun.COM 				continue;
8349663SMark.Logan@Sun.COM 			}
8359663SMark.Logan@Sun.COM 		}
8369663SMark.Logan@Sun.COM 		fsck->inuse += lcn_length;
8379663SMark.Logan@Sun.COM 	}
8389663SMark.Logan@Sun.COM 	free(rl);
8399663SMark.Logan@Sun.COM }
8409663SMark.Logan@Sun.COM 
8419663SMark.Logan@Sun.COM 
attr_get_search_ctx(ntfs_inode * ni,MFT_RECORD * mrec)8429663SMark.Logan@Sun.COM static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec)
8439663SMark.Logan@Sun.COM {
8449663SMark.Logan@Sun.COM 	ntfs_attr_search_ctx *ret;
8459663SMark.Logan@Sun.COM 
8469663SMark.Logan@Sun.COM 	if ((ret = ntfs_attr_get_search_ctx(ni, mrec)) == NULL)
8479663SMark.Logan@Sun.COM 		perr_printf("ntfs_attr_get_search_ctx");
8489663SMark.Logan@Sun.COM 
8499663SMark.Logan@Sun.COM 	return ret;
8509663SMark.Logan@Sun.COM }
8519663SMark.Logan@Sun.COM 
8529663SMark.Logan@Sun.COM /**
8539663SMark.Logan@Sun.COM  * walk_attributes
8549663SMark.Logan@Sun.COM  *
8559663SMark.Logan@Sun.COM  * For a given MFT Record, iterate through all its attributes.  Any non-resident
8569663SMark.Logan@Sun.COM  * data runs will be marked in lcn_bitmap.
8579663SMark.Logan@Sun.COM  */
walk_attributes(ntfs_volume * vol,ntfsck_t * fsck)8589663SMark.Logan@Sun.COM static int walk_attributes(ntfs_volume *vol, ntfsck_t *fsck)
8599663SMark.Logan@Sun.COM {
8609663SMark.Logan@Sun.COM 	if (!(fsck->ctx = attr_get_search_ctx(fsck->ni, NULL)))
8619663SMark.Logan@Sun.COM 		return -1;
8629663SMark.Logan@Sun.COM 
8639663SMark.Logan@Sun.COM 	while (!ntfs_attrs_walk(fsck->ctx)) {
8649663SMark.Logan@Sun.COM 		if (fsck->ctx->attr->type == AT_END)
8659663SMark.Logan@Sun.COM 			break;
8669663SMark.Logan@Sun.COM 		build_lcn_usage_bitmap(vol, fsck);
8679663SMark.Logan@Sun.COM 	}
8689663SMark.Logan@Sun.COM 
8699663SMark.Logan@Sun.COM 	ntfs_attr_put_search_ctx(fsck->ctx);
8709663SMark.Logan@Sun.COM 	return 0;
8719663SMark.Logan@Sun.COM }
8729663SMark.Logan@Sun.COM 
8739663SMark.Logan@Sun.COM /**
8749663SMark.Logan@Sun.COM  * compare_bitmaps
8759663SMark.Logan@Sun.COM  *
8769663SMark.Logan@Sun.COM  * Compare two bitmaps.  In this case, $Bitmap as read from the disk and
8779663SMark.Logan@Sun.COM  * lcn_bitmap which we built from the MFT Records.
8789663SMark.Logan@Sun.COM  */
compare_bitmaps(ntfs_volume * vol,struct bitmap * a)8799663SMark.Logan@Sun.COM static void compare_bitmaps(ntfs_volume *vol, struct bitmap *a)
8809663SMark.Logan@Sun.COM {
8819663SMark.Logan@Sun.COM 	s64 i, pos, count;
8829663SMark.Logan@Sun.COM 	int mismatch = 0;
8839663SMark.Logan@Sun.COM 	int backup_boot = 0;
8849663SMark.Logan@Sun.COM 	u8 bm[NTFS_BUF_SIZE];
8859663SMark.Logan@Sun.COM 
8869663SMark.Logan@Sun.COM 	printf("Accounting clusters ...\n");
8879663SMark.Logan@Sun.COM 
8889663SMark.Logan@Sun.COM 	pos = 0;
8899663SMark.Logan@Sun.COM 	while (1) {
8909663SMark.Logan@Sun.COM 		count = ntfs_attr_pread(vol->lcnbmp_na, pos, NTFS_BUF_SIZE, bm);
8919663SMark.Logan@Sun.COM 		if (count == -1)
8929663SMark.Logan@Sun.COM 			perr_exit("Couldn't get $Bitmap $DATA");
8939663SMark.Logan@Sun.COM 
8949663SMark.Logan@Sun.COM 		if (count == 0) {
8959663SMark.Logan@Sun.COM 			if (a->size > pos)
8969663SMark.Logan@Sun.COM 				err_exit("$Bitmap size is smaller than expected"
8979663SMark.Logan@Sun.COM 					 " (%lld != %lld)\n", a->size, pos);
8989663SMark.Logan@Sun.COM 			break;
8999663SMark.Logan@Sun.COM 		}
9009663SMark.Logan@Sun.COM 
9019663SMark.Logan@Sun.COM 		for (i = 0; i < count; i++, pos++) {
9029663SMark.Logan@Sun.COM 			s64 cl;  /* current cluster */
9039663SMark.Logan@Sun.COM 
9049663SMark.Logan@Sun.COM 			if (a->size <= pos)
9059663SMark.Logan@Sun.COM 				goto done;
9069663SMark.Logan@Sun.COM 
9079663SMark.Logan@Sun.COM 			if (a->bm[pos] == bm[i])
9089663SMark.Logan@Sun.COM 				continue;
9099663SMark.Logan@Sun.COM 
9109663SMark.Logan@Sun.COM 			for (cl = pos * 8; cl < (pos + 1) * 8; cl++) {
9119663SMark.Logan@Sun.COM 				char bit;
9129663SMark.Logan@Sun.COM 
9139663SMark.Logan@Sun.COM 				bit = ntfs_bit_get(a->bm, cl);
9149663SMark.Logan@Sun.COM 				if (bit == ntfs_bit_get(bm, i * 8 + cl % 8))
9159663SMark.Logan@Sun.COM 					continue;
9169663SMark.Logan@Sun.COM 
9179663SMark.Logan@Sun.COM 				if (!mismatch && !bit && !backup_boot &&
9189663SMark.Logan@Sun.COM 						cl == vol->nr_clusters / 2) {
9199663SMark.Logan@Sun.COM 					/* FIXME: call also boot sector check */
9209663SMark.Logan@Sun.COM 					backup_boot = 1;
9219663SMark.Logan@Sun.COM 					printf("Found backup boot sector in "
9229663SMark.Logan@Sun.COM 					       "the middle of the volume.\n");
9239663SMark.Logan@Sun.COM 					continue;
9249663SMark.Logan@Sun.COM 				}
9259663SMark.Logan@Sun.COM 
9269663SMark.Logan@Sun.COM 				if (++mismatch > 10 && !opt.verbose)
9279663SMark.Logan@Sun.COM 					continue;
9289663SMark.Logan@Sun.COM 
9299663SMark.Logan@Sun.COM 				printf("Cluster accounting failed at %lld "
9309663SMark.Logan@Sun.COM 						"(0x%llx): %s cluster in "
9319663SMark.Logan@Sun.COM 						"$Bitmap\n", (long long)cl,
9329663SMark.Logan@Sun.COM 						(unsigned long long)cl,
9339663SMark.Logan@Sun.COM 						bit ? "missing" : "extra");
9349663SMark.Logan@Sun.COM 			}
9359663SMark.Logan@Sun.COM 		}
9369663SMark.Logan@Sun.COM 	}
9379663SMark.Logan@Sun.COM done:
9389663SMark.Logan@Sun.COM 	if (mismatch) {
9399663SMark.Logan@Sun.COM 		printf("Filesystem check failed! Totally %d cluster "
9409663SMark.Logan@Sun.COM 		       "accounting mismatches.\n", mismatch);
9419663SMark.Logan@Sun.COM 		err_printf("%s", corrupt_volume_msg);
9429663SMark.Logan@Sun.COM 		exit(1);
9439663SMark.Logan@Sun.COM 	}
9449663SMark.Logan@Sun.COM }
9459663SMark.Logan@Sun.COM 
9469663SMark.Logan@Sun.COM /**
9479663SMark.Logan@Sun.COM  * progress_init
9489663SMark.Logan@Sun.COM  *
9499663SMark.Logan@Sun.COM  * Create and scale our progress bar.
9509663SMark.Logan@Sun.COM  */
progress_init(struct progress_bar * p,u64 start,u64 stop,int flags)9519663SMark.Logan@Sun.COM static void progress_init(struct progress_bar *p, u64 start, u64 stop, int flags)
9529663SMark.Logan@Sun.COM {
9539663SMark.Logan@Sun.COM 	p->start = start;
9549663SMark.Logan@Sun.COM 	p->stop = stop;
9559663SMark.Logan@Sun.COM 	p->unit = 100.0 / (stop - start);
9569663SMark.Logan@Sun.COM 	p->resolution = 100;
9579663SMark.Logan@Sun.COM 	p->flags = flags;
9589663SMark.Logan@Sun.COM }
9599663SMark.Logan@Sun.COM 
9609663SMark.Logan@Sun.COM /**
9619663SMark.Logan@Sun.COM  * progress_update
9629663SMark.Logan@Sun.COM  *
9639663SMark.Logan@Sun.COM  * Update the progress bar and tell the user.
9649663SMark.Logan@Sun.COM  */
progress_update(struct progress_bar * p,u64 current)9659663SMark.Logan@Sun.COM static void progress_update(struct progress_bar *p, u64 current)
9669663SMark.Logan@Sun.COM {
9679663SMark.Logan@Sun.COM 	float percent;
9689663SMark.Logan@Sun.COM 
9699663SMark.Logan@Sun.COM 	if (!(p->flags & NTFS_PROGBAR))
9709663SMark.Logan@Sun.COM 		return;
9719663SMark.Logan@Sun.COM 	if (p->flags & NTFS_PROGBAR_SUPPRESS)
9729663SMark.Logan@Sun.COM 		return;
9739663SMark.Logan@Sun.COM 
9749663SMark.Logan@Sun.COM 	/* WARNING: don't modify the texts, external tools grep for them */
9759663SMark.Logan@Sun.COM 	percent = p->unit * current;
9769663SMark.Logan@Sun.COM 	if (current != p->stop) {
9779663SMark.Logan@Sun.COM 		if ((current - p->start) % p->resolution)
9789663SMark.Logan@Sun.COM 			return;
9799663SMark.Logan@Sun.COM 		printf("%6.2f percent completed\r", percent);
9809663SMark.Logan@Sun.COM 	} else
9819663SMark.Logan@Sun.COM 		printf("100.00 percent completed\n");
9829663SMark.Logan@Sun.COM 	fflush(stdout);
9839663SMark.Logan@Sun.COM }
9849663SMark.Logan@Sun.COM 
inode_close(ntfs_inode * ni)9859663SMark.Logan@Sun.COM static int inode_close(ntfs_inode *ni)
9869663SMark.Logan@Sun.COM {
9879663SMark.Logan@Sun.COM 	if (ntfs_inode_close(ni)) {
9889663SMark.Logan@Sun.COM 		perr_printf("ntfs_inode_close for inode %llu",
9899663SMark.Logan@Sun.COM 			    (unsigned long long)ni->mft_no);
9909663SMark.Logan@Sun.COM 		return -1;
9919663SMark.Logan@Sun.COM 	}
9929663SMark.Logan@Sun.COM 	return 0;
9939663SMark.Logan@Sun.COM }
9949663SMark.Logan@Sun.COM 
9959663SMark.Logan@Sun.COM /**
9969663SMark.Logan@Sun.COM  * walk_inodes
9979663SMark.Logan@Sun.COM  *
9989663SMark.Logan@Sun.COM  * Read each record in the MFT, skipping the unused ones, and build up a bitmap
9999663SMark.Logan@Sun.COM  * from all the non-resident attributes.
10009663SMark.Logan@Sun.COM  */
build_allocation_bitmap(ntfs_volume * vol,ntfsck_t * fsck)10019663SMark.Logan@Sun.COM static int build_allocation_bitmap(ntfs_volume *vol, ntfsck_t *fsck)
10029663SMark.Logan@Sun.COM {
10039663SMark.Logan@Sun.COM 	s64 nr_mft_records, inode = 0;
10049663SMark.Logan@Sun.COM 	ntfs_inode *ni;
10059663SMark.Logan@Sun.COM 	struct progress_bar progress;
10069663SMark.Logan@Sun.COM 	int pb_flags = 0;	/* progress bar flags */
10079663SMark.Logan@Sun.COM 
10089663SMark.Logan@Sun.COM 	/* WARNING: don't modify the text, external tools grep for it */
10099663SMark.Logan@Sun.COM 	printf("Checking filesystem consistency ...\n");
10109663SMark.Logan@Sun.COM 
10119663SMark.Logan@Sun.COM 	if (fsck->flags & NTFSCK_PROGBAR)
10129663SMark.Logan@Sun.COM 		pb_flags |= NTFS_PROGBAR;
10139663SMark.Logan@Sun.COM 
10149663SMark.Logan@Sun.COM 	nr_mft_records = vol->mft_na->initialized_size >>
10159663SMark.Logan@Sun.COM 			vol->mft_record_size_bits;
10169663SMark.Logan@Sun.COM 
10179663SMark.Logan@Sun.COM 	progress_init(&progress, inode, nr_mft_records - 1, pb_flags);
10189663SMark.Logan@Sun.COM 
10199663SMark.Logan@Sun.COM 	for (; inode < nr_mft_records; inode++) {
10209663SMark.Logan@Sun.COM 		progress_update(&progress, inode);
10219663SMark.Logan@Sun.COM 
10229663SMark.Logan@Sun.COM 		if ((ni = ntfs_inode_open(vol, (MFT_REF)inode)) == NULL) {
10239663SMark.Logan@Sun.COM 			/* FIXME: continue only if it make sense, e.g.
10249663SMark.Logan@Sun.COM 			   MFT record not in use based on $MFT bitmap */
10259663SMark.Logan@Sun.COM 			if (errno == EIO || errno == ENOENT)
10269663SMark.Logan@Sun.COM 				continue;
10279663SMark.Logan@Sun.COM 			perr_printf("Reading inode %lld failed", inode);
10289663SMark.Logan@Sun.COM 			return -1;
10299663SMark.Logan@Sun.COM 		}
10309663SMark.Logan@Sun.COM 
10319663SMark.Logan@Sun.COM 		if (ni->mrec->base_mft_record)
10329663SMark.Logan@Sun.COM 			goto close_inode;
10339663SMark.Logan@Sun.COM 
10349663SMark.Logan@Sun.COM 		fsck->ni = ni;
10359663SMark.Logan@Sun.COM 		if (walk_attributes(vol, fsck) != 0) {
10369663SMark.Logan@Sun.COM 			inode_close(ni);
10379663SMark.Logan@Sun.COM 			return -1;
10389663SMark.Logan@Sun.COM 		}
10399663SMark.Logan@Sun.COM close_inode:
10409663SMark.Logan@Sun.COM 		if (inode_close(ni) != 0)
10419663SMark.Logan@Sun.COM 			return -1;
10429663SMark.Logan@Sun.COM 	}
10439663SMark.Logan@Sun.COM 	return 0;
10449663SMark.Logan@Sun.COM }
10459663SMark.Logan@Sun.COM 
build_resize_constraints(ntfs_resize_t * resize)10469663SMark.Logan@Sun.COM static void build_resize_constraints(ntfs_resize_t *resize)
10479663SMark.Logan@Sun.COM {
10489663SMark.Logan@Sun.COM 	s64 i;
10499663SMark.Logan@Sun.COM 	runlist *rl;
10509663SMark.Logan@Sun.COM 
10519663SMark.Logan@Sun.COM 	if (!resize->ctx->attr->non_resident)
10529663SMark.Logan@Sun.COM 		return;
10539663SMark.Logan@Sun.COM 
10549663SMark.Logan@Sun.COM 	if (!(rl = ntfs_mapping_pairs_decompress(resize->vol,
10559663SMark.Logan@Sun.COM 						 resize->ctx->attr, NULL)))
10569663SMark.Logan@Sun.COM 		perr_exit("ntfs_decompress_mapping_pairs");
10579663SMark.Logan@Sun.COM 
10589663SMark.Logan@Sun.COM 	for (i = 0; rl[i].length; i++) {
10599663SMark.Logan@Sun.COM 		/* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */
10609663SMark.Logan@Sun.COM 		if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED)
10619663SMark.Logan@Sun.COM 			continue;
10629663SMark.Logan@Sun.COM 
10639663SMark.Logan@Sun.COM 		collect_resize_constraints(resize, rl + i);
10649663SMark.Logan@Sun.COM 		if (resize->shrink)
10659663SMark.Logan@Sun.COM 			collect_relocation_info(resize, rl + i);
10669663SMark.Logan@Sun.COM 	}
10679663SMark.Logan@Sun.COM 	free(rl);
10689663SMark.Logan@Sun.COM }
10699663SMark.Logan@Sun.COM 
resize_constraints_by_attributes(ntfs_resize_t * resize)10709663SMark.Logan@Sun.COM static void resize_constraints_by_attributes(ntfs_resize_t *resize)
10719663SMark.Logan@Sun.COM {
10729663SMark.Logan@Sun.COM 	if (!(resize->ctx = attr_get_search_ctx(resize->ni, NULL)))
10739663SMark.Logan@Sun.COM 		exit(1);
10749663SMark.Logan@Sun.COM 
10759663SMark.Logan@Sun.COM 	while (!ntfs_attrs_walk(resize->ctx)) {
10769663SMark.Logan@Sun.COM 		if (resize->ctx->attr->type == AT_END)
10779663SMark.Logan@Sun.COM 			break;
10789663SMark.Logan@Sun.COM 		build_resize_constraints(resize);
10799663SMark.Logan@Sun.COM 	}
10809663SMark.Logan@Sun.COM 
10819663SMark.Logan@Sun.COM 	ntfs_attr_put_search_ctx(resize->ctx);
10829663SMark.Logan@Sun.COM }
10839663SMark.Logan@Sun.COM 
set_resize_constraints(ntfs_resize_t * resize)10849663SMark.Logan@Sun.COM static void set_resize_constraints(ntfs_resize_t *resize)
10859663SMark.Logan@Sun.COM {
10869663SMark.Logan@Sun.COM 	s64 nr_mft_records, inode;
10879663SMark.Logan@Sun.COM 	ntfs_inode *ni;
10889663SMark.Logan@Sun.COM 
10899663SMark.Logan@Sun.COM 	printf("Collecting resizing constraints ...\n");
10909663SMark.Logan@Sun.COM 
10919663SMark.Logan@Sun.COM 	nr_mft_records = resize->vol->mft_na->initialized_size >>
10929663SMark.Logan@Sun.COM 			resize->vol->mft_record_size_bits;
10939663SMark.Logan@Sun.COM 
10949663SMark.Logan@Sun.COM 	for (inode = 0; inode < nr_mft_records; inode++) {
10959663SMark.Logan@Sun.COM 
10969663SMark.Logan@Sun.COM 		ni = ntfs_inode_open(resize->vol, (MFT_REF)inode);
10979663SMark.Logan@Sun.COM 		if (ni == NULL) {
10989663SMark.Logan@Sun.COM 			if (errno == EIO || errno == ENOENT)
10999663SMark.Logan@Sun.COM 				continue;
11009663SMark.Logan@Sun.COM 			perr_exit("Reading inode %lld failed", inode);
11019663SMark.Logan@Sun.COM 		}
11029663SMark.Logan@Sun.COM 
11039663SMark.Logan@Sun.COM 		if (ni->mrec->base_mft_record)
11049663SMark.Logan@Sun.COM 			goto close_inode;
11059663SMark.Logan@Sun.COM 
11069663SMark.Logan@Sun.COM 		resize->ni = ni;
11079663SMark.Logan@Sun.COM 		resize_constraints_by_attributes(resize);
11089663SMark.Logan@Sun.COM close_inode:
11099663SMark.Logan@Sun.COM 		if (inode_close(ni) != 0)
11109663SMark.Logan@Sun.COM 			exit(1);
11119663SMark.Logan@Sun.COM 	}
11129663SMark.Logan@Sun.COM }
11139663SMark.Logan@Sun.COM 
rl_fixup(runlist ** rl)11149663SMark.Logan@Sun.COM static void rl_fixup(runlist **rl)
11159663SMark.Logan@Sun.COM {
11169663SMark.Logan@Sun.COM 	runlist *tmp = *rl;
11179663SMark.Logan@Sun.COM 
11189663SMark.Logan@Sun.COM 	if (tmp->lcn == LCN_RL_NOT_MAPPED) {
11199663SMark.Logan@Sun.COM 		s64 unmapped_len = tmp->length;
11209663SMark.Logan@Sun.COM 
11219663SMark.Logan@Sun.COM 		ntfs_log_verbose("Skip unmapped run at the beginning ...\n");
11229663SMark.Logan@Sun.COM 
11239663SMark.Logan@Sun.COM 		if (!tmp->length)
11249663SMark.Logan@Sun.COM 			err_exit("Empty unmapped runlist! Please report!\n");
11259663SMark.Logan@Sun.COM 		(*rl)++;
11269663SMark.Logan@Sun.COM 		for (tmp = *rl; tmp->length; tmp++)
11279663SMark.Logan@Sun.COM 			tmp->vcn -= unmapped_len;
11289663SMark.Logan@Sun.COM 	}
11299663SMark.Logan@Sun.COM 
11309663SMark.Logan@Sun.COM 	for (tmp = *rl; tmp->length; tmp++) {
11319663SMark.Logan@Sun.COM 		if (tmp->lcn == LCN_RL_NOT_MAPPED) {
11329663SMark.Logan@Sun.COM 			ntfs_log_verbose("Skip unmapped run at the end  ...\n");
11339663SMark.Logan@Sun.COM 
11349663SMark.Logan@Sun.COM 			if (tmp[1].length)
11359663SMark.Logan@Sun.COM 				err_exit("Unmapped runlist in the middle! "
11369663SMark.Logan@Sun.COM 					 "Please report!\n");
11379663SMark.Logan@Sun.COM 			tmp->lcn = LCN_ENOENT;
11389663SMark.Logan@Sun.COM 			tmp->length = 0;
11399663SMark.Logan@Sun.COM 		}
11409663SMark.Logan@Sun.COM 	}
11419663SMark.Logan@Sun.COM }
11429663SMark.Logan@Sun.COM 
replace_attribute_runlist(ntfs_volume * vol,ntfs_attr_search_ctx * ctx,runlist * rl)11439663SMark.Logan@Sun.COM static void replace_attribute_runlist(ntfs_volume *vol,
11449663SMark.Logan@Sun.COM 				      ntfs_attr_search_ctx *ctx,
11459663SMark.Logan@Sun.COM 				      runlist *rl)
11469663SMark.Logan@Sun.COM {
11479663SMark.Logan@Sun.COM 	int mp_size, l;
11489663SMark.Logan@Sun.COM 	void *mp;
11499663SMark.Logan@Sun.COM 	ATTR_RECORD *a = ctx->attr;
11509663SMark.Logan@Sun.COM 
11519663SMark.Logan@Sun.COM 	rl_fixup(&rl);
11529663SMark.Logan@Sun.COM 
11539663SMark.Logan@Sun.COM 	if ((mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0)) == -1)
11549663SMark.Logan@Sun.COM 		perr_exit("ntfs_get_size_for_mapping_pairs");
11559663SMark.Logan@Sun.COM 
11569663SMark.Logan@Sun.COM 	if (a->name_length) {
11579663SMark.Logan@Sun.COM 		u16 name_offs = le16_to_cpu(a->name_offset);
11589663SMark.Logan@Sun.COM 		u16 mp_offs = le16_to_cpu(a->u.nonres.mapping_pairs_offset);
11599663SMark.Logan@Sun.COM 
11609663SMark.Logan@Sun.COM 		if (name_offs >= mp_offs)
11619663SMark.Logan@Sun.COM 			err_exit("Attribute name is after mapping pairs! "
11629663SMark.Logan@Sun.COM 				 "Please report!\n");
11639663SMark.Logan@Sun.COM 	}
11649663SMark.Logan@Sun.COM 
11659663SMark.Logan@Sun.COM 	/* CHECKME: don't trust mapping_pairs is always the last item in the
11669663SMark.Logan@Sun.COM 	   attribute, instead check for the real size/space */
11679663SMark.Logan@Sun.COM 	l = (int)le32_to_cpu(a->length) - le16_to_cpu(a->u.nonres.mapping_pairs_offset);
11689663SMark.Logan@Sun.COM 	if (mp_size > l) {
11699663SMark.Logan@Sun.COM 		s64 remains_size;
11709663SMark.Logan@Sun.COM 		char *next_attr;
11719663SMark.Logan@Sun.COM 
11729663SMark.Logan@Sun.COM 		ntfs_log_verbose("Enlarging attribute header ...\n");
11739663SMark.Logan@Sun.COM 
11749663SMark.Logan@Sun.COM 		mp_size = (mp_size + 7) & ~7;
11759663SMark.Logan@Sun.COM 
11769663SMark.Logan@Sun.COM 		ntfs_log_verbose("Old mp size      : %d\n", l);
11779663SMark.Logan@Sun.COM 		ntfs_log_verbose("New mp size      : %d\n", mp_size);
11789663SMark.Logan@Sun.COM 		ntfs_log_verbose("Bytes in use     : %u\n", (unsigned int)
11799663SMark.Logan@Sun.COM 				 le32_to_cpu(ctx->mrec->bytes_in_use));
11809663SMark.Logan@Sun.COM 
11819663SMark.Logan@Sun.COM 		next_attr = (char *)a + le32_to_cpu(a->length);
11829663SMark.Logan@Sun.COM 		l = mp_size - l;
11839663SMark.Logan@Sun.COM 
11849663SMark.Logan@Sun.COM 		ntfs_log_verbose("Bytes in use new : %u\n", l + (unsigned int)
11859663SMark.Logan@Sun.COM 				 le32_to_cpu(ctx->mrec->bytes_in_use));
11869663SMark.Logan@Sun.COM 		ntfs_log_verbose("Bytes allocated  : %u\n", (unsigned int)
11879663SMark.Logan@Sun.COM 				 le32_to_cpu(ctx->mrec->bytes_allocated));
11889663SMark.Logan@Sun.COM 
11899663SMark.Logan@Sun.COM 		remains_size = le32_to_cpu(ctx->mrec->bytes_in_use);
11909663SMark.Logan@Sun.COM 		remains_size -= (next_attr - (char *)ctx->mrec);
11919663SMark.Logan@Sun.COM 
11929663SMark.Logan@Sun.COM 		ntfs_log_verbose("increase         : %d\n", l);
11939663SMark.Logan@Sun.COM 		ntfs_log_verbose("shift            : %lld\n",
11949663SMark.Logan@Sun.COM 				 (long long)remains_size);
11959663SMark.Logan@Sun.COM 
11969663SMark.Logan@Sun.COM 		if (le32_to_cpu(ctx->mrec->bytes_in_use) + l >
11979663SMark.Logan@Sun.COM 				le32_to_cpu(ctx->mrec->bytes_allocated))
11989663SMark.Logan@Sun.COM 			err_exit("Extended record needed (%u > %u), not yet "
11999663SMark.Logan@Sun.COM 				 "supported!\nPlease try to free less space.\n",
12009663SMark.Logan@Sun.COM 				 (unsigned int)le32_to_cpu(ctx->mrec->
12019663SMark.Logan@Sun.COM 					bytes_in_use) + l,
12029663SMark.Logan@Sun.COM 				 (unsigned int)le32_to_cpu(ctx->mrec->
12039663SMark.Logan@Sun.COM 					bytes_allocated));
12049663SMark.Logan@Sun.COM 
12059663SMark.Logan@Sun.COM 		memmove(next_attr + l, next_attr, remains_size);
12069663SMark.Logan@Sun.COM 		ctx->mrec->bytes_in_use = cpu_to_le32(l +
12079663SMark.Logan@Sun.COM 				le32_to_cpu(ctx->mrec->bytes_in_use));
12089663SMark.Logan@Sun.COM 		a->length = cpu_to_le32(le32_to_cpu(a->length) + l);
12099663SMark.Logan@Sun.COM 	}
12109663SMark.Logan@Sun.COM 
12119663SMark.Logan@Sun.COM 	mp = ntfs_calloc(mp_size);
12129663SMark.Logan@Sun.COM 	if (!mp)
12139663SMark.Logan@Sun.COM 		perr_exit("ntfsc_calloc couldn't get memory");
12149663SMark.Logan@Sun.COM 
12159663SMark.Logan@Sun.COM 	if (ntfs_mapping_pairs_build(vol, mp, mp_size, rl, 0, NULL))
12169663SMark.Logan@Sun.COM 		perr_exit("ntfs_mapping_pairs_build");
12179663SMark.Logan@Sun.COM 
12189663SMark.Logan@Sun.COM 	memmove((u8*)a + le16_to_cpu(a->u.nonres.mapping_pairs_offset), mp, mp_size);
12199663SMark.Logan@Sun.COM 
12209663SMark.Logan@Sun.COM 	free(mp);
12219663SMark.Logan@Sun.COM }
12229663SMark.Logan@Sun.COM 
set_bitmap_range(struct bitmap * bm,s64 pos,s64 length,u8 bit)12239663SMark.Logan@Sun.COM static void set_bitmap_range(struct bitmap *bm, s64 pos, s64 length, u8 bit)
12249663SMark.Logan@Sun.COM {
12259663SMark.Logan@Sun.COM 	while (length--)
12269663SMark.Logan@Sun.COM 		ntfs_bit_set(bm->bm, pos++, bit);
12279663SMark.Logan@Sun.COM }
12289663SMark.Logan@Sun.COM 
set_bitmap_clusters(struct bitmap * bm,runlist * rl,u8 bit)12299663SMark.Logan@Sun.COM static void set_bitmap_clusters(struct bitmap *bm, runlist *rl, u8 bit)
12309663SMark.Logan@Sun.COM {
12319663SMark.Logan@Sun.COM 	for (; rl->length; rl++)
12329663SMark.Logan@Sun.COM 		set_bitmap_range(bm, rl->lcn, rl->length, bit);
12339663SMark.Logan@Sun.COM }
12349663SMark.Logan@Sun.COM 
release_bitmap_clusters(struct bitmap * bm,runlist * rl)12359663SMark.Logan@Sun.COM static void release_bitmap_clusters(struct bitmap *bm, runlist *rl)
12369663SMark.Logan@Sun.COM {
12379663SMark.Logan@Sun.COM 	max_free_cluster_range = 0;
12389663SMark.Logan@Sun.COM 	set_bitmap_clusters(bm, rl, 0);
12399663SMark.Logan@Sun.COM }
12409663SMark.Logan@Sun.COM 
set_max_free_zone(s64 length,s64 end,runlist_element * rle)12419663SMark.Logan@Sun.COM static void set_max_free_zone(s64 length, s64 end, runlist_element *rle)
12429663SMark.Logan@Sun.COM {
12439663SMark.Logan@Sun.COM 	if (length > rle->length) {
12449663SMark.Logan@Sun.COM 		rle->lcn = end - length;
12459663SMark.Logan@Sun.COM 		rle->length = length;
12469663SMark.Logan@Sun.COM 	}
12479663SMark.Logan@Sun.COM }
12489663SMark.Logan@Sun.COM 
find_free_cluster(struct bitmap * bm,runlist_element * rle,s64 nr_vol_clusters,int hint)12499663SMark.Logan@Sun.COM static int find_free_cluster(struct bitmap *bm,
12509663SMark.Logan@Sun.COM 			     runlist_element *rle,
12519663SMark.Logan@Sun.COM 			     s64 nr_vol_clusters,
12529663SMark.Logan@Sun.COM 			     int hint)
12539663SMark.Logan@Sun.COM {
12549663SMark.Logan@Sun.COM 	/* FIXME: get rid of this 'static' variable */
12559663SMark.Logan@Sun.COM 	static s64 pos = 0;
12569663SMark.Logan@Sun.COM 	s64 i, items = rle->length;
12579663SMark.Logan@Sun.COM 	s64 free_zone = 0;
12589663SMark.Logan@Sun.COM 
12599663SMark.Logan@Sun.COM 	if (pos >= nr_vol_clusters)
12609663SMark.Logan@Sun.COM 		pos = 0;
12619663SMark.Logan@Sun.COM 	if (!max_free_cluster_range)
12629663SMark.Logan@Sun.COM 		max_free_cluster_range = nr_vol_clusters;
12639663SMark.Logan@Sun.COM 	rle->lcn = rle->length = 0;
12649663SMark.Logan@Sun.COM 	if (hint)
12659663SMark.Logan@Sun.COM 		pos = nr_vol_clusters / 2;
12669663SMark.Logan@Sun.COM 	i = pos;
12679663SMark.Logan@Sun.COM 
12689663SMark.Logan@Sun.COM 	do {
12699663SMark.Logan@Sun.COM 		if (!ntfs_bit_get(bm->bm, i)) {
12709663SMark.Logan@Sun.COM 			if (++free_zone == items) {
12719663SMark.Logan@Sun.COM 				set_max_free_zone(free_zone, i + 1, rle);
12729663SMark.Logan@Sun.COM 				break;
12739663SMark.Logan@Sun.COM 			}
12749663SMark.Logan@Sun.COM 		} else {
12759663SMark.Logan@Sun.COM 			set_max_free_zone(free_zone, i, rle);
12769663SMark.Logan@Sun.COM 			free_zone = 0;
12779663SMark.Logan@Sun.COM 		}
12789663SMark.Logan@Sun.COM 		if (++i == nr_vol_clusters) {
12799663SMark.Logan@Sun.COM 			set_max_free_zone(free_zone, i, rle);
12809663SMark.Logan@Sun.COM 			i = free_zone = 0;
12819663SMark.Logan@Sun.COM 		}
12829663SMark.Logan@Sun.COM 		if (rle->length == max_free_cluster_range)
12839663SMark.Logan@Sun.COM 			break;
12849663SMark.Logan@Sun.COM 	} while (i != pos);
12859663SMark.Logan@Sun.COM 
12869663SMark.Logan@Sun.COM 	if (i)
12879663SMark.Logan@Sun.COM 		set_max_free_zone(free_zone, i, rle);
12889663SMark.Logan@Sun.COM 
12899663SMark.Logan@Sun.COM 	if (!rle->lcn) {
12909663SMark.Logan@Sun.COM 		errno = ENOSPC;
12919663SMark.Logan@Sun.COM 		return -1;
12929663SMark.Logan@Sun.COM 	}
12939663SMark.Logan@Sun.COM 	if (rle->length < items && rle->length < max_free_cluster_range) {
12949663SMark.Logan@Sun.COM 		max_free_cluster_range = rle->length;
12959663SMark.Logan@Sun.COM 		ntfs_log_verbose("Max free range: %7lld     \n",
12969663SMark.Logan@Sun.COM 				 (long long)max_free_cluster_range);
12979663SMark.Logan@Sun.COM 	}
12989663SMark.Logan@Sun.COM 	pos = rle->lcn + items;
12999663SMark.Logan@Sun.COM 	if (pos == nr_vol_clusters)
13009663SMark.Logan@Sun.COM 		pos = 0;
13019663SMark.Logan@Sun.COM 
13029663SMark.Logan@Sun.COM 	set_bitmap_range(bm, rle->lcn, rle->length, 1);
13039663SMark.Logan@Sun.COM 	return 0;
13049663SMark.Logan@Sun.COM }
13059663SMark.Logan@Sun.COM 
alloc_cluster(struct bitmap * bm,s64 items,s64 nr_vol_clusters,int hint)13069663SMark.Logan@Sun.COM static runlist *alloc_cluster(struct bitmap *bm,
13079663SMark.Logan@Sun.COM 			      s64 items,
13089663SMark.Logan@Sun.COM 			      s64 nr_vol_clusters,
13099663SMark.Logan@Sun.COM 			      int hint)
13109663SMark.Logan@Sun.COM {
13119663SMark.Logan@Sun.COM 	runlist_element rle;
13129663SMark.Logan@Sun.COM 	runlist *rl = NULL;
13139663SMark.Logan@Sun.COM 	int rl_size, runs = 0;
13149663SMark.Logan@Sun.COM 	s64 vcn = 0;
13159663SMark.Logan@Sun.COM 
13169663SMark.Logan@Sun.COM 	if (items <= 0) {
13179663SMark.Logan@Sun.COM 		errno = EINVAL;
13189663SMark.Logan@Sun.COM 		return NULL;
13199663SMark.Logan@Sun.COM 	}
13209663SMark.Logan@Sun.COM 
13219663SMark.Logan@Sun.COM 	while (items > 0) {
13229663SMark.Logan@Sun.COM 
13239663SMark.Logan@Sun.COM 		if (runs)
13249663SMark.Logan@Sun.COM 			hint = 0;
13259663SMark.Logan@Sun.COM 		rle.length = items;
13269663SMark.Logan@Sun.COM 		if (find_free_cluster(bm, &rle, nr_vol_clusters, hint) == -1)
13279663SMark.Logan@Sun.COM 			return NULL;
13289663SMark.Logan@Sun.COM 
13299663SMark.Logan@Sun.COM 		rl_size = (runs + 2) * sizeof(runlist_element);
13309663SMark.Logan@Sun.COM 		if (!(rl = (runlist *)realloc(rl, rl_size)))
13319663SMark.Logan@Sun.COM 			return NULL;
13329663SMark.Logan@Sun.COM 
13339663SMark.Logan@Sun.COM 		rl_set(rl + runs, vcn, rle.lcn, rle.length);
13349663SMark.Logan@Sun.COM 
13359663SMark.Logan@Sun.COM 		vcn += rle.length;
13369663SMark.Logan@Sun.COM 		items -= rle.length;
13379663SMark.Logan@Sun.COM 		runs++;
13389663SMark.Logan@Sun.COM 	}
13399663SMark.Logan@Sun.COM 
13409663SMark.Logan@Sun.COM 	rl_set(rl + runs, vcn, -1LL, 0LL);
13419663SMark.Logan@Sun.COM 
13429663SMark.Logan@Sun.COM 	if (runs > 1) {
13439663SMark.Logan@Sun.COM 		ntfs_log_verbose("Multi-run allocation:    \n");
13449663SMark.Logan@Sun.COM 		dump_runlist(rl);
13459663SMark.Logan@Sun.COM 	}
13469663SMark.Logan@Sun.COM 	return rl;
13479663SMark.Logan@Sun.COM }
13489663SMark.Logan@Sun.COM 
read_all(struct ntfs_device * dev,void * buf,int count)13499663SMark.Logan@Sun.COM static int read_all(struct ntfs_device *dev, void *buf, int count)
13509663SMark.Logan@Sun.COM {
13519663SMark.Logan@Sun.COM 	int i;
13529663SMark.Logan@Sun.COM 
13539663SMark.Logan@Sun.COM 	while (count > 0) {
13549663SMark.Logan@Sun.COM 
13559663SMark.Logan@Sun.COM 		i = count;
13569663SMark.Logan@Sun.COM 		if (!NDevReadOnly(dev))
13579663SMark.Logan@Sun.COM 			i = dev->d_ops->read(dev, buf, count);
13589663SMark.Logan@Sun.COM 
13599663SMark.Logan@Sun.COM 		if (i < 0) {
13609663SMark.Logan@Sun.COM 			if (errno != EAGAIN && errno != EINTR)
13619663SMark.Logan@Sun.COM 				return -1;
13629663SMark.Logan@Sun.COM 		} else if (i > 0) {
13639663SMark.Logan@Sun.COM 			count -= i;
13649663SMark.Logan@Sun.COM 			buf = i + (char *)buf;
13659663SMark.Logan@Sun.COM 		} else
13669663SMark.Logan@Sun.COM 			err_exit("Unexpected end of file!\n");
13679663SMark.Logan@Sun.COM 	}
13689663SMark.Logan@Sun.COM 	return 0;
13699663SMark.Logan@Sun.COM }
13709663SMark.Logan@Sun.COM 
write_all(struct ntfs_device * dev,void * buf,int count)13719663SMark.Logan@Sun.COM static int write_all(struct ntfs_device *dev, void *buf, int count)
13729663SMark.Logan@Sun.COM {
13739663SMark.Logan@Sun.COM 	int i;
13749663SMark.Logan@Sun.COM 
13759663SMark.Logan@Sun.COM 	while (count > 0) {
13769663SMark.Logan@Sun.COM 
13779663SMark.Logan@Sun.COM 		i = count;
13789663SMark.Logan@Sun.COM 		if (!NDevReadOnly(dev))
13799663SMark.Logan@Sun.COM 			i = dev->d_ops->write(dev, buf, count);
13809663SMark.Logan@Sun.COM 
13819663SMark.Logan@Sun.COM 		if (i < 0) {
13829663SMark.Logan@Sun.COM 			if (errno != EAGAIN && errno != EINTR)
13839663SMark.Logan@Sun.COM 				return -1;
13849663SMark.Logan@Sun.COM 		} else {
13859663SMark.Logan@Sun.COM 			count -= i;
13869663SMark.Logan@Sun.COM 			buf = i + (char *)buf;
13879663SMark.Logan@Sun.COM 		}
13889663SMark.Logan@Sun.COM 	}
13899663SMark.Logan@Sun.COM 	return 0;
13909663SMark.Logan@Sun.COM }
13919663SMark.Logan@Sun.COM 
13929663SMark.Logan@Sun.COM /**
13939663SMark.Logan@Sun.COM  * write_mft_record
13949663SMark.Logan@Sun.COM  *
13959663SMark.Logan@Sun.COM  * Write an MFT Record back to the disk.  If the read-only command line option
13969663SMark.Logan@Sun.COM  * was given, this function will do nothing.
13979663SMark.Logan@Sun.COM  */
write_mft_record(ntfs_volume * v,const MFT_REF mref,MFT_RECORD * buf)13989663SMark.Logan@Sun.COM static int write_mft_record(ntfs_volume *v, const MFT_REF mref, MFT_RECORD *buf)
13999663SMark.Logan@Sun.COM {
14009663SMark.Logan@Sun.COM 	if (ntfs_mft_record_write(v, mref, buf))
14019663SMark.Logan@Sun.COM 		perr_exit("ntfs_mft_record_write");
14029663SMark.Logan@Sun.COM 
14039663SMark.Logan@Sun.COM //	if (v->u.dev->d_ops->sync(v->u.dev) == -1)
14049663SMark.Logan@Sun.COM //		perr_exit("Failed to sync device");
14059663SMark.Logan@Sun.COM 
14069663SMark.Logan@Sun.COM 	return 0;
14079663SMark.Logan@Sun.COM }
14089663SMark.Logan@Sun.COM 
lseek_to_cluster(ntfs_volume * vol,s64 lcn)14099663SMark.Logan@Sun.COM static void lseek_to_cluster(ntfs_volume *vol, s64 lcn)
14109663SMark.Logan@Sun.COM {
14119663SMark.Logan@Sun.COM 	off_t pos;
14129663SMark.Logan@Sun.COM 	pos = (off_t)(lcn * vol->cluster_size);
14139663SMark.Logan@Sun.COM 	if (vol->u.dev->d_ops->seek(vol->u.dev, pos, SEEK_SET) == (off_t)-1)
14149663SMark.Logan@Sun.COM 		perr_exit("seek failed to position %lld", lcn);
14159663SMark.Logan@Sun.COM }
14169663SMark.Logan@Sun.COM 
copy_clusters(ntfs_resize_t * resize,s64 dest,s64 src,s64 len)14179663SMark.Logan@Sun.COM static void copy_clusters(ntfs_resize_t *resize, s64 dest, s64 src, s64 len)
14189663SMark.Logan@Sun.COM {
14199663SMark.Logan@Sun.COM 	s64 i;
14209663SMark.Logan@Sun.COM 	char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */
14219663SMark.Logan@Sun.COM 	ntfs_volume *vol = resize->vol;
14229663SMark.Logan@Sun.COM 
14239663SMark.Logan@Sun.COM 	for (i = 0; i < len; i++) {
14249663SMark.Logan@Sun.COM 
14259663SMark.Logan@Sun.COM 		lseek_to_cluster(vol, src + i);
14269663SMark.Logan@Sun.COM 
14279663SMark.Logan@Sun.COM 		if (read_all(vol->u.dev, buff, vol->cluster_size) == -1) {
14289663SMark.Logan@Sun.COM 			perr_printf("Failed to read from the disk");
14299663SMark.Logan@Sun.COM 			if (errno == EIO)
14309663SMark.Logan@Sun.COM 				printf("%s", bad_sectors_warning_msg);
14319663SMark.Logan@Sun.COM 			exit(1);
14329663SMark.Logan@Sun.COM 		}
14339663SMark.Logan@Sun.COM 
14349663SMark.Logan@Sun.COM 		lseek_to_cluster(vol, dest + i);
14359663SMark.Logan@Sun.COM 
14369663SMark.Logan@Sun.COM 		if (write_all(vol->u.dev, buff, vol->cluster_size) == -1) {
14379663SMark.Logan@Sun.COM 			perr_printf("Failed to write to the disk");
14389663SMark.Logan@Sun.COM 			if (errno == EIO)
14399663SMark.Logan@Sun.COM 				printf("%s", bad_sectors_warning_msg);
14409663SMark.Logan@Sun.COM 			exit(1);
14419663SMark.Logan@Sun.COM 		}
14429663SMark.Logan@Sun.COM 
14439663SMark.Logan@Sun.COM 		resize->relocations++;
14449663SMark.Logan@Sun.COM 		progress_update(&resize->progress, resize->relocations);
14459663SMark.Logan@Sun.COM 	}
14469663SMark.Logan@Sun.COM }
14479663SMark.Logan@Sun.COM 
relocate_clusters(ntfs_resize_t * r,runlist * dest_rl,s64 src_lcn)14489663SMark.Logan@Sun.COM static void relocate_clusters(ntfs_resize_t *r, runlist *dest_rl, s64 src_lcn)
14499663SMark.Logan@Sun.COM {
14509663SMark.Logan@Sun.COM 	/* collect_shrink_constraints() ensured $MFTMir DATA is one run */
14519663SMark.Logan@Sun.COM 	if (r->mref == FILE_MFTMirr && r->ctx->attr->type == AT_DATA) {
14529663SMark.Logan@Sun.COM 		if (!r->mftmir_old) {
14539663SMark.Logan@Sun.COM 			r->mftmir_rl.lcn = dest_rl->lcn;
14549663SMark.Logan@Sun.COM 			r->mftmir_rl.length = dest_rl->length;
14559663SMark.Logan@Sun.COM 			r->mftmir_old = src_lcn;
14569663SMark.Logan@Sun.COM 		} else
14579663SMark.Logan@Sun.COM 			err_exit("Multi-run $MFTMirr. Please report!\n");
14589663SMark.Logan@Sun.COM 	}
14599663SMark.Logan@Sun.COM 
14609663SMark.Logan@Sun.COM 	for (; dest_rl->length; src_lcn += dest_rl->length, dest_rl++)
14619663SMark.Logan@Sun.COM 		copy_clusters(r, dest_rl->lcn, src_lcn, dest_rl->length);
14629663SMark.Logan@Sun.COM }
14639663SMark.Logan@Sun.COM 
rl_split_run(runlist ** rl,int run,s64 pos)14649663SMark.Logan@Sun.COM static void rl_split_run(runlist **rl, int run, s64 pos)
14659663SMark.Logan@Sun.COM {
14669663SMark.Logan@Sun.COM 	runlist *rl_new, *rle_new, *rle;
14679663SMark.Logan@Sun.COM 	int items, new_size, size_head, size_tail;
14689663SMark.Logan@Sun.COM 	s64 len_head, len_tail;
14699663SMark.Logan@Sun.COM 
14709663SMark.Logan@Sun.COM 	items = rl_items(*rl);
14719663SMark.Logan@Sun.COM 	new_size = (items + 1) * sizeof(runlist_element);
14729663SMark.Logan@Sun.COM 	size_head = run * sizeof(runlist_element);
14739663SMark.Logan@Sun.COM 	size_tail = (items - run - 1) * sizeof(runlist_element);
14749663SMark.Logan@Sun.COM 
14759663SMark.Logan@Sun.COM 	rl_new = ntfs_malloc(new_size);
14769663SMark.Logan@Sun.COM 	if (!rl_new)
14779663SMark.Logan@Sun.COM 		perr_exit("ntfs_malloc");
14789663SMark.Logan@Sun.COM 
14799663SMark.Logan@Sun.COM 	rle_new = rl_new + run;
14809663SMark.Logan@Sun.COM 	rle = *rl + run;
14819663SMark.Logan@Sun.COM 
14829663SMark.Logan@Sun.COM 	memmove(rl_new, *rl, size_head);
14839663SMark.Logan@Sun.COM 	memmove(rle_new + 2, rle + 1, size_tail);
14849663SMark.Logan@Sun.COM 
14859663SMark.Logan@Sun.COM 	len_tail = rle->length - (pos - rle->lcn);
14869663SMark.Logan@Sun.COM 	len_head = rle->length - len_tail;
14879663SMark.Logan@Sun.COM 
14889663SMark.Logan@Sun.COM 	rl_set(rle_new, rle->vcn, rle->lcn, len_head);
14899663SMark.Logan@Sun.COM 	rl_set(rle_new + 1, rle->vcn + len_head, rle->lcn + len_head, len_tail);
14909663SMark.Logan@Sun.COM 
14919663SMark.Logan@Sun.COM 	ntfs_log_verbose("Splitting run at cluster %lld:\n", (long long)pos);
14929663SMark.Logan@Sun.COM 	dump_run(rle); dump_run(rle_new); dump_run(rle_new + 1);
14939663SMark.Logan@Sun.COM 
14949663SMark.Logan@Sun.COM 	free(*rl);
14959663SMark.Logan@Sun.COM 	*rl = rl_new;
14969663SMark.Logan@Sun.COM }
14979663SMark.Logan@Sun.COM 
rl_insert_at_run(runlist ** rl,int run,runlist * ins)14989663SMark.Logan@Sun.COM static void rl_insert_at_run(runlist **rl, int run, runlist *ins)
14999663SMark.Logan@Sun.COM {
15009663SMark.Logan@Sun.COM 	int items, ins_items;
15019663SMark.Logan@Sun.COM 	int new_size, size_tail;
15029663SMark.Logan@Sun.COM 	runlist *rle;
15039663SMark.Logan@Sun.COM 	s64 vcn;
15049663SMark.Logan@Sun.COM 
15059663SMark.Logan@Sun.COM 	items  = rl_items(*rl);
15069663SMark.Logan@Sun.COM 	ins_items = rl_items(ins) - 1;
15079663SMark.Logan@Sun.COM 	new_size = ((items - 1) + ins_items) * sizeof(runlist_element);
15089663SMark.Logan@Sun.COM 	size_tail = (items - run - 1) * sizeof(runlist_element);
15099663SMark.Logan@Sun.COM 
15109663SMark.Logan@Sun.COM 	if (!(*rl = (runlist *)realloc(*rl, new_size)))
15119663SMark.Logan@Sun.COM 		perr_exit("realloc");
15129663SMark.Logan@Sun.COM 
15139663SMark.Logan@Sun.COM 	rle = *rl + run;
15149663SMark.Logan@Sun.COM 
15159663SMark.Logan@Sun.COM 	memmove(rle + ins_items, rle + 1, size_tail);
15169663SMark.Logan@Sun.COM 
15179663SMark.Logan@Sun.COM 	for (vcn = rle->vcn; ins->length; rle++, vcn += ins->length, ins++) {
15189663SMark.Logan@Sun.COM 		rl_set(rle, vcn, ins->lcn, ins->length);
15199663SMark.Logan@Sun.COM //		dump_run(rle);
15209663SMark.Logan@Sun.COM 	}
15219663SMark.Logan@Sun.COM 
15229663SMark.Logan@Sun.COM 	return;
15239663SMark.Logan@Sun.COM 
15249663SMark.Logan@Sun.COM 	/* FIXME: fast path if ins_items = 1 */
15259663SMark.Logan@Sun.COM //	(*rl + run)->lcn = ins->lcn;
15269663SMark.Logan@Sun.COM }
15279663SMark.Logan@Sun.COM 
relocate_run(ntfs_resize_t * resize,runlist ** rl,int run)15289663SMark.Logan@Sun.COM static void relocate_run(ntfs_resize_t *resize, runlist **rl, int run)
15299663SMark.Logan@Sun.COM {
15309663SMark.Logan@Sun.COM 	s64 lcn, lcn_length;
15319663SMark.Logan@Sun.COM 	s64 new_vol_size;	/* (last LCN on the volume) + 1 */
15329663SMark.Logan@Sun.COM 	runlist *relocate_rl;	/* relocate runlist to relocate_rl */
15339663SMark.Logan@Sun.COM 	int hint;
15349663SMark.Logan@Sun.COM 
15359663SMark.Logan@Sun.COM 	lcn = (*rl + run)->lcn;
15369663SMark.Logan@Sun.COM 	lcn_length = (*rl + run)->length;
15379663SMark.Logan@Sun.COM 	new_vol_size = resize->new_volume_size;
15389663SMark.Logan@Sun.COM 
15399663SMark.Logan@Sun.COM 	if (lcn + lcn_length <= new_vol_size)
15409663SMark.Logan@Sun.COM 		return;
15419663SMark.Logan@Sun.COM 
15429663SMark.Logan@Sun.COM 	if (lcn < new_vol_size) {
15439663SMark.Logan@Sun.COM 		rl_split_run(rl, run, new_vol_size);
15449663SMark.Logan@Sun.COM 		return;
15459663SMark.Logan@Sun.COM 	}
15469663SMark.Logan@Sun.COM 
15479663SMark.Logan@Sun.COM 	hint = (resize->mref == FILE_MFTMirr) ? 1 : 0;
15489663SMark.Logan@Sun.COM 	if (!(relocate_rl = alloc_cluster(&resize->lcn_bitmap, lcn_length,
15499663SMark.Logan@Sun.COM 					  new_vol_size, hint)))
15509663SMark.Logan@Sun.COM 		perr_exit("Cluster allocation failed for %llu:%lld",
15519663SMark.Logan@Sun.COM 			  resize->mref, lcn_length);
15529663SMark.Logan@Sun.COM 
15539663SMark.Logan@Sun.COM 	/* FIXME: check $MFTMirr DATA isn't multi-run (or support it) */
15549663SMark.Logan@Sun.COM 	ntfs_log_verbose("Relocate record %7llu:0x%x:%08lld:0x%08llx:0x%08llx "
15559663SMark.Logan@Sun.COM 			 "--> 0x%08llx\n", (unsigned long long)resize->mref,
15569663SMark.Logan@Sun.COM 			 (unsigned int)le32_to_cpu(resize->ctx->attr->type),
15579663SMark.Logan@Sun.COM 			 (long long)lcn_length,
15589663SMark.Logan@Sun.COM 			 (unsigned long long)(*rl + run)->vcn,
15599663SMark.Logan@Sun.COM 			 (unsigned long long)lcn,
15609663SMark.Logan@Sun.COM 			 (unsigned long long)relocate_rl->lcn);
15619663SMark.Logan@Sun.COM 
15629663SMark.Logan@Sun.COM 	relocate_clusters(resize, relocate_rl, lcn);
15639663SMark.Logan@Sun.COM 	rl_insert_at_run(rl, run, relocate_rl);
15649663SMark.Logan@Sun.COM 
15659663SMark.Logan@Sun.COM 	/* We don't release old clusters in the bitmap, that area isn't
15669663SMark.Logan@Sun.COM 	   used by the allocator and will be truncated later on */
15679663SMark.Logan@Sun.COM 	free(relocate_rl);
15689663SMark.Logan@Sun.COM 
15699663SMark.Logan@Sun.COM 	resize->dirty_inode = DIRTY_ATTRIB;
15709663SMark.Logan@Sun.COM }
15719663SMark.Logan@Sun.COM 
relocate_attribute(ntfs_resize_t * resize)15729663SMark.Logan@Sun.COM static void relocate_attribute(ntfs_resize_t *resize)
15739663SMark.Logan@Sun.COM {
15749663SMark.Logan@Sun.COM 	ATTR_RECORD *a;
15759663SMark.Logan@Sun.COM 	runlist *rl;
15769663SMark.Logan@Sun.COM 	int i;
15779663SMark.Logan@Sun.COM 
15789663SMark.Logan@Sun.COM 	a = resize->ctx->attr;
15799663SMark.Logan@Sun.COM 
15809663SMark.Logan@Sun.COM 	if (!a->non_resident)
15819663SMark.Logan@Sun.COM 		return;
15829663SMark.Logan@Sun.COM 
15839663SMark.Logan@Sun.COM 	if (!(rl = ntfs_mapping_pairs_decompress(resize->vol, a, NULL)))
15849663SMark.Logan@Sun.COM 		perr_exit("ntfs_decompress_mapping_pairs");
15859663SMark.Logan@Sun.COM 
15869663SMark.Logan@Sun.COM 	for (i = 0; rl[i].length; i++) {
15879663SMark.Logan@Sun.COM 		s64 lcn = rl[i].lcn;
15889663SMark.Logan@Sun.COM 		s64 lcn_length = rl[i].length;
15899663SMark.Logan@Sun.COM 
15909663SMark.Logan@Sun.COM 		if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED)
15919663SMark.Logan@Sun.COM 			continue;
15929663SMark.Logan@Sun.COM 
15939663SMark.Logan@Sun.COM 		/* FIXME: ntfs_mapping_pairs_decompress should return error */
15949663SMark.Logan@Sun.COM 		if (lcn < 0 || lcn_length <= 0)
15959663SMark.Logan@Sun.COM 			err_exit("Corrupt runlist in MTF %llu attr %x LCN "
15969663SMark.Logan@Sun.COM 				 "%llx length %llx\n", resize->mref,
15979663SMark.Logan@Sun.COM 				 (unsigned int)le32_to_cpu(a->type),
15989663SMark.Logan@Sun.COM 				 lcn, lcn_length);
15999663SMark.Logan@Sun.COM 
16009663SMark.Logan@Sun.COM 		relocate_run(resize, &rl, i);
16019663SMark.Logan@Sun.COM 	}
16029663SMark.Logan@Sun.COM 
16039663SMark.Logan@Sun.COM 	if (resize->dirty_inode == DIRTY_ATTRIB) {
16049663SMark.Logan@Sun.COM 		replace_attribute_runlist(resize->vol, resize->ctx, rl);
16059663SMark.Logan@Sun.COM 		resize->dirty_inode = DIRTY_INODE;
16069663SMark.Logan@Sun.COM 	}
16079663SMark.Logan@Sun.COM 
16089663SMark.Logan@Sun.COM 	free(rl);
16099663SMark.Logan@Sun.COM }
16109663SMark.Logan@Sun.COM 
is_mftdata(ntfs_resize_t * resize)16119663SMark.Logan@Sun.COM static int is_mftdata(ntfs_resize_t *resize)
16129663SMark.Logan@Sun.COM {
16139663SMark.Logan@Sun.COM 	if (resize->ctx->attr->type != AT_DATA)
16149663SMark.Logan@Sun.COM 		return 0;
16159663SMark.Logan@Sun.COM 
16169663SMark.Logan@Sun.COM 	if (resize->mref == 0)
16179663SMark.Logan@Sun.COM 		return 1;
16189663SMark.Logan@Sun.COM 
16199663SMark.Logan@Sun.COM 	if (MREF_LE(resize->mrec->base_mft_record) == 0 &&
16209663SMark.Logan@Sun.COM 	    MSEQNO_LE(resize->mrec->base_mft_record) != 0)
16219663SMark.Logan@Sun.COM 		return 1;
16229663SMark.Logan@Sun.COM 
16239663SMark.Logan@Sun.COM 	return 0;
16249663SMark.Logan@Sun.COM }
16259663SMark.Logan@Sun.COM 
handle_mftdata(ntfs_resize_t * resize,int do_mftdata)16269663SMark.Logan@Sun.COM static int handle_mftdata(ntfs_resize_t *resize, int do_mftdata)
16279663SMark.Logan@Sun.COM {
16289663SMark.Logan@Sun.COM 	ATTR_RECORD *attr = resize->ctx->attr;
16299663SMark.Logan@Sun.COM 	VCN highest_vcn, lowest_vcn;
16309663SMark.Logan@Sun.COM 
16319663SMark.Logan@Sun.COM 	if (do_mftdata) {
16329663SMark.Logan@Sun.COM 
16339663SMark.Logan@Sun.COM 		if (!is_mftdata(resize))
16349663SMark.Logan@Sun.COM 			return 0;
16359663SMark.Logan@Sun.COM 
16369663SMark.Logan@Sun.COM 		highest_vcn = sle64_to_cpu(attr->u.nonres.highest_vcn);
16379663SMark.Logan@Sun.COM 		lowest_vcn  = sle64_to_cpu(attr->u.nonres.lowest_vcn);
16389663SMark.Logan@Sun.COM 
16399663SMark.Logan@Sun.COM 		if (resize->mft_highest_vcn != highest_vcn)
16409663SMark.Logan@Sun.COM 			return 0;
16419663SMark.Logan@Sun.COM 
16429663SMark.Logan@Sun.COM 		if (lowest_vcn == 0)
16439663SMark.Logan@Sun.COM 			resize->mft_highest_vcn = lowest_vcn;
16449663SMark.Logan@Sun.COM 		else
16459663SMark.Logan@Sun.COM 			resize->mft_highest_vcn = lowest_vcn - 1;
16469663SMark.Logan@Sun.COM 
16479663SMark.Logan@Sun.COM 	} else if (is_mftdata(resize)) {
16489663SMark.Logan@Sun.COM 
16499663SMark.Logan@Sun.COM 		highest_vcn = sle64_to_cpu(attr->u.nonres.highest_vcn);
16509663SMark.Logan@Sun.COM 
16519663SMark.Logan@Sun.COM 		if (resize->mft_highest_vcn < highest_vcn)
16529663SMark.Logan@Sun.COM 			resize->mft_highest_vcn = highest_vcn;
16539663SMark.Logan@Sun.COM 
16549663SMark.Logan@Sun.COM 		return 0;
16559663SMark.Logan@Sun.COM 	}
16569663SMark.Logan@Sun.COM 
16579663SMark.Logan@Sun.COM 	return 1;
16589663SMark.Logan@Sun.COM }
16599663SMark.Logan@Sun.COM 
relocate_attributes(ntfs_resize_t * resize,int do_mftdata)16609663SMark.Logan@Sun.COM static void relocate_attributes(ntfs_resize_t *resize, int do_mftdata)
16619663SMark.Logan@Sun.COM {
16629663SMark.Logan@Sun.COM 	int ret;
16639663SMark.Logan@Sun.COM 
16649663SMark.Logan@Sun.COM 	if (!(resize->ctx = attr_get_search_ctx(NULL, resize->mrec)))
16659663SMark.Logan@Sun.COM 		exit(1);
16669663SMark.Logan@Sun.COM 
16679663SMark.Logan@Sun.COM 	while (!ntfs_attrs_walk(resize->ctx)) {
16689663SMark.Logan@Sun.COM 		if (resize->ctx->attr->type == AT_END)
16699663SMark.Logan@Sun.COM 			break;
16709663SMark.Logan@Sun.COM 
16719663SMark.Logan@Sun.COM 		if (handle_mftdata(resize, do_mftdata) == 0)
16729663SMark.Logan@Sun.COM 			continue;
16739663SMark.Logan@Sun.COM 
16749663SMark.Logan@Sun.COM 		ret = ntfs_inode_badclus_bad(resize->mref, resize->ctx->attr);
16759663SMark.Logan@Sun.COM 		if (ret == -1)
16769663SMark.Logan@Sun.COM 			perr_exit("Bad sector list check failed");
16779663SMark.Logan@Sun.COM 		else if (ret == 1)
16789663SMark.Logan@Sun.COM 			continue;
16799663SMark.Logan@Sun.COM 
16809663SMark.Logan@Sun.COM 		if (resize->mref == FILE_Bitmap &&
16819663SMark.Logan@Sun.COM 		    resize->ctx->attr->type == AT_DATA)
16829663SMark.Logan@Sun.COM 			continue;
16839663SMark.Logan@Sun.COM 
16849663SMark.Logan@Sun.COM 		relocate_attribute(resize);
16859663SMark.Logan@Sun.COM 	}
16869663SMark.Logan@Sun.COM 
16879663SMark.Logan@Sun.COM 	ntfs_attr_put_search_ctx(resize->ctx);
16889663SMark.Logan@Sun.COM }
16899663SMark.Logan@Sun.COM 
relocate_inode(ntfs_resize_t * resize,MFT_REF mref,int do_mftdata)16909663SMark.Logan@Sun.COM static void relocate_inode(ntfs_resize_t *resize, MFT_REF mref, int do_mftdata)
16919663SMark.Logan@Sun.COM {
16929663SMark.Logan@Sun.COM 	if (ntfs_file_record_read(resize->vol, mref, &resize->mrec, NULL)) {
16939663SMark.Logan@Sun.COM 		/* FIXME: continue only if it make sense, e.g.
16949663SMark.Logan@Sun.COM 		   MFT record not in use based on $MFT bitmap */
16959663SMark.Logan@Sun.COM 		if (errno == EIO || errno == ENOENT)
16969663SMark.Logan@Sun.COM 			return;
16979663SMark.Logan@Sun.COM 		perr_exit("ntfs_file_record_record");
16989663SMark.Logan@Sun.COM 	}
16999663SMark.Logan@Sun.COM 
17009663SMark.Logan@Sun.COM 	if (!(resize->mrec->flags & MFT_RECORD_IN_USE))
17019663SMark.Logan@Sun.COM 		return;
17029663SMark.Logan@Sun.COM 
17039663SMark.Logan@Sun.COM 	resize->mref = mref;
17049663SMark.Logan@Sun.COM 	resize->dirty_inode = DIRTY_NONE;
17059663SMark.Logan@Sun.COM 
17069663SMark.Logan@Sun.COM 	relocate_attributes(resize, do_mftdata);
17079663SMark.Logan@Sun.COM 
17089663SMark.Logan@Sun.COM 	if (resize->dirty_inode == DIRTY_INODE) {
17099663SMark.Logan@Sun.COM //		if (vol->u.dev->d_ops->sync(vol->u.dev) == -1)
17109663SMark.Logan@Sun.COM //			perr_exit("Failed to sync device");
17119663SMark.Logan@Sun.COM 		if (write_mft_record(resize->vol, mref, resize->mrec))
17129663SMark.Logan@Sun.COM 			perr_exit("Couldn't update record %llu", mref);
17139663SMark.Logan@Sun.COM 	}
17149663SMark.Logan@Sun.COM }
17159663SMark.Logan@Sun.COM 
relocate_inodes(ntfs_resize_t * resize)17169663SMark.Logan@Sun.COM static void relocate_inodes(ntfs_resize_t *resize)
17179663SMark.Logan@Sun.COM {
17189663SMark.Logan@Sun.COM 	s64 nr_mft_records;
17199663SMark.Logan@Sun.COM 	MFT_REF mref;
17209663SMark.Logan@Sun.COM 	VCN highest_vcn;
17219663SMark.Logan@Sun.COM 
17229663SMark.Logan@Sun.COM 	printf("Relocating needed data ...\n");
17239663SMark.Logan@Sun.COM 
17249663SMark.Logan@Sun.COM 	progress_init(&resize->progress, 0, resize->relocations, resize->progress.flags);
17259663SMark.Logan@Sun.COM 	resize->relocations = 0;
17269663SMark.Logan@Sun.COM 
17279663SMark.Logan@Sun.COM 	resize->mrec = ntfs_malloc(resize->vol->mft_record_size);
17289663SMark.Logan@Sun.COM 	if (!resize->mrec)
17299663SMark.Logan@Sun.COM 		perr_exit("ntfs_malloc failed");
17309663SMark.Logan@Sun.COM 
17319663SMark.Logan@Sun.COM 	nr_mft_records = resize->vol->mft_na->initialized_size >>
17329663SMark.Logan@Sun.COM 			resize->vol->mft_record_size_bits;
17339663SMark.Logan@Sun.COM 
17349663SMark.Logan@Sun.COM 	for (mref = 0; mref < (MFT_REF)nr_mft_records; mref++)
17359663SMark.Logan@Sun.COM 		relocate_inode(resize, mref, 0);
17369663SMark.Logan@Sun.COM 
17379663SMark.Logan@Sun.COM 	while (1) {
17389663SMark.Logan@Sun.COM 		highest_vcn = resize->mft_highest_vcn;
17399663SMark.Logan@Sun.COM 		mref = nr_mft_records;
17409663SMark.Logan@Sun.COM 		do {
17419663SMark.Logan@Sun.COM 			relocate_inode(resize, --mref, 1);
17429663SMark.Logan@Sun.COM 			if (resize->mft_highest_vcn == 0)
17439663SMark.Logan@Sun.COM 				goto done;
17449663SMark.Logan@Sun.COM 		} while (mref);
17459663SMark.Logan@Sun.COM 
17469663SMark.Logan@Sun.COM 		if (highest_vcn == resize->mft_highest_vcn)
17479663SMark.Logan@Sun.COM 			err_exit("Sanity check failed! Highest_vcn = %lld. "
17489663SMark.Logan@Sun.COM 				 "Please report!\n", highest_vcn);
17499663SMark.Logan@Sun.COM 	}
17509663SMark.Logan@Sun.COM done:
17519663SMark.Logan@Sun.COM 	free(resize->mrec);
17529663SMark.Logan@Sun.COM }
17539663SMark.Logan@Sun.COM 
print_hint(ntfs_volume * vol,const char * s,struct llcn_t llcn)17549663SMark.Logan@Sun.COM static void print_hint(ntfs_volume *vol, const char *s, struct llcn_t llcn)
17559663SMark.Logan@Sun.COM {
17569663SMark.Logan@Sun.COM 	s64 runs_b, runs_mb;
17579663SMark.Logan@Sun.COM 
17589663SMark.Logan@Sun.COM 	if (llcn.lcn == 0)
17599663SMark.Logan@Sun.COM 		return;
17609663SMark.Logan@Sun.COM 
17619663SMark.Logan@Sun.COM 	runs_b = llcn.lcn * vol->cluster_size;
17629663SMark.Logan@Sun.COM 	runs_mb = rounded_up_division(runs_b, NTFS_MBYTE);
17639663SMark.Logan@Sun.COM 	printf("%-19s: %9lld MB      %8lld\n", s, (long long)runs_mb,
17649663SMark.Logan@Sun.COM 			(long long)llcn.inode);
17659663SMark.Logan@Sun.COM }
17669663SMark.Logan@Sun.COM 
17679663SMark.Logan@Sun.COM /**
17689663SMark.Logan@Sun.COM  * advise_on_resize
17699663SMark.Logan@Sun.COM  *
17709663SMark.Logan@Sun.COM  * The metadata file $Bitmap has one bit for each cluster on disk.  This has
17719663SMark.Logan@Sun.COM  * already been read into lcn_bitmap.  By looking for the last used cluster on
17729663SMark.Logan@Sun.COM  * the disk, we can work out by how much we can shrink the volume.
17739663SMark.Logan@Sun.COM  */
advise_on_resize(ntfs_resize_t * resize)17749663SMark.Logan@Sun.COM static void advise_on_resize(ntfs_resize_t *resize)
17759663SMark.Logan@Sun.COM {
17769663SMark.Logan@Sun.COM 	ntfs_volume *vol = resize->vol;
17779663SMark.Logan@Sun.COM 
17789663SMark.Logan@Sun.COM 	if (opt.verbose) {
17799663SMark.Logan@Sun.COM 		printf("Estimating smallest shrunken size supported ...\n");
17809663SMark.Logan@Sun.COM 		printf("File feature         Last used at      By inode\n");
17819663SMark.Logan@Sun.COM 		print_hint(vol, "$MFT",		resize->last_mft);
17829663SMark.Logan@Sun.COM 		print_hint(vol, "Multi-Record", resize->last_multi_mft);
17839663SMark.Logan@Sun.COM 		print_hint(vol, "$MFTMirr",	resize->last_mftmir);
17849663SMark.Logan@Sun.COM 		print_hint(vol, "Compressed",	resize->last_compressed);
17859663SMark.Logan@Sun.COM 		print_hint(vol, "Sparse",	resize->last_sparse);
17869663SMark.Logan@Sun.COM 		print_hint(vol, "Ordinary",	resize->last_lcn);
17879663SMark.Logan@Sun.COM 	}
17889663SMark.Logan@Sun.COM 
17899663SMark.Logan@Sun.COM 	print_advise(vol, resize->last_unsupp);
17909663SMark.Logan@Sun.COM }
17919663SMark.Logan@Sun.COM 
17929663SMark.Logan@Sun.COM 
rl_expand(runlist ** rl,const VCN last_vcn)17939663SMark.Logan@Sun.COM static void rl_expand(runlist **rl, const VCN last_vcn)
17949663SMark.Logan@Sun.COM {
17959663SMark.Logan@Sun.COM 	int len;
17969663SMark.Logan@Sun.COM 	runlist *p = *rl;
17979663SMark.Logan@Sun.COM 
17989663SMark.Logan@Sun.COM 	len = rl_items(p) - 1;
17999663SMark.Logan@Sun.COM 	if (len <= 0)
18009663SMark.Logan@Sun.COM 		err_exit("rl_expand: bad runlist length: %d\n", len);
18019663SMark.Logan@Sun.COM 
18029663SMark.Logan@Sun.COM 	if (p[len].vcn > last_vcn)
18039663SMark.Logan@Sun.COM 		err_exit("rl_expand: length is already more than requested "
18049663SMark.Logan@Sun.COM 			 "(%lld > %lld)\n", p[len].vcn, last_vcn);
18059663SMark.Logan@Sun.COM 
18069663SMark.Logan@Sun.COM 	if (p[len - 1].lcn == LCN_HOLE) {
18079663SMark.Logan@Sun.COM 
18089663SMark.Logan@Sun.COM 		p[len - 1].length += last_vcn - p[len].vcn;
18099663SMark.Logan@Sun.COM 		p[len].vcn = last_vcn;
18109663SMark.Logan@Sun.COM 
18119663SMark.Logan@Sun.COM 	} else if (p[len - 1].lcn >= 0) {
18129663SMark.Logan@Sun.COM 
18139663SMark.Logan@Sun.COM 		p = realloc(*rl, (++len + 1) * sizeof(runlist_element));
18149663SMark.Logan@Sun.COM 		if (!p)
18159663SMark.Logan@Sun.COM 			perr_exit("rl_expand: realloc");
18169663SMark.Logan@Sun.COM 
18179663SMark.Logan@Sun.COM 		p[len - 1].lcn = LCN_HOLE;
18189663SMark.Logan@Sun.COM 		p[len - 1].length = last_vcn - p[len - 1].vcn;
18199663SMark.Logan@Sun.COM 		rl_set(p + len, last_vcn, LCN_ENOENT, 0LL);
18209663SMark.Logan@Sun.COM 		*rl = p;
18219663SMark.Logan@Sun.COM 
18229663SMark.Logan@Sun.COM 	} else
18239663SMark.Logan@Sun.COM 		err_exit("rl_expand: bad LCN: %lld\n", p[len - 1].lcn);
18249663SMark.Logan@Sun.COM }
18259663SMark.Logan@Sun.COM 
rl_truncate(runlist ** rl,const VCN last_vcn)18269663SMark.Logan@Sun.COM static void rl_truncate(runlist **rl, const VCN last_vcn)
18279663SMark.Logan@Sun.COM {
18289663SMark.Logan@Sun.COM 	int len;
18299663SMark.Logan@Sun.COM 	VCN vcn;
18309663SMark.Logan@Sun.COM 
18319663SMark.Logan@Sun.COM 	len = rl_items(*rl) - 1;
18329663SMark.Logan@Sun.COM 	if (len <= 0)
18339663SMark.Logan@Sun.COM 		err_exit("rl_truncate: bad runlist length: %d\n", len);
18349663SMark.Logan@Sun.COM 
18359663SMark.Logan@Sun.COM 	vcn = (*rl)[len].vcn;
18369663SMark.Logan@Sun.COM 
18379663SMark.Logan@Sun.COM 	if (vcn < last_vcn)
18389663SMark.Logan@Sun.COM 		rl_expand(rl, last_vcn);
18399663SMark.Logan@Sun.COM 
18409663SMark.Logan@Sun.COM 	else if (vcn > last_vcn)
18419663SMark.Logan@Sun.COM 		if (ntfs_rl_truncate(rl, last_vcn) == -1)
18429663SMark.Logan@Sun.COM 			perr_exit("ntfs_rl_truncate");
18439663SMark.Logan@Sun.COM }
18449663SMark.Logan@Sun.COM 
18459663SMark.Logan@Sun.COM /**
18469663SMark.Logan@Sun.COM  * bitmap_file_data_fixup
18479663SMark.Logan@Sun.COM  *
18489663SMark.Logan@Sun.COM  * $Bitmap can overlap the end of the volume. Any bits in this region
18499663SMark.Logan@Sun.COM  * must be set. This region also encompasses the backup boot sector.
18509663SMark.Logan@Sun.COM  */
bitmap_file_data_fixup(s64 cluster,struct bitmap * bm)18519663SMark.Logan@Sun.COM static void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm)
18529663SMark.Logan@Sun.COM {
18539663SMark.Logan@Sun.COM 	for (; cluster < bm->size << 3; cluster++)
18549663SMark.Logan@Sun.COM 		ntfs_bit_set(bm->bm, (u64)cluster, 1);
18559663SMark.Logan@Sun.COM }
18569663SMark.Logan@Sun.COM 
18579663SMark.Logan@Sun.COM /**
18589663SMark.Logan@Sun.COM  * truncate_badclust_bad_attr
18599663SMark.Logan@Sun.COM  *
18609663SMark.Logan@Sun.COM  * The metadata file $BadClus needs to be shrunk.
18619663SMark.Logan@Sun.COM  *
18629663SMark.Logan@Sun.COM  * FIXME: this function should go away and instead using a generalized
18639663SMark.Logan@Sun.COM  * "truncate_bitmap_data_attr()"
18649663SMark.Logan@Sun.COM  */
truncate_badclust_bad_attr(ntfs_resize_t * resize)18659663SMark.Logan@Sun.COM static void truncate_badclust_bad_attr(ntfs_resize_t *resize)
18669663SMark.Logan@Sun.COM {
18679663SMark.Logan@Sun.COM 	ATTR_RECORD *a;
18689663SMark.Logan@Sun.COM 	runlist *rl_bad;
18699663SMark.Logan@Sun.COM 	s64 nr_clusters = resize->new_volume_size;
18709663SMark.Logan@Sun.COM 	ntfs_volume *vol = resize->vol;
18719663SMark.Logan@Sun.COM 
18729663SMark.Logan@Sun.COM 	a = resize->ctx->attr;
18739663SMark.Logan@Sun.COM 	if (!a->non_resident)
18749663SMark.Logan@Sun.COM 		/* FIXME: handle resident attribute value */
18759663SMark.Logan@Sun.COM 		err_exit("Resident attribute in $BadClust isn't supported!\n");
18769663SMark.Logan@Sun.COM 
18779663SMark.Logan@Sun.COM 	if (!(rl_bad = ntfs_mapping_pairs_decompress(vol, a, NULL)))
18789663SMark.Logan@Sun.COM 		perr_exit("ntfs_mapping_pairs_decompress");
18799663SMark.Logan@Sun.COM 
18809663SMark.Logan@Sun.COM 	rl_truncate(&rl_bad, nr_clusters);
18819663SMark.Logan@Sun.COM 
18829663SMark.Logan@Sun.COM 	a->u.nonres.highest_vcn = cpu_to_sle64(nr_clusters - 1LL);
18839663SMark.Logan@Sun.COM 	a->u.nonres.allocated_size = cpu_to_sle64(nr_clusters * vol->cluster_size);
18849663SMark.Logan@Sun.COM 	a->u.nonres.data_size = cpu_to_sle64(nr_clusters * vol->cluster_size);
18859663SMark.Logan@Sun.COM 
18869663SMark.Logan@Sun.COM 	replace_attribute_runlist(vol, resize->ctx, rl_bad);
18879663SMark.Logan@Sun.COM 
18889663SMark.Logan@Sun.COM 	free(rl_bad);
18899663SMark.Logan@Sun.COM }
18909663SMark.Logan@Sun.COM 
18919663SMark.Logan@Sun.COM /**
18929663SMark.Logan@Sun.COM  * realloc_bitmap_data_attr
18939663SMark.Logan@Sun.COM  *
18949663SMark.Logan@Sun.COM  * Reallocate the metadata file $Bitmap.  It must be large enough for one bit
18959663SMark.Logan@Sun.COM  * per cluster of the shrunken volume.  Also it must be a of 8 bytes in size.
18969663SMark.Logan@Sun.COM  */
realloc_bitmap_data_attr(ntfs_resize_t * resize,runlist ** rl,s64 nr_bm_clusters)18979663SMark.Logan@Sun.COM static void realloc_bitmap_data_attr(ntfs_resize_t *resize,
18989663SMark.Logan@Sun.COM 				     runlist **rl,
18999663SMark.Logan@Sun.COM 				     s64 nr_bm_clusters)
19009663SMark.Logan@Sun.COM {
19019663SMark.Logan@Sun.COM 	s64 i;
19029663SMark.Logan@Sun.COM 	ntfs_volume *vol = resize->vol;
19039663SMark.Logan@Sun.COM 	ATTR_RECORD *a = resize->ctx->attr;
19049663SMark.Logan@Sun.COM 	s64 new_size = resize->new_volume_size;
19059663SMark.Logan@Sun.COM 	struct bitmap *bm = &resize->lcn_bitmap;
19069663SMark.Logan@Sun.COM 
19079663SMark.Logan@Sun.COM 	if (!(*rl = ntfs_mapping_pairs_decompress(vol, a, NULL)))
19089663SMark.Logan@Sun.COM 		perr_exit("ntfs_mapping_pairs_decompress");
19099663SMark.Logan@Sun.COM 
19109663SMark.Logan@Sun.COM 	release_bitmap_clusters(bm, *rl);
19119663SMark.Logan@Sun.COM 	free(*rl);
19129663SMark.Logan@Sun.COM 
19139663SMark.Logan@Sun.COM 	for (i = vol->nr_clusters; i < new_size; i++)
19149663SMark.Logan@Sun.COM 		ntfs_bit_set(bm->bm, i, 0);
19159663SMark.Logan@Sun.COM 
19169663SMark.Logan@Sun.COM 	if (!(*rl = alloc_cluster(bm, nr_bm_clusters, new_size, 0)))
19179663SMark.Logan@Sun.COM 		perr_exit("Couldn't allocate $Bitmap clusters");
19189663SMark.Logan@Sun.COM }
19199663SMark.Logan@Sun.COM 
realloc_lcn_bitmap(ntfs_resize_t * resize,s64 bm_bsize)19209663SMark.Logan@Sun.COM static void realloc_lcn_bitmap(ntfs_resize_t *resize, s64 bm_bsize)
19219663SMark.Logan@Sun.COM {
19229663SMark.Logan@Sun.COM 	u8 *tmp;
19239663SMark.Logan@Sun.COM 
19249663SMark.Logan@Sun.COM 	if (!(tmp = realloc(resize->lcn_bitmap.bm, bm_bsize)))
19259663SMark.Logan@Sun.COM 		perr_exit("realloc");
19269663SMark.Logan@Sun.COM 
19279663SMark.Logan@Sun.COM 	resize->lcn_bitmap.bm = tmp;
19289663SMark.Logan@Sun.COM 	resize->lcn_bitmap.size = bm_bsize;
19299663SMark.Logan@Sun.COM 	bitmap_file_data_fixup(resize->new_volume_size, &resize->lcn_bitmap);
19309663SMark.Logan@Sun.COM }
19319663SMark.Logan@Sun.COM 
19329663SMark.Logan@Sun.COM /**
19339663SMark.Logan@Sun.COM  * truncate_bitmap_data_attr
19349663SMark.Logan@Sun.COM  */
truncate_bitmap_data_attr(ntfs_resize_t * resize)19359663SMark.Logan@Sun.COM static void truncate_bitmap_data_attr(ntfs_resize_t *resize)
19369663SMark.Logan@Sun.COM {
19379663SMark.Logan@Sun.COM 	ATTR_RECORD *a;
19389663SMark.Logan@Sun.COM 	runlist *rl;
19399663SMark.Logan@Sun.COM 	s64 bm_bsize, size;
19409663SMark.Logan@Sun.COM 	s64 nr_bm_clusters;
19419663SMark.Logan@Sun.COM 	ntfs_volume *vol = resize->vol;
19429663SMark.Logan@Sun.COM 
19439663SMark.Logan@Sun.COM 	a = resize->ctx->attr;
19449663SMark.Logan@Sun.COM 	if (!a->non_resident)
19459663SMark.Logan@Sun.COM 		/* FIXME: handle resident attribute value */
19469663SMark.Logan@Sun.COM 		err_exit("Resident attribute in $Bitmap isn't supported!\n");
19479663SMark.Logan@Sun.COM 
19489663SMark.Logan@Sun.COM 	bm_bsize = nr_clusters_to_bitmap_byte_size(resize->new_volume_size);
19499663SMark.Logan@Sun.COM 	nr_bm_clusters = rounded_up_division(bm_bsize, vol->cluster_size);
19509663SMark.Logan@Sun.COM 
19519663SMark.Logan@Sun.COM 	if (resize->shrink) {
19529663SMark.Logan@Sun.COM 		realloc_bitmap_data_attr(resize, &rl, nr_bm_clusters);
19539663SMark.Logan@Sun.COM 		realloc_lcn_bitmap(resize, bm_bsize);
19549663SMark.Logan@Sun.COM 	} else {
19559663SMark.Logan@Sun.COM 		realloc_lcn_bitmap(resize, bm_bsize);
19569663SMark.Logan@Sun.COM 		realloc_bitmap_data_attr(resize, &rl, nr_bm_clusters);
19579663SMark.Logan@Sun.COM 	}
19589663SMark.Logan@Sun.COM 
19599663SMark.Logan@Sun.COM 	a->u.nonres.highest_vcn = cpu_to_sle64(nr_bm_clusters - 1LL);
19609663SMark.Logan@Sun.COM 	a->u.nonres.allocated_size = cpu_to_sle64(nr_bm_clusters * vol->cluster_size);
19619663SMark.Logan@Sun.COM 	a->u.nonres.data_size = cpu_to_sle64(bm_bsize);
19629663SMark.Logan@Sun.COM 	a->u.nonres.initialized_size = cpu_to_sle64(bm_bsize);
19639663SMark.Logan@Sun.COM 
19649663SMark.Logan@Sun.COM 	replace_attribute_runlist(vol, resize->ctx, rl);
19659663SMark.Logan@Sun.COM 
19669663SMark.Logan@Sun.COM 	/*
19679663SMark.Logan@Sun.COM 	 * FIXME: update allocated/data sizes and timestamps in $FILE_NAME
19689663SMark.Logan@Sun.COM 	 * attribute too, for now chkdsk will do this for us.
19699663SMark.Logan@Sun.COM 	 */
19709663SMark.Logan@Sun.COM 
19719663SMark.Logan@Sun.COM 	size = ntfs_rl_pwrite(vol, rl, 0, bm_bsize, resize->lcn_bitmap.bm);
19729663SMark.Logan@Sun.COM 	if (bm_bsize != size) {
19739663SMark.Logan@Sun.COM 		if (size == -1)
19749663SMark.Logan@Sun.COM 			perr_exit("Couldn't write $Bitmap");
19759663SMark.Logan@Sun.COM 		err_exit("Couldn't write full $Bitmap file (%lld from %lld)\n",
19769663SMark.Logan@Sun.COM 				(long long)size, (long long)bm_bsize);
19779663SMark.Logan@Sun.COM 	}
19789663SMark.Logan@Sun.COM 
19799663SMark.Logan@Sun.COM 	free(rl);
19809663SMark.Logan@Sun.COM }
19819663SMark.Logan@Sun.COM 
19829663SMark.Logan@Sun.COM /**
19839663SMark.Logan@Sun.COM  * lookup_data_attr
19849663SMark.Logan@Sun.COM  *
19859663SMark.Logan@Sun.COM  * Find the $DATA attribute (with or without a name) for the given MFT reference
19869663SMark.Logan@Sun.COM  * (inode number).
19879663SMark.Logan@Sun.COM  */
lookup_data_attr(ntfs_volume * vol,MFT_REF mref,const char * aname,ntfs_attr_search_ctx ** ctx)19889663SMark.Logan@Sun.COM static void lookup_data_attr(ntfs_volume *vol,
19899663SMark.Logan@Sun.COM 			     MFT_REF mref,
19909663SMark.Logan@Sun.COM 			     const char *aname,
19919663SMark.Logan@Sun.COM 			     ntfs_attr_search_ctx **ctx)
19929663SMark.Logan@Sun.COM {
19939663SMark.Logan@Sun.COM 	ntfs_inode *ni;
19949663SMark.Logan@Sun.COM 	ntfschar *ustr;
19959663SMark.Logan@Sun.COM 	int len = 0;
19969663SMark.Logan@Sun.COM 
19979663SMark.Logan@Sun.COM 	if (!(ni = ntfs_inode_open(vol, mref)))
19989663SMark.Logan@Sun.COM 		perr_exit("ntfs_open_inode");
19999663SMark.Logan@Sun.COM 
20009663SMark.Logan@Sun.COM 	if (!(*ctx = attr_get_search_ctx(ni, NULL)))
20019663SMark.Logan@Sun.COM 		exit(1);
20029663SMark.Logan@Sun.COM 
20039663SMark.Logan@Sun.COM 	if ((ustr = ntfs_str2ucs(aname, &len)) == NULL) {
20049663SMark.Logan@Sun.COM 		perr_printf("Couldn't convert '%s' to Unicode", aname);
20059663SMark.Logan@Sun.COM 		exit(1);
20069663SMark.Logan@Sun.COM 	}
20079663SMark.Logan@Sun.COM 
20089663SMark.Logan@Sun.COM 	if (ntfs_attr_lookup(AT_DATA, ustr, len, 0, 0, NULL, 0, *ctx))
20099663SMark.Logan@Sun.COM 		perr_exit("ntfs_lookup_attr");
20109663SMark.Logan@Sun.COM 
20119663SMark.Logan@Sun.COM 	ntfs_ucsfree(ustr);
20129663SMark.Logan@Sun.COM }
20139663SMark.Logan@Sun.COM 
check_bad_sectors(ntfs_volume * vol)20149663SMark.Logan@Sun.COM static int check_bad_sectors(ntfs_volume *vol)
20159663SMark.Logan@Sun.COM {
20169663SMark.Logan@Sun.COM 	ntfs_attr_search_ctx *ctx;
20179663SMark.Logan@Sun.COM 	ntfs_inode *base_ni;
20189663SMark.Logan@Sun.COM 	runlist *rl;
20199663SMark.Logan@Sun.COM 	s64 i, badclusters = 0;
20209663SMark.Logan@Sun.COM 
20219663SMark.Logan@Sun.COM 	ntfs_log_verbose("Checking for bad sectors ...\n");
20229663SMark.Logan@Sun.COM 
20239663SMark.Logan@Sun.COM 	lookup_data_attr(vol, FILE_BadClus, "$Bad", &ctx);
20249663SMark.Logan@Sun.COM 
20259663SMark.Logan@Sun.COM 	base_ni = ctx->base_ntfs_ino;
20269663SMark.Logan@Sun.COM 	if (!base_ni)
20279663SMark.Logan@Sun.COM 		base_ni = ctx->ntfs_ino;
20289663SMark.Logan@Sun.COM 
20299663SMark.Logan@Sun.COM 	if (NInoAttrList(base_ni)) {
20309663SMark.Logan@Sun.COM 		err_printf("Hopelessly many bad sectors has been detected!\n");
20319663SMark.Logan@Sun.COM 		printf("%s", many_bad_sectors_msg);
20329663SMark.Logan@Sun.COM 		exit(1);
20339663SMark.Logan@Sun.COM 	}
20349663SMark.Logan@Sun.COM 
20359663SMark.Logan@Sun.COM 	if (!ctx->attr->non_resident)
20369663SMark.Logan@Sun.COM 		err_exit("Resident attribute in $BadClust! Please report to "
20379663SMark.Logan@Sun.COM 			 "%s\n", NTFS_DEV_LIST);
20389663SMark.Logan@Sun.COM 	/*
20399663SMark.Logan@Sun.COM 	 * FIXME: The below would be partial for non-base records in the
20409663SMark.Logan@Sun.COM 	 * not yet supported multi-record case. Alternatively use audited
20419663SMark.Logan@Sun.COM 	 * ntfs_attr_truncate after an umount & mount.
20429663SMark.Logan@Sun.COM 	 */
20439663SMark.Logan@Sun.COM 	if (!(rl = ntfs_mapping_pairs_decompress(vol, ctx->attr, NULL)))
20449663SMark.Logan@Sun.COM 		perr_exit("Decompressing $BadClust:$Bad mapping pairs failed");
20459663SMark.Logan@Sun.COM 
20469663SMark.Logan@Sun.COM 	for (i = 0; rl[i].length; i++) {
20479663SMark.Logan@Sun.COM 		/* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */
20489663SMark.Logan@Sun.COM 		if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED)
20499663SMark.Logan@Sun.COM 			continue;
20509663SMark.Logan@Sun.COM 
20519663SMark.Logan@Sun.COM 		badclusters += rl[i].length;
20529663SMark.Logan@Sun.COM 		ntfs_log_verbose("Bad cluster: %#8llx - %#llx    (%lld)\n",
20539663SMark.Logan@Sun.COM 				 rl[i].lcn, rl[i].lcn + rl[i].length - 1,
20549663SMark.Logan@Sun.COM 				 rl[i].length);
20559663SMark.Logan@Sun.COM 	}
20569663SMark.Logan@Sun.COM 
20579663SMark.Logan@Sun.COM 	if (badclusters) {
20589663SMark.Logan@Sun.COM 		printf("%sThis software has detected that the disk has at least"
20599663SMark.Logan@Sun.COM 		       " %lld bad sector%s.\n",
20609663SMark.Logan@Sun.COM 		       !opt.badsectors ? NERR_PREFIX : "WARNING: ",
20619663SMark.Logan@Sun.COM 		       badclusters, badclusters - 1 ? "s" : "");
20629663SMark.Logan@Sun.COM 		if (!opt.badsectors) {
20639663SMark.Logan@Sun.COM 			printf("%s", bad_sectors_warning_msg);
20649663SMark.Logan@Sun.COM 			exit(1);
20659663SMark.Logan@Sun.COM 		} else
20669663SMark.Logan@Sun.COM 			printf("WARNING: Bad sectors can cause reliability "
20679663SMark.Logan@Sun.COM 			       "problems and massive data loss!!!\n");
20689663SMark.Logan@Sun.COM 	}
20699663SMark.Logan@Sun.COM 
20709663SMark.Logan@Sun.COM 	free(rl);
20719663SMark.Logan@Sun.COM 	ntfs_attr_put_search_ctx(ctx);
20729663SMark.Logan@Sun.COM 
20739663SMark.Logan@Sun.COM 	return badclusters;
20749663SMark.Logan@Sun.COM }
20759663SMark.Logan@Sun.COM 
20769663SMark.Logan@Sun.COM /**
20779663SMark.Logan@Sun.COM  * truncate_badclust_file
20789663SMark.Logan@Sun.COM  *
20799663SMark.Logan@Sun.COM  * Shrink the $BadClus file to match the new volume size.
20809663SMark.Logan@Sun.COM  */
truncate_badclust_file(ntfs_resize_t * resize)20819663SMark.Logan@Sun.COM static void truncate_badclust_file(ntfs_resize_t *resize)
20829663SMark.Logan@Sun.COM {
20839663SMark.Logan@Sun.COM 	printf("Updating $BadClust file ...\n");
20849663SMark.Logan@Sun.COM 
20859663SMark.Logan@Sun.COM 	lookup_data_attr(resize->vol, FILE_BadClus, "$Bad", &resize->ctx);
20869663SMark.Logan@Sun.COM 	/* FIXME: sanity_check_attr(ctx->attr); */
20879663SMark.Logan@Sun.COM 	truncate_badclust_bad_attr(resize);
20889663SMark.Logan@Sun.COM 
20899663SMark.Logan@Sun.COM 	if (write_mft_record(resize->vol, resize->ctx->ntfs_ino->mft_no,
20909663SMark.Logan@Sun.COM 			     resize->ctx->mrec))
20919663SMark.Logan@Sun.COM 		perr_exit("Couldn't update $BadClust");
20929663SMark.Logan@Sun.COM 
20939663SMark.Logan@Sun.COM 	ntfs_attr_put_search_ctx(resize->ctx);
20949663SMark.Logan@Sun.COM }
20959663SMark.Logan@Sun.COM 
20969663SMark.Logan@Sun.COM /**
20979663SMark.Logan@Sun.COM  * truncate_bitmap_file
20989663SMark.Logan@Sun.COM  *
20999663SMark.Logan@Sun.COM  * Shrink the $Bitmap file to match the new volume size.
21009663SMark.Logan@Sun.COM  */
truncate_bitmap_file(ntfs_resize_t * resize)21019663SMark.Logan@Sun.COM static void truncate_bitmap_file(ntfs_resize_t *resize)
21029663SMark.Logan@Sun.COM {
21039663SMark.Logan@Sun.COM 	printf("Updating $Bitmap file ...\n");
21049663SMark.Logan@Sun.COM 
21059663SMark.Logan@Sun.COM 	lookup_data_attr(resize->vol, FILE_Bitmap, NULL, &resize->ctx);
21069663SMark.Logan@Sun.COM 	truncate_bitmap_data_attr(resize);
21079663SMark.Logan@Sun.COM 
21089663SMark.Logan@Sun.COM 	if (write_mft_record(resize->vol, resize->ctx->ntfs_ino->mft_no,
21099663SMark.Logan@Sun.COM 			     resize->ctx->mrec))
21109663SMark.Logan@Sun.COM 		perr_exit("Couldn't update $Bitmap");
21119663SMark.Logan@Sun.COM 
21129663SMark.Logan@Sun.COM 	ntfs_attr_put_search_ctx(resize->ctx);
21139663SMark.Logan@Sun.COM }
21149663SMark.Logan@Sun.COM 
21159663SMark.Logan@Sun.COM /**
21169663SMark.Logan@Sun.COM  * setup_lcn_bitmap
21179663SMark.Logan@Sun.COM  *
21189663SMark.Logan@Sun.COM  * Allocate a block of memory with one bit for each cluster of the disk.
21199663SMark.Logan@Sun.COM  * All the bits are set to 0, except those representing the region beyond the
21209663SMark.Logan@Sun.COM  * end of the disk.
21219663SMark.Logan@Sun.COM  */
setup_lcn_bitmap(struct bitmap * bm,s64 nr_clusters)21229663SMark.Logan@Sun.COM static int setup_lcn_bitmap(struct bitmap *bm, s64 nr_clusters)
21239663SMark.Logan@Sun.COM {
21249663SMark.Logan@Sun.COM 	/* Determine lcn bitmap byte size and allocate it. */
21259663SMark.Logan@Sun.COM 	bm->size = rounded_up_division(nr_clusters, 8);
21269663SMark.Logan@Sun.COM 
21279663SMark.Logan@Sun.COM 	bm->bm = ntfs_calloc(bm->size);
21289663SMark.Logan@Sun.COM 	if (!bm->bm)
21299663SMark.Logan@Sun.COM 		return -1;
21309663SMark.Logan@Sun.COM 
21319663SMark.Logan@Sun.COM 	bitmap_file_data_fixup(nr_clusters, bm);
21329663SMark.Logan@Sun.COM 	return 0;
21339663SMark.Logan@Sun.COM }
21349663SMark.Logan@Sun.COM 
21359663SMark.Logan@Sun.COM /**
21369663SMark.Logan@Sun.COM  * update_bootsector
21379663SMark.Logan@Sun.COM  *
21389663SMark.Logan@Sun.COM  * FIXME: should be done using ntfs_* functions
21399663SMark.Logan@Sun.COM  */
update_bootsector(ntfs_resize_t * r)21409663SMark.Logan@Sun.COM static void update_bootsector(ntfs_resize_t *r)
21419663SMark.Logan@Sun.COM {
21429663SMark.Logan@Sun.COM 	NTFS_BOOT_SECTOR bs;
21439663SMark.Logan@Sun.COM 	s64  bs_size = sizeof(NTFS_BOOT_SECTOR);
21449663SMark.Logan@Sun.COM 	ntfs_volume *vol = r->vol;
21459663SMark.Logan@Sun.COM 
21469663SMark.Logan@Sun.COM 	printf("Updating Boot record ...\n");
21479663SMark.Logan@Sun.COM 
21489663SMark.Logan@Sun.COM 	if (vol->u.dev->d_ops->seek(vol->u.dev, 0, SEEK_SET) == (off_t)-1)
21499663SMark.Logan@Sun.COM 		perr_exit("lseek");
21509663SMark.Logan@Sun.COM 
21519663SMark.Logan@Sun.COM 	if (vol->u.dev->d_ops->read(vol->u.dev, &bs, bs_size) == -1)
21529663SMark.Logan@Sun.COM 		perr_exit("read() error");
21539663SMark.Logan@Sun.COM 
21549663SMark.Logan@Sun.COM 	bs.number_of_sectors = cpu_to_sle64(r->new_volume_size *
21559663SMark.Logan@Sun.COM 			bs.bpb.sectors_per_cluster);
21569663SMark.Logan@Sun.COM 
21579663SMark.Logan@Sun.COM 	if (r->mftmir_old) {
21589663SMark.Logan@Sun.COM 		r->progress.flags |= NTFS_PROGBAR_SUPPRESS;
21599663SMark.Logan@Sun.COM 		copy_clusters(r, r->mftmir_rl.lcn, r->mftmir_old,
21609663SMark.Logan@Sun.COM 			      r->mftmir_rl.length);
21619663SMark.Logan@Sun.COM 		bs.mftmirr_lcn = cpu_to_sle64(r->mftmir_rl.lcn);
21629663SMark.Logan@Sun.COM 		r->progress.flags &= ~NTFS_PROGBAR_SUPPRESS;
21639663SMark.Logan@Sun.COM 	}
21649663SMark.Logan@Sun.COM 
21659663SMark.Logan@Sun.COM 	if (vol->u.dev->d_ops->seek(vol->u.dev, 0, SEEK_SET) == (off_t)-1)
21669663SMark.Logan@Sun.COM 		perr_exit("lseek");
21679663SMark.Logan@Sun.COM 
21689663SMark.Logan@Sun.COM 	if (!opt.ro_flag)
21699663SMark.Logan@Sun.COM 		if (vol->u.dev->d_ops->write(vol->u.dev, &bs, bs_size) == -1)
21709663SMark.Logan@Sun.COM 			perr_exit("write() error");
21719663SMark.Logan@Sun.COM }
21729663SMark.Logan@Sun.COM 
21739663SMark.Logan@Sun.COM /**
21749663SMark.Logan@Sun.COM  * vol_size
21759663SMark.Logan@Sun.COM  */
vol_size(ntfs_volume * v,s64 nr_clusters)21769663SMark.Logan@Sun.COM static s64 vol_size(ntfs_volume *v, s64 nr_clusters)
21779663SMark.Logan@Sun.COM {
21789663SMark.Logan@Sun.COM 	/* add one sector_size for the backup boot sector */
21799663SMark.Logan@Sun.COM 	return nr_clusters * v->cluster_size + v->sector_size;
21809663SMark.Logan@Sun.COM }
21819663SMark.Logan@Sun.COM 
21829663SMark.Logan@Sun.COM /**
21839663SMark.Logan@Sun.COM  * print_vol_size
21849663SMark.Logan@Sun.COM  *
21859663SMark.Logan@Sun.COM  * Print the volume size in bytes and decimal megabytes.
21869663SMark.Logan@Sun.COM  */
print_vol_size(const char * str,s64 bytes)21879663SMark.Logan@Sun.COM static void print_vol_size(const char *str, s64 bytes)
21889663SMark.Logan@Sun.COM {
21899663SMark.Logan@Sun.COM 	printf("%s: %lld bytes (%lld MB)\n", str, (long long)bytes,
21909663SMark.Logan@Sun.COM 			(long long)rounded_up_division(bytes, NTFS_MBYTE));
21919663SMark.Logan@Sun.COM }
21929663SMark.Logan@Sun.COM 
21939663SMark.Logan@Sun.COM /**
21949663SMark.Logan@Sun.COM  * print_disk_usage
21959663SMark.Logan@Sun.COM  *
21969663SMark.Logan@Sun.COM  * Display the amount of disk space in use.
21979663SMark.Logan@Sun.COM  */
print_disk_usage(ntfs_volume * vol,s64 nr_used_clusters)21989663SMark.Logan@Sun.COM static void print_disk_usage(ntfs_volume *vol, s64 nr_used_clusters)
21999663SMark.Logan@Sun.COM {
22009663SMark.Logan@Sun.COM 	s64 total, used;
22019663SMark.Logan@Sun.COM 
22029663SMark.Logan@Sun.COM 	total = vol->nr_clusters * vol->cluster_size;
22039663SMark.Logan@Sun.COM 	used = nr_used_clusters * vol->cluster_size;
22049663SMark.Logan@Sun.COM 
22059663SMark.Logan@Sun.COM 	/* WARNING: don't modify the text, external tools grep for it */
22069663SMark.Logan@Sun.COM 	printf("Space in use       : %lld MB (%.1f%%)\n",
22079663SMark.Logan@Sun.COM 	       (long long)rounded_up_division(used, NTFS_MBYTE),
22089663SMark.Logan@Sun.COM 	       100.0 * ((float)used / total));
22099663SMark.Logan@Sun.COM }
22109663SMark.Logan@Sun.COM 
print_num_of_relocations(ntfs_resize_t * resize)22119663SMark.Logan@Sun.COM static void print_num_of_relocations(ntfs_resize_t *resize)
22129663SMark.Logan@Sun.COM {
22139663SMark.Logan@Sun.COM 	s64 relocations = resize->relocations * resize->vol->cluster_size;
22149663SMark.Logan@Sun.COM 
22159663SMark.Logan@Sun.COM 	printf("Needed relocations : %lld (%lld MB)\n",
22169663SMark.Logan@Sun.COM 			(long long)resize->relocations, (long long)
22179663SMark.Logan@Sun.COM 			rounded_up_division(relocations, NTFS_MBYTE));
22189663SMark.Logan@Sun.COM }
22199663SMark.Logan@Sun.COM 
22209663SMark.Logan@Sun.COM /**
22219663SMark.Logan@Sun.COM  * mount_volume
22229663SMark.Logan@Sun.COM  *
22239663SMark.Logan@Sun.COM  * First perform some checks to determine if the volume is already mounted, or
22249663SMark.Logan@Sun.COM  * is dirty (Windows wasn't shutdown properly).  If everything is OK, then mount
22259663SMark.Logan@Sun.COM  * the volume (load the metadata into memory).
22269663SMark.Logan@Sun.COM  */
mount_volume(void)22279663SMark.Logan@Sun.COM static ntfs_volume *mount_volume(void)
22289663SMark.Logan@Sun.COM {
22299663SMark.Logan@Sun.COM 	unsigned long mntflag;
22309663SMark.Logan@Sun.COM 	ntfs_volume *vol = NULL;
22319663SMark.Logan@Sun.COM 
22329663SMark.Logan@Sun.COM 	if (ntfs_check_if_mounted(opt.volume, &mntflag)) {
22339663SMark.Logan@Sun.COM 		perr_printf("Failed to check '%s' mount state", opt.volume);
22349663SMark.Logan@Sun.COM 		printf("Probably /etc/mtab is missing. It's too risky to "
22359663SMark.Logan@Sun.COM 		       "continue. You might try\nan another Linux distro.\n");
22369663SMark.Logan@Sun.COM 		exit(1);
22379663SMark.Logan@Sun.COM 	}
22389663SMark.Logan@Sun.COM 	if (mntflag & NTFS_MF_MOUNTED) {
22399663SMark.Logan@Sun.COM 		if (!(mntflag & NTFS_MF_READONLY))
22409663SMark.Logan@Sun.COM 			err_exit("Device '%s' is mounted read-write. "
22419663SMark.Logan@Sun.COM 				 "You must 'umount' it first.\n", opt.volume);
22429663SMark.Logan@Sun.COM 		if (!opt.ro_flag)
22439663SMark.Logan@Sun.COM 			err_exit("Device '%s' is mounted. "
22449663SMark.Logan@Sun.COM 				 "You must 'umount' it first.\n", opt.volume);
22459663SMark.Logan@Sun.COM 	}
22469663SMark.Logan@Sun.COM 	/*
22479663SMark.Logan@Sun.COM 	 * Pass NTFS_MNT_FORENSIC so that the mount process does not modify the
22489663SMark.Logan@Sun.COM 	 * volume at all.  We will do the logfile emptying and dirty setting
22499663SMark.Logan@Sun.COM 	 * later if needed.
22509663SMark.Logan@Sun.COM 	 */
22519663SMark.Logan@Sun.COM 	if (!(vol = ntfs_mount(opt.volume, opt.ro_flag | NTFS_MNT_FORENSIC))) {
22529663SMark.Logan@Sun.COM 		int err = errno;
22539663SMark.Logan@Sun.COM 
22549663SMark.Logan@Sun.COM 		perr_printf("Opening '%s' as NTFS failed", opt.volume);
22559663SMark.Logan@Sun.COM 		if (err == EINVAL)
22569663SMark.Logan@Sun.COM 			printf(invalid_ntfs_msg, opt.volume);
22579663SMark.Logan@Sun.COM 		else if (err == EIO)
22589663SMark.Logan@Sun.COM 			printf("%s", corrupt_volume_msg);
22599663SMark.Logan@Sun.COM 		else if (err == EPERM)
22609663SMark.Logan@Sun.COM 			printf("%s", hibernated_volume_msg);
22619663SMark.Logan@Sun.COM 		else if (err == EOPNOTSUPP)
22629663SMark.Logan@Sun.COM 			printf("%s", unclean_journal_msg);
22639663SMark.Logan@Sun.COM 		else if (err == EBUSY)
22649663SMark.Logan@Sun.COM 			printf("%s", opened_volume_msg);
22659663SMark.Logan@Sun.COM 		exit(1);
22669663SMark.Logan@Sun.COM 	}
22679663SMark.Logan@Sun.COM 
22689663SMark.Logan@Sun.COM 	if (NVolWasDirty(vol))
22699663SMark.Logan@Sun.COM 		if (opt.force-- <= 0)
22709663SMark.Logan@Sun.COM 			err_exit("Volume is scheduled for check.\nRun chkdsk /f"
22719663SMark.Logan@Sun.COM 				 " and please try again, or see option -f.\n");
22729663SMark.Logan@Sun.COM 
22739663SMark.Logan@Sun.COM 	if (NTFS_MAX_CLUSTER_SIZE < vol->cluster_size)
22749663SMark.Logan@Sun.COM 		err_exit("Cluster size %u is too large!\n",
22759663SMark.Logan@Sun.COM 			(unsigned int)vol->cluster_size);
22769663SMark.Logan@Sun.COM 
22779663SMark.Logan@Sun.COM 	printf("Device name        : %s\n", opt.volume);
22789663SMark.Logan@Sun.COM 	printf("NTFS volume version: %d.%d\n", vol->major_ver, vol->minor_ver);
22799663SMark.Logan@Sun.COM 	if (ntfs_version_is_supported(vol))
22809663SMark.Logan@Sun.COM 		perr_exit("Unknown NTFS version");
22819663SMark.Logan@Sun.COM 
22829663SMark.Logan@Sun.COM 	printf("Cluster size       : %u bytes\n",
22839663SMark.Logan@Sun.COM 			(unsigned int)vol->cluster_size);
22849663SMark.Logan@Sun.COM 	print_vol_size("Current volume size", vol_size(vol, vol->nr_clusters));
22859663SMark.Logan@Sun.COM 
22869663SMark.Logan@Sun.COM 	return vol;
22879663SMark.Logan@Sun.COM }
22889663SMark.Logan@Sun.COM 
22899663SMark.Logan@Sun.COM /**
22909663SMark.Logan@Sun.COM  * prepare_volume_fixup
22919663SMark.Logan@Sun.COM  *
22929663SMark.Logan@Sun.COM  * Set the volume's dirty flag and wipe the filesystem journal.  When Windows
22939663SMark.Logan@Sun.COM  * boots it will automatically run chkdsk to check for any problems.  If the
22949663SMark.Logan@Sun.COM  * read-only command line option was given, this function will do nothing.
22959663SMark.Logan@Sun.COM  */
prepare_volume_fixup(ntfs_volume * vol)22969663SMark.Logan@Sun.COM static void prepare_volume_fixup(ntfs_volume *vol)
22979663SMark.Logan@Sun.COM {
22989663SMark.Logan@Sun.COM 	printf("Schedule chkdsk for NTFS consistency check at Windows boot "
22999663SMark.Logan@Sun.COM 			"time ...\n");
23009663SMark.Logan@Sun.COM 	vol->flags |= VOLUME_IS_DIRTY;
23019663SMark.Logan@Sun.COM 	if (ntfs_volume_write_flags(vol, vol->flags))
23029663SMark.Logan@Sun.COM 		perr_exit("Failed to set the volume dirty");
23039663SMark.Logan@Sun.COM 	NVolSetWasDirty(vol);
23049663SMark.Logan@Sun.COM 	if (vol->u.dev->d_ops->sync(vol->u.dev) == -1)
23059663SMark.Logan@Sun.COM 		perr_exit("Failed to sync device");
23069663SMark.Logan@Sun.COM 	printf("Resetting $LogFile ... (this might take a while)\n");
23079663SMark.Logan@Sun.COM 	if (ntfs_logfile_reset(vol))
23089663SMark.Logan@Sun.COM 		perr_exit("Failed to reset $LogFile");
23099663SMark.Logan@Sun.COM 	if (vol->u.dev->d_ops->sync(vol->u.dev) == -1)
23109663SMark.Logan@Sun.COM 		perr_exit("Failed to sync device");
23119663SMark.Logan@Sun.COM }
23129663SMark.Logan@Sun.COM 
set_disk_usage_constraint(ntfs_resize_t * resize)23139663SMark.Logan@Sun.COM static void set_disk_usage_constraint(ntfs_resize_t *resize)
23149663SMark.Logan@Sun.COM {
23159663SMark.Logan@Sun.COM 	/* last lcn for a filled up volume (no empty space) */
23169663SMark.Logan@Sun.COM 	s64 last = resize->inuse - 1;
23179663SMark.Logan@Sun.COM 
23189663SMark.Logan@Sun.COM 	if (resize->last_unsupp < last)
23199663SMark.Logan@Sun.COM 		resize->last_unsupp = last;
23209663SMark.Logan@Sun.COM }
23219663SMark.Logan@Sun.COM 
check_resize_constraints(ntfs_resize_t * resize)23229663SMark.Logan@Sun.COM static void check_resize_constraints(ntfs_resize_t *resize)
23239663SMark.Logan@Sun.COM {
23249663SMark.Logan@Sun.COM 	s64 new_size = resize->new_volume_size;
23259663SMark.Logan@Sun.COM 
23269663SMark.Logan@Sun.COM 	/* FIXME: resize.shrink true also if only -i is used */
23279663SMark.Logan@Sun.COM 	if (!resize->shrink)
23289663SMark.Logan@Sun.COM 		return;
23299663SMark.Logan@Sun.COM 
23309663SMark.Logan@Sun.COM 	if (resize->inuse == resize->vol->nr_clusters)
23319663SMark.Logan@Sun.COM 		err_exit("Volume is full. To shrink it, "
23329663SMark.Logan@Sun.COM 			 "delete unused files.\n");
23339663SMark.Logan@Sun.COM 
23349663SMark.Logan@Sun.COM 	if (opt.info)
23359663SMark.Logan@Sun.COM 		return;
23369663SMark.Logan@Sun.COM 
23379663SMark.Logan@Sun.COM 	/* FIXME: reserve some extra space so Windows can boot ... */
23389663SMark.Logan@Sun.COM 	if (new_size < resize->inuse)
23399663SMark.Logan@Sun.COM 		err_exit("New size can't be less than the space already"
23409663SMark.Logan@Sun.COM 			 " occupied by data.\nYou either need to delete unused"
23419663SMark.Logan@Sun.COM 			 " files or see the -i option.\n");
23429663SMark.Logan@Sun.COM 
23439663SMark.Logan@Sun.COM 	if (new_size <= resize->last_unsupp)
23449663SMark.Logan@Sun.COM 		err_exit("The fragmentation type, you have, isn't "
23459663SMark.Logan@Sun.COM 			 "supported yet. Rerun ntfsresize\nwith "
23469663SMark.Logan@Sun.COM 			 "the -i option to estimate the smallest "
23479663SMark.Logan@Sun.COM 			 "shrunken volume size supported.\n");
23489663SMark.Logan@Sun.COM 
23499663SMark.Logan@Sun.COM 	print_num_of_relocations(resize);
23509663SMark.Logan@Sun.COM }
23519663SMark.Logan@Sun.COM 
check_cluster_allocation(ntfs_volume * vol,ntfsck_t * fsck)23529663SMark.Logan@Sun.COM static void check_cluster_allocation(ntfs_volume *vol, ntfsck_t *fsck)
23539663SMark.Logan@Sun.COM {
23549663SMark.Logan@Sun.COM 	memset(fsck, 0, sizeof(ntfsck_t));
23559663SMark.Logan@Sun.COM 
23569663SMark.Logan@Sun.COM 	if (opt.show_progress)
23579663SMark.Logan@Sun.COM 		fsck->flags |= NTFSCK_PROGBAR;
23589663SMark.Logan@Sun.COM 
23599663SMark.Logan@Sun.COM 	if (setup_lcn_bitmap(&fsck->lcn_bitmap, vol->nr_clusters) != 0)
23609663SMark.Logan@Sun.COM 		perr_exit("Failed to setup allocation bitmap");
23619663SMark.Logan@Sun.COM 	if (build_allocation_bitmap(vol, fsck) != 0)
23629663SMark.Logan@Sun.COM 		exit(1);
23639663SMark.Logan@Sun.COM 	if (fsck->outsider || fsck->multi_ref) {
23649663SMark.Logan@Sun.COM 		err_printf("Filesystem check failed!\n");
23659663SMark.Logan@Sun.COM 		if (fsck->outsider)
23669663SMark.Logan@Sun.COM 			err_printf("%d clusters are referenced outside "
23679663SMark.Logan@Sun.COM 				   "of the volume.\n", fsck->outsider);
23689663SMark.Logan@Sun.COM 		if (fsck->multi_ref)
23699663SMark.Logan@Sun.COM 			err_printf("%d clusters are referenced multiply"
23709663SMark.Logan@Sun.COM 				   " times.\n", fsck->multi_ref);
23719663SMark.Logan@Sun.COM 		printf("%s", corrupt_volume_msg);
23729663SMark.Logan@Sun.COM 		exit(1);
23739663SMark.Logan@Sun.COM 	}
23749663SMark.Logan@Sun.COM 
23759663SMark.Logan@Sun.COM 	compare_bitmaps(vol, &fsck->lcn_bitmap);
23769663SMark.Logan@Sun.COM }
23779663SMark.Logan@Sun.COM 
main(int argc,char ** argv)23789663SMark.Logan@Sun.COM int main(int argc, char **argv)
23799663SMark.Logan@Sun.COM {
23809663SMark.Logan@Sun.COM 	ntfsck_t fsck;
23819663SMark.Logan@Sun.COM 	ntfs_resize_t resize;
23829663SMark.Logan@Sun.COM 	s64 new_size = 0;	/* in clusters; 0 = --info w/o --size */
23839663SMark.Logan@Sun.COM 	s64 device_size;        /* in bytes */
23849663SMark.Logan@Sun.COM 	ntfs_volume *vol;
23859663SMark.Logan@Sun.COM 
23869663SMark.Logan@Sun.COM 	ntfs_log_set_handler(ntfs_log_handler_outerr);
23879663SMark.Logan@Sun.COM 
23889663SMark.Logan@Sun.COM 	printf("%s v%s (libntfs %s)\n", EXEC_NAME, VERSION,
23899663SMark.Logan@Sun.COM 			ntfs_libntfs_version());
23909663SMark.Logan@Sun.COM 
23919663SMark.Logan@Sun.COM 	if (!parse_options(argc, argv))
23929663SMark.Logan@Sun.COM 		return 1;
23939663SMark.Logan@Sun.COM 
23949663SMark.Logan@Sun.COM 	utils_set_locale();
23959663SMark.Logan@Sun.COM 
23969663SMark.Logan@Sun.COM 	if (!(vol = mount_volume()))
23979663SMark.Logan@Sun.COM 		err_exit("Couldn't open volume '%s'!\n", opt.volume);
23989663SMark.Logan@Sun.COM 
23999663SMark.Logan@Sun.COM 	device_size = ntfs_device_size_get(vol->u.dev, vol->sector_size);
24009663SMark.Logan@Sun.COM 	device_size *= vol->sector_size;
24019663SMark.Logan@Sun.COM 	if (device_size <= 0)
24029663SMark.Logan@Sun.COM 		err_exit("Couldn't get device size (%lld)!\n", device_size);
24039663SMark.Logan@Sun.COM 
24049663SMark.Logan@Sun.COM 	print_vol_size("Current device size", device_size);
24059663SMark.Logan@Sun.COM 
24069663SMark.Logan@Sun.COM 	if (device_size < vol->nr_clusters * vol->cluster_size)
24079663SMark.Logan@Sun.COM 		err_exit("Current NTFS volume size is bigger than the device "
24089663SMark.Logan@Sun.COM 			 "size!\nCorrupt partition table or incorrect device "
24099663SMark.Logan@Sun.COM 			 "partitioning?\n");
24109663SMark.Logan@Sun.COM 
24119663SMark.Logan@Sun.COM 	if (!opt.bytes && !opt.info)
24129663SMark.Logan@Sun.COM 		opt.bytes = device_size;
24139663SMark.Logan@Sun.COM 
24149663SMark.Logan@Sun.COM 	/* Take the integer part: don't make the volume bigger than requested */
24159663SMark.Logan@Sun.COM 	new_size = opt.bytes / vol->cluster_size;
24169663SMark.Logan@Sun.COM 
24179663SMark.Logan@Sun.COM 	/* Backup boot sector at the end of device isn't counted in NTFS
24189663SMark.Logan@Sun.COM 	   volume size thus we have to reserve space for it. */
24199663SMark.Logan@Sun.COM 	if (new_size)
24209663SMark.Logan@Sun.COM 		--new_size;
24219663SMark.Logan@Sun.COM 
24229663SMark.Logan@Sun.COM 	if (!opt.info) {
24239663SMark.Logan@Sun.COM 		print_vol_size("New volume size    ", vol_size(vol, new_size));
24249663SMark.Logan@Sun.COM 		if (device_size < opt.bytes)
24259663SMark.Logan@Sun.COM 			err_exit("New size can't be bigger than the device size"
24269663SMark.Logan@Sun.COM 				 ".\nIf you want to enlarge NTFS then first "
24279663SMark.Logan@Sun.COM 				 "enlarge the device size by e.g. fdisk.\n");
24289663SMark.Logan@Sun.COM 	}
24299663SMark.Logan@Sun.COM 
24309663SMark.Logan@Sun.COM 	if (!opt.info && (new_size == vol->nr_clusters ||
24319663SMark.Logan@Sun.COM 			  (opt.bytes == device_size &&
24329663SMark.Logan@Sun.COM 			   new_size == vol->nr_clusters - 1))) {
24339663SMark.Logan@Sun.COM 		printf("Nothing to do: NTFS volume size is already OK.\n");
24349663SMark.Logan@Sun.COM 		exit(0);
24359663SMark.Logan@Sun.COM 	}
24369663SMark.Logan@Sun.COM 
24379663SMark.Logan@Sun.COM 	memset(&resize, 0, sizeof(resize));
24389663SMark.Logan@Sun.COM 	resize.vol = vol;
24399663SMark.Logan@Sun.COM 	resize.new_volume_size = new_size;
24409663SMark.Logan@Sun.COM 	/* This is also true if --info was used w/o --size (new_size = 0) */
24419663SMark.Logan@Sun.COM 	if (new_size < vol->nr_clusters)
24429663SMark.Logan@Sun.COM 		resize.shrink = 1;
24439663SMark.Logan@Sun.COM 	if (opt.show_progress)
24449663SMark.Logan@Sun.COM 		resize.progress.flags |= NTFS_PROGBAR;
24459663SMark.Logan@Sun.COM 	/*
24469663SMark.Logan@Sun.COM 	 * Checking and __reporting__ of bad sectors must be done before cluster
24479663SMark.Logan@Sun.COM 	 * allocation check because chkdsk doesn't fix $Bitmap's w/ bad sectors
24489663SMark.Logan@Sun.COM 	 * thus users would (were) quite confused why chkdsk doesn't work.
24499663SMark.Logan@Sun.COM 	 */
24509663SMark.Logan@Sun.COM 	resize.badclusters = check_bad_sectors(vol);
24519663SMark.Logan@Sun.COM 
24529663SMark.Logan@Sun.COM 	check_cluster_allocation(vol, &fsck);
24539663SMark.Logan@Sun.COM 
24549663SMark.Logan@Sun.COM 	print_disk_usage(vol, fsck.inuse);
24559663SMark.Logan@Sun.COM 
24569663SMark.Logan@Sun.COM 	resize.inuse = fsck.inuse;
24579663SMark.Logan@Sun.COM 	resize.lcn_bitmap = fsck.lcn_bitmap;
24589663SMark.Logan@Sun.COM 
24599663SMark.Logan@Sun.COM 	set_resize_constraints(&resize);
24609663SMark.Logan@Sun.COM 	set_disk_usage_constraint(&resize);
24619663SMark.Logan@Sun.COM 	check_resize_constraints(&resize);
24629663SMark.Logan@Sun.COM 
24639663SMark.Logan@Sun.COM 	if (opt.info) {
24649663SMark.Logan@Sun.COM 		advise_on_resize(&resize);
24659663SMark.Logan@Sun.COM 		exit(0);
24669663SMark.Logan@Sun.COM 	}
24679663SMark.Logan@Sun.COM 
24689663SMark.Logan@Sun.COM 	if (opt.force-- <= 0 && !opt.ro_flag) {
24699663SMark.Logan@Sun.COM 		printf("%s", resize_warning_msg);
24709663SMark.Logan@Sun.COM 		proceed_question();
24719663SMark.Logan@Sun.COM 	}
24729663SMark.Logan@Sun.COM 
24739663SMark.Logan@Sun.COM 	/* FIXME: performance - relocate logfile here if it's needed */
24749663SMark.Logan@Sun.COM 	prepare_volume_fixup(vol);
24759663SMark.Logan@Sun.COM 
24769663SMark.Logan@Sun.COM 	if (resize.relocations)
24779663SMark.Logan@Sun.COM 		relocate_inodes(&resize);
24789663SMark.Logan@Sun.COM 
24799663SMark.Logan@Sun.COM 	truncate_badclust_file(&resize);
24809663SMark.Logan@Sun.COM 	truncate_bitmap_file(&resize);
24819663SMark.Logan@Sun.COM 	update_bootsector(&resize);
24829663SMark.Logan@Sun.COM 
24839663SMark.Logan@Sun.COM 	/* We don't create backup boot sector because we don't know where the
24849663SMark.Logan@Sun.COM 	   partition will be split. The scheduled chkdsk will fix it */
24859663SMark.Logan@Sun.COM 
24869663SMark.Logan@Sun.COM 	if (opt.ro_flag) {
24879663SMark.Logan@Sun.COM 		printf("The read-only test run ended successfully.\n");
24889663SMark.Logan@Sun.COM 		exit(0);
24899663SMark.Logan@Sun.COM 	}
24909663SMark.Logan@Sun.COM 
24919663SMark.Logan@Sun.COM 	/* WARNING: don't modify the texts, external tools grep for them */
24929663SMark.Logan@Sun.COM 	printf("Syncing device ...\n");
24939663SMark.Logan@Sun.COM 	if (vol->u.dev->d_ops->sync(vol->u.dev) == -1)
24949663SMark.Logan@Sun.COM 		perr_exit("fsync");
24959663SMark.Logan@Sun.COM 
24969663SMark.Logan@Sun.COM 	printf("Successfully resized NTFS on device '%s'.\n", vol->u.dev->d_name);
24979663SMark.Logan@Sun.COM 	if (resize.shrink)
24989663SMark.Logan@Sun.COM 		printf("%s", resize_important_msg);
24999663SMark.Logan@Sun.COM 
25009663SMark.Logan@Sun.COM 	return 0;
25019663SMark.Logan@Sun.COM }
2502