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"
33 if [ -n "${LIVE_SNAPSHOT_CHECK_UNBOUND}" ]
40 . /usr/share/initramfs-tools/scripts/live-helpers
42 LIVE_CONF="/etc/live.conf"
45 export USERNAME USERFULLNAME HOSTNAME
48 PROGRAM=$(basename "${EXECUTABLE}")
50 # Needs to be available at run and reboot time
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}"
65 SNAP_LIST="/etc/live-snapshot.list"
66 EXCLUDE_LIST="/etc/live-snapshot.exclude_list"
70 echo "${PROGRAM}: error:" ${@}
81 echo "${PROGRAM} - utility to perform snapshots of Debian Live systems"
83 echo "usage: ${PROGRAM} [-c|--cow DIRECTORY] [-d|--device DEVICE] [-o|--output FILE] [-t|--type TYPE]"
84 echo " ${PROGRAM} [-r|--resync-string STRING]"
85 echo " ${PROGRAM} [-f|--refresh]"
86 echo " ${PROGRAM} [-h|--help]"
87 echo " ${PROGRAM} [-u|--usage]"
88 echo " ${PROGRAM} [-v|--version]"
97 echo " -c, --cow: copy on write directory (default: ${SNAP_COW})."
98 echo " -d, --device: output snapshot device (default: ${SNAP_DEV:-auto})."
99 echo " -o, --output: output image file (default: ${DEST})."
100 echo " -r, --resync-string: internally used to resync previous made snapshots."
101 echo " -f, --refresh: try to sync a running snapshot."
102 echo " -t, --type: snapshot filesystem type. Options: \"squashfs\", \"ext2\", \"ext3\", \"jffs2\" or \"cpio\".gz archive (default: ${SNAP_TYPE})"
104 echo "Look at live-snapshot(1) man page for more information."
114 echo "Try \"${PROGRAM} --help\" for more information."
123 echo "Copyright (C) 2006 Marco Amadori <marco.amadori@gmail.com>"
124 echo "Copyright (C) 2008 Chris Lamb <chris@chris-lamb.co.uk>"
126 echo "This program is free software; you can redistribute it and/or modify"
127 echo "it under the terms of the GNU General Public License as published by"
128 echo "the Free Software Foundation; either version 2 of the License, or"
129 echo "(at your option) any later version."
131 echo "This program is distributed in the hope that it will be useful,"
132 echo "but WITHOUT ANY WARRANTY; without even the implied warranty of"
133 echo "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"
134 echo "GNU General Public License for more details."
136 echo "You should have received a copy of the GNU General Public License"
137 echo "along with this program; if not, write to the Free Software"
138 echo "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA"
140 echo "On Debian systems, the complete text of the GNU General Public License"
141 echo "can be found in /usr/share/common-licenses/GPL-2 file."
143 echo "Homepage: <http://debian-live.alioth.debian.org/>"
151 if [ -n "${ROOTSNAP}" ]; then
152 "${EXECUTABLE}" --resync-string="${ROOTSNAP}"
156 if [ -n "${HOMESNAP}" ]; then
157 "${EXECUTABLE}" --resync-string="${HOMESNAP}"
163 echo "No autoconfigured snapshots found at boot;" > /dev/null 1>&2
164 echo "(no resync string in ${LIVE_CONF})." > /dev/null 1>&2
173 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})"
175 eval set -- "${ARGUMENTS}"
201 SNAP_RESYNC_STRING="${2}"
228 Error "internal error."
237 # Parse resync string
238 if [ -n "${SNAP_RESYNC_STRING}" ]
240 SNAP_COW=$(echo "${SNAP_RESYNC_STRING}" | cut -f1 -d ':')
241 SNAP_DEV=$(echo "${SNAP_RESYNC_STRING}" | cut -f2 -d ':')
242 DEST="${MOUNTP}/$(echo ${SNAP_RESYNC_STRING} | cut -f3 -d ':')"
261 Error "unrecognized resync string"
264 elif [ -z "${SNAP_OUTPUT}" ]
266 # Set target file based on image
267 case "${SNAP_TYPE}" in
269 DEST="${MOUNTP}/live-sn.cpio.gz"
273 DEST="${MOUNTP}/live-sn.${SNAP_TYPE}"
277 DEST="${MOUNTP}/live-sn.ext2"
281 DEST="${SNAP_OUTPUT}"
287 case "${SNAP_TYPE}" in
288 cpio|squashfs|jffs2|ext2|ext3)
292 Error "invalid filesystem type \"${SNAP_TYPE}\""
296 if [ ! -d "${SNAP_COW}" ]
298 Error "${SNAP_COW} is not a directory"
301 if [ "$(id -u)" -ne 0 ]
303 Error "you are not root"
309 case "${SNAP_DEV}" in
312 mount -t tmpfs -o rw tmpfs "${MOUNTP}"
316 if [ -b "${SNAP_DEV}" ]
318 try_mount "${SNAP_DEV}" "${MOUNTP}" rw
326 # Returns true if file exists and it is also present in "cow" directory
327 # This means it is modified in respect to read-only media, so it deserve
332 if [ -e "${entry}" ] || [ -L "${entry}" ]
334 if [ -e "${DEF_SNAP_COW}/${entry}" ] || [ -L "${DEF_SNAP_COW}/${entry}" ]
344 # BUGS: supports only cpio.gz types, and does not handle deleted files yet
347 if [ -f "${SNAP_LIST}" ]
349 # Generate include list removing empty and commented lines
350 for entry in $(sed -e '/^ *$/d' -e '/^#.*$/d' "${SNAP_LIST}")
355 find "${entry}" | while read line
357 if Entry_is_modified "${line}"
359 printf "%s\000" "${line}" >> "${TMP_FILELIST}"
363 elif Entry_is_modified "${entry}"
365 # if file exists and it is modified
366 printf "%s\000" "${entry}" >> "${TMP_FILELIST}"
370 if [ "${SNAP_COW}" = "${DEF_SNAP_COW}" ]
380 find . -path '*.wh.*' -prune -o -print0 >> "${TMP_FILELIST}"
388 TMP_FILELIST=$(mktemp -p "${SAFE_TMPDIR}" "${TMP_FILELIST}.XXXXXX")
390 case "${SNAP_TYPE}" in
392 echo ".${TMP_FILELIST}" > "${TMP_FILELIST}"
393 # Removing whiteheads of unionfs
395 find . -name '*.wh.*' >> "${TMP_FILELIST}"
397 if [ -e "${EXCLUDE_LIST}" ]
399 # Add explicitly excluded files
400 grep -v '^#.*$' "${EXCLUDE_LIST}" | grep -v '^ *$' >> "${TMP_FILELIST}"
404 mksquashfs "${SNAP_COW}" "${DEST}" -ef "${TMP_FILELIST}"
408 WORKING_DIR=$(Do_filelist "${TMP_FILELIST}")
410 if [ -e "${EXCLUDE_LIST}" ]
412 # Convert \0 to \n and tag existing (rare but possible) \n in filenames,
413 # this to let grep -F -v do a proper work in filtering out
414 cat "${TMP_FILELIST}" | \
417 grep -F -v -f "${EXCLUDE_LIST}" | \
420 cpio --quiet -o0 -H newc | \
421 gzip -9c > "${DEST}" || exit 1
423 cat "${TMP_FILELIST}" | \
424 cpio --quiet -o0 -H newc | \
425 gzip -9c > "${DEST}" || exit 1
430 # ext2|ext3 and jffs2 does not easily support an exclude list; files
431 # should be copied to another directory in order to filter content
433 DU_DIM="$(du -ks ${SNAP_COW} | cut -f1)"
434 REAL_DIM="$(expr ${DU_DIM} + ${DU_DIM} / 20)" # Just 5% more to be sure, need something more sophistcated here...
435 genext2fs --size-in-blocks=${REAL_DIM} --reserved-percentage=0 --root="${SNAP_COW}" "${DEST}"
439 mkfs.jffs2 --root="${SNAP_COW}" --output="${DEST}"
443 if [ -f "${TMP_FILELIST}" ]
445 rm -f "${TMP_FILELIST}"
451 if [ -z "${SNAP_RESYNC_STRING}" ] && echo "${DEST}" | grep -q "${MOUNTP}"
453 echo "${DEST} is present on ${MOUNTP}, therefore no automatic unmounting the latter." > /dev/null 1>&2
462 if [ -z "${SNAP_RESYNC_STRING}" ]
466 echo "Please move ${DEST} (if is not already in it)" > /dev/null 1>&2
467 echo "in a supported writable partition (e.g ext3, vfat)." > /dev/null 1>&2
471 echo "To use ${DEST} you need to rebuild your media or add it" > /dev/null 1>&2
472 echo "to your multisession disc under the \"/live\" directory." > /dev/null 1>&2
476 echo "Please cat or flashcp ${DEST} to your partition in order to start using it." > /dev/null 1>&2
480 if grep -qv persistent /proc/cmdline
482 echo "Remember to boot this live system with \"persistent\" specified at boot prompt." > /dev/null 1>&2