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}"
66 SNAP_LIST
="/etc/live-snapshot.list"
67 EXCLUDE_LIST
="/etc/live-snapshot.exclude_list"
71 echo "${PROGRAM}: error:" ${@}
82 echo "${PROGRAM} - utility to perform snapshots of Debian Live systems"
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]"
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})"
105 echo "Look at live-snapshot(1) man page for more information."
115 echo "Try \"${PROGRAM} --help\" for more information."
124 echo "Copyright (C) 2006 Marco Amadori <marco.amadori@gmail.com>"
125 echo "Copyright (C) 2008 Chris Lamb <chris@chris-lamb.co.uk>"
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."
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."
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"
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."
144 echo "Homepage: <http://debian-live.alioth.debian.org/>"
152 if [ -n "${ROOTSNAP}" ]; then
153 "${EXECUTABLE}" --resync-string="${ROOTSNAP}"
157 if [ -n "${HOMESNAP}" ]; then
158 "${EXECUTABLE}" --resync-string="${HOMESNAP}"
164 echo "No autoconfigured snapshots found at boot;" > /dev
/null
1>&2
165 echo "(no resync string in ${LIVE_CONF})." > /dev
/null
1>&2
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}"
202 SNAP_RESYNC_STRING
="${2}"
229 Error
"internal error."
238 # Parse resync string
239 if [ -n "${SNAP_RESYNC_STRING}" ]
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
264 SNAP_TYPE
="whole_partition"
272 Error
"unrecognized resync string"
275 elif [ -z "${SNAP_OUTPUT}" ]
277 # Set target file based on image
278 case "${SNAP_TYPE}" in
280 DEST
="${MOUNTP}/live-sn.cpio.gz"
284 DEST
="${MOUNTP}/live-sn.${SNAP_TYPE}"
288 DEST
="${MOUNTP}/live-sn.ext2"
292 DEST
="${MOUNTP}/live-sn.ext4"
296 DEST
="${SNAP_OUTPUT}"
302 case "${SNAP_TYPE}" in
303 cpio|squashfs|jffs2|ext2|ext3|ext4|whole_partition
)
307 Error
"invalid filesystem type \"${SNAP_TYPE}\""
311 if [ ! -d "${SNAP_COW}" ]
313 Error
"${SNAP_COW} is not a directory"
316 if [ "$(id -u)" -ne 0 ]
318 Error
"you are not root"
324 case "${SNAP_DEV}" in
327 mount
-t tmpfs
-o rw tmpfs
"${MOUNTP}"
331 if [ -b "${SNAP_DEV}" ]
333 try_mount
"${SNAP_DEV}" "${MOUNTP}" rw
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
347 if [ -e "${entry}" ] ||
[ -L "${entry}" ]
349 if [ -e "${SNAP_COW}/${entry}" ] || [ -L "${SNAP_COW}/${entry}" ]
359 # BUGS: does not handle deleted files yet
362 if [ -f "${SNAP_LIST}" ]
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}" ]
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}")
378 find "${entry}" |
while read line
380 if Entry_is_modified
"${line}"
382 printf "%s\000" "${line}" >> "${TMP_FILELIST}"
385 elif Entry_is_modified
"${entry}"
387 # if file exists and it is modified
388 printf "%s\000" "${entry}" >> "${TMP_FILELIST}"
397 # removing whiteouts from list
398 find .
-path '*.wh.*' -prune -o -print0 >> "${TMP_FILELIST}"
407 TMP_FILELIST
=$
(mktemp
-p "${SAFE_TMPDIR}" "${TMP_FILELIST}.XXXXXX")
409 case "${SNAP_TYPE}" in
411 echo ".${TMP_FILELIST}" > "${TMP_FILELIST}"
412 # Removing whiteheads of unionfs
414 find .
-name '*.wh.*' >> "${TMP_FILELIST}"
416 if [ -e "${EXCLUDE_LIST}" ]
418 # Add explicitly excluded files
419 grep -v '^#.*$' "${EXCLUDE_LIST}" |
grep -v '^ *$' >> "${TMP_FILELIST}"
423 mksquashfs
"${SNAP_COW}" "${DEST}" -ef "${TMP_FILELIST}"
426 cpio|whole_partition
)
427 if [ "${SNAP_TYPE}" = "cpio" ]
429 COPY_CMD
="cpio --quiet -o0 -H newc | gzip -9c > ${DEST}"
431 COPY_CMD
="cpio --quiet -pumd0 ${DEST}/"
434 WORKING_DIR
=$
(Do_filelist
"${TMP_FILELIST}")
436 if [ -e "${EXCLUDE_LIST}" ]
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}" | \
443 grep -F -v -f "${EXCLUDE_LIST}" | \
448 cat "${TMP_FILELIST}" | \
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
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}"
463 mkfs.jffs2 --root="${SNAP_COW}" --output="${DEST}"
467 if [ -f "${TMP_FILELIST}" ]
469 rm -f "${TMP_FILELIST}"
475 if [ -z "${SNAP_RESYNC_STRING}" ] && echo "${DEST}" | grep -q "${MOUNTP}"
477 echo "${DEST} is present on
${MOUNTP}, therefore no automatic unmounting the latter.
" > /dev/null 1>&2
486 if [ -z "${SNAP_RESYNC_STRING}" ]
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
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
500 echo "Please
cat or flashcp
${DEST} to your partition
in order to start using it.
" > /dev/null 1>&2
504 if grep -qv persistent /proc/cmdline
506 echo "Remember to boot this live system with
\"persistent
\" specified
at boot prompt.
" > /dev/null 1>&2