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