Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • redox-os/redoxfs
  • jD91mZM2/redoxfs
  • microcolonel/redoxfs
  • rm-dr/redoxfs
  • deepaksirone/redoxfs
  • sevenEng/redoxfs
  • mortona/redoxfs
  • potatogim/redoxfs
  • 4lDO2/redoxfs
  • malandrakisgeo/redoxfs
  • ssd/redoxfs
  • dahc/redoxfs
  • ashton/redoxfs
  • usapmz/redoxfs
  • vadorovsky/redoxfs
  • bjorn3/redoxfs
  • rw_van/redoxfs
  • mkroening/redoxfs
  • emaxx-g/redoxfs
  • CILP/redoxfs
  • AnandSMain/redoxfs
  • aaronjanse/redoxfs
  • liamnprg/redoxfs
  • coolreader18/redoxfs
  • freewilll/redoxfs
  • adi-g15/redoxfs
  • andrey.turkin/redoxfs
  • matlik/redoxfs
28 results
Show changes
extern crate redoxfs;
extern crate system;
use std::env;
use std::fs::File;
use std::io::{Read, Write};
use std::mem::size_of;
use std::sync::{Arc, Mutex};
use std::thread;
use cache::Cache;
use image::Image;
use scheme::FileScheme;
use redoxfs::FileSystem;
use system::scheme::{Packet, Scheme};
pub mod cache;
pub mod image;
pub mod resource;
pub mod scheme;
enum Status {
Starting,
Running,
Stopping
}
fn main() {
if let Some(path) = env::args().nth(1) {
let status_mutex = Arc::new(Mutex::new(Status::Starting));
let status_daemon = status_mutex.clone();
thread::spawn(move || {
match Image::open(&path).map(|image| Cache::new(image)) {
Ok(disk) => match FileSystem::open(Box::new(disk)) {
Ok(fs) => match File::create(":file") {
Ok(mut socket) => {
println!("redoxfs: mounted filesystem {} on file:", path);
*status_daemon.lock().unwrap() = Status::Running;
let mut scheme = FileScheme::new(fs);
loop {
let mut packet = Packet::default();
while socket.read(&mut packet).unwrap() == size_of::<Packet>() {
// println!("Read {:?}", packet);
scheme.handle(&mut packet);
// println!("Write {:?}", packet);
socket.write(&packet).unwrap();
}
}
},
Err(err) => println!("redoxfs: failed to create file scheme: {}", err)
},
Err(err) => println!("redoxfs: failed to open filesystem {}: {}", path, err)
},
Err(err) => println!("redoxfs: failed to open image {}: {}", path, err)
}
*status_daemon.lock().unwrap() = Status::Stopping;
});
'waiting: loop {
match *status_mutex.lock().unwrap() {
Status::Starting => (),
Status::Running => break 'waiting,
Status::Stopping => break 'waiting,
}
thread::sleep_ms(30);
}
} else {
println!("redoxfs: no disk image provided");
}
}
use redoxfs::FileSystem;
use std::cmp::{min, max};
use system::error::{Error, Result, EINVAL};
use system::syscall::{Stat, SEEK_SET, SEEK_CUR, SEEK_END, MODE_DIR, MODE_FILE};
pub trait Resource {
fn read(&mut self, buf: &mut [u8], fs: &mut FileSystem) -> Result<usize>;
fn write(&mut self, buf: &[u8], fs: &mut FileSystem) -> Result<usize>;
fn seek(&mut self, offset: usize, whence: usize) -> Result<usize>;
fn path(&self, buf: &mut [u8]) -> Result<usize>;
fn stat(&self, _stat: &mut Stat) -> Result<usize>;
fn sync(&mut self) -> Result<usize>;
fn truncate(&mut self, len: usize) -> Result<usize>;
}
pub struct DirResource {
path: String,
data: Vec<u8>,
seek: usize,
}
impl DirResource {
pub fn new(path: &str, data: Vec<u8>) -> DirResource {
DirResource {
path: path.to_string(),
data: data,
seek: 0,
}
}
}
impl Resource for DirResource {
fn read(&mut self, buf: &mut [u8], _fs: &mut FileSystem) -> Result<usize> {
let mut i = 0;
while i < buf.len() && self.seek < self.data.len() {
buf[i] = self.data[self.seek];
i += 1;
self.seek += 1;
}
Ok(i)
}
fn write(&mut self, _buf: &[u8], _fs: &mut FileSystem) -> Result<usize> {
Err(Error::new(EINVAL))
}
fn seek(&mut self, offset: usize, whence: usize) -> Result<usize> {
match whence {
SEEK_SET => self.seek = min(0, max(self.data.len() as isize, offset as isize)) as usize,
SEEK_CUR => self.seek = min(0, max(self.data.len() as isize, self.seek as isize + offset as isize)) as usize,
SEEK_END => self.seek = min(0, max(self.data.len() as isize, self.data.len() as isize + offset as isize)) as usize,
_ => return Err(Error::new(EINVAL))
}
Ok(self.seek)
}
fn path(&self, buf: &mut [u8]) -> Result<usize> {
let mut i = 0;
let path = self.path.as_bytes();
while i < buf.len() && i < path.len() {
buf[i] = path[i];
i += 1;
}
Ok(i)
}
fn stat(&self, stat: &mut Stat) -> Result<usize> {
stat.st_mode = MODE_DIR;
stat.st_size = self.data.len() as u64;
Ok(0)
}
fn sync(&mut self) -> Result<usize> {
Err(Error::new(EINVAL))
}
fn truncate(&mut self, _len: usize) -> Result<usize> {
Err(Error::new(EINVAL))
}
}
pub struct FileResource {
path: String,
block: u64,
seek: u64,
size: u64,
}
impl FileResource {
pub fn new(path: &str, block: u64, size: u64) -> FileResource {
FileResource {
path: path.to_string(),
block: block,
seek: 0,
size: size,
}
}
}
impl Resource for FileResource {
fn read(&mut self, buf: &mut [u8], fs: &mut FileSystem) -> Result<usize> {
let count = try!(fs.read_node(self.block, self.seek, buf));
self.seek += count as u64;
Ok(count)
}
fn write(&mut self, buf: &[u8], fs: &mut FileSystem) -> Result<usize> {
let count = try!(fs.write_node(self.block, self.seek, buf));
self.seek += count as u64;
if self.seek > self.size {
self.size = self.seek;
}
Ok(count)
}
fn seek(&mut self, offset: usize, whence: usize) -> Result<usize> {
match whence {
SEEK_SET => self.seek = min(0, max(self.size as i64, offset as i64)) as u64,
SEEK_CUR => self.seek = min(0, max(self.size as i64, self.seek as i64 + offset as i64)) as u64,
SEEK_END => self.seek = min(0, max(self.size as i64, self.size as i64 + offset as i64)) as u64,
_ => return Err(Error::new(EINVAL))
}
Ok(self.seek as usize)
}
fn path(&self, buf: &mut [u8]) -> Result<usize> {
let mut i = 0;
let path = self.path.as_bytes();
while i < buf.len() && i < path.len() {
buf[i] = path[i];
i += 1;
}
Ok(i)
}
fn stat(&self, stat: &mut Stat) -> Result<usize> {
stat.st_mode = MODE_FILE;
stat.st_size = self.size;
Ok(0)
}
fn sync(&mut self) -> Result<usize> {
Ok(0)
}
fn truncate(&mut self, len: usize) -> Result<usize> {
Ok(0)
}
}
use resource::{Resource, DirResource, FileResource};
use redoxfs::{FileSystem, Node};
use std::collections::BTreeMap;
use system::error::{Error, Result, EEXIST, EISDIR, ENOTDIR, EPERM, ENOENT, EBADF};
use system::scheme::Scheme;
use system::syscall::{Stat, O_CREAT};
pub struct FileScheme {
fs: FileSystem,
next_id: isize,
files: BTreeMap<usize, Box<Resource>>
}
impl FileScheme {
pub fn new(fs: FileSystem) -> FileScheme {
FileScheme {
fs: fs,
next_id: 1,
files: BTreeMap::new()
}
}
fn open_inner(&mut self, url: &str, flags: usize) -> Result<Box<Resource>> {
let path = url.split(':').nth(1).unwrap_or("").trim_matches('/');
let mut nodes = Vec::new();
let node_result = self.fs.path_nodes(path, &mut nodes);
match node_result {
Ok(node) => if node.1.is_dir() {
let mut data = Vec::new();
let mut children = Vec::new();
try!(self.fs.child_nodes(&mut children, node.0));
for child in children.iter() {
if let Ok(name) = child.1.name() {
if ! data.is_empty() {
data.push(b'\n');
}
data.extend_from_slice(&name.as_bytes());
if child.1.is_dir() {
data.push(b'/');
}
}
}
return Ok(Box::new(DirResource::new(url, data)));
} else {
let size = try!(self.fs.node_len(node.0));
return Ok(Box::new(FileResource::new(url, node.0, size)));
},
Err(err) => if err.errno == ENOENT && flags & O_CREAT == O_CREAT {
let mut last_part = String::new();
for part in path.split('/') {
if ! part.is_empty() {
last_part = part.to_string();
}
}
if ! last_part.is_empty() {
if let Some(parent) = nodes.last() {
let node = try!(self.fs.create_node(Node::MODE_FILE, &last_part, parent.0));
return Ok(Box::new(FileResource::new(url, node.0, 0)));
} else {
return Err(Error::new(EPERM));
}
} else {
return Err(Error::new(EPERM));
}
} else {
return Err(err);
}
}
}
}
impl Scheme for FileScheme {
fn open(&mut self, url: &str, flags: usize, _mode: usize) -> Result<usize> {
// println!("Open '{}' {:X}", path, flags);
let resource = try!(self.open_inner(url, flags));
let id = self.next_id as usize;
self.next_id += 1;
if self.next_id < 0 {
self.next_id = 1;
}
self.files.insert(id, resource);
Ok(id)
}
fn mkdir(&mut self, url: &str, _mode: usize) -> Result<usize> {
let path = url.split(':').nth(1).unwrap_or("").trim_matches('/');
// println!("Mkdir '{}'", path);
let mut nodes = Vec::new();
match self.fs.path_nodes(path, &mut nodes) {
Ok(_node) => Err(Error::new(EEXIST)),
Err(err) => if err.errno == ENOENT {
let mut last_part = String::new();
for part in path.split('/') {
if ! part.is_empty() {
last_part = part.to_owned();
}
}
if ! last_part.is_empty() {
if let Some(parent) = nodes.last() {
self.fs.create_node(Node::MODE_DIR, &last_part, parent.0).and(Ok(0))
} else {
Err(Error::new(EPERM))
}
} else {
Err(Error::new(EPERM))
}
} else {
Err(err)
}
}
}
fn rmdir(&mut self, url: &str) -> Result<usize> {
let path = url.split(':').nth(1).unwrap_or("").trim_matches('/');
// println!("Rmdir '{}'", path);
let mut nodes = Vec::new();
let child = try!(self.fs.path_nodes(path, &mut nodes));
if let Some(parent) = nodes.last() {
if child.1.is_dir() {
if let Ok(child_name) = child.1.name() {
self.fs.remove_node(Node::MODE_DIR, child_name, parent.0).and(Ok(0))
} else {
Err(Error::new(ENOENT))
}
} else {
Err(Error::new(ENOTDIR))
}
} else {
Err(Error::new(EPERM))
}
}
fn unlink(&mut self, url: &str) -> Result<usize> {
let path = url.split(':').nth(1).unwrap_or("").trim_matches('/');
// println!("Unlink '{}'", path);
let mut nodes = Vec::new();
let child = try!(self.fs.path_nodes(path, &mut nodes));
if let Some(parent) = nodes.last() {
if ! child.1.is_dir() {
if let Ok(child_name) = child.1.name() {
self.fs.remove_node(Node::MODE_FILE, child_name, parent.0).and(Ok(0))
} else {
Err(Error::new(ENOENT))
}
} else {
Err(Error::new(EISDIR))
}
} else {
Err(Error::new(EPERM))
}
}
/* Resource operations */
#[allow(unused_variables)]
fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> {
// println!("Read {}, {:X} {}", id, buf.as_ptr() as usize, buf.len());
if let Some(mut file) = self.files.get_mut(&id) {
file.read(buf, &mut self.fs)
} else {
Err(Error::new(EBADF))
}
}
fn write(&mut self, id: usize, buf: &[u8]) -> Result<usize> {
// println!("Write {}, {:X} {}", id, buf.as_ptr() as usize, buf.len());
if let Some(mut file) = self.files.get_mut(&id) {
file.write(buf, &mut self.fs)
} else {
Err(Error::new(EBADF))
}
}
fn seek(&mut self, id: usize, pos: usize, whence: usize) -> Result<usize> {
// println!("Seek {}, {} {}", id, pos, whence);
if let Some(mut file) = self.files.get_mut(&id) {
file.seek(pos, whence)
} else {
Err(Error::new(EBADF))
}
}
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
// println!("Fpath {}, {:X} {}", id, buf.as_ptr() as usize, buf.len());
if let Some(file) = self.files.get(&id) {
file.path(buf)
} else {
Err(Error::new(EBADF))
}
}
fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
// println!("Fstat {}, {:X}", id, stat as *mut Stat as usize);
if let Some(file) = self.files.get(&id) {
file.stat(stat)
} else {
Err(Error::new(EBADF))
}
}
fn fsync(&mut self, id: usize) -> Result<usize> {
// println!("Fsync {}", id);
if let Some(mut file) = self.files.get_mut(&id) {
file.sync()
} else {
Err(Error::new(EBADF))
}
}
fn ftruncate(&mut self, id: usize, len: usize) -> Result<usize> {
// println!("Ftruncate {}, {}", id, len);
if let Some(mut file) = self.files.get_mut(&id) {
file.truncate(len)
} else {
Err(Error::new(EBADF))
}
}
fn close(&mut self, id: usize) -> Result<usize> {
// println!("Close {}", id);
if self.files.remove(&id).is_some() {
Ok(0)
} else {
Err(Error::new(EBADF))
}
}
}
use alloc::vec::Vec;
use core::{fmt, mem, ops, slice};
use endian_num::Le;
use crate::{BlockAddr, BlockLevel, BlockPtr, BlockTrait, BLOCK_SIZE};
pub const ALLOC_LIST_ENTRIES: usize =
(BLOCK_SIZE as usize - mem::size_of::<BlockPtr<AllocList>>()) / mem::size_of::<AllocEntry>();
/// The RedoxFS block allocator. This struct manages all "data" blocks in RedoxFS
/// (i.e, all blocks that aren't reserved or part of the header chain).
///
/// [`Allocator`] can allocate blocks of many "levels"---that is, it can
/// allocate multiple consecutive [`BLOCK_SIZE`] blocks in one operation.
///
/// This reduces the amount of memory that the [`Allocator`] uses:
/// Instead of storing the index of each free [`BLOCK_SIZE`] block,
/// the `levels` array can keep track of higher-level blocks, splitting
/// them when a smaller block is requested.
///
/// Higher-level blocks also allow us to more efficiently allocate memory
/// for large files.
#[derive(Clone, Default)]
pub struct Allocator {
/// This array keeps track of all free blocks of each level,
/// and is initialized using the AllocList chain when we open the filesystem.
///
/// Every element of the outer array represents a block level:
/// - item 0: free level 0 blocks (with size [`BLOCK_SIZE`])
/// - item 1: free level 1 blocks (with size 2*[`BLOCK_SIZE`])
/// - item 2: free level 2 blocks (with size 4*[`BLOCK_SIZE`])
/// ...and so on.
///
/// Each inner array contains a list of free block indices,
levels: Vec<Vec<u64>>,
}
impl Allocator {
pub fn levels(&self) -> &Vec<Vec<u64>> {
&self.levels
}
/// Count the number of free [`BLOCK_SIZE`] available to this [`Allocator`].
pub fn free(&self) -> u64 {
let mut free = 0;
for level in 0..self.levels.len() {
let level_size = 1 << level;
free += self.levels[level].len() as u64 * level_size;
}
free
}
/// Find a free block of the given level, mark it as "used", and return its address.
/// Returns [`None`] if there are no free blocks with this level.
pub fn allocate(&mut self, block_level: BlockLevel) -> Option<BlockAddr> {
// First, find the lowest level with a free block
let mut index_opt = None;
let mut level = block_level.0;
// Start searching at the level we want. Smaller levels are too small!
while level < self.levels.len() {
if !self.levels[level].is_empty() {
index_opt = self.levels[level].pop();
break;
}
level += 1;
}
// If a free block was found, split it until we find a usable block of the right level.
// The left side of the split block is kept free, and the right side is allocated.
let index = index_opt?;
while level > block_level.0 {
level -= 1;
let level_size = 1 << level;
self.levels[level].push(index + level_size);
}
Some(unsafe { BlockAddr::new(index, block_level) })
}
/// Try to allocate the exact block specified, making all necessary splits.
/// Returns [`None`] if this some (or all) of this block is already allocated.
///
/// Note that [`BlockAddr`] encodes the blocks location _and_ level.
pub fn allocate_exact(&mut self, exact_addr: BlockAddr) -> Option<BlockAddr> {
// This function only supports level 0 right now
assert_eq!(exact_addr.level().0, 0);
let exact_index = exact_addr.index();
let mut index_opt = None;
// Go from the highest to the lowest level
for level in (0..self.levels.len()).rev() {
let level_size = 1 << level;
// Split higher block if found
if let Some(index) = index_opt.take() {
self.levels[level].push(index);
self.levels[level].push(index + level_size);
}
// Look for matching block and remove it
for i in 0..self.levels[level].len() {
let start = self.levels[level][i];
if start <= exact_index {
let end = start + level_size;
if end > exact_index {
self.levels[level].remove(i);
index_opt = Some(start);
break;
}
}
}
}
Some(unsafe { BlockAddr::new(index_opt?, exact_addr.level()) })
}
/// Deallocate the given block, marking it "free" so that it can be re-used later.
pub fn deallocate(&mut self, addr: BlockAddr) {
// When we deallocate, we check if block we're deallocating has a free sibling.
// If it does, we join the two to create one free block in the next (higher) level.
//
// We repeat this until we no longer have a sibling to join.
let mut index = addr.index();
let mut level = addr.level().0;
loop {
while level >= self.levels.len() {
self.levels.push(Vec::new());
}
let level_size = 1 << level;
let next_size = level_size << 1;
let mut found = false;
let mut i = 0;
// look at all free blocks in the current level...
while i < self.levels[level].len() {
// index of the second block we're looking at
let level_index = self.levels[level][i];
// - the block we just freed aligns with the next largest block, and
// - the second block we're looking at is the right sibling of this block
if index % next_size == 0 && index + level_size == level_index {
// "alloc" the next highest block, repeat deallocation process.
self.levels[level].remove(i);
found = true;
break;
// - the index of this block doesn't align with the next largest block, and
// - the block we're looking at is the left neighbor of this block
} else if level_index % next_size == 0 && level_index + level_size == index {
// "alloc" the next highest block, repeat deallocation process.
self.levels[level].remove(i);
index = level_index; // index moves to left block
found = true;
break;
}
i += 1;
}
// We couldn't find a higher block,
// deallocate this one and finish
if !found {
self.levels[level].push(index);
return;
}
// repeat deallocation process on the
// higher-level block we just created.
level += 1;
}
}
}
#[repr(C, packed)]
pub struct AllocEntry {
/// The index of the first block this [`AllocEntry`] refers to
index: Le<u64>,
/// The number of blocks after (and including) `index` that are are free or used.
/// If negative, they are used; if positive, they are free.
count: Le<i64>,
}
impl AllocEntry {
pub fn new(index: u64, count: i64) -> Self {
Self {
index: index.into(),
count: count.into(),
}
}
pub fn allocate(addr: BlockAddr) -> Self {
Self::new(addr.index(), -addr.level().blocks())
}
pub fn deallocate(addr: BlockAddr) -> Self {
Self::new(addr.index(), addr.level().blocks())
}
pub fn index(&self) -> u64 {
self.index.to_ne()
}
pub fn count(&self) -> i64 {
self.count.to_ne()
}
pub fn is_null(&self) -> bool {
self.count() == 0
}
}
impl Clone for AllocEntry {
fn clone(&self) -> Self {
*self
}
}
impl Copy for AllocEntry {}
impl Default for AllocEntry {
fn default() -> Self {
Self {
index: 0.into(),
count: 0.into(),
}
}
}
impl fmt::Debug for AllocEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let index = self.index();
let count = self.count();
f.debug_struct("AllocEntry")
.field("index", &index)
.field("count", &count)
.finish()
}
}
/// A node in the allocation chain.
#[repr(C, packed)]
pub struct AllocList {
/// A pointer to the previous AllocList.
/// If this is the null pointer, this is the first element of the chain.
pub prev: BlockPtr<AllocList>,
/// Allocation entries.
pub entries: [AllocEntry; ALLOC_LIST_ENTRIES],
}
unsafe impl BlockTrait for AllocList {
fn empty(level: BlockLevel) -> Option<Self> {
if level.0 == 0 {
Some(Self {
prev: BlockPtr::default(),
entries: [AllocEntry::default(); ALLOC_LIST_ENTRIES],
})
} else {
None
}
}
}
impl fmt::Debug for AllocList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let prev = self.prev;
let entries: Vec<&AllocEntry> = self
.entries
.iter()
.filter(|entry| entry.count() > 0)
.collect();
f.debug_struct("AllocList")
.field("prev", &prev)
.field("entries", &entries)
.finish()
}
}
impl ops::Deref for AllocList {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const AllocList as *const u8,
mem::size_of::<AllocList>(),
) as &[u8]
}
}
}
impl ops::DerefMut for AllocList {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut AllocList as *mut u8,
mem::size_of::<AllocList>(),
) as &mut [u8]
}
}
}
#[test]
fn alloc_node_size_test() {
assert_eq!(mem::size_of::<AllocList>(), crate::BLOCK_SIZE as usize);
}
#[test]
fn allocator_test() {
let mut alloc = Allocator::default();
assert_eq!(alloc.allocate(BlockLevel::default()), None);
alloc.deallocate(unsafe { BlockAddr::new(1, BlockLevel::default()) });
assert_eq!(
alloc.allocate(BlockLevel::default()),
Some(unsafe { BlockAddr::new(1, BlockLevel::default()) })
);
assert_eq!(alloc.allocate(BlockLevel::default()), None);
for addr in 1023..2048 {
alloc.deallocate(unsafe { BlockAddr::new(addr, BlockLevel::default()) });
}
assert_eq!(alloc.levels.len(), 11);
for level in 0..alloc.levels.len() {
if level == 0 {
assert_eq!(alloc.levels[level], [1023]);
} else if level == 10 {
assert_eq!(alloc.levels[level], [1024]);
} else {
assert_eq!(alloc.levels[level], []);
}
}
for addr in 1023..2048 {
assert_eq!(
alloc.allocate(BlockLevel::default()),
Some(unsafe { BlockAddr::new(addr, BlockLevel::default()) })
);
}
assert_eq!(alloc.allocate(BlockLevel::default()), None);
assert_eq!(alloc.levels.len(), 11);
for level in 0..alloc.levels.len() {
assert_eq!(alloc.levels[level], []);
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
use system::error::Result;
/// A disk
pub trait Disk {
fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize>;
fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize>;
fn size(&mut self) -> Result<u64>;
}
This diff is collapsed.
This diff is collapsed.
use std::io::{Read, Seek, SeekFrom, Write};
use syscall::error::{Error, Result, EIO};
use crate::disk::Disk;
use crate::BLOCK_SIZE;
macro_rules! try_disk {
($expr:expr) => {
match $expr {
Ok(val) => val,
Err(err) => {
eprintln!("Disk I/O Error: {}", err);
return Err(Error::new(EIO));
}
}
};
}
pub struct DiskIo<T>(pub T);
impl<T: Read + Write + Seek> Disk for DiskIo<T> {
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
try_disk!(self.0.seek(SeekFrom::Start(block * BLOCK_SIZE)));
let count = try_disk!(self.0.read(buffer));
Ok(count)
}
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
try_disk!(self.0.seek(SeekFrom::Start(block * BLOCK_SIZE)));
let count = try_disk!(self.0.write(buffer));
Ok(count)
}
fn size(&mut self) -> Result<u64> {
let size = try_disk!(self.0.seek(SeekFrom::End(0)));
Ok(size)
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.