Files
test-repo/boot-disk.org
2022-05-08 10:13:55 +02:00

35 KiB

Second Ubuntu desktop boot disk

Introduction

It maybe happened to you already : A disk failure.

Or you are unsure of a new distribution upgrade, and you prefer to keep an operational one in case of.

Or you want to try a new distribution while keeping the old one, or simply test a new kernel, such as linux-next.

Of course, I have already a daily backup (on a different machine), but it does not allow to easily recover in case of serious system issue.

I considered having a mirrored system disk, but it is useful only in case of disk failure, surely not in case of human mistakes.

This document is a step-by-step explanation of the creation of such second (or third) bootable disk, from the copy to the kernel generation. It should work as-is on any debian-based distribution.

Initial configuration and goal

My desktop configuration is described below :

DONE Disks

I have 3 500Gb disks, /dev/sda, /dev/sdb, /dev/sdc. Only one is initially used, /dev/sdb, which contains the following partitions :

$ sudo sgdisk -p /dev/sdb
Disk /dev/sdb: 976773168 sectors, 465.8 GiB
Model: SAMSUNG HD501LJ
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 6BAC802E-87B0-4DA6-9E60-E008558C6948
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 976773134
Partitions will be aligned on 2048-sector boundaries
Total free space is 2029 sectors (1014.5 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1015807   495.0 MiB   EF00  efi1
   2         1026048        42969087   20.0 GiB    8300  root1
   3        42969088        51357695   4.0 GiB     8200  swap1
   4        51357696       976773119   441.3 GiB   8300  export1
   5         1015808         1026047   5.0 MiB     EF02  bios1

$ lsblk -l -o PATH,TYPE,PARTFLAGS,SIZE,FSTYPE,PTTYPE,LABEL /dev/sdb
PATH      TYPE PARTFLAGS   SIZE FSTYPE PTTYPE LABEL
/dev/sdb  disk           465.8G        gpt
/dev/sdb1 part             495M vfat   gpt    EFI1
/dev/sdb2 part              20G ext4   gpt    root1
/dev/sdb3 part               4G swap   gpt    swap1
/dev/sdb4 part           441.3G ext4   gpt    export1
/dev/sdb5 part               5M        gpt
Active system partitions (example)

What is important here:

  • Partitioning is GPT
  • All partitions have a name (LABEL column) which ends with "1". This number means simply "my first disk". It is not linked to an hardware position
  • Partitions look strange : (EFI1 and partition 5) exist as I currently boot with BIOS, but plan to migrate to UEFI boot when I have time (this is not as simple as I originally thought).

Looking at /etc/fstab, corresponding lines are :

$ grep '[[:alpha:]+]1[[:space:]]' /etc/fstab
LABEL=EFI1          /boot/efi.old   vfat    noauto,defaults                 0 2
LABEL=root1         /               ext4    errors=remount-ro               0 1
LABEL=export1       /export         ext4    defaults                        0 2
LABEL=swap1         none            swap    nofail,sw,pri=0                 0 0
/etc/fstab (example)

DONE Automount

The automounter (see autofs(8)), when configured, allows to automagically mount/umount file systems according to rules. As an example, we can decide that a directory /mnt/hdd could handle all partitions with a given label (you may choose different rules).

For example, if a given partition label is FOO, you could just do a ls /mnt/hd/foo to view its contents. In this case, the automounter will mount the partition before the execution of the ls command. This mount willl be released (un-mounted) later, after some time.

As an example, my autofs(8) configuration is :

/mnt/hd         /etc/auto.hd            --timeout=120 --ghost
/etc/auto.master

This line says :

  1. /mnt/hd is managed by autofs,
  2. The automounter rules for this directory are in /etc/auto.hd,
  3. The mounts will be automatically unmounted if not accessed for 120 seconds,
  4. the --ghost option says that the automounter will create directories in /mnt/hd, without having to access their contents.

See auto.master(5) for full details.

,*              -fstype=auto,defaults   :LABEL=&
/etc/auto.hd

Here, we say that for any X ('*') access in /mnt/hd, the partition with a label X will be mounted with the options specified. See autofs(5) for the description of automounter maps.

Practical example : If a partition's LABEL is export2, when a processus will try to access /mnt/hd/export2, that partition will be automagically mounted. The partition will be un-mounted 2 minutes after being not used.

Demonstration : Let say, with the above configuration, that I have a partition /dev/sdb4, with the label export1.

$ ls -l /mnt/hd
total 0
drwxr-xr-x 2 root root 0 Oct 13 21:39 EFI1
drwxr-xr-x 2 root root 0 Oct 13 21:39 EFI2
drwxr-xr-x 2 root root 0 Oct 13 14:05 export1
drwxr-xr-x 2 root root 0 Oct 13 01:08 export2
drwxr-xr-x 2 root root 0 Oct 13 02:00 porsche1
drwxr-xr-x 2 root root 0 Oct 13 01:01 root1
drwxr-xr-x 2 root root 0 Oct 13 01:01 root2

$ mount -l | grep sdb4
/dev/sdb4 on /export type ext4 (rw,relatime) [export1]

$ ls -l /mnt/hd/export1
total 32
drwxr-xr-x  8 root root  4096 Oct  2 06:47 home
drwx------  2 root root 16384 May 20  2018 lost+found
drwxr-xr-x  4 root root  4096 Aug 16 08:50 to-classify

$ mount -l | grep sdb4
/dev/sdb4 on /export type ext4 (rw,relatime) [export1]
/dev/sdb4 on /mnt/hd/export1 type ext4 (rw,relatime) [export1]

$ sleep 120

$ mount -l | grep sdb4
/dev/sdb4 on /export type ext4 (rw,relatime) [export1]
automount demonstration

DONE Goal

I will here duplicate /dev/sdb (my disk 1) on /dev/sda (disk 2), and install grub on sda (to make it bootable). Eventually, a script will allow to automate subsequent updates (for instance to run it on a daily basis).

DONE Ensure destination disk is not in use

In this document, SRC and DST variables will be the source and destination disks. In my case : /dev/sdb, and /dev/sda.

export SRC=/dev/sdb
export DST=/dev/sda
Setting $SRC and $DST

It is necessary to check that DST is not used. It may be for instance used for:

  • A mount point (try sudo mount | grep ${DST})
  • A swap device (try swapon | grep ${DST})
  • By automount
  • By another operating system if you have a multi-boot systemavez un multi-boot
  • By a database in raw mode, or more generally by any application using directly a raw device
  • etc…

Be sure to understand these dependancies before goint further.

Once done, you have to umount/"swapoff" (see umount(8) and swapoff(8) for details) anything related to DST, and remove any reference to this device in fstab, database configuration, etc.

Step 1: Destination disk partitioning

DONE Basic concepts

DONE Disk partitioning (partition table)

This is a table, stored on disk, which describes the different partitions. Most common nowadays are :

  • GPT : GTP is used by default on most systems nowadays (Linux, MacOS, Windows).
  • DOS/MBR : Used on end-of-century operating systems (DOS, Windows). MBR could be found on old machines, and maybe on new disks. You probably remember the "primary/secondary partition" jargon ?
  • And many others

DONE Partition types

This is the format of an individual partition, within the disk itself. On GNU/Linux, you will use mostly :

  • Extended File System : The most common nowadays. The last version of this family, EXT4, if often the default on many distributions, such as Debian, Ubuntu etc…
  • BTRFS : Designed by Oracle, and relatively recent, this file system has some interesting features, such as snapshots, integration of some RAID configurations, atomic file cloning, etc… It is the default file system on Fedora since version 33 (October 2020).
  • HFS and HFS+ : If you have a MaxOS machine, for instance dual-booting MacOS/Linux, you will probably have have some HFS+ partitions.
  • FAT, VFAT, FAT32, exFAT etc… : The different types/extensions of Microsoft's FAT file system. You will often find them by default on external disks, as they are well supported by most Operating systems. FAT is also used in EFI specifications to host the boot system.
  • SWAP : As its name says.
  • etc…

DONE Partitions label

Partitions often have a name (you can see it for instance when you insert a CD, an external disk), etc… This naming can be confusing, as for example, if you use gparted, you will see one Label column and one Name column.

  • Label : This is a name which resides inside the partition itself, and depends on the support of such names within this partition type. All partitions types described above (EXT, BTRFS, FAT, SWAP, etc…) have support for some naming, with different counstraints : case sensitivity, label lenght, etc… This label can be created independently of partitition table type (GPT, MBR, APM, etc…), as it is part of the partition itself. As this label is partition-type specific, we can set/modify it while creating the partition, or with different commands (e2label for extX, swaplabel for swap etc…
  • Name/PARTLABEL : Only for GPT disks, unsupported on MBR disks. This name is handled at partition table level, therefore it can be set with partitioning tools such as parted(8), sgdisk(8), etc…

Practical use ot these names is mostly in /etc/fstab and for the automounter. As you may know, it is badtm to use physical device names (such as /dev/sda1) as reference, as nothing guarantees they will keep the same name between reboots. We can use instead four different naming schemes : UUID, PARTUUID, LABEL, and PARTLABEL.

I originally switched from UUID/PARTUID (long and impossible to remember) to PARTLABEL, but now I simply use LABEL, reserving PARTLABEL for more descriptive information, such as "disk 2 root partition".

Example:

LABEL=foo      /foo       ext4  defaults  0 2  # GPT or MBR
PARTLABEL=exp  /export    ext4  defaults  0 2  # Only on GPT
Labels in fstab

As a sidenote, the blkid(8) and lsblk(8) commands can show all above information. Here with blkid :

$ blkid /dev/sdb*
/dev/sdb1: LABEL="EFI1" UUID="25E3-E3C5" TYPE="vfat" PARTLABEL="efi1" PARTUUID="9ce00989-9e23-4866-8c3b-8926604505a0"
/dev/sdb2: LABEL="root1" UUID="13243a74-031c-41b5-8a72-6c03d339e857" TYPE="ext4" PARTLABEL="root1" PARTUUID="6b32e47d-faaa-4fb6-8766-601e471e2305"
/dev/sdb3: LABEL="swap1" UUID="e9aa408c-33a4-41d9-aec3-afca45e5f9bd" TYPE="swap" PARTLABEL="swap1" PARTUUID="8ab9aba3-9331-4e4b-94e7-62ab22f9aeed"
/dev/sdb4: LABEL="export1" UUID="85b3c324-d374-4718-afd9-f6110eb2fcfa" TYPE="ext4" PARTLABEL="export1" PARTUUID="4ab6583a-9417-4c8c-a4bb-10770820471c"
blkid output

DONE partition table creation

We will create here a GPT disk able to boot both with BIOS and UEFI methods.

The sizes used will be, for my 500Gb disk:

# Partition Partition type FS type Size
- protective MBR - - Whole disk
1 BIOS boot 0xef02 - BIOS boot - 1 MiB
2 UEFI boot 0xef00 - EFI boot fat 100 MiB
3 / 0x8300 - Linux filesystem ext4 30 GiB
4 swap 0x8200 - Linux swap swap 4 GiB
5 export 0x8300 - Linux filesystem ext4 Remaining
partition scheme example

Note : to get a summary of partition types, you may use sgdisk -L.

Method 1: General case

Many commands can create a partition table. Here, I will create a GPT scheme, with sgdisk(8) :

$ sudo sgdisk --clear \
--new 1::+1M   --typecode=1:ef02 --change-name=1:'BIOS boot partition' \
--new 2::+100M --typecode=2:ef00 --change-name=2:'EFI System' \
--new 3::+30G  --typecode=3:8300 --change-name=3:'Linux root partition' \
--new 4::+4G   --typecode=4:8200 --change-name=4:'Linux swap' \
--new 5::-0    --typecode=5:8300 --change-name=5:'Linux export partition' \
${DST}
partition table creation

Source : gpt bios efi boot

Method 2: Duplicate existing partition table

If ${SRC} and ${DST} disks are identical, and if you wish to keep exactly the same partitioning, you can (1) replicate the partition table (2) generate new random UIDs for the new disk.

$ sudo sgdisk ${SRC} --replicate=${DST}
The operation has completed successfully.

$ sudo sgdisk --randomize-guids ${DST}
The operation has completed successfully.
partition table duplication

Basic partitions check

We can use lsblk(8) (or blkid(8) as above) to check the new disk situation :

$ sudo lsblk -l -o NAME,SIZE,PARTLABEL,PARTUUID,FSTYPE,LABEL ${DST}
NAME   SIZE PARTLABEL              PARTUUID                             FSTYPE LABEL
sda  465.8G
sda1     1M BIOS boot partition    59e81b6b-26cf-40b3-ba8e-2d0366b52a6b ext4   root2
sda2   100M EFI System             843d4f41-d774-44fb-9285-ba310786720a
sda3    30G Linux root partition   e14f484f-337a-44f2-8775-dcbe45a69b1f
sda4     4G Linux swap             c1bc4b83-2861-4bb8-93de-e1279c58b58c
sda5 431.7G Linux export partition 41397dd5-6e98-4fdb-82af-c0da1afccbb1
DST still has something wrong…

We can note that the two last columns (file system type and file system label) are still meaningless for destination disk, as we did not initialize them. For instance, sda1 contains information about a previous ext4 filesystem. We can wipe this with a dd(1), or wipefs(8) :

$ sudo wipefs /dev/sda1
DEVICE OFFSET TYPE UUID                                 LABEL
sda1   0x438  ext4 38fcc5a5-2faf-4fa3-a8b7-512007938938 root2

$ sudo wipefs --all /dev/sda1
/dev/sda1: 2 bytes were erased at offset 0x00000438 (ext4): 53 ef
wiping a partition signature
$ sudo lsblk -l -o NAME,SIZE,PARTLABEL,PARTUUID,FSTYPE,LABEL ${DST}
NAME   SIZE PARTLABEL              PARTUUID                             FSTYPE LABEL
sda  465.8G
sda1     1M BIOS boot partition    59e81b6b-26cf-40b3-ba8e-2d0366b52a6b
sda2   100M EFI System             843d4f41-d774-44fb-9285-ba310786720a
sda3    30G Linux root partition   e14f484f-337a-44f2-8775-dcbe45a69b1f
sda4     4G Linux swap             c1bc4b83-2861-4bb8-93de-e1279c58b58c
sda5 431.7G Linux export partition 41397dd5-6e98-4fdb-82af-c0da1afccbb1
The final (clean) DST partitions

Voilà !

DONE Filesystems creation

If you remember, we will format DST partitions as following:

Part # usage / mount point FS type Label/FS name (for fstab)
${DST}1 BIOS boot - -
${DST}2 EFI boot fat EFI2
${DST}3 / ext4 root2
${DST}4 swap swap swap2
${DST}5 /export ext4 export2
My own mission

Let's do it:

$ sudo mkfs.vfat -n EFI2 ${DST}2
mkfs.fat 4.1 (2017-01-24)
${DST}2 : FAT filesystem, named 'EFI2'
$ sudo mkfs.ext4 -L root2 ${DST}3
mke2fs 1.45.5 (07-Jan-2020)
Creating filesystem with 7864320 4k blocks and 1966080 inodes
Filesystem UUID: 3e921e46-19a7-4c3f-b72a-0169e573bc4f
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000

Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
${DST}3 : ext4 filesystem, named 'root2'
$ sudo mkswap  -L swap2 ${DST}4
Setting up swapspace version 1, size = 4 GiB (4294963200 bytes)
LABEL=swap2, UUID=6613ebaf-3000-46ee-917f-c577054b5861
${DST}4 : swap partition, named 'swap2'
$ sudo mkfs.ext4 -L export2 ${DST}5
mke2fs 1.45.5 (07-Jan-2020)
Creating filesystem with 113157633 4k blocks and 28295168 inodes
Filesystem UUID: fa670dfa-0a24-4c58-8013-f55a9cca6383
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
102400000

Allocating group tables: done
Writing inode tables: done
Creating journal (262144 blocks): done
Writing superblocks and filesystem accounting information: done
${DST}5 : ext4 filesystem, named 'export2'

Here is the final result :

$ sudo lsblk -l -o NAME,SIZE,FSTYPE,LABEL,PARTLABEL,UUID,PARTUUID ${DST}
NAME   SIZE FSTYPE LABEL   PARTLABEL              UUID                                 PARTUUID
sda  465.8G
sda1     1M ext4   root2   BIOS boot partition    38fcc5a5-2faf-4fa3-a8b7-512007938938 59e81b6b-26cf-40b3-ba8e-2d0366b52a6b
sda2   100M vfat   EFI2    EFI System             67DC-2F82                            843d4f41-d774-44fb-9285-ba310786720a
sda3    30G ext4   root2   Linux root partition   3e921e46-19a7-4c3f-b72a-0169e573bc4f e14f484f-337a-44f2-8775-dcbe45a69b1f
sda4     4G swap   swap2   Linux swap             6613ebaf-3000-46ee-917f-c577054b5861 c1bc4b83-2861-4bb8-93de-e1279c58b58c
sda5 431.7G ext4   export2 Linux export partition b4db512d-02fb-448f-af07-64c4ede7e135 41397dd5-6e98-4fdb-82af-c0da1afccbb1
disks labels mapping

DONE /etc/fstab preparation

/etc/fstab will differ between SRC and DST. I prefer to keep both versions on all disks, and copy the correct one as running version later.

First, I prepare it with the following changes :

[...]
LABEL=EFI1          /boot/efi.old   vfat    noauto,defaults                 0 2
LABEL=root1         /               ext4    errors=remount-ro               0 1
LABEL=export1       /export         ext4    defaults                        0 2
LABEL=swap1         none            swap    nofail,sw,pri=0                 0 0
[...]
/etc/fstab before changes

I create a new section for disk2, and merge the swap information (better to use all available swap).

[...]
######################## When booting on disk1
LABEL=EFI1          /boot/efi.old   vfat    noauto,defaults                 0 2
LABEL=root1         /               ext4    errors=remount-ro               0 1
LABEL=export1       /export         ext4    defaults                        0 2

######################## When booting on disk2
#LABEL=EFI2         /boot/efi.old   vfat    noauto,defaults                 0 2
#LABEL=root2        /               ext4    errors=remount-ro               0 1
#LABEL=export2      /export         ext4    defaults                        0 2

######################################## swap
LABEL=swap1         none            swap    nofail,sw,pri=0                 0 0
LABEL=swap2         none            swap    nofail,sw,pri=0                 0 0
[...]
/etc/fstab after adding new information

We copy the fstab file to two versions: fstab.disk1 and fstab.disk2 :

$ sudo cp -p /etc/fstab /etc/fstab.disk1
$ sudo cp -p /etc/fstab /etc/fstab.disk2
create /etc/fstabs for different boot disks

And now, we immediately fix the version for the new disk, commenting-out the disk1 part, and uncommenting the disk2 one :

[...]
######################## When booting on disk1
#LABEL=EFI1         /boot/efi.old   vfat    noauto,defaults                 0 2
#LABEL=root1        /               ext4    errors=remount-ro               0 1
#LABEL=export1      /export         ext4    defaults                        0 2

######################## When booting on disk2
LABEL=EFI2          /boot/efi.old   vfat    noauto,defaults                 0 2
LABEL=root2         /               ext4    errors=remount-ro               0 1
LABEL=export2       /export         ext4    defaults                        0 2

######################################## swap
LABEL=swap1         none            swap    nofail,sw,pri=0                 0 0
LABEL=swap2         none            swap    nofail,sw,pri=0                 0 0
[...]
/etc/fstab.disk2

Step 2 : First copies

Double-double check everything

GNU/Linux (to be more precise: the magic comes from udev(7)) offers a very convenient naming system for devices. I mean : partitions can be directly be accessed by their name, UUID, etc…

If, like me, you prefer to base your naming on partition labels, you can just have a look in /dev/disk/by-label :

$ ls -l /dev/disk/by-label
total 0
lrwxrwxrwx 1 root root 10 Oct 12 22:07 EFI1 -> ../../sdb1
lrwxrwxrwx 1 root root 10 Oct 12 22:07 EFI2 -> ../../sda2
lrwxrwxrwx 1 root root 10 Oct 12 22:07 export1 -> ../../sdb4
lrwxrwxrwx 1 root root 10 Oct 12 22:07 export2 -> ../../sda5
lrwxrwxrwx 1 root root 10 Oct 12 22:07 porsche1 -> ../../sdc1
lrwxrwxrwx 1 root root 10 Oct 12 22:07 root1 -> ../../sdb2
lrwxrwxrwx 1 root root 10 Oct 12 22:07 root2 -> ../../sda3
lrwxrwxrwx 1 root root 10 Oct 12 22:07 root3 -> ../../sdd1
lrwxrwxrwx 1 root root 10 Oct 12 22:07 swap1 -> ../../sdb3
lrwxrwxrwx 1 root root 10 Oct 12 22:07 swap2 -> ../../sda4
/dev/disk/by-label example

Please ensure that these labels correspond to what you expect, and point to the correct devices.

Do the filesystems copies

We will mount the new disk partitions we wish to duplicate (in my case: the root and export partitions) in /mnt.

First ensure that in the disk labels mapping table above, the partitions labels correctly

DONE Mount destination filesystems (automounter version)

With a correct automount system, such as explained above, we just create symlinks in mnt to the corresponding /labels in /dev/hd. This is not necessary, but it is simpler to access directly /mnt/export2/ instead of /mnt/hd/export2/.

$ cd /mnt/
$ for d in root export EFI; do ln -s hd/${i}2; done
with automounter

DONE Mount destination filesystems (manual version)

We need to create the same directories than above in ,/mnt and mount the corresponding filesystems :

$ cd /mnt
$ sudo mkdir root2 export2 EFI2
$ sudo mount -t ext4 LABEL=root2 /mnt/root2
$ sudo mount -t ext4 LABEL=export2 /mnt/export2
without automounter

I do not mount EFI2 partition here, it is useless until migration to UEFI boot is complete.

DONE Last checks

We double check that all mount points are correct. We should have something like :

$ ls /mnt/{root,export}2
/mnt/export2:
lost+found

/mnt/root2:
lost+found

$ mount -l | grep $DST
/dev/sda5 on /mnt/hd/export2 type ext4 (rw,relatime) [export2]
/dev/sda3 on /mnt/hd/root2 type ext4 (rw,relatime) [root2]
last check of destination partitions

The copies

We eventually make the copies. As it will be long, I run them in background and send me an email. You may prefer to send the output to a file, for instance if your email system is not configured. Or run the commands in different terminals.

TAKE CARE DESTINATION DIRECTORIES : Use your own names/mount points.

$ sudo rsync -axH /        /mnt/root2/   2>&1 | mail -s "copy root" me@domain.com
$ sudo rsync -axH /export/ /mnt/export2/ 2>&1 | mail -s "copy export" me@domain.com
rsync copies. CHANGE DEVICES !!

Note : The option -x above (same as --one-file-system) prevents disallow crossing source filesystems boundaries. -H preserves hard links. If you use ACLs, or extended attributes, you may consider -A and/or -X options.

If you have the automount setup, you may find easier to use something like that, if you have many partitions :

$ for i in root export; do
>    sudo rsync -axH /mnt/${i}1/ /mnt/${i}2/   2>&1 | mail -s "copy ${i}" me@domain.com &
> done
rsync copies with automount.

basic checks

When copies are done, you can roughly check that DST partitions occupation is similar to SRC :

$ df -h / /mnt/root2
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdb2        20G   17G  1.8G  91% /
/dev/sda3        30G   17G   12G  61% /mnt/hd/root2

$ df -h /export /mnt/export2
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdb4       434G  165G  247G  41% /export
/dev/sda5       424G  166G  237G  42% /mnt/hd/export2
after-copy check

Reminder table

I strongly suggest you write down a table such as this one, as a reminder :

Description SRC Label DST Label SRC dir DST dir
root partition root1 root2 / /mnt/root2
export partition export1 export2 /export /mnt/export2
EFI partition EFI1 EFI2 /boot/efi.old /mnt/EFI2
SRC/DEST reminder

Step 3 : Destination disk setup after initial copy

We will need some changes on destination disks. This includes fstab adjustment, crontabs (if they contain specific device references), grub, etc…

DONE fstab fix

We simply copy the fstab we prepared earlier I suggested above :

$ sudo cp /mnt/root2/fstab.disk2 /mnt/root2/fstab
fix new disk fstab

DONE Make the new disk bootable

I suggest to read some information about chroot(8) and grub before going further.

I will mount the new filesystems to a full subtree so that we can create a bootable disk on it. Note that if you have a separate boot or usr partition, they must be mounted relatively to new root mount-point. Nothing below should be done if you do not understand the commands. NOTHING.

$ sudo mkdir /mnt/disk2
$ sudo mount LABEL=root2 /mnt/disk2

# if you have separate boot (or /usr) partition do something similar to :
$ sudo mount LABEL=boot2 /mnt/disk2/boot
Prepare a chroot environment for new disk

Now, we bind-mount the virtual filesystems from the running system :

$ sudo mount -o bind  /sys    /mnt/disk2/sys
$ sudo mount -o bind  /proc   /mnt/disk2/proc
$ sudo mount -o bind  /dev    /mnt/disk2/dev
Virtual filesystems mount

We can now change our root reference with the new disk partitions. I strongly suggest to use a terminal dedicated for that :

$ cd /mnt/disk2

$ sudo chroot .

# update-grub
Sourcing file `/etc/default/grub'
Sourcing file `/etc/default/grub.d/init-select.cfg'
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-5.4.0-48-generic
Found initrd image: /boot/initrd.img-5.4.0-48-generic
Found linux image: /boot/vmlinuz-5.4.0-47-generic
Found initrd image: /boot/initrd.img-5.4.0-47-generic
Found linux image: /boot/vmlinuz-5.4.0-45-generic
Found initrd image: /boot/initrd.img-5.4.0-45-generic
Found memtest86+ image: /boot/memtest86+.elf
Found memtest86+ image: /boot/memtest86+.bin
Found Ubuntu 20.04.1 LTS (20.04) on /dev/sdb2
done
change root reference to new disk, create its grub configuration

You probably noticed the last line: A boot system was found on /dev/sdb2, which was the original disk.

Let's install grub now, to finish the procedure :

# grub-install ${DST}
Installing for i386-pc platform.
Installation finished. No error reported.
install grub on new disk

Testing

You can now reboot your system, and change BIOS settings to boot from new disk. This step is necessary (choosing second disk kernel from first disk grub menu is not sufficient, as it does not confirm grub is correct on second disk).

Some problems with what was done

There are a couple of issues with what we did.

fstab

As fstab is part of new disk, we can either :

  • Prevent it to be copied, in rsync rules
  • Copy the correct fstab on target disk after the rsync. I prefer the latter method.

Useless dirs

We may want to avoid copying some directories, possibly huge: log files, cache files, virtual file systems, etc…

Databases

Obviously, copying a database while it is live is a badTM idea. Again, we have three solutions :

  1. Stop it on source disk before the copy, if your system allows it.
  2. Separate the database directory sync from the container filesystem one, to allow a shorter database downtime. Something like :

    1. copy container filesystem without database directory
    2. stop database
    3. copy database directory
    4. restart database
  3. Make a database dump, and include them in rsync, while excluding database directory itself. The difficulty here is to reload the dumps on target disk : If we do it after the copy, it probably means we need a second database instance. If we delay this restore operation, for instance to do it when we boot on target disk, there are risks we simply forget, and we need some changes for database server startup, that should work also on source disk, as next copies will also include this configuration.

Solutions 2 and 3 look messy, and are prone to errors.

My preference is for first solution (DB server shutdown before sync, and restart after the copy is done).

Mariadb

*TODO *: For mariadb/mysql, check importance of innodb_fast_shutdown global variable (should it be 0 or 1 ?)

As explained above, I prefer to stop/restart database : as it is a desktop, database uptime is not really a priority for me.

$ sudo systemctl stop mariadb
$ sudo systemctl start mariadb
stopping/starting mariadb before rsync, using systemctl

TESTING script to ease subsequent copies

Below is a tentative bash script, which will automate the processus after the first copy. We also assume that automounter is configured as explained above. And finally, it is assumed that ,/boot is not a separate partition, and that source disk have same structure (1 partition on source disk corresponds to 1 partition on destination disk), and that "/" partitions labels are "rootX", such as root1, or root2.

TESTING The bash script

#!/bin/bash
#
# dup-boot-disk.sh - duplicate live system partitions and install grub on new disk.
#
# This script will not work for all situations, I strongly suggest you don't use it
# if you don't fully understand it.

# dest device (used for grub)
DSTDISK=/dev/sda

# partitions suffixes, for source and destination partitions.
# For example, if we want to copy XX partition, source partition will be
# /mnt/XX${SRCNUM}, and destination will be /mnt/XX${DSTNUM}
SRCNUM=2
DSTNUM=1

# array of partitions to copy
TO_COPY=(root export)

# An alternative to SRCNUM, DSTNUM, and TO_COPY variables would be to have
# an array containing src and destination partitions:
#   (partsrc1 partdst1 partsrc2 partdst2 etc...)
# example:
# TO_COPY=(root2 root1 export2 export1)
# declare -i i
# for ((i=0; i<${#TO_COPY[@]}; i+=2)); do
#     SRC=${#TO_COPY[$i]}
#     DST=${#TO_COPY[$i + 1]}
# etc...

# where we will configure/install grub: mount point, device
GRUB_ROOT=/mnt/root${DSTNUM}
GRUB_DEV=/dev/$(lsblk -no pkname /dev/disk/by-label/root${DSTNUM})

# we will use ".rsync-disk-copy" files to exclude files/dirs
RSYNCOPTS=(-axH --delete)
FILTER="--filter=dir-merge .rsync-disk-copy"

# stop what could be problematic (databases, etc...)
echo systemctl stop mariadb

# partitions copy
for part in ${TO_COPY[@]}; do
    SRCPART=/mnt/${part}${SRCNUM}
    DSTPART=/mnt/${part}${DSTNUM}

    echo copy from $SRCPART to $DSTPART
    echo sudo rsync ${RSYNCOPTS} "$FILTER" "$SRCPART" "$DSTPART"
done

# grub install
# mount virtual devices
echo mount -o bind  /sys    ${GRUB_ROOT}/sys
echo mount -o bind  /proc   ${GRUB_ROOT}/proc
echo mount -o bind  /dev    ${GRUB_ROOT}/dev

echo chroot ${GRUB_ROOT} update-grub
echo chroot ${GRUB_ROOT} grub-install ${GRUB_DEV}

# restart stopped process (db, etc...)
echo systemctl start mariadb
TODO - all-in one script (with automount)

DONE rsync filter files

An example of files that can be excluded. Note the last line, /mnt/*/ : It is linked to automounter, to avoid having all directories to be mounted ruring rsync copy.

- /dev
- /media
- /lost+found
- /proc
- /run
- /srv
- /sys
- /tmp
- core
- *~
- #*#
- /etc/fstab
- /mnt/*/
/.rsync-disk-copy example