VFS API

Useful helper functions can be found in the kinode_process_lib

The VFS API tries to map over the std::fs calls as directly as possible.

Every request takes a path and a corresponding action.

Drives

A drive is a directory within a package's VFS directory, e.g., app_store:sys/pkg/ or your_package:publisher.os/my_drive/. Drives are owned by packages. Packages can share access to drives they own via capabilities. Each package is spawned with two drives: pkg/ and tmp/. All processes in a package have caps to those drives. Processes can also create additional drives.

pkg/ drive

The pkg/ drive contains metadata about the package that Kinode requires to run that package, .wasm binaries, and optionally the API of the package and the UI. When creating packages, the pkg/ drive is populated by kit build and loaded into the Kinode using kit start-package.

tmp/ drive

The tmp/ drive can be written to directly by the owning package using standard filesystem functionality (i.e. std::fs in Rust) via WASI in addition to the Kinode VFS.

Imports

#![allow(unused)]
fn main() {
use kinode_process_lib::vfs::{
  create_drive, open_file, open_dir, create_file, metadata, File, Directory,
};
}

Opening/Creating a Drive

#![allow(unused)]
fn main() {
let drive_path: String = create_drive(our.package_id(), "drive_name")?;
// you can now prepend this path to any files/directories you're interacting with
let file = open_file(&format!("{}/hello.txt", &drive_path), true);
}

Sharing a Drive Capability

#![allow(unused)]
fn main() {
let vfs_read_cap = serde_json::json!({
    "kind": "read",
    "drive": drive_path,
}).to_string();

let vfs_address = Address {
    node: our.node.clone(),
    process: ProcessId::from_str("vfs:distro:sys").unwrap(),
};

// get this capability from our store
let cap = get_capability(&vfs_address, &vfs_read_cap);

// now if we have that Capability, we can attach it to a subsequent message.
if let Some(cap) = cap {
    Request::new()
        .capabilities(vec![cap])
        .body(b"hello".to_vec())
        .send()?;
}
}
#![allow(unused)]
fn main() {
// the receiving process can then save the capability to it's store, and open the drive.
save_capabilities(incoming_request.capabilities);
let dir = open_dir(&drive_path, false)?;
}

Files

Open a File

#![allow(unused)]
fn main() {
/// Opens a file at path, if no file at path, creates one if boolean create is true.
let file_path = format!("{}/hello.txt", &drive_path);
let file = open_file(&file_path, true);
}

Create a File

#![allow(unused)]
fn main() {
/// Creates a file at path, if file found at path, truncates it to 0.
let file_path = format!("{}/hello.txt", &drive_path);
let file = create_file(&file_path);
}

Read a File

#![allow(unused)]
fn main() {
/// Reads the entire file, from start position.
/// Returns a vector of bytes.
let contents = file.read()?;
}

Write a File

#![allow(unused)]
fn main() {
/// Write entire slice as the new file.
/// Truncates anything that existed at path before.
let buffer = b"Hello!";
file.write(&buffer)?;
}

Write to File

#![allow(unused)]
fn main() {
/// Write buffer to file at current position, overwriting any existing data.
let buffer = b"World!";
file.write_all(&buffer)?;
}

Read at position

#![allow(unused)]
fn main() {
/// Read into buffer from current cursor position
/// Returns the amount of bytes read.
let mut buffer = vec![0; 5];
file.read_at(&buffer)?;
}

Set Length

#![allow(unused)]
fn main() {
/// Set file length, if given size > underlying file, fills it with 0s.
file.set_len(42)?;
}

Seek to a position

#![allow(unused)]
fn main() {
/// Seek file to position.
/// Returns the new position.
let position = SeekFrom::End(0);
file.seek(&position)?;
}

Sync

#![allow(unused)]
fn main() {
/// Syncs path file buffers to disk.
file.sync_all()?;
}

Metadata

#![allow(unused)]
fn main() {
/// Metadata of a path, returns file type and length.
let metadata = file.metadata()?;
}

Directories

Open a Directory

#![allow(unused)]
fn main() {
/// Opens or creates a directory at path.
/// If trying to create an existing file, will just give you the path.
let dir_path = format!("{}/my_pics", &drive_path);
let dir = open_dir(&dir_path, true);
}

Read a Directory

#![allow(unused)]
fn main() {
/// Iterates through children of directory, returning a vector of DirEntries.
/// DirEntries contain the path and file type of each child.
let entries = dir.read()?;
}

General path Metadata

#![allow(unused)]
fn main() {
/// Metadata of a path, returns file type and length.
let some_path = format!("{}/test", &drive_path);
let metadata = metadata(&some_path)?;
}

API

#![allow(unused)]
fn main() {
pub struct VfsRequest {
    /// path is always prepended by package_id, the capabilities of the topmost directory are checked
    /// "/your_package:publisher.os/drive_dir/another_dir_or_file"
    pub path: String,
    pub action: VfsAction,
}

pub enum VfsAction {
    CreateDrive,
    CreateDir,
    CreateDirAll,
    CreateFile,
    OpenFile { create: bool },
    CloseFile,
    Write,
    WriteAt,
    Append,
    SyncAll,
    Read,
    ReadDir,
    ReadToEnd,
    ReadExact(u64),
    ReadToString,
    Seek { seek_from: SeekFrom },
    RemoveFile,
    RemoveDir,
    RemoveDirAll,
    Rename { new_path: String },
    Metadata,
    AddZip,
    CopyFile { new_path: String },
    Len,
    SetLen(u64),
    Hash,
}

pub enum SeekFrom {
    Start(u64),
    End(i64),
    Current(i64),
}

pub enum FileType {
    File,
    Directory,
    Symlink,
    Other,
}

pub struct FileMetadata {
    pub file_type: FileType,
    pub len: u64,
}

pub struct DirEntry {
    pub path: String,
    pub file_type: FileType,
}

pub enum VfsResponse {
    Ok,
    Err(VfsError),
    Read,
    SeekFrom(u64),
    ReadDir(Vec<DirEntry>),
    ReadToString(String),
    Metadata(FileMetadata),
    Len(u64),
    Hash([u8; 32]),
}

pub enum VfsError {
    NoCap { action: String, path: String },
    BadBytes { action: String, path: String },
    BadRequest { error: String },
    ParseError { error: String, path: String },
    IOError { error: String, path: String },
    CapChannelFail { error: String },
    BadJson { error: String },
    NotFound { path: String },
    CreateDirError { path: String, error: String },
}
}
Get Help: