Restoring a system backup made on another hardware

Backup restore as an interesting alternative to fresh install

I wanted to have the system installed and configured on my new laptop promptly, so I decided not to install the system from fresh, but restore my Debian lenny backup made on another hardware instead. This procedure saved me some time, but it necessitated to reconfigure the restored backups slightly so that the new system was able to boot up correctly. This approach was possible and quick because the original system contained almost no hardware-dependent configuration, most drivers were loaded automatically and their settings were autodetected. The processor architectures weren't surprisingly the same, but they were compatible. Whereas the backup was made on the i386 Debian architecture, the new laptop hardware was a 64-bit Intel processor with the EM64T support and thus designed for a 64-bit operating system as is the amd64 port on Debian. I plan to reinstall the system to the amd64 port in the future, but I wanted to try this interesting approach.

Backing up with tar

The first important question is how the backup was made. I used the GNU version of the traditional Unix/Linux tar archiving tool whose main advantage is that it's so heavily used that it should be part of any Linux distribution or its live version. Even if its name is derived from the phrase tape archiver, it is of course able to perform a backup on another medium such as a usual hard disk drive and the resultant archive is of sufficient quality if proper arguments are given. At least on the standard Linux ext2 based file systems (ext2, ext3, ext4).

GNU/tar is capable of archiving all Linux file types except unix domain sockets. However, the file of this type normally exists only if the process that created it is running unless the process exits abnormally. Hence, this isn't an issue. All other file types including regular files, directories, symbolic links, named pipes and character and block devices are stored. All important information about these file types is contained in the archive: filenames, permissions, owner, group, modification time, symlink target and major and minor device numbers. Hard links are stored and extracted properly too if they're put into the same archive. GNU/tar can also handle sparse files efficiently, but only if the --sparse option is given. However, the special lost+found directory in a root directory of an ext2 based file system is treated as a usual directory and should therefore be excluded from the backup by using the --exclude option.

Furthermore, GNU/tar can create incremental backups if it is called with the -g switch. It uses ctime (time of last modification of file status information) to determine whether the file or its status has changed. This is a second best choice after using hashes because if the file contents changes, the mtime (modification time) and therefore also the ctime change. However, if the file is only accessed and atime (access time) is modified, ctime remains unaltered. If only the file status changes (permissions, owner, group etc.), ctime is also updated. The only drawback of this method is that it is necessary to maintain correct system time. It is not possible to modify ctime arbitrarily by using some command, but if the system time changes, later modifications may occur as older ones and the incremental backup will unfortunately not contain such files. You should also take into account that the -g switch requires an argument denoting the path to so called snapshot archive. The snapshot archive contains additional information that is necessary to determine whether a file should be included into the incremental archive or not. More precisely, the snapshot archive contains the list of all files and their ctimes.

I performed the system backup from a live-CD distribution to ensure the consistency of the data. Otherwise if the system had been running, there could have been a problem with backing up files that were currently undergoing change. This situation might have been avoided by using file systems on top of LVM where a special snapshot volume could have been created that could have held the frozen file system, but my original system wasn't using LVM. I also used an external USB hard disk to store the backups so that a disk failure wouldn't have destroyed them. The backup wasn't made only once for the purpose of moving the system, but it was used long before to protect my data.

Let's now look at the exact commands used to back up the system. I ran my own script from the live-CD and the script called basically the following commands for each file system:

  • mount /dev/sdaX /mnt/misc
  • df -h | grep '/mnt/misc$' >> df.log
  • tar -cvvzf archive.0.tgz -g archive.0.snar -C /mnt/misc --sparse --numeric-owner --exclude=./lost+found
  • umount /mnt/misc

I backed up my system incrementally always after a certain period of time and I therefore created several archives for each file system. The number 0 before the tgz or snar suffix in the example above denotes the current level of the incremental archive and was increased by one for each backup.

The --numeric-owner tar option is important if extracting the archive on another system than that which will be used later. The reason is that GNU/tar stores both the user names/group names and UIDs/GIDs by default when creating an archive. But when extracting, it firstly tries to match the user names and group names and if the match is found, it assigns the files these user or group names even if they have different UIDs/GIDs. Only if no matching names are found, the UIDs and GIDs are used. This UID/GID renumbering could have been a problem in my case and I therefore forced GNU/tar to store and use only UIDs/GIDs irrespective of names.

The -C argument tells tar to change the directory to the directory specified before starting the backup.

Creating partitions and file systems

The backups were already available and I therefore started with creating partitions and file systems on the new notebook in order to restore the backups. It's very good to know the sizes of the file systems on the original machine so that new file systems of sufficient sizes with enough free space to operate further may be created. The sizes of mounted file systems can be obtained by running the df command, it's advisable to store its output together with the archives.

Note that all the operations from this point of this subsection until the subsection Booting up were performed from a live-CD. Such a live-CD must contain the essential system utilities such as fdisk, mke2fs, lilo, mount, tar, chroot etc. I used the Debian Live Rescue CD.

I firstly booted from the live-CD and then ran:

  • fdisk /dev/sda

Then I switched off the DOS compatibility mode (command c) and changed display units to sectors (command u). The default CHS (cylinder/head/sector) addressing scheme is deprecated for new hard disks because it doesn't correspond to the real disk geometry. Constant number of sectors per track isn't used any more.

I deleted the existing partitions (command d) and started to create new ones (command n). I always checked that the start of each partition equals to such number of sectors that is divisible by 8. The reason is that my hard disk might use Advanced format according to the information I received with my laptop which means that the internal sector size might be larger than 512 bytes. Most often 4KB. This technology is also called 512e. Even if my hard disk should use 512 bytes per sector according to the specification I found on the web, I decided to check the alignment of the partitions to 4K as described in the 7007193 document on the Suse home page referenced by the documentation I received with my notebook. The partition types were correctly set to Linux (id 83) and I therefore didn't have to change them (command t) except the swap partition type which I had to change to Linux swap (id 82).

After the partitions were ready to use, I created the file systems by using:

  • mkfs.ext3 -b 4096 -L boot /dev/sda1
  • mkfs.ext3 -b 4096 -L root /dev/sda2
    ...

I used the -b argument because in some cases, the block size was autodetected as only 1024B and I wanted to optimize the file systems for the potential 4K sector size.

Finally, I created the swap space by running

  • mkswap -L swap /dev/sda3
Restoring the backups

After the file systems were prepared, I was able to start with restoring the backups made on the other machine. This was simply done by calling the following sequence for each file system backup:

  • mount /dev/sdaX /mnt/misc
  • tar -xvvzf archive.0.tgz -g /dev/null -C /mnt/misc
  • tar -xvvzf archive.1.tgz -g /dev/null -C /mnt/misc
    ...
  • umount /mnt/misc

There're more tar commands in the example above because more incremental archives of the same file system were extracted.

The strange tar argument -g /dev/null indicates that an incremental archive was extracted, but the snapshot archive isn't needed for extracting because each archive contains the list of all files belonging to it whether they have changed or not. Of course that the file status information and file contents are included only for the files that have changed.

Mounting the file systems and running chroot

The next step comprised mounting of all the restored file systems into the same directory tree into which they were originally structured. The chroot command could have been then simply run on the directory where the directory tree in question was mounted. This wasn't needed for most of the subsequent operations, but it was necessary for updating initrd.

I, for instance, called the following command sequence:

  • mkdir /mnt/root
  • mount /dev/sda2 /mnt/root
  • mount /dev/sda1 /mnt/root/boot
    ...
  • mount -t proc proc /mnt/root/proc
  • mount -t sysfs sysfs /mnt/root/sys
  • chroot /mnt/root
Modifications of hardware-dependent information

I decided to restore a backup made on another hardware in spite of the fact that the processor architectures weren't the same but compatible only. The backup was made on the i386 Debian architecture whereas the new processor was designed for a 64-bit operating system such as is Debian on the amd64 port. The original system didn't fortunately contain much of a hardware-dependent configuration. Most of the drivers were loaded automatically as kernel modules or were already built into the stock kernel. Also Xorg was set up in the default way which means that its drivers were autodetected. I had to customize only the following settings.

Firstly the cpufreq kernel modules that were loaded during system startup and were specified in the file /etc/modules. I removed the p4-clockmod cpufreq kernel driver and also the cpufreq-userspace governor, i.e. the lines:

  • p4_clockmod
  • cpufreq_userspace

The new processor cpufreq driver was acpi-cpufreq and I wanted to use different governor. That's why I edited also the System V init symbolic links in the /etc/rc?.d directories for the powernowd daemon which was used for userspace frequency scaling so that it wasn't run at system startup. This could have been done by manually removing the S??powernowd symbolic links from the /etc/rc?.d directories and replacing them by K??powernowd symbolic links for the affected runlevels or by using the update-rc.d executable with appropriate parameters in the chroot environment:

  • update-rc.d -f powernowd remove
  • update-rc.d powernowd stop 20 0 1 2 3 4 5 6 .

The original system used an external USB wireless card as a default network adapter and it was possiblle to plug this card into the USB port of the new laptop easily. Hence, there wasn't a problem with this piece of hardware. But I wasn't sure if the card gets the same name on the new machine because there was another wireless adapter present. I therefore decided to remove the following line from the configuration file /etc/network/interfaces so that this adapter wasn't brought up during system boot.

  • auto wlan0

The other devices such as the hard disk, CD-ROM drive, graphics card, sound card and Ethernet adapter didn't use any hardware-specific configuration. I checked the configuration of hdparm in /etc/hdparm.conf and Xorg in /etc/X11/xorg.conf and no updates were necessary.

However, there was a need to reconfigure the file /etc/fstab on my machine because the original computer contained PATA disks whereas the new laptop used the newer SATA disk drives. These drives were accessible by different devices on the 2.6.26 kernel that is the default kernel in Debian lenny (current oldstable release at the time of this writing) and my backed up system was the lenny release. I therefore had to change the hda device strings to sda as in the following example:

  • /dev/sda2 / ext3 defaults,errors=remount-ro 0 1
  • /dev/sda1 /boot ext3 defaults,errors=remount-ro,nodev,nosuid,noexec 0 1
    ...

This renaming could have been avoided if the file /etc/fstab used UUIDs instead of device names to identify file systems, but my older system didn't use UUIDs yet.

Because my system was using lilo to boot up, I had to update lilo.conf as well. I had to update two lines only on my system:

  • boot=/dev/sda
    ...
  • root=/dev/sda2

I was using the uswsusp package for suspend and I therefore modified the device names in the same way in the file /etc/uswsusp.conf, specifically the line:

  • resume device = /etc/sda3

And I also had to update the file /etc/initramfs-tools/conf.d/resume so that the system was able to resume after hibernation. The contents of this file after the update was:

  • RESUME=/dev/sda3

The last change required updating initrd to take effect. This procedure is described in the next subsection.

Updating initrd

It was necessary to update initrd after the change of the configuration file /etc/initramfs-tools/conf.d/resume. This update could have been done by invoking the update-initramfs tool, but this executable had to be used in an environment prepared by running chroot on a directory into which the whole directory tree with the restored file systems was mounted. Otherwise the command in question couldn't have been able to gather all the necessary information and have produced a working initrd image. Such a chroot environment was already prepared in one of the previous subsections.

One more file had to be edited before generating the initrd image, the configuration file /etc/initramfs-tools/initramfs.conf. I had to change the value of its MODULES variable from dep to most:

  • MODULES=most

Without this change, the command update-initramfs generated a warning that the system might become unbootable and that setting MODULES to most should help. This situation occurred only when I updated initrd in the chroot environment using the live-CD. The warning disappeared when I used update-initramfs on the restored system later.

Finally, the update-initramfs tool was run. When not updating, but deleting and re-creating initrd from scratch, lilo wasn't run. It had to be installed separately anyway. That's why I used this command sequence.

  • update-initramfs -k all -d
  • update-initramfs -k 2.6.26-2-686 -c
Installing lilo

To make the system bootable, not only initrd had to be updated, but also a boot loader had to be installed on the newly created system. I was using lilo to boot up my original computer and I therefore set it up again.

Unlike grub, lilo doesn't understand file systems, it has to store exact offsets of the initrd and kernel images on the disk so that it is able to load them using BIOS during the initial phase of the boot process. That's why lilo has to be updated everytime when the initrd image file or kernel file change. It wasn't a problem to run lilo from the live-CD, but it wasn't possible to run it in the chroot environment because the udev daemon wasn't running above these file systems and the /dev directory didn't contain correct devices. This meant that it had to be run on the root file system created by the live-CD where the proper devices existed. However, the lilo configuration file /etc/lilo.conf (or /mnt/root/etc/lilo.conf outside the chroot environment if the restored root file system was mounted to /mnt/root) couldn't have been used in the same form which it had on the restored file system. The reason was that the paths used in the configuration file didn't point to existing files on the live-CD directory tree. Because the path strings were not important, the information used by lilo are the disk offsets, it was possible to create a temporary configuration file using the path strings pointing to correct files. I therefore created such temporary file /mnt/root/tmp/lilo.conf:

  • cp /mnt/root/etc/lilo.conf /mnt/root/tmp

And I changed the following lines in this file:

  • map=/mnt/root/boot/map
    ...
  • image=/mnt/root/vmlinuz
    ...
  • initrd=/mnt/root/initrd

The last step was running lilo. The temporary configuration file mentioned above should have been passed to it as an argument to the -C option.

  • lilo -C /mnt/root/tmp/lilo.conf

A warning was displayed that initrd was too big to fit between the kernel and the 15M-16M memory hole and that it was assumed that the BIOS supports memory moves above 16M. Naturally, my new BIOS supported this feature.

Booting up

After the initrd image and the boot loader were installed properly, it was possible to restart the system from the disk. So I shutted down the live-CD operating system and removed the CD from the tray. Lilo started, loaded the kernel and the initrd image from the disk, decompressed them, mounted initrd as a temporary root file system and ran the kernel. However, it wasn't able to mount the real root file system and stopped. The problem was that AHCI was set as the SATA mode in the BIOS. This behaviour was unexpected because the ahci kernel module was present in the generated initrd, I checked this by booting from the live-CD again and invoking the following commands:

  • mkdir /mnt/boot
  • mount /dev/sda1 /mnt/boot
  • mkdir /tmp/initrd
  • cd /tmp/initrd
  • cat /mnt/boot/initrd.img-2.6.26-2-686 | gunzip - | cpio -i
  • find . -name 'ahci.ko'

But after I changed the SATA mode settings in the BIOS from AHCI (SATA) to IDE (PATA), the system booted up correctly. So I didn't use the live-CD any more and continued working on the restored system.

I wanted to use the newer AHCI mode, of course. I tried what can be done and I found out that if I set the MODULES settings back to dep in the file /etc/initramfs-tools/initramfs.conf

  • MODULES=dep
and if I add the ahci kernel module to the file /etc/initramfs-tools/modules,
  • ahci

I can change the SATA mode in BIOS back to AHCI and the system is able to boot. Of course, I had to regenerate initrd after the changes to the configuration files by running update-initramfs before I restarted the system and changed the BIOS settings:

  • update-initramfs -k all -u

I didn't have to invoke the lilo command because it was run automatically by update-initramfs when using the -u argument, i.e. the update mode.

The system booted up correctly this time and the AHCI mode was used for accessing hard disk and also DVD-RW drive. It was possible to remove the ahci module from the file /etc/initramfs-tools/modules again, because it will be autodetected next time if it is used. I therefore created the following comment in the file in question.

  • # ahci
Kernel upgrade and additional installations

The Debian lenny system backup was already successfully restored on my HP ProBook 4520s laptop as decribed in the preceding subsections. However, not all of its devices were working on this system. The reason was that lenny changed its status from the stable to the oldstable Debian release shortly before the time of this writing and it therefore contained only older drivers. For example, both sound cards and the wireless card weren't detected at all on lenny even if they worked on the lenny successor squeeze. The hardware compatibility of this notebook with Debian squeeze is decribed in the Hardware specification and compatibility with Debian squeeze subsection.

It wasn't necessary to upgrade the whole system from lenny to squeeze to make the devices working, but at least the kernel and its dependencies and also some non-free firmware packages had to be upgraded or installed. I therefore changed the references to lenny to the references to squeeze in the file /etc/apt/sources.list and added the contrib and non-free sections to this file because I don't use them by default:

  • deb ftp://ftp.cz.debian.org/debian/ squeeze main contrib non-free
  • deb-src ftp://ftp.cz.debian.org/debian/ squeeze main contrib non-free
  • deb http://security.debian.org/ squeeze/updates main contrib non-free
  • deb-src http://security.debian.org/ squeeze/updates main contrib non-free

I had to update the package index files after that by running:

  • apt-get update

First of all, I upgraded the kernel. I chose the kernel package with the bigmem suffix in order to use the whole memory as explained in the Memory subsection of the Hardware specification and compatibility with Debian squeeze subsection. Specifically, I invoked the following command:

  • apt-get install linux-image-2.6.32-5-686-bigmem

I updated lilo to use the new kernel and rebooted so that the new kernel was loaded. I also installed the non-free firmware packages required by both network cards, i.e. firmware-realtek and firmware-brcm80211:

  • apt-get install firmware-realtek firmware-brcm80211

Because I wanted to use cpufreq to enable the processor frequency and voltage scaling, I installed the package cpufrequtils:

  • apt-get install cpufrequtils

This package loads the cpufreq kernel modules automatically during system startup. I changed the default governor as described in the Processor subsection of the Hardware specification and compatibility with Debian squeeze subsection.

After these modifications, all the devices were detected and their drivers loaded as decribed in the Hardware specification and compatibility with Debian squeeze subsection mentioned earlier. However, I ran one more command to reconfigure the uswsusp package and modify its configuration file /etc/uswsusp.conf once again. This was already done in one of the preceding subsections, but I wanted to change not only the resume device, but also adjust the preferred snapshot size to a larger value because my physical memory and swap space were much larger on the new system. Hence, I ran the following command:

  • dpkg-reconfigure uswsusp

I could have changed more suspend and resume settings by answering a few questions, but it wasn't necessary.