Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Ensure you have the following requirements installed:
- Cargo (Rust's package manager)
- PowerShell (for running signing scripts)
- OpenSSL (used in the signing process) [Download OpenSSL](https://slproweb.com/products/Win32OpenSSL.html)
- **SignTool.exe** from the Windows SDK (for signing the plugin) [Download Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk/)
- **SignTool.exe** from the Windows SDK (for signing the plugin) [Download Windows SDK](https://developer.microsoft.com/windows/downloads/windows-10-sdk/)
- nuget.exe (for downloading [Microsoft's WSL Plugin API](https://www.nuget.org/packages/Microsoft.WSL.PluginApi))

### Important Notes
Expand Down
8 changes: 4 additions & 4 deletions wslplugins-macro-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pub(crate) mod generator;
pub(crate) mod hooks;
pub(crate) mod parser;
pub(crate) mod utils;
mod generator;
mod hooks;
mod parser;
mod utils;

use generator::generate;
use proc_macro2::TokenStream;
Expand Down
4 changes: 4 additions & 0 deletions wslplugins-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ optional = true
sys = []
log-full = ["log", "log-instrument"]
macro = ["dep:wslplugins-macro", "sys"]

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
106 changes: 101 additions & 5 deletions wslplugins-rs/src/api/api_v1.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
extern crate wslplugins_sys;
#[cfg(doc)]
use super::Error;
use super::Result;
use crate::api::errors::require_update_error::Result as UpReqResult;
use crate::utils::{cstring_from_str, encode_wide_null_terminated};
Expand All @@ -20,23 +22,56 @@ use windows::{
core::{Result as WinResult, GUID, PCSTR, PCWSTR},
Win32::Foundation::BOOL,
};
use wslplugins_sys::WSLVersion;

use wslplugins_sys::{WSLPluginAPIV1, WSLVersion};

use super::utils::check_required_version_result;
pub struct ApiV1<'a>(&'a wslplugins_sys::WSLPluginAPIV1);

impl<'a> From<&'a wslplugins_sys::WSLPluginAPIV1> for ApiV1<'a> {
fn from(value: &'a wslplugins_sys::WSLPluginAPIV1) -> Self {
/// Represents a structured interface for interacting with the WSLPluginAPIV1 API.
/// This struct encapsulates the methods provided by the WSLPluginAPIV1 API, allowing
/// idiomatic interaction with the Windows Subsystem for Linux (WSL).
pub struct ApiV1<'a>(&'a WSLPluginAPIV1);

/// Converts a raw reference to `WSLPluginAPIV1` into [ApiV1].
impl<'a> From<&'a WSLPluginAPIV1> for ApiV1<'a> {
fn from(value: &'a WSLPluginAPIV1) -> Self {
Self(value)
}
}

impl ApiV1<'_> {
/// Returns the current version of the WSL API being used.
///
/// This is useful for checking compatibility with specific API features.
///
/// # Example
/// ```ignore
/// let api_v1: ApiV1 = ...;
/// let version = api_v1.version();
/// println!(
/// "WSL API version: {}.{}.{}",
/// version.Major, version.Minor, version.Revision
/// );
#[cfg_attr(feature = "log-instrument", instrument)]
pub fn version(&self) -> &WSLVersion {
&self.0.Version
}

/// Create plan9 mount between Windows & Linux
/// Allows sharing a folder between the Windows host and the Linux environment.
///
/// # Arguments
/// - `session`: The current WSL session.
/// - `windows_path`: The Windows path of the folder to be mounted.
/// - `linux_path`: The Linux path where the folder will be mounted.
/// - `read_only`: Whether the mount should be read-only.
/// - `name`: A custom name for the mount.
///
/// # Example
/// ``` rust,ignore
/// api.mount_folder(&session, "C:\\path", "/mnt/path", false, "MyMount")?;
/// ```
#[doc(alias = "MountFolder")]
#[cfg_attr(feature = "log-instrument", instrument)]
pub fn mount_folder<WP: AsRef<Path>, UP: AsRef<Utf8UnixPath>>(
&self,
Expand Down Expand Up @@ -66,7 +101,36 @@ impl ApiV1<'_> {
}

/// Execute a program in the root namespace.
///
/// This method runs a program in the root namespace of the current WSL session. It connects the standard input and output
/// streams of the executed process to a [`TcpStream`], allowing interaction with the process.
///
/// # Arguments
/// - `session`: The current WSL session.
/// - `path`: Path to the program to execute.
/// - `args`: Arguments to pass to the program (including `arg0`).
///
/// # Returns
/// On success, this method returns a [`TcpStream`] connected to the standard input and output streams of the executed process.
/// - **Standard Input**: Data written to the stream will be sent to the process.
/// - **Standard Output**: Data output by the process will be readable from the stream.
///
/// # Errors
/// This method can return the following a [`windows::core::Error`]: If the underlying Windows API call fails.
///
/// # Example
/// ```rust,ignore
/// let stream = api.execute_binary(&session, "/bin/ls", ["/bin/ls", "-l", "/etc"])?;
/// // Write to the process (stdin)
/// writeln!(stream, "input data").unwrap();
///
/// // Read from the process (stdout)
/// let mut buffer = String::new();
/// stream.read_to_string(&mut buffer).unwrap();
/// println!("Process output: {}", buffer);
/// ```
#[cfg_attr(feature = "log-instrument", instrument)]
#[doc(alias = "ExecuteBinary")]
pub fn execute_binary<P: AsRef<Utf8UnixPath>>(
&self,
session: &WSLSessionInformation,
Expand Down Expand Up @@ -109,8 +173,39 @@ impl ApiV1<'_> {
let error_vec = encode_wide_null_terminated(error);
unsafe { self.0.PluginError.unwrap_unchecked()(PCWSTR::from_raw(error_vec.as_ptr())).ok() }
}

/// Execute a program in a user distribution
/// Introduced in 2.1.2
///
/// # Introduced
/// This requires API version 2.1.2 or later.
///
/// # Arguments
/// - `session`: The current WSL session.
/// - `distribution_id`: The ID of the target distribution.
/// - `path`: Path to the program to execute.
/// - `args`: Arguments to pass to the program (including arg0).
///
/// # Returns
/// A [`TcpStream`] connected to the process's stdin and stdout.
///
/// # Errors
/// This function may return the following errors:
///
/// - [`Error::RequiresUpdate`]: If the API version is lower than 2.1.2.
/// - [`Error::WinError`]: If the Windows API fails during execution.
///
/// # Example
/// ```rust,ignore
/// let stream = api.execute_binary_in_distribution(&session, "/bin/ls", ["/bin/ls", "-l", "/etc"])?;
/// // Write to the process (stdin)
/// writeln!(stream, "input data").unwrap();
///
/// // Read from the process (stdout)
/// let mut buffer = String::new();
/// stream.read_to_string(&mut buffer).unwrap();
/// println!("Process output: {}", buffer);
/// ```
#[doc(alias = "ExecuteBinaryInDistribution")]
#[cfg_attr(feature = "log-instrument", instrument)]
pub fn execute_binary_in_distribution<P: AsRef<Utf8UnixPath>>(
&self,
Expand Down Expand Up @@ -151,6 +246,7 @@ impl ApiV1<'_> {
};
Ok(stream)
}

fn check_required_version(&self, version: &WSLVersion) -> UpReqResult<()> {
check_required_version_result(self.version(), version)
}
Expand Down
32 changes: 32 additions & 0 deletions wslplugins-rs/src/api/errors.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
//! # Error Handling for WSL Plugins
//!
//! This module defines a unified error type for handling errors in WSL plugins, combining
//! both custom plugin-specific errors and Windows system errors. It provides seamless
//! interoperability with Windows APIs using `HRESULT`.

use thiserror::Error;
pub mod require_update_error;
pub use require_update_error::Error as RequireUpdateError;
use windows::core::{Error as WinError, HRESULT};
use wslplugins_sys::WSL_E_PLUGIN_REQUIRES_UPDATE;

/// A comprehensive error type for WSL plugins.
///
/// This enum encapsulates two main error categories:
/// - Plugin-specific errors (`RequireUpdateError`).
/// - Errors originating from Windows APIs (`WinError`).
#[derive(Debug, Error)]
pub enum Error {
/// Indicates that the current WSL version does not meet the required version.
#[error("Require Update Error")]
RequiresUpdate(#[from] RequireUpdateError),

/// Represents an error from Windows APIs.
#[error("Windows Error: {0}")]
WinError(#[from] WinError),
}

impl From<Error> for HRESULT {
/// Converts the `Error` enum into an `HRESULT` code.
///
/// - Maps `Error::RequiresUpdate` to `WSL_E_PLUGIN_REQUIRES_UPDATE`.
/// - Maps `Error::WinError` to its corresponding `HRESULT` code.
///
/// # Returns
/// An `HRESULT` code representing the error.
fn from(value: Error) -> Self {
match value {
Error::RequiresUpdate(err) => err.into(),
Expand All @@ -22,6 +43,13 @@ impl From<Error> for HRESULT {
}

impl From<Error> for WinError {
/// Converts the `Error` enum into a `WinError`.
///
/// - Maps `Error::RequiresUpdate` to `WSL_E_PLUGIN_REQUIRES_UPDATE`.
/// - Maps `Error::WinError` directly to itself.
///
/// # Returns
/// A `WinError` representing the error.
fn from(value: Error) -> Self {
match value {
Error::RequiresUpdate { .. } => WSL_E_PLUGIN_REQUIRES_UPDATE.into(),
Expand All @@ -30,4 +58,8 @@ impl From<Error> for WinError {
}
}

/// A type alias for results using the custom `Error` type.
///
/// This alias simplifies the return type for functions that may fail, ensuring consistency
/// across the codebase.
pub type Result<T> = std::result::Result<T, Error>;
34 changes: 34 additions & 0 deletions wslplugins-rs/src/api/errors/require_update_error.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
//! # WSL Plugin Error Handling
//!
//! This module provides an error type and result alias to handle scenarios where the current WSL version
//! does not meet the required version. It integrates with Windows error codes for seamless interop
//! with WSL APIs.

use thiserror::Error;
use windows::core::HRESULT;
use wslplugins_sys::{WSLVersion, WSL_E_PLUGIN_REQUIRES_UPDATE};

/// Represents an error when the current WSL version is unsupported.
///
/// This error is returned when the WSL version being used does not satisfy
/// the required version for a plugin.
///
/// # Fields
/// - `current_version`: The current WSL version.
/// - `required_version`: The required WSL version.
#[derive(Debug, Error)]
#[error("WSLVersion unsupported: current version {current_version}, required version {required_version}")]
pub struct Error {
/// The current version of WSL.
pub current_version: WSLVersion,
/// The required version of WSL.
pub required_version: WSLVersion,
}

impl Error {
/// Creates a new `Error` with the specified current and required WSL versions.
///
/// # Arguments
/// - `current_version`: The version currently in use.
/// - `required_version`: The version required for compatibility.
///
/// # Returns
/// A new instance of `Error`.
pub fn new(current_version: WSLVersion, required_version: WSLVersion) -> Self {
Self {
current_version,
Expand All @@ -19,9 +43,19 @@ impl Error {
}

impl From<Error> for HRESULT {
/// Converts the `Error` into an `HRESULT` error code.
///
/// This implementation maps the custom `Error` to the `WSL_E_PLUGIN_REQUIRES_UPDATE` HRESULT.
///
/// # Returns
/// - `[WSL_E_PLUGIN_REQUIRES_UPDATE]: Indicates the WSL version is insufficient for the plugin.
fn from(_: Error) -> Self {
WSL_E_PLUGIN_REQUIRES_UPDATE
}
}

/// A result type alias for operations that may return a WSL plugin error.
///
/// This alias provides convenience when working with operations that might fail due to unsupported
/// WSL versions.
pub type Result<T> = std::result::Result<T, Error>;
27 changes: 25 additions & 2 deletions wslplugins-rs/src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
pub mod api_v1;
//! # API Module
//!
//! This module provides the core functionality for interacting with the WSL plugin API.
//! It includes abstractions for API interactions, error handling, and utility functions.

mod api_v1;
pub mod errors;

/// The `ApiV1` struct provides an interface to interact with version 1 of the WSL Plugin API.
///
/// It encapsulates low-level interactions with the API and exposes a safe and idiomatic Rust interface.
pub use api_v1::ApiV1;
pub use errors::{Error, Result};

/// The `Error` type represents errors that may occur while using the WSL Plugin API.
///
/// This type encapsulates various error scenarios, providing a consistent and ergonomic way
/// to handle failures.
pub use errors::Error;

/// A specialized `Result` type for operations that may fail in the context of the WSL Plugin API.
///
/// This alias simplifies the function signatures by standardizing error handling.
pub use errors::Result;

/// The `utils` module provides utility functions and helpers for working with the WSL Plugin API.
///
/// These utilities simplify common tasks, such as version checking or string manipulation.
pub mod utils;
8 changes: 7 additions & 1 deletion wslplugins-rs/src/api/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use crate::WSLContext;
//! # WSL Version Checking Utilities
//!
//! This module provides utilities to verify that the current WSL version meets the required version for plugin compatibility.
//! It includes functions to perform version checks and unit tests to ensure correctness.

use super::errors::require_update_error::{Error, Result};
use crate::WSLContext;
use wslplugins_sys::WSLVersion;

pub(crate) fn check_required_version_result(
Expand Down Expand Up @@ -30,6 +34,7 @@ mod tests {
use super::*;
use wslplugins_sys::WSLVersion;

/// Tests that `check_required_version_result` returns `Ok` when the current version meets the requirement.
#[test]
fn test_check_required_version_result_ok() {
let current_version = WSLVersion::new(2, 1, 3);
Expand All @@ -40,6 +45,7 @@ mod tests {
assert!(result.is_ok());
}

/// Tests that `check_required_version_result` returns `Err` when the current version is insufficient.
#[test]
fn test_check_required_version_result_error() {
let current_version = WSLVersion::new(2, 1, 1);
Expand Down
Loading