Adding casper 1.59+debian-1.
[live-boot-grml.git] / scripts / casper
1 #!/bin/sh
2
3 # set -e
4
5 mountpoint=/live_media
6 root_persistence="casper-rw"
7 home_persistence="home-rw"
8
9 mkdir -p $mountpoint
10
11 overlay_method=unionfs
12 if [ "${DPKG_ARCH}" = "ia64" ] || [ "${DPKG_ARCH}" = "hppa" ] || [ "${DPKG_ARCH}" = "sparc" ]; then
13     overlay_method=devmapper
14 fi
15
16 USERNAME=debian
17 USERFULLNAME="Debian Live user"
18 HOST=debian
19
20 [ -f /etc/casper.conf ] && . /etc/casper.conf
21
22 export USERNAME USERFULLNAME HOST
23
24 casper_path() { # Fixme: uglyness
25     path=$1
26     if [ -e "$path/casper/filesystem.cloop" ]; then
27         echo "$path/casper/filesystem.cloop"
28         return 0
29     elif [ -e "$path/casper/filesystem.squashfs" ]; then
30         echo "$path/casper/filesystem.squashfs"
31         return 0
32     elif [ -e "$path/casper/filesystem.ext2" ]; then
33         echo "$path/casper/filesystem.ext2"
34         return 0
35     elif [ -e "$path/casper/filesystem.xfs" ]; then
36         echo "$path/casper/filesystem.xfs"
37         return 0
38     fi
39     return 1
40 }
41
42 subdevices() {
43     sysblock=$1
44     r=""
45     for dev in "${sysblock}" "${sysblock}"/*; do
46         if [ -e "${dev}/dev" ]; then
47             r="${r} ${dev}"
48         fi
49     done
50     echo ${r}
51 }
52
53 get_backing_device() {
54         case "$1" in
55             *.cloop)
56                 echo $(setup_loop "$1" "cloop" "/sys/block/cloop*")
57                         ;;
58             *.squashfs|*.ext2|*.xfs)
59                 echo $(setup_loop "$1" "loop" "/sys/block/loop*")
60                 ;;
61             *)
62                 panic "Unrecognized casper filesystem: $1"
63                 ;;
64         esac
65 }
66
67 setup_cow() {
68         case "$1" in
69             unionfs)
70                 setup_unionfs "$2" "$rootmnt"
71                 ;;
72             devmapper)
73                 setup_devmapper "$2" "$rootmnt"
74         esac
75 }
76
77 sys2dev() {
78     sysdev=${1#/sys}
79     echo "/dev/$(udevinfo -q name -p ${sysdev} 2>/dev/null|| echo ${sysdev##*/})"
80 }
81
82 setup_loop() {
83     local fspath=$1
84     local module=$2
85     local pattern=$3
86
87     modprobe -qb "$module"
88     if [ -x /sbin/udevplug ]; then
89         udevplug -W
90     else
91         udevtrigger
92     fi
93  
94     for loopdev in $pattern; do
95         if [ "$(cat $loopdev/size)" -eq 0 ]; then
96             dev=$(sys2dev "${loopdev}")
97             losetup "$dev" "$fspath"
98             echo "$dev"
99             return 0
100         fi
101     done
102     panic "No loop devices available"
103 }
104
105 get_fstype() {
106     local FSTYPE
107     local FSSIZE
108     eval $(fstype < $1)
109     if [ "$FSTYPE" != "unknown" ]; then
110         echo $FSTYPE
111         return 0
112     fi
113     /lib/udev/vol_id -t $1 2>/dev/null
114 }
115
116 setup_devmapper() {
117     backdev="$1"
118     rootmnt="$2"
119
120     modprobe -qb dm-mod
121     COW_DEVICE=/dev/ram1
122     COW_NAME="casper-cow"
123
124     BACKING_FILE_SIZE=$(blockdev --getsize "$backdev")
125     MAX_COW_SIZE=$(blockdev --getsize "$COW_DEVICE")
126     CHUNK_SIZE=8 # sectors
127
128     if [ -z "$COW_SIZE" -o "$COW_SIZE" -gt "$MAX_COW_SIZE" ]; then
129         COW_SIZE=$MAX_COW_SIZE
130     fi
131
132     echo "0 $COW_SIZE linear $COW_DEVICE 0" | dmsetup create $COW_NAME
133
134     echo "0 $BACKING_FILE_SIZE snapshot $backdev /dev/mapper/$COW_NAME p $CHUNK_SIZE" | \
135         dmsetup create casper-snapshot
136     if [ "$(get_fstype $backdev)" = "unknown" ]; then
137         panic "Unknown file system type on $backdev"
138     fi
139     mount -t $(get_fstype "$backdev") /dev/mapper/casper-snapshot $rootmnt || panic "Can not mount /dev/mapper/casper/snapshot on $rootmnt"
140
141     mkdir -p "$rootmnt/rofs"
142     echo "0 $BACKING_FILE_SIZE linear $backdev 0" | dmsetup create casper-backing
143     mount -t $(get_fstype "$backdev") /dev/mapper/casper-backing "$rootmnt/rofs"
144 }
145
146 where_is_mounted() {
147     device=$1
148     if grep -q "^$device " /proc/mounts; then
149         grep "^$device " /proc/mounts | read d mountpoint rest
150         echo $mountpoint
151         return 0
152     fi
153     return 1
154 }
155
156 copy_to_ram() {
157         copyto="${mountpoint}_swap"
158
159         size=$(du -ks ${mountpoint} | cut -f1)
160         size=$(expr ${size} + ${size}/20 ) # Fixme: 5% more to be sure
161         needed_space=$(expr ${size} * 1024)
162         freespace=$( expr $(awk '/MemFree/{print $2}' /proc/meminfo) + $( cat /proc/meminfo | grep Cached | head -n 1 | awk '/Cached/{print $2}' - ))
163         
164         if [ ! ${freespace} -lt ${needed_space}  ] ; then
165                 [ "$quiet" != "y" ] && log_begin_msg "Not enough free memory to copy to ram"
166                 [ "$quiet" != "y" ] && log_end_msg
167                 return
168         else
169                 [ "$quiet" != "y" ] && log_begin_msg "Copying live media to ram..."
170                 mkdir "${copyto}"
171                 mount -t tmpfs -o size=${size}k /dev/shm ${copyto}
172                 cp -a ${mountpoint}/* ${copyto}
173                 umount ${mountpoint}
174                 mount -r -o move ${copyto} ${mountpoint}
175                 rmdir ${copyto}
176                 [ "$quiet" != "y" ] && log_end_msg
177         fi
178 }
179
180 find_cow_device() {
181         pers_label="${1}"
182         cow_backing="/${pers_label}-backing"
183         for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -v loop); do
184                 for dev in $(subdevices "${sysblock}"); do
185                         devname=$(sys2dev "${dev}")
186                         if [ "$(/lib/udev/vol_id -l $devname 2>/dev/null)" = "${pers_label}" ]; then
187                                 echo "$devname"
188                                 return
189                         elif [ "$(get_fstype ${devname})" = "vfat" ]; then
190                                 mkdir -p "${cow_backing}"
191                                 if where_is_mounted ${devname} > /dev/null; then
192                                         mount -o remount,rw ${devname} $(where_is_mounted ${devname}) || panic "Remounting failed"
193                                         mount -o bind $(where_is_mounted ${devname}) ${cow_backing} || panic "Cannot bind-mount"
194                                 else
195                                         mount -t $(get_fstype "${devname}") -o rw "${devname}" ${cow_backing} || panic "Cannot mount $devname on /cow-backing"
196                                 fi
197
198                                 if [ -e "${cow_backing}/${pers_label}" ]; then
199                                         echo $(setup_loop "${cow_backing}/${pers_label}" "loop" "/sys/block/loop*")
200                                         return 0
201                                 else
202                                         umount ${cow_backing}
203                                 fi
204                         fi
205                 done
206         done
207 }
208
209 do_netmount() {
210         rofsmnt="$1"
211         # adapted from NFS filesystem mounting
212
213         modprobe -q cifs
214         # For DHCP
215         modprobe -q af_packet
216
217         ipconfig ${DEVICE} /tmp/net-${DEVICE}.conf
218         if [ "x${NFSROOT}" = "xauto" ]; then
219                 NFSROOT=${ROOTSERVER}:${ROOTPATH}
220         fi
221
222         NFSOPTS="-ouser=root,password="
223
224         [ "$quiet" != "y" ] && log_begin_msg "Mounting using mount.cifs with ${NFSROOT} ${rofsmnt} ${NFSOPTS}"
225         mount.cifs "${NFSROOT}" "${rofsmnt}" "${NFSOPTS}" 
226         [ "$quiet" != "y" ] && log_end_msg
227 }
228
229 setup_unionfs() {
230         backdev="$1"
231         rootmnt="$2"
232         modprobe -qb unionfs
233         mkdir -p /cow
234         cowdevice="tmpfs"
235         cow_fstype="tmpfs"
236         # Looking for "${root_persistence}" device or file
237         if grep -q persistent /proc/cmdline; then
238                 cowprobe=$(find_cow_device "${root_persistence}")
239                 if [ -b "${cowprobe}" ]; then
240                         cowdevice=${cowprobe}
241                         cow_fstype=$(get_fstype "${cowprobe}")
242                 else
243                         [ "$quiet" != "y" ] &&  log_begin_msg "Unable to find the persistent medium"
244                 fi
245         fi
246
247         mount ${cowdevice} -t ${cow_fstype} -o rw /cow || panic "Can not mount $cowdevice on /cow"
248
249         mkdir -p /rofs
250         if grep -q netboot /proc/cmdline; then
251                 do_netmount /rofs || panic "Can not mount netroot on /rofs"
252         else
253                 if [ "$(get_fstype $backdev)" = "unknown" ]; then
254                         panic "Unknown file system type on $backdev"
255                 fi
256                 mount -t $(get_fstype "$backdev") -o ro "$backdev" /rofs || panic "Can not mount $backdev on /rofs"
257         fi
258         
259         mount -t unionfs -o dirs=/cow=rw:/rofs=ro unionfs "$rootmnt"
260         if grep -q show-cow /proc/cmdline; then
261                 mkdir -p "$rootmnt/cow"
262                 mount -o bind /cow "$rootmnt/cow"
263         fi
264         mkdir -p "$rootmnt/rofs"
265         mount -o bind /rofs "$rootmnt/rofs"
266
267         # Adding home persitence
268         if grep -q homepersistence /proc/cmdline; then
269                 homecow=$(find_cow_device "${home_persistence}" )
270                 if [ -b "${homecow}" ]; then
271                         mount ${homecow} -t $(get_fstype "${homecow}") -o rw "${rootmnt}/home"
272                 else 
273                         [ "$quiet" != "y" ] &&  log_begin_msg "Unable to find the persistent home medium"
274                 fi
275         fi
276 }
277
278 is_usb_device() {
279     sysfs_path="${1#/sys}"
280     if /lib/udev/path_id "${sysfs_path}" | grep -q "ID_PATH=usb"; then
281         return 0
282     fi
283     return 1
284 }
285
286 find_live() {
287         mounted=
288         for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -v loop | grep -v ram); do
289             devname=$(sys2dev "${sysblock}")
290             fstype=$(get_fstype "${devname}")
291             if /lib/udev/cdrom_id ${devname} > /dev/null; then
292                 mount -t ${fstype} -o ro "$devname" $mountpoint || continue
293                 if casper_path $mountpoint; then
294                     echo $(casper_path $mountpoint)
295                     return
296                 else
297                     umount $mountpoint
298                 fi
299             elif is_usb_device "$sysblock"; then
300                 for dev in $(subdevices "${sysblock}"); do
301                     devname=$(sys2dev "${dev}")
302                     fstype=$(get_fstype "${devname}")
303                     case ${fstype} in
304                         vfat|iso9660|udf)
305                             mount -t ${fstype} -o ro "${devname}" $mountpoint || continue
306                             if casper_path $mountpoint; then
307                                 echo $(casper_path $mountpoint)
308                                 return
309                             else
310                                 umount $mountpoint
311                             fi
312                             ;;
313                     esac
314                 done
315             elif [ "${fstype}" = "squashfs" ]; then
316
317                 # This is an ugly hack situation, the block device has
318                 # a squashfs image directly on it.  It's hopefully
319                 # casper, so take it and run with it.
320
321                 ln -s "${devname}" "${devname}.${fstype}"
322                 echo "${devname}.${fstype}"
323                 return
324             fi
325         done
326 }
327
328 set_usplash_timeout() {
329     if [ -x /sbin/usplash_write ]; then
330         /sbin/usplash_write "TIMEOUT 120"
331     fi
332 }
333
334 mountroot() {
335     exec 6>&1
336     exec 7>&2
337     exec > casper.log
338     exec 2>&1
339
340     set_usplash_timeout
341     [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/casper-premount"
342     run_scripts /scripts/casper-premount
343     [ "$quiet" != "y" ] && log_end_msg
344
345     # Needed here too because some things (*cough* udev *cough*)
346     # changes the timeout
347
348     set_usplash_timeout
349
350     for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13; do
351         live_image=$(find_live)
352         if [ "${live_image}" ]; then
353             break
354         fi
355         sleep 1
356     done
357     if [ "$?" -gt 0 ]; then
358         panic "Unable to find a medium containing a live file system"
359     fi
360     
361         if grep -q toram /proc/cmdline; then
362                 copy_to_ram
363         fi
364
365     setup_cow "$overlay_method" "$(get_backing_device $live_image)" "$rootmnt"
366
367         # show it on new rootfs
368         mkdir ${rootmnt}/${mountpoint}
369         mount -o bind ${mountpoint} ${rootmnt}/${mountpoint}
370
371     log_end_msg
372
373     maybe_break casper-bottom
374     [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/casper-bottom"
375
376     PATH=/root/usr/bin:/root/usr/sbin:/root/bin:/root/sbin:$PATH run_scripts /scripts/casper-bottom
377     [ "$quiet" != "y" ] && log_end_msg
378
379     exec 1>&6 6>&-
380     exec 2>&7 7>&-
381     cp casper.log "${rootmnt}/var/log/"
382 }