Removing dead lang2locale function and associated files, not used anywhere.
[live-boot-grml.git] / scripts / boot / misc-helpers.sh
1 #!/bin/sh
2
3 #set -e
4
5 really_export ()
6 {
7         STRING="${1}"
8         VALUE="$(eval echo -n \${$STRING})"
9
10         if [ -f /live.vars ] && grep -sq "export ${STRING}" /live.vars
11         then
12                 sed -i -e 's/\('${STRING}'=\).*$/\1'${VALUE}'/' /live.vars
13         else
14                 echo "export ${STRING}=\"${VALUE}\"" >> /live.vars
15         fi
16
17         eval export "${STRING}"="${VALUE}"
18 }
19
20 is_in_list_separator_helper () {
21         local sep=${1}
22         shift
23         local element=${1}
24         shift
25         local list=${*}
26         echo ${list} | grep -qe "^\(.*${sep}\)\?${element}\(${sep}.*\)\?$"
27 }
28
29 is_in_space_sep_list () {
30         local element=${1}
31         shift
32         is_in_list_separator_helper "[[:space:]]" "${element}" "${*}"
33 }
34
35 is_in_comma_sep_list () {
36         local element=${1}
37         shift
38         is_in_list_separator_helper "," "${element}" "${*}"
39 }
40
41 sys2dev ()
42 {
43         sysdev=${1#/sys}
44         echo "/dev/$($udevinfo -q name -p ${sysdev} 2>/dev/null|| echo ${sysdev##*/})"
45 }
46
47 subdevices ()
48 {
49         sysblock=${1}
50         r=""
51
52         for dev in "${sysblock}"/* "${sysblock}"
53         do
54                 if [ -e "${dev}/dev" ]
55                 then
56                         r="${r} ${dev}"
57                 fi
58         done
59
60         echo ${r}
61 }
62
63 storage_devices()
64 {
65         black_listed_devices="${1}"
66         white_listed_devices="${2}"
67
68         for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -vE "loop|ram|fd")
69         do
70                 fulldevname=$(sys2dev "${sysblock}")
71
72                 if is_in_space_sep_list ${fulldevname} ${black_listed_devices} || \
73                         [ -n "${white_listed_devices}" ] && \
74                         ! is_in_space_sep_list ${fulldevname} ${white_listed_devices}
75                 then
76                         # skip this device entirely
77                         continue
78                 fi
79
80                 for dev in $(subdevices "${sysblock}")
81                 do
82                         devname=$(sys2dev "${dev}")
83
84                         if is_in_space_sep_list ${devname} ${black_listed_devices}
85                         then
86                                 # skip this subdevice
87                                 continue
88                         else
89                                 echo "${devname}"
90                         fi
91                 done
92         done
93 }
94
95 is_supported_fs ()
96 {
97         fstype="${1}"
98
99         # Validate input first
100         if [ -z "${fstype}" ]
101         then
102                 return 1
103         fi
104
105         # Try to look if it is already supported by the kernel
106         if grep -q ${fstype} /proc/filesystems
107         then
108                 return 0
109         else
110                 # Then try to add support for it the gentle way using the initramfs capabilities
111                 modprobe ${fstype}
112                 if grep -q ${fstype} /proc/filesystems
113                 then
114                         return 0
115                 # Then try the hard way if /root is already reachable
116                 else
117                         kmodule="/root/lib/modules/`uname -r`/${fstype}/${fstype}.ko"
118                         if [ -e "${kmodule}" ]
119                         then
120                                 insmod "${kmodule}"
121                                 if grep -q ${fstype} /proc/filesystems
122                                 then
123                                         return 0
124                                 fi
125                         fi
126                 fi
127         fi
128
129         return 1
130 }
131
132 get_fstype ()
133 {
134         /sbin/blkid -s TYPE -o value $1 2>/dev/null
135 }
136
137 where_is_mounted ()
138 {
139         device=${1}
140         # return first found
141         grep -m1 "^${device} " /proc/mounts | cut -f2 -d ' '
142 }
143
144 trim_path () {
145     # remove all unnecessary /:s in the path, including last one (except
146     # if path is just "/")
147     echo ${1} | sed 's|//\+|/|g' | sed 's|^\(.*[^/]\)/$|\1|'
148 }
149
150 what_is_mounted_on ()
151 {
152         local dir="$(trim_path ${1})"
153         grep -m1 "^[^ ]\+ ${dir} " /proc/mounts | cut -d' ' -f1
154 }
155
156 chown_ref ()
157 {
158         local reference="${1}"
159         shift
160         local targets=${@}
161         local owner=$(stat -c %u:%g "${reference}")
162         chown -h ${owner} ${targets}
163 }
164
165 chmod_ref ()
166 {
167         local reference="${1}"
168         shift
169         local targets=${@}
170         local rights=$(stat -c %a "${reference}")
171         chmod ${rights} ${targets}
172 }
173
174 lastline ()
175 {
176         while read lines
177         do
178                 line=${lines}
179         done
180
181         echo "${line}"
182 }
183
184 base_path ()
185 {
186         testpath="${1}"
187         mounts="$(awk '{print $2}' /proc/mounts)"
188         testpath="$(busybox realpath ${testpath})"
189
190         while true
191         do
192                 if echo "${mounts}" | grep -qs "^${testpath}"
193                 then
194                         set -- $(echo "${mounts}" | grep "^${testpath}" | lastline)
195                         echo ${1}
196                         break
197                 else
198                         testpath=$(dirname $testpath)
199                 fi
200         done
201 }
202
203 fs_size ()
204 {
205         # Returns used/free fs kbytes + 5% more
206         # You could pass a block device as ${1} or the mount point as ${2}
207
208         dev="${1}"
209         mountp="${2}"
210         used="${3}"
211
212         if [ -z "${mountp}" ]
213         then
214                 mountp="$(where_is_mounted ${dev})"
215
216                 if [ -z "${mountp}" ]
217                 then
218                         mountp="/mnt/tmp_fs_size"
219
220                         mkdir -p "${mountp}"
221                         mount -t $(get_fstype "${dev}") -o ro "${dev}" "${mountp}" || log_warning_msg "cannot mount -t $(get_fstype ${dev}) -o ro ${dev} ${mountp}"
222
223                         doumount=1
224                 fi
225         fi
226
227         if [ "${used}" = "used" ]
228         then
229                 size=$(du -ks ${mountp} | cut -f1)
230                 size=$(expr ${size} + ${size} / 20 ) # FIXME: 5% more to be sure
231         else
232                 # free space
233                 size="$(df -k | grep -s ${mountp} | awk '{print $4}')"
234         fi
235
236         if [ -n "${doumount}" ]
237         then
238                 umount "${mountp}" || log_warning_msg "cannot umount ${mountp}"
239                 rmdir "${mountp}"
240         fi
241
242         echo "${size}"
243 }
244
245 load_keymap ()
246 {
247         # Load custom keymap
248         if [ -x /bin/loadkeys -a -r /etc/boottime.kmap.gz ]
249         then
250                 loadkeys /etc/boottime.kmap.gz
251         fi
252 }
253
254 setup_loop ()
255 {
256         local fspath=${1}
257         local module=${2}
258         local pattern=${3}
259         local offset=${4}
260         local encryption=${5}
261         local readonly=${6}
262
263         # the output of setup_loop is evaluated in other functions,
264         # modprobe leaks kernel options like "libata.dma=0"
265         # as "options libata dma=0" on stdout, causing serious
266         # problems therefor, so instead always avoid output to stdout
267         modprobe -q -b "${module}" 1>/dev/null
268
269         udevadm settle
270
271         for loopdev in ${pattern}
272         do
273                 if [ "$(cat ${loopdev}/size)" -eq 0 ]
274                 then
275                         dev=$(sys2dev "${loopdev}")
276                         options=''
277
278                         if [ -n "${readonly}" ]
279                         then
280                                 if losetup --help 2>&1 | grep -q -- "-r\b"
281                                 then
282                                         options="${options} -r"
283                                 fi
284                         fi
285
286                         if [ -n "${offset}" ] && [ 0 -lt "${offset}" ]
287                         then
288                                 options="${options} -o ${offset}"
289                         fi
290
291                         if [ -z "${encryption}" ]
292                         then
293                                 losetup ${options} "${dev}" "${fspath}"
294                         else
295                                 # Loop AES encryption
296                                 while true
297                                 do
298                                         load_keymap
299
300                                         echo -n "Enter passphrase for root filesystem: " >&6
301                                         read -s passphrase
302                                         echo "${passphrase}" > /tmp/passphrase
303                                         unset passphrase
304                                         exec 9</tmp/passphrase
305                                         /sbin/losetup ${options} -e "${encryption}" -p 9 "${dev}" "${fspath}"
306                                         error=${?}
307                                         exec 9<&-
308                                         rm -f /tmp/passphrase
309
310                                         if [ 0 -eq ${error} ]
311                                         then
312                                                 unset error
313                                                 break
314                                         fi
315
316                                         echo
317                                         echo -n "There was an error decrypting the root filesystem ... Retry? [Y/n] " >&6
318                                         read answer
319
320                                         if [ "$(echo "${answer}" | cut -b1 | tr A-Z a-z)" = "n" ]
321                                         then
322                                                 unset answer
323                                                 break
324                                         fi
325                                 done
326                         fi
327
328                         echo "${dev}"
329                         return 0
330                 fi
331         done
332
333         panic "No loop devices available"
334 }
335
336 try_mount ()
337 {
338         dev="${1}"
339         mountp="${2}"
340         opts="${3}"
341         fstype="${4}"
342
343         old_mountp="$(where_is_mounted ${dev})"
344
345         if [ -n "${old_mountp}" ]
346         then
347                 if [ "${opts}" != "ro" ]
348                 then
349                         mount -o remount,"${opts}" "${dev}" "${old_mountp}" || panic "Remounting ${dev} ${opts} on ${old_mountp} failed"
350                 fi
351
352                 mount -o bind "${old_mountp}" "${mountp}" || panic "Cannot bind-mount ${old_mountp} on ${mountp}"
353         else
354                 if [ -z "${fstype}" ]
355                 then
356                         fstype=$(get_fstype "${dev}")
357                 fi
358                 mount -t "${fstype}" -o "${opts}" "${dev}" "${mountp}" || \
359                 ( echo "SKIPPING: Cannot mount ${dev} on ${mountp}, fstype=${fstype}, options=${opts}" > boot.log && return 0 )
360         fi
361 }
362
363 mount_persistence_media ()
364 {
365         local device=${1}
366         local probe=${2}
367
368         local backing="/live/persistence/$(basename ${device})"
369
370         mkdir -p "${backing}"
371         local old_backing="$(where_is_mounted ${device})"
372         if [ -z "${old_backing}" ]
373         then
374                 local fstype="$(get_fstype ${device})"
375                 local mount_opts="rw,noatime"
376                 if [ -n "${PERSISTENCE_READONLY}" ]
377                 then
378                         mount_opts="ro,noatime"
379                 fi
380                 if mount -t "${fstype}" -o "${mount_opts}" "${device}" "${backing}" >/dev/null
381                 then
382                         echo ${backing}
383                         return 0
384                 else
385                         [ -z "${probe}" ] && log_warning_msg "Failed to mount persistence media ${device}"
386                         rmdir "${backing}"
387                         return 1
388                 fi
389         elif [ "${backing}" != "${old_backing}" ]
390         then
391                 if mount --move ${old_backing} ${backing} >/dev/null
392                 then
393                         echo ${backing}
394                         return 0
395                 else
396                         [ -z "${probe}" ] && log_warning_msg "Failed to move persistence media ${device}"
397                         rmdir "${backing}"
398                         return 1
399                 fi
400         fi
401         return 0
402 }
403
404 close_persistence_media () {
405         local device=${1}
406         local backing="$(where_is_mounted ${device})"
407
408         if [ -d "${backing}" ]
409         then
410                 umount "${backing}" >/dev/null 2>&1
411                 rmdir "${backing}" >/dev/null 2>&1
412         fi
413
414         if is_active_luks_mapping ${device}
415         then
416                 /sbin/cryptsetup luksClose ${device}
417         fi
418 }
419
420 open_luks_device ()
421 {
422         dev="${1}"
423         name="$(basename ${dev})"
424         opts="--key-file=-"
425         if [ -n "${PERSISTENCE_READONLY}" ]
426         then
427                 opts="${opts} --readonly"
428         fi
429
430         if /sbin/cryptsetup status "${name}" >/dev/null 2>&1
431         then
432                 re="^[[:space:]]*device:[[:space:]]*\([^[:space:]]*\)$"
433                 opened_dev=$(cryptsetup status ${name} 2>/dev/null | grep "${re}" | sed "s|${re}|\1|")
434                 if [ "${opened_dev}" = "${dev}" ]
435                 then
436                         luks_device="/dev/mapper/${name}"
437                         echo ${luks_device}
438                         return 0
439                 else
440                         log_warning_msg "Cannot open luks device ${dev} since ${opened_dev} already is opened with its name"
441                         return 1
442                 fi
443         fi
444
445         load_keymap
446
447         while true
448         do
449                 /lib/cryptsetup/askpass "Enter passphrase for ${dev}: " | \
450                         /sbin/cryptsetup -T 1 luksOpen ${dev} ${name} ${opts}
451
452                 if [ 0 -eq ${?} ]
453                 then
454                         luks_device="/dev/mapper/${name}"
455                         echo ${luks_device}
456                         return 0
457                 fi
458
459                 echo >&6
460                 echo -n "There was an error decrypting ${dev} ... Retry? [Y/n] " >&6
461                 read answer
462
463                 if [ "$(echo "${answer}" | cut -b1 | tr A-Z a-z)" = "n" ]
464                 then
465                         return 2
466                 fi
467         done
468 }
469
470 get_gpt_name ()
471 {
472     local dev="${1}"
473     /sbin/blkid -s PART_ENTRY_NAME -p -o value ${dev} 2>/dev/null
474 }
475
476 is_gpt_device ()
477 {
478     local dev="${1}"
479     [ "$(/sbin/blkid -s PART_ENTRY_SCHEME -p -o value ${dev} 2>/dev/null)" = "gpt" ]
480 }
481
482 probe_for_gpt_name ()
483 {
484         local overlays="${1}"
485         local snapshots="${2}"
486         local dev="${3}"
487
488         local gpt_dev="${dev}"
489         if is_active_luks_mapping ${dev}
490         then
491                 # if $dev is an opened luks device, we need to check
492                 # GPT stuff on the backing device
493                 gpt_dev=$(get_luks_backing_device "${dev}")
494         fi
495
496         if ! is_gpt_device ${gpt_dev}
497         then
498                 return
499         fi
500
501         local gpt_name=$(get_gpt_name ${gpt_dev})
502         for label in ${overlays} ${snapshots}
503         do
504                 if [ "${gpt_name}" = "${label}" ]
505                 then
506                         echo "${label}=${dev}"
507                 fi
508         done
509 }
510
511 probe_for_fs_label ()
512 {
513         local overlays="${1}"
514         local snapshots="${2}"
515         local dev="${3}"
516
517         for label in ${overlays} ${snapshots}
518         do
519                 if [ "$(/sbin/blkid -s LABEL -o value $dev 2>/dev/null)" = "${label}" ]
520                 then
521                         echo "${label}=${dev}"
522                 fi
523         done
524 }
525
526 probe_for_file_name ()
527 {
528         local overlays="${1}"
529         local snapshots="${2}"
530         local dev="${3}"
531
532         local ret=""
533         local backing="$(mount_persistence_media ${dev} probe)"
534         if [ -z "${backing}" ]
535         then
536             return
537         fi
538
539         for label in ${overlays}
540         do
541                 path=${backing}/${PERSISTENCE_PATH}${label}
542                 if [ -f "${path}" ]
543                 then
544                         local loopdev=$(setup_loop "${path}" "loop" "/sys/block/loop*")
545                         ret="${ret} ${label}=${loopdev}"
546                 fi
547         done
548         for label in ${snapshots}
549         do
550                 for ext in squashfs cpio.gz ext2 ext3 ext4 jffs2
551                 do
552                         path="${PERSISTENCE_PATH}${label}.${ext}"
553                         if [ -f "${backing}/${path}" ]
554                         then
555                                 ret="${ret} ${label}=${dev}:${backing}:${path}"
556                         fi
557                 done
558         done
559
560         if [ -n "${ret}" ]
561         then
562                 echo ${ret}
563         else
564                 umount ${backing} > /dev/null 2>&1 || true
565         fi
566 }
567
568 find_persistence_media ()
569 {
570         # Scans devices for overlays and snapshots, and returns a whitespace
571         # separated list of how to use them. Only overlays with a partition
572         # label or file name in ${overlays} are returned, and ditto for
573         # snapshots with labels in ${snapshots}.
574         #
575         # When scanning a LUKS device, the user will be asked to enter the
576         # passphrase; on failure to enter it, or if no persistence partitions
577         # or files were found, the LUKS device is closed.
578         #
579         # For a snapshot file the return value is ${label}=${snapdata}", where
580         # ${snapdata} is the parameter used for try_snap().
581         #
582         # For all other cases (overlay/snapshot partition and overlay file) the
583         # return value is "${label}=${device}", where ${device} a device that
584         # can mount the content. In the case of an overlay file, the device
585         # containing the file will remain mounted as a side-effect.
586         #
587         # No devices in ${black_listed_devices} will be scanned, and if
588         # ${white_list_devices} is non-empty, only devices in it will be
589         # scanned.
590
591         local overlays="${1}"
592         local snapshots="${2}"
593         local white_listed_devices="${3}"
594         local ret=""
595
596         local black_listed_devices="$(what_is_mounted_on /live/image)"
597
598         for dev in $(storage_devices "${black_listed_devices}" "${white_listed_devices}")
599         do
600                 local result=""
601
602                 local luks_device=""
603                 # Check if it's a luks device; we'll have to open the device
604                 # in order to probe any filesystem it contains, like we do
605                 # below. activate_custom_mounts() also depends on that any luks
606                 # device already has been opened.
607                 if is_in_comma_sep_list luks ${PERSISTENCE_ENCRYPTION} && \
608                    is_luks_partition ${dev}
609                 then
610                         if luks_device=$(open_luks_device "${dev}")
611                         then
612                                 dev="${luks_device}"
613                         else
614                                 # skip $dev since we failed/chose not to open it
615                                 continue
616                         fi
617                 elif ! is_in_comma_sep_list none ${PERSISTENCE_ENCRYPTION}
618                 then
619                         # skip $dev since we don't allow unencrypted storage
620                         continue
621                 fi
622
623                 # Probe for matching GPT partition names or filesystem labels
624                 if is_in_comma_sep_list filesystem ${PERSISTENCE_STORAGE}
625                 then
626                         result=$(probe_for_gpt_name "${overlays}" "${snapshots}" ${dev})
627                         if [ -n "${result}" ]
628                         then
629                                 ret="${ret} ${result}"
630                                 continue
631                         fi
632
633                         result=$(probe_for_fs_label "${overlays}" "${snapshots}" ${dev})
634                         if [ -n "${result}" ]
635                         then
636                                 ret="${ret} ${result}"
637                                 continue
638                         fi
639                 fi
640
641                 # Probe for files with matching name on mounted partition
642                 if is_in_comma_sep_list file ${PERSISTENCE_STORAGE}
643                 then
644                         result=$(probe_for_file_name "${overlays}" "${snapshots}" ${dev})
645                         if [ -n "${result}" ]
646                         then
647                                 ret="${ret} ${result}"
648                                 continue
649                         fi
650                 fi
651
652                 # Close luks device if it isn't used
653                 if [ -z "${result}" ] && [ -n "${luks_device}" ] && \
654                    is_active_luks_mapping "${luks_device}"
655                 then
656                         /sbin/cryptsetup luksClose "${luks_device}"
657                 fi
658         done
659
660         if [ -n "${ret}" ]
661         then
662                 echo ${ret}
663         fi
664 }
665
666 get_mac ()
667 {
668         mac=""
669
670         for adaptor in /sys/class/net/*
671         do
672                 status="$(cat ${adaptor}/iflink)"
673
674                 if [ "${status}" -eq 2 ]
675                 then
676                         mac="$(cat ${adaptor}/address)"
677                         mac="$(echo ${mac} | sed 's/:/-/g' | tr '[a-z]' '[A-Z]')"
678                 fi
679         done
680
681         echo ${mac}
682 }
683
684 is_luks_partition ()
685 {
686         device="${1}"
687         /sbin/cryptsetup isLuks "${device}" 1>/dev/null 2>&1
688 }
689
690 is_active_luks_mapping ()
691 {
692         device="${1}"
693         /sbin/cryptsetup status "${device}" 1>/dev/null 2>&1
694 }
695
696 get_luks_backing_device () {
697         device=${1}
698         cryptsetup status ${device} 2> /dev/null | \
699                 awk '{if ($1 == "device:") print $2}'
700 }
701
702 removable_dev ()
703 {
704         output_format="${1}"
705         want_usb="${2}"
706         ret=
707
708         for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -vE "/(loop|ram|dm-|fd)")
709         do
710                 dev_ok=
711                 if [ "$(cat ${sysblock}/removable)" = "1" ]
712                 then
713                         if [ -z "${want_usb}" ]
714                         then
715                                 dev_ok="yes"
716                         else
717                                 if readlink ${sysblock} | grep -q usb
718                                 then
719                                         dev_ok="yes"
720                                 fi
721                         fi
722                 fi
723
724                 if [ "${dev_ok}" = "yes" ]
725                 then
726                         case "${output_format}" in
727                                 sys)
728                                         ret="${ret} ${sysblock}"
729                                         ;;
730                                 *)
731                                         devname=$(sys2dev "${sysblock}")
732                                         ret="${ret} ${devname}"
733                                         ;;
734                         esac
735                 fi
736         done
737
738         echo "${ret}"
739 }
740
741 removable_usb_dev ()
742 {
743         output_format="${1}"
744
745         removable_dev "${output_format}" "want_usb"
746 }
747
748 non_removable_dev ()
749 {
750         output_format="${1}"
751         ret=
752
753         for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -vE "/(loop|ram|dm-|fd)")
754         do
755                 if [ "$(cat ${sysblock}/removable)" = "0" ]
756                 then
757                         case "${output_format}" in
758                                 sys)
759                                         ret="${ret} ${sysblock}"
760                                         ;;
761                                 *)
762                                         devname=$(sys2dev "${sysblock}")
763                                         ret="${ret} ${devname}"
764                                         ;;
765                         esac
766                 fi
767         done
768
769         echo "${ret}"
770 }
771
772 link_files ()
773 {
774         # create source's directory structure in dest, and recursively
775         # create symlinks in dest to to all files in source. if mask
776         # is non-empty, remove mask from all source paths when
777         # creating links (will be necessary if we change root, which
778         # live-boot normally does (into $rootmnt)).
779
780         # remove multiple /:s and ensure ending on /
781         local src_dir="$(trim_path ${1})/"
782         local dest_dir="$(trim_path ${2})/"
783         local src_mask="${3}"
784
785         # This check can only trigger on the inital, non-recursive call since
786         # we create the destination before recursive calls
787         if [ ! -d "${dest_dir}" ]
788         then
789                 log_warning_msg "Must link_files into a directory"
790                 return
791         fi
792
793         find "${src_dir}" -mindepth 1 -maxdepth 1 | while read src; do
794                 local dest="${dest_dir}$(basename "${src}")"
795                 if [ -d "${src}" ]
796                 then
797                         if [ -z "$(ls -A "${src}")" ]
798                         then
799                                 continue
800                         fi
801                         if [ ! -d "${dest}" ]
802                         then
803                                 mkdir -p "${dest}"
804                                 chown_ref "${src}" "${dest}"
805                                 chmod_ref "${src}" "${dest}"
806                         fi
807                         link_files "${src}" "${dest}" "${src_mask}"
808                 else
809                         local final_src=${src}
810                         if [ -n "${src_mask}" ]
811                         then
812                                 final_src="$(echo ${final_src} | sed "s|^${src_mask}||")"
813                         fi
814                         rm -rf "${dest}" 2> /dev/null
815                         ln -s "${final_src}" "${dest}"
816                         chown_ref "${src}" "${dest}"
817                 fi
818         done
819 }
820
821 do_union ()
822 {
823         local unionmountpoint="${1}"    # directory where the union is mounted
824         local unionrw="${2}"            # branch where the union changes are stored
825         local unionro1="${3}"           # first underlying read-only branch (optional)
826         local unionro2="${4}"           # second underlying read-only branch (optional)
827
828         if [ "${UNIONTYPE}" = "aufs" ]
829         then
830                 rw_opt="rw"
831                 ro_opt="rr+wh"
832                 noxino_opt="noxino"
833         elif [ "${UNIONTYPE}" = "unionfs-fuse" ]
834         then
835                 rw_opt="RW"
836                 ro_opt="RO"
837         else
838                 rw_opt="rw"
839                 ro_opt="ro"
840         fi
841
842         case "${UNIONTYPE}" in
843                 unionfs-fuse)
844                         unionmountopts="-o cow -o noinitgroups -o default_permissions -o allow_other -o use_ino -o suid"
845                         unionmountopts="${unionmountopts} ${unionrw}=${rw_opt}"
846                         if [ -n "${unionro1}" ]
847                         then
848                                 unionmountopts="${unionmountopts}:${unionro1}=${ro_opt}"
849                         fi
850                         if [ -n "${unionro2}" ]
851                         then
852                                 unionmountopts="${unionmountopts}:${unionro2}=${ro_opt}"
853                         fi
854                         ( sysctl -w fs.file-max=391524 ; ulimit -HSn 16384
855                         unionfs-fuse ${unionmountopts} "${unionmountpoint}" ) && \
856                         ( mkdir -p /run/sendsigs.omit.d
857                         pidof unionfs-fuse >> /run/sendsigs.omit.d/unionfs-fuse || true )
858                         ;;
859
860                 overlayfs)
861                         # XXX: can unionro2 be used? (overlayfs only handles two dirs, but perhaps they can be chained?)
862                         # XXX: and can unionro1 be optional? i.e. can overlayfs skip lowerdir?
863                         unionmountopts="-o noatime,lowerdir=${unionro1},upperdir=${unionrw}"
864                         mount -t ${UNIONTYPE} ${unionmountopts} ${UNIONTYPE} "${unionmountpoint}"
865                         ;;
866
867                 *)
868                         unionmountopts="-o noatime,${noxino_opt},dirs=${unionrw}=${rw_opt}"
869                         if [ -n "${unionro1}" ]
870                         then
871                                 unionmountopts="${unionmountopts}:${unionro1}=${ro_opt}"
872                         fi
873                         if [ -n "${unionro2}" ]
874                         then
875                                 unionmountopts="${unionmountopts}:${unionro2}=${ro_opt}"
876                         fi
877                         mount -t ${UNIONTYPE} ${unionmountopts} ${UNIONTYPE} "${unionmountpoint}"
878                         ;;
879         esac
880 }
881
882 get_custom_mounts ()
883 {
884         # Side-effect: leaves $devices with live-persistence.conf mounted in /live/persistence
885         # Side-effect: prints info to file $custom_mounts
886
887         local custom_mounts=${1}
888         shift
889         local devices=${@}
890
891         local bindings="/tmp/bindings.list"
892         local links="/tmp/links.list"
893         rm -rf ${bindings} ${links} 2> /dev/null
894
895         for device in ${devices}
896         do
897                 if [ ! -b "${device}" ]
898                 then
899                         continue
900                 fi
901
902                 local device_name="$(basename ${device})"
903                 local backing=$(mount_persistence_media ${device})
904                 if [ -z "${backing}" ]
905                 then
906                         continue
907                 fi
908
909                 local include_list="${backing}/${persistence_list}"
910                 if [ ! -r "${include_list}" ]
911                 then
912                         continue
913                 fi
914
915                 if [ -n "${DEBUG}" ] && [ -e "${include_list}" ]
916                 then
917                         cp ${include_list} /live/persistence/${persistence_list}.${device_name}
918                 fi
919
920                 while read dir options # < ${include_list}
921                 do
922                         if echo ${dir} | grep -qe "^[[:space:]]*\(#.*\)\?$"
923                         then
924                                 # skipping empty or commented lines
925                                 continue
926                         fi
927
928                         if trim_path ${dir} | grep -q -e "^[^/]" -e "^/live\(/.*\)\?$" -e "^/\(.*/\)\?\.\.\?\(/.*\)\?$"
929                         then
930                                 log_warning_msg "Skipping unsafe custom mount ${dir}: must be an absolute path containing neither the \".\" nor \"..\" special dirs, and cannot be \"/live\" or any sub-directory therein."
931                                 continue
932                         fi
933
934                         local opt_source=""
935                         local opt_link=""
936                         for opt in $(echo ${options} | tr ',' ' ');
937                         do
938                                 case "${opt}" in
939                                         source=*)
940                                                 opt_source=${opt#source=}
941                                                 ;;
942                                         link)
943                                                 opt_link="yes"
944                                                 ;;
945                                         union|bind)
946                                                 ;;
947                                         *)
948                                                 log_warning_msg "Skipping custom mount with unkown option: ${opt}"
949                                                 continue 2
950                                                 ;;
951                                 esac
952                         done
953
954                         local source="${dir}"
955                         if [ -n "${opt_source}" ]
956                         then
957                                 if echo ${opt_source} | grep -q -e "^/" -e "^\(.*/\)\?\.\.\?\(/.*\)\?$" && [ "${source}" != "." ]
958                                 then
959                                         log_warning_msg "Skipping unsafe custom mount with option source=${opt_source}: must be either \".\" (the media root) or a relative path w.r.t. the media root that contains neither comas, nor the special \".\" and \"..\" path components"
960                                         continue
961                                 else
962                                         source="${opt_source}"
963                                 fi
964                         fi
965
966                         local full_source="$(trim_path ${backing}/${source})"
967                         local full_dest="$(trim_path ${rootmnt}/${dir})"
968                         if [ -n "${opt_link}" ]
969                         then
970                                 echo "${device} ${full_source} ${full_dest} ${options}" >> ${links}
971                         else
972                                 echo "${device} ${full_source} ${full_dest} ${options}" >> ${bindings}
973                         fi
974                 done < ${include_list}
975         done
976
977         # We sort the list according to destination so we're sure that
978         # we won't hide a previous mount. We also ignore duplicate
979         # destinations in a more or less arbitrary way.
980         [ -e "${bindings}" ] && sort -k3 -sbu ${bindings} >> ${custom_mounts} && rm ${bindings}
981
982         # After all mounts are considered we add symlinks so they
983         # won't be hidden by some mount.
984         [ -e "${links}" ] && cat ${links} >> ${custom_mounts} && rm ${links}
985
986         # We need to make sure that no two custom mounts have the same sources
987         # or are nested; if that is the case, too much weird stuff can happen.
988         local prev_source="impossible source" # first iteration must not match
989         local prev_dest=""
990         # This sort will ensure that a source /a comes right before a source
991         # /a/b so we only need to look at the previous source
992         sort -k2 -b ${custom_mounts} |
993         while read device source dest options
994         do
995                 if echo ${source} | grep -qe "^${prev_source}\(/.*\)\?$"
996                 then
997                         panic "Two persistence mounts have the same or nested sources: ${source} on ${dest}, and ${prev_source} on ${prev_dest}"
998                 fi
999                 prev_source=${source}
1000                 prev_dest=${dest}
1001         done
1002 }
1003
1004 activate_custom_mounts ()
1005 {
1006         local custom_mounts="${1}" # the ouput from get_custom_mounts()
1007         local used_devices=""
1008
1009         while read device source dest options # < ${custom_mounts}
1010         do
1011                 local opt_bind="yes"
1012                 local opt_link=""
1013                 local opt_union=""
1014                 for opt in $(echo ${options} | tr ',' ' ');
1015                 do
1016                         case "${opt}" in
1017                                 bind)
1018                                         opt_bind="yes"
1019                                         unset opt_link opt_union
1020                                         ;;
1021                                 link)
1022                                         opt_link="yes"
1023                                         unset opt_bind opt_union
1024                                         ;;
1025                                 union)
1026                                         opt_union="yes"
1027                                         unset opt_bind opt_link
1028                                         ;;
1029                         esac
1030                 done
1031
1032                 if [ -n "$(what_is_mounted_on "${dest}")" ]
1033                 then
1034                         if [ "${dest}" = "${rootmnt}" ]
1035                         then
1036                                 umount "${dest}"
1037                         else
1038                                 log_warning_msg "Skipping custom mount ${dest}: $(what_is_mounted_on "${dest}") is already mounted there"
1039                                 continue
1040                         fi
1041                 fi
1042
1043                 if [ ! -d "${dest}" ]
1044                 then
1045                         # create the destination and delete existing files in
1046                         # its path that are in the way
1047                         path="/"
1048                         for dir in $(echo ${dest} | sed -e 's|/\+| |g')
1049                         do
1050                                 path=$(trim_path ${path}/${dir})
1051                                 if [ -f ${path} ]
1052                                 then
1053                                         rm -f ${path}
1054                                 fi
1055                                 if [ ! -e ${path} ]
1056                                 then
1057                                         mkdir -p ${path}
1058                                         if echo ${path} | grep -qe "^${rootmnt}/*home/[^/]\+"
1059                                         then
1060                                                 # if ${dest} is in /home try fixing proper ownership by assuming that the intended user is the first, which is usually the case
1061                                                 # FIXME: this should really be handled by live-config since we don't know for sure which uid a certain user has until then
1062                                                 chown 1000:1000 ${path}
1063                                         fi
1064                                 fi
1065                         done
1066                 fi
1067
1068                 # if ${source} doesn't exist on our persistence media
1069                 # we bootstrap it with $dest from the live filesystem.
1070                 # this both makes sense and is critical if we're
1071                 # dealing with /etc or other system dir.
1072                 if [ ! -d "${source}" ]
1073                 then
1074                         if [ -n "${PERSISTENCE_READONLY}" ]
1075                         then
1076                                 continue
1077                         elif [ -n "${opt_union}" ] || [ -n "${opt_link}" ]
1078                         then
1079                                 # unions and don't need to be bootstrapped
1080                                 # link dirs can't be bootstrapped in a sensible way
1081                                 mkdir -p "${source}"
1082                                 chown_ref "${dest}" "${source}"
1083                                 chmod_ref "${dest}" "${source}"
1084                         elif [ -n "${opt_bind}" ]
1085                         then
1086                                 # ensure that $dest is not copied *into* $source
1087                                 mkdir -p "$(dirname ${source})"
1088                                 cp -a "${dest}" "${source}"
1089                         fi
1090                 fi
1091
1092                 # XXX: If CONFIG_AUFS_ROBR is added to the Debian kernel we can
1093                 # ignore the loop below and set rofs_dest_backing=$dest
1094                 local rofs_dest_backing=""
1095                 if [ -n "${opt_link}"]
1096                 then
1097                         for d in /live/rofs/*
1098                         do
1099                                 if [ -n "${rootmnt}" ]
1100                                 then
1101                                         rofs_dest_backing="${d}/$(echo ${dest} | sed -e "s|${rootmnt}||")"
1102                                 else
1103                                         rofs_dest_backing="${d}/${dest}"
1104                                 fi
1105                                 if [ -d "${rofs_dest_backing}" ]
1106                                 then
1107                                         break
1108                                 else
1109                                         rofs_dest_backing=""
1110                                 fi
1111                         done
1112                 fi
1113
1114                 if [ -n "${opt_link}" ] && [ -z "${PERSISTENCE_READONLY}" ]
1115                 then
1116                         link_files ${source} ${dest} ${rootmnt}
1117                 elif [ -n "${opt_link}" ] && [ -n "${PERSISTENCE_READONLY}" ]
1118                 then
1119                         mkdir -p /live/persistence
1120                         local links_source=$(mktemp -d /live/persistence/links-source-XXXXXX)
1121                         chown_ref ${source} ${links_source}
1122                         chmod_ref ${source} ${links_source}
1123                         # We put the cow dir in the below strange place to
1124                         # make it absolutely certain that the link source
1125                         # has its own directory and isn't nested with some
1126                         # other custom mount (if so that mount's files would
1127                         # be linked, causing breakage.
1128                         local cow_dir="/live/overlay/live/persistence/$(basename ${links_source})"
1129                         mkdir -p ${cow_dir}
1130                         chown_ref "${source}" "${cow_dir}"
1131                         chmod_ref "${source}" "${cow_dir}"
1132                         do_union ${links_source} ${cow_dir} ${source} ${rofs_dest_backing}
1133                         link_files ${links_source} ${dest} ${rootmnt}
1134                 elif [ -n "${opt_union}" ] && [ -z "${PERSISTENCE_READONLY}" ]
1135                 then
1136                         do_union ${dest} ${source} ${rofs_dest_backing}
1137                 elif [ -n "${opt_bind}" ] && [ -z "${PERSISTENCE_READONLY}" ]
1138                 then
1139                         mount --bind "${source}" "${dest}"
1140                 elif [ -n "${opt_bind}" -o -n "${opt_union}" ] && [ -n "${PERSISTENCE_READONLY}" ]
1141                 then
1142                         # bind-mount and union mount are handled the same
1143                         # in read-only mode, but note that rofs_dest_backing
1144                         # is non-empty (and necessary) only for unions
1145                         if [ -n "${rootmnt}" ]
1146                         then
1147                                 local cow_dir="$(echo ${dest} | sed -e "s|^${rootmnt}|/live/overlay/|")"
1148                         else
1149                                 # This is happens if persistence is activated
1150                                 # post boot
1151                                 local cow_dir="/live/overlay/${dest}"
1152                         fi
1153                         if [ -e "${cow_dir}" ] && [ -z "${opt_link}" ]
1154                         then
1155                                 # If an earlier custom mount has files here
1156                                 # it will "block" the current mount's files
1157                                 # which is undesirable
1158                                 rm -rf "${cow_dir}"
1159                         fi
1160                         mkdir -p ${cow_dir}
1161                         chown_ref "${source}" "${cow_dir}"
1162                         chmod_ref "${source}" "${cow_dir}"
1163                         do_union ${dest} ${cow_dir} ${source} ${rofs_dest_backing}
1164                 fi
1165
1166                 PERSISTENCE_IS_ON="1"
1167                 export PERSISTENCE_IS_ON
1168
1169                 if echo ${used_devices} | grep -qve "^\(.* \)\?${device}\( .*\)\?$"
1170                 then
1171                         used_devices="${used_devices} ${device}"
1172                 fi
1173         done < ${custom_mounts}
1174
1175         echo ${used_devices}
1176 }
1177
1178 fix_backwards_compatibility ()
1179 {
1180         local device=${1}
1181         local dir=${2}
1182         local opt=${3}
1183
1184         if [ -n "${PERSISTENCE_READONLY}" ]
1185         then
1186                 return
1187         fi
1188
1189         local backing="$(mount_persistence_media ${device})"
1190         if [ -z "${backing}" ]
1191         then
1192                 return
1193         fi
1194
1195         local include_list="${backing}/${persistence_list}"
1196         if [ ! -r "${include_list}" ]
1197         then
1198                 echo "# persistence backwards compatibility:
1199 ${dir} ${opt},source=." > "${include_list}"
1200         fi
1201 }
1202
1203 is_mountpoint ()
1204 {
1205         directory="$1"
1206
1207         [ $(stat -fc%d:%D "${directory}") != $(stat -fc%d:%D "${directory}/..") ]
1208 }