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