Skip to content

Commit

Permalink
add maximum size control
Browse files Browse the repository at this point in the history
  • Loading branch information
wasm-forge committed Jan 1, 2025
1 parent 8c93a40 commit b844377
Show file tree
Hide file tree
Showing 12 changed files with 322 additions and 141 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ lcov.info

tarpaulin-report.html

tests/svg/*
tests/svg/*
.codegpt
7 changes: 7 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
NotFound,
Unimplemented,
InvalidOffset,
InvalidFileType,
InvalidFileName,
Expand All @@ -19,4 +20,10 @@ pub enum Error {
CannotRemoveMountedMemoryFile,
IncompatibleChunkSize,
InvalidMagicMarker,

MaxFileSizeExceeded,
NoSpaceLeftOnDevice,
OperationNotSupported,
MetadataUpdateInvalid,
TooManyOpenFiles,
}
25 changes: 19 additions & 6 deletions src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,22 @@ impl FileSystem {
}

// update metadata of a given file descriptor
pub fn set_metadata(&mut self, fd: Fd, metadata: Metadata) -> Result<(), Error> {
pub fn set_metadata(&mut self, fd: Fd, metadata: &Metadata) -> Result<(), Error> {
let node = self.get_node(fd)?;
self.storage.put_metadata(node, metadata);
self.storage.put_metadata(node, metadata)?;

Ok(())
}

// Set maxmum file size limit in bytes. Reading, writing, and setting the cursor above the limit will result in error.
// Use this feature to limit, how much memory can be consumed by the mounted memory files.
pub fn set_file_size_limit(&mut self, fd: Fd, max_size: FileSize) -> Result<(), Error> {
let node = self.get_node(fd)?;
let mut metadata = self.storage.get_metadata(node)?;

metadata.maximum_size_allowed = Some(max_size);

self.storage.put_metadata(node, &metadata)?;

Ok(())
}
Expand All @@ -331,7 +344,7 @@ impl FileSystem {

metadata.times.accessed = time;

self.storage.put_metadata(node, metadata);
self.storage.put_metadata(node, &metadata)?;

Ok(())
}
Expand All @@ -343,7 +356,7 @@ impl FileSystem {

metadata.times.modified = time;

self.storage.put_metadata(node, metadata);
self.storage.put_metadata(node, &metadata)?;

Ok(())
}
Expand Down Expand Up @@ -1488,7 +1501,7 @@ mod tests {
.unwrap();
let mut metadata = fs.metadata(fd).unwrap();
metadata.size = len as FileSize * count as FileSize;
fs.set_metadata(fd, metadata).unwrap();
fs.set_metadata(fd, &metadata).unwrap();
fs.close(fd).unwrap();

// store memory into a file
Expand Down Expand Up @@ -1570,7 +1583,7 @@ mod tests {
let mut meta = fs.metadata(fd).unwrap();
meta.size = size;

fs.set_metadata(fd, meta).unwrap();
fs.set_metadata(fd, &meta).unwrap();

fd
}
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ impl File {
pub fn truncate(&self, storage: &mut dyn Storage) -> Result<(), Error> {
let mut metadata = storage.get_metadata(self.node)?;
metadata.size = 0;
storage.put_metadata(self.node, metadata);
storage.put_metadata(self.node, &metadata)?;
Ok(())
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/runtime/structure_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ pub fn create_hard_link(
}

metadata.link_count += 1;
storage.put_metadata(node, metadata);
storage.put_metadata(node, &metadata)?;

add_dir_entry(dir_node, node, leaf_name.as_bytes(), storage)?;

Expand Down Expand Up @@ -149,7 +149,7 @@ pub fn create_dir_entry(

storage.put_metadata(
node,
Metadata {
&Metadata {
node,
file_type: entry_type,
link_count: 1,
Expand All @@ -164,7 +164,7 @@ pub fn create_dir_entry(
chunk_type,
maximum_size_allowed: None,
},
);
)?;

add_dir_entry(parent_dir_node, node, entry_name, storage)?;

Expand Down Expand Up @@ -326,7 +326,7 @@ pub fn add_dir_entry(
}
metadata.size += 1;

storage.put_metadata(parent_dir_node, metadata);
storage.put_metadata(parent_dir_node, &metadata)?;

Ok(())
}
Expand Down Expand Up @@ -418,13 +418,13 @@ pub fn rm_dir_entry(
parent_dir_metadata.size -= 1;

// update parent metadata
storage.put_metadata(parent_dir_node, parent_dir_metadata);
storage.put_metadata(parent_dir_node, &parent_dir_metadata)?;

// remove the entry
storage.rm_direntry(parent_dir_node, removed_entry_index);

removed_metadata.link_count -= 1;
storage.put_metadata(removed_metadata.node, removed_metadata.clone());
storage.put_metadata(removed_metadata.node, &removed_metadata)?;

Ok((removed_dir_entry_node, removed_metadata))
}
Expand Down
5 changes: 4 additions & 1 deletion src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub trait Storage {
// Get the metadata associated with the node.
fn get_metadata(&self, node: Node) -> Result<Metadata, Error>;
// Update the metadata associated with the node.
fn put_metadata(&mut self, node: Node, metadata: Metadata);
fn put_metadata(&mut self, node: Node, metadata: &Metadata) -> Result<(), Error>;

// Retrieve the DirEntry instance given the Node and DirEntryIndex.
fn get_direntry(&self, node: Node, index: DirEntryIndex) -> Result<DirEntry, Error>;
Expand All @@ -65,6 +65,9 @@ pub trait Storage {
// Write file at the current file cursor, the cursor position will NOT be updated after reading.
fn write(&mut self, node: Node, offset: FileSize, buf: &[u8]) -> Result<FileSize, Error>;

// delete chunks to match the new file size specified
fn resize_file(&mut self, node: Node, new_size: FileSize) -> Result<(), Error>;

// remove all file chunks
fn rm_file(&mut self, node: Node) -> Result<(), Error>;

Expand Down
29 changes: 16 additions & 13 deletions src/storage/chunk_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,23 @@ mod tests {
fn create_file_with_size<M: Memory>(size: FileSize, storage: &mut StableStorage<M>) -> Node {
let node = storage.new_node();

storage.put_metadata(
node,
Metadata {
storage
.put_metadata(
node,
file_type: FileType::RegularFile,
link_count: 1,
size,
times: Times::default(),
first_dir_entry: Some(42),
last_dir_entry: Some(24),
chunk_type: Some(storage.chunk_type()),
maximum_size_allowed: None,
},
);
&Metadata {
node,
file_type: FileType::RegularFile,
link_count: 1,
size,
times: Times::default(),
first_dir_entry: Some(42),
last_dir_entry: Some(24),
chunk_type: Some(storage.chunk_type()),
maximum_size_allowed: None,
},
)
.unwrap();

node
}

Expand Down
34 changes: 20 additions & 14 deletions src/storage/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl Storage for DummyStorage {
panic!("Not supported")
}

fn put_metadata(&mut self, _node: Node, _metadata: Metadata) {
fn put_metadata(&mut self, _node: Node, _metadata: &Metadata) -> Result<(), Error> {
panic!("Not supported")
}

Expand Down Expand Up @@ -114,6 +114,10 @@ impl Storage for DummyStorage {
fn flush(&mut self, _node: Node) {
panic!("Not supported")
}

fn resize_file(&mut self, _node: Node, _new_size: FileSize) -> Result<(), Error> {
panic!("Not supported")
}
}

#[cfg(test)]
Expand All @@ -127,20 +131,22 @@ mod tests {
fn put_metadata_panic() {
let mut storage = DummyStorage::new();
let node = storage.new_node();
storage.put_metadata(
node,
Metadata {
storage
.put_metadata(
node,
file_type: FileType::RegularFile,
link_count: 1,
size: 10,
times: Times::default(),
first_dir_entry: Some(42),
last_dir_entry: Some(24),
chunk_type: None,
maximum_size_allowed: None,
},
)
&Metadata {
node,
file_type: FileType::RegularFile,
link_count: 1,
size: 10,
times: Times::default(),
first_dir_entry: Some(42),
last_dir_entry: Some(24),
chunk_type: None,
maximum_size_allowed: None,
},
)
.unwrap();
}

#[test]
Expand Down
47 changes: 19 additions & 28 deletions src/storage/metadata_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ use super::types::{Metadata, Node};
use crate::fs::FileSize;
use crate::runtime::structure_helpers::{grow_memory, read_obj, write_obj};
use crate::storage::ptr_cache::PtrCache;
use crate::storage::stable::ZEROES;
use crate::storage::Error;
use crate::storage::types::ZEROES;
use ic_stable_structures::memory_manager::VirtualMemory;
use ic_stable_structures::BTreeMap;
use ic_stable_structures::Memory;
Expand Down Expand Up @@ -64,11 +63,6 @@ impl MetadataCache {
let meta = (*self.meta).borrow();
meta.get(&node).cloned()
}

pub fn get_ptr(&self, node: Node) -> std::option::Option<FileChunkPtr> {
let meta = (*self.meta).borrow();
meta.get(&node).and_then(|x| x.1)
}
}

pub(crate) struct MetadataProvider<M: Memory> {
Expand Down Expand Up @@ -119,32 +113,29 @@ impl<M: Memory> MetadataProvider<M> {
write_obj(v2_chunks, ptr, meta);
}

// try to get metadata and the data pointer, or return None if not found.
pub(crate) fn get_metadata(
&self,
node: Node,
is_mounted: bool,
v2_chunk_ptr: &BTreeMap<(Node, FileChunkIndex), FileChunkPtr, VirtualMemory<M>>,
v2_chunks: &VirtualMemory<M>,
) -> Result<Metadata, crate::error::Error> {
) -> Option<(Metadata, Option<FileChunkPtr>)> {
let (meta_index, meta_storage, meta_cache) = if is_mounted {
(
MOUNTED_METADATA_CHUNK_INDEX,
&self.metadata,
&self.meta_cache,
)
} else {
(
METADATA_CHUNK_INDEX,
&self.mounted_meta,
&self.mounted_meta_cache,
)
} else {
(METADATA_CHUNK_INDEX, &self.metadata, &self.meta_cache)
};

// try to get meta from cache
let meta_rec = meta_cache.get(node);

if let Some(meta_rec) = meta_rec {
return Ok(meta_rec.0);
return Some((meta_rec.0.clone(), meta_rec.1));
}

// meta not found in cache, try to get it from the file chunks
Expand All @@ -157,14 +148,14 @@ impl<M: Memory> MetadataProvider<M> {
// update cache
meta_cache.update(node, &meta, Some(meta_ptr));

return Ok(meta);
return Some((meta, Some(meta_ptr)));
}

// meta not found in chunks, try to get it from the storage
let meta_found: Result<Metadata, Error> = meta_storage.get(&node).ok_or(Error::NotFound);
let meta_found = meta_storage.get(&node);

if meta_found.is_err() {
// root node is not found on the new file system, just return the generated one.
if meta_found.is_none() {
// if root node is not found on the new file system, just return the generated one.
if node == ROOT_NODE {
let metadata = Metadata {
node: ROOT_NODE,
Expand All @@ -178,40 +169,40 @@ impl<M: Memory> MetadataProvider<M> {
maximum_size_allowed: None,
};

return Ok(metadata);
return Some((metadata, None));
}
}

// return error, if not found
// return None, if no metadata was found under given node
let metadata = meta_found?;

// update cache
meta_cache.update(node, &metadata, None);

Ok(metadata)
Some((metadata, None))
}

// put new metadata value, while overwriting the existing record, at this point the metadata should already be validated
pub(crate) fn put_metadata(
&mut self,
node: u64,
is_mounted: bool,
metadata: &Metadata,
meta_ptr: Option<FileChunkPtr>,
v2_chunk_ptr: &mut BTreeMap<(Node, FileChunkIndex), FileChunkPtr, VirtualMemory<M>>,
v2_chunks: &mut VirtualMemory<M>,
v2_allocator: &mut ChunkPtrAllocator<M>,
) {
assert_eq!(node, metadata.node, "Node does not match metadata.node!");

let (meta_index, meta_cache) = if is_mounted {
(MOUNTED_METADATA_CHUNK_INDEX, &self.meta_cache)
(MOUNTED_METADATA_CHUNK_INDEX, &self.mounted_meta_cache)
} else {
(METADATA_CHUNK_INDEX, &self.mounted_meta_cache)
(METADATA_CHUNK_INDEX, &self.meta_cache)
};

// try to get meta pointer from cache or else chunk pointers or else create a new chunk
let mut meta_ptr = if let Some(meta_ptr) = meta_cache.get_ptr(node) {
meta_ptr
} else if let Some(meta_ptr) = v2_chunk_ptr.get(&(node, meta_index)) {
// create a new meta pointer if it was not
let meta_ptr = if let Some(meta_ptr) = meta_ptr {
meta_ptr
} else {
let meta_ptr = v2_allocator.allocate();
Expand Down
Loading

0 comments on commit b844377

Please sign in to comment.