Allow read of filesystem names
This commit is contained in:
+12
@@ -0,0 +1,12 @@
|
||||
# Compiled files
|
||||
*.o
|
||||
*.so
|
||||
*.rlib
|
||||
*.dll
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
|
||||
# Generated by Cargo
|
||||
Cargo.lock
|
||||
/target/
|
||||
@@ -0,0 +1,6 @@
|
||||
language: rust
|
||||
rust:
|
||||
- nightly
|
||||
sudo: false
|
||||
notifications:
|
||||
email: false
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "redoxfs"
|
||||
description = "The Redox Filesystem"
|
||||
repository = "https://github.com/redox-os/redoxfs"
|
||||
version = "0.1.0"
|
||||
license-file = "LICENSE"
|
||||
readme = "README.md"
|
||||
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
||||
|
||||
[lib]
|
||||
name = "redoxfs"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "redoxfs-utility"
|
||||
path = "src/utility.rs"
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Jeremy Soller
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,6 @@
|
||||
# redoxfs
|
||||
The Redox Filesystem. Compatible with Redox and Linux.
|
||||
|
||||
[](https://travis-ci.org/redox-os/redoxfs)
|
||||
[](./LICENSE)
|
||||
[](https://crates.io/crates/redoxfs)
|
||||
@@ -0,0 +1,8 @@
|
||||
use std::io::Result;
|
||||
|
||||
/// A disk
|
||||
pub trait Disk {
|
||||
fn name(&self) -> &str;
|
||||
fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize>;
|
||||
fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize>;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/// A disk extent
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
pub struct Extent {
|
||||
pub block: u64,
|
||||
pub length: u64,
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use std::cmp;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::{Disk, Header, Node};
|
||||
|
||||
/// A file system
|
||||
pub struct FileSystem {
|
||||
pub disk: Box<Disk>,
|
||||
pub header: Header,
|
||||
pub nodes: BTreeMap<u64, Node>,
|
||||
}
|
||||
|
||||
impl FileSystem {
|
||||
/// Create a file system from a disk
|
||||
pub fn new(mut disk: Box<Disk>) -> Result<Self, String> {
|
||||
let mut header = Header::new();
|
||||
try!(disk.read_at(1, &mut header).map_err(|err| format!("{}: could not read header: {}", disk.name(), err)));
|
||||
if header.valid() {
|
||||
let mut nodes = BTreeMap::new();
|
||||
for extent in &header.extents {
|
||||
if extent.block > 0 && extent.length > 0 {
|
||||
let current_sectors = (extent.length + 511) / 512;
|
||||
let max_size = current_sectors * 512;
|
||||
|
||||
let size = cmp::min(extent.length, max_size);
|
||||
|
||||
for i in 0..size / 512 {
|
||||
let node_block = extent.block + i;
|
||||
let mut node = Node::new();
|
||||
try!(disk.read_at(node_block, &mut node).map_err(|err| format!("{}: could not read node {}: {}", disk.name(), node_block, err)));
|
||||
nodes.insert(node_block, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(FileSystem {
|
||||
disk: disk,
|
||||
header: header,
|
||||
nodes: nodes,
|
||||
})
|
||||
}else{
|
||||
Err(format!("{}: invalid header", disk.name()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use std::{mem, slice};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use super::Extent;
|
||||
|
||||
/// The header of the filesystem
|
||||
#[repr(packed)]
|
||||
pub struct Header {
|
||||
pub signature: [u8; 8],
|
||||
pub version: u64,
|
||||
pub free_space: Extent,
|
||||
pub padding: [u8; 224],
|
||||
pub extents: [Extent; 16],
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn new() -> Header {
|
||||
Header {
|
||||
signature: [0; 8],
|
||||
version: 0,
|
||||
free_space: Extent::default(),
|
||||
padding: [0; 224],
|
||||
extents: [Extent::default(); 16]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn valid(&self) -> bool {
|
||||
&self.signature == b"REDOXFS\0" && self.version == 1
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Header {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts(self as *const Header as *const u8, mem::size_of::<Header>()) as &[u8]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Header {
|
||||
fn deref_mut(&mut self) -> &mut [u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(self as *mut Header as *mut u8, mem::size_of::<Header>()) as &mut [u8]
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
pub use self::disk::Disk;
|
||||
pub use self::extent::Extent;
|
||||
pub use self::filesystem::FileSystem;
|
||||
pub use self::header::Header;
|
||||
pub use self::node::Node;
|
||||
|
||||
pub mod disk;
|
||||
pub mod extent;
|
||||
pub mod filesystem;
|
||||
pub mod header;
|
||||
pub mod node;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
use std::{mem, slice};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use super::Extent;
|
||||
|
||||
/// A file/folder node
|
||||
#[repr(packed)]
|
||||
pub struct Node {
|
||||
pub name: [u8; 256],
|
||||
pub extents: [Extent; 16],
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn new() -> Node {
|
||||
Node {
|
||||
name: [0; 256],
|
||||
extents: [Extent::default(); 16]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Node {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts(self as *const Node as *const u8, mem::size_of::<Node>()) as &[u8]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Node {
|
||||
fn deref_mut(&mut self) -> &mut [u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(self as *mut Node as *mut u8, mem::size_of::<Node>()) as &mut [u8]
|
||||
}
|
||||
}
|
||||
}
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
use common::slice::GetSlice;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
||||
use arch::memory::Memory;
|
||||
|
||||
use collections::slice;
|
||||
use collections::string::{String, ToString};
|
||||
use collections::vec::Vec;
|
||||
|
||||
use common::debug;
|
||||
|
||||
use core::cmp;
|
||||
|
||||
use disk::Disk;
|
||||
use disk::ide::Extent;
|
||||
|
||||
use fs::redoxfs::{FileSystem, Node, NodeData};
|
||||
|
||||
use fs::{KScheme, Resource, ResourceSeek, Url, VecResource};
|
||||
|
||||
use syscall::{Error, Result, O_CREAT, ENOENT, EIO};
|
||||
|
||||
/// A file resource
|
||||
pub struct FileResource {
|
||||
pub scheme: *mut FileScheme,
|
||||
pub node: Node,
|
||||
pub vec: Vec<u8>,
|
||||
pub seek: usize,
|
||||
pub dirty: bool,
|
||||
}
|
||||
|
||||
impl Resource for FileResource {
|
||||
fn dup(&self) -> Result<Box<Resource>> {
|
||||
Ok(Box::new(FileResource {
|
||||
scheme: self.scheme,
|
||||
node: self.node.clone(),
|
||||
vec: self.vec.clone(),
|
||||
seek: self.seek,
|
||||
dirty: self.dirty,
|
||||
}))
|
||||
}
|
||||
|
||||
fn path(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
let path_a = b"file:/";
|
||||
let path_b = self.node.name.as_bytes();
|
||||
for (b, p) in buf.iter_mut().zip(path_a.iter().chain(path_b.iter())) {
|
||||
*b = *p;
|
||||
}
|
||||
|
||||
Ok(cmp::min(buf.len(), path_a.len() + path_b.len()))
|
||||
}
|
||||
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
let mut i = 0;
|
||||
while i < buf.len() && self.seek < self.vec.len() {
|
||||
match self.vec.get(self.seek) {
|
||||
Some(b) => buf[i] = *b,
|
||||
None => (),
|
||||
}
|
||||
self.seek += 1;
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
let mut i = 0;
|
||||
while i < buf.len() && self.seek < self.vec.len() {
|
||||
self.vec[self.seek] = buf[i];
|
||||
self.seek += 1;
|
||||
i += 1;
|
||||
}
|
||||
while i < buf.len() {
|
||||
self.vec.push(buf[i]);
|
||||
self.seek += 1;
|
||||
i += 1;
|
||||
}
|
||||
if i > 0 {
|
||||
self.dirty = true;
|
||||
}
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn seek(&mut self, pos: ResourceSeek) -> Result<usize> {
|
||||
match pos {
|
||||
ResourceSeek::Start(offset) => self.seek = offset,
|
||||
ResourceSeek::Current(offset) =>
|
||||
self.seek = cmp::max(0, self.seek as isize + offset) as usize,
|
||||
ResourceSeek::End(offset) =>
|
||||
self.seek = cmp::max(0, self.vec.len() as isize + offset) as usize,
|
||||
}
|
||||
while self.vec.len() < self.seek {
|
||||
self.vec.push(0);
|
||||
}
|
||||
Ok(self.seek)
|
||||
}
|
||||
|
||||
// TODO: Check to make sure proper amount of bytes written. See Disk::write
|
||||
// TODO: Allow reallocation
|
||||
fn sync(&mut self) -> Result<()> {
|
||||
if self.dirty {
|
||||
let mut node_dirty = false;
|
||||
let mut pos = 0;
|
||||
let mut remaining = self.vec.len() as isize;
|
||||
for ref mut extent in &mut self.node.extents {
|
||||
if remaining > 0 && extent.empty() {
|
||||
debug::d("Reallocate file, extra: ");
|
||||
debug::ds(remaining);
|
||||
debug::dl();
|
||||
|
||||
unsafe {
|
||||
let sectors = ((remaining + 511) / 512) as u64;
|
||||
if (*self.scheme).fs.header.free_space.length >= sectors * 512 {
|
||||
extent.block = (*self.scheme).fs.header.free_space.block;
|
||||
extent.length = remaining as u64;
|
||||
(*self.scheme).fs.header.free_space.block = (*self.scheme)
|
||||
.fs
|
||||
.header
|
||||
.free_space
|
||||
.block +
|
||||
sectors;
|
||||
(*self.scheme).fs.header.free_space.length = (*self.scheme)
|
||||
.fs
|
||||
.header
|
||||
.free_space
|
||||
.length -
|
||||
sectors * 512;
|
||||
|
||||
node_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure it is a valid extent
|
||||
if !extent.empty() {
|
||||
let current_sectors = (extent.length as usize + 511) / 512;
|
||||
let max_size = current_sectors * 512;
|
||||
|
||||
let size = cmp::min(remaining as usize, max_size);
|
||||
|
||||
if size as u64 != extent.length {
|
||||
extent.length = size as u64;
|
||||
node_dirty = true;
|
||||
}
|
||||
|
||||
while self.vec.len() < pos + max_size {
|
||||
self.vec.push(0);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _ = (*self.scheme).fs.disk.write(extent.block, &self.vec[pos .. pos + max_size]);
|
||||
}
|
||||
|
||||
self.vec.truncate(pos + size);
|
||||
|
||||
pos += size;
|
||||
remaining -= size as isize;
|
||||
}
|
||||
}
|
||||
|
||||
if node_dirty {
|
||||
debug::d("Node dirty, rewrite\n");
|
||||
|
||||
if self.node.block > 0 {
|
||||
unsafe {
|
||||
if let Some(mut node_data) = Memory::<NodeData>::new(1) {
|
||||
node_data.write(0, self.node.data());
|
||||
|
||||
let mut buffer = slice::from_raw_parts(node_data.address() as *mut u8, 512);
|
||||
let _ = (*self.scheme).fs.disk.write(self.node.block, &mut buffer);
|
||||
|
||||
debug::d("Renode\n");
|
||||
|
||||
for mut node in (*self.scheme).fs.nodes.iter_mut() {
|
||||
if node.block == self.node.block {
|
||||
*node = self.node.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug::d("Need to place Node block\n");
|
||||
}
|
||||
}
|
||||
|
||||
self.dirty = false;
|
||||
|
||||
if remaining > 0 {
|
||||
debug::d("Need to defragment file, extra: ");
|
||||
debug::ds(remaining);
|
||||
debug::dl();
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn truncate(&mut self, len: usize) -> Result<()> {
|
||||
while len > self.vec.len() {
|
||||
self.vec.push(0);
|
||||
}
|
||||
self.vec.truncate(len);
|
||||
self.seek = cmp::min(self.seek, self.vec.len());
|
||||
self.dirty = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FileResource {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.sync();
|
||||
}
|
||||
}
|
||||
|
||||
/// A file scheme (pci + fs)
|
||||
pub struct FileScheme {
|
||||
fs: FileSystem,
|
||||
}
|
||||
|
||||
impl FileScheme {
|
||||
/// Create a new file scheme from an array of Disks
|
||||
pub fn new(mut disks: Vec<Box<Disk>>) -> Option<Box<Self>> {
|
||||
while ! disks.is_empty() {
|
||||
if let Some(fs) = FileSystem::from_disk(disks.remove(0)) {
|
||||
return Some(Box::new(FileScheme { fs: fs }));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl KScheme for FileScheme {
|
||||
fn on_irq(&mut self, _irq: u8) {
|
||||
/*if irq == self.fs.disk.irq {
|
||||
}*/
|
||||
}
|
||||
|
||||
fn scheme(&self) -> &str {
|
||||
"file"
|
||||
}
|
||||
|
||||
fn open(&mut self, url: &Url, flags: usize) -> Result<Box<Resource>> {
|
||||
let mut path = url.reference();
|
||||
while path.starts_with('/') {
|
||||
path = &path[1..];
|
||||
}
|
||||
if path.is_empty() || path.ends_with('/') {
|
||||
let mut list = String::new();
|
||||
let mut dirs: Vec<String> = Vec::new();
|
||||
|
||||
for file in self.fs.list(path).iter() {
|
||||
let mut line = String::new();
|
||||
match file.find('/') {
|
||||
Some(index) => {
|
||||
let dirname = file.get_slice(..index + 1).to_string();
|
||||
let mut found = false;
|
||||
for dir in dirs.iter() {
|
||||
if dirname == *dir {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if found {
|
||||
line.clear();
|
||||
} else {
|
||||
line = dirname.clone();
|
||||
dirs.push(dirname);
|
||||
}
|
||||
}
|
||||
None => line = file.clone(),
|
||||
}
|
||||
if !line.is_empty() {
|
||||
if !list.is_empty() {
|
||||
list = list + "\n" + &line;
|
||||
} else {
|
||||
list = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if list.len() > 0 {
|
||||
Ok(Box::new(VecResource::new(&url.string, list.into_bytes())))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
} else {
|
||||
match self.fs.node(path) {
|
||||
Some(node) => {
|
||||
let mut vec: Vec<u8> = Vec::new();
|
||||
for extent in &node.extents {
|
||||
if extent.block > 0 && extent.length > 0 {
|
||||
let current_sectors = (extent.length as usize + 511) / 512;
|
||||
let max_size = current_sectors * 512;
|
||||
|
||||
let size = cmp::min(extent.length as usize, max_size);
|
||||
|
||||
let pos = vec.len();
|
||||
|
||||
while vec.len() < pos + max_size {
|
||||
vec.push(0);
|
||||
}
|
||||
|
||||
let _ = self.fs.disk.read(extent.block, &mut vec[pos..pos + max_size]);
|
||||
|
||||
vec.truncate(pos + size);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Box::new(FileResource {
|
||||
scheme: self,
|
||||
node: node,
|
||||
vec: vec,
|
||||
seek: 0,
|
||||
dirty: false,
|
||||
}))
|
||||
}
|
||||
None => {
|
||||
if flags & O_CREAT == O_CREAT {
|
||||
// TODO: Create file
|
||||
let mut node = Node {
|
||||
block: 0,
|
||||
name: path.to_string(),
|
||||
extents: [Extent {
|
||||
block: 0,
|
||||
length: 0,
|
||||
}; 16],
|
||||
};
|
||||
|
||||
if self.fs.header.free_space.length >= 512 {
|
||||
node.block = self.fs.header.free_space.block;
|
||||
self.fs.header.free_space.block = self.fs.header.free_space.block + 1;
|
||||
self.fs.header.free_space.length = self.fs.header.free_space.length -
|
||||
512;
|
||||
}
|
||||
|
||||
self.fs.nodes.push(node.clone());
|
||||
|
||||
Ok(Box::new(FileResource {
|
||||
scheme: self,
|
||||
node: node,
|
||||
vec: Vec::new(),
|
||||
seek: 0,
|
||||
dirty: false,
|
||||
}))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unlink(&mut self, url: &Url) -> Result<()> {
|
||||
let mut ret = Err(Error::new(ENOENT));
|
||||
|
||||
let mut path = url.reference();
|
||||
while path.starts_with('/') {
|
||||
path = &path[1..];
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
while i < self.fs.nodes.len() {
|
||||
let mut remove = false;
|
||||
|
||||
if let Some(node) = self.fs.nodes.get(i) {
|
||||
remove = node.name == path;
|
||||
}
|
||||
|
||||
if remove {
|
||||
self.fs.nodes.remove(i);
|
||||
ret = Ok(());
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
extern crate redoxfs;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Result, Read, Write, Seek, SeekFrom};
|
||||
use std::str;
|
||||
|
||||
use redoxfs::{Disk, FileSystem};
|
||||
|
||||
pub struct FileDisk {
|
||||
path: String,
|
||||
file: File
|
||||
}
|
||||
|
||||
impl FileDisk {
|
||||
pub fn new(path: &str) -> Result<FileDisk> {
|
||||
let file = try!(File::open(path));
|
||||
Ok(FileDisk {
|
||||
path: path.to_string(),
|
||||
file: file
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Disk for FileDisk {
|
||||
fn name(&self) -> &str {
|
||||
&self.path
|
||||
}
|
||||
|
||||
fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||
try!(self.file.seek(SeekFrom::Start(block * 512)));
|
||||
self.file.read(buffer)
|
||||
}
|
||||
|
||||
fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
|
||||
try!(self.file.seek(SeekFrom::Start(block * 512)));
|
||||
self.file.write(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let disk = FileDisk::new("../../build/i386-unknown-redox/debug/harddrive.bin").unwrap();
|
||||
let filesystem = FileSystem::new(Box::new(disk)).unwrap();
|
||||
for (node_block, node) in filesystem.nodes.iter() {
|
||||
let name = unsafe { str::from_utf8_unchecked(&node.name) };
|
||||
println!("{}: {}", node_block, name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user