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