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-boot 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"
33 if [ -n "${LIVE_SNAPSHOT_CHECK_UNBOUND}" ]
40 .
/usr
/share
/initramfs-tools
/scripts
/live-helpers
42 LIVE_CONF
="/etc/live.conf"
44 if [ -r "${LIVE_CONF}" ]
49 export USERNAME USERFULLNAME HOSTNAME
52 PROGRAM
=$
(basename "${EXECUTABLE}")
54 # Needs to be available at run and reboot time
57 # Permits multiple runs
58 MOUNTP
="$(mktemp -d -p ${SAFE_TMPDIR} live-snapshot-mnt.XXXXXX)"
59 DEST
="${MOUNTP}/live-sn.cpio.gz"
60 DEF_SNAP_COW
="/live/cow"
61 TMP_FILELIST
="${PROGRAM}.list"
63 # Command line defaults and declarations
64 SNAP_COW
="${DEF_SNAP_COW}"
70 SNAP_LIST
="/etc/live-snapshot.list"
71 EXCLUDE_LIST
="/etc/live-snapshot.exclude_list"
75 echo "${PROGRAM}: error:" ${@}
86 echo "${PROGRAM} - utility to perform snapshots of Debian Live systems"
88 echo "usage: ${PROGRAM} [-c|--cow DIRECTORY] [-d|--device DEVICE] [-o|--output FILE] [-t|--type TYPE]"
89 echo " ${PROGRAM} [-r|--resync-string STRING]"
90 echo " ${PROGRAM} [-f|--refresh]"
91 echo " ${PROGRAM} [-h|--help]"
92 echo " ${PROGRAM} [-u|--usage]"
93 echo " ${PROGRAM} [-v|--version]"
102 echo " -c, --cow: copy on write directory (default: ${SNAP_COW})."
103 echo " -d, --device: output snapshot device (default: ${SNAP_DEV:-auto})."
104 echo " -o, --output: output image file (default: ${DEST})."
105 echo " -r, --resync-string: internally used to resync previous made snapshots."
106 echo " -f, --refresh: try to sync a running snapshot."
107 echo " -t, --type: snapshot filesystem type. Options: \"squashfs\", \"ext2\", \"ext3\", \"ext4\", \"jffs2\" or \"cpio\".gz archive (default: ${SNAP_TYPE})"
109 echo "Look at live-snapshot(1) man page for more information."
119 echo "Try \"${PROGRAM} --help\" for more information."
128 echo "Copyright (C) 2006 Marco Amadori <marco.amadori@gmail.com>"
129 echo "Copyright (C) 2008 Chris Lamb <chris@chris-lamb.co.uk>"
131 echo "This program is free software; you can redistribute it and/or modify"
132 echo "it under the terms of the GNU General Public License as published by"
133 echo "the Free Software Foundation; either version 2 of the License, or"
134 echo "(at your option) any later version."
136 echo "This program is distributed in the hope that it will be useful,"
137 echo "but WITHOUT ANY WARRANTY; without even the implied warranty of"
138 echo "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"
139 echo "GNU General Public License for more details."
141 echo "You should have received a copy of the GNU General Public License"
142 echo "along with this program; if not, write to the Free Software"
143 echo "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA"
145 echo "On Debian systems, the complete text of the GNU General Public License"
146 echo "can be found in /usr/share/common-licenses/GPL-2 file."
148 echo "Homepage: <http://debian-live.alioth.debian.org/>"
156 if [ -n "${ROOTSNAP}" ]; then
157 "${EXECUTABLE}" --resync-string="${ROOTSNAP}"
161 if [ -n "${HOMESNAP}" ]; then
162 "${EXECUTABLE}" --resync-string="${HOMESNAP}"
168 echo "No autoconfigured snapshots found at boot;" > /dev
/null
1>&2
169 echo "(no resync string in ${LIVE_CONF})." > /dev
/null
1>&2
178 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})"
180 eval set -- "${ARGUMENTS}"
206 SNAP_RESYNC_STRING
="${2}"
233 Error
"internal error."
242 # Parse resync string
243 if [ -n "${SNAP_RESYNC_STRING}" ]
245 SNAP_COW
=$
(echo "${SNAP_RESYNC_STRING}" |
sed -e 's|^/root\([^:.]*\).*$|'"${DEF_SNAP_COW}"'\1|')
246 SNAP_DEV
=$
(echo "${SNAP_RESYNC_STRING}" | cut
-f2 -d ':')
247 SNAP_MNT
=$
(echo "${SNAP_RESYNC_STRING}" | cut
-f3 -d ':')
248 DEST
="${MOUNTP}/${SNAP_MNT}"
250 case "${SNAP_MNT}" in
268 SNAP_TYPE
="whole_partition"
276 Error
"unrecognized resync string"
279 elif [ -z "${SNAP_OUTPUT}" ]
281 # Set target file based on image
282 case "${SNAP_TYPE}" in
284 DEST
="${MOUNTP}/live-sn.cpio.gz"
288 DEST
="${MOUNTP}/live-sn.${SNAP_TYPE}"
292 DEST
="${MOUNTP}/live-sn.ext2"
296 DEST
="${MOUNTP}/live-sn.ext4"
300 DEST
="${SNAP_OUTPUT}"
306 case "${SNAP_TYPE}" in
307 cpio|squashfs|jffs2|ext2|ext3|ext4|whole_partition
)
311 Error
"invalid filesystem type \"${SNAP_TYPE}\""
315 if [ ! -d "${SNAP_COW}" ]
317 Error
"${SNAP_COW} is not a directory"
320 if [ "$(id -u)" -ne 0 ]
322 Error
"you are not root"
328 case "${SNAP_DEV}" in
331 mount
-t tmpfs
-o rw tmpfs
"${MOUNTP}"
335 if [ -b "${SNAP_DEV}" ]
337 try_mount
"${SNAP_DEV}" "${MOUNTP}" rw
345 # Returns true if file exists and it is also present in "cow" directory
346 # This means it is modified in respect to read-only media, so it deserve
351 if [ -e "${entry}" ] ||
[ -L "${entry}" ]
353 if [ -e "${SNAP_COW}/${entry}" ] || [ -L "${SNAP_COW}/${entry}" ]
363 # BUGS: does not handle deleted files yet
366 if [ -f "${SNAP_LIST}" ]
368 # if SNAP_COW == /live/cow/home, SNAP_RW = /home
369 SNAP_RW
=$
(echo "${SNAP_COW}" |
sed -e "s|${DEF_SNAP_COW}||g")
370 if [ -z "${SNAP_RW}" ]
376 # Generate include list removing empty and commented lines
377 # and transforming paths to relatives
378 for entry
in $
(sed -e '/^ *$/d' -e '/^#.*$/d' -e 's#^.*$#./&#' -e 's#/\+#/#g' "${SNAP_LIST}")
382 find "${entry}" |
while read line
384 if Entry_is_modified
"${line}"
386 printf "%s\000" "${line}" >> "${TMP_FILELIST}"
389 elif Entry_is_modified
"${entry}"
391 # if file exists and it is modified
392 printf "%s\000" "${entry}" >> "${TMP_FILELIST}"
401 # removing whiteouts from list
402 find .
-path '*.wh.*' -prune -o -print0 >> "${TMP_FILELIST}"
411 TMP_FILELIST
=$
(mktemp
-p "${SAFE_TMPDIR}" "${TMP_FILELIST}.XXXXXX")
413 case "${SNAP_TYPE}" in
415 echo ".${TMP_FILELIST}" > "${TMP_FILELIST}"
416 # Removing whiteheads of unionfs
418 find .
-name '*.wh.*' >> "${TMP_FILELIST}"
420 if [ -e "${EXCLUDE_LIST}" ]
422 # Add explicitly excluded files
423 grep -v '^#.*$' "${EXCLUDE_LIST}" |
grep -v '^ *$' >> "${TMP_FILELIST}"
427 mksquashfs
"${SNAP_COW}" "${DEST}" -ef "${TMP_FILELIST}"
430 cpio|whole_partition
)
431 if [ "${SNAP_TYPE}" = "cpio" ]
433 COPY_CMD
="cpio --quiet -o0 -H newc | gzip -9c > ${DEST}"
435 COPY_CMD
="cpio --quiet -pumd0 ${DEST}/"
438 WORKING_DIR
=$
(Do_filelist
"${TMP_FILELIST}")
440 if [ -e "${EXCLUDE_LIST}" ]
442 # Convert \0 to \n and tag existing (rare but possible) \n in filenames,
443 # this to let grep -F -v do a proper work in filtering out
444 cat "${TMP_FILELIST}" | \
447 grep -F -v -f "${EXCLUDE_LIST}" | \
450 eval $COPY_CMD ||
exit 1
452 cat "${TMP_FILELIST}" | \
453 eval $COPY_CMD ||
exit 1
458 # ext2|ext3|ext4 and jffs2 does not easily support an exclude list; files
459 # should be copied to another directory in order to filter content
461 DU_DIM
="$(du -ks ${SNAP_COW} | cut -f1)"
462 REAL_DIM
="$(expr ${DU_DIM} + ${DU_DIM} / 20)" # Just 5% more to be sure, need something more sophistcated here...
463 genext2fs
--size-in-blocks=${REAL_DIM} --reserved-percentage=0 --root="${SNAP_COW}" "${DEST}"
467 mkfs.jffs2 --root="${SNAP_COW}" --output="${DEST}"
471 if [ -f "${TMP_FILELIST}" ]
473 rm -f "${TMP_FILELIST}"
479 if [ -z "${SNAP_RESYNC_STRING}" ] && echo "${DEST}" | grep -q "${MOUNTP}"
481 echo "${DEST} is present on
${MOUNTP}, therefore no automatic unmounting the latter.
" > /dev/null 1>&2
490 if [ -z "${SNAP_RESYNC_STRING}" ]
494 echo "Please move
${DEST} (if is not already
in it
)" > /dev/null 1>&2
495 echo "in a supported writable partition
(e.g ext3
, vfat
).
" > /dev/null 1>&2
499 echo "To use
${DEST} you need to rebuild your media or add it
" > /dev/null 1>&2
500 echo "to your multisession disc under the
\"/live
\" directory.
" > /dev/null 1>&2
504 echo "Please
cat or flashcp
${DEST} to your partition
in order to start using it.
" > /dev/null 1>&2
508 if grep -qv persistent /proc/cmdline
510 echo "Remember to boot this live system with
\"persistent
\" specified
at boot prompt.
" > /dev/null 1>&2