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