Adding UEFI Windows 10 Menu Entry to Grub

If you install Ubuntu first and Windows later, you’ll notice that it’s not possible to boot into Linux anymore. As Windows boot loader doesn’t really handle Linux, you’ll need to tell Winodws to use Grub.

Once you’re in command prompt with administrative privileges, you can execute:

> bcdedit /set {bootmgr} path \EFI\Ubuntu\grubx64.efi

After reboot Grub will show it’s ugly face and you’ll have another problem – there are no Windows entries.

To get them into Grub menu, one can simply update grub:

$ sudo update-grub

On most Linux distributions this will trigger OS Prober and Windows Boot Manager entry will magically appear. However, if you have OS Prober disabled or you want to disable it in future for some reason, you can add manual entry too:

$ cat << EOF | sudo tee /etc/grub.d/25_windows
#!/bin/sh
exec tail -n +3 \$0
menuentry 'Windows 10' {
  savedefault
  search --no-floppy --set=root --file /EFI/Microsoft/Boot/bootmgfw.efi
  chainloader (\${root})/EFI/Microsoft/Boot/bootmgfw.efi
}
EOF

$ sudo chmod +x /etc/grub.d/25_windows

$ sudo update-grub

In either case, boot menu should now offer you option to get into Windows.

Symbolic Links on VirtualBox Share

If you are running VirtualBox with Linux guest under a Linux host, you will definitely need symbolic links at one point in time. And, while you can do links toward shared folders, you cannot create a link on shared folder itself. You will get something like:

$ ln -s /something /somethingelse
ln: failed to create symbolic link '/somethingElse': Read-only file system

Well, fortunately it’s easy to enable connection on per share basis. Just run the following command on your host (replacing GuestName and ShareName with correct information):

$ VBoxManage setextradata 'GuestName' VBoxInternal2/SharedFoldersEnableSymlinksCreate/ShareName 1

If your machine is running, you will need to fully shut it down (no, restart doesn’t help) and you get to enjoy the full symbolic link glory.

Detecting Status Bar Double-Click in VSCode Extension

If you’re developing Visual Studio Code extension that deals with statusbar, you might have noticed that it’s activated with a single click. Yes, there is no way to specify double-click behavior. But what if you really, really, want that double-click? Well, you can use trickery.

For example, you can use a timer to help you out:

var doubleClickTimerId

vscode.commands.registerTextEditorCommand("statusbar.command", () => {
  if (!doubleClickTimerId) { //if timer still exists, it's a double-click
    doubleClickTimerId = setTimeout(singleClick, 250); //do single-click once timer has elapsed
  } else {
    clearTimeout(doubleClickTimerId) //cancel timer
    doubleClickTimerId = undefined

    //code for double-click
  }
})

function singleClick() {
  clearTimeout(doubleClickTimerId) //cancel timer
  doubleClickTimerId = undefined

    //code for single click
}

On first click this code will setup a callback to be executed in 250 ms. If nothing else happens, after timer has elapsed, function singleClick will be called and your single-click code will be executed.

However, if another click happens before timer elapses, statusbar command will cancel the timer and execute the double-click code.

Final effect is that any clicks slower than 250 ms get detected as independent while all faster clicks get a double-click treatment – exactly what we wanted.

PS: If you’re wondering why I needed this, it was for my Code Point extension. Single click changes between display style for Unicode character while double click sends you to a web page with much more information.

Installing UEFI ZFS Root on Ubuntu 19.04

As rumors of Ubuntu 19.04 including ZFS installer proved to be a bit premature, I guess it’s time for a slight adjustment to my previous ZFS instructions.

Again, all this is just a derivation on ZFS-on-Linux project’s instruction for older version.

As before, we first need to get into root prompt:

$ sudo -i

Followed by getting a few basic packages ready:

# apt-add-repository universe
# apt update
# apt install --yes debootstrap gdisk zfs-initramfs

Disk setup is quite simple with only two partitions:

# sgdisk --zap-all             /dev/disk/by-id/ata_disk

# sgdisk -n2:1M:+511M -t2:EF00 /dev/disk/by-id/ata_disk
# sgdisk -n1:0:0      -t1:8300 /dev/disk/by-id/ata_disk

# sgdisk --print               /dev/disk/by-id/ata_disk
Number  Start (sector)    End (sector)  Size       Code  Name
   1         1050624        67108830   31.5 GiB    8300
   2            2048         1050623   512.0 MiB   8300

I believe full disk encryption should be a no-brainer so of course we set up LUKS:

# cryptsetup luksFormat -q --cipher aes-xts-plain64 --key-size 256 \
      --pbkdf pbkdf2 --hash sha256 /dev/disk/by-id/ata_disk-part1
# cryptsetup luksOpen /dev/disk/by-id/ata_disk-part1 luksSystem

Creating ZFS stays the same as before:

# zpool create -o ashift=12 -O atime=off -O canmount=off -O compression=lz4 -O normalization=formD \
      -O xattr=sa -O mountpoint=none system /dev/mapper/luksSystem
# zfs create -o canmount=noauto -o mountpoint=/mnt/system/ system/root
# zfs mount system/root

Getting basic installation on our disks follows next:

# debootstrap disco /mnt/system/
# zfs set devices=off system
# zfs list

And then we setup EFI boot partition:

# mkdosfs -F 32 -n EFI /dev/disk/by-id/ata_disk-part2
# mount /dev/disk/by-id/ata_disk-part2 /mnt/system/boot/

We need to ensure boot partition auto-mounts:

# echo PARTUUID=$(blkid -s PARTUUID -o value /dev/disk/by-id/ata_disk-part2) /boot vfat noatime,nofail,x-systemd.device-timeout=1 0 1 >> /mnt/system/etc/fstab
# cat /mnt/system/etc/fstab

Before we start using anything, we should prepare a few necessary files:

# echo "hostname" > /mnt/system/etc/hostname
# cp /etc/hosts /mnt/system/etc/hosts
# cp /etc/netplan/*.yaml /mnt/system/etc/netplan/
# sed '/cdrom/d' /etc/apt/sources.list > /mnt/system/etc/apt/sources.list

With chroot we can get the first taste of our new system:

# mount --rbind /dev  /mnt/system/dev
# mount --rbind /proc /mnt/system/proc
# mount --rbind /sys  /mnt/system/sys
# chroot /mnt/system/ /bin/bash --login

Now we can update our software:

# apt update

Imediatelly followed with locale and time zone setup:

# locale-gen --purge "en_US.UTF-8"
# update-locale LANG=en_US.UTF-8 LANGUAGE=en_US
# dpkg-reconfigure --frontend noninteractive locales

# dpkg-reconfigure tzdata

Now we install Linux image and basic ZFS boot packages:

# apt install --yes --no-install-recommends linux-image-generic
# apt install --yes zfs-initramfs

Since we’re dealing with encrypted data, our cryptsetup should be also auto mounted:

# apt install --yes cryptsetup

# echo "luksSystem UUID=$(blkid -s UUID -o value /dev/disk/by-id/ata_disk-part1) none luks,discard,initramfs" >> /etc/crypttab
# cat /etc/crypttab

Now we get grub started:

# apt install --yes grub-efi-amd64

And update our boot environment again (seeing errors is nothing unusual):

# update-initramfs -u -k all

And then we finalize our grup setup:

# update-grub
# grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=Ubuntu --recheck --no-floppy

Finally we get the rest of desktop system:

# apt-get install --yes ubuntu-desktop samba linux-headers-generic
# apt dist-upgrade --yes

We can omit creation of the swap dataset but I always find it handy:

# zfs create -V 4G -b $(getconf PAGESIZE) -o compression=off -o logbias=throughput -o sync=always \
      -o primarycache=metadata -o secondarycache=none system/swap
# mkswap -f /dev/zvol/system/swap
# echo "/dev/zvol/system/swap none swap defaults 0 0" >> /etc/fstab
# echo RESUME=none > /etc/initramfs-tools/conf.d/resume

If one is so inclined, /home directory can get a separate dataset too:

# rmdir /home
# zfs create -o mountpoint=/home system/home

Only remaining thing before restart is to create user:

# adduser user
# usermod -a -G adm,cdrom,dip,lpadmin,plugdev,sambashare,sudo user
# chown -R user:user /home/user

As install is ready, we can exit our chroot environment and reboot:

# exit
# reboot

You will get stuck after the password prompt as our mount point for system dataset is wrong. That’s easy to correct:

# zfs set mountpoint=/ system/root
# exit
# reboot

Assuming nothing went wrong, your UEFI system is now ready.