Instantly share code, notes, and snippets.

Embed
What would you like to do?
Copy data from a Time Machine volume mounted on a Linux box.
#!/bin/bash
#
# Copy data from a Time Machine volume mounted on a Linux box.
#
# Usage: copy-from-time-machine.sh <source> <target>
#
# source: the source directory inside a time machine backup
# target: the target directory in which to copy the reconstructed
# directory trees. Created if it does not exists.
#
# Details:
#
# Time machine implements directory hard links by creating an
# empty file in place of the directory and storing in its
# "number of hard links" metadata attribute a pointer to a
# real directory in "/.HFS Private directory data^M" named
# "dir_$number".
#
# This script reconstructs a plain directory tree from this
# really ugly apple hack. Tested on a 650GB backup from OSX
# 10.6 mounted on a Linux 3.2.0-38 Ubuntu box. YMMV.
#
# MIT License.
#
# - vjt@openssl.it
#
self="$0"
source="$1"
target="$2"
hfsd="$3"
set -e
if [ -z "$source" -o -z "$target" ]; then
echo "Usage: $self <source> <target>"
exit -1
fi
if [ ! -d "$target" ]; then
mkdir -p "$target"
fi
if [ -z "$hfsd" ]; then
# Look for HFS Private directory data
sysname="$(echo -ne '.HFS+ Private Directory Data\r')"
hfsd=$source
while [ "$hfsd" != "/" -a ! -d "$hfsd/$sysname" ]; do
hfsd=`dirname "$hfsd"`;
done
if [ "$hfsd" = '/' ]; then
echo "HFS Private Directory Data not found in $source, is it an HFS filesystem?"
exit -2
else
echo "HFS Private Directory Data found in '$hfsd'"
hfsd="$hfsd/$sysname"
fi
fi
find "$source" -mindepth 1 -maxdepth 1 -and -not -name . -and -not -name .. | while read entry; do
dest="$target/`basename "$entry"`"
read hlnum type <<<$(stat -c '%h %F' "$entry")
case $type in
'regular file'|'symbolic link')
cp -van "$entry" "$dest"
;;
'directory')
# Recurse
$self "$entry" "$dest" "$hfsd"
;;
'regular empty file')
if [ -d "$hfsd/dir_$hlnum" ]; then
# Recurse
$self "$hfsd/dir_$hlnum" "$dest" "$hfsd"
else
echo "Skipping empty file $entry"
fi
;;
esac
done
@ticky

This comment has been minimized.

Copy link

ticky commented Mar 22, 2014

Thank you so much for this. This has really saved me a whole bunch of time.

@dragonee

This comment has been minimized.

Copy link

dragonee commented Apr 27, 2014

Thanks! You have saved my life with that. :)

Two suggestions after running this gist:

  1. In case of read failures on disk, set -e should be commented out to recover as many files as much you can.
  2. I've found it necessary to add LANG=C to stat, because it returned localized strings for file types and the case statement did not recognize output.
@itsmrgomez

This comment has been minimized.

Copy link

itsmrgomez commented Oct 15, 2014

Hello! I've made sure the scrips is executable then ran:

sudo bash copy-from-time-machine.sh "source" "target"

with my appropriate directories of course, but I keep getting:

HFS Private Directory Data found in '/media/Time Machine/'
copy-from-time-machine.sh: line 72: copy-from-time-machine.sh: command not found

Please help, did I do something wrong?

@iimog

This comment has been minimized.

Copy link

iimog commented Nov 1, 2014

Thanks for the nice script! I also needed the LANG=C option.
@itsmrgomez: make the script executable and call it as ./copy-from-time-machine.sh
The script recursively calls itself using the $0 variable. In case of your call it is just
copy-from-time-machine.sh which is not in your $PATH (therefore command not found).
If you call it directly as ./copy-from-time-machine.sh the recursive calls use the same and it should work.
Hope that helps!

@yongkangchen

This comment has been minimized.

Copy link

yongkangchen commented Nov 14, 2014

stat: cannot stat `/mnt/usb_1/.HFS+ Private Directory Data\r/dir_1027471/2014/ImportersPhotoshopImport.xml': No such file or directory

There occur error with stat when path contains \
The right path is:

/mnt/usb_1/.HFS+ Private Directory Data?/dir_1027471/2014/Importers\PhotoshopImport.xml
@grandmasterB

This comment has been minimized.

Copy link

grandmasterB commented Feb 27, 2015

Hi, when I run this script this is the output:

Syslnx ~ # ./timemachine.sh /media/thomas/MAXELL1 /tmp/osx
HFS Private Directory Data found in '/media/thomas/MAXELL1'

But nothing else happened. (the /tmp/osx dir still empty)
What should I do?

@er4z0r

This comment has been minimized.

Copy link

er4z0r commented Jun 16, 2015

Same problem as @grandmasterB. Script finds the private data and then no output is generated

@FlimFlam

This comment has been minimized.

Copy link

FlimFlam commented Jun 20, 2015

Same here :(

@magicoli

This comment has been minimized.

Copy link

magicoli commented Aug 9, 2015

Nothing happens when your locale is set to something else than english.
Add
unset LANG
or
export LANG=C
at the beginning of the script and it will fix it.

I made a fork including this fix:
https://gist.github.com/magicoli/283785bdf21ebafd2202

@magicoli

This comment has been minimized.

Copy link

magicoli commented Aug 9, 2015

Scritp does not work either if the source is a Time-Machine hard link. My fork https://gist.github.com/magicoli/283785bdf21ebafd2202 fixes that too.

@lifeofguenter

This comment has been minimized.

Copy link

lifeofguenter commented Nov 27, 2015

I am getting a lot of the following errors:

stat: cannot stat ‘/xxxx’: Cannot allocate memory

Is there any way to fix this?

@BrunoCodeman

This comment has been minimized.

Copy link

BrunoCodeman commented Jun 4, 2016

You saved my life, lol! Thanks!

@shushugah

This comment has been minimized.

Copy link

shushugah commented Oct 24, 2016

Is it intended behavior that my Backups.backupdb folder is now empty? How can I use this with a mac again?

@ekortright

This comment has been minimized.

Copy link

ekortright commented May 6, 2018

Thank you very much for making this script available!

@shushugah's comment worried me, but if you read through the script you can see that no files are deleted or modified in any way in the source folder. I can see all the files in my Backups.backupdb folder after extracting everything I needed.

@kadirmalak

This comment has been minimized.

Copy link

kadirmalak commented Dec 18, 2018

Thank you :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment