Skip to content

Commit 5e30efd

Browse files
committed
Import the cap-primitives crate
Copy the entire contents of this crate into `crates/wasi/src/filesystem/primitives` for future modifications to get it building.
1 parent 3d0ec7e commit 5e30efd

123 files changed

Lines changed: 10582 additions & 2 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/wasi/Cargo.toml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,23 @@ env_logger = { workspace = true }
4949

5050
[target.'cfg(unix)'.dependencies]
5151
rustix = { workspace = true, features = ["event", "fs", "net"] }
52+
rustix-linux-procfs = "0.1.1"
5253

5354
[target.'cfg(windows)'.dependencies]
5455
io-extras = { workspace = true }
55-
windows-sys = { workspace = true }
5656
rustix = { workspace = true, features = ["event", "net"] }
57+
winx = "0.36.0"
58+
59+
[target.'cfg(windows)'.dependencies.windows-sys]
60+
workspace = true
61+
features = [
62+
"Wdk_Storage_FileSystem",
63+
"Win32_Foundation",
64+
"Win32_Storage_FileSystem",
65+
"Win32_System_IO",
66+
"Win32_System_Ioctl",
67+
"Win32_System_Performance",
68+
]
5769

5870
[features]
5971
default = ["preview1"]

crates/wasi/src/ctx.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,8 @@ impl WasiCtxBuilder {
298298
dir_perms: DirPerms,
299299
file_perms: FilePerms,
300300
) -> Result<&mut Self> {
301-
let dir = cap_std::fs::Dir::open_ambient_dir(host_path.as_ref(), ambient_authority())?;
301+
let dir = crate::filesystem::primitives::open_ambient_dir(host_path.as_ref())?;
302+
let dir = cap_std::fs::Dir::from_std_file(dir);
302303
let mut open_mode = OpenMode::empty();
303304
if dir_perms.contains(DirPerms::READ) {
304305
open_mode |= OpenMode::READ;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//! This defines `create_dir`, the primary entrypoint to sandboxed directory
2+
//! creation.
3+
4+
use crate::filesystem::primitives::{DirOptions, create_dir_impl};
5+
use std::path::Path;
6+
use std::{fs, io};
7+
8+
/// Perform a `mkdirat`-like operation, ensuring that the resolution of the
9+
/// path never escapes the directory tree rooted at `start`.
10+
#[inline]
11+
pub fn create_dir(start: &fs::File, path: &Path, options: &DirOptions) -> io::Result<()> {
12+
// Call the underlying implementation.
13+
create_dir_impl(start, path, options)
14+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use crate::filesystem::primitives::{DirEntryInner, Metadata};
2+
#[cfg(not(windows))]
3+
use rustix::fs::DirEntryExt;
4+
use std::ffi::OsString;
5+
use std::{fmt, io};
6+
7+
/// Entries returned by the `ReadDir` iterator.
8+
///
9+
/// This corresponds to [`std::fs::DirEntry`].
10+
///
11+
/// Unlike `std::fs::DirEntry`, this API has no `DirEntry::path`, because
12+
/// absolute paths don't interoperate well with the capability model.
13+
///
14+
/// There is a `file_name` function, however there are also `open`,
15+
/// `open_with`, `open_dir`, `remove_file`, and `remove_dir` functions for
16+
/// opening or removing the entry directly, which can be more efficient and
17+
/// convenient.
18+
///
19+
/// There is no `from_std` method, as `std::fs::DirEntry` doesn't provide a way
20+
/// to construct a `DirEntry` without opening directories by ambient paths.
21+
pub struct DirEntry {
22+
pub(crate) inner: DirEntryInner,
23+
}
24+
25+
impl DirEntry {
26+
/// Returns the metadata for the file that this entry points at.
27+
///
28+
/// This corresponds to [`std::fs::DirEntry::metadata`].
29+
///
30+
/// # Platform-specific behavior
31+
///
32+
/// On Windows, this produces a `Metadata` object which does not contain
33+
/// the optional values returned by [`MetadataExt`]. Use
34+
/// [`cap_fs_ext::DirEntryExt::full_metadata`] to obtain a `Metadata` with
35+
/// the values filled in.
36+
///
37+
/// [`MetadataExt`]: https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html
38+
/// [`cap_fs_ext::DirEntryExt::full_metadata`]: https://docs.rs/cap-fs-ext/latest/cap_fs_ext/trait.DirEntryExt.html#tymethod.full_metadata
39+
#[inline]
40+
pub fn metadata(&self) -> io::Result<Metadata> {
41+
self.inner.metadata()
42+
}
43+
44+
/// Returns the bare file name of this directory entry without any other
45+
/// leading path component.
46+
///
47+
/// This corresponds to [`std::fs::DirEntry::file_name`].
48+
#[inline]
49+
pub fn file_name(&self) -> OsString {
50+
self.inner.file_name()
51+
}
52+
}
53+
54+
#[cfg(not(windows))]
55+
impl DirEntryExt for DirEntry {
56+
#[inline]
57+
fn ino(&self) -> u64 {
58+
self.inner.ino()
59+
}
60+
}
61+
62+
impl fmt::Debug for DirEntry {
63+
// Like libstd's version, but doesn't print the path.
64+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65+
self.inner.fmt(f)
66+
}
67+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#[cfg(not(target_os = "wasi"))]
2+
use crate::filesystem::primitives::DirOptionsExt;
3+
4+
/// Options and flags which can be used to configure how a directory is
5+
/// created.
6+
///
7+
/// This is to `create_dir` what to `OpenOptions` is to `open`.
8+
#[derive(Debug, Clone)]
9+
pub struct DirOptions {
10+
#[cfg(not(target_os = "wasi"))]
11+
#[allow(dead_code)]
12+
pub(crate) ext: DirOptionsExt,
13+
}
14+
15+
impl DirOptions {
16+
/// Creates a blank new set of options ready for configuration.
17+
#[allow(clippy::new_without_default)]
18+
#[inline]
19+
pub const fn new() -> Self {
20+
Self {
21+
#[cfg(not(target_os = "wasi"))]
22+
ext: DirOptionsExt::new(),
23+
}
24+
}
25+
}
26+
27+
#[cfg(target_os = "vxworks")]
28+
impl crate::fs::DirBuilderExt for DirOptions {
29+
#[inline]
30+
fn mode(&mut self, mode: u32) -> &mut Self {
31+
self.ext.mode(mode);
32+
self
33+
}
34+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use std::io;
2+
3+
#[cfg(not(windows))]
4+
pub(crate) use crate::filesystem::primitives::rustix::fs::errors::*;
5+
#[cfg(windows)]
6+
pub(crate) use crate::filesystem::primitives::windows::fs::errors::*;
7+
8+
#[cold]
9+
pub(crate) fn escape_attempt() -> io::Error {
10+
io::Error::new(
11+
io::ErrorKind::PermissionDenied,
12+
"a path led outside of the filesystem",
13+
)
14+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//! The `FileType` struct.
2+
3+
use crate::filesystem::primitives::ImplFileTypeExt;
4+
5+
/// `FileType`'s inner state.
6+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
7+
enum Inner {
8+
/// A directory.
9+
Dir,
10+
11+
/// A file.
12+
File,
13+
14+
/// An unknown entity.
15+
Unknown,
16+
17+
/// A `FileTypeExt` type.
18+
Ext(ImplFileTypeExt),
19+
}
20+
21+
/// A structure representing a type of file with accessors for each file type.
22+
///
23+
/// This corresponds to [`std::fs::FileType`].
24+
///
25+
/// <details>
26+
/// We need to define our own version because the libstd `FileType` doesn't
27+
/// have a public constructor that we can use.
28+
/// </details>
29+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
30+
#[repr(transparent)]
31+
pub struct FileType(Inner);
32+
33+
impl FileType {
34+
/// Creates a `FileType` for which `is_dir()` returns `true`.
35+
#[inline]
36+
pub const fn dir() -> Self {
37+
Self(Inner::Dir)
38+
}
39+
40+
/// Creates a `FileType` for which `is_file()` returns `true`.
41+
#[inline]
42+
pub const fn file() -> Self {
43+
Self(Inner::File)
44+
}
45+
46+
/// Creates a `FileType` for which `is_unknown()` returns `true`.
47+
#[inline]
48+
pub const fn unknown() -> Self {
49+
Self(Inner::Unknown)
50+
}
51+
52+
/// Creates a `FileType` from extension type.
53+
#[inline]
54+
pub(crate) const fn ext(ext: ImplFileTypeExt) -> Self {
55+
Self(Inner::Ext(ext))
56+
}
57+
58+
/// Tests whether this file type represents a directory.
59+
///
60+
/// This corresponds to [`std::fs::FileType::is_dir`].
61+
#[inline]
62+
pub fn is_dir(&self) -> bool {
63+
self.0 == Inner::Dir
64+
}
65+
66+
/// Tests whether this file type represents a regular file.
67+
///
68+
/// This corresponds to [`std::fs::FileType::is_file`].
69+
#[inline]
70+
pub fn is_file(&self) -> bool {
71+
self.0 == Inner::File
72+
}
73+
74+
/// Tests whether this file type represents a symbolic link.
75+
///
76+
/// This corresponds to [`std::fs::FileType::is_symlink`].
77+
#[inline]
78+
pub fn is_symlink(&self) -> bool {
79+
if let Inner::Ext(ext) = self.0 {
80+
ext.is_symlink()
81+
} else {
82+
false
83+
}
84+
}
85+
}
86+
87+
/// Unix-specific extensions for [`FileType`].
88+
///
89+
/// This corresponds to [`std::os::unix::fs::FileTypeExt`].
90+
#[cfg(any(unix, target_os = "vxworks"))]
91+
pub trait FileTypeExt {
92+
/// Returns `true` if this file type is a block device.
93+
fn is_block_device(&self) -> bool;
94+
/// Returns `true` if this file type is a character device.
95+
fn is_char_device(&self) -> bool;
96+
}
97+
98+
#[cfg(any(unix, target_os = "vxworks"))]
99+
impl FileTypeExt for FileType {
100+
#[inline]
101+
fn is_block_device(&self) -> bool {
102+
self.0 == Inner::Ext(ImplFileTypeExt::block_device())
103+
}
104+
105+
#[inline]
106+
fn is_char_device(&self) -> bool {
107+
self.0 == Inner::Ext(ImplFileTypeExt::char_device())
108+
}
109+
}
110+
111+
/// Extension trait to allow `is_block_device` etc. to be exposed by
112+
/// the `cap-fs-ext` crate.
113+
///
114+
/// This is hidden from the main API since this functionality isn't present in
115+
/// `std`. Use `cap_fs_ext::FileTypeExt` instead of calling this directly.
116+
#[cfg(windows)]
117+
#[doc(hidden)]
118+
pub trait _WindowsFileTypeExt {
119+
fn is_block_device(&self) -> bool;
120+
fn is_char_device(&self) -> bool;
121+
fn is_fifo(&self) -> bool;
122+
fn is_socket(&self) -> bool;
123+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/// Should symlinks be followed in the last component of a path?
2+
///
3+
/// This doesn't affect path components other than the last. So for example in
4+
/// "foo/bar/baz", if "foo" or "bar" are symlinks, they will always be
5+
/// followed. This enum value only determines whether "baz" is followed.
6+
///
7+
/// Instead of passing bare `bool`s as parameters, pass a distinct enum so that
8+
/// the intent is clear.
9+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
10+
pub enum FollowSymlinks {
11+
/// Yes, do follow symlinks in the last component of a path.
12+
Yes,
13+
14+
/// No, do not follow symlinks in the last component of a path.
15+
No,
16+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//! This defines `hard_link`, the primary entrypoint to sandboxed hard-link
2+
//! creation.
3+
4+
use crate::filesystem::primitives::hard_link_impl;
5+
use std::path::Path;
6+
use std::{fs, io};
7+
8+
/// Perform a `linkat`-like operation, ensuring that the resolution of the path
9+
/// never escapes the directory tree rooted at `start`.
10+
#[inline]
11+
pub fn hard_link(
12+
old_start: &fs::File,
13+
old_path: &Path,
14+
new_start: &fs::File,
15+
new_path: &Path,
16+
) -> io::Result<()> {
17+
// Call the underlying implementation.
18+
hard_link_impl(old_start, old_path, new_start, new_path)
19+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use std::ffi::OsStr;
2+
use std::path::{Component, PathBuf};
3+
4+
/// Utility for collecting the canonical path components.
5+
pub(super) struct CanonicalPath<'path_buf> {
6+
/// If the user requested a canonical path, a reference to the `PathBuf` to
7+
/// write it to.
8+
path: Option<&'path_buf mut PathBuf>,
9+
}
10+
11+
impl<'path_buf> CanonicalPath<'path_buf> {
12+
pub(super) fn new(path: Option<&'path_buf mut PathBuf>) -> Self {
13+
Self { path }
14+
}
15+
16+
pub(super) fn push(&mut self, one: &OsStr) {
17+
if let Some(path) = &mut self.path {
18+
path.push(one)
19+
}
20+
}
21+
22+
pub(super) fn pop(&mut self) -> bool {
23+
if let Some(path) = &mut self.path {
24+
path.pop()
25+
} else {
26+
true
27+
}
28+
}
29+
30+
/// The complete canonical path has been scanned. Set `path` to `None`
31+
/// so that it isn't cleared when `self` is dropped.
32+
pub(super) fn complete(&mut self) {
33+
// Replace "" with ".", since "" as a relative path is interpreted as
34+
// an error.
35+
if let Some(path) = &mut self.path {
36+
if path.as_os_str().is_empty() {
37+
path.push(Component::CurDir);
38+
}
39+
self.path = None;
40+
}
41+
}
42+
}
43+
44+
impl<'path_buf> Drop for CanonicalPath<'path_buf> {
45+
fn drop(&mut self) {
46+
// If `self.path` is still `Some` here, it means that we haven't called
47+
// `complete()` yet, meaning the `CanonicalPath` is being dropped
48+
// before the complete path has been processed. In that case, clear
49+
// `path` to indicate that we weren't able to obtain a complete path.
50+
if let Some(path) = &mut self.path {
51+
path.clear();
52+
self.path = None;
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)