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 Windows 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

There is a newer version of this guide for Ubuntu 19.10.


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 -n3:1M:+511M -t3:8300 -c3:Boot /dev/disk/by-id/^^ata_disk^^
sgdisk -n2:0:+128M  -t2:EF00 -c2:EFI  /dev/disk/by-id/^^ata_disk^^
sgdisk -n1:0:0      -t1:8300 -c1:Data /dev/disk/by-id/^^ata_disk^^

sgdisk --print                        /dev/disk/by-id/^^ata_disk^^

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 512 \
    --pbkdf pbkdf2 --hash sha256 /dev/disk/by-id/^^ata_disk^^-part1
cryptsetup luksOpen /dev/disk/by-id/^^ata_disk^^-part1 system

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/system
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:

yes | mkfs.ext4 /dev/disk/by-id/^^ata_disk^^-part3
mount /dev/disk/by-id/^^ata_disk^^-part3 /mnt/system/boot/

mkdir /mnt/system/boot/efi
mkfs.msdos -F 32 -n EFI /dev/disk/by-id/^^ata_disk^^-part2
mount /dev/disk/by-id/^^ata_disk^^-part2 /mnt/system/boot/efi

We need to ensure boot partition auto-mounts:

echo PARTUUID=$(blkid -s PARTUUID -o value /dev/disk/by-id/^^ata_disk^^-part3) \
    /boot ext4 noatime,nofail,x-systemd.device-timeout=5s 0 1 >> /mnt/system/etc/fstab
echo PARTUUID=$(blkid -s PARTUUID -o value /dev/disk/by-id/^^ata_disk^^-part2) \
    /boot/efi vfat noatime,nofail,x-systemd.device-timeout=5s 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
sed 's/ubuntu/^^hostname^^/' /etc/hosts > /mnt/system/etc/hosts
sed '/cdrom/d' /etc/apt/sources.list > /mnt/system/etc/apt/sources.list
cp /etc/netplan/*.yaml /mnt/system/etc/netplan/

If you are installing via WiFi, you might as well copy your credentials:

mkdir -p /mnt/system/etc/NetworkManager/system-connections/
cp /etc/NetworkManager/system-connections/* /mnt/system/etc/NetworkManager/system-connections/

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

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

Now we can update our software:

apt update

Immediately 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 keyutils

echo "system UUID=$(blkid -s UUID -o value /dev/disk/by-id/^^ata_disk^^-part1) \
    none luks,discard,initramfs,keyscript=decrypt_keyctl" >> /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/efi \
    --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 unmount our new environment. If unmount fails, just repeat it until it doesn’t. :)

exit
umount -R /mnt/system

Finally we can correct root’s mount point and reboot:

zfs set mountpoint=/ system/root
reboot

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


[2019-10-27: Added --make-rslave]


PS: There are versions of this guide using the native ZFS encryption for other Ubuntu versions: 21.10 and 20.04

PPS: For LUKS-based ZFS setup, check the following posts: 20.04, 19.10, and 18.10.

Using Visual Studio Code for .NET Framework Projects in C#

As Visual Studio Code is the only completely free member of Visual Studio family now that Visual Studio Express is gone (and no, Community edition doesn’t count as a proper replacement), I wanted to have some of my projects debuggable from within it. While .NET Core works out of box, it takes a bit of effort to setup a full .NET Framework project.

First step is, of course, hunt on Stack Overflow. If you are lucky you might find a guide how to do it that actually works perfectly once you do a few modifications to your project. However, depending on your project, you might not need to go that far.

To start, destination computer will require something that can build your projects. Since installing Visual Studio makes the whole exercise a bit pointless, go and download Build Tools for Visual Studio 2019.

To your Visual Studio Code workspace add familiar .vscode directory and add tasks.json that will build your project. Based on a few of my .NET Framework projects, I found the following common denominator to do wonders (of course, change name of solution and exact path to your source and binaries):

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build",
      "dependsOn": [ "clean" ],
      "type": "process",
      "windows": {
        "command": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\MSBuild\\Current\\Bin\\amd64\\msbuild.exe",
        "args": [
          "-p:Configuration=Debug;DebugType=portable;PlatformTarget=x64",
          "^^Solution.sln^^"
        ]
      },
      "options": { "cwd": "${workspaceFolder}/^^Source^^/" },
      "group": "build"
    },
    {
      "label": "clean",
      "type": "shell",
      "windows": {
        "command": "DEL",
        "args": [ "^^.\\Binaries\\*^^" ]
      }
    }
  ]
}

Here you can already see the trick - we’re adding parameters as argument to MSBuild instead of modifying the project file. I found that most projects (definitely all I tried) are only missing portable debug type and x64 as a platform target (I am still sucker for Any CPU). If it doesn’t work for you, go and check the original article to see if something else is missing.

And the last step is placing launch.json into .vscode directory (again, adjust names):

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": ".NET Framework Launch",
      "type": "clr",
      "request": "launch",
      "preLaunchTask": "build",
      "program": "^^Program.exe^^",
      "args": [],
      "cwd": "${workspaceFolder}/^^Binaries^^/",
      "stopAtEntry": false
    }
  ]
}

This should be sufficient to get you debugging in a pinch.