live-snapshot: fixes snapshotting from resync string.
[live-boot-grml.git] / bin / live-snapshot
1 #!/bin/sh
2
3 # live-snapshot - utility to manage Debian Live systems snapshots
4 #
5 #   This program mounts a device (fallback to /tmpfs under $MOUNTP
6 #   and saves the /live/cow (or a different dir) filesystem in it for reuse
7 #   in another live-initramfs session. Look at manpage for more info.
8 #
9 # Copyright (C) 2006-2008 Marco Amadori <marco.amadori@gmail.com>
10 # Copyright (C) 2008 Chris Lamb <chris@chris-lamb.co.uk>
11 #
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 2 of the License, or
15 # (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #
26 # On Debian systems, the complete text of the GNU General Public License
27 # can be found in /usr/share/common-licenses/GPL-2 file.
28
29 set -eu
30
31 . /usr/share/initramfs-tools/scripts/live-helpers
32 . /etc/live.conf
33
34 export USERNAME USERFULLNAME HOSTNAME
35
36 PROGRAM="$(basename $0)"
37
38 # Needs to be available at run and reboot time
39 SAFE_TMPDIR="/live"
40
41 # Permits multiple runs
42 MOUNTP="$(mktemp -d -p ${SAFE_TMPDIR} live-snapshot-mnt.XXXXXX)"
43 SNAP_COW="/live/cow"
44 SNAP_DEV=""
45 DEST="${MOUNTP}/live-sn.cpio.gz"
46 SNAP_TYPE="cpio"
47 DESKTOP_LINK="/home/${USERNAME}/Desktop/live-snapshot"
48 SNAP_RESYNC_STRING=""
49
50 Error ()
51 {
52         echo "${PROGRAM}: error:" ${@}
53         exit 1
54 }
55
56 panic ()
57 {
58         Error ${@}
59 }
60
61 Header ()
62 {
63         echo "${PROGRAM} - utility to perform snapshots of Debian Live systems"
64         echo
65         echo "usage: ${PROGRAM} [-c|--cow DIRECTORY] [-d|--device DEVICE] [-o|--output FILE] [-t|--type TYPE]"
66         echo "       ${PROGRAM} [-r|--resync-string STRING]"
67         echo "       ${PROGRAM} [-h|--help]"
68         echo "       ${PROGRAM} [-u|--usage]"
69         echo "       ${PROGRAM} [-v|--version]"
70 }
71
72 Help ()
73 {
74         Header
75
76         echo
77         echo "Options:"
78         echo "  -c, --cow: copy on write directory (default: ${SNAP_COW})."
79         echo "  -d, --device: output snapshot device (default: ${SNAP_DEV:-auto})."
80         echo "  -o, --output: output image file (default: ${DEST})."
81         echo "  -r, --resync-string: internally used to resync previous made snapshots."
82         echo "  -t, --type: snapshot filesystem type. Options: \"squashfs\", \"ext2\", \"ext3\", \"jffs2\" or \"cpio\".gz archive (default: ${SNAP_TYPE})"
83         echo
84         echo "Look at live-snapshot(1) man page for more information."
85
86         exit 0
87 }
88
89 Usage ()
90 {
91         Header
92
93         echo
94         echo "Try \"${PROGRAM} --help\" for more information."
95
96         exit 0
97 }
98
99 Version ()
100 {
101         echo "${PROGRAM}"
102         echo
103         echo "Copyright (C) 2006 Marco Amadori <marco.amadori@gmail.com>"
104         echo "Copyright (C) 2008 Chris Lamb <chris@chris-lamb.co.uk>"
105         echo
106         echo "This program is free software; you can redistribute it and/or modify"
107         echo "it under the terms of the GNU General Public License as published by"
108         echo "the Free Software Foundation; either version 2 of the License, or"
109         echo "(at your option) any later version."
110         echo
111         echo "This program is distributed in the hope that it will be useful,"
112         echo "but WITHOUT ANY WARRANTY; without even the implied warranty of"
113         echo "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"
114         echo "GNU General Public License for more details."
115         echo
116         echo "You should have received a copy of the GNU General Public License"
117         echo "along with this program; if not, write to the Free Software"
118         echo "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA"
119         echo
120         echo "On Debian systems, the complete text of the GNU General Public License"
121         echo "can be found in /usr/share/common-licenses/GPL-2 file."
122         echo
123         echo "Homepage: <http://debian-live.alioth.debian.org/>"
124
125         exit 0
126 }
127
128 Is_same_mount ()
129 {
130         dir1="$(Base_path ${1})"
131         dir2="$(Base_path ${2})"
132
133         if [ "${dir1}" = "${dir2}" ]
134         then
135                 return 0
136         else
137                 return 1
138         fi
139 }
140
141 Parse_args ()
142 {
143         # Parse command line
144         ARGS="${*}"
145         ARGUMENTS="$(getopt --longoptions cow:,device:,output,resync-string:,type:,help,usage,version --name=${PROGRAM} --options c:d:o:t:r:,h,u,v --shell sh -- ${ARGS})"
146
147         eval set -- "${ARGUMENTS}"
148
149         while true
150         do
151                 case "${1}" in
152                         -c|--cow)
153                                 SNAP_COW="${2}"
154                                 shift 2
155                                 ;;
156
157                         -d|--device)
158                                 SNAP_DEV="${2}"
159                                 shift 2
160                                 ;;
161
162                         -o|--output)
163                                 SNAP_OUTPUT="${2}"
164                                 shift 2
165                                 ;;
166
167                         -t|--type)
168                                 SNAP_TYPE="${2}"
169                                 shift 2
170                                 ;;
171
172                         -r|--resync-string)
173                                 SNAP_RESYNC_STRING="${2}"
174                                 break
175                                 ;;
176
177                         -h|--help)
178                                 Help
179                                 ;;
180
181                         -u|--usage)
182                                 Usage
183                                 ;;
184
185                         -v|--version)
186                                 Version
187                                 ;;
188
189                         --)
190                                 shift
191                                 break
192                                 ;;
193
194                         *)
195                                 Error "internal error."
196                                 ;;
197
198                 esac
199         done
200 }
201
202 Defaults ()
203 {
204         # Parse resync string
205         if [ -n "${SNAP_RESYNC_STRING}" ]
206         then
207                 SNAP_COW=$(echo "${SNAP_RESYNC_STRING}" | cut -f1 -d ':')
208                 SNAP_DEV=$(echo "${SNAP_RESYNC_STRING}" | cut -f2 -d ':')
209                 DEST="${MOUNTP}/$(echo ${SNAP_RESYNC_STRING} | cut -f3 -d ':')"
210
211                 case "${DEST}" in
212                         *.cpio.gz)
213                                 SNAP_TYPE="cpio"
214                                 ;;
215
216                         *.squashfs)
217                                 SNAP_TYPE="squashfs"
218                                 ;;
219
220                         *.jffs2)
221                                 SNAP_TYPE="jffs2"
222                                 ;;
223
224                         ""|*.ext2|*.ext3)
225                                 SNAP_TYPE="ext2"
226                                 ;;
227                         *)
228                                 Error "unrecognized resync string"
229                                 ;;
230                 esac
231         else
232                 # Set target file based on image
233                 case "${SNAP_TYPE}" in
234                         cpio)
235                                 DEST="${MOUNTP}/live-sn.cpio.gz"
236                                 ;;
237
238                         squashfs|jffs2|ext2)
239                                 DEST="${MOUNTP}/live-sn.${SNAP_TYPE}"
240                                 ;;
241
242                         ext3)
243                                 DEST="${MOUNTP}/live-sn.ext2"
244                                 ;;
245                 esac
246         fi
247 }
248
249 Validate_input ()
250 {
251         case "${SNAP_TYPE}" in
252                 cpio|squashfs|jffs2|ext2|ext3)
253                         ;;
254
255                 *)
256                         Error "invalid filesystem type \"${SNAP_TYPE}\""
257                         ;;
258         esac
259
260         if [ ! -d "${SNAP_COW}" ]
261         then
262                 Error "${SNAP_COW} is not a directory"
263         fi
264
265         if [ "$(id -u)" -ne 0 ]
266         then
267                 Error "you are not root"
268         fi
269 }
270
271 Mount_device ()
272 {
273         mkdir -p "${MOUNTP}"
274
275         case "${SNAP_DEV}" in
276                 "")
277                         # create a temp
278                         mount -t tmpfs -o rw tmpfs "${MOUNTP}"
279                         ;;
280
281                 *)
282                         if [ -b "${SNAP_DEV}" ]
283                         then
284                                 try_mount "${SNAP_DEV}" "${MOUNTP}" rw
285                         fi
286                         ;;
287         esac
288 }
289
290 Do_snapshot ()
291 {
292         case "${SNAP_TYPE}" in
293                 squashfs)
294                         EXCLUDE_LIST="$(mktemp -p ${SAFE_TMPDIR} live-snapshot-exclude-list.XXXXXX)"
295                         echo "./${EXCLUDE_LIST}" > "${EXCLUDE_LIST}"
296                         cd "${SNAP_COW}"
297                         find . -name '*.wh.*' >> "${EXCLUDE_LIST}"
298                         cd "${OLDPWD}"
299                         mksquashfs "${SNAP_COW}" "${DEST}" -ef "${EXCLUDE_LIST}"
300                         rm -f "${EXCLUDE_LIST}"
301                         ;;
302
303                 cpio)
304                         ( cd "${SNAP_COW}" && find . -path '*.wh.*' -prune -o -print0 | cpio --quiet -o0 -H newc | gzip -9c > "${DEST}" ) || exit 1
305                         ;;
306
307                 ext2|ext3)
308                         DU_DIM="$(du -ks ${SNAP_COW} | cut -f1)"
309                         REAL_DIM="$(expr ${DU_DIM} + ${DU_DIM} / 20)" # Just 5% more to be sure, need something more sophistcated here...
310                         genext2fs --size-in-blocks=${REAL_DIM} --reserved-percentage=0 --root="${SNAP_COW}" "${DEST}"
311                         ;;
312
313                 jffs2)
314                         mkfs.jffs2 --root="${SNAP_COW}" --output="${DEST}"
315                         ;;
316         esac
317 }
318
319 Clean ()
320 {
321         if echo "${DEST}" | grep -q "${MOUNTP}"
322         then
323                 echo "${DEST} is present on ${MOUNTP}, therefore no automatic unmounting the latter." > /dev/null 1>&2
324         else
325                 umount "${MOUNTP}"
326                 rmdir "${MOUNTP}"
327         fi
328 }
329
330 Main ()
331 {
332         Parse_args "${@}"
333         Defaults
334         Validate_input
335         trap 'Clean' EXIT
336         Mount_device
337         Do_snapshot
338 }
339
340 Main "${@:-}"