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