diff --git a/src/fs.rs b/src/fs.rs index 0fd9a67..7525dde 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -100,6 +100,8 @@ impl FileSystem { } } + // mounte memory file on the top of the given file name, if the file does not exist, it will be created. + // The method fails if the file system couldn't create the file. pub fn mount_memory_file( &mut self, filename: &str, @@ -122,6 +124,24 @@ impl FileSystem { Ok(()) } + // initialize mounted memory with the data stored in the host file + pub fn init_memory_file(&mut self, filename: &str) -> Result<(), Error> { + // create a file for the mount + let fd = self.open_or_create( + self.root_fd, + filename, + FdStat::default(), + OpenFlags::empty(), + 0, + )?; + + let node = self.get_node(fd)?; + self.close(fd)?; + + self.storage.init_mounted_memory(node) + } + + // helper method to set the size of the mounted memory pub fn set_memory_file_size( &mut self, filename: &str, @@ -144,6 +164,42 @@ impl FileSystem { Ok(()) } + // store content of the currently active memory file to the file system + pub fn store_memory_file(&mut self, filename: &str) -> Result<(), Error> { + // create a file for the mount + let fd = self.open_or_create( + self.root_fd, + filename, + FdStat::default(), + OpenFlags::empty(), + 0, + )?; + + let node = self.get_node(fd)?; + self.close(fd)?; + + self.storage.store_mounted_memory(node) + } + + // Unmount memory file, the system will continue to work with its own memory + pub fn unmount_memory_file(&mut self, filename: &str) -> Result, Error> { + // create a file for the mount + let fd = self.open_or_create( + self.root_fd, + filename, + FdStat::default(), + OpenFlags::empty(), + 0, + )?; + + let node = self.get_node(fd)?; + self.close(fd)?; + + let memory = self.storage.unmount_node(node)?; + + Ok(memory) + } + // Get dir entry for a given directory and the directory index. pub fn get_direntry(&self, fd: Fd, index: DirEntryIndex) -> Result { self.get_dir(fd)?.get_entry(index, self.storage.as_ref()) @@ -514,7 +570,7 @@ mod tests { structure_helpers::find_node, types::{FdStat, OpenFlags}, }, - storage::types::FileType, + storage::types::{FileSize, FileType}, test_utils::{ new_vector_memory, read_text_file, test_fs, test_fs_setups, test_fs_transient, write_text_fd, write_text_file, @@ -1277,6 +1333,54 @@ mod tests { println!("{:?}", buf); } + #[test] + fn mounted_memory_store_and_init_roundtrip() { + for mut fs in test_fs_setups("") { + let memory1: VectorMemory = new_vector_memory(); + let memory2: VectorMemory = new_vector_memory(); + + let file_name = "test.txt"; + + fs.mount_memory_file(file_name, Box::new(memory1.clone())) + .unwrap(); + + let content = "ABCDEFG123"; + let len = content.len(); + let count = 1000; + + memory1.grow(5); + + // fill up memory with some data + for i in 0..count { + memory1.write(i as u64 * len as u64, content.as_bytes()); + } + + fs.set_memory_file_size(file_name, len as FileSize * count as FileSize) + .unwrap(); + + // store memory into a file + fs.store_memory_file(file_name).unwrap(); + + fs.unmount_memory_file(file_name).unwrap(); + + fs.mount_memory_file(file_name, Box::new(memory2.clone())) + .unwrap(); + + // init new memory into a file + fs.init_memory_file(file_name).unwrap(); + + let mut buf1 = [0u8; 10]; + let mut buf2 = [0u8; 10]; + + for i in 0..count { + memory1.read(i as u64 * len as u64, &mut buf1); + memory2.read(i as u64 * len as u64, &mut buf2); + + assert_eq!(buf1, buf2); + } + } + } + #[test] fn writing_from_different_file_descriptors() { for mut fs in test_fs_setups("f1/f2/text.txt") { diff --git a/src/runtime.rs b/src/runtime.rs index e246fff..502e0c2 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,5 +1,5 @@ pub mod dir; pub mod fd; pub mod file; -pub mod structure_helpers; +pub(crate) mod structure_helpers; pub mod types; diff --git a/src/runtime/file.rs b/src/runtime/file.rs index 3d65ba0..eb6ab8e 100644 --- a/src/runtime/file.rs +++ b/src/runtime/file.rs @@ -2,7 +2,7 @@ use crate::{ error::Error, runtime::types::{FdFlags, FdStat, Whence}, storage::{ - types::{FileChunkIndex, FileSize, FileType, Node, FILE_CHUNK_SIZE}, + types::{FileSize, FileType, Node}, Storage, }, }; @@ -126,6 +126,8 @@ impl File { buf: &mut [u8], storage: &mut dyn Storage, ) -> Result { + use super::structure_helpers::get_chunk_infos; + if buf.is_empty() { return Ok(0 as FileSize); } @@ -155,26 +157,7 @@ impl File { buf: &[u8], storage: &mut dyn Storage, ) -> Result { - let mut metadata = storage.get_metadata(self.node)?; - let end = offset + buf.len() as FileSize; - let chunk_infos = get_chunk_infos(offset, end); - let mut written_size = 0; - for chunk in chunk_infos.into_iter() { - storage.write_filechunk( - self.node, - chunk.index, - chunk.offset, - &buf[written_size..written_size + chunk.len as usize], - ); - written_size += chunk.len as usize; - } - - if end > metadata.size { - metadata.size = end; - storage.put_metadata(self.node, metadata) - } - - Ok(written_size as FileSize) + storage.write_with_offset(self.node, offset, buf) } // Truncate file to 0 size. @@ -186,41 +169,6 @@ impl File { } } -#[derive(Debug, PartialEq, Eq)] -struct ChunkHandle { - index: FileChunkIndex, - offset: FileSize, - len: FileSize, -} - -fn offset_to_file_chunk_index(offset: FileSize) -> FileChunkIndex { - (offset / FILE_CHUNK_SIZE as FileSize) as FileChunkIndex -} - -fn file_chunk_index_to_offset(index: FileChunkIndex) -> FileSize { - index as FileSize * FILE_CHUNK_SIZE as FileSize -} - -fn get_chunk_infos(start: FileSize, end: FileSize) -> Vec { - let mut result = vec![]; - let start_index = offset_to_file_chunk_index(start); - let end_index = offset_to_file_chunk_index(end); - for index in start_index..=end_index { - let start_of_chunk = file_chunk_index_to_offset(index); - assert!(start_of_chunk <= end); - let start_in_chunk = start_of_chunk.max(start) - start_of_chunk; - let end_in_chunk = (start_of_chunk + FILE_CHUNK_SIZE as FileSize).min(end) - start_of_chunk; - if start_in_chunk < end_in_chunk { - result.push(ChunkHandle { - index, - offset: start_in_chunk, - len: end_in_chunk - start_in_chunk, - }); - } - } - result -} - #[cfg(test)] mod tests { use crate::{ @@ -230,55 +178,6 @@ mod tests { use super::*; - #[test] - fn get_chunk_infos_parital() { - let chunks = get_chunk_infos( - FILE_CHUNK_SIZE as FileSize - 1, - 2 * FILE_CHUNK_SIZE as FileSize + 1, - ); - assert_eq!( - chunks[0], - ChunkHandle { - index: 0, - offset: FILE_CHUNK_SIZE as FileSize - 1, - len: 1 - } - ); - assert_eq!( - chunks[1], - ChunkHandle { - index: 1, - offset: 0, - len: FILE_CHUNK_SIZE as FileSize, - } - ); - - assert_eq!( - chunks[2], - ChunkHandle { - index: 2, - offset: 0, - len: 1, - } - ); - } - - #[test] - fn get_chunk_infos_full() { - let chunks = get_chunk_infos(0, 10 * FILE_CHUNK_SIZE as FileSize); - #[allow(clippy::needless_range_loop)] - for i in 0..10 { - assert_eq!( - chunks[i], - ChunkHandle { - index: i as FileChunkIndex, - offset: 0, - len: FILE_CHUNK_SIZE as FileSize, - } - ); - } - } - #[test] fn seek_and_tell() { let mut fs = test_fs(); diff --git a/src/runtime/structure_helpers.rs b/src/runtime/structure_helpers.rs index 1fba20a..2309b33 100644 --- a/src/runtime/structure_helpers.rs +++ b/src/runtime/structure_helpers.rs @@ -1,7 +1,13 @@ +use ic_cdk::api::stable::WASM_PAGE_SIZE_IN_BYTES; +use ic_stable_structures::Memory; + use crate::{ error::Error, storage::{ - types::{DirEntry, DirEntryIndex, FileName, FileType, Metadata, Node, Times}, + types::{ + ChunkHandle, DirEntry, DirEntryIndex, FileChunkIndex, FileName, FileSize, FileType, + Metadata, Node, Times, FILE_CHUNK_SIZE, + }, Storage, }, }; @@ -383,6 +389,44 @@ pub fn rm_dir_entry( Ok((removed_dir_entry_node, removed_metadata)) } +pub fn grow_memory(memory: &dyn Memory, max_address: FileSize) { + let pages_required = (max_address + WASM_PAGE_SIZE_IN_BYTES - 1) / WASM_PAGE_SIZE_IN_BYTES; + + let cur_pages = memory.size(); + + if cur_pages < pages_required { + memory.grow(pages_required - cur_pages); + } +} + +pub fn offset_to_file_chunk_index(offset: FileSize) -> FileChunkIndex { + (offset / FILE_CHUNK_SIZE as FileSize) as FileChunkIndex +} + +pub fn file_chunk_index_to_offset(index: FileChunkIndex) -> FileSize { + index as FileSize * FILE_CHUNK_SIZE as FileSize +} + +pub fn get_chunk_infos(start: FileSize, end: FileSize) -> Vec { + let mut result = vec![]; + let start_index = offset_to_file_chunk_index(start); + let end_index = offset_to_file_chunk_index(end); + for index in start_index..=end_index { + let start_of_chunk = file_chunk_index_to_offset(index); + assert!(start_of_chunk <= end); + let start_in_chunk = start_of_chunk.max(start) - start_of_chunk; + let end_in_chunk = (start_of_chunk + FILE_CHUNK_SIZE as FileSize).min(end) - start_of_chunk; + if start_in_chunk < end_in_chunk { + result.push(ChunkHandle { + index, + offset: start_in_chunk, + len: end_in_chunk - start_in_chunk, + }); + } + } + result +} + #[cfg(test)] mod tests { @@ -390,10 +434,63 @@ mod tests { use crate::{ error::Error, - runtime::structure_helpers::{create_path, find_node}, - storage::{stable::StableStorage, types::FileType, Storage}, + runtime::structure_helpers::{create_path, find_node, get_chunk_infos}, + storage::{ + stable::StableStorage, + types::{ChunkHandle, FileChunkIndex, FileSize, FileType, FILE_CHUNK_SIZE}, + Storage, + }, }; + #[test] + fn get_chunk_infos_parital() { + let chunks = get_chunk_infos( + FILE_CHUNK_SIZE as FileSize - 1, + 2 * FILE_CHUNK_SIZE as FileSize + 1, + ); + assert_eq!( + chunks[0], + ChunkHandle { + index: 0, + offset: FILE_CHUNK_SIZE as FileSize - 1, + len: 1 + } + ); + assert_eq!( + chunks[1], + ChunkHandle { + index: 1, + offset: 0, + len: FILE_CHUNK_SIZE as FileSize, + } + ); + + assert_eq!( + chunks[2], + ChunkHandle { + index: 2, + offset: 0, + len: 1, + } + ); + } + + #[test] + fn get_chunk_infos_full() { + let chunks = get_chunk_infos(0, 10 * FILE_CHUNK_SIZE as FileSize); + #[allow(clippy::needless_range_loop)] + for i in 0..10 { + assert_eq!( + chunks[i], + ChunkHandle { + index: i as FileChunkIndex, + offset: 0, + len: FILE_CHUNK_SIZE as FileSize, + } + ); + } + } + #[test] fn create_path_with_subfolders() { let mut storage_box = Box::new(StableStorage::new(DefaultMemoryImpl::default())); diff --git a/src/storage.rs b/src/storage.rs index 4ad0c45..4a4eb53 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -30,6 +30,11 @@ pub trait Storage { // return mounted memory related to the node, or None fn get_mounted_memory(&self, node: Node) -> Option<&dyn Memory>; + // initialize memory with the contents from file + fn init_mounted_memory(&mut self, node: Node) -> Result<(), Error>; + // store mounted memory state back to host file + fn store_mounted_memory(&mut self, node: Node) -> Result<(), Error>; + // Get the metadata associated with the node. fn get_metadata(&self, node: Node) -> Result; // Update the metadata associated with the node. @@ -64,7 +69,15 @@ pub trait Storage { ) -> Result; // Insert of update a selected file chunk with the data provided in buffer. - fn write_filechunk(&mut self, node: Node, index: FileChunkIndex, offset: FileSize, buf: &[u8]); + //fn write_filechunk(&mut self, node: Node, index: FileChunkIndex, offset: FileSize, buf: &[u8]); + + // Write file at the current file cursor, the cursor position will NOT be updated after reading. + fn write_with_offset( + &mut self, + node: Node, + offset: FileSize, + buf: &[u8], + ) -> Result; // Remove file chunk from a given file node. fn rm_filechunk(&mut self, node: Node, index: FileChunkIndex); diff --git a/src/storage/dummy.rs b/src/storage/dummy.rs index ca7b566..d7ae5b2 100644 --- a/src/storage/dummy.rs +++ b/src/storage/dummy.rs @@ -67,16 +67,6 @@ impl Storage for DummyStorage { panic!("Not supported") } - fn write_filechunk( - &mut self, - _node: Node, - _index: FileChunkIndex, - _offset_in_first_chunk: FileSize, - _buf: &[u8], - ) { - panic!("Not supported") - } - fn rm_filechunk(&mut self, _node: Node, _index: FileChunkIndex) { panic!("Not supported") } @@ -113,6 +103,23 @@ impl Storage for DummyStorage { fn get_mounted_memory(&self, _node: Node) -> Option<&dyn ic_stable_structures::Memory> { panic!("Not supported") } + + fn init_mounted_memory(&mut self, _node: Node) -> Result<(), Error> { + panic!("Not supported") + } + + fn store_mounted_memory(&mut self, _node: Node) -> Result<(), Error> { + panic!("Not supported") + } + + fn write_with_offset( + &mut self, + _node: Node, + _offset: FileSize, + _buf: &[u8], + ) -> Result { + panic!("Not supported") + } } #[cfg(test)] @@ -205,13 +212,6 @@ mod tests { let _ = storage.read_filechunk(0, 0, 0, &mut []); } - #[test] - #[should_panic] - fn write_filechunk_panic() { - let mut storage = DummyStorage::new(); - storage.write_filechunk(0, 0, 0, &[]); - } - #[test] #[should_panic] fn rm_filechunk_panic() { diff --git a/src/storage/stable.rs b/src/storage/stable.rs index ea54fbe..b80ff72 100644 --- a/src/storage/stable.rs +++ b/src/storage/stable.rs @@ -6,7 +6,10 @@ use ic_stable_structures::{ BTreeMap, Cell, Memory, }; -use crate::error::Error; +use crate::{ + error::Error, + runtime::structure_helpers::{get_chunk_infos, grow_memory}, +}; use super::{ types::{ @@ -138,6 +141,27 @@ impl StableStorage { result } + + // Insert of update a selected file chunk with the data provided in a buffer. + fn write_filechunk(&mut self, node: Node, index: FileChunkIndex, offset: FileSize, buf: &[u8]) { + if let Some(memory) = self.get_mounted_memory(node) { + // grow memory if needed + let max_address = index as FileSize * FILE_CHUNK_SIZE as FileSize + + offset as FileSize + + buf.len() as FileSize; + + grow_memory(memory, max_address); + + // store data + let address = index as FileSize * FILE_CHUNK_SIZE as FileSize + offset as FileSize; + + memory.write(address, buf); + } else { + let mut entry = self.filechunk.get(&(node, index)).unwrap_or_default(); + entry.bytes[offset as usize..offset as usize + buf.len()].copy_from_slice(buf); + self.filechunk.insert((node, index), entry); + } + } } impl Storage for StableStorage { @@ -287,31 +311,33 @@ impl Storage for StableStorage { Ok(size_read) } - // Insert of update a selected file chunk with the data provided in a buffer. - fn write_filechunk(&mut self, node: Node, index: FileChunkIndex, offset: FileSize, buf: &[u8]) { - if let Some(memory) = self.active_mounts.get(&node) { - // grow memory if needed - let max_address = index as FileSize * FILE_CHUNK_SIZE as FileSize - + offset as FileSize - + buf.len() as FileSize; - let pages_required = - (max_address + WASM_PAGE_SIZE_IN_BYTES - 1) / WASM_PAGE_SIZE_IN_BYTES; - - let cur_pages = memory.size(); - - if cur_pages < pages_required { - memory.grow(pages_required - cur_pages); - } - - // store data - let address = index as FileSize * FILE_CHUNK_SIZE as FileSize + offset as FileSize; + // Write file at the current file cursor, the cursor position will NOT be updated after reading. + fn write_with_offset( + &mut self, + node: Node, + offset: FileSize, + buf: &[u8], + ) -> Result { + let mut metadata = self.get_metadata(node)?; + let end = offset + buf.len() as FileSize; + let chunk_infos = get_chunk_infos(offset, end); + let mut written_size = 0; + for chunk in chunk_infos.into_iter() { + self.write_filechunk( + node, + chunk.index, + chunk.offset, + &buf[written_size..written_size + chunk.len as usize], + ); + written_size += chunk.len as usize; + } - memory.write(address, buf); - } else { - let mut entry = self.filechunk.get(&(node, index)).unwrap_or_default(); - entry.bytes[offset as usize..offset as usize + buf.len()].copy_from_slice(buf); - self.filechunk.insert((node, index), entry); + if end > metadata.size { + metadata.size = end; + self.put_metadata(node, metadata) } + + Ok(written_size as FileSize) } // Remove file chunk from a given file node. @@ -358,6 +384,76 @@ impl Storage for StableStorage { res.map(|b| b.as_ref()) } + + fn init_mounted_memory(&mut self, node: Node) -> Result<(), Error> { + // temporary disable mount to activate access to the original file + let memory = self.unmount_node(node)?; + + let meta = self.get_metadata(node)?; + let file_size = meta.size; + println!("stored file size {file_size}"); + + // grow memory if needed + grow_memory(memory.as_ref(), file_size); + + let mut remainder = file_size; + + let mut buf = [0u8; WASM_PAGE_SIZE_IN_BYTES as usize]; + + let mut offset = 0; + + while remainder > 0 { + let to_read = remainder.min(buf.len() as FileSize); + + self.read_range(node, offset, file_size, &mut buf[..to_read as usize])?; + + memory.write(offset, &buf[..to_read as usize]); + + offset += to_read; + remainder -= to_read; + } + + self.mount_node(node, memory)?; + + self.put_metadata(node, meta); + + Ok(()) + } + + fn store_mounted_memory(&mut self, node: Node) -> Result<(), Error> { + // get current size of the mounted memory + let meta = self.get_metadata(node)?; + let file_size = meta.size; + + // temporary disable mount to activate access to the original file + let memory = self.unmount_node(node)?; + + // grow memory if needed + grow_memory(memory.as_ref(), file_size); + + let mut remainder = file_size; + + let mut buf = [0u8; WASM_PAGE_SIZE_IN_BYTES as usize]; + + let mut offset = 0; + + while remainder > 0 { + let to_read = remainder.min(buf.len() as FileSize); + + memory.read(offset, &mut buf[..to_read as usize]); + + self.write_with_offset(node, offset, &buf[..to_read as usize])?; + + offset += to_read; + remainder -= to_read; + } + + self.put_metadata(node, meta); + + self.mount_node(node, memory)?; + + Ok(()) + } } #[cfg(test)] diff --git a/src/storage/transient.rs b/src/storage/transient.rs index 3864185..65ca43f 100644 --- a/src/storage/transient.rs +++ b/src/storage/transient.rs @@ -5,11 +5,14 @@ use ic_stable_structures::Memory; use crate::{ error::Error, - storage::types::{ - DirEntry, DirEntryIndex, FileChunk, FileChunkIndex, FileSize, FileType, Metadata, Node, - Times, + runtime::structure_helpers::{get_chunk_infos, grow_memory}, + storage::{ + types::{ + DirEntry, DirEntryIndex, FileChunk, FileChunkIndex, FileSize, FileType, Metadata, Node, + Times, + }, + Storage, }, - storage::Storage, }; use super::types::{Header, FILE_CHUNK_SIZE}; @@ -62,6 +65,25 @@ impl TransientStorage { result.put_metadata(ROOT_NODE, metadata); result } + + // Insert of update a selected file chunk with the data provided in buffer. + fn write_filechunk(&mut self, node: Node, index: FileChunkIndex, offset: FileSize, buf: &[u8]) { + if let Some(memory) = self.get_mounted_memory(node) { + // grow memory if needed + let max_address = index as FileSize * FILE_CHUNK_SIZE as FileSize + + offset as FileSize + + buf.len() as FileSize; + + grow_memory(memory, max_address); + + // store data + let address = index as FileSize * FILE_CHUNK_SIZE as FileSize + offset as FileSize; + memory.write(address, buf); + } else { + let entry = self.filechunk.entry((node, index)).or_default(); + entry.bytes[offset as usize..offset as usize + buf.len()].copy_from_slice(buf) + } + } } impl Storage for TransientStorage { @@ -208,31 +230,6 @@ impl Storage for TransientStorage { Ok(size_read) } - // Insert of update a selected file chunk with the data provided in buffer. - fn write_filechunk(&mut self, node: Node, index: FileChunkIndex, offset: FileSize, buf: &[u8]) { - if let Some(memory) = self.get_mounted_memory(node) { - // grow memory if needed - let max_address = index as FileSize * FILE_CHUNK_SIZE as FileSize - + offset as FileSize - + buf.len() as FileSize; - let pages_required = - (max_address + WASM_PAGE_SIZE_IN_BYTES - 1) / WASM_PAGE_SIZE_IN_BYTES; - - let cur_pages = memory.size(); - - if cur_pages < pages_required { - memory.grow(pages_required - cur_pages); - } - - // store data - let address = index as FileSize * FILE_CHUNK_SIZE as FileSize + offset as FileSize; - memory.write(address, buf); - } else { - let entry = self.filechunk.entry((node, index)).or_default(); - entry.bytes[offset as usize..offset as usize + buf.len()].copy_from_slice(buf) - } - } - // Remove file chunk from a given file node. fn rm_filechunk(&mut self, node: Node, index: FileChunkIndex) { self.filechunk.remove(&(node, index)); @@ -277,6 +274,103 @@ impl Storage for TransientStorage { res.map(|b| b.as_ref()) } + + fn init_mounted_memory(&mut self, node: Node) -> Result<(), Error> { + // temporary disable mount to activate access to the original file + let memory = self.unmount_node(node)?; + + let meta = self.get_metadata(node)?; + let file_size = meta.size; + + // grow memory if needed + grow_memory(memory.as_ref(), file_size); + + let mut remainder = file_size; + + let mut buf = [0u8; WASM_PAGE_SIZE_IN_BYTES as usize]; + + let mut offset = 0; + + while remainder > 0 { + let to_read = remainder.min(buf.len() as FileSize); + + self.read_range(node, offset, file_size, &mut buf[..to_read as usize])?; + + memory.write(offset, &buf[..to_read as usize]); + + offset += to_read; + remainder -= to_read; + } + + self.mount_node(node, memory)?; + + self.put_metadata(node, meta); + + Ok(()) + } + + fn store_mounted_memory(&mut self, node: Node) -> Result<(), Error> { + // get current size of the mounted memory + let meta = self.get_metadata(node)?; + let file_size = meta.size; + + // temporary disable mount to activate access to the original file + let memory = self.unmount_node(node)?; + + // grow memory if needed + grow_memory(memory.as_ref(), file_size); + + let mut remainder = file_size; + + let mut buf = [0u8; WASM_PAGE_SIZE_IN_BYTES as usize]; + + let mut offset = 0; + + while remainder > 0 { + let to_read = remainder.min(buf.len() as FileSize); + + memory.read(offset, &mut buf[..to_read as usize]); + + self.write_with_offset(node, offset, &buf[..to_read as usize])?; + + offset += to_read; + remainder -= to_read; + } + + self.put_metadata(node, meta); + + self.mount_node(node, memory)?; + + Ok(()) + } + + fn write_with_offset( + &mut self, + node: Node, + offset: FileSize, + buf: &[u8], + ) -> Result { + let mut metadata = self.get_metadata(node)?; + let end = offset + buf.len() as FileSize; + let chunk_infos = get_chunk_infos(offset, end); + let mut written_size = 0; + for chunk in chunk_infos.into_iter() { + self.write_filechunk( + node, + chunk.index, + chunk.offset, + &buf[written_size..written_size + chunk.len as usize], + ); + written_size += chunk.len as usize; + } + + if end > metadata.size { + metadata.size = end; + self.put_metadata(node, metadata) + } + + Ok(written_size as FileSize) + } } #[cfg(test)] diff --git a/src/storage/types.rs b/src/storage/types.rs index 2e96aea..f1baddc 100644 --- a/src/storage/types.rs +++ b/src/storage/types.rs @@ -15,6 +15,14 @@ pub type FileSize = u64; // An index of a file chunk. pub type FileChunkIndex = u32; +// A handle used for writing files in chunks +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct ChunkHandle { + pub index: FileChunkIndex, + pub offset: FileSize, + pub len: FileSize, +} + // A file consists of multiple file chunks. #[derive(Clone, Debug)] pub struct FileChunk { diff --git a/tests/demo_test_upgraded/Cargo.lock b/tests/demo_test_upgraded/Cargo.lock index 6eea1bf..909c616 100644 --- a/tests/demo_test_upgraded/Cargo.lock +++ b/tests/demo_test_upgraded/Cargo.lock @@ -620,7 +620,7 @@ dependencies = [ [[package]] name = "stable-fs" -version = "0.5.1" +version = "0.6.0" dependencies = [ "bitflags", "ciborium",