Skip to main content

FrozenFile

Struct FrozenFile 

Source
pub struct FrozenFile { /* private fields */ }
Expand description

Custom implementation of std::fs::File

§Example

use frozen_core::ffile::{FrozenFile, FFCfg};

const MID: u8 = 0;

let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("tmp_frozen_file");

let cfg = FFCfg {
    chunk_size: 0x10,
    path: path.to_path_buf(),
    initial_chunk_amount: 0x0A,
};

let file = FrozenFile::new::<MID>(cfg.clone()).unwrap();
assert_eq!(file.length().unwrap(), 0x10 * 0x0A);

let mut data = vec![1u8; 0x10];
assert!(file.pwrite(data.as_mut_ptr(), 0).is_ok());
assert!(file.sync().is_ok());

let mut buf = vec![0u8; data.len()];
assert!(file.pread(buf.as_mut_ptr(), 0).is_ok());
assert_eq!(buf, data);

assert!(FrozenFile::new::<MID>(cfg.clone()).is_err());

assert!(file.delete().is_ok());
assert!(!path.exists());

drop(file);
assert!(FrozenFile::new::<MID>(cfg).is_ok());

Implementations§

Source§

impl FrozenFile

Source

pub fn length(&self) -> FrozenResult<usize>

Read current length of FrozenFile

Source

pub fn fd(&self) -> TFileId

Get file descriptor for FrozenFile

Source

pub fn exists(&self) -> FrozenResult<bool>

Check if the FrozenFile exists on the fs

Source

pub fn new<const MODULE_ID: u8>(cfg: FFCfg) -> FrozenResult<Self>

Create a new or open an existing FrozenFile

§FFCfg

All configs for FrozenFile are stored in FFCfg

§Important

The provided FFCfg must remain identical across all reopen cycles of the FrozenFile.

Changing any of the feilds after initial creation, may violate internal layout invariants and cause the file to be treated as corrupted.

§Multiple Instances

Every instance of FrozenFile tries to acquire an exclusive lock, which protects against operating with multiple simultenious instances.

If trying to call FrozenFile::new when already called, [FFileErr::Lck] error will be thrown.

§Example
use frozen_core::ffile::{FrozenFile, FFCfg};

const MID: u8 = 0;

let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("tmp_frozen_file");

let cfg = FFCfg {
    chunk_size: 0x10,
    path: path.to_path_buf(),
    initial_chunk_amount: 0x0A,
};

let file = FrozenFile::new::<MID>(cfg).unwrap();
assert_eq!(file.length().unwrap(), 0x10 * 0x0A);
Source

pub fn sync(&self) -> FrozenResult<()>

Syncs in-mem data on the storage device

Source

pub fn sync_range(&self, index: usize, count: usize) -> FrozenResult<()>

A best-effort call to prompt kernel to start flushing dirty pages in the specified range

This call, by itself, does not guarantee any kind of durability, and must always be paired with strong sync call i.e. FrozenFile::sync

Source

pub fn delete(&self) -> FrozenResult<()>

Delete FrozenFile from fs

§Example
use frozen_core::ffile::{FrozenFile, FFCfg};

const MID: u8 = 0;

let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("tmp_frozen_file");

let cfg = FFCfg {
    chunk_size: 0x10,
    path: path.to_path_buf(),
    initial_chunk_amount: 0x0A,
};

let file = FrozenFile::new::<MID>(cfg).unwrap();
assert!(file.exists().unwrap());

file.delete().unwrap();
assert!(!file.exists().unwrap());
Source

pub fn pread(&self, buf: *mut u8, index: usize) -> FrozenResult<()>

Read a single chunk at given index w/ pread syscall

§Example
use frozen_core::ffile::{FrozenFile, FFCfg};

const MID: u8 = 0;

let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("tmp_frozen_file");

let cfg = FFCfg {
    chunk_size: 0x10,
    path: path.to_path_buf(),
    initial_chunk_amount: 0x0A,
};

let file = FrozenFile::new::<MID>(cfg).unwrap();

let mut data = [7u8; 0x10];
file.pwrite(data.as_mut_ptr(), 2).unwrap();
file.sync().unwrap();

let mut buf = [0u8; 0x10];
file.pread(buf.as_mut_ptr(), 2).unwrap();

assert_eq!(buf, data);
Source

pub fn pwrite(&self, buf: *mut u8, index: usize) -> FrozenResult<()>

Write a single chunk at given index w/ pwrite syscall

§Example
use frozen_core::ffile::{FrozenFile, FFCfg};

const MID: u8 = 0;

let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("tmp_frozen_file");

let cfg = FFCfg {
    chunk_size: 0x10,
    path: path.to_path_buf(),
    initial_chunk_amount: 0x0A,
};

let file = FrozenFile::new::<MID>(cfg).unwrap();

let mut data = [9u8; 0x10];
file.pwrite(data.as_mut_ptr(), 4).unwrap();
file.sync().unwrap();

let mut buf = [0u8; 0x10];
file.pread(buf.as_mut_ptr(), 4).unwrap();

assert_eq!(buf, data);
Source

pub fn preadv(&self, bufs: &[*mut u8], index: usize) -> FrozenResult<()>

Read multiple chunks starting from given index till bufs.len() w/ preadv syscall

§Example
use frozen_core::ffile::{FrozenFile, FFCfg};

const MID: u8 = 0;

let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("tmp_frozen_file");

let cfg = FFCfg {
    chunk_size: 0x10,
    path: path.to_path_buf(),
    initial_chunk_amount: 0x0A,
};

let file = FrozenFile::new::<MID>(cfg).unwrap();

let mut write_bufs = [[1u8; 0x10], [2u8; 0x10]];
let ptrs: Vec<*mut u8> = write_bufs.iter_mut().map(|b| b.as_mut_ptr()).collect();

file.pwritev(&ptrs, 0).unwrap();
file.sync().unwrap();

let mut read_bufs = [[0u8; 0x10], [0u8; 0x10]];
let rptrs: Vec<*mut u8> = read_bufs.iter_mut().map(|b| b.as_mut_ptr()).collect();

file.preadv(&rptrs, 0).unwrap();

assert!(read_bufs[0].iter().all(|b| *b == 1));
assert!(read_bufs[1].iter().all(|b| *b == 2));
Source

pub fn pwritev(&self, bufs: &[*mut u8], index: usize) -> FrozenResult<()>

Write multiple chunks starting from given index till bufs.len() w/ pwritev syscall

§Example
use frozen_core::ffile::{FrozenFile, FFCfg};

const MID: u8 = 0;

let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("tmp_frozen_file");

let cfg = FFCfg {
    chunk_size: 0x10,
    path: path.to_path_buf(),
    initial_chunk_amount: 0x0A,
};

let file = FrozenFile::new::<MID>(cfg).unwrap();

let mut bufs = [[3u8; 0x10], [4u8; 0x10]];
let ptrs: Vec<*mut u8> = bufs.iter_mut().map(|b| b.as_mut_ptr()).collect();

file.pwritev(&ptrs, 2).unwrap();
file.sync().unwrap();

let mut a = [0u8; 0x10];
let mut b = [0u8; 0x10];

file.pread(a.as_mut_ptr(), 2).unwrap();
file.pread(b.as_mut_ptr(), 3).unwrap();

assert!(a.iter().all(|v| *v == 3));
assert!(b.iter().all(|v| *v == 4));
Source

pub fn grow(&self, count: usize) -> FrozenResult<()>

Grow file size of FrozenFile by given count of chunks

After successful execution, updated file length will be current_length + (count * chunk_size)

§Example
use frozen_core::ffile::{FrozenFile, FFCfg};

const MID: u8 = 0;

let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("tmp_frozen_file");

let cfg = FFCfg {
    chunk_size: 0x10,
    path: path.to_path_buf(),
    initial_chunk_amount: 0x0A,
};

let file = FrozenFile::new::<MID>(cfg).unwrap();
assert_eq!(file.length().unwrap(), 0x10 * 0x0A);

file.grow(0x20).unwrap();
assert_eq!(file.length().unwrap(), 0x10 * (0x0A + 0x20));
Source

pub fn total_chunks(&self) -> FrozenResult<usize>

Fetch total available chunks in FrozenFile from fs

§Working

This call performs a syscall to fetch current length of FrozenFile from fs, as the current length of the file is not cached anywhere in the pipeline to avoid TOCTAU race conditions

§Example
use frozen_core::ffile::{FrozenFile, FFCfg};

const MID: u8 = 0;

let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("tmp_frozen_file");

let cfg = FFCfg {
    chunk_size: 0x10,
    path: path.to_path_buf(),
    initial_chunk_amount: 0x0A,
};

let file = FrozenFile::new::<MID>(cfg).unwrap();
assert_eq!(file.length().unwrap(), 0x10 * 0x0A);

file.grow(0x20).unwrap();
assert_eq!(file.total_chunks().unwrap(), 0x0A + 0x20);

Trait Implementations§

Source§

impl Debug for FrozenFile

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for FrozenFile

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for FrozenFile

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
Source§

impl Send for FrozenFile

Source§

impl Sync for FrozenFile

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.