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
#[cfg(not(target_os = "redox"))]
#[cfg(all(not(target_os = "redox"), not(fuzzing)))]
mod fuse;
#[cfg(all(not(target_os = "redox"), fuzzing))]
pub mod fuse;
#[cfg(not(target_os = "redox"))]
pub use self::fuse::mount;
......
use std::fs::File;
use std::io::{self, Read, Write};
use redox_scheme::{RequestKind, SignalBehavior, Socket, V2};
use std::io;
use std::path::Path;
use std::sync::atomic::Ordering;
use syscall::{Packet, SchemeMut};
use crate::{Disk, FileSystem, IS_UMT};
use crate::{Disk, FileSystem, Transaction, IS_UMT};
use self::scheme::FileScheme;
......@@ -15,41 +14,37 @@ pub fn mount<D, P, T, F>(filesystem: FileSystem<D>, mountpoint: P, mut callback:
where
D: Disk,
P: AsRef<Path>,
F: FnMut(&Path) -> T,
F: FnOnce(&Path) -> T,
{
let mountpoint = mountpoint.as_ref();
let socket_path = format!(":{}", mountpoint.display());
let mut socket = File::create(&socket_path)?;
let socket = Socket::<V2>::create(&format!("{}", mountpoint.display()))?;
let mounted_path = format!("{}:", mountpoint.display());
let res = callback(Path::new(&mounted_path));
let mut scheme = FileScheme::new(format!("{}", mountpoint.display()), filesystem);
loop {
if IS_UMT.load(Ordering::SeqCst) > 0 {
break Ok(res);
}
let mut packet = Packet::default();
match socket.read(&mut packet) {
Ok(0) => break Ok(res),
Ok(_ok) => (),
Err(err) => {
if err.kind() == io::ErrorKind::Interrupted {
continue;
while IS_UMT.load(Ordering::SeqCst) == 0 {
let req = match socket.next_request(SignalBehavior::Restart)? {
None => break,
Some(req) => {
if let RequestKind::Call(r) = req.kind() {
r
} else {
break Err(err);
// TODO: Redoxfs does not yet support asynchronous file IO. It might still make
// sense to implement cancellation for huge buffers, e.g. dd bs=1G
continue;
}
}
}
};
let response = req.handle_scheme_mut(&mut scheme);
scheme.handle(&mut packet);
match socket.write(&packet) {
Ok(_ok) => (),
Err(err) => {
break Err(err);
}
if !socket.write_response(response, SignalBehavior::Restart)? {
break;
}
}
// Squash allocations and sync on unmount
let _ = Transaction::new(&mut scheme.fs).commit(true);
Ok(res)
}
use std::cmp::{max, min};
use std::slice;
use std::time::{SystemTime, UNIX_EPOCH};
use alloc::collections::BTreeMap;
use libredox::call::MmapArgs;
use range_tree::RangeTree;
use syscall::{MAP_PRIVATE, PAGE_SIZE, EBADFD};
use syscall::data::{Map, Stat, TimeSpec};
use syscall::error::{Error, Result, EBADF, EINVAL, EISDIR, ENOMEM, EPERM};
use syscall::data::{Stat, TimeSpec};
use syscall::error::{Error, Result, EBADF, EINVAL, EISDIR, EPERM};
use syscall::flag::{
MapFlags, F_GETFL, F_SETFL, MODE_PERM, O_ACCMODE, O_APPEND, O_RDONLY, O_RDWR, O_WRONLY,
PROT_READ, PROT_WRITE, SEEK_CUR, SEEK_END, SEEK_SET,
PROT_READ, PROT_WRITE,
};
use syscall::{EBADFD, PAGE_SIZE};
use crate::{Disk, Node, Transaction, TreePtr};
......@@ -28,15 +28,28 @@ pub trait Resource<D: Disk> {
fn set_path(&mut self, path: &str);
fn read(&mut self, buf: &mut [u8], tx: &mut Transaction<D>) -> Result<usize>;
fn read(&mut self, buf: &mut [u8], offset: u64, tx: &mut Transaction<D>) -> Result<usize>;
fn write(&mut self, buf: &[u8], tx: &mut Transaction<D>) -> Result<usize>;
fn write(&mut self, buf: &[u8], offset: u64, tx: &mut Transaction<D>) -> Result<usize>;
fn seek(&mut self, offset: isize, whence: usize, tx: &mut Transaction<D>) -> Result<isize>;
fn fsize(&mut self, tx: &mut Transaction<D>) -> Result<u64>;
fn fmap(&mut self, fmaps: &mut Fmaps, flags: MapFlags, size: usize, offset: u64, tx: &mut Transaction<D>) -> Result<usize>;
fn fmap(
&mut self,
fmaps: &mut Fmaps,
flags: MapFlags,
size: usize,
offset: u64,
tx: &mut Transaction<D>,
) -> Result<usize>;
fn funmap(&mut self, fmaps: &mut Fmaps, offset: u64, size: usize, tx: &mut Transaction<D>) -> Result<usize>;
fn funmap(
&mut self,
fmaps: &mut Fmaps,
offset: u64,
size: usize,
tx: &mut Transaction<D>,
) -> Result<usize>;
fn fchmod(&mut self, mode: u16, tx: &mut Transaction<D>) -> Result<usize> {
let mut node = tx.read_tree(self.node_ptr())?;
......@@ -129,7 +142,6 @@ pub struct DirResource {
parent_ptr_opt: Option<TreePtr<Node>>,
node_ptr: TreePtr<Node>,
data: Option<Vec<u8>>,
seek: isize,
uid: u32,
}
......@@ -146,7 +158,6 @@ impl DirResource {
parent_ptr_opt,
node_ptr,
data,
seek: 0,
uid,
}
}
......@@ -171,7 +182,6 @@ impl<D: Disk> Resource<D> for DirResource {
parent_ptr_opt: self.parent_ptr_opt,
node_ptr: self.node_ptr,
data: self.data.clone(),
seek: self.seek,
uid: self.uid,
}))
}
......@@ -180,38 +190,43 @@ impl<D: Disk> Resource<D> for DirResource {
self.path = path.to_string();
}
fn read(&mut self, buf: &mut [u8], _tx: &mut Transaction<D>) -> Result<usize> {
fn read(&mut self, buf: &mut [u8], offset: u64, _tx: &mut Transaction<D>) -> Result<usize> {
let data = self.data.as_ref().ok_or(Error::new(EISDIR))?;
let size = data.len() as isize;
let mut i = 0;
while i < buf.len() && self.seek < size {
buf[i] = data[self.seek as usize];
i += 1;
self.seek += 1;
}
Ok(i)
let src = usize::try_from(offset)
.ok()
.and_then(|o| data.get(o..))
.unwrap_or(&[]);
let byte_count = core::cmp::min(src.len(), buf.len());
buf[..byte_count].copy_from_slice(&src[..byte_count]);
Ok(byte_count)
}
fn write(&mut self, _buf: &[u8], _tx: &mut Transaction<D>) -> Result<usize> {
fn write(&mut self, _buf: &[u8], _offset: u64, _tx: &mut Transaction<D>) -> Result<usize> {
Err(Error::new(EBADF))
}
fn seek(&mut self, offset: isize, whence: usize, _tx: &mut Transaction<D>) -> Result<isize> {
let data = self.data.as_ref().ok_or(Error::new(EBADF))?;
let size = data.len() as isize;
self.seek = match whence {
SEEK_SET => max(0, min(size, offset)),
SEEK_CUR => max(0, min(size, self.seek + offset)),
SEEK_END => max(0, min(size, size + offset)),
_ => return Err(Error::new(EINVAL)),
};
Ok(self.seek)
fn fsize(&mut self, _tx: &mut Transaction<D>) -> Result<u64> {
Ok(self.data.as_ref().ok_or(Error::new(EBADF))?.len() as u64)
}
fn fmap(&mut self, _fmaps: &mut Fmaps, _flags: MapFlags, _size: usize, _offset: u64, _tx: &mut Transaction<D>) -> Result<usize> {
fn fmap(
&mut self,
_fmaps: &mut Fmaps,
_flags: MapFlags,
_size: usize,
_offset: u64,
_tx: &mut Transaction<D>,
) -> Result<usize> {
Err(Error::new(EBADF))
}
fn funmap(&mut self, _fmaps: &mut Fmaps, _offset: u64, _size: usize, _tx: &mut Transaction<D>) -> Result<usize> {
fn funmap(
&mut self,
_fmaps: &mut Fmaps,
_offset: u64,
_size: usize,
_tx: &mut Transaction<D>,
) -> Result<usize> {
Err(Error::new(EBADF))
}
......@@ -263,16 +278,11 @@ impl Fmap {
let buf = slice::from_raw_parts_mut(address, unaligned_size);
let count = match tx.read_node(
node_ptr,
offset,
buf,
atime.as_secs(),
atime.subsec_nanos(),
) {
let count = match tx.read_node(node_ptr, offset, buf, atime.as_secs(), atime.subsec_nanos())
{
Ok(ok) => ok,
Err(err) => {
let _ = syscall::funmap(address as usize, aligned_size);
let _ = libredox::call::munmap(address.cast(), aligned_size);
return Err(err);
}
};
......@@ -287,7 +297,14 @@ impl Fmap {
})
}
pub unsafe fn sync<D: Disk>(&mut self, node_ptr: TreePtr<Node>, base: *mut u8, offset: u64, size: usize, tx: &mut Transaction<D>) -> Result<()> {
pub unsafe fn sync<D: Disk>(
&mut self,
node_ptr: TreePtr<Node>,
base: *mut u8,
offset: u64,
size: usize,
tx: &mut Transaction<D>,
) -> Result<()> {
if self.flags & PROT_WRITE == PROT_WRITE {
let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
tx.write_node(
......@@ -307,11 +324,12 @@ pub struct FileResource {
parent_ptr_opt: Option<TreePtr<Node>>,
node_ptr: TreePtr<Node>,
flags: usize,
seek: isize,
uid: u32,
}
#[derive(Debug)]
pub struct FileMmapInfo {
base: *mut u8,
size: usize,
ranges: RangeTree<Fmap>,
pub open_fds: usize,
}
......@@ -319,6 +337,7 @@ impl Default for FileMmapInfo {
fn default() -> Self {
Self {
base: core::ptr::null_mut(),
size: 0,
ranges: RangeTree::new(),
open_fds: 0,
}
......@@ -338,7 +357,6 @@ impl FileResource {
parent_ptr_opt,
node_ptr,
flags,
seek: 0,
uid,
}
}
......@@ -363,7 +381,6 @@ impl<D: Disk> Resource<D> for FileResource {
parent_ptr_opt: self.parent_ptr_opt,
node_ptr: self.node_ptr,
flags: self.flags,
seek: self.seek,
uid: self.uid,
}))
}
......@@ -372,58 +389,53 @@ impl<D: Disk> Resource<D> for FileResource {
self.path = path.to_string();
}
fn read(&mut self, buf: &mut [u8], tx: &mut Transaction<D>) -> Result<usize> {
if self.flags & O_ACCMODE == O_RDWR || self.flags & O_ACCMODE == O_RDONLY {
let atime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let count = tx.read_node(
self.node_ptr,
self.seek as u64,
buf,
atime.as_secs(),
atime.subsec_nanos(),
)?;
self.seek += count as isize;
Ok(count)
} else {
Err(Error::new(EBADF))
fn read(&mut self, buf: &mut [u8], offset: u64, tx: &mut Transaction<D>) -> Result<usize> {
if self.flags & O_ACCMODE != O_RDWR && self.flags & O_ACCMODE != O_RDONLY {
return Err(Error::new(EBADF));
}
let atime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
tx.read_node(
self.node_ptr,
offset,
buf,
atime.as_secs(),
atime.subsec_nanos(),
)
}
fn write(&mut self, buf: &[u8], tx: &mut Transaction<D>) -> Result<usize> {
if self.flags & O_ACCMODE == O_RDWR || self.flags & O_ACCMODE == O_WRONLY {
if self.flags & O_APPEND == O_APPEND {
let node = tx.read_tree(self.node_ptr)?;
self.seek = node.data().size() as isize;
}
let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let count = tx.write_node(
self.node_ptr,
self.seek as u64,
buf,
mtime.as_secs(),
mtime.subsec_nanos(),
)?;
self.seek += count as isize;
Ok(count)
} else {
Err(Error::new(EBADF))
fn write(&mut self, buf: &[u8], offset: u64, tx: &mut Transaction<D>) -> Result<usize> {
if self.flags & O_ACCMODE != O_RDWR && self.flags & O_ACCMODE != O_WRONLY {
return Err(Error::new(EBADF));
}
let effective_offset = if self.flags & O_APPEND == O_APPEND {
let node = tx.read_tree(self.node_ptr)?;
node.data().size()
} else {
offset
};
let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
tx.write_node(
self.node_ptr,
effective_offset,
buf,
mtime.as_secs(),
mtime.subsec_nanos(),
)
}
fn seek(&mut self, offset: isize, whence: usize, tx: &mut Transaction<D>) -> Result<isize> {
self.seek = match whence {
SEEK_SET => max(0, offset),
SEEK_CUR => max(0, self.seek + offset),
SEEK_END => {
let node = tx.read_tree(self.node_ptr)?;
max(0, node.data().size() as isize + offset)
}
_ => return Err(Error::new(EINVAL)),
};
Ok(self.seek)
fn fsize(&mut self, tx: &mut Transaction<D>) -> Result<u64> {
let node = tx.read_tree(self.node_ptr)?;
Ok(node.data().size())
}
fn fmap(&mut self, fmaps: &mut Fmaps, flags: MapFlags, unaligned_size: usize, offset: u64, tx: &mut Transaction<D>) -> Result<usize> {
fn fmap(
&mut self,
fmaps: &mut Fmaps,
flags: MapFlags,
unaligned_size: usize,
offset: u64,
tx: &mut Transaction<D>,
) -> Result<usize> {
//dbg!(&self.fmaps);
let accmode = self.flags & O_ACCMODE;
if flags.contains(PROT_READ) && !(accmode == O_RDWR || accmode == O_RDONLY) {
......@@ -441,33 +453,44 @@ impl<D: Disk> Resource<D> for FileResource {
// TODO: Pass entry directory to Resource trait functions, since the node_ptr can be
// obtained by the caller.
let fmap_info = fmaps.get_mut(&self.node_ptr.id()).ok_or(Error::new(EBADFD))?;
let max_offset = fmap_info.ranges.end();
if offset + aligned_size as u64 > max_offset {
if fmap_info.base.is_null() {
fmap_info.base = unsafe {
syscall::fmap(!0, &Map {
size: offset as usize + aligned_size,
let fmap_info = fmaps
.get_mut(&self.node_ptr.id())
.ok_or(Error::new(EBADFD))?;
let new_size = (offset as usize + aligned_size).next_multiple_of(PAGE_SIZE);
if new_size > fmap_info.size {
fmap_info.base = if fmap_info.base.is_null() {
unsafe {
libredox::call::mmap(MmapArgs {
length: new_size,
// PRIVATE/SHARED doesn't matter once the pages are passed in the fmap
// handler.
flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE,
prot: libredox::flag::PROT_READ | libredox::flag::PROT_WRITE,
flags: libredox::flag::MAP_PRIVATE,
offset: 0,
address: 0,
fd: !0,
addr: core::ptr::null_mut(),
})? as *mut u8
};
}
} else {
let new_size = (offset as usize + aligned_size).next_multiple_of(PAGE_SIZE);
let old_size = max_offset as usize;
fmap_info.base = unsafe {
syscall::syscall5(syscall::SYS_MREMAP, fmap_info.base as usize, old_size, 0, new_size, syscall::MremapFlags::empty().bits() | (PROT_READ | PROT_WRITE).bits())? as *mut u8
};
}
unsafe {
syscall::syscall5(
syscall::SYS_MREMAP,
fmap_info.base as usize,
fmap_info.size,
0,
new_size,
syscall::MremapFlags::empty().bits() | (PROT_READ | PROT_WRITE).bits(),
)? as *mut u8
}
};
fmap_info.size = new_size;
}
let affected_fmaps = fmap_info.ranges.remove_and_unused(offset..offset + aligned_size as u64);
let affected_fmaps = fmap_info
.ranges
.remove_and_unused(offset..offset + aligned_size as u64);
for (range, v_opt) in affected_fmaps {
//dbg!(&range);
......@@ -475,9 +498,20 @@ impl<D: Disk> Resource<D> for FileResource {
fmap.rc += 1;
fmap.flags |= flags;
fmap_info.ranges.insert(range.start, range.end - range.start, fmap);
fmap_info
.ranges
.insert(range.start, range.end - range.start, fmap);
} else {
let map = unsafe { Fmap::new(self.node_ptr, flags, unaligned_size, offset, fmap_info.base, tx)? };
let map = unsafe {
Fmap::new(
self.node_ptr,
flags,
unaligned_size,
offset,
fmap_info.base,
tx,
)?
};
fmap_info.ranges.insert(offset, aligned_size as u64, map);
}
}
......@@ -486,11 +520,20 @@ impl<D: Disk> Resource<D> for FileResource {
Ok(fmap_info.base as usize + offset as usize)
}
fn funmap(&mut self, fmaps: &mut Fmaps, offset: u64, size: usize, tx: &mut Transaction<D>) -> Result<usize> {
let fmap_info = fmaps.get_mut(&self.node_ptr.id()).ok_or(Error::new(EBADFD))?;
fn funmap(
&mut self,
fmaps: &mut Fmaps,
offset: u64,
size: usize,
tx: &mut Transaction<D>,
) -> Result<usize> {
let fmap_info = fmaps
.get_mut(&self.node_ptr.id())
.ok_or(Error::new(EBADFD))?;
//dbg!(&self.fmaps);
//dbg!(self.fmaps.conflicts(offset..offset + size as u64).collect::<Vec<_>>());
#[allow(unused_mut)]
let mut affected_fmaps = fmap_info.ranges.remove(offset..offset + size as u64);
for (range, mut fmap) in affected_fmaps {
......@@ -498,11 +541,19 @@ impl<D: Disk> Resource<D> for FileResource {
//log::info!("SYNCING {}..{}", range.start, range.end);
unsafe {
fmap.sync(self.node_ptr, fmap_info.base, range.start, (range.end - range.start) as usize, tx)?;
fmap.sync(
self.node_ptr,
fmap_info.base,
range.start,
(range.end - range.start) as usize,
tx,
)?;
}
if fmap.rc > 0 {
fmap_info.ranges.insert(range.start, range.end - range.start, fmap);
fmap_info
.ranges
.insert(range.start, range.end - range.start, fmap);
}
}
//dbg!(&self.fmaps);
......@@ -529,7 +580,13 @@ impl<D: Disk> Resource<D> for FileResource {
if let Some(fmap_info) = fmaps.get_mut(&self.node_ptr.id()) {
for (range, fmap) in fmap_info.ranges.iter_mut() {
unsafe {
fmap.sync(self.node_ptr, fmap_info.base, range.start, (range.end - range.start) as usize, tx)?;
fmap.sync(
self.node_ptr,
fmap_info.base,
range.start,
(range.end - range.start) as usize,
tx,
)?;
}
}
}
......@@ -615,9 +672,15 @@ impl range_tree::Value for Fmap {
Err(self)
}
}
fn split(self, prev_range: Option<core::ops::Range<Self::K>>, range: core::ops::Range<Self::K>, next_range: Option<core::ops::Range<Self::K>>) -> (Option<Self>, Self, Option<Self>) {
#[allow(unused_variables)]
fn split(
self,
prev_range: Option<core::ops::Range<Self::K>>,
range: core::ops::Range<Self::K>,
next_range: Option<core::ops::Range<Self::K>>,
) -> (Option<Self>, Self, Option<Self>) {
(
prev_range.map(|range| Fmap {
prev_range.map(|_range| Fmap {
rc: self.rc,
flags: self.flags,
last_page_tail: 0,
......@@ -625,9 +688,13 @@ impl range_tree::Value for Fmap {
Fmap {
rc: self.rc,
flags: self.flags,
last_page_tail: if next_range.is_none() { self.last_page_tail } else { 0 },
last_page_tail: if next_range.is_none() {
self.last_page_tail
} else {
0
},
},
next_range.map(|range| Fmap {
next_range.map(|_range| Fmap {
rc: self.rc,
flags: self.flags,
last_page_tail: self.last_page_tail,
......
This diff is collapsed.
use core::{fmt, mem, ops, slice};
use simple_endian::*;
use endian_num::Le;
use crate::{BlockList, BlockPtr, BlockRaw};
use crate::{BlockLevel, BlockList, BlockPtr, BlockTrait, RecordRaw, BLOCK_SIZE, RECORD_LEVEL};
/// An index into a [`Node`]'s block table.
pub enum NodeLevel {
L0(usize),
L1(usize, usize),
......@@ -12,61 +13,66 @@ pub enum NodeLevel {
}
impl NodeLevel {
// Warning: this uses constant block offsets, make sure to sync with Node
pub fn new(mut block_offset: u64) -> Option<Self> {
// Warning: this uses constant record offsets, make sure to sync with Node
/// Return the [`NodeLevel`] of the record with the given index.
/// - the first 128 are level 0,
/// - the next 64*256 are level 1,
/// - ...and so on.
pub fn new(mut record_offset: u64) -> Option<Self> {
// 1 << 8 = 256, this is the number of entries in a BlockList
const SHIFT: u64 = 8;
const NUM: u64 = 1 << SHIFT;
const MASK: u64 = NUM - 1;
const L0: u64 = 128;
if block_offset < L0 {
return Some(Self::L0((block_offset & MASK) as usize));
if record_offset < L0 {
return Some(Self::L0((record_offset & MASK) as usize));
} else {
block_offset -= L0;
record_offset -= L0;
}
const L1: u64 = 64 * NUM;
if block_offset < L1 {
if record_offset < L1 {
return Some(Self::L1(
((block_offset >> SHIFT) & MASK) as usize,
(block_offset & MASK) as usize,
((record_offset >> SHIFT) & MASK) as usize,
(record_offset & MASK) as usize,
));
} else {
block_offset -= L1;
record_offset -= L1;
}
const L2: u64 = 32 * NUM * NUM;
if block_offset < L2 {
if record_offset < L2 {
return Some(Self::L2(
((block_offset >> (2 * SHIFT)) & MASK) as usize,
((block_offset >> SHIFT) & MASK) as usize,
(block_offset & MASK) as usize,
((record_offset >> (2 * SHIFT)) & MASK) as usize,
((record_offset >> SHIFT) & MASK) as usize,
(record_offset & MASK) as usize,
));
} else {
block_offset -= L2;
record_offset -= L2;
}
const L3: u64 = 16 * NUM * NUM * NUM;
if block_offset < L3 {
if record_offset < L3 {
return Some(Self::L3(
((block_offset >> (3 * SHIFT)) & MASK) as usize,
((block_offset >> (2 * SHIFT)) & MASK) as usize,
((block_offset >> SHIFT) & MASK) as usize,
(block_offset & MASK) as usize,
((record_offset >> (3 * SHIFT)) & MASK) as usize,
((record_offset >> (2 * SHIFT)) & MASK) as usize,
((record_offset >> SHIFT) & MASK) as usize,
(record_offset & MASK) as usize,
));
} else {
block_offset -= L3;
record_offset -= L3;
}
const L4: u64 = 12 * NUM * NUM * NUM * NUM;
if block_offset < L4 {
if record_offset < L4 {
Some(Self::L4(
((block_offset >> (4 * SHIFT)) & MASK) as usize,
((block_offset >> (3 * SHIFT)) & MASK) as usize,
((block_offset >> (2 * SHIFT)) & MASK) as usize,
((block_offset >> SHIFT) & MASK) as usize,
(block_offset & MASK) as usize,
((record_offset >> (4 * SHIFT)) & MASK) as usize,
((record_offset >> (3 * SHIFT)) & MASK) as usize,
((record_offset >> (2 * SHIFT)) & MASK) as usize,
((record_offset >> SHIFT) & MASK) as usize,
(record_offset & MASK) as usize,
))
} else {
None
......@@ -74,38 +80,86 @@ impl NodeLevel {
}
}
type BlockListL1 = BlockList<BlockRaw>;
type BlockListL1 = BlockList<RecordRaw>;
type BlockListL2 = BlockList<BlockListL1>;
type BlockListL3 = BlockList<BlockListL2>;
type BlockListL4 = BlockList<BlockListL3>;
/// A file/folder node
#[repr(packed)]
#[repr(C, packed)]
pub struct Node {
pub mode: u16le,
pub uid: u32le,
pub gid: u32le,
pub links: u32le,
pub size: u64le,
pub ctime: u64le,
pub ctime_nsec: u32le,
pub mtime: u64le,
pub mtime_nsec: u32le,
pub atime: u64le,
pub atime_nsec: u32le,
pub padding: [u8; 6],
// 128 * BLOCK_SIZE (512 KiB, 4 KiB each)
pub level0: [BlockPtr<BlockRaw>; 128],
// 64 * 256 * BLOCK_SIZE (64 MiB, 1 MiB each)
/// This node's type & permissions.
/// - first four bits are permissions
/// - next four bits are permissions for the file's user
/// - next four bits are permissions for the file's group
/// - last four bits are permissions for everyone else
pub mode: Le<u16>,
/// The uid that owns this file
pub uid: Le<u32>,
/// The gid that owns this file
pub gid: Le<u32>,
/// The number of links to this file
/// (directory entries, symlinks, etc)
pub links: Le<u32>,
/// The length of this file, in bytes
pub size: Le<u64>,
pub ctime: Le<u64>,
pub ctime_nsec: Le<u32>,
pub mtime: Le<u64>,
pub mtime_nsec: Le<u32>,
pub atime: Le<u64>,
pub atime_nsec: Le<u32>,
pub record_level: Le<u32>,
pub padding: [u8; BLOCK_SIZE as usize - 4094],
/// The first 128 blocks of this file.
///
/// Total size: 128 * RECORD_SIZE (16 MiB, 128 KiB each)
pub level0: [BlockPtr<RecordRaw>; 128],
/// The next 64 * 256 blocks of this file,
/// stored behind 64 level one tables.
///
/// Total size: 64 * 256 * RECORD_SIZE (2 GiB, 32 MiB each)
pub level1: [BlockPtr<BlockListL1>; 64],
// 32 * 256 * 256 * BLOCK_SIZE (8 GiB, 256 MiB each)
/// The next 32 * 256 * 256 blocks of this file,
/// stored behind 32 level two tables.
/// Each level two table points to 256 level one tables.
///
/// Total size: 32 * 256 * 256 * RECORD_SIZE (256 GiB, 8 GiB each)
pub level2: [BlockPtr<BlockListL2>; 32],
// 16 * 256 * 256 * 256 * BLOCK_SIZE (1 TiB, 64 GiB each)
/// The next 16 * 256 * 256 * 256 blocks of this file,
/// stored behind 16 level three tables.
///
/// Total size: 16 * 256 * 256 * 256 * RECORD_SIZE (32 TiB, 2 TiB each)
pub level3: [BlockPtr<BlockListL3>; 16],
// 12 * 256 * 256 * 256 * 256 * BLOCK_SIZE (192 TiB, 16 TiB each)
/// The next 12 * 256 * 256 * 256 * 256 blocks of this file,
/// stored behind 12 level four tables.
///
/// Total size: 12 * 256 * 256 * 256 * 256 * RECORD_SIZE (6 PiB, 512 TiB each)
pub level4: [BlockPtr<BlockListL4>; 12],
}
unsafe impl BlockTrait for Node {
fn empty(level: BlockLevel) -> Option<Self> {
if level.0 == 0 {
Some(Self::default())
} else {
None
}
}
}
impl Default for Node {
fn default() -> Self {
Self {
......@@ -120,7 +174,8 @@ impl Default for Node {
mtime_nsec: 0.into(),
atime: 0.into(),
atime_nsec: 0.into(),
padding: [0; 6],
record_level: 0.into(),
padding: [0; BLOCK_SIZE as usize - 4094],
level0: [BlockPtr::default(); 128],
level1: [BlockPtr::default(); 64],
level2: [BlockPtr::default(); 32],
......@@ -136,11 +191,13 @@ impl Node {
pub const MODE_DIR: u16 = 0x4000;
pub const MODE_SYMLINK: u16 = 0xA000;
/// Mask for node permission bits
pub const MODE_PERM: u16 = 0x0FFF;
pub const MODE_EXEC: u16 = 0o1;
pub const MODE_WRITE: u16 = 0o2;
pub const MODE_READ: u16 = 0o4;
/// Create a new, empty node with the given metadata
pub fn new(mode: u16, uid: u32, gid: u32, ctime: u64, ctime_nsec: u32) -> Self {
Self {
mode: mode.into(),
......@@ -153,40 +210,62 @@ impl Node {
mtime_nsec: ctime_nsec.into(),
atime: ctime.into(),
atime_nsec: ctime_nsec.into(),
record_level: if mode & Self::MODE_TYPE == Self::MODE_FILE {
// Files take on record level
RECORD_LEVEL as u32
} else {
// Folders do not
0
}
.into(),
..Default::default()
}
}
/// This node's type & permissions.
/// - first four bits are permissions
/// - next four bits are permissions for the file's user
/// - next four bits are permissions for the file's group
/// - last four bits are permissions for everyone else
pub fn mode(&self) -> u16 {
{ self.mode }.to_native()
self.mode.to_ne()
}
/// The uid that owns this file
pub fn uid(&self) -> u32 {
{ self.uid }.to_native()
self.uid.to_ne()
}
/// The gid that owns this file
pub fn gid(&self) -> u32 {
{ self.gid }.to_native()
self.gid.to_ne()
}
/// The number of links to this file
/// (directory entries, symlinks, etc)
pub fn links(&self) -> u32 {
{ self.links }.to_native()
self.links.to_ne()
}
/// The length of this file, in bytes.
pub fn size(&self) -> u64 {
{ self.size }.to_native()
self.size.to_ne()
}
pub fn ctime(&self) -> (u64, u32) {
({ self.ctime }.to_native(), { self.ctime_nsec }.to_native())
(self.ctime.to_ne(), self.ctime_nsec.to_ne())
}
pub fn mtime(&self) -> (u64, u32) {
({ self.mtime }.to_native(), { self.mtime_nsec }.to_native())
(self.mtime.to_ne(), self.mtime_nsec.to_ne())
}
pub fn atime(&self) -> (u64, u32) {
({ self.atime }.to_native(), { self.atime_nsec }.to_native())
(self.atime.to_ne(), self.atime_nsec.to_ne())
}
pub fn record_level(&self) -> BlockLevel {
BlockLevel(self.record_level.to_ne() as usize)
}
pub fn set_mode(&mut self, mode: u16) {
......
use alloc::{boxed::Box, vec};
use core::ops;
use crate::{BlockLevel, BlockTrait, RECORD_LEVEL};
//TODO: this is a box to prevent stack overflows
pub struct RecordRaw(Box<[u8]>);
unsafe impl BlockTrait for RecordRaw {
fn empty(level: BlockLevel) -> Option<Self> {
if level.0 <= RECORD_LEVEL {
Some(Self(vec![0; level.bytes() as usize].into_boxed_slice()))
} else {
None
}
}
}
impl Clone for RecordRaw {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl ops::Deref for RecordRaw {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.0
}
}
impl ops::DerefMut for RecordRaw {
fn deref_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
#[test]
fn record_raw_size_test() {
for level_i in 0..RECORD_LEVEL {
let level = BlockLevel(level_i);
assert_eq!(
RecordRaw::empty(level).unwrap().len(),
level.bytes() as usize
);
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
use failure::Error;
use std::{
fs,
io::{self},
......@@ -33,7 +31,7 @@ fn unmount_linux_path(mount_path: &str) -> io::Result<ExitStatus> {
))
}
pub fn unmount_path(mount_path: &str) -> Result<(), Error> {
pub fn unmount_path(mount_path: &str) -> Result<(), io::Error> {
if cfg!(target_os = "redox") {
fs::remove_file(format!(":{}", mount_path))?
} else {
......@@ -45,7 +43,10 @@ pub fn unmount_path(mount_path: &str) -> Result<(), Error> {
let status = status_res?;
if !status.success() {
return Err(io::Error::new(io::ErrorKind::Other, "redoxfs umount failed").into());
return Err(io::Error::new(
io::ErrorKind::Other,
"redoxfs umount failed",
));
}
}
......
......@@ -34,15 +34,22 @@ ls -lah image
mkdir image/test
time cp -r src image/test/src
dd if=/dev/urandom of=image/test/random bs=1M count=256
dd if=image/test/random of=/dev/null bs=1M count=256
time truncate --size=256M image/test/sparse
dd if=image/test/sparse of=/dev/null bs=1M count=256
dd if=/dev/zero of=image/test/zero bs=1M count=256
dd if=image/test/zero of=/dev/null bs=1M count=256
ls -lah image/test
df -h image
rm image/test/random
rm image/test/sparse
rm image/test/zero
rm -rf image/test/src
rmdir image/test
......