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