Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP experimental btrfs setup #6

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions builder.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/bash -x

set -e

[ $# != 1 ] && exit 1

# osimage is the image reference build with "make build-os"
osimage=$1

# Create an empty raw disk image
qemu-img create -f raw disk.img 16G

loopdev=$(losetup -f --show disk.img)

# Just to create the basi disk layout and EFI partition
./build/elemental --debug install --firmware efi --disable-boot-entry --local --system.uri ${osimage} ${loopdev}

# Reformat STATE partition with btrfs
mkfs.btrfs -L COS_STATE ${loopdev}p4 -f

# mound partition and start feeding it as the installer should have done it
mount ${loopdev}p4 /mnt

# Enable quota management and create subvolumes
btrfs quota enable /mnt
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@/.snapshots

#mkdir -p /mnt/.snapshots
mkdir -p /mnt/@/.snapshots/1

btrfs subvolume snapshot /mnt/@ /mnt/@/.snapshots/1/snapshot

# ID should be computed from a "btrfs subvolume list" command
# default set to the equivalent of the STATE partition (probably a /@/state subvolume would make more sense)
volid=$(btrfs subvolume list --sort path /mnt | head -n 1 | cut -d" " -f2)
btrfs subvolume set-default ${volid} /mnt

# Create snapshot 1 info
date=$(date +'%Y-%m-%d %H:%M:%S')
cat << EOF > /mnt/@/.snapshots/1/info.xml
<?xml version="1.0"?>
<snapshot>
<type>single</type>
<num>1</num>
<date>${date}</date>
<description>first root filesystem</description>
</snapshot>
EOF

# Dump the built image into the first snapshot subvolume
./build/elemental pull-image --local ${osimage} /mnt/@/.snapshots/1/snapshot/


# Create the quota group
btrfs qgroup create 1/0 /mnt
#chroot /mnt/@/.snapshots/1/snapshot snapper --no-dbus set-config QGROUP=1/0

# set first snapshot as readonly
btrfs property set /mnt/@/.snapshots/1/snapshot ro true

# Some state vars required for the bootloader to boot
grub2-editenv /mnt/@/grub_oem_env set oem_label=COS_OEM persistent_label=COS_PERSISTENT state_label=COS_STATE recovery_label=COS_RECOVERY active_snapshot=/@/.snapshots/1/snapshot

# Copy grub configuration where it is expected
mkdir -p /mnt/@/grub2
cp /mnt/@/.snapshots/1/snapshot/etc/cos/grub.cfg /mnt/@/grub2

sync
umount /mnt
13 changes: 13 additions & 0 deletions examples/green/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ RUN ARCH=$(uname -m); \
less \
sudo \
curl \
btrfsprogs \
btrfsmaintenance \
snapper \
tukit \
sed

# Just add the elemental cli
Expand All @@ -53,6 +57,15 @@ RUN systemctl enable NetworkManager.service
# Enable /tmp to be on tmpfs
RUN cp /usr/share/systemd/tmp.mount /etc/systemd/system

# Configure snapper with defaults
RUN cp /etc/snapper/config-templates/default /etc/snapper/configs/root && \
sed -i 's|SNAPPER_CONFIGS=.*$|SNAPPER_CONFIGS="root"|g' /etc/sysconfig/snapper && \
sed -i 's|^TIMELINE_CREATE=.*$|TIMELINE_CREATE="no"|g' /etc/snapper/configs/root && \
sed -i 's|^QGROUP=.*$|QGROUP="1/0"|g' /etc/snapper/configs/root && \
sed -i 's|^NUMBER_LIMIT=.*$|NUMBER_LIMIT="2-10"|g' /etc/snapper/configs/root && \
sed -i 's|^NUMBER_LIMIT_IMPORTANT=.*$|NUMBER_LIMIT_IMPORTANT="4-10"|g' /etc/snapper/configs/root


# Generate initrd with required elemental services
RUN elemental init -f && \
kernel=$(ls /boot/Image-* | head -n1) && \
Expand Down
39 changes: 16 additions & 23 deletions pkg/features/embedded/grub-config/etc/cos/grub.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -42,40 +42,33 @@ insmod all_video
insmod gfxterm
insmod loopback
insmod squash4
insmod btrfs

set loopdev="loop0"
set btrfs_relative_path="y"

menuentry "${display_name}" --id cos {
# label is kept around for backward compatibility
set label=${active_label}
set img=/cOS/active.img
loopback $loopdev /$img
source ($loopdev)/etc/cos/bootargs.cfg
linux ($loopdev)$kernel $kernelcmd ${extra_cmdline} ${extra_active_cmdline}
initrd ($loopdev)$initramfs
loopback -d $loopdev
btrfs-mount-subvol ($root) / ${active_snapshot}
set mode="active"
set img=${active_snapshot}
source /etc/cos/bootargs.cfg
linux $kernel $kernelcmd ${extra_cmdline} ${extra_active_cmdline}
initrd $initramfs
}

menuentry "${display_name} (fallback)" --id fallback {
# label is kept around for backward compatibility
set label=${passive_label}
set img=/cOS/passive.img
loopback $loopdev /$img
source ($loopdev)/etc/cos/bootargs.cfg
linux ($loopdev)$kernel $kernelcmd ${extra_cmdline} ${extra_passive_cmdline}
initrd ($loopdev)$initramfs
loopback -d $loopdev
btrfs-mount-subvol ($root) / ${passive_snapshot}
set mode="passive"
set img=${passive_snapshot}
source /etc/cos/bootargs.cfg
linux $kernel $kernelcmd ${extra_cmdline} ${extra_passive_cmdline}
initrd $initramfs
}

menuentry "${display_name} recovery" --id recovery {
# label and recoverylabel are kept around for backward compatibility
if [ -n "${system_label}" ]; then
set label=${system_label}
else
set recoverylabel=${recovery_label}
fi
set img=/cOS/recovery.img
search --no-floppy --label --set=root $recovery_label
set mode="recovery"
set img=/cOS/recovery.img
loopback $loopdev /$img
source ($loopdev)/etc/cos/bootargs.cfg
linux ($loopdev)$kernel $kernelcmd ${extra_cmdline} ${extra_recovery_cmdline}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# TODO we could sanity check $partlabel is set here so we can error out before even attempting to boot
set kernel=/boot/vmlinuz
if [ "${img}" == "/cOS/recovery.img" ]; then
set kernelcmd="console=tty1 console=ttyS0 root=LABEL=$recovery_label cos-img/filename=$img security=selinux selinux=0 rd.neednet=1 rd.cos.oemlabel=$oem_label rd.cos.mount=LABEL=$oem_label:/oem"
set kernelcmd="console=tty1 console=ttyS0 root=LABEL=$recovery_label cos-img/filename=$img security=selinux selinux=0 rd.neednet=1 rd.cos.oemlabel=$oem_label rd.cos.mount=LABEL=$oem_label:/oem cos-mode=${mode}"
else
set kernelcmd="console=tty1 console=ttyS0 root=LABEL=$state_label cos-img/filename=$img panic=5 security=selinux selinux=1 rd.neednet=1 rd.cos.oemlabel=$oem_label rd.cos.mount=LABEL=$oem_label:/oem rd.cos.mount=LABEL=$persistent_label:/usr/local fsck.mode=force fsck.repair=yes"
set kernelcmd="console=tty1 console=ttyS0 root=LABEL=$state_label cos-img/filename=$img panic=5 security=selinux selinux=1 rd.neednet=1 rd.cos.oemlabel=$oem_label rd.cos.mount=LABEL=$oem_label:/oem rd.cos.mount=LABEL=$persistent_label:/usr/local cos-mode=${mode}"
fi
set initramfs=/boot/initrd
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ type getarg >/dev/null 2>&1 || . /lib/dracut-lib.sh
cos_unit="elemental-immutable-rootfs.service"
cos_layout="/run/cos/cos-layout.env"
root_part_mnt="/run/initramfs/cos-state" # TODO change to something under /run/cos
snapshots_mnt="/.snapshots"
snapshots_vol="@/.snapshots"

# Omit any immutable roofs module logic if disabled
if getargbool 0 rd.cos.disable; then
Expand All @@ -15,6 +17,8 @@ fi
cos_img=$(getarg cos-img/filename=)
[ -z "${cos_img}" ] && exit 0

cos_mode=$(getarg cos-mode=)

[ -z "${root}" ] && root=$(getarg root=)

cos_root_perm="ro"
Expand Down Expand Up @@ -148,12 +152,38 @@ mkdir -p "$GENERATOR_DIR/$dev.device.d"
echo "RequiresMountsFor=${root_part_mnt}"
echo "[Mount]"
echo "Where=/sysroot"
echo "What=${root_part_mnt}/${cos_img#/}"
echo "Options=${cos_root_perm},suid,dev,exec,auto,nouser,async"
if [ "${cos_mode}" == "recovery" ]; then
echo "What=${root_part_mnt}/${cos_img#/}"
echo "Options=${cos_root_perm},suid,dev,exec,auto,nouser,async"
else
echo "What=${root}"
echo "Options=${cos_root_perm},subvol=${cos_img},suid,dev,exec,auto,nouser,async"
fi
} > "$GENERATOR_DIR"/sysroot.mount

if [ ! -e "$GENERATOR_DIR/initrd-root-fs.target.requires/sysroot.mount" ]; then
mkdir -p "$GENERATOR_DIR"/initrd-root-fs.target.requires
ln -s "$GENERATOR_DIR"/sysroot.mount \
"$GENERATOR_DIR"/initrd-root-fs.target.requires/sysroot.mount
fi

if [ "${cos_mode}" != "recovery" ]; then
snapshots_unit="${snapshots_mnt#/}"
snapshots_unit="${snapshots_unit//-/\\x2d}"
snapshots_unit="${snapshots_unit//\//-}.mount"
{
echo "[Unit]"
echo "Before=initrd-root-fs.target"
echo "DefaultDependencies=no"
echo "[Mount]"
echo "Where=${snapshots_mnt}"
echo "What=${root}"
echo "Options=defaults,subvol=${snapshots_vol}"
} > "$GENERATOR_DIR/${snapshots_unit}"

if [ ! -e "$GENERATOR_DIR/initrd-root-fs.target.requires/${snapshots_unit}" ]; then
mkdir -p "$GENERATOR_DIR"/initrd-root-fs.target.requires
ln -s "$GENERATOR_DIR/${snapshots_unit}" \
"$GENERATOR_DIR/initrd-root-fs.target.requires/${snapshots_unit}"
fi
fi
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ declare cos_layout="/run/cos/cos-layout.env"
declare root_fstype=$(findmnt -rno FSTYPE /sysroot)
declare root=$(findmnt -rno SOURCE /sysroot)
declare base_part=$(findmnt -rno SOURCE /run/cos/root)
declare snapshots_mnt="/.snapshots"
declare snapshots_vol="@/.snapshots"
declare partname
declare fstab
declare state_paths
Expand All @@ -260,12 +262,24 @@ readLayoutConfig

[ -z "${cos_overlay}" ] && exit 0

# Hack to get the root device and its mounted subvolume
snapshot=${root##*[}
snapshot=${snapshot%%]}
root=${root%%[*}

# If sysroot is already an overlay do not prepare the rw overlay
if [ "${root_fstype}" != "overlay" ]; then
if [ -f "${base_part}" ]; then
fstab="${base_part} /run/initramfs/cos-state auto ${cos_root_perm} 0 0\n"
fi
fstab+="${root} / auto ${cos_root_perm} 0 0\n"

if [ "${root_fstype}" == "btrfs" ]; then
fstab+="${root} / auto ${cos_root_perm},subvol=${snapshot} 0 0\n"
fstab+="${root} ${snapshots_mnt} auto defaults,subvol=${snapshots_vol} 0 0\n"
else
fstab+="${root} / auto ${cos_root_perm} 0 0\n"
fi

fstab+=$(mountOverlayBase)
fi

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ if getargbool 0 rd.cos.disable; then
return 0
fi

cos_img=$(getarg cos-img/filename=)
[ -z "${cos_img}" ] && return 0
cos_mode=$(getarg cos-mode=)
[ -z "${cos_mode}" ] && return 0
[ -z "${root}" ] && root=$(getarg root=)

cos_root_perm="ro"
Expand Down Expand Up @@ -53,7 +53,7 @@ fi

# set sentinel file for boot mode
mkdir -p /run/cos
case "${cos_img}" in
case "${cos_mode}" in
*recovery*)
echo -n 1 > /run/cos/recovery_mode ;;
*active*)
Expand Down
8 changes: 6 additions & 2 deletions pkg/utils/grub.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ const (
grubCfgFile = "grub.cfg"

grubEFICfgTmpl = `
insmod btrfs

set btrfs_relative_path="y"

search --no-floppy --label --set=root %s
set prefix=($root)/` + grubConfDir + `
configfile ($root)/` + grubConfDir + `/%s
configfile ${prefix}/` + grubCfgFile + `
`
)

Expand Down Expand Up @@ -254,7 +258,7 @@ func (g Grub) InstallEFI(rootDir, bootDir, efiDir, deviceLabel string) (string,
// Add grub.cfg in EFI that chainloads the grub.cfg in recovery
// Notice that we set the config to /grub2/grub.cfg which means the above we need to copy the file from
// the installation source into that dir
grubCfgContent := []byte(fmt.Sprintf(grubEFICfgTmpl, deviceLabel, grubCfgFile))
grubCfgContent := []byte(fmt.Sprintf(grubEFICfgTmpl, deviceLabel))
// Fallback
err = g.config.Fs.WriteFile(filepath.Join(efiDir, fallbackEFIPath, grubCfgFile), grubCfgContent, cnst.FilePerm)
if err != nil {
Expand Down
Loading