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