8

I am using Ubuntu with WSL with a zsh/bash shell. My host OS is Windows 10. I already figured out how to mount a Windows external drive:

sudo mkdir /mnt/d
sudo mount -t drvfs D: /mnt/d

I rarely plug this drive in, so I'd like to automatically mount it when I do. It's fine if this occurs after I plug the drive in, then start a new WSL terminal.

I don't want to put this command in a .bashrc type of file because having to type in my password every time I open a terminal would be more trouble than it's worth, especially if 99% of the time, the command will fail anyway. Putting it in a bash script won't help either, because I'd run it so infrequently, by the time I need to use it, I'll forget I created it in the first place.

So, is there a way to mount an external drive when it becomes available in WSL?


I don't know if this is a good solution, but this tells you how to turn off the password request for sudo. This is the route I took: https://superuser.com/a/1492456/89165

Daniel Kaplan
  • 725
  • 1
  • 10
  • 26

3 Answers3

7

It looks to me like this will need two different approachs:

  • First we need to handle the case when WSL starts up while the drive is already attached.
  • And we also need to handle the case when the drive is attached while WSL is already running.

Drive already attached when WSL starts

The first part should be pretty easy.

I don't want to put this command in a .bashrc type of file because having to type in my password every time I open a terminal

That's simple to overcome. Add the following to your ~/.bashrc instead:

wsl.exe -u root -e mount -t drvfs D: /mnt/d > /dev/null 2>&1

That will mount the drive if it's available. If not, it will silently fail.

Drive is attached while WSL is running

It's not easy, and it probably needs more error handling to be "robust", but I was able to get this to work by creating a PowerShell script to:

  • Register an action when a USB drive is attached
  • Run wsl -u root -e mount -t drvfs /mnt/<driveletter> <Drive> when the event fires.
$query = "select * from __InstanceCreationEvent within 5 where TargetInstance ISA 'Win32_LogicalDisk' and TargetInstance.DriveType = 2"

$action = { $drivePath = $event.SourceEventArgs.NewEvent.TargetInstance.Name $driveLetter = $drivePath.ToLower()[0] wsl -u root -e mount -t drvfs $drivePath /mnt/$driveLetter }

Register-WmiEvent -Query $Query -Action $Action -SourceIdentifier USBFlashDriveWSLMount

Note that this needs to run in Windows PowerShell (rather than PowerShell Core) since it uses WMIEvent. I'm sure there's a PowerShell Core equivalent using CIM, but I haven't tried it that way yet.

If you run into a problem, check the output. From PowerShell:

Get-Job
# Get the Id of the job then
Receive-Job <job_id>

In theory, you can set this script to run at Windows Login via Task Scheduler, but I haven't tried it. I'm 80% confident it will work. You'll need to call it through the powershell command (not pwsh since it uses WMIEvent).

Note, there's also apparently a way to register the event listener permanently though CIM. I spent some time on this last week, but got rabbit-trailed when I followed some documentation that created a CIM class on my system that I couldn't get rid of. I may come back to it eventually, but hopefully this works for you as a Scheduled Task. I just didn't want to hold off posting until I got it "just right", because I might never finish it up.

NotTheDr01ds
  • 21,923
  • Still on the first paragraph, but "And we also need to handle the case when the drive is attached while WSL is already running" isn't a requirement for me, just a nice to have. – Daniel Kaplan Aug 09 '22 at 21:11
  • @DanielKaplan Excellent - Then that should make it pretty easy! – NotTheDr01ds Aug 09 '22 at 21:12
  • @DanielKaplan And while I'm thinking about it (since I just answered another related question), see this Ask Ubuntu answer for a slight tweak on the mount command. – NotTheDr01ds Aug 09 '22 at 21:13
  • I just tried this and it didn't work. It errors, saying User not found. – Daniel Kaplan Aug 19 '22 at 04:55
  • @DanielKaplan Hmm - You mean the wsl.exe -u root part? What happens if you run wsl -u root without the rest of it? I would expect that to essentially be the same as sudo -s, but without requiring a password. – NotTheDr01ds Aug 19 '22 at 19:24
  • ➜ ~ wsl.exe -u root\n User not found. FWIW, I'm running this in the ubuntu shell. – Daniel Kaplan Aug 22 '22 at 21:19
  • @DanielKaplan That's pretty odd. How about (when running inside Ubuntu) wsl.exe -d $WSL_DISTRO_NAME -u $USER? – NotTheDr01ds Aug 22 '22 at 23:27
  • That literally prints no output, but succeeds. The values of those variables are Ubuntu and the value of my whoami, respectively. The latter is different from "root". – Daniel Kaplan Aug 23 '22 at 02:24
  • @DanielKaplan Sorry, forgot to come back to this. Its sounding like you really may not have a root user in that instance (or it's named something else). Does less /etc/passwd show a root:x:0:0 ... (usually in the first position)? If not, is some other user else in there with a 0:0 in the third and fourth columns? – NotTheDr01ds Aug 30 '22 at 00:35
  • Yep, it's there like root:x:0:0... – Daniel Kaplan Aug 31 '22 at 02:32
1

This 1 liner will remove password request for sudo.

sudo sed -i 's|^%sudo.*$|%sudo    ALL=(ALL:ALL) NOPASSWD: ALL|' -i /etc/sudoers

This 1 liner will list drives not mounted.

echo -e "$(cmd.exe /C "wmic logicaldisk get name" 2>/dev/null | sed '1d;$d')\n$(mount | grep drvfs | sed 's|\(^.:\).*|\1|')" | tr '\r' ' ' |sed 's| ||g' | sort | uniq -u

Therefore we can define a function in ~/.bashrc or create a bash script to mount them. It is up to you if you call the function manually or add it to ~/.bashrc

mountext(){ # Find unmounted windows drives and mount them.
    ds=$(echo -e "$(cmd.exe /C "wmic logicaldisk get name" 2>/dev/null | sed '1d;$d')\n$(mount | grep drvfs | sed 's|\(^.:\).*|\1|')" | tr '\r' ' ' |sed 's| ||g' | sort | uniq -u | cut -c1)
    if [ ! $ds = $'\n' ] || [ ! -z "${ds}" ]; then
        for d in $ds; do
        sudo bash -c "mkdir -p /mnt/${d,} && mount -t drvfs ${d}: /mnt/${d,}"
        echo "Mounted drive ${d}: at /mnt/${d,}." 
        done
        else echo "No drives to mount."
    fi
}
5p0ng3b0b
  • 203
  • despite the message bash: [: too many arguments using mountext only, my drives get mounted – user1767316 Mar 29 '24 at 13:15
  • despite the message bash: [: too many arguments (some quotes may be missing somewhere) using mountext only, mounts al my drives. thanks – user1767316 Mar 29 '24 at 14:37
1

This command:

cmd.exe /C "wmic logicaldisk get name"

will show network mounts as well as drive letters that don't actually have media loaded. For example, I have a multiformat memory card reader that reserves five drive letters (one for each slot, e.g., SD, micro-SD, etc.), all of which are returned by this query even if no card is mounted. So I use the following to obtain local drive letters that have media in them:

powershell.exe -Command "gdr -PSProvider 'FileSystem'"|grep '[0-9]\+'|grep -o '[A-Z]:'

Assuming you have powershell.exe in your path and are running as sudo.

The first grep statement selects lines in the list of filesystems that show space available (i.e. a proxy for mounted media); the second grep just pulls out the drive letter at the end. You should be able to feed the output of this into the same script provided above for mounting as drvfs in WSL.

To put it all together, you could run this as a cronjob as root:

#!/bin/bash
ds=$(echo -e "$(powershell -Command "gdr -PSProvider 'FileSystem'"|grep '[0-9]\+'|grep -o '[A-Z]:' 2>/dev/null | sed '1d;$d')\n$(mount | grep drvfs | sed 's|\(^.:\).*|\1|')" | tr '\r' ' ' |sed 's| ||g' | sort | uniq -u | cut -c1)
if [ ! $ds = $'\n' ] || [ ! -z "${ds}" ]; then
  for d in $ds; do
    bash -c "mkdir -p /mnt/${d,} && mount -t drvfs ${d}: /mnt/${d,}"
    echo "Mounted drive ${d}: at /mnt/${d,}."
  done
else echo "No drives to mount."
fi