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/overlway (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-2011 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 # 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 ## Begin FIXME: this is an embedded copy of the old 'live-helpers' initramfs script
41 if [ ! -x "/bin/fstype" ]
43 # klibc not in path -> not in initramfs
44 export PATH="${PATH}:/usr/lib/klibc/bin"
47 # handle upgrade path from old udev (using udevinfo) to
48 # recent versions of udev (using udevadm info)
49 if [ -x /sbin/udevadm ]
51 udevinfo='/sbin/udevadm info'
56 old_root_overlay_label="live-rw"
57 old_home_overlay_label="home-rw"
58 custom_overlay_label="custom-ov"
59 root_snapshot_label="live-sn"
60 old_root_snapshot_label="live-sn"
61 home_snapshot_label="home-sn"
62 persistence_list="live-persistence.conf"
64 # include all scripts for the time being until snapshots are either dropped or cleaned up
65 for _SCRIPT in /lib/live/boot/*
67 if [ -e "${_SCRIPT}" ]
72 ## End FIXME: this is an embedded copy of the old 'live-helpers' initramfs script
74 LIVE_CONF="/etc/live/boot.d/snapshot.conf"
76 if [ -r "${LIVE_CONF}" ]
81 export USERNAME USERFULLNAME HOSTNAME
84 PROGRAM=$(basename "${EXECUTABLE}")
86 # Needs to be available at run and reboot time
89 # Permits multiple runs
90 MOUNTP="$(mktemp -d -p ${SAFE_TMPDIR} live-snapshot-mnt.XXXXXX)"
91 DEST="${MOUNTP}/live-sn.cpio.gz"
92 DEF_SNAP_COW="/live/overlay"
93 TMP_FILELIST="${PROGRAM}.list"
95 # Command line defaults and declarations
96 SNAP_COW="${DEF_SNAP_COW}"
100 SNAP_RESYNC_STRING=""
102 SNAP_LIST="/etc/live-snapshot.list"
103 EXCLUDE_LIST="/etc/live-snapshot.exclude_list"
107 echo "${PROGRAM}: error:" ${@}
118 echo "${PROGRAM} - utility to perform snapshots of Debian Live systems"
120 echo "usage: ${PROGRAM} [-c|--cow DIRECTORY] [-d|--device DEVICE] [-o|--output FILE] [-t|--type TYPE]"
121 echo " ${PROGRAM} [-r|--resync-string STRING]"
122 echo " ${PROGRAM} [-f|--refresh]"
123 echo " ${PROGRAM} [-h|--help]"
124 echo " ${PROGRAM} [-u|--usage]"
125 echo " ${PROGRAM} [-v|--version]"
134 echo " -c, --cow: copy on write directory (default: ${SNAP_COW})."
135 echo " -d, --device: output snapshot device (default: ${SNAP_DEV:-auto})."
136 echo " -o, --output: output image file (default: ${DEST})."
137 echo " -r, --resync-string: internally used to resync previous made snapshots."
138 echo " -f, --refresh: try to sync a running snapshot."
139 echo " -t, --type: snapshot filesystem type. Options: \"squashfs\", \"ext2\", \"ext3\", \"ext4\", \"jffs2\" or \"cpio\".gz archive (default: ${SNAP_TYPE})"
141 echo "Look at live-snapshot(1) man page for more information."
151 echo "Try \"${PROGRAM} --help\" for more information."
160 echo "Copyright (C) 2006-2011 Marco Amadori <marco.amadori@gmail.com>"
161 echo "Copyright (C) 2008 Chris Lamb <chris@chris-lamb.co.uk>"
163 echo "This program is free software; you can redistribute it and/or modify"
164 echo "it under the terms of the GNU General Public License as published by"
165 echo "the Free Software Foundation; either version 3 of the License, or"
166 echo "(at your option) any later version."
168 echo "This program is distributed in the hope that it will be useful,"
169 echo "but WITHOUT ANY WARRANTY; without even the implied warranty of"
170 echo "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"
171 echo "GNU General Public License for more details."
173 echo "You should have received a copy of the GNU General Public License"
174 echo "along with this program; if not, write to the Free Software"
175 echo "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA"
177 echo "The complete text of the GNU General Public License"
178 echo "can be found in /usr/share/common-licenses/GPL-3 file."
180 echo "Homepage: <http://live.debian.net/>"
188 if [ -n "${ROOTSNAP}" ]; then
189 "${EXECUTABLE}" --resync-string="${ROOTSNAP}"
193 if [ -n "${HOMESNAP}" ]; then
194 "${EXECUTABLE}" --resync-string="${HOMESNAP}"
200 echo "No autoconfigured snapshots found at boot;" > /dev/null 1>&2
201 echo "(no resync string in ${LIVE_CONF})." > /dev/null 1>&2
210 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})"
212 eval set -- "${ARGUMENTS}"
238 SNAP_RESYNC_STRING="${2}"
265 Error "internal error."
274 # Parse resync string
275 if [ -n "${SNAP_RESYNC_STRING}" ]
277 SNAP_COW=$(echo "${SNAP_RESYNC_STRING}" | sed -r -e 's#^([^:]*).*$#'"${DEF_SNAP_COW}"'\1#')
278 SNAP_DEV=$(echo "${SNAP_RESYNC_STRING}" | cut -f2 -d ':')
279 SNAP_MNT=$(echo "${SNAP_RESYNC_STRING}" | cut -f3 -d ':')
280 DEST="${MOUNTP}/${SNAP_MNT}"
282 case "${SNAP_MNT}" in
300 SNAP_TYPE="whole_partition"
308 Error "unrecognized resync string"
311 elif [ -z "${SNAP_OUTPUT}" ]
313 # Set target file based on image
314 case "${SNAP_TYPE}" in
316 DEST="${MOUNTP}/live-sn.cpio.gz"
320 DEST="${MOUNTP}/live-sn.${SNAP_TYPE}"
324 DEST="${MOUNTP}/live-sn.ext2"
328 DEST="${MOUNTP}/live-sn.ext4"
332 DEST="${SNAP_OUTPUT}"
338 case "${SNAP_TYPE}" in
339 cpio|squashfs|jffs2|ext2|ext3|ext4|whole_partition)
343 Error "invalid filesystem type \"${SNAP_TYPE}\""
347 if [ ! -d "${SNAP_COW}" ]
349 Error "${SNAP_COW} is not a directory"
352 if [ "$(id -u)" -ne 0 ]
354 Error "you are not root"
360 case "${SNAP_DEV}" in
363 mount -t tmpfs -o rw tmpfs "${MOUNTP}"
367 if [ -b "${SNAP_DEV}" ]
369 try_mount "${SNAP_DEV}" "${MOUNTP}" rw
377 # Returns true if file exists and it is also present in "cow" directory
378 # This means it is modified in respect to read-only media, so it deserve
383 if [ -e "${entry}" ] || [ -L "${entry}" ]
385 if [ -e "${SNAP_COW}/${entry}" ] || [ -L "${SNAP_COW}/${entry}" ]
395 # BUGS: does not handle deleted files yet
398 if [ -f "${SNAP_LIST}" ]
400 # if SNAP_COW == /live/overlay/home, SNAP_RW = /home
401 SNAP_RW=$(echo "${SNAP_COW}" | sed -e "s|${DEF_SNAP_COW}||g")
402 if [ -z "${SNAP_RW}" ]
408 # Generate include list removing empty and commented lines
409 # and transforming paths to relatives
410 for entry in $(sed -e '/^ *$/d' -e '/^#.*$/d' -e 's#^.*$#./&#' -e 's#/\+#/#g' "${SNAP_LIST}")
414 find "${entry}" | while read line
416 if Entry_is_modified "${line}"
418 printf "%s\000" "${line}" >> "${TMP_FILELIST}"
421 elif Entry_is_modified "${entry}"
423 # if file exists and it is modified
424 printf "%s\000" "${entry}" >> "${TMP_FILELIST}"
433 # removing whiteouts from list
434 find . -path '*.wh.*' -prune -o -print0 >> "${TMP_FILELIST}"
443 TMP_FILELIST=$(mktemp -p "${SAFE_TMPDIR}" "${TMP_FILELIST}.XXXXXX")
444 if [ -e "${EXCLUDE_LIST}" ]
446 # Create a TMP filelist removing empty lines (grep -f does not like them)
447 # and comments (for speedup and LST)
448 TMP_EXCLUDE_LIST=$(mktemp -p "${SAFE_TMPDIR}" "${PROGRAM}_excludelist.XXXXXX")
449 grep -v '^#.*$' "${EXCLUDE_LIST}" | grep -v '^ *$' > "${TMP_EXCLUDE_LIST}"
452 case "${SNAP_TYPE}" in
454 echo ".${TMP_FILELIST}" > "${TMP_FILELIST}"
455 # Removing whiteheads of unionfs
457 find . -name '*.wh.*' >> "${TMP_FILELIST}"
459 if [ -e "${EXCLUDE_LIST}" ]
461 # Add explicitly excluded files
462 cat "${TMP_EXCLUDE_LIST}" >> "${TMP_FILELIST}"
466 mksquashfs "${SNAP_COW}" "${DEST}" -ef "${TMP_FILELIST}"
469 cpio|whole_partition)
470 if [ "${SNAP_TYPE}" = "cpio" ]
472 COPY_CMD="cpio --quiet -o0 -H newc | gzip -9c > ${DEST}"
474 COPY_CMD="cpio --quiet -pumd0 ${DEST}/"
477 WORKING_DIR=$(Do_filelist "${TMP_FILELIST}")
479 if [ -e "${EXCLUDE_LIST}" ]
482 # Convert \0 to \n and tag existing (rare but possible) \n in filenames,
483 # this to let grep -F -v do a proper work in filtering out
484 cat "${TMP_FILELIST}" | \
487 grep -F -v -f "${TMP_EXCLUDE_LIST}" | \
490 eval $COPY_CMD || exit 1
492 cat "${TMP_FILELIST}" | \
493 eval $COPY_CMD || exit 1
498 # ext2|ext3|ext4 and jffs2 does not easily support an exclude list; files
499 # should be copied to another directory in order to filter content
501 DU_DIM="$(du -ks ${SNAP_COW} | cut -f1)"
502 REAL_DIM="$(expr ${DU_DIM} + ${DU_DIM} / 20)" # Just 5% more to be sure, need something more sophistcated here...
503 genext2fs --size-in-blocks=${REAL_DIM} --reserved-percentage=0 --root="${SNAP_COW}" "${DEST}"
507 mkfs.jffs2 --root="${SNAP_COW}" --output="${DEST}"
511 # Remove temporary file lists
512 for filelist in "${TMP_FILELIST}" "${TMP_EXCLUDE_LIST}"
514 if [ -f "${filelist}" ]
523 if [ -z "${SNAP_RESYNC_STRING}" ] && echo "${DEST}" | grep -q "${MOUNTP}"
525 echo "${DEST} is present on ${MOUNTP}, therefore no automatic unmounting the latter." > /dev/null 1>&2
534 if [ -z "${SNAP_RESYNC_STRING}" ]
538 echo "Please move ${DEST} (if is not already in it)" > /dev/null 1>&2
539 echo "in a supported writable partition (e.g ext3, vfat)." > /dev/null 1>&2
543 echo "To use ${DEST} you need to rebuild your media or add it" > /dev/null 1>&2
544 echo "to your multisession disc under the \"/live\" directory." > /dev/null 1>&2
548 echo "Please cat or flashcp ${DEST} to your partition in order to start using it." > /dev/null 1>&2
552 if grep -qv persistence /proc/cmdline
554 echo "Remember to boot this live system with \"persistence\" specified at boot prompt." > /dev/null 1>&2