From 7dd6bcb7dbb9e1fd86d67d308521408b441dcce1 Mon Sep 17 00:00:00 2001 From: Gitea Date: Mon, 18 Jul 2022 13:26:48 +0300 Subject: [PATCH] Implemented ChunkedFile --- src/fsys/file.rs | 154 +++++++++++++++++++++++++++++++++++++--- src/nodes/mod.rs | 1 + src/nodes/node.rs | 0 src/protocol/packets.rs | 14 +++- 4 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 src/nodes/mod.rs create mode 100644 src/nodes/node.rs diff --git a/src/fsys/file.rs b/src/fsys/file.rs index 46cba39..dadadf6 100644 --- a/src/fsys/file.rs +++ b/src/fsys/file.rs @@ -14,6 +14,10 @@ GNU Affero General Public License for more details. */ use crate::util::error::Error; +use std::io::SeekFrom; +use std::io::Seek; +use std::io::Write; +use std::io::Read; pub struct ChunkedFile { pub size: u128, @@ -23,19 +27,66 @@ pub struct ChunkedFile { } impl ChunkedFile { - pub fn new(path: &std::path::Path, chunksize: u128) -> Result { + fn empty() -> ChunkedFile { + return ChunkedFile{ + size: 0, + chunk_size: 0, + chunks_amount: 0, + path: std::path::PathBuf::new(), + } + } + + /// Create a new, ready-to-write chunked regular file + pub fn new(path: &std::path::Path, size: u128, chunk_size: u128) -> Result { + let file_handle: std::fs::File; + match std::fs::File::create(path) { + Ok(fh) => { + file_handle = fh; + } + + Err(error) => { + return Err( + Error::new(format!("{}", error).as_str()) + ); + } + } + + let mut new_chunked_file: ChunkedFile = ChunkedFile::empty(); + + // path + new_chunked_file.path = path.to_path_buf(); + + // resize file + match file_handle.set_len(size as u64) { + Ok(()) => {} + Err(error) => { + return Err( + Error::new(format!("could not resize \'{}\': {}", path.display(), error).as_str()) + ); + } + } + new_chunked_file.size = size; + + // chunks + new_chunked_file.chunk_size = chunk_size; + if new_chunked_file.size % chunk_size != 0 { + new_chunked_file.chunks_amount = (new_chunked_file.size / chunk_size) + 1; + } else { + new_chunked_file.chunks_amount = new_chunked_file.size / chunk_size; + } + + return Ok(new_chunked_file); + } + + /// Create a new, ready-to-read chunked file from already existing regular file + pub fn from(path: &std::path::Path, chunk_size: u128) -> Result { if !path.is_file() { return Err( Error::new(format!("\'{}\' does not exist or not a regular file", path.display()).as_str()) ); } - let mut new_chunked_file: ChunkedFile = ChunkedFile{ - size: 0, - chunk_size: 0, - chunks_amount: 0, - path: std::path::Path::new("").to_path_buf(), - }; + let mut new_chunked_file: ChunkedFile = ChunkedFile::empty(); // path new_chunked_file.path = path.to_path_buf(); @@ -53,17 +104,98 @@ impl ChunkedFile { } // chunks - new_chunked_file.chunk_size = chunksize; - new_chunked_file.chunks_amount = (new_chunked_file.size as f32 / chunksize as f32).ceil() as u128; + new_chunked_file.chunk_size = chunk_size; + if new_chunked_file.size % chunk_size != 0 { + new_chunked_file.chunks_amount = (new_chunked_file.size / chunk_size) + 1; + } else { + new_chunked_file.chunks_amount = new_chunked_file.size / chunk_size; + } return Ok(new_chunked_file); } - pub fn read_chunk(&self, buf: &[u8], chunk_no: u128) -> Option { + pub fn read_chunk(&self, buf: &mut [u8], chunk_no: u128) -> Option { + if chunk_no > self.chunks_amount { + return Some(Error::new("chunk number is bigger than there are chunks")); + } + + if (buf.len() as u128) < self.chunk_size { + return Some(Error::new("buffer is too small to fit a chunk")); + } + + // open underlying file + let mut file_handle: std::fs::File; + match std::fs::File::open(&self.path) { + Ok(fh) => { + file_handle = fh; + } + Err(error) => { + return Some(Error::new(format!("{}", error).as_str())); + } + } + + // set position and read + let chunk_start_pos: u128 = self.chunk_size * chunk_no; + match file_handle.seek(SeekFrom::Start(chunk_start_pos as u64)) { + Ok(_) => {} + Err(error) => { + return Some(Error::new(format!("seek error: {}", error).as_str())) + } + } + + match file_handle.read(buf) { + Ok(_) => {} + Err(error) => { + return Some(Error::new(format!("read error: {}", error).as_str())) + } + } + return None; } - pub fn write_chunk(&mut self, chunk: &[u8] ,chunk_no: u128) -> Option { + pub fn write_chunk(&mut self, chunk: &[u8], chunk_no: u128) -> Option { + if chunk_no > self.chunks_amount { + // can't write outsize of pre-determined boundaries of a file ! + return Some(Error::new("chunk number is bigger than there are chunks")); + } + + if (chunk.len() as u128) < self.chunk_size && chunk_no != self.chunks_amount { + // unfull chunk in the middle of a file + return Some(Error::new("cannot write unfull chunk in a not last position")); + } + + if (chunk.len() as u128) > self.chunk_size { + // too big chunk + return Some(Error::new("chunk is too big")); + } + + // open file and write a new chunk + let mut file_handle: std::fs::File; + match std::fs::File::options().truncate(false).write(true).create(true).open(&self.path) { + Ok(file) => { + file_handle = file; + } + + Err(error) => { + return Some(Error::new(format!("{}", error).as_str())); + } + } + + let chunk_start_pos: u128 = self.chunk_size * chunk_no; + match file_handle.seek(SeekFrom::Start(chunk_start_pos as u64)) { + Ok(_) => {} + Err(error) => { + return Some(Error::new(format!("seek error: {}", error).as_str())); + } + } + + match file_handle.write_all(chunk) { + Ok(()) => {} + Err(error) => { + return Some(Error::new(format!("write error: {}", error).as_str())) + } + } + return None; } } \ No newline at end of file diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs new file mode 100644 index 0000000..12e2c60 --- /dev/null +++ b/src/nodes/mod.rs @@ -0,0 +1 @@ +pub mod node; \ No newline at end of file diff --git a/src/nodes/node.rs b/src/nodes/node.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/protocol/packets.rs b/src/protocol/packets.rs index 95e1833..5678b87 100644 --- a/src/protocol/packets.rs +++ b/src/protocol/packets.rs @@ -110,7 +110,7 @@ impl FileInfoPacket { }; } - pub fn new(file: ChunkedFile, file_id: u128, rel_path: String) -> Result { + pub fn new(file: &ChunkedFile, file_id: u128, rel_path: String) -> Result { let mut new_fileinfo_packet: FileInfoPacket = FileInfoPacket::empty(); // id @@ -175,6 +175,12 @@ impl Packet for FileInfoPacket { } fn from_bytes(&mut self, packet_bytes: Vec) -> Option { + if packet_bytes.len() < 16+16+16+16+1+1 { + return Some( + Error::new(format!("{} bytes is too small for a fileinfo packet to be valid", packet_bytes.len()).as_str() + )); + } + // file id self.file_id = read_u128_slice(&packet_bytes[0..16]); @@ -236,6 +242,12 @@ impl Packet for FileDataPacket { } fn from_bytes(&mut self, packet_bytes: Vec) -> Option { + if packet_bytes.len() < 16+16+16+1 { + return Some( + Error::new(format!("{} bytes is too small for a fileinfo packet to be valid", packet_bytes.len()).as_str() + )); + } + // file id self.file_id = read_u128_slice(&packet_bytes[0..16]);