Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| # -*- shell-script -*- vim:ft=sh: | |
| #--------------------------------------------------------------------- | |
| # Description: Mount appropriate Ubuntu Core root filesystem read-only | |
| # and writable partition writable. | |
| # Entry point: mountroot(). | |
| #--------------------------------------------------------------------- | |
| . /scripts/ubuntu-core-functions | |
| sync_dirs() | |
| { | |
| base="$1" | |
| source="$2" | |
| target="$3" | |
| OLD_PWD="$PWD" | |
| cd "$base" | |
| for file in "$source"/* | |
| do | |
| # Skip empty directories | |
| [ ! -e "$base/$file" -a ! -L "$base/$file" ] && continue | |
| # If the target already exists as a file or link, there's nothing we can do | |
| [ -e "$target/$file" -o -L "$target/$file" ] && [ ! -d "$target/$file" ] && continue | |
| # If the target doesn't exist, just copy it over | |
| if [ ! -e "$target/$file" -a ! -L "$target/$file" ]; then | |
| cp -Ra "$base/$file" "$target/$file" | |
| continue | |
| fi | |
| # That leaves us with directories and a recursive call | |
| [ -d "$file" ] && sync_dirs "$base" "$file" "$target" | |
| done | |
| cd "$OLD_PWD" | |
| } | |
| # Process the list of bind-mounts (but don't mount them - systemd will handle that) | |
| # File format is documented in writable-paths(5). | |
| handle_writable_paths() | |
| { | |
| writable_paths="$1" | |
| fstab="$2" | |
| [ -n "$writable_paths" ] || panic "need writeable paths" | |
| [ -e "$writable_paths" ] || panic "writeable paths does not exist" | |
| [ -n "fstab" ] || panic "need fstab" | |
| cat "$writable_paths" | while read line; do | |
| # tokenise | |
| set -- $line | |
| # skip invalid/commented entries | |
| ([ -z "$1" ] || \ | |
| [ -z "$2" ] || \ | |
| [ -z "$3" ] || \ | |
| [ -z "$4" ] || \ | |
| [ -z "$5" ]) && continue | |
| # ignore anything that isn't an absolute path (including comments) | |
| case "$1" in | |
| /*) ;; | |
| *) continue ;; | |
| esac | |
| # skip invalid mount points | |
| dstpath="${rootmnt}$1" | |
| [ ! -e "$dstpath" ] && continue | |
| if [ "$3" = "temporary" ]; then | |
| # Temporary entries are simple, just mount a tmpfs | |
| echo "tmpfs $1 tmpfs $5 0 0" >> "$fstab" | |
| elif [ "$3" = "persistent" ] || \ | |
| [ "$3" = "synced" ]; then | |
| # Figure out the source path | |
| if [ "$2" = "auto" ]; then | |
| srcpath="${rootmnt}/writable/system-data${1}" | |
| path="/writable/system-data${1}" | |
| else | |
| srcpath="${rootmnt}/writable/$2" | |
| path="/writable/$2" | |
| fi | |
| if [ ! -e "$srcpath" ]; then | |
| # Process new persistent or synced paths | |
| dstown=$(stat -c "%u:%g" "$dstpath") | |
| dstmode=$(stat -c "%a" "$dstpath") | |
| mkdir -p ${srcpath%/*} | |
| if [ ! -d "$dstpath" ]; then | |
| # Deal with redirected files | |
| if [ "$4" = "transition" ]; then | |
| cp -a "$dstpath" "$srcpath" | |
| else | |
| touch "$srcpath" | |
| chown "$dstown" "$srcpath" | |
| chmod "$dstmode" "$srcpath" | |
| fi | |
| else | |
| # Deal with redirected directories | |
| if [ "$4" = "transition" ] || [ "$3" = "synced" ]; then | |
| cp -aR "$dstpath" "$srcpath" | |
| else | |
| mkdir "$srcpath" | |
| chown "$dstown" "$srcpath" | |
| chmod "$dstmode" "$srcpath" | |
| fi | |
| fi | |
| elif [ "$3" = "synced" ]; then | |
| # Process existing synced paths | |
| sync_dirs "$dstpath" . "$srcpath" | |
| fi | |
| # mount all /etc dirs right now, not later when fstab is | |
| # processed, as it will cause races. | |
| case $1 in | |
| /etc*) | |
| [ -d "${rootmnt}/writable/system-data/$1" ] || mkdir -p "${rootmnt}/writable/system-data/$1" | |
| mount -o bind "${rootmnt}/writable/system-data/$1" "${rootmnt}/$1" | |
| ;; | |
| *) | |
| # Write the fstab entry | |
| if [ "$5" = "none" ]; then | |
| echo "$path $1 none bind 0 0" >> "$fstab" | |
| else | |
| echo "$path $1 none bind,$5 0 0" >> "$fstab" | |
| fi | |
| ;; | |
| esac | |
| else | |
| continue | |
| fi | |
| done | |
| } | |
| fsck_writable() | |
| { | |
| local writable_label="$1" | |
| local writable_mnt="$2" | |
| path=$(get_partition_from_label "$writable_label") | |
| [ -n "$path" ] || panic "cannot find '$writable_label' partition" | |
| # Mount the writable partition to a temporary mount point | |
| # (to allow it to be move-mounted on top of the read-only root). | |
| logfile="/run/initramfs/fsck-${writable_label}" | |
| echo "initrd: checking filesystem for ${writable_label} partition" >/dev/kmsg || true | |
| echo "$(date '+%s'): start" >> "$logfile" || true | |
| # XXX: The following commands must not fail (to ensure the system boots!) | |
| # Mount and umount first to let the kernel handle | |
| # the journal and orphaned inodes (much faster than e2fsck). | |
| mount -o errors=remount-ro "$path" "$writable_mnt" || true | |
| umount "$writable_mnt" || true | |
| # Automatically fix errors | |
| /sbin/e2fsck -va "$path" >> "$logfile" 2>&1 || true | |
| echo "$(date '+%s'): end" >> "$logfile" || true | |
| } | |
| # Mount core and kernel snaps | |
| mount_snaps() | |
| { | |
| # mount OS snap | |
| mount -o ro "${writable_mnt}/system-data/var/lib/snapd/snaps/${snap_core}" "$rootmnt" | |
| # now add a kernel bind mounts to it | |
| local kernel_mnt="/tmpmnt_kernel" | |
| mkdir -p "$kernel_mnt" | |
| mount -o ro "${writable_mnt}/system-data/var/lib/snapd/snaps/${snap_kernel}" "$kernel_mnt" | |
| for d in modules firmware; do | |
| if [ -d "${kernel_mnt}/$d" ]; then | |
| mount -o bind "${kernel_mnt}/$d" "$rootmnt/lib/$d" | |
| fi | |
| done | |
| # now we can umount the kernel and the bind mounts are still intact | |
| # (magic!) | |
| umount "$kernel_mnt" | |
| } | |
| #--------------------------------------------------------------------- | |
| # XXX: Entry point - called by the initramfs "/init" script. | |
| #--------------------------------------------------------------------- | |
| mountroot() | |
| { | |
| pre_mountroot | |
| [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-premount" | |
| run_scripts /scripts/local-premount | |
| [ "$quiet" != "y" ] && log_end_msg | |
| # find what snappy-os version to use | |
| for x in $(cat /proc/cmdline); do | |
| case "${x}" in | |
| # new kernel/os snap vars | |
| snap_core=*) | |
| snap_core="${x#*=}" | |
| ;; | |
| snap_kernel=*) | |
| snap_kernel="${x#*=}" | |
| ;; | |
| esac | |
| done | |
| # basic validation | |
| if [ -z "$snap_core" ]; then | |
| panic "the requried kernel commandline snap_core is not set" | |
| fi | |
| if [ -z "$snap_kernel" ]; then | |
| panic "the requried kernel commandline snap_kernel is not set" | |
| fi | |
| # always ensure writable is in a good state | |
| writable_label="writable" | |
| writable_mnt="/tmpmnt_${writable_label}" | |
| mkdir -p "$writable_mnt" | |
| fsck_writable "$writable_label" "$writable_mnt" | |
| # mount the root fs | |
| do_root_mounting | |
| # mount core and kernel snaps | |
| mount_snaps | |
| # mount /run | |
| echo "initrd: mounting /run" >/dev/kmsg || true | |
| mount -o rw,nosuid,noexec,relatime,mode=755 -t tmpfs tmpfs "${rootmnt}/run" | |
| # move /writable to its final destination | |
| mount --move "$writable_mnt" "${rootmnt}/writable" | |
| # Prepare the fstab | |
| fstab="${rootmnt}/etc/fstab" | |
| writable_paths="${rootmnt}/etc/system-image/writable-paths" | |
| # Add writable overlays | |
| if [ -e "$writable_paths" ]; then | |
| touch "${rootmnt}/run/image.fstab" | |
| mount -o bind "${rootmnt}/run/image.fstab" "$fstab" || panic "Cannot bind mount fstab" | |
| echo "# Auto-generated by $0" >> "$fstab" | |
| echo "# DO NOT EDIT THIS FILE BY HAND - YOUR CHANGES WILL BE OVERWRITTEN" >> "$fstab" | |
| echo "# (See writable-paths(5) for details)" >> "$fstab" | |
| echo "/dev/root / rootfs defaults,ro 0 0" >> "$fstab" | |
| # FIXME: ideally we would mount /writable RO here and | |
| # let systemd do a "remount,rw" for us. unfortunately | |
| # this is not supported by systemd so we need to do | |
| # the RW mount and fsck dance etc here :/ | |
| echo "LABEL=writable /writable auto defaults 0 0" >> "$fstab" | |
| handle_writable_paths "$writable_paths" "$fstab" | |
| fi | |
| # IMPORTANT: ensure we synced everything back to disk | |
| sync | |
| # Request bootloader partition be mounted | |
| boot_partition=$(findfs LABEL="system-boot" 2>/dev/null || :) | |
| if [ -n "$boot_partition" ]; then | |
| # determine bootloader type, we need to inspect the boot | |
| # partition for this | |
| grubdir="/boot/grub" | |
| ubootdir="/boot/uboot" | |
| tmpboot_mnt="/tmpmnt_system-boot" | |
| mkdir -p $tmpboot_mnt | |
| mount -o ro "$boot_partition" "$tmpboot_mnt" | |
| # Since the boot partition is not required to actually boot | |
| # the image, request the init system handle it (fsck+mount). | |
| if [ -f "${tmpboot_mnt}/EFI/ubuntu/grub/grub.cfg" ]; then | |
| # OLD style (ubuntu-device-flash) base dir | |
| efidir="/boot/efi" | |
| efibinddir="${efidir}/EFI/ubuntu/grub" | |
| if [ -d "${rootmnt}/${efidir}" ]; then | |
| echo "$boot_partition $efidir auto defaults 0 2" >> "$fstab" | |
| echo "$efibinddir $grubdir none bind 0 0" >> "$fstab" | |
| fi | |
| elif [ -f "${tmpboot_mnt}/EFI/ubuntu/grub.cfg" ]; then | |
| # NEW style (u-i base dir) | |
| efidir="/boot/efi" | |
| efibinddir="${efidir}/EFI/ubuntu/" | |
| if [ -d "${rootmnt}/${efidir}" ]; then | |
| echo "$boot_partition $efidir auto defaults 0 2" >> "$fstab" | |
| echo "$efibinddir $grubdir none bind 0 0" >> "$fstab" | |
| fi | |
| else | |
| echo "$boot_partition $ubootdir auto defaults 0 2" >> "$fstab" | |
| fi | |
| # let systemd do the actual mounting | |
| umount "$tmpboot_mnt" | |
| elif abootimg -i "$(findfs PARTLABEL=recovery)" >/dev/null 2>&1; then | |
| echo "android style bootloader detected" >/dev/kmsg || true | |
| androiddir="writable/androidboot" | |
| mkdir -p "${rootmnt}/${androiddir}" | |
| if [ ! -e "${rootmnt}/${androiddir}/androidboot.env" ]; then | |
| echo -e "snap_mode=\nsnap_core=$snap_core\nsnap_kernel=$snap_kernel\nsnap_try_core=\nsnap_try_kernel=" > \ | |
| "${rootmnt}/${androiddir}/androidboot.env" | |
| fi | |
| mount -o bind "${rootmnt}/${androiddir}" "${rootmnt}/boot/androidboot" | |
| fi | |
| # Mount the systemd overlay so that we have a complete root partition during boot | |
| mkdir -p "${rootmnt}/writable/system-data/etc/systemd/system" | |
| mount -o bind "${rootmnt}/writable/system-data/etc/systemd/system" "${rootmnt}/etc/systemd/system" | |
| # create "machine-id" if it does not exist and bind mount it (LP: #1619721) | |
| if [ ! -e ${rootmnt}/writable/system-data/etc/machine-id ]; then | |
| cat /proc/sys/kernel/random/uuid|tr -d - >${rootmnt}/writable/system-data/etc/machine-id | |
| fi | |
| mount -o bind "${rootmnt}/writable/system-data/etc/machine-id" "${rootmnt}/etc/machine-id" | |
| # Apply customized content | |
| for user in "${rootmnt}"/writable/user-data/* | |
| do | |
| if [ -d "${rootmnt}/custom/home" ] && [ ! -e "$user/.customized" ]; then | |
| echo "initrd: copying custom content tp " >/dev/kmsg || true | |
| cp -Rap "${rootmnt}"/custom/home/* "$user/" | |
| cp -Rap "${rootmnt}"/custom/home/.[a-zA-Z0-9]* "$user/" | |
| touch "$user/.customized" | |
| dstown=$(stat -c "%u:%g" "$user") | |
| chown -R "$dstown" "$user/" | |
| fi | |
| done | |
| [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-bottom" | |
| run_scripts /scripts/local-bottom | |
| [ "$quiet" != "y" ] && log_end_msg | |
| } |