Added a tmbless utility

Added a utility to bless a snapshot directory
This commit is contained in:
Lee Ockert
2022-07-27 11:06:40 -04:00
parent a13fa65b17
commit a577ae5199
3 changed files with 190 additions and 2 deletions

View File

@ -59,6 +59,26 @@ ______________________________________________________________________________
______________________________________________________________________________ ______________________________________________________________________________
**tmbless**
------------
TIME MACHINE BLESS
USAGE
tmbless.sh <snapshot directory>
DESCRIPTION
Time Machine Blessing modifies the metadata of a snapshot directory
(i.e., a datestamped directory inside a Backups.backupdb/machinename/
directory) so that the metadata reflects a backup completed on that date
and the metadata of the top-level drive matches that of the current drive.
These modifications should allow restoration of files within the Time
Machine restore UI.
______________________________________________________________________________
**dirdedupe** **dirdedupe**
------------- -------------
DIRECTORY DE-DUPLICATOR DIRECTORY DE-DUPLICATOR

168
tmbless.sh Executable file
View File

@ -0,0 +1,168 @@
#!/usr/bin/env bash
# BITSC LICENSE NOTICE (MODIFIED ISC LICENSE)
#
# TIME MACHINE BLESS
#
# Copyright (c) 2022 Lee Ockert <torstenvl@gmail.com>
# https://github.com/torstenvl
#
# THIS WORK IS PROVIDED "AS IS" WITH NO WARRANTY OF ANY KIND. THE IMPLIED
# WARRANTIES OF MERCHANTABILITY, FITNESS, NON-INFRINGEMENT, AND TITLE ARE
# EXPRESSLY DISCLAIMED. NO AUTHOR SHALL BE LIABLE UNDER ANY THEORY OF LAW
# FOR ANY DAMAGES OF ANY KIND RESULTING FROM THE USE OF THIS WORK.
#
# Permission to use, copy, modify, and/or distribute this work for any
# purpose is hereby granted, provided this notice appears in all copies.
function dispusage() {
if [ ${#1} -gt 0 ]; then
printf "%s\n\n" "${1}"
fi
echo "\
TIME MACHINE BLESS
USAGE
${0} <snapshot directory>
DESCRIPTION
Time Machine Blessing modifies the metadata of a snapshot directory
(i.e., a datestamped directory inside a Backups.backupdb/machinename/
directory) so that the metadata reflects a backup completed on that date
and the metadata of the top-level drive matches that of the current drive.
These modifications should allow restoration of files within the Time
Machine restore UI.
"
}
function canonicalname() {
echo $(stat -f %R ${1})
}
############################################################################
## PARSE & MAKE SENSE OF COMMAND LINE ##
############################################################################
if [ ! $# -eq 1 ]; then
dispusage "Invalid number of arguments" && exit
fi
# Try to get canonical name
DATEDIRNAME=$(stat -f %R ${1})
if [[ "${DATEDIRNAME}" == "" ]]; then
dispusage "Could not get canonical name of directory ${1}" && exit
fi
# Ensure it's a directory
if [ ! -d "${DATEDIRNAME}" ]; then
dispusage "${DATEDIRNAME} is not a valid directory" && exit
fi
# Ensure it's a Backups.backupdb dir's subdir's subdir
if [[ ! "${DATEDIRNAME}" =~ ^.*\/Backups\.backupdb\/.*\/.*$ ]]; then
dispusage "${DATEDIRNAME} does not appear to be in a Backups.backupdb directory" && exit
fi
# Get the basename and ensure it's in a Time Machine timestamp format
DATEDIRBASENAME=$(basename "${DATEDIRNAME}")
if [[ ! "${DATEDIRBASENAME}" =~ ^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]\/?$ ]]; then
dispusage "${DATEDIRBASENAME} does not appear to be a snapshot directory" && exit
fi
# Get the Unix timestamp from the directory's Time Machine timestamp format
TIMESTAMP=$(date -j -f "%Y-%m-%d-%H%M%S" "${DATEDIRBASENAME}" +"%s")
TIMESTAMP="${TIMESTAMP}100000"
if [[ ! "${TIMESTAMP}" =~ ^[0-9]*$ ]]; then
dispusage "Could not automatically set snapshot timestamp due to folder name format" && exit
fi
if [ -d "${DATEDIRNAME}/Macintosh HD - Data" ]; then
DRVDIRNAME="${DATEDIRNAME}/Macintosh HD - Data"
elif [ -d "${DATEDIRNAME}/Macintosh HD" ]; then
DRVDIRNAME="${DATEDIRNAME}/Macintosh HD"
else
## TODO: Take the output of `ls -d ${DATEDIRNAME}` and try to autodetect?
dispusage "Could not find a volume name within ${DATEDIRNAME}" && exit
fi
############################################################################
## GET NECESSARY SYSTEM INFORMATION ##
############################################################################
VOLGRPUUID=`diskutil info / | awk -F' ' '/^ APFS Volume Group/{print $(NF)}'`
VOLDSKUUID=`diskutil info / | awk -F' ' '/^ Volume UUID/{print $(NF)}'`
if [ ! VOLGRPUUID == "" ]; then
TGTUUID="${VOLGRPUUID}"
elif [ ! VOLDSKUUID == "" ]; then
TGTUUID="${VOLDSKUUID}"
fi
KERNELVER=`uname -a | sed 's/.*Version \([0-9][0-9]*\).*/\1/g'`
if [ $KERNELVER -lt 20 ]; then
SIMONSAYS="sudo /System/Library/Extensions/TMSafetyNet.kext/Contents/Helpers/bypass"
else
SIMONSAYS="sudo"
fi
############################################################################
## CONFIRM ACTION INFORMATION ##
############################################################################
printf "\n\
Snapshot Directory: %s\n\
Snapshot Volume: %s\n\
Computer Volume: %s\n\
Volume Group: %s\n\\n" "${DATEDIRNAME}" "${DRVDIRNAME}" "${VOLDSKUUID}" "${VOLGRPUUID}"
printf "\
Preparing to run the following commands:\n\
%s xattr -c \"%s\"\n\
%s xattr -c \"%s\"\n\
%s xattr -w \"com.apple.backupd.SnapshotCompletionDate\" \"%s\" \"%s\"\n\
%s xattr -w \"com.apple.backupd.SnapshotState\" %s \"%s\"\n\
%s xattr -w \"com.apple.backupd.SnapshotVolumeUUID\" \"%s\" \"%s\"\n\
\n" "${SIMONSAYS}" "${DATEDIRNAME}" \
"${SIMONSAYS}" "${DRVDIRNAME}" \
"${SIMONSAYS}" "${TIMESTAMP}" "${DATEDIRNAME}" \
"${SIMONSAYS}" "4" "${DATEDIRNAME}" \
"${SIMONSAYS}" "${TGTUUID}" "${DRVDIRNAME}"
printf "Does everything look right?\n"
select response in "Bless Time Machine Snapshot" "ABORT ABORT ABORT!"; do
if [ "${response}" == "Bless Time Machine Snapshot" ]; then
"${SIMONSAYS}" xattr -c "${DATEDIRNAME}" && \
"${SIMONSAYS}" xattr -c "${DRVDIRNAME}" && \
"${SIMONSAYS}" xattr -w "com.apple.backupd.SnapshotCompletionDate" "${TIMESTAMP}" "${DATEDIRNAME}" && \
"${SIMONSAYS}" xattr -w "com.apple.backupd.SnapshotState" "4" "${DATEDIRNAME}" && \
"${SIMONSAYS}" xattr -w "com.apple.backupd.SnapshotVolumeUUID" "${TGTUUID}" "${DRVDIRNAME}"
if [ ! $? -eq 0 ]; then
printf "\nOperation failed.\n\n"
else
printf "\nOperation completed.\n\n"
printf "The snapshot directory now has the following metadata:\n"
printf "%s\n" "——————————————————————————————————————————————————————"
xattr -lv "${DATEDIRNAME}"
printf "\n\n"
printf "The snapshot volume now has the following metadata:\n"
printf "%s\n" "———————————————————————————————————————————————————"
xattr -lv "${DRVDIRNAME}"
printf "\n\n"
fi
break
else
printf "\nOperation aborted. No action has been taken.\n\n"
break
fi
done

View File

@ -141,7 +141,7 @@ Preparing to run the following commands:\n\
"${SIMONSAYS}" "${UUID}" "${BACKUPPATH}" \ "${SIMONSAYS}" "${UUID}" "${BACKUPPATH}" \
"${SIMONSAYS}" "${BACKUPPATH}" "${SIMONSAYS}" "${BACKUPPATH}"
if [ ! "$(basename "${BACKUPPATH}")" -eq "$(scutil --get ComputerName)"]; then if [ ! "$(basename "${BACKUPPATH}")" == "$(scutil --get ComputerName)" ]; then
printf "#############################################################################\n" printf "#############################################################################\n"
printf "## W A R N I N G W A R N I N G W A R N I N G ##\n" printf "## W A R N I N G W A R N I N G W A R N I N G ##\n"
printf "#############################################################################\n" printf "#############################################################################\n"
@ -155,7 +155,7 @@ if [ ! "$(basename "${BACKUPPATH}")" -eq "$(scutil --get ComputerName)"]; then
printf "Only proceed if you are very certain of what you're doing!\n" printf "Only proceed if you are very certain of what you're doing!\n"
printf "Even if successful, the Time Machine restore UI will be adversely affected.\n" printf "Even if successful, the Time Machine restore UI will be adversely affected.\n"
else else
printf "Does everything look right?\n\n" printf "Does everything look right?\n"
fi fi