42

I am looking to see if Linux can mount and read the files natively stored on a Time Capsule over a network share. Like this question, I am looking for something that replicates at least read-only function of hdiutil to attach and then mount a sparse bundle disk images.

The SMB mount is easy since the Time Capsule shares using both AFP and SMB, but I'm not so sure the sparse disk bundle can be mounted as the reconstituted HFS+ directory.

Bonus points for a working mount command or pointer to the appropriate package that parses this DMG format.

In case it's not clear - this is how the band files look to me when mounted from a Mac in Terminal and what I expect Linux to see without the ability to mount the actual file system that is encoded in a multitude of binary band files.

host:iMac.sparsebundle mike$ ls -la
total 24
drwxrwxrwx@     7 mike  staff      264 Jul  5 10:01 .
drwx------      6 mike  staff      264 Mar 26 13:11 ..
-rwxrwxrwx      1 mike  staff      499 Feb 24 15:33 Info.bckup
-rwxrwxrwx      1 mike  staff      499 Feb 24 15:33 Info.plist
drwxrwxrwx  31101 mike  staff  1057390 Jun 17 20:19 bands
-rwxrwxrwx      1 mike  staff      532 Jun 24 22:06 com.apple.TimeMachine.MachineID.plist
-rwxrwxrwx      1 mike  staff        0 Feb 24 15:33 token
host:iMac.sparsebundle mike$ ls -la bands | head -10
total 1582092552
-rwxrwxrwx  1 mike  staff  8388608 Jul  5 08:33 0
-rwxrwxrwx  1 mike  staff  8388608 May 31 13:02 1
-rwxrwxrwx  1 mike  staff  8388608 Jun 24 22:16 10
-rwxrwxrwx  1 mike  staff  8388608 Mar 19 17:15 1000
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:50 10000
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:50 10001
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:50 10002
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:50 10003
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:50 10004
host:iMac.sparsebundle mike$ ls -la bands | tail -10
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:51 fff6
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:51 fff7
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:51 fff8
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:51 fff9
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:51 fffa
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:50 fffb
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:50 fffc
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:50 fffd
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:50 fffe
-rwxrwxrwx  1 mike  staff  8388608 May 31 00:50 ffff
host:~ mike$ ls -la bands|wc -l
   96636
2
  • Why do you need to "mount" the sparsebundle? Linux should see it as a directory, just cd in to it once the volume where your Time Machine backups are being stored is mounted on Linux.
    – Ian C.
    Jul 5, 2011 at 13:25
  • 1
    Thanks Ian C - I edited the question to clarify what I'm looking for - the raw data is all there, just not stored in a more readable format that is most useful for reading a specific file from a specific point in time.
    – bmike
    Jul 5, 2011 at 14:03

6 Answers 6

3

Newer MacOS versions don't use HFS+ as an underlying FS for Time Machine's sparsebundle, but uses APFS instead. The advantage of APFS is that it natively (as in: inside the FS itself) supports snapshots.

Here is a complete tutorial, tested and working on Fedora Workstation 33, on how you can mount a MacOS Time Machine backup folder on SMB, then mount the sparsebundle, then mount the APFS filesystem and access all snapshots.

How to mount a Mac Time Machine backup sparsebundle directory (on SMB) as a browseable filesystem on Linux

This guide was tested on Fedora 33.

Prerequisites

We need sparsebundlefs to mount a sparsebundle directory as a DMG disk image. Also, we need APFS fuse driver and user space utilities to mount the Mac APFS partition inside the DMG file. With the apfsutil binary, we can even browse the snapshots inside the APFS partition.

# we assume the directory /usr/local/bin exists and is in the path

# become root
sudo su

# install sparsebundlefs (https://github.com/torarnv/sparsebundlefs)
yum -y install fuse-devel
cd
git clone https://github.com/torarnv/sparsebundlefs.git
cd sparsebundlefs
make
mv sparsebundlefs /usr/local/bin

# install apfs-fuse (https://github.com/sgan81/apfs-fuse, https://linuxnewbieguide.org/how-to-mount-macos-apfs-disk-volumes-in-linux/)
yum -y install bzip2-devel fuse3-devel
cd
git clone https://github.com/sgan81/apfs-fuse.git
cd apfs-fuse
git submodule init
git submodule update
mkdir build
cd build
cmake ..
make
mv apfs* /usr/local/bin
ln -s /usr/local/bin/apfs-fuse /usr/sbin/mount.apfs

Script

Create a file mount_timebackup.sh containing the following. Don't forget to chmod +x.

#!/usr/bin/env bash

# mount_timebackup.sh

## CHANGE VARIABLES HERE
SMB_PATH="\\\\server_name_or_ip_address/share"
SMB_USERNAME="some_username"
SMB_PASSWORD="some_password"
## --END--

SMB_MNT="/mnt/timebackup_sparsebundle"
SMB_OPTIONS="user=${SMB_USERNAME},pass=${SMB_PASSWORD},ro"
SB_MNT="/mnt/sparsebundlefs"
SB_DMG="${SB_MNT}/sparsebundle.dmg"
APFS_MNT="/mnt/apfs"

# Check availability of binaries
SBFS_BIN="$(which sparsebundlefs)"
if [[ -z "${SBFS_BIN}" ]]; then echo "[!] sparsebundlefs binary not found, aborting."; exit 1; fi

PARTED_BIN="$(which parted)"
if [[ -z "${PARTED_BIN}" ]]; then echo "[!] parted binary not found, aborting."; exit 1; fi

LOSETUP_BIN="$(which losetup)"
if [[ -z "${LOSETUP_BIN}" ]]; then echo "[!] losetup binary not found, aborting."; exit 1; fi

APFSUTIL_BIN="$(which apfsutil)"
if [[ -z "${APFSUTIL_BIN}" ]]; then echo "[!] apfsutil binary not found, aborting."; exit 1; fi

# Make directories
mkdir -p "${SMB_MNT}" 2>/dev/null
mkdir -p "${SB_MNT}" 2>/dev/null
mkdir -p "${APFS_MNT}" 2>/dev/null

# Mount SMB share
if ! grep -q ${SMB_MNT} /proc/mounts
then
    echo "[i] Mounting share \"${SMB_PATH}\" as user ${SMB_USERNAME} on ${SMB_MNT}..."
    if ! mount.cifs "${SMB_PATH}" "${SMB_MNT}" -o "${SMB_OPTIONS}"
    then
        echo "[!] Error mounting SMB share, check output. Aborting."
        exit 1
    fi
else
    echo "[i] SMB share ${SMB_PATH} found on ${SMB_MNT}..."
fi

# Mount the sparse bundle
if ! grep -q ${SB_MNT} /proc/mounts
then
    echo "[i] Finding sparsebundle directory..."
    SB="$(find "${SMB_MNT}" -maxdepth 1 -type d -name '*.sparsebundle')"

    if [[ -z "${SB}" ]]
    then
        echo "[!] Sparsebundle directory not found under $SMB_MNT, aborting."
        exit 1
    fi

    echo "[i] Mounting sparsebundle directory \"${SB}\" as sparsebundle filesystem on ${SB_MNT}..."
    if ! "${SBFS_BIN}" "$SB" "$SB_MNT"
    then
        echo "[!] Error mounting sparsebundlefs, check output. Aborting."
        exit 1
    fi
else
    echo "[i] Sparsebundle mount found on ${SB_MNT}..."
fi

# Mount the APFS partition as loopback device
LO="$("${LOSETUP_BIN}" | grep "${SB_MNT}" | awk '{print $1}')"
if [[ -z "${LO}" ]]
then
    echo "[i] Determining characteristics of APFS filesystem inside ${SB_DMG}..."
    OFF="$("${PARTED_BIN}" "${SB_DMG}" unit B print 2>/dev/null | tr 'B' ' ' | awk '/disk image/ {print $2}')"
    SZ="$("${PARTED_BIN}" "${SB_DMG}" unit B print 2>/dev/null | tr 'B' ' ' | awk '/disk image/ {print $4}')"

    if [[ -z "${OFF}" ]] || [[ -z "${SZ}" ]]
    then
        echo "[!] Unable to determine APFS filesystem offset and size characteristics, aborting."
        exit 1
    fi

    echo "Mounting APFS filesystem inside ${SB_DMG} from offset ${OFF} with max size ${SZ} on loopback device..."
    LO="$("${LOSETUP_BIN}" -f "${SB_DMG}" --offset ${OFF} --sizelimit ${SZ} --show)"
    if [[ -z "${LO}" ]]
    then
        echo "[!] Error mounting APFS filesystem, aborting."
        exit 1
    fi
else
    echo "[i] APFS filesystem found at ${LO}."
fi

# List snapshots
echo "[i] Listing available snapshots in the APFS filesystem at ${LO}:"
"${APFSUTIL_BIN}" "${LO}"

echo "[i] To mount the latest available snapshot, run:"
echo "    mount.apfs \"${LO}\" \"${APFS_MNT}\""
echo
echo "[i] To mount a specific snapshot, run:"
echo "    mount.apfs -o snap=XXXXX \"${LO}\" \"${APFS_MNT}\""

Usage

Edit the mount_timebackup.sh file and put in the SMB path, username and password. Then, run the script. It will show something like this:

14:07 ★root(su)@fedora /root/bin
0» ./mount_timebackup.sh
[i] Mounting share "\\192.168.1.1/My_Timebackup" as user edward on /mnt/timebackup_sparsebundle...
[i] Finding sparsebundle directory...
[i] Mounting sparsebundle directory "/mnt/timebackup_sparsebundle/edward.sparsebundle" as sparsebundle filesystem on /mnt/sparsebundlefs...
[i] Determining characteristics of APFS filesystem inside /mnt/sparsebundlefs/sparsebundle.dmg...
Mounting APFS filesystem inside /mnt/sparsebundlefs/sparsebundle.dmg from offset 209735680 with max size 22685610287104 on loopback device...
[i] Listing available snapshots in the APFS filesystem at /dev/loop0:
Volume 0 B2853571-BC03-4DCC-91B7-295D046776BF
---------------------------------------------
Role:               Backup
Name:               Reservekopieën van Edward (Case-sensitive)
Capacity Consumed:  177614307328 Bytes
FileVault:          No
Snapshots:
    587 : 'com.apple.TimeMachine.2021-05-27-131216.backup'
    3122 : 'com.apple.TimeMachine.2021-06-03-142024.backup'
    5354 : 'com.apple.TimeMachine.2021-06-10-185257.backup'
    7193 : 'com.apple.TimeMachine.2021-06-17-210159.backup'
    8278 : 'com.apple.TimeMachine.2021-06-24-135457.backup'
    10556 : 'com.apple.TimeMachine.2021-07-01-221410.backup'
    12345 : 'com.apple.TimeMachine.2021-07-09-155806.backup'
    13326 : 'com.apple.TimeMachine.2021-07-17-074435.backup'
    13747 : 'com.apple.TimeMachine.2021-07-24-112952.backup'
    16670 : 'com.apple.TimeMachine.2021-07-31-085515.backup'
    17164 : 'com.apple.TimeMachine.2021-08-25-112049.backup'
    19466 : 'com.apple.TimeMachine.2021-09-01-202809.backup'
    21613 : 'com.apple.TimeMachine.2021-09-08-200654.backup'
    24023 : 'com.apple.TimeMachine.2021-09-15-202105.backup'
    25807 : 'com.apple.TimeMachine.2021-09-23-194219.backup'
    27372 : 'com.apple.TimeMachine.2021-09-30-161110.backup'
    29229 : 'com.apple.TimeMachine.2021-10-08-064900.backup'
    30021 : 'com.apple.TimeMachine.2021-10-14-222820.backup'
    30260 : 'com.apple.TimeMachine.2021-10-22-132638.backup'
    33163 : 'com.apple.TimeMachine.2021-10-30-141729.backup'
    36728 : 'com.apple.TimeMachine.2021-11-06-102305.backup'
    40140 : 'com.apple.TimeMachine.2021-11-13-201741.backup'
    41362 : 'com.apple.TimeMachine.2021-11-21-100951.backup'
    42977 : 'com.apple.TimeMachine.2021-11-28-212032.backup'
    45488 : 'com.apple.TimeMachine.2021-12-06-155539.backup'
    48657 : 'com.apple.TimeMachine.2021-12-13-154439.backup'
    50554 : 'com.apple.TimeMachine.2021-12-20-084732.backup'
    51856 : 'com.apple.TimeMachine.2022-02-06-105522.backup'
    54178 : 'com.apple.TimeMachine.2022-02-13-215427.backup'
    57082 : 'com.apple.TimeMachine.2022-02-21-151624.backup'
    58494 : 'com.apple.TimeMachine.2022-03-06-155624.backup'
    60265 : 'com.apple.TimeMachine.2022-03-13-124536.backup'
    64198 : 'com.apple.TimeMachine.2022-03-20-160040.backup'
    67262 : 'com.apple.TimeMachine.2022-03-25-173848.backup'
    68136 : 'com.apple.TimeMachine.2022-03-26-233827.backup'
    69024 : 'com.apple.TimeMachine.2022-03-28-165918.backup'
    69886 : 'com.apple.TimeMachine.2022-03-29-164136.backup'
    70515 : 'com.apple.TimeMachine.2022-03-30-194139.backup'
    70869 : 'com.apple.TimeMachine.2022-04-01-014510.backup'
    72140 : 'com.apple.TimeMachine.2022-04-02-082134.backup'
    72965 : 'com.apple.TimeMachine.2022-04-08-204109.backup'
    73092 : 'com.apple.TimeMachine.2022-04-10-194926.backup'
    73745 : 'com.apple.TimeMachine.2022-04-11-204535.backup'
    74595 : 'com.apple.TimeMachine.2022-04-12-204033.backup'
    75319 : 'com.apple.TimeMachine.2022-04-13-190833.backup'
    75971 : 'com.apple.TimeMachine.2022-04-14-222526.backup'
    77237 : 'com.apple.TimeMachine.2022-04-16-083710.backup'
    77319 : 'com.apple.TimeMachine.2022-04-17-094739.backup'
    77619 : 'com.apple.TimeMachine.2022-04-18-090536.backup'
    77705 : 'com.apple.TimeMachine.2022-04-18-104921.backup'
    77825 : 'com.apple.TimeMachine.2022-04-19-225314.backup'
    78369 : 'com.apple.TimeMachine.2022-04-20-171232.backup'
    78469 : 'com.apple.TimeMachine.2022-04-20-190918.backup'
    78598 : 'com.apple.TimeMachine.2022-04-20-203829.backup'
    78668 : 'com.apple.TimeMachine.2022-04-20-220645.backup'
    78794 : 'com.apple.TimeMachine.2022-04-21-155554.backup'
    79746 : 'com.apple.TimeMachine.2022-04-30-101539.backup'
    79836 : 'com.apple.TimeMachine.2022-04-30-112852.backup'
    79863 : 'com.apple.TimeMachine.2022-04-30-114329.backup'
    79937 : 'com.apple.TimeMachine.2022-04-30-122935.backup'
    80049 : 'com.apple.TimeMachine.2022-05-01-083701.backup'
    80131 : 'com.apple.TimeMachine.2022-05-01-093828.backup'
    80253 : 'com.apple.TimeMachine.2022-05-01-110758.backup'
    80289 : 'com.apple.TimeMachine.2022-05-01-122415.backup'

[i] To mount the latest available snapshot, run:
    mount.apfs "/dev/loop0" "/mnt/apfs"

[i] To mount a specific snapshot, run:
    mount.apfs -o snap=XXXXX "/dev/loop0" "/mnt/apfs"

Unmounting

# as root, in this specific order
umount /mnt/apfs
losetup -D
umount /mnt/sparsebundlefs
umount /mnt/timebackup_sparsebundle
41

You may use a combination of these two:

FUSE filesystem for reading Mac OS sparse-bundle disk images

Apple's Time Machine fuse read only file system

The first takes care of the .sparsebundle format, presenting it as a dmg file, which can then be mounted like normal. The second takes care of the directory hard-links used by Time Machine.

3
  • This should be the accepted answer. tmfs did the job for me perfectly on Debian 7.0. May 10, 2013 at 16:14
  • I thank @GordonBailey for pinging me to this - I missed the notification that another answer was provided.
    – bmike
    Jun 22, 2013 at 18:35
  • I couldn't mount the dmg out of the box so I also had to use darling-dmg which mounted it via FUSE. Mar 12, 2017 at 21:32
7

This is an extension to the answer by @TorArneVestbø.

Once you have installed https://github.com/torarnv/sparsebundlefs and https://github.com/abique/tmfs you need to run the following script in Bash. Make sure to update the two variables at the beginning to be the source and destination.

SB="/path/to/your/Backup.sparsebundle"
TM_MNT="/path/to/where/to/mount"

# Make directories
mkdir -p "$TM_MNT"
SB_MNT=`mktemp --tmpdir -d sparsebundle_mnt.XXX`
SB_DMG="$SB_MNT/sparsebundle.dmg"
HFS_MNT=`mktemp --tmpdir -d hfsx_mnt.XXX`

# Mount the sparse bundle
sudo `which sparsebundlefs` "$SB" "$SB_MNT"

# Mount the HFS+ partition
OFF=`sudo parted "$SB_DMG" unit B print | tr 'B' ' ' | awk '/hfsx/ {print $2}'`
SZ=`sudo parted "$SB_DMG" unit B print | tr 'B' ' ' | awk '/hfsx/ {print $4}'`
LO=`sudo losetup -f "$SB_DMG" --offset $OFF --sizelimit $SZ --show`
sudo mount -t hfsplus -r "$LO" "$HFS_MNT"

# Mount the Time Machine filesystem
sudo `which tmfs` "$HFS_MNT" "$TM_MNT" -ouid=$(id -u $USER),gid=$(id -g $USER),allow_other

The final mount will be accessible by you (as long as $TM_MNT is accessible to you). The final line may fail if FUSE is not setup to allow other user, it tells you how to fix it.

To unmount you need to do the following:

sudo umount "$TM_MNT"
sudo rmdir "$TM_MNT"
sudo umount "$HFS_MNT"
sudo rmdir "$HFS_MNT"
sudo losetup -d "$LO"
sudo umount "$SB_MNT"
sudo rmdir "$SB_MNT"

This was tested on a Fedora 28 system and is working well.

3

Apple's Time Machine fuse read only file system

https://github.com/abique/tmfs

1
  • 6
    What's the purpose of this answer? Does it do anything? Are we to guess it even answers the question? You'll need to unpack what the purpose of this even is and how it fixes the problem
    – random
    Nov 6, 2011 at 16:59
3

The above post, from Alexandre Bicque, provides a Linux (?unix) program that will open a Time Machine sparsebundle stored on a Mac-formatted HFS+ disk or disk partition, allowing reading of the files on a Linux server.

Getting it set up isn't for the faint-hearted. It's written in C++ and requires 3 C++ libraries - cmake, FUSE, and Boost, with certain minimum versions (which may not be default latest versions for my Ubuntu Server 10.04.) It also requires finding and installing a g++ compiler and the above libraries.

I use Ubuntu server 10.04 and am not much of a programmer. However, after a fair bit of work and time, I did manage to install all the necessary libraries, compile and link the tmfs package, and use it. It does work, allowing mounting a TimeMachine Time Capsule. HOWEVER, it does require that the disk on which the sparsebundle image is written be an HFS+ disk or partition. It won't work if the image is written on an NTFS or ext2/ext3/ext4 file system on a Linux server.

As of Apple's OS X 10.7 (Lion), Time Machine (sparsebundle) images will no longer work if mounted on a Windows (smb/Samba) Linux share, and it's necessary to run Linux/Unix Netatalk (afpd plus avahi-daemon) services to use Linux as a Time Machine server.

I've done a lot of looking for another solution. I suspect that a Linux/Unix C++ programmer could do better than have I, extending Alexandre Bicque's work to allow the use of ext4 or ntfs file systems. I'm trying to figure out how to do it, but have a long way to go.

I think it will require that I understand much better the fuse (user-space file system) and perhaps the boost::filesystem system development helpers in order to move forward.

1
  • AFAICS, currently no requirement for HFS+ exists for either of tmfs. The description mentions HFS, but the steps described will work anyway. Jun 17, 2013 at 1:41
2

Unfortunately the path to finding things in a sparsebundle from Linux is not straightforward. It can be done, but it requires interpreting some inode information that Apple embeds in the hardlinks to find the actual file in the sparsebundle. This MacWorld hint describes how you go about figuring out where a hardlink in a sparsebundle points to in terms of the actual file so you can access it from a Linux system. It deals with a Time Machine disk that's been attached as a local disk to a single machine.

In your case <mount point>/Backups.backupdb is most likely <machinename>.backupdb`.

I'm not sure whether <mount point>/.HFS+ Private Directory Data exists in the same spot for a shared disk being used for Time Machine backups by multiple machines. You'll have to do a little ls -la inspection of the disk and sparsebundles to find that.

But otherwise those MacWorld instructions will help you retrieve files on a Time Machine bundle, from Linux.

A update regarding the mount point.

I did some experimenting based on your updated question. It looks like the mount point should be the *.sparsebundle directory and not the drive. If I mount the drive in OS X and the go to /Volumes/Remote Backups/mymachine.sparsebundle I see the bands directory like you do and it's useless.

But if I mount mymachine.sparsebundle such that I can go to /Volumes/Time Machine Backups (that's what it mounts as automatically in Finder when I double click on the mymachine.sparsebundle) I see the expected Backups.backupdb directory and under that the date-time directories as expected.

3
  • thanks for the answer, but I'm not seeing the same file structure as you. I don't get Backups.backupdb since that is encoded inside the band files. Since it takes so long to enumerate that directory in raw form, I ran a ls | tee /tmp/bands to capture the output. I have 96636 binary files, most of them 8388608 in size. I have no issue getting around the backup structure once it's turned into a file system by mac's disk utility and then re-shared. I just can't figure how to process the bands on linux without OS X system to re-share the file system.
    – bmike
    Jul 5, 2011 at 14:57
  • @bmike: try mounting the *.sparsebundle file as a hfsplus filesystem on Linux instead of the remote drive. Similar to what happens if you open the .sparsebundle on your Mac and you end up with a /Volumes/Time Machine Backups volume on OS X. Updated answer with more detail.
    – Ian C.
    Jul 5, 2011 at 19:02
  • On Linux you can't mount directly sparsebundle files, unlike Mac OS X. Jun 17, 2013 at 1:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.