Skip to content

Plan9 file server is never started on WSL2 when both automount.enabled and automount.mountFsTab are false, making \\wsl.localhost inaccessible #41056

Description

@Lsseeker

Windows Version

Microsoft Windows [Version 10.0.19045.6466]

WSL Version

2.6.3.0 (the root-cause analysis below is based on current master, where the logic is unchanged)

Are you using WSL 1 or WSL 2?

  • WSL 2
  • WSL 1

Kernel Version

6.6.87.2-1

Distro Version

Ubuntu 24.04.2 LTS

Other Software

None

Repro Steps

  1. In a WSL2 distro, set /etc/wsl.conf to:
   [automount]
   enabled = false
   mountFsTab = false
  1. Run wsl --shutdown to apply the change.
  2. Start the distro again and try to access \\wsl.localhost\<distro> (or \\wsl$\<distro>) from Windows.

Expected Behavior

The Linux filesystem is accessible from Windows. Per the documentation, automount.enabled and automount.mountFsTab only control automatic mounting of Windows drives and the processing of /etc/fstab. No coupling between these options and the 9p file server is documented.

Actual Behavior

\\wsl.localhost\<distro> and \\wsl$\<distro> are inaccessible:

PS C:\Users\Administrator> ls \\wsl.localhost\Ubuntu\home
ls : Cannot find path '\\wsl.localhost\Ubuntu\home' because it does not exist.
    + CategoryInfo          : ObjectNotFound: (\\wsl.localhost\Ubuntu\home:String) [Get-ChildItem], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

(Original console output was localized; this is the equivalent English message.)

Inside the distro:

  • ps -ef | grep -i plan9 shows no plan9 process.
  • dmesg contains no plan9-related errors — init skips starting the server rather than failing to start it (see root cause below).
    Setting either of the two options back to true makes the plan9 process appear and restores \\wsl.localhost access.

Root cause

In ConfigInitializeInstance() (src/linux/init/config.cpp), the call to StartPlan9Server() is guarded by:

unsigned int Plan9Port = LX_INIT_UTILITY_VM_INVALID_PORT;
if ((WI_IsFlagClear(Config.FeatureFlags.value(), LxInitFeatureDisable9pServer)) && (Config.Plan9Enabled) &&
    (Config.AutoMount || Config.MountFsTab))
{
    std::tie(Plan9Port, Config.Plan9ControlChannel) = StartPlan9Server(Plan9SocketPath, Config);
}

When both AutoMount and MountFsTab are false, the branch is skipped entirely, and Plan9Port is sent back to wslservice as LX_INIT_UTILITY_VM_INVALID_PORT in the LX_INIT_CONFIGURATION_INFORMATION_RESPONSE, so there is no server for the Windows side (p9rdr.sys via wslservice.exe) to connect to.

The comment above this block explains the rationale for the guard: the plan9 server needs a DrvFs mount for its socket file, so either fstab or automount must be enabled for that mount to be available. That rationale only holds for WSL1. On WSL2 (utility VM mode), StartPlan9Server() (src/linux/init/plan9.cpp) takes the vsock branch and never touches the socket path at all:

if (UtilIsUtilityVm())
{
    sockaddr_vm address;
    server.reset(UtilBindVsockAnyPort(&address, (SOCK_STREAM | SOCK_NONBLOCK)));
    ...
    result = address.svm_port;
}
else
{
    // Translate the socket path (store a copy for unlinking on shutdown).
    translatedSocketPath = TranslatePath(const_cast<char*>(socketWindowsPath));
    // Create the server socket.
    server = CreateUnixServerSocket(translatedSocketPath.c_str());
}

So on WSL2 the server has no dependency on any DrvFs mount, and the guard is a WSL1-era constraint that unintentionally disables the 9p server on WSL2. Note also that a dedicated setting already exists for intentionally disabling the server (Config.Plan9Enabled), so whether the server runs shouldn't be implicitly decided by two mount-related options — a side effect that isn't documented anywhere.

Proposed fix

Exempt WSL2 from the mount requirement, keeping WSL1 behavior unchanged:

(UtilIsUtilityVm() || Config.AutoMount || Config.MountFsTab)

and update the comment to note that the DrvFs dependency only applies to WSL1. This is backward compatible: WSL1 behavior is unchanged, WSL2 is fixed.

Happy to submit a PR if this approach looks right.

Workaround

For anyone hitting this: keep mountFsTab = true together with enabled = false. As long as /etc/fstab contains no drvfs entries, mount -a is effectively a no-op, so no Windows drives get mounted, but the plan9 server starts normally.

Diagnostic Logs

An ETW trace was recorded with diagnostics/collect-wsl-logs.ps1 across a cold start of the distro (wsl --shutdown → start) followed by a failing \\wsl.localhost access. Since the repro above takes under a minute, I'm including only the relevant excerpt here for privacy; the full trace is available on request.

Excerpt from logs.etl, decoded with tracerpt logs.etl -o logs.csv -of CSV -y (provider WslCorePort). The hosts-file content embedded in the message is redacted and lines are re-wrapped for readability; otherwise unmodified:

SentMessage (wslservice → init):
  Header = {MessageType = LxInitMessageInitialize MessageSize = 591 SequenceNumber = 1}
  HostnameOffset = DESKTOP-DTB60UU  DomainnameOffset = localdomain
  WindowsHostsOffset = <redacted>
  Plan9SocketOffset =  TimezoneOffset = Asia/Taipei
  DrvFsVolumesBitmap = 12  DrvFsDefaultOwner = 1000  FeatureFlags = 0  DrvfsMount = 2
 
ReceivedMessage (init → wslservice):
  Header = {MessageType = LxInitMessageInitializeResponse MessageSize = 61 SequenceNumber = 2}
  Plan9Port = 4294967295  DefaultUid = 1000  InteropPort = 3162867010
  SystemdEnabled = 0  PidNamespace = 4026532221  FlavorIndex = ubuntu  VersionIndex = 24.04

Plan9Port = 4294967295 is 0xFFFFFFFF, i.e. LX_INIT_UTILITY_VM_INVALID_PORT. With FeatureFlags = 0 (LxInitFeatureDisable9pServer not set) and the 9p server left enabled (default) in /etc/wsl.conf, the only clause of the guard that can have skipped StartPlan9Server() is (Config.AutoMount || Config.MountFsTab) — consistent with the analysis above. Note also the empty Plan9SocketOffset, confirming that WSL2 does not use a socket path (it uses vsock).

(The trace also shows an invalid Plan9Port for the system distro, FlavorIndex = mariner — that one is expected: unlike the Ubuntu distro, its LxInitMessageInitialize carries non-zero FeatureFlags = 20, and the system distro is never exposed via \\wsl$.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions