live-snapshot: removed a useless mkdir.
[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 Parse_args ()
129 {
130         # Parse command line
131         ARGS="${*}"
132         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})"
133
134         eval set -- "${ARGUMENTS}"
135
136         while true
137         do
138                 case "${1}" in
139                         -c|--cow)
140                                 SNAP_COW="${2}"
141                                 shift 2
142                                 ;;
143
144                         -d|--device)
145                                 SNAP_DEV="${2}"
146                                 shift 2
147                                 ;;
148
149                         -o|--output)
150                                 SNAP_OUTPUT="${2}"
151                                 shift 2
152                                 ;;
153
154                         -t|--type)
155                                 SNAP_TYPE="${2}"
156                                 shift 2
157                                 ;;
158
159                         -r|--resync-string)
160                                 SNAP_RESYNC_STRING="${2}"
161                                 break
162                                 ;;
163
164                         -h|--help)
165                                 Help
166                                 ;;
167
168                         -u|--usage)
169                                 Usage
170                                 ;;
171
172                         -v|--version)
173                                 Version
174                                 ;;
175
176                         --)
177                                 shift
178                                 break
179                                 ;;
180
181                         *)
182                                 Error "internal error."
183                                 ;;
184
185                 esac
186         done
187 }
188
189 Defaults ()
190 {
191         # Parse resync string
192         if [ -n "${SNAP_RESYNC_STRING}" ]
193         then
194                 SNAP_COW=$(echo "${SNAP_RESYNC_STRING}" | cut -f1 -d ':')
195                 SNAP_DEV=$(echo "${SNAP_RESYNC_STRING}" | cut -f2 -d ':')
196                 DEST="${MOUNTP}/$(echo ${SNAP_RESYNC_STRING} | cut -f3 -d ':')"
197
198                 case "${DEST}" in
199                         *.cpio.gz)
200                                 SNAP_TYPE="cpio"
201                                 ;;
202
203                         *.squashfs)
204                                 SNAP_TYPE="squashfs"
205                                 ;;
206
207                         *.jffs2)
208                                 SNAP_TYPE="jffs2"
209                                 ;;
210
211                         ""|*.ext2|*.ext3)
212                                 SNAP_TYPE="ext2"
213                                 ;;
214                         *)
215                                 Error "unrecognized resync string"
216                                 ;;
217                 esac
218         else
219                 # Set target file based on image
220                 case "${SNAP_TYPE}" in
221                         cpio)
222                                 DEST="${MOUNTP}/live-sn.cpio.gz"
223                                 ;;
224
225                         squashfs|jffs2|ext2)
226                                 DEST="${MOUNTP}/live-sn.${SNAP_TYPE}"
227                                 ;;
228
229                         ext3)
230                                 DEST="${MOUNTP}/live-sn.ext2"
231                                 ;;
232                 esac
233         fi
234 }
235
236 Validate_input ()
237 {
238         case "${SNAP_TYPE}" in
239                 cpio|squashfs|jffs2|ext2|ext3)
240                         ;;
241
242                 *)
243                         Error "invalid filesystem type \"${SNAP_TYPE}\""
244                         ;;
245         esac
246
247         if [ ! -d "${SNAP_COW}" ]
248         then
249                 Error "${SNAP_COW} is not a directory"
250         fi
251
252         if [ "$(id -u)" -ne 0 ]
253         then
254                 Error "you are not root"
255         fi
256 }
257
258 Mount_device ()
259 {
260         case "${SNAP_DEV}" in
261                 "")
262                         # create a temp
263                         mount -t tmpfs -o rw tmpfs "${MOUNTP}"
264                         ;;
265
266                 *)
267                         if [ -b "${SNAP_DEV}" ]
268                         then
269                                 try_mount "${SNAP_DEV}" "${MOUNTP}" rw
270                         fi
271                         ;;
272         esac
273 }
274
275 Do_snapshot ()
276 {
277         case "${SNAP_TYPE}" in
278                 squashfs)
279                         EXCLUDE_LIST="$(mktemp -p ${SAFE_TMPDIR} live-snapshot-exclude-list.XXXXXX)"
280                         echo "./${EXCLUDE_LIST}" > "${EXCLUDE_LIST}"
281                         cd "${SNAP_COW}"
282                         find . -name '*.wh.*' >> "${EXCLUDE_LIST}"
283                         cd "${OLDPWD}"
284                         mksquashfs "${SNAP_COW}" "${DEST}" -ef "${EXCLUDE_LIST}"
285                         rm -f "${EXCLUDE_LIST}"
286                         ;;
287
288                 cpio)
289                         ( cd "${SNAP_COW}" && find . -path '*.wh.*' -prune -o -print0 | cpio --quiet -o0 -H newc | gzip -9c > "${DEST}" ) || exit 1
290                         ;;
291
292                 ext2|ext3)
293                         DU_DIM="$(du -ks ${SNAP_COW} | cut -f1)"
294                         REAL_DIM="$(expr ${DU_DIM} + ${DU_DIM} / 20)" # Just 5% more to be sure, need something more sophistcated here...
295                         genext2fs --size-in-blocks=${REAL_DIM} --reserved-percentage=0 --root="${SNAP_COW}" "${DEST}"
296                         ;;
297
298                 jffs2)
299                         mkfs.jffs2 --root="${SNAP_COW}" --output="${DEST}"
300                         ;;
301         esac
302 }
303
304 Clean ()
305 {
306         if echo "${DEST}" | grep -q "${MOUNTP}"
307         then
308                 echo "${DEST} is present on ${MOUNTP}, therefore no automatic unmounting the latter." > /dev/null 1>&2
309         else
310                 umount "${MOUNTP}"
311                 rmdir "${MOUNTP}"
312         fi
313 }
314
315 Main ()
316 {
317         Parse_args "${@}"
318         Defaults
319         Validate_input
320         trap 'Clean' EXIT
321         Mount_device
322         Do_snapshot
323 }
324
325 Main "${@:-}"