Added a tmbless utility
Added a utility to bless a snapshot directory
This commit is contained in:
20
README.md
20
README.md
@ -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
168
tmbless.sh
Executable 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
|
||||||
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user