CARP pfsync and gmirror for firewall failover and raid1 mirror under FreeBSD 6.2

Jephe Wu - http://linuxtechres.blogspot.com


Secure Firewall is very important for company network, it protects all valuable resources on the internal LAN. To avoid the single point of failure, it's always good practice to implement firewall fail-over. FreeBSD is a good choice to act as a firewall because it's popular, secure and support packet filter(PF) which is ported from OpenBSD, as well as CARP, pfsync. Software raid tool - gmirror in FreeBSD is extremely easy to configure.

Environment
1. 2 HP netserver lp1000r (running Freebsd 6.2)
2. each lp1000r server has 2 built-in network ports, one connects to Internet, the another connects to internal network
3. each lp1000r
3. web servers behind the firewalls using internal IP addresses (running CentOS 4)


Objective
1. when firewall1 is down, firewall2 will be taking over. This should be transparent to the end web user.
2. since each hp netserver lp1000r has 2 idential hard disks(18G), we need to build it as raid-1 mirroring. In case any one of hard disk dies, the firewall is still able to work.
3. 2 web servers are also DNS server, SMTP server and NTP client which means we need to configure the relevant PF rules on each firewall.

Naming conversion

hostname for firewall1: bsd1 (master firewall)
domain name: maxposs.com
external network: a.b.c.192/28 (replace a.b.c. with your own IPs)
default gateway: a.b.c.193
fxp0: a.b.c.204
carp0: a.b.c.201
carp0_alias0: a.b.c.200
fxp1: 10.0.0.5
carp1: 10.0.0.1

hostname for firewall2: bsd2 (backup firewall)
domain name: maxposs.com
external network: a.b.c.192/28
default gateway: a.b.c.193
fxp0: a.b.c.205
carp0: a.b.c.201
carp0_alias0: a.b.c.200
fxp1: 10.0.0.6
carp1: 10.0.0.1

hostname for internal web server 1: web1
ext0: 10.0.0.7
gateway: 10.0.0.1 (carp1 on firewalls)
services: web, smtp, dns, ntp client, ssh client

hostname for internal web server 2: web2
ext0: 10.0.0.8
gateway: 10.0.0.1 (carp1 on firewall)
services: web, smtp, dns, ntp client, ssh client

Freebsd 6.2 OS Installation
I'm using the default installation for FreeBSD, slide a for /, slide b for swap, slide d for /var and slide e for /usr.

Freebsd 6.2 raid-1 mirroring
Since there're 2 firewalls that needs to install, It's better to install one firewall, then the clone everything to the another one.

We installed FreeBSD OS on the first hard disk on bsd1, then use the following steps to add the second hard disk to become a raid-1 mirrored system.

# sysctl kern.geom.debugflags=16
# gmirror label -v -b round-robin gm0 /dev/da0
# echo geom_mirror_load=YES >> /boot/loader.conf
# vi /etc/fstab (to change all /dev/da0 to /dev/mirror/gm0)

bsd1# more /etc/fstab
# Device Mountpoint FStype Options Dump Pass#
/dev/mirror/gm0s2b none swap sw 0 0
/dev/mirror/gm0s1a / ufs rw 1 1
/dev/mirror/gm0s4d /usr ufs rw 2 2
/dev/mirror/gm0s3d /var ufs rw 2 2
/dev/acd0 /cdrom cd9660 ro,noauto 0 0
# reboot
After reboot, you can use command 'gmirror status' or 'gmirror list' to check the raid1 status.
For adding the second hard disk to raid array, run
#gmirror forget gm0
# gmirror insert gm0 /dev/da1

Compiling kernel to enable carp and pfsync interfaces
During OS installation, we installed kernel developer packages so that we can do kernel
compilation for carp and pfsync later.

First of all, backup your current original kernel first.
# cp -pr /boot/kernel /boot/kernel.orig

note:After the whole system is stablized, you might want to backup the working kernel again
# cp -pr /boot/kernel /boot/kernel.good


# cd /usr/src/sys/i386/conf
# cp GENERIC MYKERNEL
# vi MYKERNEL (to add the following lines)
device pf
device pfsync
device pflog
device carp
note: you cannot just add 'device pfsync' without adding 'device pf' first.

If you want to be able to use ALTQ then the following as well:

options ALTQ
options ALTQ_CBQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_PRIQ
options ALTQ_NOPCC
# cd /usr/src

# make buildkernel KERNCONF=MYKERNEL 
# make installkernel KERNCONF=MYKERNEL
# reboot

In case the new kernel doesn't boot up, please refer to http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-trouble.html#KERNELCONFIG-NOBOOT

Clone the FreeBSD OS to another firewall/ Replacing failed hard disk with a new one

shutdown bsd1 firewall, put the 2 hard disks to the left slot on each firewall, then
insert empty 2 hard disks to the right slot on each firewall, then run the following
commands to add the second hard disk to raid1 array
# gmirror forget gm0
# gmirror insert gm0 /dev/da1
note: you must run the first command, otherwise you will get error message 'not all
hard disks connected'


Configuring CARP and Pfsync on firewall

/etc/rc.conf on bsd1:
gateway="YES"

pf_enable="YES"
pf_rules="/etc/pf.conf"
pf_flags=""
pflog_enable="YES"
pflog_logfile="/var/log/pflog"
pflog_flags=""


cloned_interfaces="carp0 carp1"
ifconfig_carp0="vhid 1 pass maxposs a.b.c.201/28"
ifconfig_carp0_alias0="vhid 1 pass maxposs a.b.c.200/28"
ifconfig_carp1="vhid 2 pass maxposs 10.0.0.1/24"
ifconfig_pfsync0="up syncif fxp1"

/etc/rc.conf on bsd2
gateway="YES"

pf_enable="YES"
pf_rules="/etc/pf.conf"
pf_flags=""
pflog_enable="YES"
pflog_logfile="/var/log/pflog"
pflog_flags=""


cloned_interfaces="carp0 carp1"
ifconfig_carp0="vhid 1 advskew 100 pass maxposs a.b.c.201/28"
ifconfig_carp0_alias0="vhid 1 advskew 100 pass maxposs a.b.c.200/28"
ifconfig_carp1="vhid 2 advskew 100 pass maxposs 10.0.0.1/24"
ifconfig_pfsync0="up syncif fxp1"

note:
1. maxposs is the password, must be same for the same vhid (virtual host ID)
2. advskew 100 on bsd2 makes the carp advertisement packet less frequent than bsd1 so
it will be backup firewall whenever the election happens.

/etc/sysctl.conf on both bsd1 and bsd2
add the following lines

net.inet.tcp.blackhole=2
net.inet.udp.blackhole=1

#if one interface fails then all will fail over
net.inet.carp.preempt=1

net.inet.tcp.sendspace=65536
net.inet.tcp.recvspace=65536


/etc/pf.conf on both bsd1 and bsd2

int_if="fxp1"
ext_if="fxp0"
lo_if="lo0"

int_network="10.0.0.0/24"

web1 = "10.0.0.7/32"
web2 = "10.0.0.8/32"

int_www = "{ $web1, $web2 }"
int_ns = "{ $web1, $web2 }"

ext_www = "a.b.c.201"
ext_ns = "a.b.c.200"
ext_smtp="a.b.c.206"

ssh_client1 = "x.y.z.1/32"
ssh_client2 = "x.y.z.2/32"


# Normalization
scrub in all


# NAT for all
#nat on $ext_if from $int_network to any -> $ext_if
# above is commented, for using physical external interface for outgoing smtp nat
nat on $ext_if from $int_network to any -> $ext_smtp
# using virtual interface for outgoing smtp nat

# web service rdr
rdr on fxp0 proto tcp from any to $ext_www port 80 -> $int_www round-robin
rdr on fxp0 proto tcp from any to $ext_www port 443 -> $int_www round-robin

# dns rdr
rdr on fxp0 proto udp from any to $ext_ns port 53 -> $int_ns port 53

# default rule
block in log all

# Allow all Loopback
pass quick on $lo_if all

# Allow pfsync Updates In/Out
pass quick on $int_if proto pfsync keep state

# Allow CARP Advertisements In/Out
pass quick on {$ext_if, $int_if} proto carp keep state

# dns incoming traffic
pass in log quick on fxp0 proto udp from any to $int_www port = 53 keep state
pass out quick on fxp1 proto udp from any to $int_www port = 53 keep state

# dns outgoing traffic
pass out log quick on fxp0 proto udp from fxp0 to any port = 53 keep state
pass in quick on fxp1 proto udp from $int_www to any port = 53 keep state

# smtp outgoing traffic from physical interface fxp0 and virtual interface $ext_smtp
pass out quick on fxp0 proto tcp from fxp0 to any port = 25 keep state
# for allowing smtp traffic from virtual external interface to any
pass out quick on fxp0 proto tcp from $ext_smtp to any port = 25 keep state
pass in quick on fxp1 proto tcp from $int_www to any port = 25 keep state

# ssh outgoing traffic
pass out quick on fxp0 proto tcp from fxp0 to $ssh_clients port = 22 keep state
pass in quick on fxp1 proto tcp from $int_www to $ssh_clients port = 22 keep state

# ntp outgoing traffic
pass out quick on fxp0 proto udp from fxp0 to any port = 123 keep state
pass in quick on fxp1 proto udp from $int_www to any port = 123 keep state

# web incoming traffic
pass in quick on fxp0 proto tcp from any to $int_www port {80,443} keep state
pass out quick on fxp1 proto tcp from any to $int_www port {80,443} keep state

  • for outgoing traffic NAT, also can do NAT on carp alias interface as follows
put the following line to nat part in /etc/pf.conf
nat on $ext_if from $int_network to any -> 1.2.3.4
then enable carp alias interface as follows:
# ifconfig carp0 alias 1.2.3.4 netmask 255.255.255.0
note: for disable alias, run
# ifconfig carp0 -alias 1.2.3.4

note: you even can define NAT only for port 25 as follows in /etc/pf.conf
nat on $ext_if from $int_network to any port 25 -> 1.2.3.4



How to convert CentOS root file system to XFS

Jephe Wu - http://linuxtechres.blogspot.com


As you know, the CentOS is a clone of Redhat Enterprise Linux. CentOS doesn't support XFS file system by default which is the same as RHEL, but CentOS provides additional kernel which has built-in XFS support. You can download it to replace the existing kernel to easily get XFS support in kernel space. But the problem is how to make your /, /usr,/var etc partition as XFS file system since you already used ext3 during installation.

This article is to guide you to do that, the following is my testing environments:

CentOS 4.1
partitions:
/dev/hda1 -/boot (ext3)
/dev/hda2 - swap
/dev/hda3 - / (ext3)
/dev/hda5 - /usr (ext3)
/dev/hda6 -/var (ext3)
/dev/hda7 - /tmp (ext3)
/dev/hda8 - /serverdata (ext3)(for storing data later, it's empty after installation)


Our objective:
convert all partitions except for /boot from default file system ext3 to XFS. Grub doesn't support XFS so we leave /boot as ext3.

Clone and XFS convert concept:
a. download XFS-enabled kernel from CentOS and install it
b. use /serverdata partition to save all server OS partitions such as /boot, /,/usr,/var,/tmp
c. boot up server using RIP(Recovery Is Possible) CD
d. format all partitions as XFS
e. copy back all data from /serverdata
f. change /etc/fstab and /boot/initrd image file
g. done. reboot

Steps:

a. download the latest RIP CD at http://www.tux.org/pub/people/kent-robotti/looplinux/rip/
download Non-X version is enough

b. download the XFS enabled supported kernel from CentOS website and install it to the server
# rpm -ivh xfs-enabled-kernel
# vi /etc/grub.conf
to make sure it will boot with the new kernel next time
# reboot
(make sure it can boot normally with new kernel)

c. copy the whole server OS to /serverdata, make xfs file system then copy back
c.1 reboot server with RIP CD after upgrading to xfs enabled kernel, you might want to choose the second option to skip keyboard map
c.2 login as root without password
c.3 make /serverdata partition as XFS first and backup the whole OS to /serverdata
mkfs -t xfs /dev/hda8
cd /mnt
mount /dev/hda3 hd
cd hd
mount /dev/hda1 boot
mount /dev/hda5 usr
mount /dev/hda6 var
mount /dev/hda7 tmp
mount /dev/hda8 serverdata
chroot .
tar --exclude ./boot --exclude ./proc --exclude ./serverdata -cpf - .| (cd serverdata; tar xvpf -)

c.4 umount all partitions then make xfs file systems
exit (exit from chroot environement)
cd /mnt/hd
umount usr
umount var
umount tmp
umount serverdata
umount boot
cd ..
umount hd

mkfs -t xfs -L / /dev/hda3
mkfs -t xfs -L /usr /dev/hda5
mkfs -t xfs -L /var /dev/hda6
mkfs -t xfs -L /tmp /dev/hda7
note: -L to make label for partition, be sure to use the same label name in /etc/fstab.

c.5 mount them again like c.3
cd /mnt
mount /dev/hda3 hd
cd hd
mount /dev/hda1 boot
mount /dev/hda5 usr
mount /dev/hda6 var
mount /dev/hda7 tmp
mount /dev/hda8 serverdata

c.6 copy back the whole OS from backup
(cd serverdata; tar cpf - . ) | tar xvpf -

d. make necessary changes
d.1 change /etc/fstab
cd /mnt/hd
chroot .
vi /etc/fstab (to change file system for those changed from ext3 to xfs, if you didn't use -L option in above mkfs -t xfs for /, /usr,/var,/tmp partitions, you also need to change the LABEL= to the real device name like from LABEL=/usr to /dev/hda5)

d.2 change /boot/initrd image file
cd /mnt/hd
chroot .
cp /boot/initrd.img /tmp/a (use the correct name for your initrd image, I use /boot/initrd.img here)
cd /tmp

zcat a > a1

mkdir a1.dir

cd a1.dir

cpio -iv < ../a1

modify something

vi init ( change ext3 to xfs, add xfs.ko module in init as well as lib/ folder) as follows:
[root@linuxtechres ]# more init
#!/bin/nash
mount -t proc /proc /proc
setquiet
echo Mounted /proc filesystem
echo Mounting sysfs
mount -t sysfs none /sys
echo Creating /dev
mount -o mode=0755 -t tmpfs none /dev
mknod /dev/console c 5 1
mknod /dev/null c 1 3
mknod /dev/zero c 1 5
mknod /dev/hda3 b 3 3 ---> added, hda3 is the root file system device
mkdir /dev/pts
mkdir /dev/shm
echo Starting udev
/sbin/udevstart
echo -n "/sbin/hotplug" > /proc/sys/kernel/hotplug
echo "Loading jbd.ko module"
insmod /lib/jbd.ko
echo "Loading ext3.ko module"
insmod /lib/ext3.ko
echo "Loading xfs.ko module"

insmod /lib/xfs.ko ---> added - please remember to copy xfs.ko file from /lib/modules/kernel_version/kernel/fs/xfs/xfs.ko to /lib
/sbin/udevstart
echo Creating root device
mkrootdev /dev/root
umount /sys
echo Mounting root filesystem
mount -o defaults --ro -t xfs /dev/root /sysroot ---> change ext3 to xfs
mount -t tmpfs --bind /dev /sysroot/dev
echo Switching to new root
switchroot /sysroot
umount /initrd/dev
re-generate initrd.img with xfs support
find . | cpio -co > ../c
cd ..
gzip c
cp c.gz /boot/initrd.xfs.img
change /etc/grub.conf to use this new initrd.xfs.img file.
d.3 install grub
cd /mnt/hd
chroot .
grub-install /dev/hda
After that, umount all partitions, reboot the server.

Clone Linux server with LVM2 partition

Jephe Wu - http://linuxtechres.blogspot.com


You might want to clone your production Linux server to another, but the production server is having LVM2 partition which is enabled by default installation.


1. My test environment is as follows:

Source:
Dell Latitude C510
CentOS 5
IDE 30G HDD
/dev/hda1 mounted as /boot
/dev/VolGroup00/LogVol00 mounted as /
/dev/VolGroup00/LogVol01 is swap partition

Destination:
Acer TravelMate 603TER
IDE 20G HDD

2. Cloning concept
a. use RIP(Recovery Is Possible) CD to boot up Acer laptop
b. make /boot normal partition and LVM2 partition
c. create physical volume, volume group and logical volumes
d. mount logical volumes then clone all files over from the source
e. modify /boot/initrd image file if the destination VG and LV names are different from the source ones, it's recommended to use the same name so that this step can be ignored
f. modify /etc/fstab and /etc/sysconfig/network-scripts/ifcfg-ethX etc
g. done

3. Cloning steps
a. download RIP CD from http://www.tux.org/pub/people/kent-robotti/looplinux/rip/
download smaller non-X version which is about 37M since we don't need X window for cloning

b. boot up the Acer laptop using RIP cd, choose boot up option 2 to skip keyboard map

c. login as root without password

d. Set up the ip address for Acer
# ifconfig eth0 192.168.0.3 up (assuming the source server IP is 192.168.0.2 and they are on the same network)

e. Make partitions for Acer
If the both the source and destination hard disks are the same size, you can just use the following command to clone the partition table over.
# ssh 192.168.0.2 'sfdisk -d /dev/hda' | sfdisk /dev/hda

In my case, I have to do it manually since my Acer HDD is smaller. So I created /dev/hda1(type:83) and /dev/hda2 as LVM(type: 8e)

f. Create logical volumes and make file systems on Acer
# pvcreate /dev/hda2 (initialize it)
# vgcreate VolGroup00 /dev/hda2 (use the same volume group name as the source so that you don't have to modify /boot/initrd image file later)
# vgdisplay -v (to find out the total PE numbers and available space to create logical volumes)
# lvcreate -l 4617 -n LogVol00 VolGroup00 (to create / partition )
# lvcreate -l 128 -n LogVol01 VolGroup00 (for swap partition inside LVM)

note : you don't have to create the same number of extend for each logical volume, you can specify any value, this setting is not fixed somewhere in source server, so you can still be able to startup destination server later.

# mkfs -t ext3 /dev/VolGroup00/LogVol00
# mkswap /dev/VolGroup00/LogVol01
# mkfs -t ext3 /dev/hda1 (for /boot)
# e2label /dev/hda1 /boot (for labelling /boot partition, to be the same as the source which is indicated in /etc/fstab)


g. Cloning over everything from the source
cd /mnt
mkdir hd
mount /dev/VolGroup00/LogVol00 hd
cd hd
mkdir boot proc sys ( boot is a separated partition, and proc and sys are empty folder for kernel memory information)
mount /dev/hda1 boot
ssh 192.168.0.2 'cd /; tar --exclude ./proc --exclude ./sys -cpf - .'| tar xvpf -

h. Make necessary changes for Acer
cd /mnt/hd;chroot .

vi /etc/fstab(optional) (since you are using the same volume group and logical volume names, so you can ignore this step, otherwise, change the names to the new ones)

vi /etc/grub.conf (optional) (since your are using the same volume group and logical volume names, so you can ignore this step, otherwise, change the all root= line to the new logical volume path)

vi /etc/sysconfig/network-scripts/ifcfg-ethx (to change IP address and comment out hardware address line)

i. Install grub for Acer
cd /mnt/hd
chroot .
grub-install hd0
exit

change necessary configuration such as /etc/sysconfig/networking-scripts/ifcfg-eth0 mac address

j. Finishing up
cd /mnt/hd
umount boot
cd /
umount /mnt/hd
reboot

Note:

1. if you forgot to do something, you can always reboot the destination server with RIP CD again, the following are the steps:
login as root after RIP bootup
sh /etc/rc.d/rc.lvm2 start (to activate all volume groups)
(if not, use 'vgchange -a y' to activate all volume groups
cd /mnt
mount /dev/VolGroup00/LogVol00 hd
cd hd
mount /dev/hda1 boot
chroot .
now you can do whatever forgotten changes

2. If you used the different volume group and logical volume names, you need to do some additional changes:

2.1 change /boot/initrd-2.6.18-8.el5.img before rebooting destination
cd /mnt
mount /dev/VolGroup00/LogVol00 hd
cd hd
mount /dev/hda1 boot
chroot .
cp /boot/initrd-2.6.18.el5.img /boot/intrd-2.6.18-el5.img.orig -va (backup it first)
cp /boot/initrd-2.6.18-8.el5.img /tmp/a.img
cd /tmp
zcat a.img > a
mkdir a.dir
cd a.dir
cpio -iv < ../a vi init (to change VolGroup00 to vg0 and LogVol00 to lv0 and LogVol01 to lv1 provided you used the names vg0, lv0 and lv1) find . | cpio -co > ../b
cd ..
gzip b (zip file b as b.gz)
cp b.gz /boot/initrd-2.6.18-8.el5.img
sync
exit
cd /
umount /mnt/hd/boot
umount /mnt/hd
reboot

2.2 change /etc/fstab to reflect the new volume group and logical volume names
2.3 change /etc/grub.conf root= line to use the new names





How to use ntfsresize from the command line

Jephe Wu http://linuxtechres.blogspot.com/


In order to resize Windows ntfs partition for whatever reason(e.g. shrink it for installing Linux in free space for multi boot), you should get ntfsresize tool. I recommend you download RIP(Recovery Is Possible) rescue CD(non-X version is enough).

The following is the step by step guide. Actually, the original article is from http://mlf.linux.rulez.org/mlf/ezaz/ntfsresize.html but I couldn't access it anymore, so I also copy it on my blog for easier access for other people.

I'm not responsible for any data loss it could cause.

1. get RIP CD, boot up your Windows server with it, you might want to choose the second GRUB options to skip keyboard map

2. check your Windows ntfs partition first


# fdisk -l /dev/hda

Disk /dev/hda: 255 heads, 63 sectors, 2480 cylinders
Units = cylinders of 16065 * 512 bytes

Device Boot    Start       End    Blocks   Id  System
/dev/hda1   *         1      2479  19912536    7  HPFS/NTFS    
Only one partition, it's /dev/hda1 and NTFS, also marked as bootable

3. Find out where you could resize

# ./ntfsresize --info /dev/hda1
ntfsresize v1.9.4
NTFS volume version: 3.1
Cluster size       : 4096 bytes
Current volume size: 20390432768 bytes (20391 MB)                  
Current device size: 20390436864 bytes (20391 MB)
Checking filesystem consistency ...
100.00 percent completed
Accounting clusters ...
Space in use       : 7851 MB (38.5%)
Collecting shrinkage constrains ...
Estimating smallest shrunken size supported ...
You might resize at 7850958848 bytes or 7851 MB (freeing 12540 MB).


So we could free over 12 GB disk space using NTFS.

4. Make an ntfsresize test run, using the --no-action option

# ./ntfsresize --no-action --size 11000M /dev/hda1
ntfsresize v1.9.4
NTFS volume version: 3.1
Cluster size       : 4096 bytes
Current volume size: 20390432768 bytes (20391 MB)                  
Current device size: 20390436864 bytes (20391 MB)
New volume size    : 10999996416 bytes (11000 MB)
Checking filesystem consistency ...
100.00 percent completed
Accounting clusters ...
Space in use       : 7851 MB (38.5%)
Collecting shrinkage constrains ...
Needed relocations : 265947 (1090 MB)
Schedule chkdsk NTFS consistency check at Windows boot time ...
Resetting $LogFile ... (this might take a while)
Relocating needed data ...
100.00 percent completed
Updating $BadClust file ...
Updating $Bitmap file ...
Updating Boot record ...

The read-only test run ended successfully.


Everything looks good, let's go on

5. Resize NTFS
#
# ./ntfsresize --size 11000M /dev/hda1
ntfsresize v1.9.4
NTFS volume version: 3.1
Cluster size       : 4096 bytes
Current volume size: 20390432768 bytes (20391 MB)
Current device size: 20390436864 bytes (20391 MB)
New volume size    : 10999996416 bytes (11000 MB)
Checking filesystem consistency ...
100.00 percent completed
Accounting clusters ...
Space in use       : 7851 MB (38.5%)
Collecting shrinkage constrains ...
Needed relocations : 265947 (1090 MB)
WARNING: Every sanity check passed and only the DANGEROUS operations left.
Please make sure all your important data had been backed up in case of an
unexpected failure!
Are you sure you want to proceed (y/[n])? y
Schedule chkdsk NTFS consistency check at Windows boot time ...
Resetting $LogFile ... (this might take a while)
Relocating needed data ...
100.00 percent completed
Updating $BadClust file ...
Updating $Bitmap file ...
Updating Boot record ...
Syncing device ...
NTFS had been successfully resized on device '/dev/hda1'.
You can go on to resize the device e.g. with 'fdisk'.
IMPORTANT: When recreating the partition, make sure you
1)  create it with the same starting disk cylinder
2)  create it with the same partition type (usually 7, HPFS/NTFS)
3)  do not make it smaller than the new NTFS filesystem size
4)  set the bootable flag for the partition if it existed before
Otherwise you may lose your data or can't boot your computer from the disk!


6. Optionally you could check your NTFS integrity
#
./ntfsresize --info --force /dev/hda1
ntfsresize v1.9.4
NTFS volume version: 3.1
Cluster size       : 4096 bytes
Current volume size: 10999992320 bytes (11000 MB)                  
Current device size: 20390436864 bytes (20391 MB)
Checking filesystem consistency ...
100.00 percent completed
Accounting clusters ...
Space in use       : 7851 MB (71.3%)
Collecting shrinkage constrains ...
Estimating smallest shrunken size supported ...
You might resize at 7850958848 bytes or 7851 MB (freeing 3149 MB).


Notice that the volume (NTFS) size indeed became 11000 MB but
the device (partition) size is still 20399 MB
7. Repartition the disk
  • do backup first


# sfdisk -d /dev/hda > hda.pt                # saves the partition table
# dd if=/dev/hda of=hda.mbr bs=512 count=1   # saves the MBR



  • Follow the steps below in order.
    • List the partition table. If you should see the correct partition types (NTFS),
    • Temporarily delete the entry for the 1st partition. This is an in-memory operation only.
    • Recreate the 1st partition entry at the same starting cylinder (sector) and using a larger size than above. This is very important because partitioning tools, fdisk included, may round down slightly the provided value to cylinder boundary. This would cause unbootable Windows. The theoretical maximum cylinder size currently is less than 140 MB hence this extra size should be always enough. We also use 11140 MB, it must be safe.
    • Set the partition type to NTFS (type 7).
    • Mark it bootable if it was also marked before.
    • Print the partition table again to check everything is right.
    • Commit the whole process by writing it to disk.
    # fdisk /dev/hda
    
    Command (m for help): p
    
    Disk /dev/hda: 255 heads, 63 sectors, 2480 cylinders
    Units = cylinders of 16065 * 512 bytes
    
    Device Boot    Start       End    Blocks   Id  System
    /dev/hda1   *         1      2479  19912536    7  HPFS/NTFS
    
    Command (m for help): d
    Partition number (1-4): 1
    
    Command (m for help): n
    Command action
    e   extended
    p   primary partition (1-4)
    p
    Partition number (1-4): 1
    First cylinder (1-2480, default 1): 1
    Last cylinder or +size or +sizeM or +sizeK (1-2480, default 2480): +11140M
    
    Command (m for help): t
    Partition number (1-4): 1
    Hex code (type L to list codes): 7
    Changed system type of partition 1 to 7 (HPFS/NTFS)
    
    Command (m for help): a
    Partition number (1-4): 1
    
    Command (m for help): p
    
    Disk /dev/hda: 255 heads, 63 sectors, 2480 cylinders
    Units = cylinders of 16065 * 512 bytes
    
    Device Boot    Start       End    Blocks   Id  System
    /dev/hda1   *         1      1355  10884006    7  HPFS/NTFS
    
    Command (m for help): w
    The partition table has been altered!
    
    Calling ioctl() to re-read partition table.
    Syncing disks.
  • Next step is also optional but strongly recommended. We check again, using ntfsresize, if we can still access our NTFS. For this purpose one must use both the --info and --force options again.
    # ./ntfsresize --info --force /dev/hda1
    ntfsresize v1.9.4
    NTFS volume version: 3.1
    Cluster size       : 4096 bytes
    Current volume size: 10999992320 bytes (11000 MB)                  
    Current device size: 11145222144 bytes (11146 MB)
    Checking filesystem consistency ...
    100.00 percent completed
    Accounting clusters ...
    Space in use       : 7851 MB (71.3%)
    Collecting shrinkage constrains ...
    Estimating smallest shrunken size supported ...
    You might resize at 7850958848 bytes or 7851 MB (freeing 3149 MB).

    Excellent, still no error.
If you got error, you might need to give a bit more partition size for above fdisk operation.
  • Reboot to Windows to check everything is right (e.g. pressing [Ctrl]-[Alt]-[Del]). Don't get scared when during the boot process Windows will check the filesystem. It was scheduled by ntfsresize for extra safety. If the partition is a system partition then Windows will restart automatically after the filesystem check.
Now you should have unallocated disk space that all Linux distribution installations must be able to handle.