Migrate a physical Ubuntu 20.04 Desktop to WSLg in Windows 11 by mounting the system SSD

Solution 1:

While I'm not sure (yet) that there's a way to use the drive as a WSL instance itself, I do think we can get the chroot method working, at least to some degree.

Here's the setup that I used:

  • Ubuntu Base 21.10 rootfs image extracted (as sudo) to $HOME/chroot/Ubuntu21.10.
  • For testing, after the below configuration was completed, apt install xterm
  • For anyone attempting this with an actual rootfs, note that you should untar with the xattrs option, e.g.:
    # Download Ubuntu Base rootfs into the current directory, then:
    mkdir Ubuntu21.10
    cd !$
    sudo tar -xvz --xattrs -f ../ubuntu-base-21.10-base-amd64.tar.gz
    

Off the top of my head, I can't see a reason that this wouldn't be a semi-accurate representation of an existing SSD that you've been able to mount into a directory inside of a WSL/Ubuntu instance. However, I'm sure there will be items I didn't anticipate from your configuration. Let me know via comment here (plus possibly via new question if warranted) if you run into issues.

What's working

  • Networking
  • Running Windows .exes from within the chroot (including GUI apps)
  • Running Linux GUI apps from within the chroot (requires, of course, Windows 11)

What's known not to work

  • Cannot mount Windows drives from within the chroot (e.g. mount -t drvfs ...). This is likely due to the fact that I haven't yet been able to find a way to mount the special WSL drivers and lib filesystems. Everything else works here via either a mount of the actual filesystem into the chroot path or via a mount --bind ("bind mount") of an existing path from WSL into the chroot path.

  • The Windows path is not populated into that of the chroot (which WSL normally does via /init. This means that you will have to use the fully-qualified path to run any .exes. Populating the path isn't all that difficult to script, but this answer is long enough as it is, so I'll leave the as an exercise to the reader (or a separate question).

Not Tested

  • Audio: If it doesn't work, then it's probably just a matter of finding the right environment variables, or perhaps one more socket to mount if needed.

  • VSCode: May have issues due to "default user" expectation on VSCode's part)

  • Lots of other stuff

One-time prep

There are a few tasks to prep the chroot filesystem for the first time.

  • From inside WSL (not in the chroot), create mountpoints and copy over some WSL items:

    cd <your_chroot_mountpoint_root>
    # Create mount points
    sudo mkdir mnt/c
    sudo mkdir run/WSL
    sudo mkdir mnt/wslg
    sudo mkdir mnt/wsl
    sudo mkdir -p usr/lib/wsl/drivers usr/lib/wsl/lib
    
    # Copy WSL generated config files - Currently doesn't work, but there for future reference/use
    # sudo cp /etc/ld.so.conf.d/ld.wsl.conf etc/ld.so.conf.d
    
    # Delete existing resolv.conf and use the one generated by WSL
    sudo rm etc/resolv.conf
    sudo ln -rs mnt/wsl/resolv.conf etc/
    
    # Delete existing X socket (if exists) and use the one generated by WSL
    [ -S tmp/.X11-unix ] && sudo rm tmp/.X11-unix
    [ -f tmp/.X11-unix ] && sudo rm tmp/.X11-unix
    [ -L tmp/.X11-unix ] && sudo rm tmp/.X11-unix
    sudo ln -rs mnt/wslg/.X11-unix/ tmp/.X11-unix
    
    # Set up fstab for recurring mount points.
    
    # (0) Make sure you are in the root of the chroot
    #     (the mounted drive, in your case) before
    #     running each command.
    # (A) I suggest running these lines one-by-one to make sure
    #     they work since ...
    # (B) They are not idempotent
    # (C) Check /etc/fstab after each one.  They should be 
    # pointing to the appropriate directory in your chroot
    
    # Set up bind mounts for the WSL generated sockets and files:
    sudo sh -c 'echo "/mnt/wsl $PWD/mnt/wsl none bind 0 0" >> /etc/fstab'
    sudo sh -c 'echo "/mnt/wslg $PWD/mnt/wslg none bind 0 0" >> /etc/fstab'
    #sudo sh -c 'echo "/run/WSL $PWD/run/WSL none bind 0 0" >> /etc/fstab' # Currently causes issues
    
    # Create fstab entries for the necessary filesystems.  For
    # the most part, we do this by finding the entry in 
    # /etc/mtab that WSL generated, copying that over to
    # /etc/fstab, and substituting the chroot path.
    
    sudo sh -c "grep '^drvfs[[:space:]]\+/mnt/c\W' /etc/mtab | sed \"s-\(/mnt/c\)-${PWD}\1-\" >> /etc/fstab"
    sudo sh -c "grep '^none[[:space:]]\+/dev[[:space:]]' /etc/mtab | sed \"s-\(/dev\)-${PWD}\1-\" >> /etc/fstab"
    sudo sh -c "grep '^proc[[:space:]]\+/proc[[:space:]]' /etc/mtab | sed \"s-\(/proc\)-${PWD}\1-\" >> /etc/fstab"
    sudo sh -c "grep '^devpts[[:space:]]\+/dev/pts[[:space:]]' /etc/mtab | sed \"s-\(/dev/pts\)-${PWD}\1-\" >> /etc/fstab"
    sudo sh -c "grep '^sysfs[[:space:]]\+/sys[[:space:]]' /etc/mtab | sed \"s-\(/sys\)-${PWD}\1-\" >> /etc/fstab"
    sudo sh -c "grep '^binfmt_misc[[:space:]]\+/proc/sys/fs/binfmt_misc[[:space:]]' /etc/mtab | sed \"s-\(/proc/sys/fs/binfmt_misc\)-${PWD}\1-\" >> /etc/fstab"
    
    # Mount these newly added entries in `/etc/fstab`.  Only needed once -- From here on out, they will automount when you restart the WSL instance:
    sudo mount -a
    
  • There's one thing that doesn't quite work here. We can't seem to bind mount /run/WSL via /etc/fstab. I believe this is due to a timing issue -- WSL probably hasn't completed mounting the original /run/WSL, so we can't yet bind mount it. Attempting to do so will result in an error at WSL start-up. This error is not fatal.

    You have several options. You can either: * Run the commented-out line above to generate the fstab entry, deal with the error, and then run sudo mount -a after you restart WSL (or add it to a startup file) * Just run sudo mount --bind /run/WSL/ $PWD/run/WSL/ (from inside the chroot root directory) after a WSL restart (Or add it to a startup file).

Starting the chroot:

sudo chroot $PWD env WSL_INTEROP="$WSL_INTEROP" DISPLAY="$DISPLAY" WAYLAND_DISPLAY="$WAYLAND_DISPLAY" XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR" /usr/bin/bash

This populates the environment with the necessary environment variables before starting the shell. There may be additional variables that you want to map, and that's easy enough.

I recommend an apt update to start. This will at least tell you if DNS resolution is working. If not, check the /etc/resolv.conf symlink.

Other startup alternatives include creating a user with the same name as in your WSL instance and then:

sudo chroot $PWD env WSL_INTEROP="$WSL_INTEROP" DISPLAY="$DISPLAY" WAYLAND_DISPLAY="$WAYLAND_DISPLAY" XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR" runuser -u $USER /usr/bin/bash