Commit bc2d90f5 authored by Jeremy Soller's avatar Jeremy Soller

Allow read of filesystem names

parents
# Compiled files
*.o
*.so
*.rlib
*.dll
# Executables
*.exe
# Generated by Cargo
Cargo.lock
/target/
language: rust
rust:
- nightly
sudo: false
notifications:
email: false
[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"
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.
# redoxfs
The Redox Filesystem. Compatible with Redox and Linux.
[![Travis Build Status](https://travis-ci.org/redox-os/redoxfs.svg?branch=master)](https://travis-ci.org/redox-os/redoxfs)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![crates.io](http://meritbadge.herokuapp.com/redoxfs)](https://crates.io/crates/redoxfs)
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>;
}
/// A disk extent
#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
pub struct Extent {
pub block: u64,
pub length: u64,
}
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()))
}
}
}
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]
}
}
}
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;
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]
}
}
}
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
}
}
extern crate redoxfs;
use std::fs::File;
use std::io::{Result, Read, Write, Seek, SeekFrom};
use std::str;
use redoxfs::{Disk, FileSystem};