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