Extend certain WSL workarounds to WSLv2

This updates is_windows_subsystem_for_linux() to take a WSL version to test for
(any, v1, or v2) and returns the boolean result depending on the system. I've
benchmarked and when running on regular Linux, this is still just as fast as the
previous binary check; it's only when it's WSL that this takes about 20ns
longer to figure out which variant.

Note that older WSLv2 kernels had a `-microsoft-standard` suffix while newer
ones appear to have a `-microsoft-standard-WSL2` suffix, so we make sure to test
for the least common denominator. (It doesn't matter to us, but note that newer
WSLv2 kernels have four dots in the version string!)

WSL workarounds pertaining to the default Windows terminal or executable
behavior of win32 binaries under a WSL shell are extended to WSLv2 while those
specific to oddities in kernel behavior are confined to WSLv1 only. (It
technically wouldn't hurt to extend them to WSLv2 but there's no good reason to
do so, either.)
This commit is contained in:
Mahmoud Al-Qudsi 2024-05-20 14:06:50 -05:00
parent 3374692b91
commit 8c62f733b3
5 changed files with 36 additions and 20 deletions

View file

@ -1081,7 +1081,7 @@ pub fn has_working_tty_timestamps() -> bool {
if cfg!(target_os = "windows") {
false
} else if cfg!(target_os = "linux") {
!is_windows_subsystem_for_linux()
!is_windows_subsystem_for_linux(WSL::V1)
} else {
true
}
@ -1462,7 +1462,7 @@ pub fn fish_setlocale() {
ELLIPSIS_STRING.store(LL!("..."));
}
if is_windows_subsystem_for_linux() {
if is_windows_subsystem_for_linux(WSL::Any) {
// neither of \u23CE and \u25CF can be displayed in the default fonts on Windows, though
// they can be *encoded* just fine. Use alternative glyphs.
OMITTED_NEWLINE_STR.store(LL!("\u{00b6}")); // "pilcrow"
@ -1675,12 +1675,20 @@ pub fn subslice_position<T: Eq>(a: &[T], b: &[T]) -> Option<usize> {
a.windows(b.len()).position(|aw| aw == b)
}
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
pub enum WSL {
Any,
V1,
V2,
}
/// Determines if we are running under Microsoft's Windows Subsystem for Linux to work around
/// some known limitations and/or bugs.
///
/// See https://github.com/Microsoft/WSL/issues/423 and Microsoft/WSL#2997
#[inline(always)]
#[cfg(not(target_os = "linux"))]
pub fn is_windows_subsystem_for_linux() -> bool {
pub fn is_windows_subsystem_for_linux(_: WSL) -> bool {
false
}
@ -1689,8 +1697,9 @@ pub fn is_windows_subsystem_for_linux() -> bool {
///
/// See https://github.com/Microsoft/WSL/issues/423 and Microsoft/WSL#2997
#[cfg(target_os = "linux")]
pub fn is_windows_subsystem_for_linux() -> bool {
static RESULT: once_cell::race::OnceBool = once_cell::race::OnceBool::new();
pub fn is_windows_subsystem_for_linux(v: WSL) -> bool {
use std::sync::OnceLock;
static RESULT: OnceLock<Option<WSL>> = OnceLock::new();
// This is called post-fork from [`report_setpgid_error()`], so the fast path must not involve
// any allocations or mutexes. We can't rely on all the std functions to be alloc-free in both
@ -1705,16 +1714,21 @@ pub fn is_windows_subsystem_for_linux() -> bool {
);
}
RESULT.get_or_init(|| {
let wsl = RESULT.get_or_init(|| {
let mut info: libc::utsname = unsafe { mem::zeroed() };
let release: &[u8] = unsafe {
libc::uname(&mut info);
std::mem::transmute(&info.release[..])
};
// Sample utsname.release under WSLv2, testing for something like `4.19.104-microsoft-standard`
// or `5.10.16.3-microsoft-standard-WSL2`
if slice_contains_slice(release, b"microsoft-standard") {
return Some(WSL::V2);
}
// Sample utsname.release under WSL, testing for something like `4.4.0-17763-Microsoft`
if !slice_contains_slice(release, b"Microsoft") {
return false;
return None;
}
let release: Vec<_> = release
@ -1726,9 +1740,9 @@ pub fn is_windows_subsystem_for_linux() -> bool {
.collect();
let build: Result<u32, _> = std::str::from_utf8(&release).unwrap().parse();
match build {
Ok(17763..) => return true,
Ok(_) => (), // return true, but first warn (see below)
_ => return false, // if parsing fails, assume this isn't WSL
Ok(17763..) => return Some(WSL::V1),
Ok(_) => (), // return true, but first warn (see below)
_ => return None, // if parsing fails, assume this isn't WSL
};
// #5298, #5661: There are acknowledged, published, and (later) fixed issues with
@ -1752,8 +1766,10 @@ pub fn is_windows_subsystem_for_linux() -> bool {
)
);
}
true
})
Some(WSL::V1)
});
wsl.map(|wsl| v == WSL::Any || wsl == v).unwrap_or(false)
}
/// Return true if the character is in a range reserved for fish's private use.

View file

@ -1,7 +1,7 @@
use libc::STDOUT_FILENO;
use crate::common::{
fish_reserved_codepoint, is_windows_subsystem_for_linux, read_blocked, shell_modes,
fish_reserved_codepoint, is_windows_subsystem_for_linux, read_blocked, shell_modes, WSL,
};
use crate::env::{EnvStack, Environment};
use crate::fd_readable_set::FdReadableSet;
@ -1042,7 +1042,7 @@ pub trait InputEventQueuer {
};
// Prevent signal starvation on WSL causing the `torn_escapes.py` test to fail
if is_windows_subsystem_for_linux() {
if is_windows_subsystem_for_linux(WSL::V1) {
// Merely querying the current thread's sigmask is sufficient to deliver a pending signal
let _ = unsafe { libc::pthread_sigmask(0, ptr::null(), &mut sigs) };
}

View file

@ -2,7 +2,7 @@
//! for testing if a command with a given name can be found in the PATH, and various other
//! path-related issues.
use crate::common::{is_windows_subsystem_for_linux as is_wsl, wcs2zstring};
use crate::common::{is_windows_subsystem_for_linux as is_wsl, wcs2zstring, WSL};
use crate::env::{EnvMode, EnvStack, Environment};
use crate::expand::{expand_tilde, HOME_DIRECTORY};
use crate::flog::{FLOG, FLOGF};
@ -313,7 +313,7 @@ fn path_get_path_core<S: AsRef<wstr>>(cmd: &wstr, pathsv: &[S]) -> GetPathResult
// any "normal" nix binaries under these paths, so we can skip them unless we are executing bins
// with Windows-ish names. We try to keep paths manually added to $fish_user_paths by only
// chopping off entries after the last "normal" PATH entry.
let pathsv = if is_wsl() && !cmd.contains('.') {
let pathsv = if is_wsl(WSL::Any) && !cmd.contains('.') {
let win_path_count = pathsv
.iter()
.rev()

View file

@ -1,4 +1,4 @@
use crate::common::{is_windows_subsystem_for_linux, str2wcstring, wcs2osstring, wcs2string};
use crate::common::{is_windows_subsystem_for_linux, str2wcstring, wcs2osstring, wcs2string, WSL};
use crate::env::{EnvMode, EnvStack};
use crate::history::{self, History, HistoryItem, HistorySearch, PathList, SearchDirection};
use crate::path::path_get_data;
@ -243,7 +243,7 @@ fn test_history_races_pound_on_history(item_count: usize, idx: usize) {
fn test_history_races() {
let _cleanup = test_init();
// This always fails under WSL
if is_windows_subsystem_for_linux() {
if is_windows_subsystem_for_linux(WSL::V1) {
return;
}

View file

@ -7,7 +7,7 @@ use std::fs;
use crate::common::{
char_offset, is_windows_subsystem_for_linux, unescape_string, UnescapeFlags,
UnescapeStringStyle, WILDCARD_RESERVED_BASE,
UnescapeStringStyle, WILDCARD_RESERVED_BASE, WSL,
};
use crate::complete::{CompleteFlags, Completion, CompletionReceiver, PROG_COMPLETE_SEP};
use crate::expand::ExpandFlags;
@ -373,7 +373,7 @@ fn wildcard_test_flags_then_complete(
}
if executables_only
&& is_windows_subsystem_for_linux()
&& is_windows_subsystem_for_linux(WSL::Any)
&& string_suffixes_string_case_insensitive(L!(".dll"), filename)
{
return false;