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