This patch enables support for verifying the ISO through bootid=.... bootoption.
[live-boot-grml.git] / components / 9990-misc-helpers.sh
1 #!/bin/sh
2
3 #set -e
4
5 is_live_path()
6 {
7         DIRECTORY="${1}/${LIVE_MEDIA_PATH}"
8         for FILESYSTEM in squashfs ext2 ext3 ext4 xfs dir jffs
9         do
10                 if ls "${DIRECTORY}/"*.${FILESYSTEM} > /dev/null 2>&1
11                 then
12                         return 0
13                 fi
14         done
15         return 1
16 }
17
18 grml_match_bootid()
19 {
20         path="$1"
21
22         if [ -n "$IGNORE_BOOTID" ] ; then
23                 echo " * Ignoring verification of bootid.txt as requested via ignore_bootid.">>/boot.log
24                 return 0
25         fi
26
27         if [ -n "$BOOTID" ] && ! [ -r "${path}/conf/bootid.txt" ] ; then
28                 echo "  * Warning: bootid=... specified but no bootid.txt found on currently requested device.">>/boot.log
29                 return 1
30         fi
31
32         [ -r "${path}/conf/bootid.txt" ] || return 0
33
34         bootid_conf=$(cat "${path}/conf/bootid.txt")
35
36         if [ -z "$BOOTID" -a -z "$IGNORE_BOOTID" ]
37         then
38                 echo " * Warning: bootid.txt found but ignore_bootid / bootid=.. bootoption missing...">>/boot.log
39                 return 1
40         fi
41
42         if [ "$BOOTID" = "$bootid_conf" ]
43         then
44                 echo " * Successfully verified /conf/bootid.txt from ISO, continuing... ">>/boot.log
45         else
46                 echo " * Warning: BOOTID of ISO does not match. Retrying and continuing search...">>/boot.log
47                 return 1
48         fi
49
50         return 0
51 }
52
53
54 matches_uuid ()
55 {
56         if [ "${IGNORE_UUID}" ] || [ ! -e /conf/uuid.conf ]
57         then
58                 return 0
59         fi
60
61         path="${1}"
62         uuid="$(cat /conf/uuid.conf)"
63
64         for try_uuid_file in "${path}/.disk/live-uuid"*
65         do
66                 [ -e "${try_uuid_file}" ] || continue
67
68                 try_uuid="$(cat "${try_uuid_file}")"
69
70                 if [ "${uuid}" = "${try_uuid}" ]
71                 then
72                         return 0
73                 fi
74         done
75
76         return 1
77 }
78
79 get_backing_device ()
80 {
81         case "${1}" in
82                 *.squashfs|*.ext2|*.ext3|*.ext4|*.jffs2)
83                         echo $(setup_loop "${1}" "loop" "/sys/block/loop*" '0' "${LIVE_MEDIA_ENCRYPTION}" "${2}")
84                         ;;
85
86                 *.dir)
87                         echo "directory"
88                         ;;
89
90                 *)
91                         panic "Unrecognized live filesystem: ${1}"
92                         ;;
93         esac
94 }
95
96 mount_images_in_directory ()
97 {
98         directory="${1}"
99         rootmnt="${2}"
100         mac="${3}"
101
102         if is_live_path "${directory}"
103         then
104                 [ -n "${mac}" ] && adddirectory="${directory}/${LIVE_MEDIA_PATH}/${mac}"
105                 setup_unionfs "${directory}/${LIVE_MEDIA_PATH}" "${rootmnt}" "${adddirectory}"
106         else
107                 panic "No supported filesystem images found at /${LIVE_MEDIA_PATH}."
108         fi
109 }
110
111 is_nice_device ()
112 {
113         sysfs_path="${1#/sys}"
114
115         if udevadm info --query=all --path="${sysfs_path}" | egrep -q "DEVTYPE=disk"
116         then
117                 return 0
118         elif echo "${sysfs_path}" | grep -q '^/block/vd[a-z]$'
119         then
120                 return 0
121         elif echo ${sysfs_path} | grep -q "^/block/dm-"
122         then
123                 return 0
124         elif echo ${sysfs_path} | grep -q "^/block/mtdblock"
125         then
126                 return 0
127         fi
128
129         return 1
130 }
131
132 check_dev ()
133 {
134         sysdev="${1}"
135         devname="${2}"
136         skip_uuid_check="${3}"
137
138         # support for fromiso=.../isofrom=....
139         if [ -n "$FROMISO" ]
140         then
141                 ISO_DEVICE=$(dirname $FROMISO)
142                 if ! [ -b $ISO_DEVICE ]
143                 then
144                         # to support unusual device names like /dev/cciss/c0d0p1
145                         # as well we have to identify the block device name, let's
146                         # do that for up to 15 levels
147                         i=15
148                         while [ -n "$ISO_DEVICE" ] && [ "$i" -gt 0 ]
149                         do
150                                 ISO_DEVICE=$(dirname ${ISO_DEVICE})
151                                 [ -b "$ISO_DEVICE" ] && break
152                                 i=$(($i -1))
153                         done
154                 fi
155
156                 if [ "$ISO_DEVICE" = "/" ]
157                 then
158                         echo "Warning: device for bootoption fromiso= ($FROMISO) not found.">>/boot.log
159                 else
160                         fs_type=$(get_fstype "${ISO_DEVICE}")
161                         if is_supported_fs ${fs_type}
162                         then
163                                 mkdir /live/fromiso
164                                 mount -t $fs_type "$ISO_DEVICE" /live/fromiso
165                                 ISO_NAME="$(echo $FROMISO | sed "s|$ISO_DEVICE||")"
166                                 loopdevname=$(setup_loop "/live/fromiso/${ISO_NAME}" "loop" "/sys/block/loop*" "" '')
167                                 devname="${loopdevname}"
168                         else
169                                 echo "Warning: unable to mount $ISO_DEVICE." >>/boot.log
170                         fi
171                 fi
172         fi
173
174         if [ -z "${devname}" ]
175         then
176                 devname=$(sys2dev "${sysdev}")
177         fi
178
179         if [ -d "${devname}" ]
180         then
181                 mount -o bind "${devname}" $mountpoint || continue
182
183                 if is_live_path $mountpoint
184                 then
185                         echo $mountpoint
186                         return 0
187                 else
188                         umount $mountpoint
189                 fi
190         fi
191
192         IFS=","
193         for device in ${devname}
194         do
195                 case "$device" in
196                         *mapper*)
197                                 # Adding lvm support
198                                 if [ -x /scripts/local-top/lvm2 ]
199                                 then
200                                         ROOT="$device" resume="" /scripts/local-top/lvm2 >>/boot.log
201                                 fi
202                                 ;;
203
204                         /dev/md*)
205                                 # Adding raid support
206                                 if [ -x /scripts/local-top/mdadm ]
207                                 then
208                                         [ -r /conf/conf.d/md ] && cp /conf/conf.d/md /conf/conf.d/md.orig
209                                         echo "MD_DEVS=$device " >> /conf/conf.d/md
210                                         /scripts/local-top/mdadm >>/boot.log
211                                         [ -r /conf/conf.d/md.orig ] && mv /conf/conf.d/md.orig /conf/conf.d/md
212                                 fi
213                                 ;;
214                 esac
215         done
216         unset IFS
217
218         [ -n "$device" ] && devname="$device"
219
220         [ -e "$devname" ] || continue
221
222         if [ -n "${LIVE_MEDIA_OFFSET}" ]
223         then
224                 loopdevname=$(setup_loop "${devname}" "loop" "/sys/block/loop*" "${LIVE_MEDIA_OFFSET}" '')
225                 devname="${loopdevname}"
226         fi
227
228         fstype=$(get_fstype "${devname}")
229
230         if is_supported_fs ${fstype}
231         then
232                 devuid=$(blkid -o value -s UUID "$devname")
233                 [ -n "$devuid" ] && grep -qs "\<$devuid\>" /var/lib/live/boot/devices-already-tried-to-mount && continue
234                 mount -t ${fstype} -o ro,noatime "${devname}" ${mountpoint} || continue
235                 [ -n "$devuid" ] && echo "$devuid" >> /var/lib/live/boot/devices-already-tried-to-mount
236
237                 if [ -n "${FINDISO}" ]
238                 then
239                         if [ -f ${mountpoint}/${FINDISO} ]
240                         then
241                                 umount ${mountpoint}
242                                 mkdir -p /live/findiso
243                                 mount -t ${fstype} -o ro,noatime "${devname}" /live/findiso
244                                 loopdevname=$(setup_loop "/live/findiso/${FINDISO}" "loop" "/sys/block/loop*" 0 "")
245                                 devname="${loopdevname}"
246                                 mount -t iso9660 -o ro,noatime "${devname}" ${mountpoint}
247                         else
248                                 umount ${mountpoint}
249                         fi
250                 fi
251
252                 if is_live_path ${mountpoint} && \
253                         ([ "${skip_uuid_check}" ] || grml_match_bootid ${mountpoint})
254                 then
255                         echo ${mountpoint}
256                         return 0
257                 else
258                         umount ${mountpoint} 2>/dev/null
259                 fi
260         fi
261
262         if [ -n "${LIVE_MEDIA_OFFSET}" ]
263         then
264                 losetup -d "${loopdevname}"
265         fi
266
267         return 1
268 }
269
270 find_livefs ()
271 {
272         timeout="${1}"
273
274         # don't start autodetection before timeout has expired
275         if [ -n "${LIVE_MEDIA_TIMEOUT}" ]
276         then
277                 if [ "${timeout}" -lt "${LIVE_MEDIA_TIMEOUT}" ]
278                 then
279                         return 1
280                 fi
281         fi
282
283         # first look at the one specified in the command line
284         case "${LIVE_MEDIA}" in
285                 removable-usb)
286                         for sysblock in $(removable_usb_dev "sys")
287                         do
288                                 for dev in $(subdevices "${sysblock}")
289                                 do
290                                         if check_dev "${dev}"
291                                         then
292                                                 return 0
293                                         fi
294                                 done
295                         done
296                         return 1
297                         ;;
298
299                 removable)
300                         for sysblock in $(removable_dev "sys")
301                         do
302                                 for dev in $(subdevices "${sysblock}")
303                                 do
304                                         if check_dev "${dev}"
305                                         then
306                                                 return 0
307                                         fi
308                                 done
309                         done
310                         return 1
311                         ;;
312
313                 *)
314                         if [ ! -z "${LIVE_MEDIA}" ]
315                         then
316                                 if check_dev "null" "${LIVE_MEDIA}" "skip_uuid_check"
317                                 then
318                                         return 0
319                                 fi
320                         fi
321                         ;;
322         esac
323
324         # or do the scan of block devices
325         # prefer removable devices over non-removable devices, so scan them first
326         devices_to_scan="$(removable_dev 'sys') $(non_removable_dev 'sys')"
327
328         for sysblock in $devices_to_scan
329         do
330                 devname=$(sys2dev "${sysblock}")
331                 [ -e "$devname" ] || continue
332                 fstype=$(get_fstype "${devname}")
333
334                 if /lib/udev/cdrom_id ${devname} > /dev/null
335                 then
336                         if check_dev "null" "${devname}"
337                         then
338                                 return 0
339                         fi
340                 elif is_nice_device "${sysblock}"
341                 then
342                         for dev in $(subdevices "${sysblock}")
343                         do
344                                 if check_dev "${dev}"
345                                 then
346                                         return 0
347                                 fi
348                         done
349                 fi
350         done
351
352         return 1
353 }
354
355 is_in_list_separator_helper ()
356 {
357         local sep element list
358         sep=${1}
359         shift
360         element=${1}
361         shift
362         list=${*}
363         echo ${list} | grep -qe "^\(.*${sep}\)\?${element}\(${sep}.*\)\?$"
364 }
365
366 is_in_space_sep_list ()
367 {
368         local element
369         element=${1}
370         shift
371         is_in_list_separator_helper "[[:space:]]" "${element}" "${*}"
372 }
373
374 is_in_comma_sep_list ()
375 {
376         local element
377         element=${1}
378         shift
379         is_in_list_separator_helper "," "${element}" "${*}"
380 }
381
382 sys2dev ()
383 {
384         sysdev=${1#/sys}
385         echo "/dev/$(udevadm info -q name -p ${sysdev} 2>/dev/null|| echo ${sysdev##*/})"
386 }
387
388 subdevices ()
389 {
390         sysblock=${1}
391         r=""
392
393         for dev in "${sysblock}"/* "${sysblock}"
394         do
395                 if [ -e "${dev}/dev" ]
396                 then
397                         r="${r} ${dev}"
398                 fi
399         done
400
401         echo ${r}
402 }
403
404 storage_devices()
405 {
406         black_listed_devices="${1}"
407         white_listed_devices="${2}"
408
409         for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -vE "loop|ram|fd")
410         do
411                 fulldevname=$(sys2dev "${sysblock}")
412
413                 if is_in_space_sep_list ${fulldevname} ${black_listed_devices} || \
414                         [ -n "${white_listed_devices}" ] && \
415                         ! is_in_space_sep_list ${fulldevname} ${white_listed_devices}
416                 then
417                         # skip this device entirely
418                         continue
419                 fi
420
421                 for dev in $(subdevices "${sysblock}")
422                 do
423                         devname=$(sys2dev "${dev}")
424
425                         if is_in_space_sep_list ${devname} ${black_listed_devices}
426                         then
427                                 # skip this subdevice
428                                 continue
429                         else
430                                 echo "${devname}"
431                         fi
432                 done
433         done
434 }
435
436 is_supported_fs ()
437 {
438         fstype="${1}"
439
440         # Validate input first
441         if [ -z "${fstype}" ]
442         then
443                 return 1
444         fi
445
446         # Try to look if it is already supported by the kernel
447         if grep -q ${fstype} /proc/filesystems
448         then
449                 return 0
450         else
451                 # Then try to add support for it the gentle way using the initramfs capabilities
452                 modprobe -q -b ${fstype}
453                 if grep -q ${fstype} /proc/filesystems
454                 then
455                         return 0
456                 # Then try the hard way if /root is already reachable
457                 else
458                         kmodule="/root/lib/modules/`uname -r`/${fstype}/${fstype}.ko"
459                         if [ -e "${kmodule}" ]
460                         then
461                                 insmod "${kmodule}"
462                                 if grep -q ${fstype} /proc/filesystems
463                                 then
464                                         return 0
465                                 fi
466                         fi
467                 fi
468         fi
469
470         return 1
471 }
472
473 get_fstype ()
474 {
475         /sbin/blkid -s TYPE -o value $1 2>/dev/null
476 }
477
478 where_is_mounted ()
479 {
480         device=${1}
481         # return first found
482         grep -m1 "^${device} " /proc/mounts | cut -f2 -d ' '
483 }
484
485 trim_path ()
486 {
487         # remove all unnecessary /:s in the path, including last one (except
488         # if path is just "/")
489         echo ${1} | sed 's|//\+|/|g' | sed 's|^\(.*[^/]\)/$|\1|'
490 }
491
492 what_is_mounted_on ()
493 {
494         local dir
495         dir="$(trim_path ${1})"
496         grep -m1 "^[^ ]\+ ${dir} " /proc/mounts | cut -d' ' -f1
497 }
498
499 chown_ref ()
500 {
501         local reference targets owner
502         reference="${1}"
503         shift
504         targets=${@}
505         owner=$(stat -c %u:%g "${reference}")
506         chown -h ${owner} ${targets}
507 }
508
509 chmod_ref ()
510 {
511         local reference targets rights
512         reference="${1}"
513         shift
514         targets=${@}
515         rights=$(stat -c %a "${reference}")
516         chmod ${rights} ${targets}
517 }
518
519 lastline ()
520 {
521         while read lines
522         do
523                 line=${lines}
524         done
525
526         echo "${line}"
527 }
528
529 base_path ()
530 {
531         testpath="${1}"
532         mounts="$(awk '{print $2}' /proc/mounts)"
533         testpath="$(busybox realpath ${testpath})"
534
535         while true
536         do
537                 if echo "${mounts}" | grep -qs "^${testpath}"
538                 then
539                         set -- $(echo "${mounts}" | grep "^${testpath}" | lastline)
540                         echo ${1}
541                         break
542                 else
543                         testpath=$(dirname $testpath)
544                 fi
545         done
546 }
547
548 fs_size ()
549 {
550         # Returns used/free fs kbytes + 5% more
551         # You could pass a block device as ${1} or the mount point as ${2}
552
553         dev="${1}"
554         mountp="${2}"
555         used="${3}"
556
557         if [ -z "${mountp}" ]
558         then
559                 mountp="$(where_is_mounted ${dev})"
560
561                 if [ -z "${mountp}" ]
562                 then
563                         mountp="/mnt/tmp_fs_size"
564
565                         mkdir -p "${mountp}"
566                         mount -t $(get_fstype "${dev}") -o ro "${dev}" "${mountp}" || log_warning_msg "cannot mount -t $(get_fstype ${dev}) -o ro ${dev} ${mountp}"
567
568                         doumount=1
569                 fi
570         fi
571
572         if [ "${used}" = "used" ]
573         then
574                 size=$(du -ks ${mountp} | cut -f1)
575                 size=$(expr ${size} + ${size} / 20 ) # FIXME: 5% more to be sure
576         else
577                 # free space
578                 size="$(df -kP | grep -s ${mountp} | awk '{print $4}')"
579         fi
580
581         if [ -n "${doumount}" ]
582         then
583                 umount "${mountp}" || log_warning_msg "cannot umount ${mountp}"
584                 rmdir "${mountp}"
585         fi
586
587         echo "${size}"
588 }
589
590 load_keymap ()
591 {
592         # Load custom keymap
593         if [ -x /bin/loadkeys -a -r /etc/boottime.kmap.gz ]
594         then
595                 loadkeys --quiet /etc/boottime.kmap.gz
596         fi
597 }
598
599 setup_loop ()
600 {
601         local fspath module pattern offset encryption readonly
602         fspath=${1}
603         module=${2}
604         pattern=${3}
605         offset=${4}
606         encryption=${5}
607         readonly=${6}
608
609         # the output of setup_loop is evaluated in other functions,
610         # modprobe leaks kernel options like "libata.dma=0"
611         # as "options libata dma=0" on stdout, causing serious
612         # problems therefor, so instead always avoid output to stdout
613         modprobe -q -b "${module}" 1>/dev/null
614
615         udevadm settle
616
617         for loopdev in ${pattern}
618         do
619                 if [ "$(cat ${loopdev}/size)" -eq 0 ]
620                 then
621                         dev=$(sys2dev "${loopdev}")
622                         options=''
623
624                         if [ -n "${readonly}" ]
625                         then
626                                 if losetup --help 2>&1 | grep -q -- "-r\b"
627                                 then
628                                         options="${options} -r"
629                                 fi
630                         fi
631
632                         if [ -n "${offset}" ] && [ 0 -lt "${offset}" ]
633                         then
634                                 options="${options} -o ${offset}"
635                         fi
636
637                         if [ -z "${encryption}" ]
638                         then
639                                 losetup ${options} "${dev}" "${fspath}"
640                         else
641                                 # Loop AES encryption
642                                 while true
643                                 do
644                                         load_keymap
645
646                                         echo -n "Enter passphrase for root filesystem: " >&6
647                                         read -s passphrase
648                                         echo "${passphrase}" > /tmp/passphrase
649                                         unset passphrase
650                                         exec 9</tmp/passphrase
651                                         /sbin/losetup ${options} -e "${encryption}" -p 9 "${dev}" "${fspath}"
652                                         error=${?}
653                                         exec 9<&-
654                                         rm -f /tmp/passphrase
655
656                                         if [ 0 -eq ${error} ]
657                                         then
658                                                 unset error
659                                                 break
660                                         fi
661
662                                         echo
663                                         echo -n "There was an error decrypting the root filesystem ... Retry? [Y/n] " >&6
664                                         read answer
665
666                                         if [ "$(echo "${answer}" | cut -b1 | tr A-Z a-z)" = "n" ]
667                                         then
668                                                 unset answer
669                                                 break
670                                         fi
671                                 done
672                         fi
673
674                         echo "${dev}"
675                         return 0
676                 fi
677         done
678
679         panic "No loop devices available"
680 }
681
682 try_mount ()
683 {
684         dev="${1}"
685         mountp="${2}"
686         opts="${3}"
687         fstype="${4}"
688
689         old_mountp="$(where_is_mounted ${dev})"
690
691         if [ -n "${old_mountp}" ]
692         then
693                 if [ "${opts}" != "ro" ]
694                 then
695                         mount -o remount,"${opts}" "${dev}" "${old_mountp}" || panic "Remounting ${dev} ${opts} on ${old_mountp} failed"
696                 fi
697
698                 mount -o bind "${old_mountp}" "${mountp}" || panic "Cannot bind-mount ${old_mountp} on ${mountp}"
699         else
700                 if [ -z "${fstype}" ]
701                 then
702                         fstype=$(get_fstype "${dev}")
703                 fi
704                 mount -t "${fstype}" -o "${opts}" "${dev}" "${mountp}" || \
705                 ( echo "SKIPPING: Cannot mount ${dev} on ${mountp}, fstype=${fstype}, options=${opts}" > boot.log && return 0 )
706         fi
707 }
708
709 # Try to mount $device to the place expected by live-boot. If $device
710 # is already mounted somewhere, move it to the expected place. If $device
711 # ends with a "/" this is a directory path.
712 # If we're only probing $device (to check if it has custom persistence)
713 # $probe should be set, which suppresses warnings upon failure. On
714 # success, print the mount point for $device.
715 mount_persistence_media ()
716 {
717         local device probe backing old_backing fstype mount_opts
718         device=${1}
719         probe=${2}
720
721         # get_custom_mounts() might call this with a directory path instead
722         # of a block device path. This means we have found sub-directory path
723         # underneath /lib/live/mounts/persistence, so we're done
724         if [ -d "${device}" ]
725         then
726                 echo "${device}"
727                 return 0
728         fi
729
730         if [ ! -b "${device}" ]
731         then
732                 return 1
733         fi
734
735         backing="/live/persistence/$(basename ${device})"
736
737         mkdir -p "${backing}"
738         old_backing="$(where_is_mounted ${device})"
739         if [ -z "${old_backing}" ]
740         then
741                 fstype="$(get_fstype ${device})"
742                 mount_opts="rw,noatime"
743                 if [ -n "${PERSISTENCE_READONLY}" ]
744                 then
745                         mount_opts="ro,noatime"
746                 fi
747                 if mount -t "${fstype}" -o "${mount_opts}" "${device}" "${backing}" >/dev/null
748                 then
749                         echo ${backing}
750                         return 0
751                 else
752                         [ -z "${probe}" ] && log_warning_msg "Failed to mount persistence media ${device}"
753                         rmdir "${backing}"
754                         return 1
755                 fi
756         elif [ "${backing}" != "${old_backing}" ]
757         then
758                 if ! mount --move ${old_backing} ${backing} >/dev/null
759                 then
760                         [ -z "${probe}" ] && log_warning_msg "Failed to move persistence media ${device}"
761                         rmdir "${backing}"
762                         return 1
763                 fi
764                 mount_opts="rw,noatime"
765                 if [ -n "${PERSISTENCE_READONLY}" ]
766                 then
767                         mount_opts="ro,noatime"
768                 fi
769                 if ! mount -o "remount,${mount_opts}" "${backing}" >/dev/null
770                 then
771                         log_warning_msg "Failed to remount persistence media ${device} writable"
772                         # Don't unmount or rmdir the new mountpoint in this case
773                 fi
774                 echo ${backing}
775                 return 0
776         else
777                 # This means that $device has already been mounted on
778                 # the place expected by live-boot, so we're done.
779                 echo ${backing}
780                 return 0
781         fi
782 }
783
784 close_persistence_media ()
785 {
786         local device backing
787         device=${1}
788         backing="$(where_is_mounted ${device})"
789
790         if [ -d "${backing}" ]
791         then
792                 umount "${backing}" >/dev/null 2>&1
793                 rmdir "${backing}" >/dev/null 2>&1
794         fi
795
796         if is_active_luks_mapping ${device}
797         then
798                 /sbin/cryptsetup luksClose ${device}
799         fi
800 }
801
802 open_luks_device ()
803 {
804         dev="${1}"
805         name="$(basename ${dev})"
806         opts="--key-file=-"
807         if [ -n "${PERSISTENCE_READONLY}" ]
808         then
809                 opts="${opts} --readonly"
810         fi
811
812         if /sbin/cryptsetup status "${name}" >/dev/null 2>&1
813         then
814                 re="^[[:space:]]*device:[[:space:]]*\([^[:space:]]*\)$"
815                 opened_dev=$(cryptsetup status ${name} 2>/dev/null | grep "${re}" | sed "s|${re}|\1|")
816                 if [ "${opened_dev}" = "${dev}" ]
817                 then
818                         luks_device="/dev/mapper/${name}"
819                         echo ${luks_device}
820                         return 0
821                 else
822                         log_warning_msg "Cannot open luks device ${dev} since ${opened_dev} already is opened with its name"
823                         return 1
824                 fi
825         fi
826
827         load_keymap
828
829         # check for plymouth
830         if [ -x /bin/plymouth ]
831         then
832                 _PLYMOUTH="true"
833         fi
834
835         case "${_PLYMOUTH}" in
836                 true)
837                         plymouth --ping
838
839                         cryptkeyscript="plymouth ask-for-password --prompt"
840                         # Plymouth will add a : if it is a non-graphical prompt
841                         cryptkeyprompt="Please unlock disk ${dev}"
842                         ;;
843
844                 *)
845                         cryptkeyscript="/lib/cryptsetup/askpass"
846                         cryptkeyprompt="Please unlock disk ${dev}: "
847                         ;;
848         esac
849
850         while true
851         do
852                 $cryptkeyscript "$cryptkeyprompt" | \
853                         /sbin/cryptsetup -T 1 luksOpen ${dev} ${name} ${opts}
854
855                 if [ 0 -eq ${?} ]
856                 then
857                         luks_device="/dev/mapper/${name}"
858                         echo ${luks_device}
859                         return 0
860                 fi
861
862                 echo >&6
863                 retryprompt="There was an error decrypting ${dev} ... Retry? [Y/n]"
864
865                 case "${_PLYMOUTH}" in
866                         true)
867                                 plymouth display-message --text "${retryprompt}"
868                                 answer=$(plymouth watch-keystroke --keys="YNyn")
869                                 ;;
870
871                         *)
872                                 echo -n "${retryprompt} " >&6
873                                 read answer
874                                 ;;
875                 esac
876
877                 if [ "$(echo "${answer}" | cut -b1 | tr A-Z a-z)" = "n" ]
878                 then
879                         case "${_PLYMOUTH}" in
880                                 true)
881                                         plymouth display-message --text ""
882                                         ;;
883                         esac
884
885                         return 2
886                 fi
887         done
888 }
889
890 get_gpt_name ()
891 {
892     local dev
893     dev="${1}"
894     /sbin/blkid -s PART_ENTRY_NAME -p -o value ${dev} 2>/dev/null
895 }
896
897 is_gpt_device ()
898 {
899     local dev
900     dev="${1}"
901     [ "$(/sbin/blkid -s PART_ENTRY_SCHEME -p -o value ${dev} 2>/dev/null)" = "gpt" ]
902 }
903
904 probe_for_gpt_name ()
905 {
906         local overlays dev gpt_dev gpt_name
907         overlays="${1}"
908         dev="${2}"
909
910         gpt_dev="${dev}"
911         if is_active_luks_mapping ${dev}
912         then
913                 # if $dev is an opened luks device, we need to check
914                 # GPT stuff on the backing device
915                 gpt_dev=$(get_luks_backing_device "${dev}")
916         fi
917
918         if ! is_gpt_device ${gpt_dev}
919         then
920                 return
921         fi
922
923         gpt_name=$(get_gpt_name ${gpt_dev})
924         for label in ${overlays}
925         do
926                 if [ "${gpt_name}" = "${label}" ]
927                 then
928                         echo "${label}=${dev}"
929                 fi
930         done
931 }
932
933 probe_for_fs_label ()
934 {
935         local overlays dev
936         overlays="${1}"
937         dev="${2}"
938
939         for label in ${overlays}
940         do
941                 if [ "$(/sbin/blkid -s LABEL -o value $dev 2>/dev/null)" = "${label}" ]
942                 then
943                         echo "${label}=${dev}"
944                 fi
945         done
946 }
947
948 probe_for_file_name ()
949 {
950         local overlays dev ret backing
951         overlays="${1}"
952         dev="${2}"
953
954         ret=""
955         backing="$(mount_persistence_media ${dev} probe)"
956         if [ -z "${backing}" ]
957         then
958             return
959         fi
960
961         for label in ${overlays}
962         do
963                 path=${backing}/${PERSISTENCE_PATH}/${label}
964                 if [ -f "${path}" ]
965                 then
966                         local loopdev
967                         loopdev=$(setup_loop "${path}" "loop" "/sys/block/loop*")
968                         ret="${ret} ${label}=${loopdev}"
969                 fi
970         done
971
972         if [ -n "${ret}" ]
973         then
974                 echo ${ret}
975         else
976                 # unmount and remove mountpoint
977                 umount ${backing} > /dev/null 2>&1 || true
978                 rmdir ${backing} > /dev/null 2>&1 || true
979         fi
980 }
981
982 probe_for_directory_name ()
983 {
984         local overlays dev ret backing
985         overlays="${1}"
986         dev="${2}"
987
988         ret=""
989         backing="$(mount_persistence_media ${dev} probe)"
990         if [ -z "${backing}" ]
991         then
992             return
993         fi
994
995         for label in ${overlays}
996         do
997                 path=${backing}/${PERSISTENCE_PATH}/${label}
998                 if [ -d "${path}" ]
999                 then
1000                         # in this case the "device" ends with a "/"
1001                         ret="${ret} ${label}=${backing}/${PERSISTENCE_PATH}/${label%%/}/"
1002                 fi
1003         done
1004
1005         if [ -n "${ret}" ]
1006         then
1007                 echo ${ret}
1008         else
1009                 # unmount and remove mountpoint
1010                 umount ${backing} > /dev/null 2>&1 || true
1011                 rmdir ${backing} > /dev/null 2>&1 || true
1012         fi
1013 }
1014
1015 find_persistence_media ()
1016 {
1017         # Scans devices for overlays, and returns a whitespace
1018         # separated list of how to use them. Only overlays with a partition
1019         # label or file name in ${overlays} are returned.
1020         #
1021         # When scanning a LUKS device, the user will be asked to enter the
1022         # passphrase; on failure to enter it, or if no persistence partitions
1023         # or files were found, the LUKS device is closed.
1024         #
1025         # For all other cases (overlay partition and overlay file) the
1026         # return value is "${label}=${device}", where ${device} a device that
1027         # can mount the content. In the case of an overlay file, the device
1028         # containing the file will remain mounted as a side-effect.
1029         #
1030         # No devices in ${black_listed_devices} will be scanned, and if
1031         # ${white_list_devices} is non-empty, only devices in it will be
1032         # scanned.
1033
1034         local overlays white_listed_devices ret black_listed_devices
1035         overlays="${1}"
1036         white_listed_devices="${2}"
1037         ret=""
1038
1039         #
1040         # The devices that are hosting the actual live rootfs should not be
1041         # used for persistence storage since otherwise you might mount a
1042         # parent directory on top of a sub-directory of the same filesystem
1043         # in one union together.
1044         #
1045         black_listed_devices=""
1046         for d in /live/rootfs/* /live/findiso /live/fromiso
1047         do
1048                 black_listed_devices="${black_listed_devices} $(what_is_mounted_on d)"
1049         done
1050
1051         for dev in $(storage_devices "${black_listed_devices}" "${white_listed_devices}")
1052         do
1053                 local result luks_device
1054                 result=""
1055
1056                 luks_device=""
1057                 # Check if it's a luks device; we'll have to open the device
1058                 # in order to probe any filesystem it contains, like we do
1059                 # below. activate_custom_mounts() also depends on that any luks
1060                 # device already has been opened.
1061                 if is_in_comma_sep_list luks ${PERSISTENCE_ENCRYPTION} && is_luks_partition ${dev}
1062                 then
1063                         if luks_device=$(open_luks_device "${dev}")
1064                         then
1065                                 dev="${luks_device}"
1066                         else
1067                                 # skip $dev since we failed/chose not to open it
1068                                 continue
1069                         fi
1070                 elif ! is_in_comma_sep_list none ${PERSISTENCE_ENCRYPTION}
1071                 then
1072                         # skip $dev since we don't allow unencrypted storage
1073                         continue
1074                 fi
1075
1076                 # Probe for matching GPT partition names or filesystem labels
1077                 if is_in_comma_sep_list filesystem ${PERSISTENCE_STORAGE}
1078                 then
1079                         result=$(probe_for_gpt_name "${overlays}" ${dev})
1080                         if [ -n "${result}" ]
1081                         then
1082                                 ret="${ret} ${result}"
1083                                 continue
1084                         fi
1085
1086                         result=$(probe_for_fs_label "${overlays}" ${dev})
1087                         if [ -n "${result}" ]
1088                         then
1089                                 ret="${ret} ${result}"
1090                                 continue
1091                         fi
1092                 fi
1093
1094                 # Probe for files with matching name on mounted partition
1095                 if is_in_comma_sep_list file ${PERSISTENCE_STORAGE}
1096                 then
1097                         result=$(probe_for_file_name "${overlays}" ${dev})
1098                         if [ -n "${result}" ]
1099                         then
1100                                 local loopdevice
1101                                 loopdevice=${result##*=}
1102                                 if is_in_comma_sep_list luks ${PERSISTENCE_ENCRYPTION} && is_luks_partition ${loopdevice}
1103                                 then
1104                                         local luksfile
1105                                         luksfile=""
1106                                         if luksfile=$(open_luks_device "${loopdevice}")
1107                                         then
1108                                                 result=${result%%=*}
1109                                                 result="${result}=${luksfile}"
1110                                         else
1111                                                 losetup -d $loopdevice
1112                                                 result=""
1113                                         fi
1114                                 fi
1115                                 ret="${ret} ${result}"
1116                                 continue
1117                         fi
1118                 fi
1119
1120                 # Probe for directory with matching name on mounted partition
1121                 if is_in_comma_sep_list directory ${PERSISTENCE_STORAGE}
1122                 then
1123                         result=$(probe_for_directory_name "${overlays}" ${dev})
1124                         if [ -n "${result}" ]
1125                         then
1126                                 ret="${ret} ${result}"
1127                                 continue
1128                         fi
1129                 fi
1130
1131                 # Close luks device if it isn't used
1132                 if [ -z "${result}" ] && [ -n "${luks_device}" ] && is_active_luks_mapping "${luks_device}"
1133                 then
1134                         /sbin/cryptsetup luksClose "${luks_device}"
1135                 fi
1136         done
1137
1138         if [ -n "${ret}" ]
1139         then
1140                 echo ${ret}
1141         fi
1142 }
1143
1144 get_mac ()
1145 {
1146         mac=""
1147
1148         for adaptor in /sys/class/net/*
1149         do
1150                 status="$(cat ${adaptor}/iflink)"
1151
1152                 if [ "${status}" -eq 2 ]
1153                 then
1154                         mac="$(cat ${adaptor}/address)"
1155                         mac="$(echo ${mac} | sed 's/:/-/g' | tr '[a-z]' '[A-Z]')"
1156                 fi
1157         done
1158
1159         echo ${mac}
1160 }
1161
1162 is_luks_partition ()
1163 {
1164         device="${1}"
1165         /sbin/cryptsetup isLuks "${device}" 1>/dev/null 2>&1
1166 }
1167
1168 is_active_luks_mapping ()
1169 {
1170         device="${1}"
1171         /sbin/cryptsetup status "${device}" 1>/dev/null 2>&1
1172 }
1173
1174 get_luks_backing_device ()
1175 {
1176         device=${1}
1177         cryptsetup status ${device} 2> /dev/null | \
1178                 awk '{if ($1 == "device:") print $2}'
1179 }
1180
1181 removable_dev ()
1182 {
1183         output_format="${1}"
1184         want_usb="${2}"
1185         ret=
1186
1187         for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -vE "/(loop|ram|dm-|fd)")
1188         do
1189                 dev_ok=
1190                 if [ "$(cat ${sysblock}/removable)" = "1" ]
1191                 then
1192                         if [ -z "${want_usb}" ]
1193                         then
1194                                 dev_ok="true"
1195                         else
1196                                 if readlink ${sysblock} | grep -q usb
1197                                 then
1198                                         dev_ok="true"
1199                                 fi
1200                         fi
1201                 fi
1202
1203                 if [ "${dev_ok}" = "true" ]
1204                 then
1205                         case "${output_format}" in
1206                                 sys)
1207                                         ret="${ret} ${sysblock}"
1208                                         ;;
1209                                 *)
1210                                         devname=$(sys2dev "${sysblock}")
1211                                         ret="${ret} ${devname}"
1212                                         ;;
1213                         esac
1214                 fi
1215         done
1216
1217         echo "${ret}"
1218 }
1219
1220 removable_usb_dev ()
1221 {
1222         output_format="${1}"
1223
1224         removable_dev "${output_format}" "want_usb"
1225 }
1226
1227 non_removable_dev ()
1228 {
1229         output_format="${1}"
1230         ret=
1231
1232         for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -vE "/(loop|ram|dm-|fd)")
1233         do
1234                 if [ "$(cat ${sysblock}/removable)" = "0" ]
1235                 then
1236                         case "${output_format}" in
1237                                 sys)
1238                                         ret="${ret} ${sysblock}"
1239                                         ;;
1240                                 *)
1241                                         devname=$(sys2dev "${sysblock}")
1242                                         ret="${ret} ${devname}"
1243                                         ;;
1244                         esac
1245                 fi
1246         done
1247
1248         echo "${ret}"
1249 }
1250
1251 link_files ()
1252 {
1253         # create source's directory structure in dest, and recursively
1254         # create symlinks in dest to to all files in source. if mask
1255         # is non-empty, remove mask from all source paths when
1256         # creating links (will be necessary if we change root, which
1257         # live-boot normally does (into $rootmnt)).
1258         local src_dir dest_dir src_transform
1259
1260         # remove multiple /:s and ensure ending on /
1261         src_dir="$(trim_path ${1})/"
1262         dest_dir="$(trim_path ${2})/"
1263         src_transform="${3}"
1264
1265         # This check can only trigger on the inital, non-recursive call since
1266         # we create the destination before recursive calls
1267         if [ ! -d "${dest_dir}" ]
1268         then
1269                 log_warning_msg "Must link_files into a directory"
1270                 return
1271         fi
1272
1273         find "${src_dir}" -mindepth 1 -maxdepth 1 | \
1274         while read src
1275         do
1276                 local dest final_src
1277                 dest="${dest_dir}$(basename "${src}")"
1278                 if [ -d "${src}" ]
1279                 then
1280                         if [ -z "$(ls -A "${src}")" ]
1281                         then
1282                                 continue
1283                         fi
1284                         if [ ! -d "${dest}" ]
1285                         then
1286                                 mkdir -p "${dest}"
1287                                 chown_ref "${src}" "${dest}"
1288                                 chmod_ref "${src}" "${dest}"
1289                         fi
1290                         link_files "${src}" "${dest}" "${src_transform}"
1291                 else
1292                         final_src=${src}
1293                         if [ -n "${src_transform}" ]
1294                         then
1295                                 final_src="$(echo ${final_src} | sed "${src_transform}")"
1296                         fi
1297                         rm -rf "${dest}" 2> /dev/null
1298                         ln -s "${final_src}" "${dest}"
1299                         chown_ref "${src}" "${dest}"
1300                 fi
1301         done
1302 }
1303
1304 do_union ()
1305 {
1306         local unionmountpoint unionrw unionro
1307         unionmountpoint="${1}"  # directory where the union is mounted
1308         shift
1309         unionrw="${1}"          # branch where the union changes are stored
1310         shift
1311         unionro="${*}"          # space separated list of read-only branches (optional)
1312
1313         case "${UNIONTYPE}" in
1314                 aufs)
1315                         rw_opt="rw"
1316                         ro_opt="rr+wh"
1317                         noxino_opt="noxino"
1318
1319                         unionmountopts="-o noatime,${noxino_opt},dirs=${unionrw}=${rw_opt}"
1320                         if [ -n "${unionro}" ]
1321                         then
1322                                 for rofs in ${unionro}
1323                                 do
1324                                         unionmountopts="${unionmountopts}:${rofs}=${ro_opt}"
1325                                 done
1326                         fi
1327                         ;;
1328
1329                 overlay)
1330                         # XXX: can multiple unionro be used? (overlay only handles two dirs, but perhaps they can be chained?)
1331                         # XXX: and can unionro be optional? i.e. can overlay skip lowerdir?
1332                         if echo ${unionro} | grep -q " "
1333                         then
1334                                 panic "Multiple lower filesystems are currently not supported with overlay (unionro = ${unionro})."
1335                         elif [ -z "${unionro}"  ]
1336                         then
1337                                 panic "overlay needs at least one lower filesystem (read-only branch)."
1338                         fi
1339                         # overlayfs requires:
1340                         # + a workdir to become mounted
1341                         # + workdir and upperdir to reside under the same mount
1342                         # + workdir and upperdir to be in separate directories
1343                         mkdir "${unionrw}/rw"
1344                         mkdir "${unionrw}/work"
1345                         unionmountopts="-o noatime,lowerdir=${unionro},upperdir=${unionrw}/rw,workdir=${unionrw}/work"
1346                         ;;
1347         esac
1348
1349         mount -t ${UNIONTYPE} ${unionmountopts} ${UNIONTYPE} "${unionmountpoint}"
1350 }
1351
1352 get_custom_mounts ()
1353 {
1354         # Side-effect: leaves $devices with persistence.conf mounted in /live/persistence
1355         # Side-effect: prints info to file $custom_mounts
1356
1357         local custom_mounts devices bindings links
1358         custom_mounts=${1}
1359         shift
1360         devices=${@}
1361
1362         bindings="/tmp/bindings.list"
1363         links="/tmp/links.list"
1364         rm -rf ${bindings} ${links} 2> /dev/null
1365
1366         for device in ${devices}
1367         do
1368                 local device_name backing include_list
1369                 device_name="$(basename ${device})"
1370                 backing=$(mount_persistence_media ${device})
1371                 if [ -z "${backing}" ]
1372                 then
1373                         continue
1374                 fi
1375
1376                 if [ -r "${backing}/${persistence_list}" ]
1377                 then
1378                         include_list="${backing}/${persistence_list}"
1379                 else
1380                         continue
1381                 fi
1382
1383                 if [ -n "${LIVE_BOOT_DEBUG}" ] && [ -e "${include_list}" ]
1384                 then
1385                         cp ${include_list} /live/persistence/${persistence_list}.${device_name}
1386                 fi
1387
1388                 while read dir options # < ${include_list}
1389                 do
1390                         if echo ${dir} | grep -qe "^[[:space:]]*\(#.*\)\?$"
1391                         then
1392                                 # skipping empty or commented lines
1393                                 continue
1394                         fi
1395
1396                         if trim_path ${dir} | grep -q -e "^[^/]" -e "^/lib" -e "^/lib/live\(/.*\)\?$" -e "^/\(.*/\)\?\.\.\?\(/.*\)\?$"
1397                         then
1398                                 log_warning_msg "Skipping unsafe custom mount ${dir}: must be an absolute path containing neither the \".\" nor \"..\" special dirs, and cannot be \"/lib\", or \"/lib/live\" or any of its sub-directories."
1399                                 continue
1400                         fi
1401
1402                         local opt_source opt_link source full_source full_dest
1403                         opt_source=""
1404                         opt_link=""
1405                         for opt in $(echo ${options} | tr ',' ' ');
1406                         do
1407                                 case "${opt}" in
1408                                         source=*)
1409                                                 opt_source=${opt#source=}
1410                                                 ;;
1411                                         link)
1412                                                 opt_link="true"
1413                                                 ;;
1414                                         union|bind)
1415                                                 ;;
1416                                         *)
1417                                                 log_warning_msg "Skipping custom mount with unknown option: ${opt}"
1418                                                 continue 2
1419                                                 ;;
1420                                 esac
1421                         done
1422
1423                         source="${dir}"
1424                         if [ -n "${opt_source}" ]
1425                         then
1426                                 if echo ${opt_source} | grep -q -e "^/" -e "^\(.*/\)\?\.\.\?\(/.*\)\?$" && [ "${opt_source}" != "." ]
1427                                 then
1428                                         log_warning_msg "Skipping unsafe custom mount with option source=${opt_source}: must be either \".\" (the media root) or a relative path w.r.t. the media root that contains neither comas, nor the special \".\" and \"..\" path components"
1429                                         continue
1430                                 else
1431                                         source="${opt_source}"
1432                                 fi
1433                         fi
1434
1435                         full_source="$(trim_path ${backing}/${source})"
1436                         full_dest="$(trim_path ${rootmnt}/${dir})"
1437                         if [ -n "${opt_link}" ]
1438                         then
1439                                 echo "${device} ${full_source} ${full_dest} ${options}" >> ${links}
1440                         else
1441                                 echo "${device} ${full_source} ${full_dest} ${options}" >> ${bindings}
1442                         fi
1443                 done < ${include_list}
1444         done
1445
1446         # We sort the list according to destination so we're sure that
1447         # we won't hide a previous mount. We also ignore duplicate
1448         # destinations in a more or less arbitrary way.
1449         [ -e "${bindings}" ] && sort -k3 -sbu ${bindings} >> ${custom_mounts} && rm ${bindings}
1450
1451         # After all mounts are considered we add symlinks so they
1452         # won't be hidden by some mount.
1453         [ -e "${links}" ] && cat ${links} >> ${custom_mounts} && rm ${links}
1454
1455         # We need to make sure that no two custom mounts have the same sources
1456         # or are nested; if that is the case, too much weird stuff can happen.
1457         local prev_source prev_dest
1458         prev_source="impossible source" # first iteration must not match
1459         prev_dest=""
1460         # This sort will ensure that a source /a comes right before a source
1461         # /a/b so we only need to look at the previous source
1462         [ -e ${custom_mounts} ] && sort -k2 -b ${custom_mounts} |
1463         while read device source dest options
1464         do
1465                 if echo ${source} | grep -qe "^${prev_source}\(/.*\)\?$"
1466                 then
1467                         panic "Two persistence mounts have the same or nested sources: ${source} on ${dest}, and ${prev_source} on ${prev_dest}"
1468                 fi
1469                 prev_source=${source}
1470                 prev_dest=${dest}
1471         done
1472 }
1473
1474 activate_custom_mounts ()
1475 {
1476         local custom_mounts used_devices
1477         custom_mounts="${1}" # the ouput from get_custom_mounts()
1478         used_devices=""
1479
1480         while read device source dest options # < ${custom_mounts}
1481         do
1482                 local opt_bind opt_link opt_union
1483                 opt_bind="true"
1484                 opt_link=""
1485                 opt_union=""
1486                 for opt in $(echo ${options} | tr ',' ' ');
1487                 do
1488                         case "${opt}" in
1489                                 bind)
1490                                         opt_bind="true"
1491                                         unset opt_link opt_union
1492                                         ;;
1493                                 link)
1494                                         opt_link="true"
1495                                         unset opt_bind opt_union
1496                                         ;;
1497                                 union)
1498                                         opt_union="true"
1499                                         unset opt_bind opt_link
1500                                         ;;
1501                         esac
1502                 done
1503
1504                 if [ -n "$(what_is_mounted_on "${dest}")" ]
1505                 then
1506                         if [ "${dest}" = "${rootmnt}" ]
1507                         then
1508                                 umount "${dest}"
1509                         else
1510                                 log_warning_msg "Skipping custom mount ${dest}: $(what_is_mounted_on "${dest}") is already mounted there"
1511                                 continue
1512                         fi
1513                 fi
1514
1515                 if [ ! -d "${dest}" ]
1516                 then
1517                         # create the destination and delete existing files in
1518                         # its path that are in the way
1519                         path="/"
1520                         for dir in $(echo ${dest} | sed -e 's|/\+| |g')
1521                         do
1522                                 path=$(trim_path ${path}/${dir})
1523                                 if [ -f ${path} ]
1524                                 then
1525                                         rm -f ${path}
1526                                 fi
1527                                 if [ ! -e ${path} ]
1528                                 then
1529                                         mkdir -p ${path}
1530                                         if echo ${path} | grep -qe "^${rootmnt}/*home/[^/]\+"
1531                                         then
1532                                                 # if ${dest} is in /home try fixing proper ownership by assuming that the intended user is the first, which is usually the case
1533                                                 # FIXME: this should really be handled by live-config since we don't know for sure which uid a certain user has until then
1534                                                 chown 1000:1000 ${path}
1535                                         fi
1536                                 fi
1537                         done
1538                 fi
1539
1540                 # if ${source} doesn't exist on our persistence media
1541                 # we bootstrap it with $dest from the live filesystem.
1542                 # this both makes sense and is critical if we're
1543                 # dealing with /etc or other system dir.
1544                 if [ ! -d "${source}" ]
1545                 then
1546                         if [ -n "${PERSISTENCE_READONLY}" ]
1547                         then
1548                                 continue
1549                         elif [ -n "${opt_union}" ] || [ -n "${opt_link}" ]
1550                         then
1551                                 # unions and don't need to be bootstrapped
1552                                 # link dirs can't be bootstrapped in a sensible way
1553                                 mkdir -p "${source}"
1554                                 chown_ref "${dest}" "${source}"
1555                                 chmod_ref "${dest}" "${source}"
1556                         elif [ -n "${opt_bind}" ]
1557                         then
1558                                 # ensure that $dest is not copied *into* $source
1559                                 mkdir -p "$(dirname ${source})"
1560                                 cp -a "${dest}" "${source}"
1561                         fi
1562                 fi
1563
1564                 # XXX: If CONFIG_AUFS_ROBR is added to the Debian kernel we can
1565                 # ignore the loop below and set rootfs_dest_backing=$dest
1566                 local rootfs_dest_backing
1567                 rootfs_dest_backing=""
1568                 if [ -n "${opt_link}" ] || [ -n "${opt_union}" ]
1569                 then
1570                         for d in /live/rootfs/*
1571                         do
1572                                 if [ -n "${rootmnt}" ]
1573                                 then
1574                                         fs="${d}/$(echo ${dest} | sed -e "s|${rootmnt}||")"
1575                                 else
1576                                         fs="${d}/${dest}"
1577                                 fi
1578                                 if [ -d "${fs}" ]
1579                                 then
1580                                         rootfs_dest_backing="${rootfs_dest_backing} ${fs}"
1581                                 fi
1582                         done
1583                 fi
1584
1585                 local cow_dir links_source
1586                 if [ -n "${opt_link}" ] && [ -z "${PERSISTENCE_READONLY}" ]
1587                 then
1588                         link_files ${source} ${dest} "s|^/live/|/lib/live/mount/|"
1589                 elif [ -n "${opt_link}" ] && [ -n "${PERSISTENCE_READONLY}" ]
1590                 then
1591                         mkdir -p ${rootmnt}/lib/live/mount/persistence
1592                         links_source=$(mktemp -d ${rootmnt}/lib/live/mount/persistence/links-source-XXXXXX)
1593                         chown_ref ${source} ${links_source}
1594                         chmod_ref ${source} ${links_source}
1595                         # We put the cow dir in the below strange place to
1596                         # make it absolutely certain that the link source
1597                         # has its own directory and isn't nested with some
1598                         # other custom mount (if so that mount's files would
1599                         # be linked, causing breakage.
1600                         cow_dir="/live/overlay/lib/live/mount/persistence/$(basename ${links_source})"
1601                         mkdir -p ${cow_dir}
1602                         chown_ref "${source}" "${cow_dir}"
1603                         chmod_ref "${source}" "${cow_dir}"
1604                         do_union ${links_source} ${cow_dir} ${source} ${rootfs_dest_backing}
1605                         link_files ${links_source} ${dest} "s|^${rootmnt}||"
1606                 elif [ -n "${opt_union}" ] && [ -z "${PERSISTENCE_READONLY}" ]
1607                 then
1608                         do_union ${dest} ${source} ${rootfs_dest_backing}
1609                 elif [ -n "${opt_bind}" ] && [ -z "${PERSISTENCE_READONLY}" ]
1610                 then
1611                         mount --bind "${source}" "${dest}"
1612                 elif [ -n "${opt_bind}" -o -n "${opt_union}" ] && [ -n "${PERSISTENCE_READONLY}" ]
1613                 then
1614                         # bind-mount and union mount are handled the same
1615                         # in read-only mode, but note that rootfs_dest_backing
1616                         # is non-empty (and necessary) only for unions
1617                         cow_dir="/live/overlay/${dest}"
1618                         if [ -e "${cow_dir}" ] && [ -z "${opt_link}" ]
1619                         then
1620                                 # If an earlier custom mount has files here
1621                                 # it will "block" the current mount's files
1622                                 # which is undesirable
1623                                 rm -rf "${cow_dir}"
1624                         fi
1625                         mkdir -p ${cow_dir}
1626                         chown_ref "${source}" "${cow_dir}"
1627                         chmod_ref "${source}" "${cow_dir}"
1628                         do_union ${dest} ${cow_dir} ${source} ${rootfs_dest_backing}
1629                 fi
1630
1631                 PERSISTENCE_IS_ON="1"
1632                 export PERSISTENCE_IS_ON
1633
1634                 if echo ${used_devices} | grep -qve "^\(.* \)\?${device}\( .*\)\?$"
1635                 then
1636                         used_devices="${used_devices} ${device}"
1637                 fi
1638         done < ${custom_mounts}
1639
1640         echo ${used_devices}
1641 }
1642
1643 is_mountpoint ()
1644 {
1645         directory="$1"
1646
1647         [ $(stat -fc%d:%D "${directory}") != $(stat -fc%d:%D "${directory}/..") ]
1648 }