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