Adding upstream version 1.157.3.
[debian-live-boot/hramrach.git] / bin / live-snapshot
blob18b067bda71924b5b3abea8501b7e5d45c189397
1 #!/bin/sh
3 # live-snapshot - utility to manage Debian Live systems snapshots
5 # This program mounts a device (fallback to /tmpfs under $MOUNTP
6 # and saves the /live/cow (or a different directory) filesystem in it
7 # for reuse in another live-initramfs session.
8 # Look at the manpage for more informations.
10 # Copyright (C) 2006-2008 Marco Amadori <marco.amadori@gmail.com>
11 # Copyright (C) 2008 Chris Lamb <chris@chris-lamb.co.uk>
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # On Debian systems, the complete text of the GNU General Public License
27 # can be found in /usr/share/common-licenses/GPL-3 file.
29 # declare here two vars from /etc/live.conf because of "set -u"
30 ROOTSNAP=""
31 HOMESNAP=""
33 if [ -n "${LIVE_SNAPSHOT_CHECK_UNBOUND}" ]
34 then
35 set -eu
36 else
37 set -e
40 . /usr/share/initramfs-tools/scripts/live-helpers
42 LIVE_CONF="/etc/live.conf"
43 . "${LIVE_CONF}"
45 export USERNAME USERFULLNAME HOSTNAME
47 EXECUTABLE="${0}"
48 PROGRAM=$(basename "${EXECUTABLE}")
50 # Needs to be available at run and reboot time
51 SAFE_TMPDIR="/live"
53 # Permits multiple runs
54 MOUNTP="$(mktemp -d -p ${SAFE_TMPDIR} live-snapshot-mnt.XXXXXX)"
55 DEST="${MOUNTP}/live-sn.cpio.gz"
56 DEF_SNAP_COW="/live/cow"
57 TMP_FILELIST="${PROGRAM}.list"
59 # Command line defaults and declarations
60 SNAP_COW="${DEF_SNAP_COW}"
61 SNAP_DEV=""
62 SNAP_MNT=""
63 SNAP_OUTPUT=""
64 SNAP_RESYNC_STRING=""
65 SNAP_TYPE="cpio"
66 SNAP_LIST="/etc/live-snapshot.list"
67 EXCLUDE_LIST="/etc/live-snapshot.exclude_list"
69 Error ()
71 echo "${PROGRAM}: error:" ${@}
72 exit 1
75 panic ()
77 Error ${@}
80 Header ()
82 echo "${PROGRAM} - utility to perform snapshots of Debian Live systems"
83 echo
84 echo "usage: ${PROGRAM} [-c|--cow DIRECTORY] [-d|--device DEVICE] [-o|--output FILE] [-t|--type TYPE]"
85 echo " ${PROGRAM} [-r|--resync-string STRING]"
86 echo " ${PROGRAM} [-f|--refresh]"
87 echo " ${PROGRAM} [-h|--help]"
88 echo " ${PROGRAM} [-u|--usage]"
89 echo " ${PROGRAM} [-v|--version]"
92 Help ()
94 Header
96 echo
97 echo "Options:"
98 echo " -c, --cow: copy on write directory (default: ${SNAP_COW})."
99 echo " -d, --device: output snapshot device (default: ${SNAP_DEV:-auto})."
100 echo " -o, --output: output image file (default: ${DEST})."
101 echo " -r, --resync-string: internally used to resync previous made snapshots."
102 echo " -f, --refresh: try to sync a running snapshot."
103 echo " -t, --type: snapshot filesystem type. Options: \"squashfs\", \"ext2\", \"ext3\", \"ext4\", \"jffs2\" or \"cpio\".gz archive (default: ${SNAP_TYPE})"
104 echo
105 echo "Look at live-snapshot(1) man page for more information."
107 exit 0
110 Usage ()
112 Header
114 echo
115 echo "Try \"${PROGRAM} --help\" for more information."
117 exit 0
120 Version ()
122 echo "${PROGRAM}"
123 echo
124 echo "Copyright (C) 2006 Marco Amadori <marco.amadori@gmail.com>"
125 echo "Copyright (C) 2008 Chris Lamb <chris@chris-lamb.co.uk>"
126 echo
127 echo "This program is free software; you can redistribute it and/or modify"
128 echo "it under the terms of the GNU General Public License as published by"
129 echo "the Free Software Foundation; either version 2 of the License, or"
130 echo "(at your option) any later version."
131 echo
132 echo "This program is distributed in the hope that it will be useful,"
133 echo "but WITHOUT ANY WARRANTY; without even the implied warranty of"
134 echo "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"
135 echo "GNU General Public License for more details."
136 echo
137 echo "You should have received a copy of the GNU General Public License"
138 echo "along with this program; if not, write to the Free Software"
139 echo "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA"
140 echo
141 echo "On Debian systems, the complete text of the GNU General Public License"
142 echo "can be found in /usr/share/common-licenses/GPL-2 file."
143 echo
144 echo "Homepage: <http://debian-live.alioth.debian.org/>"
146 exit 0
149 Try_refresh ()
151 FOUND=""
152 if [ -n "${ROOTSNAP}" ]; then
153 "${EXECUTABLE}" --resync-string="${ROOTSNAP}"
154 FOUND="Yes"
157 if [ -n "${HOMESNAP}" ]; then
158 "${EXECUTABLE}" --resync-string="${HOMESNAP}"
159 FOUND="Yes"
162 if [ -z "${FOUND}" ]
163 then
164 echo "No autoconfigured snapshots found at boot;" > /dev/null 1>&2
165 echo "(no resync string in ${LIVE_CONF})." > /dev/null 1>&2
166 exit 1
170 Parse_args ()
172 # Parse command line
173 ARGS="${*}"
174 ARGUMENTS="$(getopt --longoptions cow:,device:,output,resync-string:,refresh,type:,help,usage,version --name=${PROGRAM} --options c:d:o:t:r:fhuv --shell sh -- ${ARGS})"
176 eval set -- "${ARGUMENTS}"
178 while true
180 case "${1}" in
181 -c|--cow)
182 SNAP_COW="${2}"
183 shift 2
186 -d|--device)
187 SNAP_DEV="${2}"
188 shift 2
191 -o|--output)
192 SNAP_OUTPUT="${2}"
193 shift 2
196 -t|--type)
197 SNAP_TYPE="${2}"
198 shift 2
201 -r|--resync-string)
202 SNAP_RESYNC_STRING="${2}"
203 break
206 -f|--refresh)
207 Try_refresh
208 exit 0
211 -h|--help)
212 Help
215 -u|--usage)
216 Usage
219 -v|--version)
220 Version
224 shift
225 break
229 Error "internal error."
232 esac
233 done
236 Defaults ()
238 # Parse resync string
239 if [ -n "${SNAP_RESYNC_STRING}" ]
240 then
241 SNAP_COW=$(echo "${SNAP_RESYNC_STRING}" | sed -e 's|^/root\([^:.]*\).*$|'"${DEF_SNAP_COW}"'\1|')
242 SNAP_DEV=$(echo "${SNAP_RESYNC_STRING}" | cut -f2 -d ':')
243 SNAP_MNT=$(echo "${SNAP_RESYNC_STRING}" | cut -f3 -d ':')
244 DEST="${MOUNTP}/${SNAP_MNT}"
246 case "${SNAP_MNT}" in
247 *.cpio.gz)
248 SNAP_TYPE="cpio"
251 *.squashfs)
252 SNAP_TYPE="squashfs"
255 *.jffs2)
256 SNAP_TYPE="jffs2"
259 *.ext2|*.ext3)
260 SNAP_TYPE="ext2"
264 SNAP_TYPE="whole_partition"
267 *.ext4)
268 SNAP_TYPE="ext4"
272 Error "unrecognized resync string"
274 esac
275 elif [ -z "${SNAP_OUTPUT}" ]
276 then
277 # Set target file based on image
278 case "${SNAP_TYPE}" in
279 cpio)
280 DEST="${MOUNTP}/live-sn.cpio.gz"
283 squashfs|jffs2|ext2)
284 DEST="${MOUNTP}/live-sn.${SNAP_TYPE}"
287 ext3)
288 DEST="${MOUNTP}/live-sn.ext2"
291 ext4)
292 DEST="${MOUNTP}/live-sn.ext4"
294 esac
295 else
296 DEST="${SNAP_OUTPUT}"
300 Validate_input ()
302 case "${SNAP_TYPE}" in
303 cpio|squashfs|jffs2|ext2|ext3|ext4|whole_partition)
307 Error "invalid filesystem type \"${SNAP_TYPE}\""
309 esac
311 if [ ! -d "${SNAP_COW}" ]
312 then
313 Error "${SNAP_COW} is not a directory"
316 if [ "$(id -u)" -ne 0 ]
317 then
318 Error "you are not root"
322 Mount_device ()
324 case "${SNAP_DEV}" in
326 # create a temp
327 mount -t tmpfs -o rw tmpfs "${MOUNTP}"
331 if [ -b "${SNAP_DEV}" ]
332 then
333 try_mount "${SNAP_DEV}" "${MOUNTP}" rw
336 esac
339 Entry_is_modified ()
341 # Returns true if file exists and it is also present in "cow" directory
342 # This means it is modified in respect to read-only media, so it deserve
343 # to be saved
345 entry="${1}"
347 if [ -e "${entry}" ] || [ -L "${entry}" ]
348 then
349 if [ -e "${SNAP_COW}/${entry}" ] || [ -L "${SNAP_COW}/${entry}" ]
350 then
351 return 0
354 return 1
357 Do_filelist ()
359 # BUGS: does not handle deleted files yet
360 TMP_FILELIST=$1
362 if [ -f "${SNAP_LIST}" ]
363 then
364 # if SNAP_COW == /live/cow/home, SNAP_RW = /home
365 SNAP_RW=$(echo "${SNAP_COW}" | sed -e "s|${DEF_SNAP_COW}||g")
366 if [ -z "${SNAP_RW}" ]
367 then
368 SNAP_RW="/"
371 cd "${SNAP_RW}"
372 # Generate include list removing empty and commented lines
373 # and transforming paths to relatives
374 for entry in $(sed -e '/^ *$/d' -e '/^#.*$/d' -e 's#^.*$#./&#' -e 's#/\+#/#g' "${SNAP_LIST}")
376 if [ -d "${entry}" ]
377 then
378 find "${entry}" | while read line
380 if Entry_is_modified "${line}"
381 then
382 printf "%s\000" "${line}" >> "${TMP_FILELIST}"
384 done
385 elif Entry_is_modified "${entry}"
386 then
387 # if file exists and it is modified
388 printf "%s\000" "${entry}" >> "${TMP_FILELIST}"
390 done
391 cd "${OLDPWD}"
393 # echo Working dir
394 echo "${SNAP_RW}"
395 else
396 cd "${SNAP_COW}"
397 # removing whiteouts from list
398 find . -path '*.wh.*' -prune -o -print0 >> "${TMP_FILELIST}"
399 cd "${OLDPWD}"
400 # echo Working dir
401 echo "${SNAP_COW}"
405 Do_snapshot ()
407 TMP_FILELIST=$(mktemp -p "${SAFE_TMPDIR}" "${TMP_FILELIST}.XXXXXX")
409 case "${SNAP_TYPE}" in
410 squashfs)
411 echo ".${TMP_FILELIST}" > "${TMP_FILELIST}"
412 # Removing whiteheads of unionfs
413 cd "${SNAP_COW}"
414 find . -name '*.wh.*' >> "${TMP_FILELIST}"
416 if [ -e "${EXCLUDE_LIST}" ]
417 then
418 # Add explicitly excluded files
419 grep -v '^#.*$' "${EXCLUDE_LIST}" | grep -v '^ *$' >> "${TMP_FILELIST}"
422 cd "${OLDPWD}"
423 mksquashfs "${SNAP_COW}" "${DEST}" -ef "${TMP_FILELIST}"
426 cpio|whole_partition)
427 if [ "${SNAP_TYPE}" = "cpio" ]
428 then
429 COPY_CMD="cpio --quiet -o0 -H newc | gzip -9c > ${DEST}"
430 else
431 COPY_CMD="cpio --quiet -pumd0 ${DEST}/"
434 WORKING_DIR=$(Do_filelist "${TMP_FILELIST}")
435 cd "${WORKING_DIR}"
436 if [ -e "${EXCLUDE_LIST}" ]
437 then
438 # Convert \0 to \n and tag existing (rare but possible) \n in filenames,
439 # this to let grep -F -v do a proper work in filtering out
440 cat "${TMP_FILELIST}" | \
441 tr '\n' '\1' | \
442 tr '\0' '\n' | \
443 grep -F -v -f "${EXCLUDE_LIST}" | \
444 tr '\n' '\0' | \
445 tr '\1' '\n' | \
446 $COPY_CMD || exit 1
447 else
448 cat "${TMP_FILELIST}" | \
449 $COPY_CMD || exit 1
451 cd "${OLDPWD}"
454 # ext2|ext3|ext4 and jffs2 does not easily support an exclude list; files
455 # should be copied to another directory in order to filter content
456 ext2|ext3|ext4)
457 DU_DIM="$(du -ks ${SNAP_COW} | cut -f1)"
458 REAL_DIM="$(expr ${DU_DIM} + ${DU_DIM} / 20)" # Just 5% more to be sure, need something more sophistcated here...
459 genext2fs --size-in-blocks=${REAL_DIM} --reserved-percentage=0 --root="${SNAP_COW}" "${DEST}"
462 jffs2)
463 mkfs.jffs2 --root="${SNAP_COW}" --output="${DEST}"
465 esac
467 if [ -f "${TMP_FILELIST}" ]
468 then
469 rm -f "${TMP_FILELIST}"
473 Clean ()
475 if [ -z "${SNAP_RESYNC_STRING}" ] && echo "${DEST}" | grep -q "${MOUNTP}"
476 then
477 echo "${DEST} is present on ${MOUNTP}, therefore no automatic unmounting the latter." > /dev/null 1>&2
478 else
479 umount "${MOUNTP}"
480 rmdir "${MOUNTP}"
484 Warn_user ()
486 if [ -z "${SNAP_RESYNC_STRING}" ]
487 then
488 case ${SNAP_TYPE} in
489 cpio|ext2|ext3|ext4)
490 echo "Please move ${DEST} (if is not already in it)" > /dev/null 1>&2
491 echo "in a supported writable partition (e.g ext3, vfat)." > /dev/null 1>&2
494 squashfs)
495 echo "To use ${DEST} you need to rebuild your media or add it" > /dev/null 1>&2
496 echo "to your multisession disc under the \"/live\" directory." > /dev/null 1>&2
499 jffs2)
500 echo "Please cat or flashcp ${DEST} to your partition in order to start using it." > /dev/null 1>&2
502 esac
504 if grep -qv persistent /proc/cmdline
505 then
506 echo "Remember to boot this live system with \"persistent\" specified at boot prompt." > /dev/null 1>&2
511 Main ()
513 Parse_args "${@}"
514 Defaults
515 Validate_input
516 trap 'Clean' EXIT
517 Mount_device
518 Do_snapshot
519 Warn_user
522 Main "${@:-}"