wasmer_cli/commands/run/capabilities/
mod.rs

1use crate::config::WasmerEnv;
2
3use super::PackageSource;
4use anyhow::anyhow;
5use sha2::{Digest, Sha256};
6use std::{
7    path::{Path, PathBuf},
8    time::UNIX_EPOCH,
9};
10
11use wasmer_config::package::PackageSource as PackageSpecifier;
12
13/// A custom implementation of the [`virtual_net::VirtualNetwork`] that asks users if they want to
14/// use networking features at runtime.
15pub(crate) mod net;
16
17/// The default name of the directory to store cached capabilities for packages.
18const DEFAULT_WASMER_PKG_CAPABILITY_CACHE_DIR: &str = "pkg_capabilities";
19
20/// A struct representing cached capabilities for a specific package.
21#[derive(Debug, serde::Serialize, serde::Deserialize)]
22pub(crate) struct PkgCapabilityCache {
23    pub enable_networking: bool,
24}
25
26pub(crate) fn get_capability_cache_path(
27    env: &WasmerEnv,
28    input: &PackageSource,
29) -> anyhow::Result<PathBuf> {
30    let registry_name = env
31        .registry_public_url()?
32        .host_str()
33        .unwrap_or("unknown_registry")
34        .replace('.', "_");
35
36    // We don't have the bytes of the module yet, but we still want to have the
37    // package-capabilities cache be as close to an actual identifier as possible.
38    let package_cache_path = match &input {
39        PackageSource::File(f) => {
40            let full_path = f.canonicalize()?.to_path_buf();
41            let metadata = full_path
42                .parent()
43                .ok_or(anyhow!("No parent!"))?
44                .metadata()?
45                .modified()?;
46
47            let mut hash = Sha256::new();
48            hash.update(
49                full_path
50                    .into_os_string()
51                    .into_string()
52                    .map_err(|e| anyhow!("{e:?}"))?
53                    .as_bytes(),
54            );
55            hash.update(
56                metadata
57                    .duration_since(UNIX_EPOCH)?
58                    .as_millis()
59                    .to_be_bytes(),
60            );
61
62            format!("path_{}.json", hex::encode(hash.finalize()))
63        }
64        PackageSource::Dir(f) => {
65            let full_path = f.canonicalize()?.to_path_buf();
66            let metadata = full_path.metadata()?.modified()?;
67
68            let mut hash = Sha256::new();
69            hash.update(
70                full_path
71                    .into_os_string()
72                    .into_string()
73                    .map_err(|e| anyhow!("{e:?}"))?
74                    .as_bytes(),
75            );
76            hash.update(
77                metadata
78                    .duration_since(UNIX_EPOCH)?
79                    .as_millis()
80                    .to_be_bytes(),
81            );
82
83            format!("path_{}.json", hex::encode(hash.finalize()))
84        }
85        PackageSource::Package(p) => match p {
86            PackageSpecifier::Ident(id) => match id {
87                wasmer_config::package::PackageIdent::Named(n) => format!(
88                    "ident_{}_{}",
89                    n.namespace.clone().unwrap_or("unknown_namespace".into()),
90                    n.name
91                ),
92                wasmer_config::package::PackageIdent::Hash(h) => {
93                    format!("hash_{h}")
94                }
95            },
96            PackageSpecifier::Path(f) => {
97                let full_path = PathBuf::from(f).canonicalize()?.to_path_buf();
98
99                let mut hasher = Sha256::new();
100                hasher.update(
101                    full_path
102                        .clone()
103                        .into_os_string()
104                        .into_string()
105                        .map_err(|e| anyhow!("{e:?}"))?
106                        .as_bytes(),
107                );
108
109                if full_path.is_dir() {
110                    hasher.update(
111                        full_path
112                            .metadata()?
113                            .modified()?
114                            .duration_since(UNIX_EPOCH)?
115                            .as_millis()
116                            .to_be_bytes(),
117                    );
118                } else if full_path.is_file() {
119                    hasher.update(
120                        full_path
121                            .parent()
122                            .ok_or(anyhow!("No parent!"))?
123                            .metadata()?
124                            .modified()?
125                            .duration_since(UNIX_EPOCH)?
126                            .as_millis()
127                            .to_be_bytes(),
128                    );
129                }
130                format!("path_{}.json", hex::encode(hasher.finalize()))
131            }
132            PackageSpecifier::Url(u) => {
133                let mut hasher = Sha256::new();
134                hasher.update(u.to_string().as_bytes());
135                format!("path_{}.json", hex::encode(hasher.finalize()))
136            }
137        },
138    };
139    Ok(env
140        .cache_dir()
141        .join(DEFAULT_WASMER_PKG_CAPABILITY_CACHE_DIR)
142        .join(registry_name)
143        .join(package_cache_path))
144}
145
146pub(crate) fn get_cached_capability(path: &Path) -> anyhow::Result<PkgCapabilityCache> {
147    let raw = std::fs::read_to_string(path)?;
148    tracing::debug!("cache hit for package capability at {}", path.display());
149    serde_json::from_str::<PkgCapabilityCache>(&raw)
150        .map_err(|e| anyhow!("while deserializing package capability cache: {e:?}"))
151}