Dual Booting Linux and Windows NT/2000/XP

There are a lot of HOWTO's out there on this topic. It's such an old topic you'd think it was already well covered. Unfortunately, while Linux marches onward, most of those docs are still stuck in the dark ages. Since I just successfully worked through this myself recently, after endless hours of googling for tips and advice, I've decided to set down the "clever" way to do it.

Pre-requisites

For this example, we assume you have two separate hard drives, one with Windows already installed, and one for Linux. In this example we will let Windows have the main control, and install Linux as an option in the Windows bootloader. We'll use lilo for the Linux booter because it has more support for Windows-oriented disk partitions. You'll need a recent version of lilo - at the time of this writing, 22.6.1 is the latest release. Check the lilo release page to make sure you have the latest.

First steps

We want to get things done in the right order, to save ourselves from too many reboots to get things going. Assuming you're already running in Windows at the moment, you might as well edit your boot.ini right now, before doing anything with Linux.

We assume that your Windows installation boots up on drive C. You'll need to edit C:\boot.ini to add a line for the Linux boot. Normally this file is marked system/read-only and doesn't show up in a directory listing or explorer views. You'll need to reset those attributes from the command line before you can edit it.

  C:> cd \
  C:> attrib -h -r -s boot.ini
  C:> notepad boot.ini
You can of course use your favorite text editor instead of notepad if you wish. All you have to do is add one line to the "[operating systems]" section in the file:
      C:\boot.lnx="Linux"
then save the file. You can restore the system/read-only attributes if you wish, I usually don't bother.

If your Windows boot partition is using NTFS, then you should create the boot.lnx file right now. While Linux can read NTFS partitions, it has very limited write capabilities. In particular, it can't create new files, or change the size of existing files. This file needs to be exactly 512 bytes (1 disk sector) long.

Unfortunately Windows doesn't have a 'dd' command to create files of a specific size for us, but there's a pretty simple way:

  C:> copy con: xx
  1234567812345678123456781234567812345678123456781234567812345678^Z
  C:> copy /b xx+xx+xx+xx+xx+xx+xx+xx boot.lnx
The first copy command takes input from the console (stdin, in Unix terms) and writes it to the file 'xx'. The data you're typing is just the sequence from 1 to 8, repeated 8 times, then Ctrl-Z, then <Enter>. This produces a file 'xx' that is exactly 64 bytes long. The second copy command concatenates 'xx' with itself 8 times, resulting in a 'boot.lnx' file exactly 512 bytes long. The data is pure garbage of course, so don't try to use this boot menu option yet.

Installing Linux

You should have boot media for Linux already on floppy, CD, or DVD-ROM, and you should have your BIOS options set to boot from that media before trying to boot from the hard drive. (Or if your BIOS has a simple boot selector menu, just use that.) Leave Windows with the Restart option to reboot the machine, and start up your Linux installer.

So let's assume that Linux sees your first disk as /dev/hda and your second disk as /dev/hdb. Windows lives on /dev/hda, and you've set up a Linux boot partition on /dev/hdb1. (If you didn't create a dedicated boot partition, then your root partition is also the boot partition.) When partitioning the disk, make sure that your boot partition is Activated in the partition table. (Use the 'a' command in fdisk...)

When you get to setting up the Linux boot loader, you need to make sure that it installs itself in the boot partition's boot sector, not the master boot record (MBR) of the disk. You can use either LILO or GRUB for the actual bootloader, but we'll be using some special features of LILO regardless. For lilo you would set "boot=/dev/hdb1" in /etc/lilo.conf for this example.

If your Windows boot partition uses NTFS, you need to make sure your Linux distro has the newest NTFS driver if you want to be able to write directly to the boot partition. See the Linux-NTFS Status page for information on which distros have the new driver. You will probably need to install the kernel sources and all the tools to build the kernel, because the driver doesn't allow write access by default. You will have to explicitly enable that configuration flag and recompile the NTFS driver module.

The Linux install will probably need to reboot the machine before it can finish. You'll probably need to keep booting off your install media to accomplish the subsequent reboot, until we finish this procedure. If you get the opportunity to make a set of rescue disks during the install, do so. Or if your media already comes with a Rescue setup, just use that.

Grabbing the Boot sector

A lot of the HOWTOs out there would tell you at this point to use dd to make a copy of the LILO boot sector and copy it over to the Windows partition. But that's a big drag, because every time you need to rerun LILO you will also have to copy this boot sector over to Windows again. If you forget, the next time you update your kernel you won't be able to access Linux from the Windows bootloader.

There's a better way. Leave the LILO boot sector where it is. We'll use one of LILO's newer features to finish up. Using the '-M' option we can create a Master boot sector that we only need to copy once, and never need to update again, no matter what other changes lilo makes. Here's what you do:

  $ dd if=/dev/hdb of=mbr.save bs=512 count=1
  $ lilo -M /dev/hdb
  $ dd if=/dev/hdb of=/boot/boot.lnx bs=512 count=1
  $ dd if=mbr.save of=/dev/hdb bs=512
We save the existing MBR of the disk, let lilo install a new MBR, copy that off, and then restore the original MBR of the disk. Saving/restoring aren't too crucial for a regular disk (what Windows refers to as a "Basic" disk) but if you have other exotic partition information there (e.g., the disk is a Windows Dynamic Volume) you need to restore it.

Installing the Boot sector

Now, assuming your kernel has support for your Windows filesystems, you can just mount the Windows boot partition and copy this boot.lnx file into place. If your boot partition is FAT/FAT32, then just mount, copy, unmount, and you're done:
  $ mount /dev/hda1 /mnt
  $ cp /boot/boot.lnx /mnt
  $ umount /mnt
If your boot partition is NTFS, things are a bit trickier because Linux doesn't let you write to NTFS by default. You might want to just copy the boot.lnx file to a FAT-formatted floppy disk, that's easy enough. If you have the new NTFS driver and it has writes enabled, you still can't just copy the file into place. The standard Unix file utilities always open their output files in truncate mode, and the NTFS driver doesn't support altering the length of a file. You want something that opens the file in plain Write mode. Since I couldn't find any existing utility (cat, cp, dd are all unsuitable) I wrote one, it's quite simple:
/* dup.c - copy a boot sector from input path to output path */
#include <errno.h>
#include <fcntl.h>

char buf[512];

main(int argc, char *argv[]) {
        int fdin, fdout;
        int i;

        fdin = open(argv[1], O_RDONLY);
        if (fdin < 0) {
                perror("open in");
                exit(1);
        }
        fdout = open(argv[2], O_WRONLY);
        if (fdout < 0) {
                perror("open out");
                exit(1);
        }
        i = read(fdin, buf, sizeof(buf));
        i = write(fdout, buf, sizeof(buf));
        if ( i != sizeof(buf)) {
                perror("write");
                exit(1);
        }
        return 0;
}
Compile this and run it with two arguments: first, the pathname of the input file, and second, the pathname of the output file.
  $ gcc -o dup dup.c
  $ mount /dev/hda1 /mnt
  $ ./dup /boot/boot.lnx /mnt/boot.lnx
  $ umount /mnt

Finished...

OK, that's it. Reboot the machine. You should see the Windows bootloader giving you a menu of choices including "Linux." Select that, and you should see lilo (or grub) come up after that, ready to start your Linux system running.

More info

Read the lilo(8) manpage for more details. The default MBR that "lilo -M" installs will search for an active partition on the target disk and load that partition's boot sector. If you have multiple operating systems on one disk, then this approach isn't going to work for you. Instead, you should have a look at BootPart which can create boot sectors to boot any partition you want. In fact, you might just want to grab that anyway, and forget about using lilo -M. I haven't used this program myself yet, but it looks like it does everything you'd need to add arbitrary boot targets to the Windows bootloader.

Let me know if you have any questions, problems or suggestions. hyc -- 2005-02-17

back