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