forked from NeonHorizon/SDBackup
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sdbackup
executable file
·187 lines (164 loc) · 4.58 KB
/
sdbackup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#!/bin/bash
#--------------------------------------------------------------------------------
# SDBackup
# A script to backup a device such as an SD Card to a compressed image
#--------------------------------------------------------------------------------
# Daniel Bull
# daniel@neonhorizon.co.uk
#------------------------------------------------------------------------------*/
# Settings
thisapp="$(basename "$0")"
settingsfile=~/.sdbackup
defaultdev=""
# Automatically install what we need
if [[ ! $(type -P "unp") || ! $(type -P "ddrescue") || ! $(type -P "lshw") ]]; then
echo "It looks like this is the first time $thisapp has been run."
echo "Installing required packages..."
echo
sudo apt-get install unp gddrescue lshw || { echo "Error: Please install unp, gddrescue and lshw" 1>&2; exit 1;}
echo "Done..."
echo
fi
# Create the temporary folder if it doesn't already exist
if ! tempfolder="$(mktemp -td sdbackup.tmp.XXXXXXXX)"; then
echo "Error: Cannot create temporary folder" 1>&2
exit 1
fi
# Check filename parameter is present
if [[ "$1" = "" ]]; then
echo "Usage: $thisapp <filename> [<device>]" 1>&2
exit
fi
# Automatically add .gz extension if its not there
file="$1"
if ! [[ "$file" == *".gz" ]]; then
file+=".gz"
fi
# Check target file doesnt already exist
if [[ -d "$file" ]]; then
echo "Error: $file is a directory?" 1>&2
exit 1
elif [[ -f "$file" ]]; then
# Confirm overwite
read -rp "$file already exists, should it be deleted? [y/N]: " -n1 agree
if ! [[ "$agree" =~ ^[yY]$ ]]; then
exit
fi
echo
rm "$file" || { echo "Error: Cannot delete $file" 1>&2; exit 1; }
echo "Deleted $file"
echo
fi
# Get a device name
if [[ "$2" != "" ]]; then
# Supplied on CLI
dev="$2"
else
# Get the default
if [[ -f "$settingsfile" ]]; then
defaultdev="$(cat "$settingsfile")"
fi
# Scan for disks
echo "Scanning drives..."
declare -a line
declare -a devices
declare -a sizes
declare -a names
device=0;
while IFS=":" read -r -a line; do
case "${line[0]}" in
*"description")
device=$((device+1))
devices[$device]=""
sizes[$device]=""
names[$device]=""
;;
*"logical name")
devices[$device]="${line[1]# }"
;;
*"size")
sizes[$device]+="${line[1]# }"
;;
*"product")
names[$device]+="${line[1]# }"
;;
esac
done < <(sudo lshw -class disk 2> /dev/null)
unset line
# Display a list of options
echo
defaultselect=0
for i in "${!devices[@]}"; do
if [[ "${sizes[$i]}" ]]; then
echo "$i) ${devices[$i]} ${names[$i]} (${sizes[$i]})"
else
echo "$i) ${devices[$i]} ${names[$i]} (currently empty)"
fi
if [[ "${devices[$i]:1}" = "$defaultdev" ]]; then
defaultselect=$i
fi
done
echo "0) Cancel"
# Ask the user to select
echo
dev=""
while ! [[ "$dev" ]]; do
read -rp "Please select your device [$defaultselect]: " selected
if ! [[ "$selected" ]]; then
selected=$defaultselect
fi
if [[ "$selected" = 0 ]]; then
exit;
fi
if [[ "$selected" =~ ^[01234567890]$ ]]; then
dev="${devices["$selected"]:1}"
fi
done
fi
# Check the device exists
if [[ ! -b "$dev" ]]; then
echo "Error: $dev is not a block device?" 1>&2
exit 1;
fi
# Confirm
read -rp "Backup from $dev to $file is that correct? [y/N]: " -n1 agree
if ! [[ "$agree" =~ ^[yY]$ ]]; then
exit
fi
echo
echo
# Filenames
lockfile="/tmp/sdbackup${dev////_}.lock"
tempfile="$tempfolder/${file}_${dev////_}.tempfile"
# Test and create lockfile
if [[ -f $lockfile ]]; then
echo "Error: There is a $(cat "$lockfile") currently running on $dev" 1>&2;
exit 1;
fi
echo "$thisapp ($$)" > "$lockfile"
# Trap for failure
trap 'rm "$lockfile" 2> /dev/null; rm "$tempfile" 2> /dev/null; rmdir "$tempfolder" 2> /dev/null' EXIT
# Clean up anything left over from a previous attempt
sudo rm "/tmp/sdbackup.tmp."*/*.tempfile 2> /dev/null
sudo rmdir "/tmp/sdbackup.tmp."* 2> /dev/null
# Unmount the card
echo "Unmounting any mounted partitions..."
for partition in "$dev"*; do
if [[ -b "$partition" ]]; then
if grep -Eqs ^"$partition " /proc/mounts; then
sudo umount "$partition" 2> /dev/null || { echo "Error: Cannot unmount $partition" 1>&2; exit 1; }
fi
fi
done
# Create a temp file with our user (so we don't need sudo to delete it)
touch "$tempfile"
# Back it up
echo "Copying data from $dev..."
echo
sudo ddrescue "$dev" "$tempfile" || exit 1;
echo
echo "Zipping..."
gzip --stdout "$tempfile" > "$file" || { echo "Error: Cannot compress the file" 1>&2; exit 1; }
echo "Backup complete."
# Set device as default
echo "$dev" > "$settingsfile"