Handle already mounted devices and closing of unused luks devices better.
[live-boot-grml.git] / scripts / live-helpers
1 # live-boot helper functions, used by live-boot on boot and by live-snapshot
2
3 if [ ! -x "/bin/fstype" ]
4 then
5         # klibc not in path -> not in initramfs
6         export PATH="${PATH}:/usr/lib/klibc/bin"
7 fi
8
9 # handle upgrade path from old udev (using udevinfo) to
10 # recent versions of udev (using udevadm info)
11 if [ -x /sbin/udevadm ]
12 then
13         udevinfo='/sbin/udevadm info'
14 else
15         udevinfo='udevinfo'
16 fi
17
18 root_overlay_label="full-ov"
19 custom_overlay_label="custom-ov"
20 root_snapshot_label="live-sn"
21 home_snapshot_label="home-sn"
22 persistence_list="live.persist"
23
24 Arguments ()
25 {
26         PRESEEDS=""
27         LOCATIONS=""
28
29         for ARGUMENT in $(cat /proc/cmdline)
30         do
31                 case "${ARGUMENT}" in
32                         skipconfig)
33                                 NOACCESSIBILITY="Yes"
34                                 NOFASTBOOT="Yes"
35                                 NOFSTAB="Yes"
36                                 NONETWORKING="Yes"
37
38                                 export NOACCESSIBILITY NOFASTBOOT NOFSTAB NONETWORKING
39                                 ;;
40
41                         access=*)
42                                 ACCESS="${ARGUMENT#access=}"
43                                 export ACCESS
44                                 ;;
45
46                         console=*)
47                                 DEFCONSOLE="${ARGUMENT#*=}"
48                                 export DEFCONSOLE
49                                 ;;
50
51                         BOOTIF=*)
52                                 BOOTIF="${x#BOOTIF=}"
53                                 ;;
54
55                         debug)
56                                 DEBUG="Yes"
57                                 export DEBUG
58
59                                 set -x
60                                 ;;
61
62                         dhcp)
63                                 # Force dhcp even while netbooting
64                                 # Use for debugging in case somebody works on fixing dhclient
65                                 DHCP="Force";
66                                 export DHCP
67                                 ;;
68
69                         nodhcp)
70                                 unset DHCP
71                                 ;;
72
73                         ethdevice=*)
74                                 DEVICE="${ARGUMENT#ethdevice=}"
75                                 ETHDEVICE="${DEVICE}"
76                                 export DEVICE ETHDEVICE
77                                 ;;
78
79                         ethdevice-timeout=*)
80                                 ETHDEV_TIMEOUT="${ARGUMENT#ethdevice-timeout=}"
81                                 export ETHDEV_TIMEOUT
82                                 ;;
83
84                         fetch=*)
85                                 FETCH="${ARGUMENT#fetch=}"
86                                 export FETCH
87                                 ;;
88
89                         forcepersistentfsck)
90                                 FORCEPERSISTENTFSCK="Yes"
91                                 export FORCEPERSISTENTFSCK
92                                 ;;
93
94                         ftpfs=*)
95                                 FTPFS="${ARGUMENT#ftpfs=}"
96                                 export FTPFS
97                                 ;;
98
99                         httpfs=*)
100                                 HTTPFS="${ARGUMENT#httpfs=}"
101                                 export HTTPFS
102                                 ;;
103
104                         iscsi=*)
105                                 ISCSI="${ARGUMENT#iscsi=}"
106                                 #ip:port - separated by ;
107                                 ISCSI_PORTAL="${ISCSI%;*}"
108                                 if echo "${ISCSI_PORTAL}" | grep -q , ; then
109                                         ISCSI_SERVER="${ISCSI_PORTAL%,*}"
110                                         ISCSI_PORT="${ISCSI_PORTAL#*,}"
111                                 fi
112                                 #target name
113                                 ISCSI_TARGET="${ISCSI#*;}"
114                                 export ISCSI ISCSI_PORTAL ISCSI_TARGET ISCSI_SERVER ISCSI_PORT
115                                 ;;
116
117                         isofrom=*|fromiso=*)
118                                 FROMISO="${ARGUMENT#*=}"
119                                 export FROMISO
120                                 ;;
121
122                         ignore_uuid)
123                                 IGNORE_UUID="Yes"
124                                 export IGNORE_UUID
125                                 ;;
126
127                         integrity-check)
128                                 INTEGRITY_CHECK="Yes"
129                                 export INTEGRITY_CHECK
130                                 ;;
131
132                         ip=*)
133                                 STATICIP="${ARGUMENT#ip=}"
134
135                                 if [ -z "${STATICIP}" ]
136                                 then
137                                         STATICIP="frommedia"
138                                 fi
139
140                                 export STATICIP
141                                 ;;
142
143                         live-getty)
144                                 LIVE_GETTY="1"
145                                 export LIVE_GETTY
146                                 ;;
147
148                         live-media=*|bootfrom=*)
149                                 LIVE_MEDIA="${ARGUMENT#*=}"
150                                 export LIVE_MEDIA
151                                 ;;
152
153                         live-media-encryption=*|encryption=*)
154                                 LIVE_MEDIA_ENCRYPTION="${ARGUMENT#*=}"
155                                 export LIVE_MEDIA_ENCRYPTION
156                                 ;;
157
158                         live-media-offset=*)
159                                 LIVE_MEDIA_OFFSET="${ARGUMENT#live-media-offset=}"
160                                 export LIVE_MEDIA_OFFSET
161                                 ;;
162
163                         live-media-path=*)
164                                 LIVE_MEDIA_PATH="${ARGUMENT#live-media-path=}"
165                                 export LIVE_MEDIA_PATH
166                                 ;;
167
168                         live-media-timeout=*)
169                                 LIVE_MEDIA_TIMEOUT="${ARGUMENT#live-media-timeout=}"
170                                 export LIVE_MEDIA_TIMEOUT
171                                 ;;
172
173                         module=*)
174                                 MODULE="${ARGUMENT#module=}"
175                                 export MODULE
176                                 ;;
177
178                         netboot=*)
179                                 NETBOOT="${ARGUMENT#netboot=}"
180                                 export NETBOOT
181                                 ;;
182
183                         nfsopts=*)
184                                 NFSOPTS="${ARGUMENT#nfsopts=}"
185                                 export NFSOPTS
186                                 ;;
187
188                         nfscow=*)
189                                 NFS_COW="${ARGUMENT#nfscow=}"
190                                 export NFS_COW
191                                 ;;
192
193                         noaccessibility)
194                                 NOACCESSIBILITY="Yes"
195                                 export NOACCESSIBILITY
196                                 ;;
197
198                         nofastboot)
199                                 NOFASTBOOT="Yes"
200                                 export NOFASTBOOT
201                                 ;;
202
203                         nofstab)
204                                 NOFSTAB="Yes"
205                                 export NOFSTAB
206                                 ;;
207
208                         nonetworking)
209                                 NONETWORKING="Yes"
210                                 export NONETWORKING
211                                 ;;
212
213                         ramdisk-size=*)
214                                 ramdisk_size="${ARGUMENT#ramdisk-size=}"
215                                 ;;
216
217                         swapon)
218                                 SWAPON="Yes"
219                                 export SWAPON
220                                 ;;
221
222                         persistent)
223                                 PERSISTENT="Yes"
224                                 export PERSISTENT
225                                 ;;
226
227                         persistent-encryption=*)
228                                 PERSISTENT_ENCRYPTION="${ARGUMENT#*=}"
229                                 export PERSISTENT_ENCRYPTION
230                                 ;;
231
232                         persistent-media=*)
233                                 PERSISTENT_MEDIA="${ARGUMENT#*=}"
234                                 export PERSISTENT_MEDIA
235                                 ;;
236                         persistent-method=*)
237                                 PERSISTENT_METHOD="${ARGUMENT#*=}"
238                                 export PERSISTENT_METHOD
239                                 ;;
240
241                         persistent-path=*)
242                                 PERSISTENT_PATH="${ARGUMENT#persistent-path=}"
243                                 export PERSISTENT_PATH
244                                 ;;
245                         persistent-read-only)
246                                 PERSISTENT_READONLY="Yes"
247                                 export PERSISTENT_READONLY
248                                 ;;
249
250                         persistent-storage=*)
251                                 PERSISTENT_STORAGE="${ARGUMENT#persistent-storage=}"
252                                 export PERSISTENT_STORAGE
253                                 ;;
254
255                         persistent-subtext=*)
256                                 root_overlay_label="${root_overlay_label}-${ARGUMENT#persistent-subtext=}"
257                                 custom_overlay_label="${custom_overlay_label}-${ARGUMENT#persistent-subtext=}"
258                                 root_snapshot_label="${root_snapshot_label}-${ARGUMENT#persistent-subtext=}"
259                                 home_snapshot_label="${home_snapshot_label}-${ARGUMENT#persistent-subtext=}"
260                                 ;;
261
262                         nopersistent)
263                                 NOPERSISTENT="Yes"
264                                 export NOPERSISTENT
265                                 ;;
266
267                         noprompt)
268                                 NOPROMPT="Yes"
269                                 export NOPROMPT
270                                 ;;
271
272                         noprompt=*)
273                                 NOPROMPT="${ARGUMENT#noprompt=}"
274                                 export NOPROMPT
275                                 ;;
276
277                         quickusbmodules)
278                                 QUICKUSBMODULES="Yes"
279                                 export QUICKUSBMODULES
280                                 ;;
281
282                         preseed/file=*|file=*)
283                                 LOCATIONS="${ARGUMENT#*=} ${LOCATIONS}"
284                                 export LOCATIONS
285                                 ;;
286
287                         nopreseed)
288                                 NOPRESEED="Yes"
289                                 export NOPRESEED
290                                 ;;
291
292                         */*=*)
293                                 question="${ARGUMENT%%=*}"
294                                 value="${ARGUMENT#*=}"
295                                 PRESEEDS="${PRESEEDS}\"${question}=${value}\" "
296                                 export PRESEEDS
297                                 ;;
298
299                         showmounts)
300                                 SHOWMOUNTS="Yes"
301                                 export SHOWMOUNTS
302                                 ;;
303
304                         silent)
305                                 SILENT="Yes"
306                                 export SILENT
307                                 ;;
308
309                         todisk=*)
310                                 TODISK="${ARGUMENT#todisk=}"
311                                 export TODISK
312                                 ;;
313
314                         toram)
315                                 TORAM="Yes"
316                                 export TORAM
317                                 ;;
318
319                         toram=*)
320                                 TORAM="Yes"
321                                 MODULETORAM="${ARGUMENT#toram=}"
322                                 export TORAM MODULETORAM
323                                 ;;
324
325                         exposedroot)
326                                 EXPOSED_ROOT="Yes"
327                                 export EXPOSED_ROOT
328                                 ;;
329
330                         plainroot)
331                                 PLAIN_ROOT="Yes"
332                                 export PLAIN_ROOT
333                                 ;;
334
335                         skipunion)
336                                 SKIP_UNION_MOUNTS="Yes"
337                                 export SKIP_UNION_MOUNTS
338                                 ;;
339
340                         root=*)
341                                 ROOT="${ARGUMENT#root=}"
342                                 export ROOT
343                                 ;;
344
345                         union=*)
346                                 UNIONTYPE="${ARGUMENT#union=}"
347                                 export UNIONTYPE
348                                 ;;
349                 esac
350         done
351
352         # sort of compatibility with netboot.h from linux docs
353         if [ -z "${NETBOOT}" ]
354         then
355                 if [ "${ROOT}" = "/dev/nfs" ]
356                 then
357                         NETBOOT="nfs"
358                         export NETBOOT
359                 elif [ "${ROOT}" = "/dev/cifs" ]
360                 then
361                         NETBOOT="cifs"
362                         export NETBOOT
363                 fi
364         fi
365
366         if [ -z "${MODULE}" ]
367         then
368                 MODULE="filesystem"
369                 export MODULE
370         fi
371
372         if [ -z "${UNIONTYPE}" ]
373         then
374                 UNIONTYPE="aufs"
375                 export UNIONTYPE
376         fi
377
378         if [ -z "${PERSISTENT_ENCRYPTION}" ]
379         then
380                 PERSISTENT_ENCRYPTION="none"
381                 export PERSISTENT_ENCRYPTION
382         elif echo ${PERSISTENT_ENCRYPTION} | grep -qe "\<luks\>"
383         then
384                 if ! modprobe dm-crypt
385                 then
386                         log_warning_msg "Unable to load module dm-crypt"
387                         PERSISTENT_ENCRYPTION=$(echo ${PERSISTENT_ENCRYPTION} | sed -e 's/\<luks,\|,\?luks$//g')
388                         export PERSISTENT_ENCRYPTION
389                 fi
390
391                 if [ ! -x /lib/cryptsetup/askpass ] || [ ! -x /sbin/cryptsetup ]
392                 then
393                         log_warning_msg "cryptsetup in unavailable"
394                         PERSISTENT_ENCRYPTION=$(echo ${PERSISTENT_ENCRYPTION} | sed -e 's/\<luks,\|,\?luks$//g')
395                         export PERSISTENT_ENCRYPTION
396                 fi
397         fi
398
399         if [ -z "${PERSISTENT_METHOD}" ]
400         then
401                 PERSISTENT_METHOD="snapshot,overlay"
402                 export PERSISTENT_METHOD
403         fi
404
405         if [ -z "${PERSISTENT_STORAGE}" ]
406         then
407                 PERSISTENT_STORAGE="filesystem,file"
408                 export PERSISTENT_STORAGE
409         fi
410 }
411
412 sys2dev ()
413 {
414         sysdev=${1#/sys}
415         echo "/dev/$($udevinfo -q name -p ${sysdev} 2>/dev/null|| echo ${sysdev##*/})"
416 }
417
418 subdevices ()
419 {
420         sysblock=${1}
421         r=""
422
423         for dev in "${sysblock}"/* "${sysblock}"
424         do
425                 if [ -e "${dev}/dev" ]
426                 then
427                         r="${r} ${dev}"
428                 fi
429         done
430
431         echo ${r}
432 }
433
434 storage_devices()
435 {
436         black_listed_devices="${1}"
437         white_listed_devices="${2}"
438
439         for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -vE "loop|ram|fd")
440         do
441                 fulldevname=$(sys2dev "${sysblock}")
442
443                 if echo "${black_listed_devices}" | grep -qe "\<${fulldevname}\>" || \
444                         [ -n "${white_listed_devices}" ] && \
445                         echo "${white_listed_devices}" | grep -qve "\<${fulldevname}\>"
446                 then
447                         # skip this device entirely
448                         continue
449                 fi
450
451                 for dev in $(subdevices "${sysblock}")
452                 do
453                         devname=$(sys2dev "${dev}")
454
455                         if echo "${black_listed_devices}" | grep -qe "\<${devname}\>"
456                         then
457                                 # skip this subdevice
458                                 continue
459                         else
460                                 echo "${devname}"
461                         fi
462                 done
463         done
464 }
465
466 is_supported_fs ()
467 {
468         fstype="${1}"
469
470         # Validate input first
471         if [ -z "${fstype}" ]
472         then
473                 return 1
474         fi
475
476         # Try to look if it is already supported by the kernel
477         if grep -q ${fstype} /proc/filesystems
478         then
479                 return 0
480         else
481                 # Then try to add support for it the gentle way using the initramfs capabilities
482                 modprobe ${fstype}
483                 if grep -q ${fstype} /proc/filesystems
484                 then
485                         return 0
486                 # Then try the hard way if /root is already reachable
487                 else
488                         kmodule="/root/lib/modules/`uname -r`/${fstype}/${fstype}.ko"
489                         if [ -e "${kmodule}" ]
490                         then
491                                 insmod "${kmodule}"
492                                 if grep -q ${fstype} /proc/filesystems
493                                 then
494                                         return 0
495                                 fi
496                         fi
497                 fi
498         fi
499
500         return 1
501 }
502
503 get_fstype ()
504 {
505         /sbin/blkid -s TYPE -o value $1 2>/dev/null
506 }
507
508 where_is_mounted ()
509 {
510         device=${1}
511
512         if grep -q "^${device} " /proc/mounts
513         then
514                 # return the first found
515                 grep -m1 "^${device} " /proc/mounts | cut -f2 -d ' '
516         fi
517 }
518
519 lastline ()
520 {
521         while read lines
522         do
523                 line=${lines}
524         done
525
526         echo "${line}"
527 }
528
529 base_path ()
530 {
531         testpath="${1}"
532         mounts="$(awk '{print $2}' /proc/mounts)"
533         testpath="$(busybox realpath ${testpath})"
534
535         while true
536         do
537                 if echo "${mounts}" | grep -qs "^${testpath}"
538                 then
539                         set -- $(echo "${mounts}" | grep "^${testpath}" | lastline)
540                         echo ${1}
541                         break
542                 else
543                         testpath=$(dirname $testpath)
544                 fi
545         done
546 }
547
548 fs_size ()
549 {
550         # Returns used/free fs kbytes + 5% more
551         # You could pass a block device as ${1} or the mount point as ${2}
552
553         dev="${1}"
554         mountp="${2}"
555         used="${3}"
556
557         if [ -z "${mountp}" ]
558         then
559                 mountp="$(where_is_mounted ${dev})"
560
561                 if [ -z "${mountp}" ]
562                 then
563                         mountp="/mnt/tmp_fs_size"
564
565                         mkdir -p "${mountp}"
566                         mount -t $(get_fstype "${dev}") -o ro "${dev}" "${mountp}" || log_warning_msg "cannot mount -t $(get_fstype ${dev}) -o ro ${dev} ${mountp}"
567
568                         doumount=1
569                 fi
570         fi
571
572         if [ "${used}" = "used" ]
573         then
574                 size=$(du -ks ${mountp} | cut -f1)
575                 size=$(expr ${size} + ${size} / 20 ) # FIXME: 5% more to be sure
576         else
577                 # free space
578                 size="$(df -k | grep -s ${mountp} | awk '{print $4}')"
579         fi
580
581         if [ -n "${doumount}" ]
582         then
583                 umount "${mountp}" || log_warning_msg "cannot umount ${mountp}"
584                 rmdir "${mountp}"
585         fi
586
587         echo "${size}"
588 }
589
590 load_keymap ()
591 {
592         # Load custom keymap
593         if [ -x /bin/loadkeys -a -r /etc/boottime.kmap.gz ]
594         then
595                 loadkeys /etc/boottime.kmap.gz
596         fi
597 }
598
599 setup_loop ()
600 {
601         local fspath=${1}
602         local module=${2}
603         local pattern=${3}
604         local offset=${4}
605         local encryption=${5}
606         local readonly=${6}
607
608         # the output of setup_loop is evaluated in other functions,
609         # modprobe leaks kernel options like "libata.dma=0"
610         # as "options libata dma=0" on stdout, causing serious
611         # problems therefor, so instead always avoid output to stdout
612         modprobe -q -b "${module}" 1>/dev/null
613
614         udevadm settle
615
616         for loopdev in ${pattern}
617         do
618                 if [ "$(cat ${loopdev}/size)" -eq 0 ]
619                 then
620                         dev=$(sys2dev "${loopdev}")
621                         options=''
622
623                         if [ -n "${readonly}" ]
624                         then
625                                 if losetup --help 2>&1 | grep -q -- "-r\b"
626                                 then
627                                         options="${options} -r"
628                                 fi
629                         fi
630
631                         if [ -n "${offset}" ] && [ 0 -lt "${offset}" ]
632                         then
633                                 options="${options} -o ${offset}"
634                         fi
635
636                         if [ -z "${encryption}" ]
637                         then
638                                 losetup ${options} "${dev}" "${fspath}"
639                         else
640                                 # Loop AES encryption
641                                 while true
642                                 do
643                                         load_keymap
644
645                                         echo -n "Enter passphrase for root filesystem: " >&6
646                                         read -s passphrase
647                                         echo "${passphrase}" > /tmp/passphrase
648                                         unset passphrase
649                                         exec 9</tmp/passphrase
650                                         /sbin/losetup ${options} -e "${encryption}" -p 9 "${dev}" "${fspath}"
651                                         error=${?}
652                                         exec 9<&-
653                                         rm -f /tmp/passphrase
654
655                                         if [ 0 -eq ${error} ]
656                                         then
657                                                 unset error
658                                                 break
659                                         fi
660
661                                         echo
662                                         echo -n "There was an error decrypting the root filesystem ... Retry? [Y/n] " >&6
663                                         read answer
664
665                                         if [ "$(echo "${answer}" | cut -b1 | tr A-Z a-z)" = "n" ]
666                                         then
667                                                 unset answer
668                                                 break
669                                         fi
670                                 done
671                         fi
672
673                         echo "${dev}"
674                         return 0
675                 fi
676         done
677
678         panic "No loop devices available"
679 }
680
681 try_mount ()
682 {
683         dev="${1}"
684         mountp="${2}"
685         opts="${3}"
686         fstype="${4}"
687
688         old_mountp="$(where_is_mounted ${dev})"
689
690         if [ -n "${old_mountp}" ]
691         then
692                 if [ "${opts}" != "ro" ]
693                 then
694                         mount -o remount,"${opts}" "${dev}" "${old_mountp}" || panic "Remounting ${dev} ${opts} on ${old_mountp} failed"
695                 fi
696
697                 mount -o bind "${old_mountp}" "${mountp}" || panic "Cannot bind-mount ${old_mountp} on ${mountp}"
698         else
699                 if [ -z "${fstype}" ]
700                 then
701                         fstype=$(get_fstype "${dev}")
702                 fi
703                 mount -t "${fstype}" -o "${opts}" "${dev}" "${mountp}" || \
704                 ( echo "SKIPPING: Cannot mount ${dev} on ${mountp}, fstype=${fstype}, options=${opts}" > live-boot.log && return 0 )
705         fi
706 }
707
708 open_luks_device ()
709 {
710         dev="${1}"
711         name="$(basename ${dev})"
712         opts="--key-file=-"
713         if [ -n "${PERSISTENT_READONLY}" ]
714         then
715                 opts="${opts} --readonly"
716         fi
717
718         load_keymap
719
720         while true
721         do
722                 /lib/cryptsetup/askpass "Enter passphrase for ${dev}: " | \
723                         /sbin/cryptsetup -T 1 luksOpen ${dev} ${name} ${opts}
724
725                 if [ 0 -eq ${?} ]
726                 then
727                         luks_device="/dev/mapper/${name}"
728                         echo ${luks_device}
729                         return 0
730                 fi
731
732                 echo >&6
733                 echo -n "There was an error decrypting ${dev} ... Retry? [Y/n] " >&6
734                 read answer
735
736                 if [ "$(echo "${answer}" | cut -b1 | tr A-Z a-z)" = "n" ]
737                 then
738                         return 2
739                 fi
740         done
741 }
742
743 get_gpt_name () {
744     local dev="${1}"
745     /sbin/blkid -s PART_ENTRY_NAME -p -o value ${dev} 2>/dev/null
746 }
747
748 is_gpt_device () {
749     local dev="${1}"
750     [ "$(/sbin/blkid -s PART_ENTRY_SCHEME -p -o value ${dev} 2>/dev/null)" = "gpt" ]
751 }
752
753 probe_for_gpt_name ()
754 {
755         local overlays="${1}"
756         local snapshots="${2}"
757         local dev="${3}"
758
759         if ! is_gpt_device ${dev} || \
760            ( echo ${PERSISTENT_ENCRYPTION} | grep -qve "\<luks\>" && \
761              /sbin/cryptsetup isLuks ${dev} > /dev/null 2>&1 )
762         then
763                 return
764         fi
765         for label in ${overlays} ${snapshots}
766         do
767                 if [ "$(get_gpt_name ${dev})" = "${label}" ]
768                 then
769                         echo "${label}=${dev}"
770                 fi
771         done
772 }
773
774 probe_for_fs_label () {
775         local overlays="${1}"
776         local snapshots="${2}"
777         local dev="${3}"
778
779         for label in ${overlays} ${snapshots}
780         do
781                 if [ "$(/sbin/blkid -s LABEL -o value $dev 2>/dev/null)" = "${label}" ]
782                 then
783                         echo "${label}=${dev}"
784                 fi
785         done
786 }
787
788 probe_for_file_name () {
789         local overlays="${1}"
790         local snapshots="${2}"
791         local dev="${3}"
792
793         local devfstype="$(get_fstype ${dev})"
794         local backing="${rootmnt}/live/persistent/$(basename ${dev})"
795         local ret=""
796         if is_supported_fs ${devfstype} && mkdir -p "${backing}" && \
797            try_mount "${dev}" "${backing}" "rw" "${devfstype}"
798         then
799                 for label in ${overlays}
800                 do
801                         path=${backing}/${PERSISTENT_PATH}${label}
802                         if [ -f "${path}" ]
803                         then
804                                 local loopdev=$(setup_loop "${path}" "loop" "/sys/block/loop*")
805                                 ret="${ret} ${label}=${loopdev}"
806                         fi
807                 done
808                 for label in ${snapshots}
809                 do
810                         for ext in squashfs cpio.gz ext2 ext3 ext4 jffs2
811                         do
812                                 path="${PERSISTENT_PATH}${label}.${ext}"
813                                 if [ -f "${backing}/${path}" ]
814                                 then
815                                         ret="${ret} ${label}=${dev}:${backing}:${path}"
816                                 fi
817                         done
818                 done
819
820                 if [ -n "${ret}" ]
821                 then
822                         echo ${ret}
823                 else
824                         umount ${backing} > /dev/null 2>&1 || true
825                 fi
826         fi
827 }
828
829 find_persistent_media ()
830 {
831         # Scans devices for overlays and snapshots, and returns a whitespace
832         # separated list of how to use them. Only overlays with a partition
833         # label or file name in ${overlays} are returned, and ditto for
834         # snapshots with labels in ${snapshots}.
835         #
836         # When scanning a LUKS device, the user will be asked to enter the
837         # passphrase; on failure to enter it, or if no persistent partitions
838         # or files were found, the LUKS device is closed.
839         #
840         # For a snapshot file the return value is ${label}=${snapdata}", where
841         # ${snapdata} is the parameter used for try_snap().
842         #
843         # For all other cases (overlay/snapshot partition and overlay file) the
844         # return value is "${label}=${device}", where ${device} a device that
845         # can mount the content. In the case of an overlay file, the device
846         # containing the file will remain mounted as a side-effect.
847         #
848         # No devices in ${black_listed_devices} will be scanned, and if
849         # ${white_list_devices} is non-empty, only devices in it will be
850         # scanned.
851
852         local overlays="${1}"
853         local snapshots="${2}"
854         local black_listed_devices="${3}"
855         local white_listed_devices="${4}"
856         local ret=""
857
858         for dev in $(storage_devices "${black_listed_devices}" "${white_listed_devices}")
859         do
860                 local result=""
861
862                 local real_dev=""
863                 local luks_device=""
864                 # Check if it's a luks device; we'll have to open the device
865                 # in order to probe any filesystem it contains, like we do
866                 # below. do_custom_mounts() also depends on that any luks
867                 # device already has been opened.
868                 if echo ${PERSISTENT_ENCRYPTION} | grep -qe "\<luks\>" && \
869                    /sbin/cryptsetup isLuks ${dev} >/dev/null 2>&1
870                 then
871                         if luks_device=$(open_luks_device "${dev}")
872                         then
873                                 real_dev="${dev}"
874                                 dev="${luks_device}"
875                         else
876                                 # skip $dev since we failed/chose not to open it
877                                 continue
878                         fi
879                 elif echo ${PERSISTENT_ENCRYPTION} | grep -qve "\<none\>"
880                 then
881                         # skip $dev since we don't allow unencrypted storage
882                         continue
883                 fi
884
885                 # Probe for matching GPT partition names or filesystem labels
886                 if echo ${PERSISTENT_STORAGE} | grep -qe "\<filesystem\>"
887                 then
888                         local gpt_dev="${dev}"
889                         if [ -n "${luks_device}" ]
890                         then
891                                 # When we probe GPT partitions we need to look
892                                 # at the real device, not the virtual, opened
893                                 # luks device
894                                 gpt_dev="${real_dev}"
895                         fi
896                         result=$(probe_for_gpt_name "${overlays}" "${snapshots}" ${gpt_dev})
897                         if [ -n "${result}" ]
898                         then
899                                 ret="${ret} ${result}"
900                                 continue
901                         fi
902
903                         result=$(probe_for_fs_label "${overlays}" "${snapshots}" ${dev})
904                         if [ -n "${result}" ]
905                         then
906                                 ret="${ret} ${result}"
907                                 continue
908                         fi
909                 fi
910
911                 # Probe for files with matching name on mounted partition
912                 if echo ${PERSISTENT_STORAGE} | grep -qe "\<file\>"
913                 then
914                         result=$(probe_for_file_name "${overlays}" "${snapshots}" ${dev})
915                         if [ -n "${result}" ]
916                         then
917                                 ret="${ret} ${result}"
918                                 continue
919                         fi
920                 fi
921
922                 # Close luks device if it isn't used
923                 if [ -z "${result}" ] && [ -n "${luks_device}" ] && \
924                    /sbin/cryptsetup status "${luks_device}" 1> /dev/null 2>&1
925                 then
926                         /sbin/cryptsetup luksClose "${luks_device}"
927                 fi
928         done
929
930         if [ -n "${ret}" ]
931         then
932                 echo ${ret}
933         fi
934 }
935
936 get_mac ()
937 {
938         mac=""
939
940         for adaptor in /sys/class/net/*
941         do
942                 status="$(cat ${adaptor}/iflink)"
943
944                 if [ "${status}" -eq 2 ]
945                 then
946                         mac="$(cat ${adaptor}/address)"
947                         mac="$(echo ${mac} | sed 's/:/-/g' | tr '[a-z]' '[A-Z]')"
948                 fi
949         done
950
951         echo ${mac}
952 }
953
954 is_luks()
955 {
956     devname="${1}"
957     if [ -x /sbin/cryptsetup ]
958     then
959         /sbin/cryptsetup isLuks "${devname}" 2>/dev/null || ret=${?}
960         return ${ret}
961     else
962         return 1
963     fi
964
965 }
966
967 removable_dev ()
968 {
969         output_format="${1}"
970         want_usb="${2}"
971         ret=
972
973         for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -vE "/(loop|ram|dm-|fd)")
974         do
975                 dev_ok=
976                 if [ "$(cat ${sysblock}/removable)" = "1" ]
977                 then
978                         if [ -z "${want_usb}" ]
979                         then
980                                 dev_ok="yes"
981                         else
982                                 if readlink ${sysblock} | grep -q usb
983                                 then
984                                         dev_ok="yes"
985                                 fi
986                         fi
987                 fi
988
989                 if [ "${dev_ok}" = "yes" ]
990                 then
991                         case "${output_format}" in
992                                 sys)
993                                         ret="${ret} ${sysblock}"
994                                         ;;
995                                 *)
996                                         devname=$(sys2dev "${sysblock}")
997                                         ret="${ret} ${devname}"
998                                         ;;
999                         esac
1000                 fi
1001         done
1002
1003         echo "${ret}"
1004 }
1005
1006 removable_usb_dev ()
1007 {
1008         output_format="${1}"
1009
1010         removable_dev "${output_format}" "want_usb"
1011 }
1012
1013 non_removable_dev ()
1014 {
1015         output_format="${1}"
1016         ret=
1017
1018         for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -vE "/(loop|ram|dm-|fd)")
1019         do
1020                 if [ "$(cat ${sysblock}/removable)" = "0" ]
1021                 then
1022                         case "${output_format}" in
1023                                 sys)
1024                                         ret="${ret} ${sysblock}"
1025                                         ;;
1026                                 *)
1027                                         devname=$(sys2dev "${sysblock}")
1028                                         ret="${ret} ${devname}"
1029                                         ;;
1030                         esac
1031                 fi
1032         done
1033
1034         echo "${ret}"
1035 }
1036
1037 link_files ()
1038 {
1039         # create source's directory structure in dest, and recursively
1040         # create symlinks in dest to to all files in source. if mask
1041         # is non-empty, remove mask from all source paths when
1042         # creating links (will be necessary if we change root, which
1043         # live-boot normally does (into $rootmnt)).
1044
1045         # remove multiple /:s and ensure ending on /
1046         local src_dir="$(echo "${1}"/ | sed -e 's|/\+|/|g')"
1047         local dest_dir="$(echo "${2}"/ | sed -e 's|/\+|/|g')"
1048         local src_mask="${3}"
1049
1050         # This check can only trigger on the inital, non-recursive call since
1051         # we create the destination before recursive calls
1052         if [ ! -d "${dest_dir}" ];
1053         then
1054                 log_warning_msg "Must link_files into a directory"
1055                 return
1056         fi
1057
1058         find "${src_dir}" -mindepth 1 -maxdepth 1 | while read x; do
1059                 local src="${x}"
1060                 local dest="${dest_dir}$(basename "${x}")"
1061                 if [ -d "${src}" ];
1062                 then
1063                         if [ -z "$(ls -A "${src}")" ];
1064                         then
1065                                 continue
1066                         fi
1067                         if [ ! -d "${dest}" ];
1068                         then
1069                                 mkdir -p "${dest}"
1070                                 prev="$(dirname "${dest}")"
1071                                 chown --reference "${prev}" "${dest}"
1072                                 chmod --reference "${prev}" "${dest}"
1073                         fi
1074                         link_files "${src}" "${dest}" "${src_mask}"
1075                 else
1076                         if [ -n "${src_mask}" ]
1077                         then
1078                                 src="$(echo ${src} | sed "s|^${src_mask}||")"
1079                         fi
1080                         rm -rf "${dest}" 2> /dev/null
1081                         ln -s "${src}" "${dest}"
1082                 fi
1083         done
1084 }
1085
1086 do_union () {
1087         local unionmountpoint="${1}"    # directory where the union is mounted
1088         local unionrw="${2}"            # branch where the union changes are stored
1089         local unionro1="${3}"           # first underlying read-only branch (optional)
1090         local unionro2="${4}"           # second underlying read-only branch (optional)
1091
1092         if [ "${UNIONTYPE}" = "aufs" ]
1093         then
1094                 rw_opt="rw"
1095                 ro_opt="rr+wh"
1096                 noxino_opt="noxino"
1097         elif [ "${UNIONTYPE}" = "unionfs-fuse" ]
1098         then
1099                 rw_opt="RW"
1100                 ro_opt="RO"
1101         else
1102                 rw_opt="rw"
1103                 ro_opt="ro"
1104         fi
1105
1106         case "${UNIONTYPE}" in
1107                 unionfs-fuse)
1108                         unionmountopts="-o cow -o noinitgroups -o default_permissions -o allow_other -o use_ino -o suid"
1109                         unionmountopts="${unionmountopts} ${unionrw}=${rw_opt}"
1110                         if [ -n "${unionro1}" ]
1111                         then
1112                                 unionmountopts="${unionmountopts}:${unionro1}=${ro_opt}"
1113                         fi
1114                         if [ -n "${unionro2}" ]
1115                         then
1116                                 unionmountopts="${unionmountopts}:${unionro2}=${ro_opt}"
1117                         fi
1118                         ( sysctl -w fs.file-max=391524 ; ulimit -HSn 16384
1119                         unionfs-fuse ${unionmountopts} "${unionmountpoint}" ) && \
1120                         ( mkdir -p /run/sendsigs.omit.d
1121                         pidof unionfs-fuse >> /run/sendsigs.omit.d/unionfs-fuse || true )
1122                         ;;
1123
1124                 overlayfs)
1125                         # XXX: can unionro2 be used? (overlayfs only handles two dirs, but perhaps they can be chained?)
1126                         # XXX: and can unionro1 be optional? i.e. can overlayfs skip lowerdir?
1127                         unionmountopts="-o noatime,lowerdir=${unionro1},upperdir=${unionrw}"
1128                         mount -t ${UNIONTYPE} ${unionmountopts} ${UNIONTYPE} "${unionmountpoint}"
1129                         ;;
1130
1131                 *)
1132                         unionmountopts="-o noatime,${noxino_opt},dirs=${unionrw}=${rw_opt}"
1133                         if [ -n "${unionro1}" ]
1134                         then
1135                                 unionmountopts="${unionmountopts}:${unionro1}=${ro_opt}"
1136                         fi
1137                         if [ -n "${unionro2}" ]
1138                         then
1139                                 unionmountopts="${unionmountopts}:${unionro2}=${ro_opt}"
1140                         fi
1141                         mount -t ${UNIONTYPE} ${unionmountopts} ${UNIONTYPE} "${unionmountpoint}"
1142                         ;;
1143         esac
1144 }
1145
1146 get_custom_mounts () {
1147         # Side-effect: leaves $devices with live.persist mounted in ${rootmnt}/live/persistent
1148         # Side-effect: prints info to file $custom_mounts
1149
1150         local devices="${1}"
1151         local custom_mounts="${2}" # print result to this file
1152         local rootmnt="${3}"       # should be set empty post-live-boot
1153
1154         local bindings="/bindings.list"
1155         local links="/links.list"
1156         rm -rf ${bindings} ${links} 2> /dev/null
1157         local persistent_backing="${rootmnt}/live/persistent"
1158
1159         for device in ${devices}
1160         do
1161                 if [ ! -b "${device}" ]
1162                 then
1163                         continue
1164                 fi
1165
1166                 local device_name="$(basename ${device})"
1167                 local device_used=""
1168                 # $device may already have been mounted by
1169                 # probe_for_file_name() in find_persistent_media() ...
1170                 local backing=$(where_is_mounted ${device})
1171                 if [ -z "${backing}" ]
1172                 then
1173                         # ... otherwise we mount it now
1174                         backing="${persistent_backing}/${device_name}"
1175                         mkdir -p "${backing}"
1176                         local device_fstype="$(get_fstype ${device})"
1177                         if [ -z "${PERSISTENT_READONLY}" ]
1178                         then
1179                                 device_mount_opts="rw,noatime"
1180                         else
1181                                 device_mount_opts="ro,noatime"
1182                         fi
1183                         if ! mount -t "${device_fstype}" -o "${device_mount_opts}" "${device}" "${backing}" >/dev/null 2>&1
1184                         then
1185                                 log_warning_msg "Could not mount persistent media ${device} (${device_fstype})"
1186                         fi
1187                 fi
1188                 local include_list="${backing}/${persistence_list}"
1189                 if [ ! -r "${include_list}" ]
1190                 then
1191                         umount "${backing}" >/dev/null 2>&1
1192                         rmdir "${backing}" >/dev/null 2>&1
1193                         if /sbin/cryptsetup status ${device_name} >/dev/null 2>&1
1194                         then
1195                                 /sbin/cryptsetup luksClose "${device_name}"
1196                         fi
1197                         continue
1198                 fi
1199
1200                 [ "${DEBUG}" = "Yes" ] && cp ${include_list} ${persistent_backing}/${persistence_list}.${device_name}
1201                 while read source dest options # < ${include_list}
1202                 do
1203                         if echo ${source} | grep -qe "^[[:space:]]*\(#.*\)\?$"
1204                         then
1205                                 # skipping empty or commented lines
1206                                 continue
1207                         fi
1208
1209                         if echo ${dest} | grep -qe "^[^/]"
1210                         then
1211                                 options="${dest}"
1212                                 dest="${source}"
1213                         elif [ -z "${dest}" ]
1214                         then
1215                                 dest="${source}"
1216                         fi
1217
1218                         if echo ${dest} | grep -qe "^/\+$\|^/\+live\(/.*\)\?$"
1219                         then
1220                                 # mounting on / or /live could cause trouble
1221                                 log_warning_msg "Skipping unsafe custom mount on ${dest}"
1222                                 continue
1223                         fi
1224
1225                         for opt in $(echo ${options} | tr ',' ' ');
1226                         do
1227                                 case "${opt}" in
1228                                         linkfiles|union)
1229                                                 ;;
1230                                         *)
1231                                                 log_warning_msg "Skipping custom mount with unkown option: ${opt}"
1232                                                 continue 2
1233                                                 ;;
1234                                 esac
1235                         done
1236
1237                         # FIXME: handle case: we already have /a/b in
1238                         # $bindings added from current $device, but
1239                         # now we find /a -- /a should replace /a/b in
1240                         # $bindings.
1241
1242                         # FIXME: handle case: we have /a in $bindings
1243                         # from current $device, now we find /a/b, so
1244                         # we skip /a/b
1245
1246                         # ensure that no multiple-/ occur in paths
1247                         local full_source="$(echo ${backing}/${source}/ | sed -e 's|/\+|/|g')"
1248                         local full_dest="$(echo ${rootmnt}/${dest}/ | sed -e 's|/\+|/|g')"
1249                         device_used="yes"
1250                         if echo ${options} | grep -qe "\<linkfiles\>";
1251                         then
1252                                 echo "${full_source} ${full_dest} ${options}" >> ${links}
1253                         else
1254                                 echo "${full_source} ${full_dest} ${options}" >> ${bindings}
1255                         fi
1256                 done < ${include_list}
1257
1258                 if [ -z "${device_used}" ]
1259                 then
1260                         # this device was not used for / earlier, or
1261                         # custom mount point now, so it's useless
1262                         umount "${backing}"
1263                         rmdir "${backing}"
1264                 fi
1265         done
1266
1267         # We sort the list according to destination so we're sure that
1268         # we won't hide a previous mount. We also ignore duplicate
1269         # destinations in a more or less arbitrary way.
1270         [ -e "${bindings}" ] && sort -k2 -sbu ${bindings} >> ${custom_mounts} && rm ${bindings}
1271
1272         # After all mounts are considered we add symlinks so they
1273         # won't be hidden by some mount.
1274         [ -e "${links}" ] && sort -k2 -sbu ${links} >> ${custom_mounts} && rm ${links}
1275 }
1276
1277 do_custom_mounts () {
1278         local custom_mounts="${1}" # the ouput from get_custom_mounts()
1279         local rootmnt="${2}"       # should be set empty post-live-boot
1280
1281         while read source dest options # < ${custom_mounts}
1282         do
1283                 local opt_linkfiles=""
1284                 local opt_union=""
1285                 for opt in $(echo ${options} | tr ',' ' ');
1286                 do
1287                          case "${opt}" in
1288                                 linkfiles)
1289                                         opt_linkfiles="yes"
1290                                         ;;
1291                                 union)
1292                                         opt_union="yes"
1293                                         ;;
1294                         esac
1295                 done
1296
1297                 if mountpoint -q "${dest}";
1298                 then
1299                         log_warning_msg "Skipping custom mount ${source} on ${dest}: destination is already a mount point"
1300                         continue
1301                 fi
1302
1303                 # FIXME: we don't handle already existing
1304                 # non-directory files in the paths of both $source and
1305                 # $dest.
1306
1307                 if [ ! -d "${dest}" ]
1308                 then
1309                         # if ${dest} is in /home/$user, try fixing
1310                         # proper ownership
1311                         # FIXME: this should really be handled by
1312                         # live-config since we don't know for sure
1313                         # which uid a certain user has until then
1314                         if echo ${dest} | grep -qe "^${rootmnt}/*home/\+[^/]\+"
1315                         then
1316                                 path="/"
1317                                 for dir in $(echo ${dest} | sed -e 's|/\+| |g')
1318                                 do
1319                                         path=${path}/${dir}
1320                                         if [ ! -e ${path} ]
1321                                         then
1322                                                 mkdir -p ${path}
1323                                                 # assume that the intended user is the first, which is usually the case
1324                                                 chown 1000:1000 ${path}
1325                                         fi
1326                                 done
1327                         else
1328                                 mkdir -p ${dest}
1329                         fi
1330                 fi
1331
1332                 # if ${source} doesn't exist on our persistent media
1333                 # we bootstrap it with $dest from the live filesystem.
1334                 # this both makes sense and is critical if we're
1335                 # dealing with /etc or other system dir.
1336                 if [ ! -d "${source}" ]
1337                 then
1338                         if [ -n "${PERSISTENT_READONLY}" ] || [ -n "${opt_linkfiles}" ]
1339                         then
1340                                 continue
1341                         elif [ -n "${opt_union}" ]
1342                         then
1343                                 # union's don't need to be bootstrapped
1344                                 mkdir "${source}"
1345                         else
1346                                 # ensure that $dest is not copied *into* $source
1347                                 mkdir -p "$(dirname ${source})"
1348                                 cp -a "${dest}" "${source}"
1349                         fi
1350                 fi
1351
1352                 rofs_dest_backing=""
1353                 for d in ${rootmnt}/live/rofs/*
1354                 do
1355                         if [ -n "${rootmnt}" ]
1356                         then
1357                                 rofs_dest_backing="${d}/$(echo ${dest} | sed -e "s|${rootmnt}||")"
1358                         else
1359                                 rofs_dest_backing="${d}/${dest}"
1360
1361                         fi
1362                         if [ -d "${rofs_dest_backing}" ]
1363                         then
1364                                 break
1365                         else
1366                                 rofs_dest_backing=""
1367                         fi
1368                 done
1369
1370                 if [ -z "${PERSISTENT_READONLY}" ]
1371                 then
1372                         if [ -n "${opt_linkfiles}" ]
1373                         then
1374                                 links_source="${source}"
1375                                 links_dest="${dest}"
1376                         elif [ -n "${opt_union}" ]
1377                         then
1378                                 do_union ${dest} ${source} ${rofs_dest_backing}
1379                         else
1380                                 mount --bind "${source}" "${dest}"
1381                         fi
1382                 else
1383                         if [ -n "${opt_linkfiles}" ]
1384                         then
1385                                 links_dest="${dest}"
1386                                 dest="$(mktemp -d ${persistent_backing}/links_source-XXXXXX)"
1387                                 links_source="${dest}"
1388                         fi
1389                         if [ -n "${rootmnt}" ]
1390                         then
1391                                 cow_dir="$(echo ${dest} | sed -e "s|${rootmnt}|/cow/|")"
1392                         else
1393                                 cow_dir="/live/cow/${dest}"
1394                         fi
1395                         mkdir -p ${cow_dir}
1396                         do_union ${dest} ${cow_dir} ${source} ${rofs_dest_backing}
1397                 fi
1398
1399                 if [ -n "${opt_linkfiles}" ]
1400                 then
1401                         link_files "${links_source}" "${links_dest}" "${rootmnt}"
1402                 fi
1403
1404                 PERSISTENCE_IS_ON="1"
1405                 export PERSISTENCE_IS_ON
1406         done < ${custom_mounts}
1407 }