mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-03 11:18:31 +00:00
Merge branch 'main' of github.com:wasmerio/wasmer
This commit is contained in:
60
Cargo.lock
generated
60
Cargo.lock
generated
@@ -923,9 +923,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.4.0"
|
version = "1.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
|
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
]
|
]
|
||||||
@@ -976,7 +976,7 @@ version = "0.5.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
|
checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils 0.8.19",
|
"crossbeam-utils 0.8.20",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -997,7 +997,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-epoch 0.9.18",
|
"crossbeam-epoch 0.9.18",
|
||||||
"crossbeam-utils 0.8.19",
|
"crossbeam-utils 0.8.20",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1021,7 +1021,7 @@ version = "0.9.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils 0.8.19",
|
"crossbeam-utils 0.8.20",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1041,7 +1041,7 @@ version = "0.3.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
|
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils 0.8.19",
|
"crossbeam-utils 0.8.20",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1057,9 +1057,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.19"
|
version = "0.8.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm"
|
name = "crossterm"
|
||||||
@@ -2778,6 +2778,12 @@ dependencies = [
|
|||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lockfree-object-pool"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@@ -3806,7 +3812,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-deque 0.8.5",
|
"crossbeam-deque 0.8.5",
|
||||||
"crossbeam-utils 0.8.19",
|
"crossbeam-utils 0.8.20",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4680,6 +4686,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd-adler32"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simdutf8"
|
name = "simdutf8"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@@ -4999,18 +5011,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.60"
|
version = "1.0.61"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
|
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.60"
|
version = "1.0.61"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
|
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -7686,15 +7698,31 @@ checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zip"
|
name = "zip"
|
||||||
version = "1.2.3"
|
version = "2.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c700ea425e148de30c29c580c1f9508b93ca57ad31c9f4e96b83c194c37a7a8f"
|
checksum = "775a2b471036342aa69bc5a602bc889cb0a06cda00477d0c69566757d5553d39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"crossbeam-utils 0.8.19",
|
"crossbeam-utils 0.8.20",
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"flate2",
|
"flate2",
|
||||||
"indexmap 2.2.6",
|
"indexmap 2.2.6",
|
||||||
|
"memchr",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"zopfli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zopfli"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"crc32fast",
|
||||||
|
"lockfree-object-pool",
|
||||||
|
"log 0.4.21",
|
||||||
|
"once_cell",
|
||||||
|
"simd-adler32",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -146,3 +146,6 @@ in line as the events are generated
|
|||||||
Filters out a specific set of log events and drops the rest, this
|
Filters out a specific set of log events and drops the rest, this
|
||||||
capturer can be useful for restoring to a previous call point but
|
capturer can be useful for restoring to a previous call point but
|
||||||
retaining the memory changes (e.g. WCGI runner).
|
retaining the memory changes (e.g. WCGI runner).
|
||||||
|
|
||||||
|
## Improvements
|
||||||
|
https://codesandbox.io/blog/how-we-scale-our-microvm-infrastructure-using-low-latency-memory-decompression
|
||||||
@@ -164,7 +164,15 @@ pub async fn tag_package_release(
|
|||||||
.map(|r| r.tag_package_release)
|
.map(|r| r.tag_package_release)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the currently logged in used, together with all accessible namespaces.
|
/// Get the currently logged in user.
|
||||||
|
pub async fn current_user(client: &WasmerClient) -> Result<Option<types::User>, anyhow::Error> {
|
||||||
|
client
|
||||||
|
.run_graphql(types::GetCurrentUser::build(()))
|
||||||
|
.await
|
||||||
|
.map(|x| x.viewer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the currently logged in user, together with all accessible namespaces.
|
||||||
///
|
///
|
||||||
/// You can optionally filter the namespaces by the user role.
|
/// You can optionally filter the namespaces by the user role.
|
||||||
pub async fn current_user_with_namespaces(
|
pub async fn current_user_with_namespaces(
|
||||||
@@ -172,9 +180,9 @@ pub async fn current_user_with_namespaces(
|
|||||||
namespace_role: Option<types::GrapheneRole>,
|
namespace_role: Option<types::GrapheneRole>,
|
||||||
) -> Result<types::UserWithNamespaces, anyhow::Error> {
|
) -> Result<types::UserWithNamespaces, anyhow::Error> {
|
||||||
client
|
client
|
||||||
.run_graphql(types::GetCurrentUser::build(types::GetCurrentUserVars {
|
.run_graphql(types::GetCurrentUserWithNamespaces::build(
|
||||||
namespace_role,
|
types::GetCurrentUserWithNamespacesVars { namespace_role },
|
||||||
}))
|
))
|
||||||
.await?
|
.await?
|
||||||
.viewer
|
.viewer
|
||||||
.context("not logged in")
|
.context("not logged in")
|
||||||
@@ -544,9 +552,11 @@ pub async fn user_accessible_apps(
|
|||||||
|
|
||||||
// Get all aps in user-accessible namespaces.
|
// Get all aps in user-accessible namespaces.
|
||||||
let namespace_res = client
|
let namespace_res = client
|
||||||
.run_graphql(types::GetCurrentUser::build(types::GetCurrentUserVars {
|
.run_graphql(types::GetCurrentUserWithNamespaces::build(
|
||||||
namespace_role: None,
|
types::GetCurrentUserWithNamespacesVars {
|
||||||
}))
|
namespace_role: None,
|
||||||
|
},
|
||||||
|
))
|
||||||
.await?;
|
.await?;
|
||||||
let active_user = namespace_res.viewer.context("not logged in")?;
|
let active_user = namespace_res.viewer.context("not logged in")?;
|
||||||
let namespace_names = active_user
|
let namespace_names = active_user
|
||||||
@@ -655,9 +665,11 @@ pub async fn user_namespaces(
|
|||||||
client: &WasmerClient,
|
client: &WasmerClient,
|
||||||
) -> Result<Vec<types::Namespace>, anyhow::Error> {
|
) -> Result<Vec<types::Namespace>, anyhow::Error> {
|
||||||
let user = client
|
let user = client
|
||||||
.run_graphql(types::GetCurrentUser::build(types::GetCurrentUserVars {
|
.run_graphql(types::GetCurrentUserWithNamespaces::build(
|
||||||
namespace_role: None,
|
types::GetCurrentUserWithNamespacesVars {
|
||||||
}))
|
namespace_role: None,
|
||||||
|
},
|
||||||
|
))
|
||||||
.await?
|
.await?
|
||||||
.viewer
|
.viewer
|
||||||
.context("not logged in")?;
|
.context("not logged in")?;
|
||||||
|
|||||||
@@ -41,14 +41,20 @@ mod queries {
|
|||||||
Viewer,
|
Viewer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
#[cynic(graphql_type = "Query")]
|
||||||
|
pub struct GetCurrentUser {
|
||||||
|
pub viewer: Option<User>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(cynic::QueryVariables, Debug)]
|
#[derive(cynic::QueryVariables, Debug)]
|
||||||
pub struct GetCurrentUserVars {
|
pub struct GetCurrentUserWithNamespacesVars {
|
||||||
pub namespace_role: Option<GrapheneRole>,
|
pub namespace_role: Option<GrapheneRole>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(cynic::QueryFragment, Debug)]
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
#[cynic(graphql_type = "Query", variables = "GetCurrentUserVars")]
|
#[cynic(graphql_type = "Query", variables = "GetCurrentUserWithNamespacesVars")]
|
||||||
pub struct GetCurrentUser {
|
pub struct GetCurrentUserWithNamespaces {
|
||||||
pub viewer: Option<UserWithNamespaces>,
|
pub viewer: Option<UserWithNamespaces>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +453,7 @@ mod queries {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(cynic::QueryFragment, Debug, Clone, Serialize)]
|
#[derive(cynic::QueryFragment, Debug, Clone, Serialize)]
|
||||||
#[cynic(graphql_type = "User", variables = "GetCurrentUserVars")]
|
#[cynic(graphql_type = "User", variables = "GetCurrentUserWithNamespacesVars")]
|
||||||
pub struct UserWithNamespaces {
|
pub struct UserWithNamespaces {
|
||||||
pub id: cynic::Id,
|
pub id: cynic::Id,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ tun-tap = { version = "0.1.3", features = ["tokio"], optional = true }
|
|||||||
|
|
||||||
clap_complete = "4.5.2"
|
clap_complete = "4.5.2"
|
||||||
clap_mangen = "0.2.20"
|
clap_mangen = "0.2.20"
|
||||||
zip = { version = "1.2.3", default-features = false, features = ["deflate"] }
|
zip = { version = "2.1.3", default-features = false, features = ["deflate"] }
|
||||||
|
|
||||||
# NOTE: Must use different features for clap because the "color" feature does not
|
# NOTE: Must use different features for clap because the "color" feature does not
|
||||||
# work on wasi due to the anstream dependency not compiling.
|
# work on wasi due to the anstream dependency not compiling.
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ pub enum AppIdent {
|
|||||||
/// Backend app VERSION id like "dav_xxysw34234"
|
/// Backend app VERSION id like "dav_xxysw34234"
|
||||||
AppVersionId(String),
|
AppVersionId(String),
|
||||||
NamespacedName(String, String),
|
NamespacedName(String, String),
|
||||||
Alias(String),
|
Name(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppIdent {
|
impl AppIdent {
|
||||||
@@ -40,9 +40,18 @@ impl AppIdent {
|
|||||||
.with_context(|| format!("Could not query for app version id '{}'", id))?;
|
.with_context(|| format!("Could not query for app version id '{}'", id))?;
|
||||||
Ok(app)
|
Ok(app)
|
||||||
}
|
}
|
||||||
AppIdent::Alias(name) => wasmer_api::query::get_app_by_alias(client, name.clone())
|
AppIdent::Name(name) => {
|
||||||
.await?
|
// The API only allows to query by owner + name,
|
||||||
.with_context(|| format!("Could not find app with name '{name}'")),
|
// so default to the current user as the owner.
|
||||||
|
// To to so the username must first be retrieved.
|
||||||
|
let user = wasmer_api::query::current_user(client)
|
||||||
|
.await?
|
||||||
|
.context("not logged in")?;
|
||||||
|
|
||||||
|
wasmer_api::query::get_app(client, user.username, name.clone())
|
||||||
|
.await?
|
||||||
|
.with_context(|| format!("Could not find app with name '{name}'"))
|
||||||
|
}
|
||||||
AppIdent::NamespacedName(owner, name) => {
|
AppIdent::NamespacedName(owner, name) => {
|
||||||
wasmer_api::query::get_app(client, owner.clone(), name.clone())
|
wasmer_api::query::get_app(client, owner.clone(), name.clone())
|
||||||
.await?
|
.await?
|
||||||
@@ -80,7 +89,7 @@ impl std::str::FromStr for AppIdent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(Self::Alias(s.to_string()))
|
Ok(Self::Name(s.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,8 +169,10 @@ impl AppIdentOpts {
|
|||||||
|
|
||||||
let ident = if let Some(id) = &config.app_id {
|
let ident = if let Some(id) = &config.app_id {
|
||||||
AppIdent::AppId(id.clone())
|
AppIdent::AppId(id.clone())
|
||||||
|
} else if let Some(owner) = &config.owner {
|
||||||
|
AppIdent::NamespacedName(owner.clone(), config.name.clone())
|
||||||
} else {
|
} else {
|
||||||
AppIdent::Alias(config.name.clone())
|
AppIdent::Name(config.name.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ResolvedAppIdent::Config {
|
Ok(ResolvedAppIdent::Config {
|
||||||
@@ -197,7 +208,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
AppIdent::from_str("lala").unwrap(),
|
AppIdent::from_str("lala").unwrap(),
|
||||||
AppIdent::Alias("lala".to_string()),
|
AppIdent::Name("lala".to_string()),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ pub struct HttpRequest {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub method: Option<String>,
|
pub method: Option<String>,
|
||||||
|
|
||||||
|
/// HTTP headers added to the request.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub headers: Option<Vec<HttpHeader>>,
|
||||||
|
|
||||||
/// Request body as a string.
|
/// Request body as a string.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub body: Option<String>,
|
pub body: Option<String>,
|
||||||
@@ -26,6 +30,15 @@ pub struct HttpRequest {
|
|||||||
pub expect: Option<HttpRequestExpect>,
|
pub expect: Option<HttpRequestExpect>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Definition for an HTTP header.
|
||||||
|
#[derive(
|
||||||
|
schemars::JsonSchema, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Debug,
|
||||||
|
)]
|
||||||
|
pub struct HttpHeader {
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
/// Validation checks for an [`HttpRequest`].
|
/// Validation checks for an [`HttpRequest`].
|
||||||
#[derive(
|
#[derive(
|
||||||
schemars::JsonSchema, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Debug,
|
schemars::JsonSchema, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Debug,
|
||||||
|
|||||||
@@ -195,6 +195,13 @@ pub struct AppConfigCapabilityMapV1 {
|
|||||||
/// Enables app bootstrapping with startup snapshots.
|
/// Enables app bootstrapping with startup snapshots.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub instaboot: Option<AppConfigCapabilityInstaBootV1>,
|
pub instaboot: Option<AppConfigCapabilityInstaBootV1>,
|
||||||
|
|
||||||
|
/// Additional unknown capabilities.
|
||||||
|
///
|
||||||
|
/// This provides a small bit of forwards compatibility for newly added
|
||||||
|
/// capabilities.
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub other: HashMap<String, serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Memory capability settings.
|
/// Memory capability settings.
|
||||||
@@ -234,7 +241,17 @@ pub struct AppConfigCapabilityInstaBootV1 {
|
|||||||
///
|
///
|
||||||
/// NOTE: if no requests are configured, then a single HTTP
|
/// NOTE: if no requests are configured, then a single HTTP
|
||||||
/// request to '/' will be performed instead.
|
/// request to '/' will be performed instead.
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub requests: Vec<HttpRequest>,
|
pub requests: Vec<HttpRequest>,
|
||||||
|
|
||||||
|
/// Maximum age of snapshots.
|
||||||
|
///
|
||||||
|
/// Format: 5m, 1h, 2d, ...
|
||||||
|
///
|
||||||
|
/// After the specified time new snapshots will be created, and the old
|
||||||
|
/// ones discarded.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub max_age: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -29,16 +29,22 @@ pub async fn spawn_exec(
|
|||||||
env: WasiEnv,
|
env: WasiEnv,
|
||||||
runtime: &Arc<dyn Runtime + Send + Sync + 'static>,
|
runtime: &Arc<dyn Runtime + Send + Sync + 'static>,
|
||||||
) -> Result<TaskJoinHandle, SpawnError> {
|
) -> Result<TaskJoinHandle, SpawnError> {
|
||||||
// Load the WASM
|
|
||||||
let wasm = spawn_load_wasm(&env, &binary, name).await?;
|
|
||||||
|
|
||||||
// Load the module
|
|
||||||
let module = spawn_load_module(&env, name, wasm, runtime).await?;
|
|
||||||
|
|
||||||
// Spawn union the file system
|
|
||||||
spawn_union_fs(&env, &binary).await?;
|
spawn_union_fs(&env, &binary).await?;
|
||||||
|
|
||||||
// Now run the module
|
let wasm = spawn_load_wasm(&env, &binary, name).await?;
|
||||||
|
|
||||||
|
spawn_exec_wasm(wasm, name, env, runtime).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "trace", skip_all, fields(%name))]
|
||||||
|
pub async fn spawn_exec_wasm(
|
||||||
|
wasm: &[u8],
|
||||||
|
name: &str,
|
||||||
|
env: WasiEnv,
|
||||||
|
runtime: &Arc<dyn Runtime + Send + Sync + 'static>,
|
||||||
|
) -> Result<TaskJoinHandle, SpawnError> {
|
||||||
|
let module = spawn_load_module(&env, name, wasm, runtime).await?;
|
||||||
|
|
||||||
spawn_exec_module(module, env, runtime)
|
spawn_exec_module(module, env, runtime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use exec::spawn_exec_wasm;
|
||||||
use virtual_fs::{AsyncReadExt, FileSystem};
|
use virtual_fs::{AsyncReadExt, FileSystem};
|
||||||
use wasmer::FunctionEnvMut;
|
use wasmer::FunctionEnvMut;
|
||||||
use wasmer_wasix_types::wasi::Errno;
|
use wasmer_wasix_types::wasi::Errno;
|
||||||
@@ -52,54 +53,18 @@ impl BinFactory {
|
|||||||
cache.insert(name.to_string(), Some(binary));
|
cache.insert(name.to_string(), Some(binary));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove allow once BinFactory is refactored
|
|
||||||
// currently fine because a BinFactory is only used by a single process tree
|
|
||||||
#[allow(clippy::await_holding_lock)]
|
#[allow(clippy::await_holding_lock)]
|
||||||
pub async fn get_binary(
|
pub async fn get_binary(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
fs: Option<&dyn FileSystem>,
|
fs: Option<&dyn FileSystem>,
|
||||||
) -> Option<BinaryPackage> {
|
) -> Option<BinaryPackage> {
|
||||||
let name = name.to_string();
|
self.get_executable(name, fs)
|
||||||
|
.await
|
||||||
// Fast path
|
.and_then(|executable| match executable {
|
||||||
{
|
Executable::Wasm(_) => None,
|
||||||
let cache = self.local.read().unwrap();
|
Executable::BinaryPackage(pkg) => Some(pkg),
|
||||||
if let Some(data) = cache.get(&name) {
|
})
|
||||||
return data.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slow path
|
|
||||||
let mut cache = self.local.write().unwrap();
|
|
||||||
|
|
||||||
// Check the cache
|
|
||||||
if let Some(data) = cache.get(&name) {
|
|
||||||
return data.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the filesystem for the file
|
|
||||||
if name.starts_with('/') {
|
|
||||||
if let Some(fs) = fs {
|
|
||||||
match load_package_from_filesystem(fs, name.as_ref(), self.runtime()).await {
|
|
||||||
Ok(pkg) => {
|
|
||||||
cache.insert(name, Some(pkg.clone()));
|
|
||||||
return Some(pkg);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
tracing::warn!(
|
|
||||||
path = name,
|
|
||||||
error = &*e,
|
|
||||||
"Unable to load the package from disk"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NAK
|
|
||||||
cache.insert(name, None);
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn<'a>(
|
pub fn spawn<'a>(
|
||||||
@@ -111,7 +76,7 @@ impl BinFactory {
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
// Find the binary (or die trying) and make the spawn type
|
// Find the binary (or die trying) and make the spawn type
|
||||||
let res = self
|
let res = self
|
||||||
.get_binary(name.as_str(), Some(env.fs_root()))
|
.get_executable(name.as_str(), Some(env.fs_root()))
|
||||||
.await
|
.await
|
||||||
.ok_or_else(|| SpawnError::BinaryNotFound {
|
.ok_or_else(|| SpawnError::BinaryNotFound {
|
||||||
binary: name.clone(),
|
binary: name.clone(),
|
||||||
@@ -119,10 +84,17 @@ impl BinFactory {
|
|||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
env.on_exit(Some(Errno::Noent.into())).await;
|
env.on_exit(Some(Errno::Noent.into())).await;
|
||||||
}
|
}
|
||||||
let binary = res?;
|
let executable = res?;
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
spawn_exec(binary, name.as_str(), store, env, &self.runtime).await
|
match executable {
|
||||||
|
Executable::Wasm(bytes) => {
|
||||||
|
spawn_exec_wasm(&bytes, name.as_str(), env, &self.runtime).await
|
||||||
|
}
|
||||||
|
Executable::BinaryPackage(pkg) => {
|
||||||
|
spawn_exec(pkg, name.as_str(), store, env, &self.runtime).await
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,13 +117,71 @@ impl BinFactory {
|
|||||||
}
|
}
|
||||||
Err(SpawnError::BinaryNotFound { binary: name })
|
Err(SpawnError::BinaryNotFound { binary: name })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove allow once BinFactory is refactored
|
||||||
|
// currently fine because a BinFactory is only used by a single process tree
|
||||||
|
#[allow(clippy::await_holding_lock)]
|
||||||
|
pub async fn get_executable(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
fs: Option<&dyn FileSystem>,
|
||||||
|
) -> Option<Executable> {
|
||||||
|
let name = name.to_string();
|
||||||
|
|
||||||
|
// Fast path
|
||||||
|
{
|
||||||
|
let cache = self.local.read().unwrap();
|
||||||
|
if let Some(data) = cache.get(&name) {
|
||||||
|
data.clone().map(Executable::BinaryPackage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow path
|
||||||
|
let mut cache = self.local.write().unwrap();
|
||||||
|
|
||||||
|
// Check the cache
|
||||||
|
if let Some(data) = cache.get(&name) {
|
||||||
|
return data.clone().map(Executable::BinaryPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the filesystem for the file
|
||||||
|
if name.starts_with('/') {
|
||||||
|
if let Some(fs) = fs {
|
||||||
|
match load_executable_from_filesystem(fs, name.as_ref(), self.runtime()).await {
|
||||||
|
Ok(executable) => {
|
||||||
|
if let Executable::BinaryPackage(pkg) = &executable {
|
||||||
|
cache.insert(name, Some(pkg.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(executable);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!(
|
||||||
|
path = name,
|
||||||
|
error = &*e,
|
||||||
|
"Unable to load the package from disk"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NAK
|
||||||
|
cache.insert(name, None);
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_package_from_filesystem(
|
pub enum Executable {
|
||||||
|
Wasm(bytes::Bytes),
|
||||||
|
BinaryPackage(BinaryPackage),
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn load_executable_from_filesystem(
|
||||||
fs: &dyn FileSystem,
|
fs: &dyn FileSystem,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
rt: &(dyn Runtime + Send + Sync),
|
rt: &(dyn Runtime + Send + Sync),
|
||||||
) -> Result<BinaryPackage, anyhow::Error> {
|
) -> Result<Executable, anyhow::Error> {
|
||||||
let mut f = fs
|
let mut f = fs
|
||||||
.new_open_options()
|
.new_open_options()
|
||||||
.read(true)
|
.read(true)
|
||||||
@@ -161,10 +191,15 @@ async fn load_package_from_filesystem(
|
|||||||
let mut data = Vec::with_capacity(f.size() as usize);
|
let mut data = Vec::with_capacity(f.size() as usize);
|
||||||
f.read_to_end(&mut data).await.context("Read failed")?;
|
f.read_to_end(&mut data).await.context("Read failed")?;
|
||||||
|
|
||||||
let container = Container::from_bytes(data).context("Unable to parse the WEBC file")?;
|
let bytes: bytes::Bytes = data.into();
|
||||||
let pkg = BinaryPackage::from_webc(&container, rt)
|
|
||||||
.await
|
|
||||||
.context("Unable to load the package")?;
|
|
||||||
|
|
||||||
Ok(pkg)
|
if let Ok(container) = Container::from_bytes(bytes.clone()) {
|
||||||
|
let pkg = BinaryPackage::from_webc(&container, rt)
|
||||||
|
.await
|
||||||
|
.context("Unable to load the package")?;
|
||||||
|
|
||||||
|
Ok(Executable::BinaryPackage(pkg))
|
||||||
|
} else {
|
||||||
|
Ok(Executable::Wasm(bytes))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -531,6 +531,7 @@ fn wasix_exports_32(mut store: &mut impl AsStoreMut, env: &FunctionEnv<WasiEnv>)
|
|||||||
"proc_join" => Function::new_typed_with_env(&mut store, env, proc_join::<Memory32>),
|
"proc_join" => Function::new_typed_with_env(&mut store, env, proc_join::<Memory32>),
|
||||||
"proc_signal" => Function::new_typed_with_env(&mut store, env, proc_signal::<Memory32>),
|
"proc_signal" => Function::new_typed_with_env(&mut store, env, proc_signal::<Memory32>),
|
||||||
"proc_exec" => Function::new_typed_with_env(&mut store, env, proc_exec::<Memory32>),
|
"proc_exec" => Function::new_typed_with_env(&mut store, env, proc_exec::<Memory32>),
|
||||||
|
"proc_exec2" => Function::new_typed_with_env(&mut store, env, proc_exec2::<Memory32>),
|
||||||
"proc_raise" => Function::new_typed_with_env(&mut store, env, proc_raise),
|
"proc_raise" => Function::new_typed_with_env(&mut store, env, proc_raise),
|
||||||
"proc_raise_interval" => Function::new_typed_with_env(&mut store, env, proc_raise_interval),
|
"proc_raise_interval" => Function::new_typed_with_env(&mut store, env, proc_raise_interval),
|
||||||
"proc_spawn" => Function::new_typed_with_env(&mut store, env, proc_spawn::<Memory32>),
|
"proc_spawn" => Function::new_typed_with_env(&mut store, env, proc_spawn::<Memory32>),
|
||||||
@@ -652,6 +653,7 @@ fn wasix_exports_64(mut store: &mut impl AsStoreMut, env: &FunctionEnv<WasiEnv>)
|
|||||||
"proc_join" => Function::new_typed_with_env(&mut store, env, proc_join::<Memory64>),
|
"proc_join" => Function::new_typed_with_env(&mut store, env, proc_join::<Memory64>),
|
||||||
"proc_signal" => Function::new_typed_with_env(&mut store, env, proc_signal::<Memory64>),
|
"proc_signal" => Function::new_typed_with_env(&mut store, env, proc_signal::<Memory64>),
|
||||||
"proc_exec" => Function::new_typed_with_env(&mut store, env, proc_exec::<Memory64>),
|
"proc_exec" => Function::new_typed_with_env(&mut store, env, proc_exec::<Memory64>),
|
||||||
|
"proc_exec2" => Function::new_typed_with_env(&mut store, env, proc_exec2::<Memory64>),
|
||||||
"proc_raise" => Function::new_typed_with_env(&mut store, env, proc_raise),
|
"proc_raise" => Function::new_typed_with_env(&mut store, env, proc_raise),
|
||||||
"proc_raise_interval" => Function::new_typed_with_env(&mut store, env, proc_raise_interval),
|
"proc_raise_interval" => Function::new_typed_with_env(&mut store, env, proc_raise_interval),
|
||||||
"proc_spawn" => Function::new_typed_with_env(&mut store, env, proc_spawn::<Memory64>),
|
"proc_spawn" => Function::new_typed_with_env(&mut store, env, proc_spawn::<Memory64>),
|
||||||
|
|||||||
@@ -94,7 +94,10 @@ pub(crate) use self::types::{
|
|||||||
},
|
},
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
use self::{state::WasiInstanceGuardMemory, utils::WasiDummyWaker};
|
use self::{
|
||||||
|
state::{conv_env_vars, WasiInstanceGuardMemory},
|
||||||
|
utils::WasiDummyWaker,
|
||||||
|
};
|
||||||
pub(crate) use crate::os::task::{
|
pub(crate) use crate::os::task::{
|
||||||
process::{WasiProcessId, WasiProcessWait},
|
process::{WasiProcessId, WasiProcessWait},
|
||||||
thread::{WasiThread, WasiThreadId},
|
thread::{WasiThread, WasiThreadId},
|
||||||
@@ -1481,7 +1484,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function to prepare the WASI environment
|
// Function to prepare the WASI environment
|
||||||
pub(crate) fn _prepare_wasi(wasi_env: &mut WasiEnv, args: Option<Vec<String>>) {
|
pub(crate) fn _prepare_wasi(
|
||||||
|
wasi_env: &mut WasiEnv,
|
||||||
|
args: Option<Vec<String>>,
|
||||||
|
envs: Option<Vec<(String, String)>>,
|
||||||
|
) {
|
||||||
// Swap out the arguments with the new ones
|
// Swap out the arguments with the new ones
|
||||||
if let Some(args) = args {
|
if let Some(args) = args {
|
||||||
let mut wasi_state = wasi_env.state.fork();
|
let mut wasi_state = wasi_env.state.fork();
|
||||||
@@ -1489,6 +1496,38 @@ pub(crate) fn _prepare_wasi(wasi_env: &mut WasiEnv, args: Option<Vec<String>>) {
|
|||||||
wasi_env.state = Arc::new(wasi_state);
|
wasi_env.state = Arc::new(wasi_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the env vars
|
||||||
|
if let Some(envs) = envs {
|
||||||
|
let mut guard = wasi_env.state.envs.lock().unwrap();
|
||||||
|
|
||||||
|
let mut existing_envs = guard
|
||||||
|
.iter()
|
||||||
|
.map(|b| {
|
||||||
|
let string = String::from_utf8_lossy(b);
|
||||||
|
let (key, val) = string.split_once('=').expect("env var is malformed");
|
||||||
|
|
||||||
|
(key.to_string(), val.to_string().as_bytes().to_vec())
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for (key, val) in envs {
|
||||||
|
let val = val.as_bytes().to_vec();
|
||||||
|
match existing_envs
|
||||||
|
.iter_mut()
|
||||||
|
.find(|(existing_key, _)| existing_key == &key)
|
||||||
|
{
|
||||||
|
Some((_, existing_val)) => *existing_val = val,
|
||||||
|
None => existing_envs.push((key, val)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let envs = conv_env_vars(existing_envs);
|
||||||
|
|
||||||
|
*guard = envs;
|
||||||
|
|
||||||
|
drop(guard)
|
||||||
|
}
|
||||||
|
|
||||||
// Close any files after the STDERR that are not preopened
|
// Close any files after the STDERR that are not preopened
|
||||||
let close_fds = {
|
let close_fds = {
|
||||||
let preopen_fds = {
|
let preopen_fds = {
|
||||||
|
|||||||
@@ -205,6 +205,8 @@ pub(crate) fn path_open_internal(
|
|||||||
|
|
||||||
open_options.options(minimum_rights.clone());
|
open_options.options(minimum_rights.clone());
|
||||||
|
|
||||||
|
let orig_path = path;
|
||||||
|
|
||||||
let inode = if let Ok(inode) = maybe_inode {
|
let inode = if let Ok(inode) = maybe_inode {
|
||||||
// Happy path, we found the file we're trying to open
|
// Happy path, we found the file we're trying to open
|
||||||
let processing_inode = inode.clone();
|
let processing_inode = inode.clone();
|
||||||
@@ -228,7 +230,7 @@ pub(crate) fn path_open_internal(
|
|||||||
assert!(handle.is_some());
|
assert!(handle.is_some());
|
||||||
return Ok(Ok(*special_fd));
|
return Ok(Ok(*special_fd));
|
||||||
}
|
}
|
||||||
if o_flags.contains(Oflags::DIRECTORY) {
|
if o_flags.contains(Oflags::DIRECTORY) || orig_path.ends_with('/') {
|
||||||
return Ok(Err(Errno::Notdir));
|
return Ok(Err(Errno::Notdir));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,11 +332,13 @@ pub(crate) fn path_open_internal(
|
|||||||
// once we got the data we need from the parent, we lookup the host file
|
// once we got the data we need from the parent, we lookup the host file
|
||||||
// todo: extra check that opening with write access is okay
|
// todo: extra check that opening with write access is okay
|
||||||
let handle = {
|
let handle = {
|
||||||
|
// We set create_new because the path already didn't resolve to an existing file,
|
||||||
|
// so it must be created.
|
||||||
let open_options = open_options
|
let open_options = open_options
|
||||||
.read(minimum_rights.read)
|
.read(minimum_rights.read)
|
||||||
.append(minimum_rights.append)
|
.append(minimum_rights.append)
|
||||||
.write(minimum_rights.write)
|
.write(minimum_rights.write)
|
||||||
.create_new(minimum_rights.create_new);
|
.create_new(true);
|
||||||
|
|
||||||
if minimum_rights.read {
|
if minimum_rights.read {
|
||||||
open_flags |= Fd::READ;
|
open_flags |= Fd::READ;
|
||||||
@@ -349,9 +353,19 @@ pub(crate) fn path_open_internal(
|
|||||||
open_flags |= Fd::TRUNCATE;
|
open_flags |= Fd::TRUNCATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(wasi_try_ok_ok!(open_options
|
match open_options.open(&new_file_host_path) {
|
||||||
.open(&new_file_host_path)
|
Ok(handle) => Some(handle),
|
||||||
.map_err(|e| { fs_error_into_wasi_err(e) })))
|
Err(err) => {
|
||||||
|
// Even though the file does not exist, it still failed to create with
|
||||||
|
// `AlreadyExists` error. This can happen if the path resolves to a
|
||||||
|
// symlink that points outside the FS sandbox.
|
||||||
|
if err == FsError::AlreadyExists {
|
||||||
|
return Ok(Err(Errno::Perm));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(Err(fs_error_into_wasi_err(err)));
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_inode = {
|
let new_inode = {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ mod port_route_list;
|
|||||||
mod port_route_remove;
|
mod port_route_remove;
|
||||||
mod port_unbridge;
|
mod port_unbridge;
|
||||||
mod proc_exec;
|
mod proc_exec;
|
||||||
|
mod proc_exec2;
|
||||||
mod proc_fork;
|
mod proc_fork;
|
||||||
mod proc_id;
|
mod proc_id;
|
||||||
mod proc_join;
|
mod proc_join;
|
||||||
@@ -90,6 +91,7 @@ pub use port_route_list::*;
|
|||||||
pub use port_route_remove::*;
|
pub use port_route_remove::*;
|
||||||
pub use port_unbridge::*;
|
pub use port_unbridge::*;
|
||||||
pub use proc_exec::*;
|
pub use proc_exec::*;
|
||||||
|
pub use proc_exec2::*;
|
||||||
pub use proc_fork::*;
|
pub use proc_fork::*;
|
||||||
pub use proc_id::*;
|
pub use proc_id::*;
|
||||||
pub use proc_join::*;
|
pub use proc_join::*;
|
||||||
|
|||||||
@@ -25,230 +25,13 @@ pub fn proc_exec<M: MemorySize>(
|
|||||||
args: WasmPtr<u8, M>,
|
args: WasmPtr<u8, M>,
|
||||||
args_len: M::Offset,
|
args_len: M::Offset,
|
||||||
) -> Result<(), WasiError> {
|
) -> Result<(), WasiError> {
|
||||||
WasiEnv::process_signals_and_exit(&mut ctx)?;
|
proc_exec2(
|
||||||
|
ctx,
|
||||||
// If we were just restored the stack then we were woken after a deep sleep
|
name,
|
||||||
if let Some(exit_code) = unsafe { handle_rewind::<M, i32>(&mut ctx) } {
|
name_len,
|
||||||
// We should never get here as the process will be termined
|
args,
|
||||||
// in the `WasiEnv::process_signals_and_exit()` call
|
args_len,
|
||||||
let exit_code = ExitCode::from_native(exit_code);
|
WasmPtr::null(),
|
||||||
ctx.data().process.terminate(exit_code);
|
M::ZERO,
|
||||||
return Err(WasiError::Exit(exit_code));
|
)
|
||||||
}
|
|
||||||
|
|
||||||
let memory = unsafe { ctx.data().memory_view(&ctx) };
|
|
||||||
let mut name = name.read_utf8_string(&memory, name_len).map_err(|err| {
|
|
||||||
warn!("failed to execve as the name could not be read - {}", err);
|
|
||||||
WasiError::Exit(Errno::Inval.into())
|
|
||||||
})?;
|
|
||||||
Span::current().record("name", name.as_str());
|
|
||||||
let args = args.read_utf8_string(&memory, args_len).map_err(|err| {
|
|
||||||
warn!("failed to execve as the args could not be read - {}", err);
|
|
||||||
WasiError::Exit(Errno::Inval.into())
|
|
||||||
})?;
|
|
||||||
let args: Vec<_> = args
|
|
||||||
.split(&['\n', '\r'])
|
|
||||||
.map(|a| a.to_string())
|
|
||||||
.filter(|a| !a.is_empty())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Convert relative paths into absolute paths
|
|
||||||
if name.starts_with("./") {
|
|
||||||
name = ctx.data().state.fs.relative_path_to_absolute(name);
|
|
||||||
}
|
|
||||||
trace!(name);
|
|
||||||
|
|
||||||
// Convert the preopen directories
|
|
||||||
let preopen = ctx.data().state.preopen.clone();
|
|
||||||
|
|
||||||
// Get the current working directory
|
|
||||||
let (_, cur_dir) = {
|
|
||||||
let (memory, state, inodes) =
|
|
||||||
unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
|
|
||||||
match state.fs.get_current_dir(inodes, crate::VIRTUAL_ROOT_FD) {
|
|
||||||
Ok(a) => a,
|
|
||||||
Err(err) => {
|
|
||||||
warn!("failed to create subprocess for fork - {}", err);
|
|
||||||
return Err(WasiError::Exit(err.into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_store = ctx.data().runtime.new_store();
|
|
||||||
|
|
||||||
// If we are in a vfork we need to first spawn a subprocess of this type
|
|
||||||
// with the forked WasiEnv, then do a longjmp back to the vfork point.
|
|
||||||
if let Some(mut vfork) = ctx.data_mut().vfork.take() {
|
|
||||||
// We will need the child pid later
|
|
||||||
let child_process = ctx.data().process.clone();
|
|
||||||
let child_pid = child_process.pid();
|
|
||||||
let child_finished = child_process.finished;
|
|
||||||
|
|
||||||
// Restore the WasiEnv to the point when we vforked
|
|
||||||
vfork.env.swap_inner(ctx.data_mut());
|
|
||||||
std::mem::swap(vfork.env.as_mut(), ctx.data_mut());
|
|
||||||
let mut wasi_env = *vfork.env;
|
|
||||||
wasi_env.owned_handles.push(vfork.handle);
|
|
||||||
_prepare_wasi(&mut wasi_env, Some(args));
|
|
||||||
|
|
||||||
// Recrod the stack offsets before we give up ownership of the wasi_env
|
|
||||||
let stack_lower = wasi_env.layout.stack_lower;
|
|
||||||
let stack_upper = wasi_env.layout.stack_upper;
|
|
||||||
|
|
||||||
// Spawn a new process with this current execution environment
|
|
||||||
let mut err_exit_code: ExitCode = Errno::Success.into();
|
|
||||||
|
|
||||||
{
|
|
||||||
let bin_factory = Box::new(ctx.data().bin_factory.clone());
|
|
||||||
let tasks = wasi_env.tasks().clone();
|
|
||||||
|
|
||||||
let mut new_store = Some(new_store);
|
|
||||||
let mut config = Some(wasi_env);
|
|
||||||
|
|
||||||
match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut new_store, &mut config) {
|
|
||||||
Ok(a) => {}
|
|
||||||
Err(err) => {
|
|
||||||
if !err.is_not_found() {
|
|
||||||
error!("builtin failed - {}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_store = new_store.take().unwrap();
|
|
||||||
let env = config.take().unwrap();
|
|
||||||
|
|
||||||
let name_inner = name.clone();
|
|
||||||
__asyncify_light(ctx.data(), None, async {
|
|
||||||
let ret = bin_factory.spawn(name_inner, new_store, env).await;
|
|
||||||
match ret {
|
|
||||||
Ok(ret) => {
|
|
||||||
trace!(%child_pid, "spawned sub-process");
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
err_exit_code = conv_spawn_err_to_exit_code(&err);
|
|
||||||
|
|
||||||
debug!(%child_pid, "process failed with (err={})", err_exit_code);
|
|
||||||
child_finished.set_finished(Ok(err_exit_code));
|
|
||||||
|
|
||||||
warn!(
|
|
||||||
"failed to execve as the process could not be spawned (vfork) - {}",
|
|
||||||
err
|
|
||||||
);
|
|
||||||
let _ = unsafe {
|
|
||||||
stderr_write(
|
|
||||||
&ctx,
|
|
||||||
format!(
|
|
||||||
"wasm execute failed [{}] - {}\n",
|
|
||||||
name.as_str(),
|
|
||||||
err
|
|
||||||
)
|
|
||||||
.as_bytes(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Jump back to the vfork point and current on execution
|
|
||||||
// note: fork does not return any values hence passing `()`
|
|
||||||
let memory_stack = vfork.memory_stack.freeze();
|
|
||||||
let rewind_stack = vfork.rewind_stack.freeze();
|
|
||||||
let store_data = vfork.store_data;
|
|
||||||
unwind::<M, _>(ctx, move |mut ctx, _, _| {
|
|
||||||
// Rewind the stack
|
|
||||||
match rewind::<M, _>(
|
|
||||||
ctx,
|
|
||||||
memory_stack,
|
|
||||||
rewind_stack,
|
|
||||||
store_data,
|
|
||||||
ForkResult {
|
|
||||||
pid: child_pid.raw() as Pid,
|
|
||||||
ret: Errno::Success,
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Errno::Success => OnCalledAction::InvokeAgain,
|
|
||||||
err => {
|
|
||||||
warn!("fork failed - could not rewind the stack - errno={}", err);
|
|
||||||
OnCalledAction::Trap(Box::new(WasiError::Exit(err.into())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
// Otherwise we need to unwind the stack to get out of the current executing
|
|
||||||
// callstack, steal the memory/WasiEnv and switch it over to a new thread
|
|
||||||
// on the new module
|
|
||||||
else {
|
|
||||||
// Prepare the environment
|
|
||||||
let mut wasi_env = ctx.data().clone();
|
|
||||||
_prepare_wasi(&mut wasi_env, Some(args));
|
|
||||||
|
|
||||||
// Get a reference to the runtime
|
|
||||||
let bin_factory = ctx.data().bin_factory.clone();
|
|
||||||
let tasks = wasi_env.tasks().clone();
|
|
||||||
|
|
||||||
// Create the process and drop the context
|
|
||||||
let bin_factory = Box::new(ctx.data().bin_factory.clone());
|
|
||||||
|
|
||||||
let mut new_store = Some(new_store);
|
|
||||||
let mut builder = Some(wasi_env);
|
|
||||||
|
|
||||||
let process = match bin_factory.try_built_in(
|
|
||||||
name.clone(),
|
|
||||||
Some(&ctx),
|
|
||||||
&mut new_store,
|
|
||||||
&mut builder,
|
|
||||||
) {
|
|
||||||
Ok(a) => Ok(a),
|
|
||||||
Err(err) => {
|
|
||||||
if !err.is_not_found() {
|
|
||||||
error!("builtin failed - {}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_store = new_store.take().unwrap();
|
|
||||||
let env = builder.take().unwrap();
|
|
||||||
|
|
||||||
// Spawn a new process with this current execution environment
|
|
||||||
InlineWaker::block_on(bin_factory.spawn(name, new_store, env))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match process {
|
|
||||||
Ok(mut process) => {
|
|
||||||
// If we support deep sleeping then we switch to deep sleep mode
|
|
||||||
let env = ctx.data();
|
|
||||||
let thread = env.thread.clone();
|
|
||||||
|
|
||||||
// The poller will wait for the process to actually finish
|
|
||||||
let res = __asyncify_with_deep_sleep::<M, _, _>(ctx, async move {
|
|
||||||
process
|
|
||||||
.wait_finished()
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|_| Errno::Child.into())
|
|
||||||
.to_native()
|
|
||||||
})?;
|
|
||||||
match res {
|
|
||||||
AsyncifyAction::Finish(mut ctx, result) => {
|
|
||||||
// When we arrive here the process should already be terminated
|
|
||||||
let exit_code = ExitCode::from_native(result);
|
|
||||||
ctx.data().process.terminate(exit_code);
|
|
||||||
WasiEnv::process_signals_and_exit(&mut ctx)?;
|
|
||||||
Err(WasiError::Exit(Errno::Unknown.into()))
|
|
||||||
}
|
|
||||||
AsyncifyAction::Unwind => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(
|
|
||||||
"failed to execve as the process could not be spawned (fork)[0] - {}",
|
|
||||||
err
|
|
||||||
);
|
|
||||||
Err(WasiError::Exit(Errno::Noexec.into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
280
lib/wasix/src/syscalls/wasix/proc_exec2.rs
Normal file
280
lib/wasix/src/syscalls/wasix/proc_exec2.rs
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
use wasmer::FromToNativeWasmType;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::{
|
||||||
|
os::task::{OwnedTaskStatus, TaskStatus},
|
||||||
|
syscalls::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Replaces the current process with a new process
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
///
|
||||||
|
/// * `name` - Name of the process to be spawned
|
||||||
|
/// * `args` - List of the arguments to pass the process
|
||||||
|
/// (entries are separated by line feeds)
|
||||||
|
/// * `envs` - List of the environment variables to pass process
|
||||||
|
///
|
||||||
|
/// ## Return
|
||||||
|
///
|
||||||
|
/// Returns a bus process id that can be used to invoke calls
|
||||||
|
#[instrument(level = "debug", skip_all, fields(name = field::Empty, %args_len), ret)]
|
||||||
|
pub fn proc_exec2<M: MemorySize>(
|
||||||
|
mut ctx: FunctionEnvMut<'_, WasiEnv>,
|
||||||
|
name: WasmPtr<u8, M>,
|
||||||
|
name_len: M::Offset,
|
||||||
|
args: WasmPtr<u8, M>,
|
||||||
|
args_len: M::Offset,
|
||||||
|
envs: WasmPtr<u8, M>,
|
||||||
|
envs_len: M::Offset,
|
||||||
|
) -> Result<(), WasiError> {
|
||||||
|
WasiEnv::process_signals_and_exit(&mut ctx)?;
|
||||||
|
|
||||||
|
// If we were just restored the stack then we were woken after a deep sleep
|
||||||
|
if let Some(exit_code) = unsafe { handle_rewind::<M, i32>(&mut ctx) } {
|
||||||
|
// We should never get here as the process will be termined
|
||||||
|
// in the `WasiEnv::process_signals_and_exit()` call
|
||||||
|
let exit_code = ExitCode::from_native(exit_code);
|
||||||
|
ctx.data().process.terminate(exit_code);
|
||||||
|
return Err(WasiError::Exit(exit_code));
|
||||||
|
}
|
||||||
|
|
||||||
|
let memory = unsafe { ctx.data().memory_view(&ctx) };
|
||||||
|
let mut name = name.read_utf8_string(&memory, name_len).map_err(|err| {
|
||||||
|
warn!("failed to execve as the name could not be read - {}", err);
|
||||||
|
WasiError::Exit(Errno::Inval.into())
|
||||||
|
})?;
|
||||||
|
Span::current().record("name", name.as_str());
|
||||||
|
let args = args.read_utf8_string(&memory, args_len).map_err(|err| {
|
||||||
|
warn!("failed to execve as the args could not be read - {}", err);
|
||||||
|
WasiError::Exit(Errno::Inval.into())
|
||||||
|
})?;
|
||||||
|
let args: Vec<_> = args
|
||||||
|
.split(&['\n', '\r'])
|
||||||
|
.map(|a| a.to_string())
|
||||||
|
.filter(|a| !a.is_empty())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let envs = if !envs.is_null() {
|
||||||
|
let envs = envs.read_utf8_string(&memory, envs_len).map_err(|err| {
|
||||||
|
warn!("failed to execve as the envs could not be read - {}", err);
|
||||||
|
WasiError::Exit(Errno::Inval.into())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let envs = envs
|
||||||
|
.split(&['\n', '\r'])
|
||||||
|
.map(|a| a.to_string())
|
||||||
|
.filter(|a| !a.is_empty());
|
||||||
|
|
||||||
|
let mut vec = vec![];
|
||||||
|
for env in envs {
|
||||||
|
let (key, value) = env.split_once('=').unwrap();
|
||||||
|
|
||||||
|
vec.push((key.to_string(), value.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(vec)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert relative paths into absolute paths
|
||||||
|
if name.starts_with("./") {
|
||||||
|
name = ctx.data().state.fs.relative_path_to_absolute(name);
|
||||||
|
}
|
||||||
|
trace!(name);
|
||||||
|
|
||||||
|
// Convert the preopen directories
|
||||||
|
let preopen = ctx.data().state.preopen.clone();
|
||||||
|
|
||||||
|
// Get the current working directory
|
||||||
|
let (_, cur_dir) = {
|
||||||
|
let (memory, state, inodes) =
|
||||||
|
unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
|
||||||
|
match state.fs.get_current_dir(inodes, crate::VIRTUAL_ROOT_FD) {
|
||||||
|
Ok(a) => a,
|
||||||
|
Err(err) => {
|
||||||
|
warn!("failed to create subprocess for fork - {}", err);
|
||||||
|
return Err(WasiError::Exit(err.into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_store = ctx.data().runtime.new_store();
|
||||||
|
|
||||||
|
// If we are in a vfork we need to first spawn a subprocess of this type
|
||||||
|
// with the forked WasiEnv, then do a longjmp back to the vfork point.
|
||||||
|
if let Some(mut vfork) = ctx.data_mut().vfork.take() {
|
||||||
|
// We will need the child pid later
|
||||||
|
let child_process = ctx.data().process.clone();
|
||||||
|
let child_pid = child_process.pid();
|
||||||
|
let child_finished = child_process.finished;
|
||||||
|
|
||||||
|
// Restore the WasiEnv to the point when we vforked
|
||||||
|
vfork.env.swap_inner(ctx.data_mut());
|
||||||
|
std::mem::swap(vfork.env.as_mut(), ctx.data_mut());
|
||||||
|
let mut wasi_env = *vfork.env;
|
||||||
|
wasi_env.owned_handles.push(vfork.handle);
|
||||||
|
_prepare_wasi(&mut wasi_env, Some(args), envs);
|
||||||
|
|
||||||
|
// Recrod the stack offsets before we give up ownership of the wasi_env
|
||||||
|
let stack_lower = wasi_env.layout.stack_lower;
|
||||||
|
let stack_upper = wasi_env.layout.stack_upper;
|
||||||
|
|
||||||
|
// Spawn a new process with this current execution environment
|
||||||
|
let mut err_exit_code: ExitCode = Errno::Success.into();
|
||||||
|
|
||||||
|
{
|
||||||
|
let bin_factory = Box::new(ctx.data().bin_factory.clone());
|
||||||
|
let tasks = wasi_env.tasks().clone();
|
||||||
|
|
||||||
|
let mut new_store = Some(new_store);
|
||||||
|
let mut config = Some(wasi_env);
|
||||||
|
|
||||||
|
match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut new_store, &mut config) {
|
||||||
|
Ok(a) => {}
|
||||||
|
Err(err) => {
|
||||||
|
if !err.is_not_found() {
|
||||||
|
error!("builtin failed - {}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_store = new_store.take().unwrap();
|
||||||
|
let env = config.take().unwrap();
|
||||||
|
|
||||||
|
let name_inner = name.clone();
|
||||||
|
__asyncify_light(ctx.data(), None, async {
|
||||||
|
let ret = bin_factory.spawn(name_inner, new_store, env).await;
|
||||||
|
match ret {
|
||||||
|
Ok(ret) => {
|
||||||
|
trace!(%child_pid, "spawned sub-process");
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
err_exit_code = conv_spawn_err_to_exit_code(&err);
|
||||||
|
|
||||||
|
debug!(%child_pid, "process failed with (err={})", err_exit_code);
|
||||||
|
child_finished.set_finished(Ok(err_exit_code));
|
||||||
|
|
||||||
|
warn!(
|
||||||
|
"failed to execve as the process could not be spawned (vfork) - {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
let _ = unsafe {
|
||||||
|
stderr_write(
|
||||||
|
&ctx,
|
||||||
|
format!(
|
||||||
|
"wasm execute failed [{}] - {}\n",
|
||||||
|
name.as_str(),
|
||||||
|
err
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Jump back to the vfork point and current on execution
|
||||||
|
// note: fork does not return any values hence passing `()`
|
||||||
|
let memory_stack = vfork.memory_stack.freeze();
|
||||||
|
let rewind_stack = vfork.rewind_stack.freeze();
|
||||||
|
let store_data = vfork.store_data;
|
||||||
|
unwind::<M, _>(ctx, move |mut ctx, _, _| {
|
||||||
|
// Rewind the stack
|
||||||
|
match rewind::<M, _>(
|
||||||
|
ctx,
|
||||||
|
memory_stack,
|
||||||
|
rewind_stack,
|
||||||
|
store_data,
|
||||||
|
ForkResult {
|
||||||
|
pid: child_pid.raw() as Pid,
|
||||||
|
ret: Errno::Success,
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Errno::Success => OnCalledAction::InvokeAgain,
|
||||||
|
err => {
|
||||||
|
warn!("fork failed - could not rewind the stack - errno={}", err);
|
||||||
|
OnCalledAction::Trap(Box::new(WasiError::Exit(err.into())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
// Otherwise we need to unwind the stack to get out of the current executing
|
||||||
|
// callstack, steal the memory/WasiEnv and switch it over to a new thread
|
||||||
|
// on the new module
|
||||||
|
else {
|
||||||
|
// Prepare the environment
|
||||||
|
let mut wasi_env = ctx.data().clone();
|
||||||
|
_prepare_wasi(&mut wasi_env, Some(args), envs);
|
||||||
|
|
||||||
|
// Get a reference to the runtime
|
||||||
|
let bin_factory = ctx.data().bin_factory.clone();
|
||||||
|
let tasks = wasi_env.tasks().clone();
|
||||||
|
|
||||||
|
// Create the process and drop the context
|
||||||
|
let bin_factory = Box::new(ctx.data().bin_factory.clone());
|
||||||
|
|
||||||
|
let mut new_store = Some(new_store);
|
||||||
|
let mut builder = Some(wasi_env);
|
||||||
|
|
||||||
|
let process = match bin_factory.try_built_in(
|
||||||
|
name.clone(),
|
||||||
|
Some(&ctx),
|
||||||
|
&mut new_store,
|
||||||
|
&mut builder,
|
||||||
|
) {
|
||||||
|
Ok(a) => Ok(a),
|
||||||
|
Err(err) => {
|
||||||
|
if !err.is_not_found() {
|
||||||
|
error!("builtin failed - {}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_store = new_store.take().unwrap();
|
||||||
|
let env = builder.take().unwrap();
|
||||||
|
|
||||||
|
// Spawn a new process with this current execution environment
|
||||||
|
InlineWaker::block_on(bin_factory.spawn(name, new_store, env))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match process {
|
||||||
|
Ok(mut process) => {
|
||||||
|
// If we support deep sleeping then we switch to deep sleep mode
|
||||||
|
let env = ctx.data();
|
||||||
|
let thread = env.thread.clone();
|
||||||
|
|
||||||
|
// The poller will wait for the process to actually finish
|
||||||
|
let res = __asyncify_with_deep_sleep::<M, _, _>(ctx, async move {
|
||||||
|
process
|
||||||
|
.wait_finished()
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|_| Errno::Child.into())
|
||||||
|
.to_native()
|
||||||
|
})?;
|
||||||
|
match res {
|
||||||
|
AsyncifyAction::Finish(mut ctx, result) => {
|
||||||
|
// When we arrive here the process should already be terminated
|
||||||
|
let exit_code = ExitCode::from_native(result);
|
||||||
|
ctx.data().process.terminate(exit_code);
|
||||||
|
WasiEnv::process_signals_and_exit(&mut ctx)?;
|
||||||
|
Err(WasiError::Exit(Errno::Unknown.into()))
|
||||||
|
}
|
||||||
|
AsyncifyAction::Unwind => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!(
|
||||||
|
"failed to execve as the process could not be spawned (fork)[0] - {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
Err(WasiError::Exit(Errno::Noexec.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
57
tests/wasi-fyi/fs_open_trailing_slash.rs
Normal file
57
tests/wasi-fyi/fs_open_trailing_slash.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#[link(wasm_import_module = "wasi_snapshot_preview1")]
|
||||||
|
extern "C" {
|
||||||
|
pub fn path_open(
|
||||||
|
fd: i32,
|
||||||
|
dirflags: i32,
|
||||||
|
path: i32,
|
||||||
|
path_len: i32,
|
||||||
|
oflags: i32,
|
||||||
|
fs_rights_base: i64,
|
||||||
|
fs_rights_inheriting: i64,
|
||||||
|
fdflags: i32,
|
||||||
|
result_fd: i32,
|
||||||
|
) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ERRNO_SUCCESS: i32 = 0;
|
||||||
|
const ERRNO_NOTDIR: i32 = 54;
|
||||||
|
const RIGHTS_FD_READ: i64 = 2;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
unsafe {
|
||||||
|
let fd = 5;
|
||||||
|
let path_ok = "fyi/fs_open_trailing_slash.dir/file";
|
||||||
|
let path_bad = "fyi/fs_open_trailing_slash.dir/file/";
|
||||||
|
let errno = path_open(
|
||||||
|
fd,
|
||||||
|
0,
|
||||||
|
path_ok.as_ptr() as i32,
|
||||||
|
path_ok.len() as i32,
|
||||||
|
0,
|
||||||
|
RIGHTS_FD_READ,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1024,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
errno, ERRNO_SUCCESS,
|
||||||
|
"opening a file without a trailing slash works"
|
||||||
|
);
|
||||||
|
|
||||||
|
let errno = path_open(
|
||||||
|
fd,
|
||||||
|
0,
|
||||||
|
path_bad.as_ptr() as i32,
|
||||||
|
path_bad.len() as i32,
|
||||||
|
0,
|
||||||
|
RIGHTS_FD_READ,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1024,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
errno, ERRNO_NOTDIR,
|
||||||
|
"opening a regular file with a trailing slash should fail"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
tests/wasi-fyi/fs_sandbox_symlink.rs
Normal file
53
tests/wasi-fyi/fs_sandbox_symlink.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#[link(wasm_import_module = "wasi_snapshot_preview1")]
|
||||||
|
extern "C" {
|
||||||
|
pub fn path_open(
|
||||||
|
fd: i32,
|
||||||
|
dirflags: i32,
|
||||||
|
path: i32,
|
||||||
|
path_len: i32,
|
||||||
|
oflags: i32,
|
||||||
|
fs_rights_base: i64,
|
||||||
|
fs_rights_inheriting: i64,
|
||||||
|
fdflags: i32,
|
||||||
|
result_fd: i32,
|
||||||
|
) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ERRNO_PERM: i32 = 63;
|
||||||
|
const LOOKUPFLAGS_SYMLINK_FOLLOW: i32 = 1;
|
||||||
|
const OFLAGS_CREAT: i32 = 1;
|
||||||
|
const RIGHTS_FD_WRITE: i64 = 64;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let link_path = "fyi/fs_sandbox_symlink.dir/link";
|
||||||
|
let link_path_non_existant = "fyi/fs_sandbox_symlink.dir/link-non-existant";
|
||||||
|
let mut fd: i32 = 0;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let errno = path_open(
|
||||||
|
5,
|
||||||
|
LOOKUPFLAGS_SYMLINK_FOLLOW,
|
||||||
|
link_path.as_ptr() as i32,
|
||||||
|
link_path.len() as i32,
|
||||||
|
OFLAGS_CREAT,
|
||||||
|
RIGHTS_FD_WRITE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&mut fd as *mut i32 as i32,
|
||||||
|
);
|
||||||
|
assert_eq!(errno, ERRNO_PERM, "symlink cannot escape fs sandbox");
|
||||||
|
|
||||||
|
let errno = path_open(
|
||||||
|
5,
|
||||||
|
LOOKUPFLAGS_SYMLINK_FOLLOW,
|
||||||
|
link_path_non_existant.as_ptr() as i32,
|
||||||
|
link_path_non_existant.len() as i32,
|
||||||
|
OFLAGS_CREAT,
|
||||||
|
RIGHTS_FD_WRITE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&mut fd as *mut i32 as i32,
|
||||||
|
);
|
||||||
|
assert_eq!(errno, ERRNO_PERM, "symlink cannot escape fs sandbox");
|
||||||
|
}
|
||||||
|
}
|
||||||
1
tests/wasi-fyi/test_fs/fyi/fs_sandbox_symlink.dir/link
Symbolic link
1
tests/wasi-fyi/test_fs/fyi/fs_sandbox_symlink.dir/link
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../README.md
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../non-existant
|
||||||
@@ -1 +0,0 @@
|
|||||||
0
|
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
$WASMER -q run main.wasm --dir=. > output
|
$WASMER -q run main.wasm --dir=. > output
|
||||||
|
|
||||||
diff -u output expected 1>/dev/null
|
printf "0" | diff -u output - 1>/dev/null
|
||||||
34
tests/wasix/proc_exec/main.c
Normal file
34
tests/wasix/proc_exec/main.c
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc > 1 && argv[1] != NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == -1)
|
||||||
|
{
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
else if (pid == 0)
|
||||||
|
{
|
||||||
|
char *newargv[] = {argv[0], "child", NULL};
|
||||||
|
|
||||||
|
execv("/code/main.wasm", newargv);
|
||||||
|
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
printf("%d", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
5
tests/wasix/proc_exec/run.sh
Executable file
5
tests/wasix/proc_exec/run.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
$WASMER -q run main.wasm --mapdir=/code:. > output
|
||||||
|
|
||||||
|
printf "0" | diff -u output - 1>/dev/null
|
||||||
37
tests/wasix/proc_exec2/main.c
Normal file
37
tests/wasix/proc_exec2/main.c
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc > 1 && argv[1] != NULL)
|
||||||
|
{
|
||||||
|
char *bar = getenv("foo");
|
||||||
|
|
||||||
|
return (bar == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == -1)
|
||||||
|
{
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
else if (pid == 0)
|
||||||
|
{
|
||||||
|
char *newargv[] = {argv[0], "child", NULL};
|
||||||
|
char *newenviron[] = {"foo=bar", NULL};
|
||||||
|
|
||||||
|
execve("/code/main.wasm", newargv, newenviron);
|
||||||
|
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
printf("%d", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
5
tests/wasix/proc_exec2/run.sh
Executable file
5
tests/wasix/proc_exec2/run.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
$WASMER -q run main.wasm --mapdir=/code:. > output
|
||||||
|
|
||||||
|
printf "0" | diff -u output - 1>/dev/null
|
||||||
@@ -77,6 +77,7 @@ while read dir; do
|
|||||||
|
|
||||||
cmd="cd $dir; \
|
cmd="cd $dir; \
|
||||||
$CC $CFLAGS $LDFLAGS -o main.wasm main.c; \
|
$CC $CFLAGS $LDFLAGS -o main.wasm main.c; \
|
||||||
|
wasm-opt -O4 --asyncify -g main.wasm -o main.wasm; \
|
||||||
./run.sh"
|
./run.sh"
|
||||||
|
|
||||||
if bash -c "$cmd"; then
|
if bash -c "$cmd"; then
|
||||||
@@ -85,6 +86,6 @@ while read dir; do
|
|||||||
printf "\rTesting $dir ❌\n"
|
printf "\rTesting $dir ❌\n"
|
||||||
status=1
|
status=1
|
||||||
fi
|
fi
|
||||||
done < <(find . -mindepth 1 -maxdepth 1 -type d)
|
done < <(find . -mindepth 1 -maxdepth 1 -type d | sort)
|
||||||
|
|
||||||
exit $status
|
exit $status
|
||||||
Reference in New Issue
Block a user