Introduction

Back in the Before Times (tm) when international travel seemed safer, and the only way to get certain information from conferences, I bought a MacBook Air for travel, as well as some external USB drives to use as additional storage, and for macOS Time Machine backups. Because the internal drive of the MacBook Air was encrypted, Time Machine insisted the backups also be encrypted.

In the last few years the MacBook Air has not left the house, and I have not travelled much, so there is no longer a need for a "travel external drive" with additional files. (If I were to travel these days I would definitely be taking a newer laptop; and those all have larger internal drives.)

But in order to be able to retire that MacBook Air Time Machine backups off one of my network drives (to make more room for network backups of newer machines), I wanted to have more space for Time Machine backups of that older MacBook Air on those external drives, so there was more backup history available.

Since I was planning on deleting one of the older sets of Time Machine backups for that machine (on the network drive), I wanted to retain the existing backups on those external drives and expand the space available (so they would be retained for longer).

Unfortunately, due to an accident of history, the Time Machine backups and the "external file system with extra files" shared the same USB drive, with the "external file system" partition first on the USB drive. (With the benefit of hindsight: do not do this, put the Time Machine backup volume first, as it is easiest to expand that way, and hardest to move to another drive.)

So this started An Adventure, with lots of yak shaving.

Warning

If you have found this blog post while searching on how to move or expand a time machine backup, please note that you should not attempt this if you have not been using dd and diskutil for years. Moving a time machine backup on a drive is complex, and fragile, and carries a very high risk of accidental data loss.

The process described below (mostly for my own reference) is guaranteed to lose data, as it destroys at least one file system in the process.

If you are very experienced, and very careful, you might only lose the data that you intended. But if you typo something, or identify the wrong drive volume at the wrong point (they do change names part way through the steps), then it is entirely possible you will lose data you do not intend to lose. If you make a mistake you could overwrite the internal drive of your system and render it unbootable.

Make sure you have other backups of the data in question, offline, that you could fall back to if necessary. I would also highly recommend doing these steps from a spare system that does not have any critical data on it, not even on the internal drive, so that worst case you are reinstalling that spare system and restarting your time machine backups of the planned system.

In my case I had multiple external drives with time machine backup partitions on them, and I did these steps one drive at a time, making sure the expanded one was working again before moving on to the next one. Plus I also had that network time machine backup (which I did not delete until all the external drives had been successfully expanded).

If you choose to follow this guide on your own system you do so at your own risk, having been warned there is a very high risk of data loss (including accidental data loss).

I am writing this up mostly because I have off site drives which will also need these same updates as they are brought back on site. So I wanted a record of the steps.

Preparing to move the Time Machine Volume

As noted in the introduction, unfortunately the external drives with encrypted Time Machine backups had the "can be reclaimed" file system at the start of the drive.

ewen@macos:~$ diskutil list external /dev/disk9
/dev/disk9 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *1.0 TB     disk9
   1:                        EFI EFI                     209.7 MB   disk9s1
   2:                  Apple_HFS travel1                 600.0 GB   disk9s2
   3:          Apple_CoreStorage mandir_tm1              399.7 GB   disk9s3
   4:                 Apple_Boot Boot OS X               134.2 MB   disk9s4
ewen@macos:~$

The Apple_CoreStorage volume is the encrypted Time Machine backup volume. Apple_CoreStorage is a volume manager built into older macOS (before APFS was added), which supports similar Physical Volumes, Volume Groups, and Logical Volumes as the Linux LVM (Logical Volume Manager). Importantly it also supports layering in an encryption layer over the logical volume.

The first step was to make certain that everything on that data volume at the start of the drive was unnecessary to retain, and was held elsewhere.

In my case I did that by examining each folder on the data drive, to determine what it was supposed to be copied from, and then confirmed with md5sum values that I had copies of the data on other drives that were going to be retained (including my NAS and other file servers and laptops/desktops). Because I knew these drives were "copies of data to travel with", once I confirmed I had identical copies of the data I was fairly confident I had multiple additional copies of the data and did not need the copy on the travel drives.

For the photos directory on the travel drives, I elected to make a complete copy of the data onto my NAS, for certainty. Especially once I determined that the travel drives had different subsets of photos (indicating they had been updated at different times).

It is important to confirm that you do not need any of the data on the first volume (the Apple_HFS volume) before proceeding, as the next step will irrevocably delete that volume and the data will be unrecoverable.

Creating another Time Machine volume to the start of the disk

Having confirmed that the volume at the start of the disk (here travel1) is not needed, and can be completely overwritten, it is now possible to make a duplicate of the Time Machine volume.

Fortunately the volume at the start of the disk was larger than the existing Time Machine volume that I was attempting to move; so it was possible to store two copies on the disk at once. (If that was not true, you would temporarily need another larger drive to store the Time Machine Volume, and need to copy it twice -- off the drive, and then back onto the drive -- which would take even longer.)

Because I did not know if macOS would like a partition table with a gap in the middle -- the Disk Utility utility application will not allow creating this partition scheme -- I elected to split the first partition into one slightly bigger than the Time Machine volume I was copying, and another Apple_HFS file system that used up the remaining space.

To do this I used:

diskutil splitPartition /dev/disk9s2 '%Apple_CoreStorage%' mandir_tm1_new 399.8G '%Apple_HFS%' gap_1 200.2G

The % around the file system types appear to be required by splitPartition, but not by other similar commands (see below later). The arguments after those file system types are the "volume names", which are required; but as best I can tell those are ignored (they did not end up in the partition table).

I choose to make the copied Time Machine partition just slightly larger than the source partition, to make sure 100% of the source volume could be copied.

diskutil ends up creating some additional partitions of its own, including another Apple_Boot partition, so the resulting partition table after the splitPartition was:

ewen@macos:~$ diskutil list external /dev/disk9
/dev/disk9 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *1.0 TB     disk9
   1:                        EFI EFI                     209.7 MB   disk9s1
   2:          Apple_CoreStorage                         399.8 GB   disk9s2
   3:                 Apple_Boot                         134.2 MB   disk9s3
   4:                  Apple_HFS                         200.1 GB   disk9s4
   5:          Apple_CoreStorage mandir_tm1              399.7 GB   disk9s5
   6:                 Apple_Boot Boot OS X               134.2 MB   disk9s6
ewen@macos:~$

Note the lack of volume names on the new partitions.

And particularly note that the partition identifiers have changed, as this matters a great deal when we come to copy the Time Machine volume in the next step. Before the splitPartition the Time Machine volume at the end was disk9s3, and after the splitPartition the Time Machine volume at the end is disk9s5.

Copying the Time Machine volume from the end of the disk to the start of the disk

To do this copy, the source volume must be unmounted, so it is not being modified during the copy of the underlying (encrypted) bits. I did this copy step on a system that did not have the password to the encrypted volume, and skipped mounting it at each step. If you are doing this on a system that has the encryption volume password, make sure you unmount it again at each step. Because each partition change will trigger a new automatic mount of the file system (as the partition table is re-read).

After the partition change (above) we have two Apple_CoreStorage partitions, of the same size. These are Apple Core Storage physical volumes, which contain the volume group and (encrypted) logical volume.

The one at the start of the disk is "empty" (contains stray data from the old file system), and the one at the end of the disk is the original partition we want to copy. Be very sure you know which is which, as the next step will overwrite one of those volumes -- and you want to be very sure that you are retaining the older backups, and overwriting the "junk" at the start of the disk.

Once you have identified the source disk partition and destination disk partition, it is then possible to use dd to copy the data. For me, this was:

sudo dd if=/dev/rdisk9s5 of=/dev/rdisk9s2 bs=33554432

Note particularly the r at the start of the disk volume names: this tells dd to use the "raw" (unbuffered) disk volumes, which is extremely important for copy performance. macOS, like BSD Unixes, and traditional unixes, has distinction "raw" disk volumes (character devices), and "buffered" disk volumes (block devices). The buffered block devices break all input and output into page size (4kB on x86) blocks, which is verrrrry slow, especially when you are copying to and from the same volume.

If iostat shows large numbers of 4kB disk transactions to the disk you have probably used the wrong version of the disk devices; if you use the raw disk devices the bytes per transaction should match your bs value above, assuming the disk is otherwise idle -- which it should be as it needs to be unmounted for the copy to have any chance of working. (On my external USB drive this makes the difference between about 15MB/s I/O to the external drive, so copying at about 7.5MB/s taking many hours, and about 90MB/s I/O to the external drive, so copying at 45MB/s, taking fewer hours -- it runs about 6x faster. So if you see it is wrong near the start of the copy you probably want to stop the copy and start it again with the better device filenames, as it will be faster than waiting.)

The macOS dd does not have a status=progress option of GNU dd, but you can carefully ask dd to report its current progress by sending it the INFO signal from another shell session:

kill -INFO PROCESS_ID

and then output will be in the dd shell window.

Wait for that copy to finish, successfully, before continuing.

Freeing up space at the end of the disk

Once you are certain you have a copy of the Time Machine backup volume at the start of the disk, the next step is to remove the partitions at the end of the disk.

In my case I turned all of the partitions at the end of the disk into free space:

diskutil eraseVolume "Free Space" "" /dev/disk9s4
diskutil eraseVolume "Free Space" "" /dev/disk9s5

but note that when you delete the old Apple_CoreStorage volume at the end of the disk (/dev/disk9s5) the Apple_Boot volume at the end of the disk will also be removed, which causes problems for the next step.

For this reason you may wish to not remove the Apple_Boot partition at the start of the drive -- /dev/disk9s3 here -- to ensure there is at least one Apple_Boot volume left on the drive. Assuming that works you will be able to skip the re-creating the Apple_Boot partition step below.

Also if Apple CoreServices has mounted the physical volume at the end (/dev/disk9s5 here), it will probably not be possible to remove it ("The target disk is in use by Core Storage as a Physical Volume; use diskutil coreStorage verbs"). Since there is no coreStorage command to release a physical volume, deleting the end Apple_CoreStorage volume may have to be done on a system that does not have the password to mount the volume (or on a Linux system, eg with parted). It appears not to be sufficent to umount or eject the volume; Core Storage still hangs onto the physical volume (and there does not appear to be any Core Storage deactivate command). So removing the physical volume at the end needs to be done on another system (eg, a macOS system that is not able to mount it ever, or a Linux system).

Once the unnecessary volumes are removed, the partition table should look like:

ewen@macos:~$ diskutil list external /dev/disk9
/dev/disk9 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *1.0 TB     disk9
   1:                        EFI EFI                     209.7 MB   disk9s1
   2:          Apple_CoreStorage                         399.8 GB   disk9s2
                    (free space)                         600.2 GB   -
ewen@macos:~$

with the (copied) Time Machine encrypted volume at the start of the drive, and free space at the end of the drive. (Possibly with an Apple_Boot partition remaining if you did not delete the first one -- /dev/disk9s3 here -- above, to save a step.)

Labelling the partition

So I could keep track at this point I unplugged the external drive from the macOS system, and plugged it into a Linux system, and used parted to explicitly name the replacement Time Machine partition the same as the now deleted Time Machine partition.

The usage is something like sudo parted /dev/sdf, then print the partition table (print), and then name 2 mandir_tm1. Verify that it is correct by printing the partition table again (with print), and then quit (the partition table is automatically written by parted, unlike some other tools).

ewen@linux:~$ sudo parted /dev/sdf
[sudo] password for ewen:
GNU Parted 3.3
Using /dev/sdf
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print
Model: WD Elements 25A2 (scsi)
Disk /dev/sdf: 1000GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number  Start   End    Size   File system  Name                  Flags
 1      20.5kB  210MB  210MB  fat32        EFI System Partition  boot, esp
 2      210MB   400GB  400GB               travel1

(parted) name 2 mandir_tm1
(parted) print
Model: WD Elements 25A2 (scsi)
Disk /dev/sdf: 1000GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number  Start   End    Size   File system  Name                  Flags
 1      20.5kB  210MB  210MB  fat32        EFI System Partition  boot, esp
 2      210MB   400GB  400GB               mandir_tm1

(parted) quit
ewen@linux:~$

Disconnect the drive from the Linux system, and unplug it into the macOS system. With luck at this point the encrypted Time Machine volume will automatically mount (if you plug it into a system that has the volume password for the encryption saved).

Recreate the Apple_Boot partition at the end of the drive

The next step (expanding the Apple_CoreStorage volume) can only be done (a) if there is an Apple_Boot partition after it, and (b) if the volume is mounted. If you deleted it in the steps above (as I did the first time through, by accident), it needs to be recreated.

If the Apple_Boot volume is missing you get an obscure error message

Error: -69722: You can't perform this resize unless it has a booter (target partition is probably too small)

The problem is not that the volume is too small; the problem is that the Apple_Boot volume is used as part of the Apple_CoreStorage expansion task (!!) and if it is missing the size for the expansion cannot be judged.

So the next step is to recreate the Apple_Boot partition, if you deleted it above. (It might be possible just to skip deleting the Apple_Boot partition after the first Apple_CoreStorage volume, and avoid this problem.)

To recreate the Apple_Boot volume, it is easiest to use a third party disk tool like gdisk, which can be installed from MacPorts with:

sudo port install gptfdisk

And then you need to create a partition after the Apple_CoreStorage partition, which is type Apple_Boot (AB00) and 1269536 in size:

ewen@macos:~$ sudo gdisk /dev/disk9
GPT fdisk (gdisk) version 1.0.9

Warning: Devices opened with shared lock will not have their
partition table automatically reloaded!
Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.

Command (? for help): p
Disk /dev/disk9: 1953458176 sectors, 931.5 GiB
Sector size (logical): 512 bytes
Disk identifier (GUID): 42717690-01FF-4AA2-9C73-FA985D60E05C
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 1953458142
Partitions will be aligned on 8-sector boundaries
Total free space is 1172189141 sectors (558.9 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1              40          409639   200.0 MiB   EF00  EFI System Partition
   2          409640       781269007   372.3 GiB   AF05  mandir_tm1

Command (? for help): n
Partition number (3-128, default 3): 3
First sector (34-1953458142, default = 781269008) or {+-}size{KMGTP}:
Last sector (781269008-1953458142, default = 1953458135) or {+-}size{KMGTP}: +1269536
Current type is AF00 (Apple HFS/HFS+)
Hex code or GUID (L to show codes, Enter = AF00): AB00
Changed type of partition to 'Recovery HD'

Command (? for help): p
Disk /dev/disk9: 1953458176 sectors, 931.5 GiB
Sector size (logical): 512 bytes
Disk identifier (GUID): 42717690-01FF-4AA2-9C73-FA985D60E05C
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 1953458142
Partitions will be aligned on 8-sector boundaries
Total free space is 1170919605 sectors (558.3 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1              40          409639   200.0 MiB   EF00  EFI System Partition
   2          409640       781269007   372.3 GiB   AF05  mandir_tm1
   3       781269008       782538543   619.9 MiB   AB00  Recovery HD

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/disk9.
Warning: Devices opened with shared lock will not have their
partition table automatically reloaded!
Warning: The kernel may continue to use old or deleted partitions.
You should reboot or remove the drive.
The operation has completed successfully.
ewen@macos:~$

Once that was done, I ejected the drive, unplugged it, and plugged it back in again, to force the macOS kernel to refresh its view of the drive. (In theory because we just added a partition it should have been safe, but since the Apple_Boot partition was necessary for the next step it seemed best to have the kernel view of the drive up to date.)

Expanding the Time Machine volume (now at the start of the disk)

In order to expand the Time Machine volume it must be mounted, which means this step needs to be done on a macOS system. To trigger the expansion, you need to know the UUID of the inner logical volume, which can be found with:

ewen@macos:~$ diskutil cs list
CoreStorage logical volume groups (1 found)
|
+-- Logical Volume Group 60FE7879-D2AC-4B98-97E0-217FE8A45F9B
    =========================================================
    Name:         mandir_tm1
    Status:       Online
    Size:         399692394496 B (399.7 GB)
    Free Space:   8773632 B (8.8 MB)
    |
    +-< Physical Volume 771FAE36-AA9D-42F7-81F3-612FFF8984EF
    |   ----------------------------------------------------
    |   Index:    0
    |   Disk:     disk9s3
    |   Status:   Online
    |   Size:     399692394496 B (399.7 GB)
    |
    +-> Logical Volume Family 55582F40-CF74-4623-BA38-079B93EBB70D
        ----------------------------------------------------------
        Encryption Type:         AES-XTS
        Encryption Status:       Locked
        Conversion Status:       Complete
        High Level Queries:      Fully Secure
        |                        Passphrase Required
        |                        Accepts New Users
        |                        Has Visible Users
        |                        Has Volume Key
        |
        +-> Logical Volume 928AAEE1-26FB-4995-85DC-D456188D2A92
            ---------------------------------------------------
            Disk:                  -none-
            Status:                Locked
            Size (Total):          399331295232 B (399.3 GB)
            Revertible:            No
            LV Name:               mandir_tm1
            Content Hint:          Apple_HFS
ewen@macos:~$

It is the last UUID, of the inner volume that you need.

Then the entire Apple_CoreStorage "stack" (physical volume, volume group, encrypted logical volume, inner volume) can be expanded to maximum size with:

diskutil cs resizeStack 928AAEE1-26FB-4995-85DC-D456188D2A92 0g

where 0g is magic value which means "maximum size possible", ie use the remainder of the disk. Which is what I wanted.

That expansion will do a full fsck of the inner volume, which takes quite a while (especially for a Time Machine volume that has hard links to directories!). But once it is successfully checked it will then expand the physical volume, volume group, logical volume, and file system for you:

ewen@macos:~$ diskutil cs resizeStack 928AAEE1-26FB-4995-85DC-D456188D2A92 0g
The Core Storage Logical Volume UUID is 928AAEE1-26FB-4995-85DC-D456188D2A92
Started CoreStorage operation
Checking prerequisites for resizing Logical-Physical volume stack
Growing Logical-Physical volume stack
Verifying file system
Volume was successfully unmounted
Performing fsck_hfs -fn -x /dev/rdisk4
Checking Journaled HFS Plus volume
Checking extents overflow file
Checking catalog file
Checking multi-linked files
Checking catalog hierarchy
Checking extended attributes file
Checking multi-linked directories
Checking volume bitmap
Checking volume information
The volume mandir_tm1 appears to be OK
File system check exit code is 0
Restoring the original state found as mounted
Growing Core Storage Physical Volume from 399,799,996,416 to 999,310,827,520 byt
es
Copying booter
Growing disk partition
Modifying partition map
Growing Core Storage data structures
Resizing Core Storage Physical Volume structures
Resized Core Storage Physical Volume to 999,310,827,520 bytes
Growing Logical Volume
Resizing Core Storage Logical Volume structures
Resized Core Storage Logical Volume to 998,842,126,336 bytes
Growing file system
Finished CoreStorage operation
ewen@macos:~$

Once that was successful, I ejected the Time Machine volume again, disconnected the external drive, then reconnected it again, to ensure the kernel view of the drive was current. Then confirmed that the drive had been expanded correctly:

ewen@macos:~$ diskutil list external /dev/disk9
/dev/disk9 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *1.0 TB     disk9
   1:                        EFI EFI                     209.7 MB   disk9s1
   2:          Apple_CoreStorage mandir_tm1              999.3 GB   disk9s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk9s4
ewen@macos:~$
ewen@macos:~$ sudo gpt -r show /dev/disk9
Password:
       start        size  index  contents
           0           1         PMBR
           1           1         Pri GPT header
           2          32         Pri GPT table
          34           6
          40      409600      1  GPT part - C12A7328-F81F-11D2-BA4B-00A0C93EC93B
      409640  1951778960      2  GPT part - 53746F72-6167-11AA-AA11-00306543ECAC
  1952188600     1269536      3  GPT part - 426F6F74-0000-11AA-AA11-00306543ECAC
  1953458136           7
  1953458143          32         Sec GPT table
  1953458175           1         Sec GPT header
ewen@mandir:~$
ewen@mandir:~$ diskutil cs list
CoreStorage logical volume groups (1 found)
|
+-- Logical Volume Group 60FE7879-D2AC-4B98-97E0-217FE8A45F9B
    =========================================================
    Name:         mandir_tm1
    Status:       Online
    Size:         999310827520 B (999.3 GB)
    Free Space:   116375552 B (116.4 MB)
    |
    +-< Physical Volume 771FAE36-AA9D-42F7-81F3-612FFF8984EF
    |   ----------------------------------------------------
    |   Index:    0
    |   Disk:     disk9s2
    |   Status:   Online
    |   Size:     999310827520 B (999.3 GB)
    |
    +-> Logical Volume Family 55582F40-CF74-4623-BA38-079B93EBB70D
        ----------------------------------------------------------
        Encryption Type:         AES-XTS
        Encryption Status:       Unlocked
        Conversion Status:       Complete
        High Level Queries:      Fully Secure
        |                        Passphrase Required
        |                        Accepts New Users
        |                        Has Visible Users
        |                        Has Volume Key
        |
        +-> Logical Volume 928AAEE1-26FB-4995-85DC-D456188D2A92
            ---------------------------------------------------
            Disk:                  disk10
            Status:                Online
            Size (Total):          998842126336 B (998.8 GB)
            Revertible:            No
            LV Name:               mandir_tm1
            Volume Name:           mandir_tm1
            Content Hint:          Apple_HFS
ewen@macos:~$

Confirming it had worked

After expanding the drive, I had Time Machine do another backup to the drive, which worked without any problems. Between the fsck of the file system before the expansion, the volume automatically mounting, and the Time Machine backup working, I am fairly confident that the disk volume was copied correctly.

But as noted in the warnings above, copying the wrong drive partition at the wrong time could end up with a drive not working. And copying onto the wrong drive partition could overwrite vital data, and cause additional data loss. So considerable care is required in this process.