c019e3bf59eba39d4e09dd47d97db7306a788624
[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         hide-cow)
49             export HIDECOW=1 ;;
50         persistent)
51             export PERSISTENT=1 ;;
52         staticip*)
53             STATICIP=${x#staticip=} 
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 this, but we're going to move all of these
346     # away before it runs anyway.
347     croot="/casper"
348
349     # Let's just mount the read-only file systems first
350     rofsstring=""
351     rofslist=""
352     if [ "${NETBOOT}" == "nfs" ] ; then
353         roopt="nfsro" # go aroung a bug in nfs-unionfs locking
354     else
355         roopt="ro"
356     fi
357
358     mkdir -p "${croot}"
359     for image_type in "ext2" "squashfs" "dir" ; do
360         for image in "${image_directory}"/*."${image_type}"; do
361             imagename=$(basename "${image}")
362             if [ -d "${image}" ]; then
363                 # it is a plain directory: do nothing
364                 rofsstring="${image}=${roopt}:${rofsstring}"
365                 rofslist="${image} ${rofslist}"
366             elif [ -f "${image}" ]; then
367                 backdev=$(get_backing_device "$image")
368                 fstype=$(get_fstype "${backdev}")
369                 if [ "${fstype}" = "unknown" ]; then
370                     panic "Unknown file system type on ${backdev} (${image})"
371                 fi
372                 mkdir -p "${croot}/${imagename}"
373                 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}"
374             fi
375         done
376     done
377     rofsstring=${rofsstring%:}
378
379     mkdir -p /cow
380     cowdevice="tmpfs"
381     cow_fstype="tmpfs"
382
383     # Looking for "${root_persistence}" device or file
384     if [ ! -z "${PERSISTENT}" ]; then
385         cowprobe=$(find_cow_device "${root_persistence}")
386         if [ -b "${cowprobe}" ]; then
387             cowdevice=${cowprobe}
388             cow_fstype=$(get_fstype "${cowprobe}")
389         else
390             [ "$quiet" != "y" ] && log_begin_msg "Unable to find the persistent medium"
391         fi
392     fi
393
394     mount ${cowdevice} -t ${cow_fstype} -o rw /cow || panic "Can not mount $cowdevice on /cow"
395
396     # Look for a snapshot to copy
397     
398
399     mount -t unionfs -o dirs=/cow=rw:$rofsstring unionfs "$rootmnt" || panic "Unionfs mount failed"
400
401     for d in ${rofslist}; do
402         mkdir -p "${rootmnt}/casper/${d##*/}"
403         case d in
404             *.dir) # do nothing # mount -o bind "${d}" "${rootmnt}/casper/${d##*/}"
405                 ;;
406             *) mount -o move "${d}" "${rootmnt}/casper/${d##*/}"
407                 ;;
408         esac
409     done
410
411     # Adding other custom mounts
412     if [ ! -z "${PERSISTENT}" ]; then
413         homecow=$(find_cow_device "${home_persistence}" )
414         if [ -b "${homecow}" ]; then
415             mount ${homecow} -t $(get_fstype "${homecow}") -o rw "${rootmnt}/home"
416         else 
417             [ "$quiet" != "y" ] &&  log_begin_msg "Unable to find the persistent home medium"
418         fi
419     fi
420
421     if [ -z "${HIDECOW}" ]; then
422         mkdir -p "$rootmnt/cow"
423         mount -o bind /cow "$rootmnt/cow"
424     fi
425 }
426
427 is_usb_device() {
428     sysfs_path="${1#/sys}"
429     if /lib/udev/path_id "${sysfs_path}" | grep -E -q "ID_PATH=(usb|pci-[^-]*-usb)"; then
430         return 0
431     fi
432     return 1
433 }
434
435 find_livefs() {
436     mounted=
437     for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -v loop | grep -v ram); do
438         devname=$(sys2dev "${sysblock}")
439         fstype=$(get_fstype "${devname}")
440         if /lib/udev/cdrom_id ${devname} > /dev/null; then
441             mount -t ${fstype} -o ro "$devname" $mountpoint || continue
442             if is_casper_path $mountpoint; then
443                 echo $mountpoint
444                 return
445             else
446                 umount $mountpoint
447             fi
448         elif is_usb_device "$sysblock"; then
449             for dev in $(subdevices "${sysblock}"); do
450                 devname=$(sys2dev "${dev}")
451                 fstype=$(get_fstype "${devname}")
452                 case ${fstype} in
453                     vfat|iso9660|udf)
454                         mount -t ${fstype} -o ro "${devname}" $mountpoint || continue
455                         if is_casper_path $mountpoint; then
456                             echo $mountpoint
457                             return
458                         else
459                             umount $mountpoint
460                         fi
461                         ;;
462                 esac
463             done
464         elif [ "${fstype}" = "squashfs" ||  \
465                 "${fstype}" = "ext2" ]; then
466         
467             # This is an ugly hack situation, the block device has
468             # an image directly on it.  It's hopefully
469             # casper, so take it and run with it.
470         
471             ln -s "${devname}" "${devname}.${fstype}"
472             echo "${devname}.${fstype}"
473             return
474         fi
475     done
476 }
477
478 set_usplash_timeout() {
479     if [ -x /sbin/usplash_write ]; then
480         /sbin/usplash_write "TIMEOUT 120"
481     fi
482 }
483
484 mountroot() {
485     exec 6>&1
486     exec 7>&2
487     exec > casper.log
488     exec 2>&1
489     
490     set_usplash_timeout
491     [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/casper-premount"
492     run_scripts /scripts/casper-premount
493     [ "$quiet" != "y" ] && log_end_msg
494
495     # Needed here too because some things (*cough* udev *cough*)
496     # changes the timeout
497
498     set_usplash_timeout
499
500     if [ ! -z "${NETBOOT}" ]; then
501         if do_netmount ; then
502             livefs_root="${mountpoint}"
503         else
504             panic "Unable to find a the network rootfs live file system"
505         fi
506     else
507         # Scan local devices for the image
508         for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13; do
509             livefs_root=$(find_livefs)
510             if [ "${livefs_root}" ]; then
511                 break
512             fi
513         done
514
515         if [ "$?" -gt 0 ]; then
516             panic "Unable to find a medium containing a live file system"
517         fi
518             
519         if [ ! -z "${TORAM}" ]; then
520             copy_to_ram "${livefs_root}"
521         fi
522     fi
523     sleep 1
524
525     mount_images_in_directory "$livefs_root" "$rootmnt"
526
527     log_end_msg
528
529     maybe_break casper-bottom
530     [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/casper-bottom"
531
532     run_scripts /scripts/casper-bottom
533     [ "$quiet" != "y" ] && log_end_msg
534
535     exec 1>&6 6>&-
536     exec 2>&7 7>&-
537     cp casper.log "${rootmnt}/var/log/"
538 }