use crate::{
error::Error,
runtime::types::{FdFlags, FdStat, Whence},
storage::{
Storage,
types::{FileSize, FileType, Node},
},
};
#[derive(Clone, Debug)]
pub struct File {
pub node: Node,
pub cursor: FileSize,
pub stat: FdStat,
}
impl File {
pub fn new(node: Node, stat: FdStat, storage: &dyn Storage) -> Result<Self, Error> {
let metadata = storage.get_metadata(node)?;
let file_type = metadata.file_type;
match file_type {
FileType::RegularFile => {}
FileType::Directory => {
unreachable!("Unexpected file type, expected a regular file.");
}
FileType::SymbolicLink => unimplemented!("Symbolic links are not implemented yet"),
};
let cursor = if stat.flags.contains(FdFlags::APPEND) {
metadata.size
} else {
0
};
Ok(Self { node, cursor, stat })
}
pub fn seek(
&mut self,
delta: i64,
whence: Whence,
storage: &dyn Storage,
) -> Result<FileSize, Error> {
let size = storage.get_metadata(self.node)?.size;
let position = match whence {
Whence::SET => {
if delta < 0 {
return Err(Error::InvalidArgument);
}
delta as FileSize
}
Whence::CUR => {
let back = if delta < 0 {
(-delta).try_into().map_err(|_| Error::InvalidArgument)?
} else {
0
};
let fwd = if delta >= 0 { delta as FileSize } else { 0 };
if self.cursor < back {
return Err(Error::InvalidArgument);
}
self.cursor + fwd - back
}
Whence::END => {
let back: FileSize = (-delta).try_into().map_err(|_| Error::InvalidSeek)?;
if back > size {
return Err(Error::InvalidSeek);
}
size - back
}
};
self.cursor = position;
Ok(self.cursor)
}
pub fn tell(&self) -> FileSize {
self.cursor
}
pub fn read_with_cursor(
&mut self,
buf: &mut [u8],
storage: &mut dyn Storage,
) -> Result<FileSize, Error> {
let read_size = self.read_with_offset(self.cursor, buf, storage)?;
self.cursor += read_size;
Ok(read_size)
}
pub fn read_with_offset(
&self,
offset: FileSize,
buf: &mut [u8],
storage: &mut dyn Storage,
) -> Result<FileSize, Error> {
if buf.is_empty() {
return Ok(0 as FileSize);
}
let read_size = storage.read(self.node, offset, buf)?;
Ok(read_size as FileSize)
}
pub fn write_with_offset(
&self,
offset: FileSize,
buf: &[u8],
storage: &mut dyn Storage,
) -> Result<FileSize, Error> {
storage.write(self.node, offset, buf)
}
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)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::{
fs::OpenFlags,
test_utils::{test_fs_setups, test_stable_fs_v2},
};
use super::*;
#[test]
fn seek_and_tell() {
let mut fs = test_stable_fs_v2();
let fd = fs
.create_open_file(fs.root_fd(), "test", FdStat::default(), 0)
.unwrap();
let mut file = fs.get_test_file(fd);
let storage = fs.get_test_storage();
file.write_with_offset(0, &[0; 1000], storage).unwrap();
assert_eq!(file.tell(), 0);
let pos = file.seek(10, Whence::CUR, storage).unwrap();
assert_eq!(pos, 10);
assert_eq!(file.tell(), 10);
let pos = file.seek(-9, Whence::CUR, storage).unwrap();
assert_eq!(pos, 1);
assert_eq!(file.tell(), 1);
let err = file.seek(-2, Whence::CUR, storage).unwrap_err();
assert_eq!(err, Error::InvalidArgument);
assert_eq!(file.tell(), 1);
let pos = file.seek(0, Whence::END, storage).unwrap();
assert_eq!(pos, 1000);
assert_eq!(file.tell(), 1000);
let pos = file.seek(500, Whence::SET, storage).unwrap();
assert_eq!(pos, 500);
assert_eq!(file.tell(), 500);
let err = file.seek(-1, Whence::SET, storage).unwrap_err();
assert_eq!(err, Error::InvalidArgument);
assert_eq!(file.tell(), 500);
let pos = file.seek(1001, Whence::SET, storage).unwrap();
assert_eq!(pos, 1001);
assert_eq!(file.tell(), 1001);
}
#[test]
fn read_and_write_offset() {
let mut fs = test_stable_fs_v2();
let fd = fs
.create_open_file(fs.root_fd(), "test", FdStat::default(), 0)
.unwrap();
let mut file = fs.get_test_file(fd);
let storage = fs.get_test_storage();
for i in 0..1000 {
let buf = [(i % 256) as u8; 16];
file.write_with_offset(i * 16, &buf, storage).unwrap();
}
file.seek(-1000 * 16, Whence::END, storage).unwrap();
for i in 0..1000 {
let mut buf = [0; 16];
file.read_with_offset(i * 16, &mut buf, storage).unwrap();
let expected = [(i % 256) as u8; 16];
assert_eq!(buf, expected);
}
}
#[test]
fn read_and_write_small_and_big_buffer() {
let mut fs = test_stable_fs_v2();
let fd = fs
.create_open_file(fs.root_fd(), "test", FdStat::default(), 0)
.unwrap();
let file = fs.get_test_file(fd);
let storage = fs.get_test_storage();
for i in 0..1000 {
let buf = [(i % 256) as u8; 10];
file.write_with_offset(i * 16, &buf, storage).unwrap();
}
for i in 0..1000 {
let mut buf1 = [0; 13];
let mut buf2 = [0; 5000];
let mut buf3 = [0; 15000];
let r1 = file.read_with_offset(i * 17, &mut buf1, storage).unwrap() as usize;
let r2 = file.read_with_offset(i * 17, &mut buf2, storage).unwrap() as usize;
let _r3 = file.read_with_offset(i * 17, &mut buf3, storage).unwrap() as usize;
assert_eq!(buf1[..r1], buf2[..r1]);
assert_eq!(buf2[..r2], buf3[..r2]);
}
}
#[test]
fn read_and_write_offset_chunk() {
for mut fs in [test_stable_fs_v2()] {
let fd = fs
.open(
fs.root_fd(),
"test",
FdStat::default(),
OpenFlags::CREATE,
0,
)
.unwrap();
let mut file = fs.get_test_file(fd);
let storage = fs.get_test_storage();
for i in 0..1000 {
let buf = [(i % 256) as u8; 16];
file.write_with_offset(i * 16, &buf, storage).unwrap();
}
file.seek(-1000 * 16, Whence::END, storage).unwrap();
for i in 0..1000 {
let mut buf = [0; 16];
file.read_with_offset(i * 16, &mut buf, storage).unwrap();
let expected = [(i % 256) as u8; 16];
assert_eq!(buf, expected);
}
}
}
#[test]
fn read_and_write_offset_vs_range() {
for mut fs in test_fs_setups("test") {
let fd = fs
.open(
fs.root_fd(),
"test",
FdStat::default(),
OpenFlags::CREATE,
0,
)
.unwrap();
let file = fs.get_test_file(fd);
let storage = fs.get_test_storage();
for i in 0..1000 {
let buf = [(i % 256) as u8; 16];
file.write_with_offset(i * 16, &buf, storage).unwrap();
}
for i in 0..1000 {
let mut buf1 = [0; 13];
let len1 = file.read_with_offset(i * 16, &mut buf1, storage).unwrap();
let mut buf2 = [0; 13];
let len2 = file.read_with_offset(i * 16, &mut buf2, storage).unwrap();
assert_eq!(buf1, buf2);
assert_eq!(len1, len2);
}
for i in 0..2050 {
let mut buf1 = [0; 5003];
let len1 = file.read_with_offset(i * 13, &mut buf1, storage).unwrap();
let mut buf2 = [0; 5003];
let len2 = file.read_with_offset(i * 13, &mut buf2, storage).unwrap();
assert_eq!(buf1, buf2);
assert_eq!(len1, len2);
}
}
}
}