Adding casper 1.71+debian-1.
[live-boot-grml.git] / scripts / casper
1 #!/bin/sh
2
3 # set -e
4
5 export PATH=/root/usr/bin:/root/usr/sbin:/root/bin:/root/sbin:/usr/bin:/usr/sbin:/bin:/sbin
6
7 mountpoint=/live_media
8
9 root_persistence="casper-rw"
10 home_persistence="home-rw"
11
12 USERNAME="casper"
13 USERFULLNAME="Live session user"
14 HOST="live"
15 BUILD_SYSTEM="Debian"
16
17 mkdir -p $mountpoint
18
19 [ -f /etc/casper.conf ] && . /etc/casper.conf
20
21 export USERNAME USERFULLNAME HOST BUILD_SYSTEM
22
23 if [ "${BUILD_SYSTEM}" == "Ubuntu" ]; then
24     MP_QUIET="-Q"
25 else
26     MP_QUIET="-q"
27 fi
28
29 # looking for casper specifics options as kernel parameters
30 for x in $(cat /proc/cmdline); do
31     case $x in
32         userfullname*)
33             export USERFULLNAME=${x#userfullname=} 
34             export CASPERCONF="changed"
35             ;;
36         host*)
37             export HOST=${x#host=} 
38             export CASPERCONF="changed"
39             ;;
40         username*)
41             export USERNAME=${x#username=} 
42             export CASPERCONF="changed"
43             ;;
44         netboot*)
45             export NETBOOT=${x#netboot=} ;;
46         toram)
47             export TORAM=1 ;;
48         showmounts)
49             export SHOWMOUNTS=1 ;;
50         persistent)
51             export PERSISTENT=1 ;;
52         ip*)
53             STATICIP=${x#ip=}
54             if [ "${STATICIP}" == "" ]; then
55                 STATICIP="frommedia"
56             fi
57             export STATICIP ;;
58         casper-getty)
59             export CASPERGETTY=1 ;;
60     esac
61 done
62
63 # sort of compatibility with netboot.h from linux docs
64 if [ -z "${NETBOOT}" ]; then
65     if [ "${ROOT}" == "/dev/nfs" ]; then
66         NETBOOT="nfs"
67         export NETBOOT
68     elif [ "${ROOT}" == "/dev/cifs" ]; then
69         NETBOOT="cifs"
70         export NETBOOT
71     fi
72 fi
73
74 is_casper_path() {
75     path=$1
76     if [ -d "$path/casper" ]; then
77         if [ "$(echo $path/casper/*.cloop)" != "$path/casper/*.cloop" ] ||
78             [ "$(echo $path/casper/*.squashfs)" != "$path/casper/*.squashfs" ] ||
79             [ "$(echo $path/casper/*.ext2)" != "$path/casper/*.ext2" ] ||
80             [ "$(echo $path/casper/*.dir)" != "$path/casper/*.dir" ]; then
81             return 0
82         fi
83     fi
84     return 1
85 }
86
87 subdevices() {
88     sysblock=$1
89     r=""
90     for dev in "${sysblock}" "${sysblock}"/*; do
91         if [ -e "${dev}/dev" ]; then
92             r="${r} ${dev}"
93         fi
94     done
95     echo ${r}
96 }
97
98 get_backing_device() {
99     case "$1" in
100         *.cloop)
101             echo $(setup_loop "$1" "cloop" "/sys/block/cloop*")
102             ;;
103         *.squashfs|*.ext2)
104             echo $(setup_loop "$1" "loop" "/sys/block/loop*")
105             ;;
106         *.dir)
107             echo "directory"
108             ;;
109         *)
110             panic "Unrecognized casper filesystem: $1"
111             ;;
112     esac
113 }
114
115 match_files_in_dir() {
116     # Does any files match pattern $1 ?
117
118     local pattern="$1"
119     if [ "$(echo $pattern)" != "$pattern" ]; then
120         return 0
121     fi
122     return 1
123 }
124
125 mount_images_in_directory() {
126     directory="$1"
127     rootmnt="$2"
128     if match_files_in_dir "$directory/casper/*.cloop"; then
129         # Let's hope there's just one matching *.cloop... FIXME
130         setup_devmapper $(get_backing_device "$directory/casper/*.cloop") "$rootmnt"
131     elif match_files_in_dir "$directory/casper/*.squashfs" || 
132         match_files_in_dir "$directory/casper/*.ext2" ||
133         match_files_in_dir "$directory/casper/*.dir"; then
134         setup_unionfs "$directory/casper" "$rootmnt"
135     else
136         :
137     fi
138 }
139
140 sys2dev() {
141     sysdev=${1#/sys}
142     echo "/dev/$(udevinfo -q name -p ${sysdev} 2>/dev/null|| echo ${sysdev##*/})"
143 }
144
145 setup_loop() {
146     local fspath=$1
147     local module=$2
148     local pattern=$3
149
150     modprobe "${MP_QUIET}" -b "$module"
151     udevsettle
152
153     for loopdev in $pattern; do
154         if [ "$(cat $loopdev/size)" -eq 0 ]; then
155             dev=$(sys2dev "${loopdev}")
156             losetup "$dev" "$fspath"
157             echo "$dev"
158             return 0
159         fi
160     done
161     panic "No loop devices available"
162 }
163
164 get_fstype() {
165     local FSTYPE
166     local FSSIZE
167     eval $(fstype < $1)
168     if [ "$FSTYPE" != "unknown" ]; then
169         echo $FSTYPE
170         return 0
171     fi
172     /lib/udev/vol_id -t $1 2>/dev/null
173 }
174
175 setup_devmapper() {
176     backdev="$1"
177     rootmnt="$2"
178
179     modprobe "${MP_QUIET}" -b dm-mod
180     COW_DEVICE=/dev/ram1
181     COW_NAME="casper-cow"
182
183     BACKING_FILE_SIZE=$(blockdev --getsize "$backdev")
184     MAX_COW_SIZE=$(blockdev --getsize "$COW_DEVICE")
185     CHUNK_SIZE=8 # sectors
186
187     if [ -z "$COW_SIZE" -o "$COW_SIZE" -gt "$MAX_COW_SIZE" ]; then
188         COW_SIZE=$MAX_COW_SIZE
189     fi
190
191     echo "0 $COW_SIZE linear $COW_DEVICE 0" | dmsetup create $COW_NAME
192
193     echo "0 $BACKING_FILE_SIZE snapshot $backdev /dev/mapper/$COW_NAME p $CHUNK_SIZE" | \
194         dmsetup create casper-snapshot
195     if [ "$(get_fstype $backdev)" = "unknown" ]; then
196         panic "Unknown file system type on $backdev"
197     fi
198     mount -t $(get_fstype "$backdev") /dev/mapper/casper-snapshot $rootmnt || panic "Can not mount /dev/mapper/casper/snapshot on $rootmnt"
199
200     mkdir -p "$rootmnt/rofs"
201     echo "0 $BACKING_FILE_SIZE linear $backdev 0" | dmsetup create casper-backing
202     mount -t $(get_fstype "$backdev") /dev/mapper/casper-backing "$rootmnt/rofs"
203 }
204
205 where_is_mounted() {
206     device=$1
207     if grep -q "^$device " /proc/mounts; then
208         grep "^$device " /proc/mounts | read d mountpoint rest
209         echo $mountpoint
210         return 0
211     fi
212     return 1
213 }
214
215 copy_to_ram() {
216     copyfrom="$1"
217
218     if [ ! -z "${2}" ] ; then
219         # This will enable future rampersistence, todo yet
220         copyto="${2}"
221         moveit="False"
222     else
223         copyto="${copyfrom}_swap"
224         moveit="True"
225     fi
226
227     size=$(du -ks ${copyfrom} | cut -f1)
228     size=$(expr ${size} + ${size}/20 ) # Fixme: 5% more to be sure
229     needed_space=$(expr ${size} * 1024)
230     freespace=$( expr $(awk '/MemFree/{print $2}' /proc/meminfo) + $( cat /proc/meminfo | grep Cached | head -n 1 | awk '/Cached/{print $2}' - ))
231
232     if [ ! ${freespace} -lt ${needed_space}  ] ; then
233         [ "$quiet" != "y" ] && log_begin_msg "Not enough free memory to copy to ram"
234         [ "$quiet" != "y" ] && log_end_msg
235         return
236     else
237         [ "$quiet" != "y" ] && log_begin_msg "Copying live media to ram..."
238         mkdir "${copyto}"
239         mount -t tmpfs -o size=${size}k /dev/shm ${copyto}
240         cp -a ${copyfrom}/* ${copyto} # "cp -a" from busybox also copies hidden files
241         umount ${copyfrom}
242         if [ "${moveit}" == "True" ]; then
243             mount -r -o move ${copyto} ${copyfrom}
244             rmdir ${copyto}
245         fi
246         [ "$quiet" != "y" ] && log_end_msg
247     fi
248 }
249
250 find_cow_device() {
251     pers_label="${1}"
252     cow_backing="/${pers_label}-backing"
253     for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -v loop); do
254         for dev in $(subdevices "${sysblock}"); do
255             devname=$(sys2dev "${dev}")
256             if [ "$(/lib/udev/vol_id -l $devname 2>/dev/null)" = "${pers_label}" ]; then
257                 echo "$devname"
258                 return
259             elif [ "$(get_fstype ${devname})" = "vfat" ]; then
260                 mkdir -p "${cow_backing}"
261                 if where_is_mounted ${devname} > /dev/null; then
262                     mount -o remount,rw ${devname} $(where_is_mounted ${devname}) || panic "Remounting failed"
263                     mount -o bind $(where_is_mounted ${devname}) ${cow_backing} || panic "Cannot bind-mount"
264                 else
265                     mount -t $(get_fstype "${devname}") -o rw "${devname}" ${cow_backing} || panic "Cannot mount $devname on /cow-backing"
266                 fi
267
268                 if [ -e "${cow_backing}/${pers_label}" ]; then
269                     echo $(setup_loop "${cow_backing}/${pers_label}" "loop" "/sys/block/loop*")
270                     return 0
271                 else
272                     umount ${cow_backing}
273                 fi
274             fi
275         done
276     done
277 }
278
279 do_netmount() {
280     rc=1
281
282     modprobe "${MP_QUIET}" af_packet # For DHCP
283
284     ipconfig ${DEVICE} /tmp/net-${DEVICE}.conf
285
286     if [ "${NFSROOT}" = "auto" ]; then
287         NFSROOT=${ROOTSERVER}:${ROOTPATH}
288     fi
289
290     [ "$quiet" != "y" ] && log_begin_msg "Trying netboot from ${NFSROOT}"
291
292     if [ "${NETBOOT}" != "nfs" ] && do_cifsmount ; then
293         rc=0
294     elif do_nfsmount ; then
295         NETBOOT="nfs"
296         export NETBOOT
297         rc=0
298     fi
299
300     [ "$quiet" != "y" ] && log_end_msg
301     return ${rc}
302 }
303
304 do_nfsmount() {
305     rc=1
306     modprobe "${MP_QUIET}" nfs
307     if [ -z "${NFSOPTS}" ]; then
308         NFSOPTS=""
309     fi
310
311     [ "$quiet" != "y" ] && log_begin_msg "Trying nfsmount -o nolock -o ro ${NFSOPTS} ${NFSROOT} ${mountpoint}"
312     # FIXME: This for loop is an ugly HACK round an nfs bug
313     for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13; do
314         nfsmount -o nolock -o ro ${NFSOPTS} "${NFSROOT}" "${mountpoint}" && rc=0 && break
315         sleep 1
316     done
317     return ${rc}
318 }
319
320 do_cifsmount() {
321     rc=1
322     if [ -x "/sbin/mount.cifs" ]; then
323         if [ -z "${NFSOPTS}" ]; then
324             CIFSOPTS="-ouser=root,password="
325         else
326             CIFSOPTS="${NFSOPTS}"
327         fi
328
329         [ "$quiet" != "y" ] && log_begin_msg "Trying mount.cifs ${NFSROOT} ${mountpoint} ${CIFSOPTS}"
330         modprobe "${MP_QUIET}" cifs
331
332         if mount.cifs "${NFSROOT}" "${mountpoint}" "${CIFSOPTS}" ; then
333             rc=0
334         fi
335     fi
336     return ${rc}
337 }
338
339 setup_unionfs() {
340     image_directory="$1"
341     rootmnt="$2"
342
343     modprobe "${MP_QUIET}" -b unionfs
344
345     # run-init can't deal with images in a subdir, but we're going to
346     # move all of these away before it runs anyway.  No, we're not,
347     # put them in / since move-mounting them into / breaks mono and
348     # some other apps.
349
350     croot="/"
351
352     # Let's just mount the read-only file systems first
353     rofsstring=""
354     rofslist=""
355     if [ "${NETBOOT}" == "nfs" ] ; then
356         roopt="nfsro" # go aroung a bug in nfs-unionfs locking
357     else
358         roopt="ro"
359     fi
360
361     mkdir -p "${croot}"
362     for image_type in "ext2" "squashfs" "dir" ; do
363         for image in "${image_directory}"/*."${image_type}"; do
364             imagename=$(basename "${image}")
365             if [ -d "${image}" ]; then
366                 # it is a plain directory: do nothing
367                 rofsstring="${image}=${roopt}:${rofsstring}"
368                 rofslist="${image} ${rofslist}"
369             elif [ -f "${image}" ]; then
370                 backdev=$(get_backing_device "$image")
371                 fstype=$(get_fstype "${backdev}")
372                 if [ "${fstype}" = "unknown" ]; then
373                     panic "Unknown file system type on ${backdev} (${image})"
374                 fi
375                 mkdir -p "${croot}/${imagename}"
376                 mount -t "${fstype}" -o ro "${backdev}" "${croot}/${imagename}" || panic "Can not mount $backdev ($image) on ${croot}/${imagename}" && rofsstring="${croot}/${imagename}=${roopt}:${rofsstring}" && rofslist="${croot}/${imagename} ${rofslist}"
377             fi
378         done
379     done
380     rofsstring=${rofsstring%:}
381
382     mkdir -p /cow
383     cowdevice="tmpfs"
384     cow_fstype="tmpfs"
385
386     # Looking for "${root_persistence}" device or file
387     if [ ! -z "${PERSISTENT}" ]; then
388         cowprobe=$(find_cow_device "${root_persistence}")
389         if [ -b "${cowprobe}" ]; then
390             cowdevice=${cowprobe}
391             cow_fstype=$(get_fstype "${cowprobe}")
392         else
393             [ "$quiet" != "y" ] && log_begin_msg "Unable to find the persistent medium"
394         fi
395     fi
396
397     mount ${cowdevice} -t ${cow_fstype} -o rw /cow || panic "Can not mount $cowdevice on /cow"
398
399     mount -t unionfs -o dirs=/cow=rw:$rofsstring unionfs "$rootmnt" || panic "Unionfs mount failed"
400
401     # Look for a snapshot to copy
402
403     # Adding other custom mounts
404     if [ ! -z "${PERSISTENT}" ]; then
405         homecow=$(find_cow_device "${home_persistence}" )
406         if [ -b "${homecow}" ]; then
407             mount ${homecow} -t $(get_fstype "${homecow}") -o rw "${rootmnt}/home"
408         else 
409             [ "$quiet" != "y" ] &&  log_begin_msg "Unable to find the persistent home medium"
410         fi
411     fi
412
413     if [ ! -z "${SHOWMOUNTS}" ]; then
414         for d in ${rofslist}; do
415             mkdir -p "${rootmnt}/casper/${d##*/}"
416             case d in
417                 *.dir) # do nothing # mount -o bind "${d}" "${rootmnt}/casper/${d##*/}"
418                     ;;
419                 *) mount -o move "${d}" "${rootmnt}/casper/${d##*/}"
420                     ;;
421             esac
422         done
423     fi
424
425     # shows cow fs on /cow for use by casper-snapshot
426     mkdir -p "$rootmnt/cow"
427     mount -o bind /cow "$rootmnt/cow"
428 }
429
430 is_usb_device() {
431     sysfs_path="${1#/sys}"
432     if /lib/udev/path_id "${sysfs_path}" | grep -E -q "ID_PATH=(usb|pci-[^-]*-usb)"; then
433         return 0
434     fi
435     return 1
436 }
437
438 find_livefs() {
439     mounted=
440     for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -v loop | grep -v ram); do
441         devname=$(sys2dev "${sysblock}")
442         fstype=$(get_fstype "${devname}")
443         if /lib/udev/cdrom_id ${devname} > /dev/null; then
444             mount -t ${fstype} -o ro "$devname" $mountpoint || continue
445             if is_casper_path $mountpoint; then
446                 echo $mountpoint
447                 return
448             else
449                 umount $mountpoint
450             fi
451         elif is_usb_device "$sysblock"; then
452             for dev in $(subdevices "${sysblock}"); do
453                 devname=$(sys2dev "${dev}")
454                 fstype=$(get_fstype "${devname}")
455                 case ${fstype} in
456                     vfat|iso9660|udf)
457                         mount -t ${fstype} -o ro "${devname}" $mountpoint || continue
458                         if is_casper_path $mountpoint; then
459                             echo $mountpoint
460                             return
461                         else
462                             umount $mountpoint
463                         fi
464                         ;;
465                 esac
466             done
467         elif [ "${fstype}" = "squashfs" ||  \
468                 "${fstype}" = "ext2" ]; then
469
470             # This is an ugly hack situation, the block device has
471             # an image directly on it.  It's hopefully
472             # casper, so take it and run with it.
473
474             ln -s "${devname}" "${devname}.${fstype}"
475             echo "${devname}.${fstype}"
476             return
477         fi
478     done
479 }
480
481 pulsate() {
482     if [ -x /sbin/usplash_write ]; then
483         /sbin/usplash_write "PULSATE"
484     fi
485 }
486
487 set_usplash_timeout() {
488     if [ -x /sbin/usplash_write ]; then
489         /sbin/usplash_write "TIMEOUT 120"
490     fi
491 }
492
493 mountroot() {
494     exec 6>&1
495     exec 7>&2
496     exec > casper.log
497     exec 2>&1
498
499     set_usplash_timeout
500     [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/casper-premount"
501     pulsate
502     run_scripts /scripts/casper-premount
503     [ "$quiet" != "y" ] && log_end_msg
504
505     # Needed here too because some things (*cough* udev *cough*)
506     # changes the timeout
507
508     set_usplash_timeout
509
510     if [ ! -z "${NETBOOT}" ]; then
511         if do_netmount ; then
512             livefs_root="${mountpoint}"
513         else
514             panic "Unable to find a the network rootfs live file system"
515         fi
516     else
517         # Scan local devices for the image
518         for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13; do
519             livefs_root=$(find_livefs)
520             if [ "${livefs_root}" ]; then
521                 break
522             fi
523         done
524
525         if [ "$?" -gt 0 ]; then
526             panic "Unable to find a medium containing a live file system"
527         fi
528
529         if [ ! -z "${TORAM}" ]; then
530             copy_to_ram "${livefs_root}"
531         fi
532     fi
533     sleep 1
534
535     mount_images_in_directory "$livefs_root" "$rootmnt"
536
537     log_end_msg
538
539     maybe_break casper-bottom
540     [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/casper-bottom"
541
542     pulsate
543     run_scripts /scripts/casper-bottom
544     [ "$quiet" != "y" ] && log_end_msg
545
546     exec 1>&6 6>&-
547     exec 2>&7 7>&-
548     cp casper.log "${rootmnt}/var/log/"
549 }