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\", \"ext4\", \"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 ':')"
266 Error "unrecognized resync string"
269 elif [ -z "${SNAP_OUTPUT}" ]
271 # Set target file based on image
272 case "${SNAP_TYPE}" in
274 DEST="${MOUNTP}/live-sn.cpio.gz"
278 DEST="${MOUNTP}/live-sn.${SNAP_TYPE}"
282 DEST="${MOUNTP}/live-sn.ext2"
286 DEST="${MOUNTP}/live-sn.ext4"
290 DEST="${SNAP_OUTPUT}"
296 case "${SNAP_TYPE}" in
297 cpio|squashfs|jffs2|ext2|ext3|ext4)
301 Error "invalid filesystem type \"${SNAP_TYPE}\""
305 if [ ! -d "${SNAP_COW}" ]
307 Error "${SNAP_COW} is not a directory"
310 if [ "$(id -u)" -ne 0 ]
312 Error "you are not root"
318 case "${SNAP_DEV}" in
321 mount -t tmpfs -o rw tmpfs "${MOUNTP}"
325 if [ -b "${SNAP_DEV}" ]
327 try_mount "${SNAP_DEV}" "${MOUNTP}" rw
335 # Returns true if file exists and it is also present in "cow" directory
336 # This means it is modified in respect to read-only media, so it deserve
341 if [ -e "${entry}" ] || [ -L "${entry}" ]
343 if [ -e "${DEF_SNAP_COW}/${entry}" ] || [ -L "${DEF_SNAP_COW}/${entry}" ]
353 # BUGS: supports only cpio.gz types, and does not handle deleted files yet
356 if [ -f "${SNAP_LIST}" ]
358 # Generate include list removing empty and commented lines
359 for entry in $(sed -e '/^ *$/d' -e '/^#.*$/d' "${SNAP_LIST}")
364 find "${entry}" | while read line
366 if Entry_is_modified "${line}"
368 printf "%s\000" "${line}" >> "${TMP_FILELIST}"
372 elif Entry_is_modified "${entry}"
374 # if file exists and it is modified
375 printf "%s\000" "${entry}" >> "${TMP_FILELIST}"
379 if [ "${SNAP_COW}" = "${DEF_SNAP_COW}" ]
389 find . -path '*.wh.*' -prune -o -print0 >> "${TMP_FILELIST}"
397 TMP_FILELIST=$(mktemp -p "${SAFE_TMPDIR}" "${TMP_FILELIST}.XXXXXX")
399 case "${SNAP_TYPE}" in
401 echo ".${TMP_FILELIST}" > "${TMP_FILELIST}"
402 # Removing whiteheads of unionfs
404 find . -name '*.wh.*' >> "${TMP_FILELIST}"
406 if [ -e "${EXCLUDE_LIST}" ]
408 # Add explicitly excluded files
409 grep -v '^#.*$' "${EXCLUDE_LIST}" | grep -v '^ *$' >> "${TMP_FILELIST}"
413 mksquashfs "${SNAP_COW}" "${DEST}" -ef "${TMP_FILELIST}"
417 WORKING_DIR=$(Do_filelist "${TMP_FILELIST}")
419 if [ -e "${EXCLUDE_LIST}" ]
421 # Convert \0 to \n and tag existing (rare but possible) \n in filenames,
422 # this to let grep -F -v do a proper work in filtering out
423 cat "${TMP_FILELIST}" | \
426 grep -F -v -f "${EXCLUDE_LIST}" | \
429 cpio --quiet -o0 -H newc | \
430 gzip -9c > "${DEST}" || exit 1
432 cat "${TMP_FILELIST}" | \
433 cpio --quiet -o0 -H newc | \
434 gzip -9c > "${DEST}" || exit 1
439 # ext2|ext3|ext4 and jffs2 does not easily support an exclude list; files
440 # should be copied to another directory in order to filter content
442 DU_DIM="$(du -ks ${SNAP_COW} | cut -f1)"
443 REAL_DIM="$(expr ${DU_DIM} + ${DU_DIM} / 20)" # Just 5% more to be sure, need something more sophistcated here...
444 genext2fs --size-in-blocks=${REAL_DIM} --reserved-percentage=0 --root="${SNAP_COW}" "${DEST}"
448 mkfs.jffs2 --root="${SNAP_COW}" --output="${DEST}"
452 if [ -f "${TMP_FILELIST}" ]
454 rm -f "${TMP_FILELIST}"
460 if [ -z "${SNAP_RESYNC_STRING}" ] && echo "${DEST}" | grep -q "${MOUNTP}"
462 echo "${DEST} is present on ${MOUNTP}, therefore no automatic unmounting the latter." > /dev/null 1>&2
471 if [ -z "${SNAP_RESYNC_STRING}" ]
475 echo "Please move ${DEST} (if is not already in it)" > /dev/null 1>&2
476 echo "in a supported writable partition (e.g ext3, vfat)." > /dev/null 1>&2
480 echo "To use ${DEST} you need to rebuild your media or add it" > /dev/null 1>&2
481 echo "to your multisession disc under the \"/live\" directory." > /dev/null 1>&2
485 echo "Please cat or flashcp ${DEST} to your partition in order to start using it." > /dev/null 1>&2
489 if grep -qv persistent /proc/cmdline
491 echo "Remember to boot this live system with \"persistent\" specified at boot prompt." > /dev/null 1>&2