Clone Windows partition from Linux

07
2014-07
  • Fault

    So I've got a 120 GB Intel SSD, with a partition for Windows and another for Ubuntu. Ubuntu is my main OS, Windows is for gaming. But now I'm looking to install Mac OS X, and I'd like it to be on the SSD, so I'm looking to move Windows to a different drive (old 160GB external that I took out of it's shell and have been using as a test drive. I keep my games on another external anyways, so performance shouldn't be affected, other than start up time).

    What's the best way to go about this? Any good tools for cloning partitions? I ask this because Google is turning up a lot of results on cloning the drive you are actually using/have Ubuntu installed on, rather than telling me how to clone a totally unrelated partition to another unrelated location.

    Oh, and will the new Windows partition let me run it without any prior post-clone tweaking? Any other information regarding this would be much appreciated.

    (I'm doing this because I need to get my paws on XCode, and my MacBook Pro is slowly dying).

  • Answers
  • XXL

    You will need to clone 2 partitions with dd - one is where the bootloader/bootmanager resides (needed in order to chainload the OS) [System Reserved, usually 100M] and the other one being the actual W7 installation.

    Check the partition table with cfdisk - it will give you a visual representation. Then delete all partitions on the target drive - cfdisk is your friend.

    The syntax for cloning can be found on wiki here. You will also need a proper MBR (it is probably already present on your test drive).

    You will probably need to assign a bootable flag to the [System Reserved] partition as well (that should be the first one cloned) - cfdisk can accomplish that.

    If that fails - simply boot from a W7 installation disc and follow the guidelines here for Vista.

    UPDATE:

    Forgot to mention one important part of the whole process that might not be so evident. You will either have to clone the partition table off the original drive and delete everything but the 2 Windows-related partitions OR recreate them with cfdisk / parted with the same size.

    Here's a few examples (assuming, that sda is your source drive and sdb is the target):

    dd if=/dev/sda bs=1 skip=446 count=66 of=/dev/sdb seek=446 (this will effectively clone your current DOS partition table along with the MBR signature to the output drive)

    dd if=/dev/sda bs=1 skip=440 count=72 of=/dev/sdb seek=440 (this will also copy the disk ID which can sometimes result in a failed boot if missing - however, such disks will not be able to work together on a Windows environment, until the ID is changed)

    parted /dev/sda u s p (this is how you can inspect the current partition table and size in sectors on the source drive for later replication on the target with either cfdisk or parted itself)

  • sehe

    Have a look at

    • ntfsclone (copies only sectors in use)
    • fixntfs.c to fix up the boot info offsets

    IIRC, Trinity Rescue Kit contains the necessary software as well as many others (ssh, partimage, fdisk, fdisk, cfdisk, parted, gparted, testdisk, ntfsfix; ntfs-3g mounting, rsync etc. etc.) .

    /*
     * fixntfs: change some attributes of an NTFS bootsector
     *
     * brought to you by Phoenix
     * url: www.grhack.gr/phoenix
     * mail: [email protected]
     * irc: phoenix -> #grhack -> undernet
     */
    
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <stdlib.h>
    
    int main(int argc, char **argv)
    {
        FILE *fd;
        FILE *idfd;
        struct stat fst;
        unsigned char cab[32];
        unsigned short log_heads;
        unsigned short ntfs_heads;
        unsigned short force_heads;
        unsigned short ntfs_cab;
        unsigned long long sectors;
        unsigned long long new_sectors;
    
        log_heads = 0;
        ntfs_heads = 0;
        force_heads = 0;
        ntfs_cab = 0;
    
        if(argc < 2)
        {
            fprintf(stderr, "Usage:\n\t%s <device> <total_sectors> <force_heads>\n", argv[0]);
            exit(0);
        }
    
        fprintf(stderr, "Stating file %s... ", argv[1]);
    
        stat(argv[1], &fst);
    
        if(!S_ISBLK(fst.st_mode))
        {
            fprintf(stderr, "not a block device\n");
            exit(-1);
        }
    
        fprintf(stderr, "a block device\n");
    
    
        fprintf(stderr, "Opening device %s rw... ", argv[1]);
    
        fd = fopen(argv[1], "r+");
    
        if(!fd)
        {
            perror("open device");
            exit(-1);
        }
    
        fprintf(stderr, "ok\n");
    
    
        fprintf(stderr, "Checking partition... ");
    
        fseek(fd, 3, SEEK_SET);
    
        if(fread(cab, 1, 4, fd) != 4)
        {
            perror("read system_id");
            exit(-1);
        }
    
        cab[5] = 0;
    
        if(strncmp(cab, "NTFS", 4))
        {
            fprintf(stderr, "%s\n", cab);
            exit(-1);
        }
    
        fprintf(stderr, "%s\n", cab);
    
    
        fprintf(stderr, "Reading NTFS bootsector heads... ");
    
        fseek(fd, 0x1a, SEEK_SET);
    
        ntfs_heads = 0;
    
        fread(&ntfs_heads, 1, 2, fd);
    
        fprintf(stderr, "%u\n", ntfs_heads);
    
    
        fprintf(stderr, "Reading NTFS bootsector sectors... ");
    
        fseek(fd, 0x18, SEEK_SET);
    
        ntfs_cab = 0;
    
        fread(&ntfs_cab, 1, 2, fd);
    
        fprintf(stderr, "%u\n", ntfs_cab);
    
    
        fprintf(stderr, "Reading NTFS bootsector sectors_per_cluster... ");
    
        fseek(fd, 0x0d, SEEK_SET);
    
        ntfs_cab = 0;
    
        fread(&ntfs_cab, 1, 1, fd);
    
        fprintf(stderr, "%u\n", ntfs_cab);
    
    
        fprintf(stderr, "Reading NTFS bootsector sectors_size... ");
    
        fseek(fd, 0x0b, SEEK_SET);
    
        ntfs_cab = 0;
    
        fread(&ntfs_cab, 1, 2, fd);
    
        fprintf(stderr, "%u\n", ntfs_cab);
    
    
        fprintf(stderr, "Reading NTFS bootsector boot_loader_routine_jump... ");
    
        fseek(fd, 0, SEEK_SET);
    
        bzero(cab, sizeof(cab));
    
        fread(cab, 1, 3, fd);
    
        fprintf(stderr, "0x%x 0x%x 0x%x\n", cab[0], cab[1], cab[2]);
    
        fprintf(stderr, "Reading NTFS bootsector total_sectors... ");
    
        fseek(fd, 0x28, SEEK_SET);
    
        sectors = 0;
    
        fread(&sectors, 1, 8, fd);
    
        fprintf(stderr, "%Lu\n", sectors);
    
    
        fprintf(stderr, "Reading device logical heads... ");
    
        sprintf(cab, "/proc/ide/hd%c/geometry", *(strrchr(argv[1],'/') + 3));
    
        idfd = fopen(cab, "r");
    
        if(!idfd)
        {
            perror(cab);
            exit(-1);
        }
    
        fscanf(idfd, "%*s %*s\n");
    
        fscanf(idfd, "%*s %s\n", cab);
    
        *(strrchr(cab, '/')) = 0;
    
        log_heads = (unsigned short) atoi(strchr(cab, '/') + 1);
    
        fprintf(stderr, "%u\n", log_heads);
    
        if(argc == 4)
        {
            force_heads=atoi(argv[3]);
            fprintf(stderr, "Forcing heads to %u\n", force_heads);
            log_heads=force_heads;
        }
    
        if(fclose(fd) == EOF)
        {
            perror("close device");
            exit(-1);
        }
    
        if(log_heads != ntfs_heads)
        {
            fprintf(stderr, "Heads are different... Logical=%u NTFS=%u\n\n"
                    "Update NTFS bootsector? (y/n) ",
                    log_heads, ntfs_heads);
    
            if(getc(stdin) == 'y')
            {
                fd = fopen(argv[1], "r+");
    
                if(!fd)
                {
                    perror("open device");
                    exit(-1);
                }
    
                ntfs_heads = log_heads;
    
                fseek(fd, 0x1a, SEEK_SET);
    
                fwrite(&ntfs_heads, 1, 2, fd);
    
    
                fprintf(stderr, "\nBootsector updated... Verifying... ");
    
                fclose(fd);
    
                fd = fopen(argv[1], "r");
    
                if(!fd)
                {
                    perror("open device");
                    exit(-1);
                }
    
                fseek(fd, 0x1a, SEEK_SET);
    
                ntfs_heads = 0;
    
                fread(&ntfs_heads, 1, 2, fd);
    
                if(ntfs_heads == log_heads)
                {
                    fprintf(stderr, "ok\n\n");
                }
                else
                {
                    fprintf(stderr, "error [%u]\n", ntfs_heads);
                    exit(-1);
                }
                fclose(fd);
            }
            else
            {
                fprintf(stderr, "\nHeads update cancelled...\n");
            }
    
            getc(stdin);
        }
    
        if(argc >= 3 && atoll(argv[2]))
        {
            fprintf(stderr, "Update NTFS bootsector total_sectors from %Lu to %Lu? (y/n) ",
                    sectors, atoll(argv[2]));
    
            if(getc(stdin) == 'y')
            {
                fd = fopen(argv[1], "r+");
    
                if(!fd)
                {
                    perror("open device");
                    exit(-1);
                }
    
                new_sectors = atoll(argv[2]);
    
                fseek(fd, 0x28, SEEK_SET);
    
                fwrite(&new_sectors, 1, 8, fd);
    
    
                fprintf(stderr, "\nBootsector updated... Verifying... ");
    
                fclose(fd);
    
                fd = fopen(argv[1], "r");
    
                if(!fd)
                {
                    perror("open device");
                    exit(-1);
                }
    
                fseek(fd, 0x28, SEEK_SET);
    
                sectors = 0;
    
                fread(&sectors, 1, 8, fd);
    
                if(sectors == new_sectors)
                {
                    fprintf(stderr, "ok\n\n");
                }
                else
                {
                    fprintf(stderr, "error [%Lu]\n", sectors);
                    exit(-1);
                }
    
                fclose(fd);
            }
            else
            {
                fprintf(stderr, "\nTotal_sectors update cancelled...\n");
            }
            getc(stdin);
        }
    
        return(1);
    }
    
  • Chris K

    This how-to for cloning a Windows drive worked splendidly for me. Since this is the first time I've been able to transfer a Windows install to a new hard drive, I'll share my procedure here to help the next Googler to visit.

    My situation:
    Manager's Windows 7 x64 had maxed out its 128G SSD, so I bought a 240 GB replacement.

    Problem:
    I have two SATA drive docks but linux didn't recognize both at the same time, preventing an easy copy between them.

    Hardware:
    I am about to set up a dual NIC firewall, so I installed the source SSD in this computer. Destination 240G SSD went into external dock.

    Process:
    1) The first USB stick I pick up had Linux Mint live CD on it, which became /dev/sda1
    2) "Old" 128G SSD was detected and became /dev/sdb1 and /dev/sdb2
    3) Used # fdisk -l /dev/sdb from the tutorial and copied the source partition window's information to Gedit.
    -- Note, the tutorial includes the -u option, however for me, fdisk was already displaying blocks (the desired output) so including that switch gives the wrong information.
    4) Plug in and turn on the drive dock with destination 240G SSD, which becomes /dev/sdc.
    5) Use fdisk /dev/sdc to create partitions on /dev/sdc that exactly match /dev/sdb, including boot and system flags.
    6) dd if=/dev/sdb of=/dev/sda bs=446 count=1 to copy the MBR to destination drive.
    -- The guide now suggests using hdparm to turn on DMA, but the command failed for me
    7) ntfsclone -O /dev/sdc1 /dev/sdb1 to copy the windows hidden system partition.
    -- -O or --overwrite option is used to set the destination, making the command appear backwards. Kudos to Linux Mint live CD having ntfsclone, as I hadn't heard of this command before & I didn't have to get on the network.
    8) Use ntfsclone -O /dev/sdc2 /dev/sdb2 to copy the windows "C Drive". This took a few beers to complete.
    9) For resizing the partition, I used gparted
    10) Reinstalled new SSD in windows computer and it runs checkdisk (I had left the tutorial & didn't notice he does this).
    11) Rebooted Windows and all is back to normal but with more free space.


  • Related Question

    partitioning - What is the best way to clone a Linux partition onto a smaller partition?
  • thecoop

    I've currently got an 80 GB RAID 0 (yes, I know, that's why I'm changing it...) ext3 Linux device that's about 80% free, and I want to change it to a 40 GB ext4 partition using one of the previous RAID 0 partitions. I've got enough spare space on another large partition for all the files, and the best method I thought of doing the switch is:

    1. cp -a all files on the filesystem to a directory under a large partition
    2. Repartition
    3. cp -a all files back to a new partition

    The thing is, I'm worried about special file properties (it's a Linux root device); will cp -a keep all the necessary file properties so the new partition is bootable afterwards? Am I missing another way of doing it?


  • Related Answers
  • David Spillett

    I would:

    1. resize the filesystem in-place with something like resize2fs -p /dev/<device_name_fs_is_on> 20G
      It will undoubtedly refuse to run first time, suggesting that you run fsck first. You can force it to run, but the fsck operation is strongly recommended as trying to resize a filesystem with errors (even minor ones) could lead to disaster. Rerun the resize command once the check is completed
    2. copy it to the other drive with dd if=/dev/<device_name_fs_is_on> of=/path/to/other/location/filesystem.img bs=1048576 count=20480
    3. reformat the disks as needed
    4. copy the filesystem back with dd if=/path/to/other/location/filesystem.img of=/dev/<new_device_name>
    5. resize up to fill the new partition with resize2fs -p /dev/<new_device_name>
    6. mount the newly resized filesystem and edit any relevant config it contains, like /etc/fstab
    7. you will also need to check your grub configuration to make sure it refers to the new root partition name and you may need to rebuild your initrd (though probably not as you are going from RAID to normal not the other way around which causes problems if the initrd is not RAID aware)
    8. cross your fingers and reboot...

    As this is your root filesystem, you are going to need to do this from a live CD as you will not be able to resize the filesystem (step 2) while it is mounted.

    If you change the 20G passed to resize2fs in step 1, make sure you change the 1048576x20480 passed to dd in step 2 accordingly.

    Obviously this is not a risk free operation, so you might want to separately backup important data+configuration on the filesystem by other means before step 3.
    For even greater safety: if you have time and a disk to spare, restore the shrunk filesystem to the extra disk, reconfigure appropriately as per steps 6 & 7, and make sure you can boot off that before progressing to step 3. This way you know you have a fully working copy of the filesystem elsewhere before wiping it from the old location, and can easily revert back to the old setup and abort/retry if you discover problems at that stage.

    This way you are not going to lose any file/directory/device properties while copying stuff around as you are operating on the filesystem wholesale rather than individual files, dirs and device nodes.

  • Neal

    I've never used it but there is a partition imaging program, called partimage which is available on System Rescue CD.

    I'm not sure what will happen if you use partimage, but if you use cp (be careful not to try to copy the directory you are copying to when you do your copy it may need cp -ax) you will have to reinstall Grub, as it hard codes physical hard disk locations which will change during the copy.